From 7f988568ebe84147cda623be6cb8b8152374f90e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 08:46:28 -0700 Subject: [PATCH 1/4] Bump github.com/open-policy-agent/opa from 0.57.0 to 0.57.1 (#363) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.57.0 to 0.57.1. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.57.0...v0.57.1) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 +--- go.sum | 12 +++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 0f4cfdd2..f0ecb515 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/subcommands v1.2.0 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 - github.com/open-policy-agent/opa v0.57.0 + github.com/open-policy-agent/opa v0.57.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/stretchr/testify v1.8.4 @@ -95,8 +95,6 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 // indirect go.opentelemetry.io/otel/sdk v1.19.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/go.sum b/go.sum index 174c1fb1..77f80fe6 100644 --- a/go.sum +++ b/go.sum @@ -276,8 +276,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= -github.com/open-policy-agent/opa v0.57.0 h1:DftxYfOEHOheXvO2Q6HCIM2ZVdKrvnF4cZlU9C64MIQ= -github.com/open-policy-agent/opa v0.57.0/go.mod h1:3FY6GNSbUqOhjCdvTXCBJ2rNuh66p/XrIc2owr/hSwo= +github.com/open-policy-agent/opa v0.57.1 h1:LAa4Z0UkpjV94nRLy6XCvgOacQ6N1jf8TJLMUIzFRqc= +github.com/open-policy-agent/opa v0.57.1/go.mod h1:YYcVsWcdOW47owR0zElx8HPYZK60vL0MOPsEmh13us4= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -333,13 +333,11 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= From db1360e0dc136b48b4cce082c70d6d2646b2f47c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:57:50 +0000 Subject: [PATCH 2/4] Bump google.golang.org/grpc from 1.58.3 to 1.59.0 (#362) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.3 to 1.59.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.58.3...v1.59.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 14 +++++++------- go.sum | 30 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index f0ecb515..60f51677 100644 --- a/go.mod +++ b/go.mod @@ -23,15 +23,15 @@ require ( gocloud.dev v0.32.0 golang.org/x/sync v0.4.0 golang.org/x/sys v0.13.0 - google.golang.org/grpc v1.58.3 + google.golang.org/grpc v1.59.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/protobuf v1.31.0 gopkg.in/ini.v1 v1.67.0 ) require ( - cloud.google.com/go v0.110.6 // indirect - cloud.google.com/go/compute v1.22.0 // indirect + cloud.google.com/go v0.110.7 // indirect + cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.1 // indirect cloud.google.com/go/storage v1.31.0 // indirect @@ -98,14 +98,14 @@ require ( go.opentelemetry.io/otel/sdk v1.19.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/oauth2 v0.11.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.132.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect + google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index 77f80fe6..0a6f471e 100644 --- a/go.sum +++ b/go.sum @@ -13,16 +13,16 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.22.0 h1:cB8R6FtUtT1TYGl5R3xuxnW6OUIc/DrT2aiR16TTG7Y= -cloud.google.com/go/compute v1.22.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -169,7 +169,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -432,8 +432,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -603,12 +603,12 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 h1:+VoAg+OKmWaommL56xmZSE2sUK8A7m6SUO7X89F2tbw= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753/go.mod h1:iqkVr8IRpZ53gx1dEnWlCUIEwDWqWARWrbzpasaTNYM= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 h1:lCbbUxUDD+DiXx9Q6F/ttL0aAu7N2pz8XnmMm8ZW4NE= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 h1:XUODHrpzJEUeWmVo/jfNTLj0YyVveOo28oE6vkFbkO4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -626,8 +626,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= From f8d2ce3873b43ec13690d7760a8e0565e1402f41 Mon Sep 17 00:00:00 2001 From: Steven Rhodes Date: Mon, 23 Oct 2023 18:27:24 -0700 Subject: [PATCH 3/4] Add a MPA design and proto definition. (#350) * Add a MPA design and proto definition. I've prototyped this enough in https://github.com/stvnrhodes/sansshell/tree/mpa to have reasonable confidence that this design is implementable. I'm separating out the design and proto into an initial PR so that we can make sure that we agree on the high-level direction before we deeply review any implementation. The readme is meant to be used as long-term documentation on how to use MPA. Part of https://github.com/Snowflake-Labs/sansshell/issues/346 * Address feedback --- .gitattributes | 2 + services/mpa/README.md | 192 +++++ services/mpa/mpa.go | 22 + services/mpa/mpa.pb.go | 1159 ++++++++++++++++++++++++++++++ services/mpa/mpa.proto | 120 ++++ services/mpa/mpa_grpc.pb.go | 347 +++++++++ services/mpa/mpa_grpcproxy.pb.go | 440 ++++++++++++ 7 files changed, 2282 insertions(+) create mode 100644 .gitattributes create mode 100644 services/mpa/README.md create mode 100644 services/mpa/mpa.go create mode 100644 services/mpa/mpa.pb.go create mode 100644 services/mpa/mpa.proto create mode 100644 services/mpa/mpa_grpc.pb.go create mode 100644 services/mpa/mpa_grpcproxy.pb.go diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..f4b4239b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.pb.go linguist-generated +go.sum linguist-generated \ No newline at end of file diff --git a/services/mpa/README.md b/services/mpa/README.md new file mode 100644 index 00000000..79923186 --- /dev/null +++ b/services/mpa/README.md @@ -0,0 +1,192 @@ +# Multi Party Authentication + +WARNING: This document describes the intended state. https://github.com/Snowflake-Labs/sansshell/issues/346 tracks implementation. + +This module enables [multi-party authorization](https://en.wikipedia.org/wiki/Multi-party_authorization) for any sansshell command. Approval data is stored in-memory in sansshell-server. + +## User flow + +MPA must be explicitly requested. When requested, the MPA flow will be used regardless of whether a policy would allow a command without MPA. + +1. A user issues commands. + + ```bash + $ sanssh -mpa -targets=1.2.3.4 -justification emergency exec run /bin/echo hi + Waiting for approval for 1-2345-6789. Command for approving: + sanssh -targets=1.2.3.4 mpa approve 1-2345-6789 + ``` + +2. The approver views the commands and approves it. + + ```bash + $ sanssh -targets=1.2.3.4 mpa list + 1-2345-6789 + $ sanssh -targets=1.2.3.4 mpa get 1-2345-6789 + user: firstuser + justification: emergency + method: /Exec.Exec/Run + message: { + "command": "/bin/echo", + "args": ["hi"] + } + $ sanssh -targets=1.2.3.4 mpa approve 1-2345-6789 + ``` + +3. If the user's command is still running, it will complete. If the user had stopped their command, they can rerun it and the approval will still be valid as long as the command's input remains the same and the sansshell-server still has the approval in memory. Approvals are lost if the server restarts, if the server evicts the approval due to age or staleness, or if a user calls `sanssh mpa clear` oon the request id. + +## Enabling MPA + +SansShell is built on a principle of "Don't pay for what you don't use". MPA is a more invasive than the typical sansshell module, so it requires updating more places than most modules. The reference sanssh, sansshell-server, and proxy-server binaries implement these changes. + +1. In sanssh, in addition to importing the module to get the `mpa` subcommand, you should conditionally add interceptors when a `-mpa` flag is provided to the cli. This will let other sanssh commands use MPA with the `-mpa` flag and get the user experience mentioned above. There are four interceptors. + + 1. Unary interceptor for direct calls + 2. Stream interceptor for direct calls + 3. Unary interceptor for proxied calls + 4. Stream interceptor for proxied calls. + + The interceptors for direct calls get added when dialing out. + + ```go + proxy.DialContext(ctx, proxy, targets, + grpc.WithStreamInterceptor(mpahooks.StreamClientIntercepter), + grpc.WithUnaryInterceptor(mpahooks.MPAUnaryClientInterceptor)) + ``` + + The interceptors for proxied calls are added as fields in the `*proxy.Conn` struct. + + ```go + conn.UnaryInterceptors = []proxy.UnaryInterceptor{mpahooks.ProxyClientUnaryInterceptor(state)} + conn.StreamInterceptors = []proxy.StreamInterceptor{mpahooks.ProxyClientStreamInterceptor(state)} + ``` + +2. In sansshell-server, import the MPA service and add an authz hook to consult the local datastore for MPA info. + + ```go + mpa "github.com/Snowflake-Labs/sansshell/services/mpa/server" + server.WithAuthzHook(mpa.ServerMPAAuthzHook) + ``` + +3. If using the proxy-server, add an authz hook to consult the server for MPA info. + + ```go + proxy.WithAuthzHook(mpa.ProxyMPAAuthzHook) + ``` + + You'll also need to set an additional interceptor on the server to make proxied identity information available. + + ```go + func(ctx context.Context) bool { + peer := rpcauth.PeerInputFromContext(ctx) + if peer == nil { + return false + } + // Custom business logic goes here. + } + proxiedidentity.ServerProxiedIdentityUnaryInterceptor() + proxiedidentity.ServerProxiedIdentityStreamInterceptor() + ``` + +4. Any approvers must be able to call `/Mpa.Mpa/Approve` and any requestor must be able to call `/Mpa.Mpa/Store`. It's highly recommended to additionally let potential approvers call `/Mpa.Mpa/Get` and potential requestors call `/Mpa.Mpa/WaitForApproval` for better user experiences. `/Mpa.Mpa/Clear` can be used for cancelling MPA requests. + +Approvers will show up in [RPCAuthInput](https://pkg.go.dev/github.com/Snowflake-Labs/sansshell/auth/opa/rpcauth#RPCAuthInput). Match on these in the OPA policies. + +```rego +allow if { + input.approvers[_].principal.id == 'superuser' +} +``` + +## Design details + +MPA requests and approvals are stored in memory in sansshell-server. The id for a request is generated as a hash of the request information, allowing us to reuse the same id for multiple identical requests across multiple commands or across multiple machines. + +To support proxying, there are multiple ways of populating the user identity used in `/Mpa.Mpa/Store` and `/Mpa.Mpa/Approve`. + +1. From the `sansshell-proxied-identity` key in the [gRPC metadata](https://grpc.io/docs/what-is-grpc/core-concepts/#metadata), used if the identity is set and the server has been configured to accept a proxied identity. The value of this is a JSON representation of `rpcauth.Principal`. +2. From the peer identity of the call, used in all other cases. + +Justification information can be provided via a `sansshell-justification` key in the gRPC metadata, available as a constant at `rpcauth.ReqJustKey`. + +The values in [RPCAuthInput](https://pkg.go.dev/github.com/Snowflake-Labs/sansshell/auth/opa/rpcauth#RPCAuthInput) are populated by authz hooks that look up a MPA request based on the `sansshell-mpa-request-id` key in the gRPC metadata. Requests will fail if this refers to an invalid or missing request. + +Client-side streaming RPCs that involve more than one streamed message are not supported because it's not possible to evaluate the client's messages prior to the request. + +### Server-only flow + +The server-only flow is the simplest scenario. + +```mermaid +sequenceDiagram + actor approver + actor client + client->>+server: /Mpa.Mpa/Store + server->>server: PolicyCheck / log + server->>-client: ID for request + client->>approver: Give request ID + approver->>+server: /Mpa.Mpa/Approve + server->>-server: PolicyCheck / log + client->>server: Make normal call with sansshell-mpa-request-id + server->>server: PolicyCheck / log +``` + +Typical usage will involve `/Mpa.Mpa/WaitForApproval` so that `sanssh` can make the call as soon as it has approval. + +```mermaid +sequenceDiagram + actor approver + actor client + client->>+server: /Mpa.Mpa/Store + server->>server: PolicyCheck / log + server->>-client: ID for request + client->>+server: /Mpa.Mpa/WaitForApproval + server->>server: PolicyCheck / log + client->>approver: Give request ID + approver->>+server: /Mpa.Mpa/Approve + server->>-server: PolicyCheck / log + server->>-client: /Mpa.Mpa/WaitForApproval completes + client->>server: Make normal call with sansshell-mpa-request-id + server->>server: PolicyCheck / log +``` + +### Server + Proxy flow + +The proxy flow is a bit more complicated than the server flow because the proxy relies on the server for maintaining state. + +```mermaid +sequenceDiagram + actor approver + actor client + client->>+proxy: /Mpa.Mpa/Store + proxy->>proxy: PolicyCheck / log + proxy->>+server: /Mpa.Mpa/Store + server->>server: PolicyCheck / log + server->>-proxy: ID for request + proxy->>-client: ID for request + client->>+proxy: /Mpa.Mpa/WaitForApproval + proxy->>proxy: PolicyCheck / log + proxy->>+server: /Mpa.Mpa/WaitForApproval + server->>server: PolicyCheck / log + client->>approver: Give request ID + approver->>+proxy: /Mpa.Mpa/Approve + proxy->>proxy: PolicyCheck / log + proxy->>-server: /Mpa.Mpa/Approve + server->>server: PolicyCheck / log + server->>-proxy: /Mpa.Mpa/WaitForApproval completes + proxy->>-client: /Mpa.Mpa/WaitForApproval completes + client->>+proxy: Make normal call with sansshell-mpa-request-id + proxy->>+server: /Mpa.Mpa/Get + server->>server: PolicyCheck / log + server->>-proxy: Approver info + proxy->>proxy: PolicyCheck / log + proxy->>-server: Make normal call +``` + +## Caveats + +- Due to the complexity of OPA policies, we don't support automatically recognizing that a request requires MPA. + - If you want to give feedback that an action would succeed with MPA, check out [DenialHints](https://pkg.go.dev/github.com/Snowflake-Labs/sansshell/auth/opa#WithDenialHintsQuery) +- We also don't support recognizing in advance whether MPA would let an action succeed. +- You can easily write policies that allow people to approve actions even if their approval isn't useful +- All state is stored in-memory in sansshell-server. Any restarts of the server will clear approvals. +- Be wary of guarding too many actions behind MPA. If it gets used too regularly, humans will quickly get used to blindly approving commands without thinking through whether each command is necessary. diff --git a/services/mpa/mpa.go b/services/mpa/mpa.go new file mode 100644 index 00000000..f9eab4c9 --- /dev/null +++ b/services/mpa/mpa.go @@ -0,0 +1,22 @@ +/* Copyright (c) 2019 Snowflake Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +// Package mpa defines the RPC interface for the sansshell MPA actions. +package mpa + +// To regenerate the proto headers if the .proto changes, just run go generate +// and this encodes the necessary magic: +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. --go-grpc_opt=paths=source_relative --go-grpcproxy_out=. --go-grpcproxy_opt=paths=source_relative mpa.proto diff --git a/services/mpa/mpa.pb.go b/services/mpa/mpa.pb.go new file mode 100644 index 00000000..ffa971c7 --- /dev/null +++ b/services/mpa/mpa.pb.go @@ -0,0 +1,1159 @@ +// Copyright (c) 2022 Snowflake Inc. All rights reserved. +// +//Licensed under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.19.6 +// source: mpa.proto + +package mpa + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Action struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The user that created the request. + User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + // User-supplied information on why the request is being made. + Justification string `protobuf:"bytes,2,opt,name=justification,proto3" json:"justification,omitempty"` + // The GRPC method name, as '/Package.Service/Method' + Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` + // The request protocol buffer. + Message *anypb.Any `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *Action) Reset() { + *x = Action{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Action) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Action) ProtoMessage() {} + +func (x *Action) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Action.ProtoReflect.Descriptor instead. +func (*Action) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{0} +} + +func (x *Action) GetUser() string { + if x != nil { + return x.User + } + return "" +} + +func (x *Action) GetJustification() string { + if x != nil { + return x.Justification + } + return "" +} + +func (x *Action) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *Action) GetMessage() *anypb.Any { + if x != nil { + return x.Message + } + return nil +} + +type Principal struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The principal identifier (e.g. a username or service role) + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Auxiliary groups associated with this principal. + Groups []string `protobuf:"bytes,2,rep,name=groups,proto3" json:"groups,omitempty"` +} + +func (x *Principal) Reset() { + *x = Principal{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Principal) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Principal) ProtoMessage() {} + +func (x *Principal) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Principal.ProtoReflect.Descriptor instead. +func (*Principal) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{1} +} + +func (x *Principal) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Principal) GetGroups() []string { + if x != nil { + return x.Groups + } + return nil +} + +type StoreRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The GRPC method name, as '/Package.Service/Method' + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + // The request protocol buffer. + Message *anypb.Any `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *StoreRequest) Reset() { + *x = StoreRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreRequest) ProtoMessage() {} + +func (x *StoreRequest) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreRequest.ProtoReflect.Descriptor instead. +func (*StoreRequest) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{2} +} + +func (x *StoreRequest) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *StoreRequest) GetMessage() *anypb.Any { + if x != nil { + return x.Message + } + return nil +} + +type StoreResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Action *Action `protobuf:"bytes,2,opt,name=action,proto3" json:"action,omitempty"` + // All approvers of the request. Storing is idempotent, so + // approvers may be non-empty if we're storing a previously + // approved command. + Approver []*Principal `protobuf:"bytes,3,rep,name=approver,proto3" json:"approver,omitempty"` +} + +func (x *StoreResponse) Reset() { + *x = StoreResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreResponse) ProtoMessage() {} + +func (x *StoreResponse) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreResponse.ProtoReflect.Descriptor instead. +func (*StoreResponse) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{3} +} + +func (x *StoreResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *StoreResponse) GetAction() *Action { + if x != nil { + return x.Action + } + return nil +} + +func (x *StoreResponse) GetApprover() []*Principal { + if x != nil { + return x.Approver + } + return nil +} + +type ApproveRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Approve takes an action instead of an ID to improve auditability + // and allow richer authorization logic. + Action *Action `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` +} + +func (x *ApproveRequest) Reset() { + *x = ApproveRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApproveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveRequest) ProtoMessage() {} + +func (x *ApproveRequest) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApproveRequest.ProtoReflect.Descriptor instead. +func (*ApproveRequest) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{4} +} + +func (x *ApproveRequest) GetAction() *Action { + if x != nil { + return x.Action + } + return nil +} + +type ApproveResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ApproveResponse) Reset() { + *x = ApproveResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApproveResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveResponse) ProtoMessage() {} + +func (x *ApproveResponse) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApproveResponse.ProtoReflect.Descriptor instead. +func (*ApproveResponse) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{5} +} + +type WaitForApprovalRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *WaitForApprovalRequest) Reset() { + *x = WaitForApprovalRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WaitForApprovalRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitForApprovalRequest) ProtoMessage() {} + +func (x *WaitForApprovalRequest) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WaitForApprovalRequest.ProtoReflect.Descriptor instead. +func (*WaitForApprovalRequest) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{6} +} + +func (x *WaitForApprovalRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type WaitForApprovalResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *WaitForApprovalResponse) Reset() { + *x = WaitForApprovalResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WaitForApprovalResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitForApprovalResponse) ProtoMessage() {} + +func (x *WaitForApprovalResponse) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WaitForApprovalResponse.ProtoReflect.Descriptor instead. +func (*WaitForApprovalResponse) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{7} +} + +type ListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListRequest) Reset() { + *x = ListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRequest) ProtoMessage() {} + +func (x *ListRequest) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRequest.ProtoReflect.Descriptor instead. +func (*ListRequest) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{8} +} + +type ListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item []*ListResponse_Item `protobuf:"bytes,1,rep,name=item,proto3" json:"item,omitempty"` +} + +func (x *ListResponse) Reset() { + *x = ListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResponse) ProtoMessage() {} + +func (x *ListResponse) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListResponse.ProtoReflect.Descriptor instead. +func (*ListResponse) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{9} +} + +func (x *ListResponse) GetItem() []*ListResponse_Item { + if x != nil { + return x.Item + } + return nil +} + +type GetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetRequest) Reset() { + *x = GetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRequest) ProtoMessage() {} + +func (x *GetRequest) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRequest.ProtoReflect.Descriptor instead. +func (*GetRequest) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{10} +} + +func (x *GetRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Action *Action `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` + // All approvers of the request. + Approver []*Principal `protobuf:"bytes,2,rep,name=approver,proto3" json:"approver,omitempty"` +} + +func (x *GetResponse) Reset() { + *x = GetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetResponse) ProtoMessage() {} + +func (x *GetResponse) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetResponse.ProtoReflect.Descriptor instead. +func (*GetResponse) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{11} +} + +func (x *GetResponse) GetAction() *Action { + if x != nil { + return x.Action + } + return nil +} + +func (x *GetResponse) GetApprover() []*Principal { + if x != nil { + return x.Approver + } + return nil +} + +type ClearRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Action *Action `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` +} + +func (x *ClearRequest) Reset() { + *x = ClearRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClearRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearRequest) ProtoMessage() {} + +func (x *ClearRequest) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClearRequest.ProtoReflect.Descriptor instead. +func (*ClearRequest) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{12} +} + +func (x *ClearRequest) GetAction() *Action { + if x != nil { + return x.Action + } + return nil +} + +type ClearResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ClearResponse) Reset() { + *x = ClearResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClearResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearResponse) ProtoMessage() {} + +func (x *ClearResponse) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClearResponse.ProtoReflect.Descriptor instead. +func (*ClearResponse) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{13} +} + +type ListResponse_Item struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Action *Action `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` + Approver []*Principal `protobuf:"bytes,2,rep,name=approver,proto3" json:"approver,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ListResponse_Item) Reset() { + *x = ListResponse_Item{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListResponse_Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResponse_Item) ProtoMessage() {} + +func (x *ListResponse_Item) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListResponse_Item.ProtoReflect.Descriptor instead. +func (*ListResponse_Item) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{9, 0} +} + +func (x *ListResponse_Item) GetAction() *Action { + if x != nil { + return x.Action + } + return nil +} + +func (x *ListResponse_Item) GetApprover() []*Principal { + if x != nil { + return x.Approver + } + return nil +} + +func (x *ListResponse_Item) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +var File_mpa_proto protoreflect.FileDescriptor + +var file_mpa_proto_rawDesc = []byte{ + 0x0a, 0x09, 0x6d, 0x70, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x4d, 0x70, 0x61, + 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8a, 0x01, 0x0a, 0x06, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x6a, 0x75, + 0x73, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x6e, + 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x56, 0x0a, + 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x70, 0x0a, 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x61, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x4d, 0x70, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x08, 0x61, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x22, 0x35, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x72, 0x6f, + 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x11, + 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x28, 0x0a, 0x16, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x57, + 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0d, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa3, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x1a, 0x67, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, + 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2a, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, + 0x6c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x0a, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x0b, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, + 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, + 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x22, 0x33, 0x0a, 0x0c, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x0f, + 0x0a, 0x0d, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, + 0xcc, 0x02, 0x0a, 0x03, 0x4d, 0x70, 0x61, 0x12, 0x30, 0x0a, 0x05, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x12, 0x11, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x07, 0x41, 0x70, 0x70, + 0x72, 0x6f, 0x76, 0x65, 0x12, 0x13, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, + 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x4e, 0x0a, 0x0f, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x1b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, + 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x2d, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x4d, 0x70, + 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x2a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x0f, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x05, + 0x43, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x11, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x43, 0x6c, 0x65, 0x61, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x43, + 0x6c, 0x65, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x29, + 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x53, 0x6e, 0x6f, + 0x77, 0x66, 0x6c, 0x61, 0x6b, 0x65, 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x73, 0x61, 0x6e, 0x73, + 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x2f, 0x6d, 0x70, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_mpa_proto_rawDescOnce sync.Once + file_mpa_proto_rawDescData = file_mpa_proto_rawDesc +) + +func file_mpa_proto_rawDescGZIP() []byte { + file_mpa_proto_rawDescOnce.Do(func() { + file_mpa_proto_rawDescData = protoimpl.X.CompressGZIP(file_mpa_proto_rawDescData) + }) + return file_mpa_proto_rawDescData +} + +var file_mpa_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_mpa_proto_goTypes = []interface{}{ + (*Action)(nil), // 0: Mpa.Action + (*Principal)(nil), // 1: Mpa.Principal + (*StoreRequest)(nil), // 2: Mpa.StoreRequest + (*StoreResponse)(nil), // 3: Mpa.StoreResponse + (*ApproveRequest)(nil), // 4: Mpa.ApproveRequest + (*ApproveResponse)(nil), // 5: Mpa.ApproveResponse + (*WaitForApprovalRequest)(nil), // 6: Mpa.WaitForApprovalRequest + (*WaitForApprovalResponse)(nil), // 7: Mpa.WaitForApprovalResponse + (*ListRequest)(nil), // 8: Mpa.ListRequest + (*ListResponse)(nil), // 9: Mpa.ListResponse + (*GetRequest)(nil), // 10: Mpa.GetRequest + (*GetResponse)(nil), // 11: Mpa.GetResponse + (*ClearRequest)(nil), // 12: Mpa.ClearRequest + (*ClearResponse)(nil), // 13: Mpa.ClearResponse + (*ListResponse_Item)(nil), // 14: Mpa.ListResponse.Item + (*anypb.Any)(nil), // 15: google.protobuf.Any +} +var file_mpa_proto_depIdxs = []int32{ + 15, // 0: Mpa.Action.message:type_name -> google.protobuf.Any + 15, // 1: Mpa.StoreRequest.message:type_name -> google.protobuf.Any + 0, // 2: Mpa.StoreResponse.action:type_name -> Mpa.Action + 1, // 3: Mpa.StoreResponse.approver:type_name -> Mpa.Principal + 0, // 4: Mpa.ApproveRequest.action:type_name -> Mpa.Action + 14, // 5: Mpa.ListResponse.item:type_name -> Mpa.ListResponse.Item + 0, // 6: Mpa.GetResponse.action:type_name -> Mpa.Action + 1, // 7: Mpa.GetResponse.approver:type_name -> Mpa.Principal + 0, // 8: Mpa.ClearRequest.action:type_name -> Mpa.Action + 0, // 9: Mpa.ListResponse.Item.action:type_name -> Mpa.Action + 1, // 10: Mpa.ListResponse.Item.approver:type_name -> Mpa.Principal + 2, // 11: Mpa.Mpa.Store:input_type -> Mpa.StoreRequest + 4, // 12: Mpa.Mpa.Approve:input_type -> Mpa.ApproveRequest + 6, // 13: Mpa.Mpa.WaitForApproval:input_type -> Mpa.WaitForApprovalRequest + 8, // 14: Mpa.Mpa.List:input_type -> Mpa.ListRequest + 10, // 15: Mpa.Mpa.Get:input_type -> Mpa.GetRequest + 12, // 16: Mpa.Mpa.Clear:input_type -> Mpa.ClearRequest + 3, // 17: Mpa.Mpa.Store:output_type -> Mpa.StoreResponse + 5, // 18: Mpa.Mpa.Approve:output_type -> Mpa.ApproveResponse + 7, // 19: Mpa.Mpa.WaitForApproval:output_type -> Mpa.WaitForApprovalResponse + 9, // 20: Mpa.Mpa.List:output_type -> Mpa.ListResponse + 11, // 21: Mpa.Mpa.Get:output_type -> Mpa.GetResponse + 13, // 22: Mpa.Mpa.Clear:output_type -> Mpa.ClearResponse + 17, // [17:23] is the sub-list for method output_type + 11, // [11:17] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_mpa_proto_init() } +func file_mpa_proto_init() { + if File_mpa_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_mpa_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Action); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Principal); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApproveRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApproveResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WaitForApprovalRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WaitForApprovalResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mpa_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListResponse_Item); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_mpa_proto_rawDesc, + NumEnums: 0, + NumMessages: 15, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_mpa_proto_goTypes, + DependencyIndexes: file_mpa_proto_depIdxs, + MessageInfos: file_mpa_proto_msgTypes, + }.Build() + File_mpa_proto = out.File + file_mpa_proto_rawDesc = nil + file_mpa_proto_goTypes = nil + file_mpa_proto_depIdxs = nil +} diff --git a/services/mpa/mpa.proto b/services/mpa/mpa.proto new file mode 100644 index 00000000..2e140be9 --- /dev/null +++ b/services/mpa/mpa.proto @@ -0,0 +1,120 @@ +/* Copyright (c) 2022 Snowflake Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +syntax = "proto3"; + +option go_package = "github.com/Snowflake-Labs/sansshell/mpa"; + +import "google/protobuf/any.proto"; + +package Mpa; + +// The Mpa service definition +service Mpa { + // Store a request that we'd like to try running in the future. Requests + // are stored in-memory and older requests may be cleared automatically. + // This call is idempotent - requests from the same user with the same + // contents will return the same id. + // + // The user for the request and any justification is implicitly passed in + // via inspecting the peer of the RPC or via gRPC metadata. + rpc Store(StoreRequest) returns (StoreResponse) {} + // Approve a previously stored request. A request can have multiple approvals + // and can be approved by anybody but the original user that stored the request. + // + // The user for the request is implicitly passed in via inspecting the + // peer of the RPC or via gRPC metadata. + rpc Approve(ApproveRequest) returns (ApproveResponse) {} + // Block until at least one approval has been granted. This is used + // as an optimization to avoid needing to poll for MPA approval. + rpc WaitForApproval(WaitForApprovalRequest) returns (WaitForApprovalResponse) {} + // List available requests. + rpc List(ListRequest) returns (ListResponse) {} + // Get a request and all approvals associated with it. + rpc Get(GetRequest) returns (GetResponse) {} + // Clear a stored request. + // + // This is typically unnecessary due to how requests are stored in memory + // and forgotten after a process restart. + rpc Clear(ClearRequest) returns (ClearResponse) {} +} + +message Action { + // The user that created the request. + string user = 1; + // User-supplied information on why the request is being made. + string justification = 2; + // The GRPC method name, as '/Package.Service/Method' + string method = 3; + // The request protocol buffer. + google.protobuf.Any message = 4; +} + +message Principal { + // The principal identifier (e.g. a username or service role) + string id = 1; + // Auxiliary groups associated with this principal. + repeated string groups = 2; +} + + +message StoreRequest { + // The GRPC method name, as '/Package.Service/Method' + string method = 1; + // The request protocol buffer. + google.protobuf.Any message = 2; +} + +message StoreResponse { + string id = 1; + Action action = 2; + // All approvers of the request. Storing is idempotent, so + // approvers may be non-empty if we're storing a previously + // approved command. + repeated Principal approver = 3; +} + +message ApproveRequest { + // Approve takes an action instead of an ID to improve auditability + // and allow richer authorization logic. + Action action = 1; +} +message ApproveResponse {} + +message WaitForApprovalRequest { string id = 1; } +message WaitForApprovalResponse {} + +message ListRequest {} +message ListResponse { + message Item { + Action action = 1; + repeated Principal approver = 2; + string id = 3; + } + repeated Item item = 1; +} + +message GetRequest { string id = 1; } + +message GetResponse { + Action action = 1; + + // All approvers of the request. + repeated Principal approver = 2; +} + +message ClearRequest { Action action = 1; } +message ClearResponse {} \ No newline at end of file diff --git a/services/mpa/mpa_grpc.pb.go b/services/mpa/mpa_grpc.pb.go new file mode 100644 index 00000000..701253d0 --- /dev/null +++ b/services/mpa/mpa_grpc.pb.go @@ -0,0 +1,347 @@ +// Copyright (c) 2022 Snowflake Inc. All rights reserved. +// +//Licensed under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.19.6 +// source: mpa.proto + +package mpa + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Mpa_Store_FullMethodName = "/Mpa.Mpa/Store" + Mpa_Approve_FullMethodName = "/Mpa.Mpa/Approve" + Mpa_WaitForApproval_FullMethodName = "/Mpa.Mpa/WaitForApproval" + Mpa_List_FullMethodName = "/Mpa.Mpa/List" + Mpa_Get_FullMethodName = "/Mpa.Mpa/Get" + Mpa_Clear_FullMethodName = "/Mpa.Mpa/Clear" +) + +// MpaClient is the client API for Mpa service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type MpaClient interface { + // Store a request that we'd like to try running in the future. Requests + // are stored in-memory and older requests may be cleared automatically. + // This call is idempotent - requests from the same user with the same + // contents will return the same id. + // + // The user for the request and any justification is implicitly passed in + // via inspecting the peer of the RPC or via gRPC metadata. + Store(ctx context.Context, in *StoreRequest, opts ...grpc.CallOption) (*StoreResponse, error) + // Approve a previously stored request. A request can have multiple approvals + // and can be approved by anybody but the original user that stored the request. + // + // The user for the request is implicitly passed in via inspecting the + // peer of the RPC or via gRPC metadata. + Approve(ctx context.Context, in *ApproveRequest, opts ...grpc.CallOption) (*ApproveResponse, error) + // Block until at least one approval has been granted. This is used + // as an optimization to avoid needing to poll for MPA approval. + WaitForApproval(ctx context.Context, in *WaitForApprovalRequest, opts ...grpc.CallOption) (*WaitForApprovalResponse, error) + // List available requests. + List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) + // Get a request and all approvals associated with it. + Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) + // Clear a stored request. + // + // This is typically unnecessary due to how requests are stored in memory + // and forgotten after a process restart. + Clear(ctx context.Context, in *ClearRequest, opts ...grpc.CallOption) (*ClearResponse, error) +} + +type mpaClient struct { + cc grpc.ClientConnInterface +} + +func NewMpaClient(cc grpc.ClientConnInterface) MpaClient { + return &mpaClient{cc} +} + +func (c *mpaClient) Store(ctx context.Context, in *StoreRequest, opts ...grpc.CallOption) (*StoreResponse, error) { + out := new(StoreResponse) + err := c.cc.Invoke(ctx, Mpa_Store_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *mpaClient) Approve(ctx context.Context, in *ApproveRequest, opts ...grpc.CallOption) (*ApproveResponse, error) { + out := new(ApproveResponse) + err := c.cc.Invoke(ctx, Mpa_Approve_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *mpaClient) WaitForApproval(ctx context.Context, in *WaitForApprovalRequest, opts ...grpc.CallOption) (*WaitForApprovalResponse, error) { + out := new(WaitForApprovalResponse) + err := c.cc.Invoke(ctx, Mpa_WaitForApproval_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *mpaClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { + out := new(ListResponse) + err := c.cc.Invoke(ctx, Mpa_List_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *mpaClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { + out := new(GetResponse) + err := c.cc.Invoke(ctx, Mpa_Get_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *mpaClient) Clear(ctx context.Context, in *ClearRequest, opts ...grpc.CallOption) (*ClearResponse, error) { + out := new(ClearResponse) + err := c.cc.Invoke(ctx, Mpa_Clear_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MpaServer is the server API for Mpa service. +// All implementations should embed UnimplementedMpaServer +// for forward compatibility +type MpaServer interface { + // Store a request that we'd like to try running in the future. Requests + // are stored in-memory and older requests may be cleared automatically. + // This call is idempotent - requests from the same user with the same + // contents will return the same id. + // + // The user for the request and any justification is implicitly passed in + // via inspecting the peer of the RPC or via gRPC metadata. + Store(context.Context, *StoreRequest) (*StoreResponse, error) + // Approve a previously stored request. A request can have multiple approvals + // and can be approved by anybody but the original user that stored the request. + // + // The user for the request is implicitly passed in via inspecting the + // peer of the RPC or via gRPC metadata. + Approve(context.Context, *ApproveRequest) (*ApproveResponse, error) + // Block until at least one approval has been granted. This is used + // as an optimization to avoid needing to poll for MPA approval. + WaitForApproval(context.Context, *WaitForApprovalRequest) (*WaitForApprovalResponse, error) + // List available requests. + List(context.Context, *ListRequest) (*ListResponse, error) + // Get a request and all approvals associated with it. + Get(context.Context, *GetRequest) (*GetResponse, error) + // Clear a stored request. + // + // This is typically unnecessary due to how requests are stored in memory + // and forgotten after a process restart. + Clear(context.Context, *ClearRequest) (*ClearResponse, error) +} + +// UnimplementedMpaServer should be embedded to have forward compatible implementations. +type UnimplementedMpaServer struct { +} + +func (UnimplementedMpaServer) Store(context.Context, *StoreRequest) (*StoreResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Store not implemented") +} +func (UnimplementedMpaServer) Approve(context.Context, *ApproveRequest) (*ApproveResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Approve not implemented") +} +func (UnimplementedMpaServer) WaitForApproval(context.Context, *WaitForApprovalRequest) (*WaitForApprovalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WaitForApproval not implemented") +} +func (UnimplementedMpaServer) List(context.Context, *ListRequest) (*ListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (UnimplementedMpaServer) Get(context.Context, *GetRequest) (*GetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedMpaServer) Clear(context.Context, *ClearRequest) (*ClearResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Clear not implemented") +} + +// UnsafeMpaServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to MpaServer will +// result in compilation errors. +type UnsafeMpaServer interface { + mustEmbedUnimplementedMpaServer() +} + +func RegisterMpaServer(s grpc.ServiceRegistrar, srv MpaServer) { + s.RegisterService(&Mpa_ServiceDesc, srv) +} + +func _Mpa_Store_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StoreRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MpaServer).Store(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Mpa_Store_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MpaServer).Store(ctx, req.(*StoreRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Mpa_Approve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApproveRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MpaServer).Approve(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Mpa_Approve_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MpaServer).Approve(ctx, req.(*ApproveRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Mpa_WaitForApproval_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WaitForApprovalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MpaServer).WaitForApproval(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Mpa_WaitForApproval_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MpaServer).WaitForApproval(ctx, req.(*WaitForApprovalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Mpa_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MpaServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Mpa_List_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MpaServer).List(ctx, req.(*ListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Mpa_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MpaServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Mpa_Get_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MpaServer).Get(ctx, req.(*GetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Mpa_Clear_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ClearRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MpaServer).Clear(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Mpa_Clear_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MpaServer).Clear(ctx, req.(*ClearRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Mpa_ServiceDesc is the grpc.ServiceDesc for Mpa service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Mpa_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "Mpa.Mpa", + HandlerType: (*MpaServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Store", + Handler: _Mpa_Store_Handler, + }, + { + MethodName: "Approve", + Handler: _Mpa_Approve_Handler, + }, + { + MethodName: "WaitForApproval", + Handler: _Mpa_WaitForApproval_Handler, + }, + { + MethodName: "List", + Handler: _Mpa_List_Handler, + }, + { + MethodName: "Get", + Handler: _Mpa_Get_Handler, + }, + { + MethodName: "Clear", + Handler: _Mpa_Clear_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "mpa.proto", +} diff --git a/services/mpa/mpa_grpcproxy.pb.go b/services/mpa/mpa_grpcproxy.pb.go new file mode 100644 index 00000000..b5ddd5b7 --- /dev/null +++ b/services/mpa/mpa_grpcproxy.pb.go @@ -0,0 +1,440 @@ +// Auto generated code by protoc-gen-go-grpcproxy +// DO NOT EDIT + +// Adds OneMany versions of RPC methods for use by proxy clients + +package mpa + +import ( + context "context" + proxy "github.com/Snowflake-Labs/sansshell/proxy/proxy" + grpc "google.golang.org/grpc" +) + +import ( + "fmt" +) + +// MpaClientProxy is the superset of MpaClient which additionally includes the OneMany proxy methods +type MpaClientProxy interface { + MpaClient + StoreOneMany(ctx context.Context, in *StoreRequest, opts ...grpc.CallOption) (<-chan *StoreManyResponse, error) + ApproveOneMany(ctx context.Context, in *ApproveRequest, opts ...grpc.CallOption) (<-chan *ApproveManyResponse, error) + WaitForApprovalOneMany(ctx context.Context, in *WaitForApprovalRequest, opts ...grpc.CallOption) (<-chan *WaitForApprovalManyResponse, error) + ListOneMany(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (<-chan *ListManyResponse, error) + GetOneMany(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (<-chan *GetManyResponse, error) + ClearOneMany(ctx context.Context, in *ClearRequest, opts ...grpc.CallOption) (<-chan *ClearManyResponse, error) +} + +// Embed the original client inside of this so we get the other generated methods automatically. +type mpaClientProxy struct { + *mpaClient +} + +// NewMpaClientProxy creates a MpaClientProxy for use in proxied connections. +// NOTE: This takes a proxy.Conn instead of a generic ClientConnInterface as the methods here are only valid in proxy.Conn contexts. +func NewMpaClientProxy(cc *proxy.Conn) MpaClientProxy { + return &mpaClientProxy{NewMpaClient(cc).(*mpaClient)} +} + +// StoreManyResponse encapsulates a proxy data packet. +// It includes the target, index, response and possible error returned. +type StoreManyResponse struct { + Target string + // As targets can be duplicated this is the index into the slice passed to proxy.Conn. + Index int + Resp *StoreResponse + Error error +} + +// StoreOneMany provides the same API as Store but sends the same request to N destinations at once. +// N can be a single destination. +// +// NOTE: The returned channel must be read until it closes in order to avoid leaking goroutines. +func (c *mpaClientProxy) StoreOneMany(ctx context.Context, in *StoreRequest, opts ...grpc.CallOption) (<-chan *StoreManyResponse, error) { + conn := c.cc.(*proxy.Conn) + ret := make(chan *StoreManyResponse) + // If this is a single case we can just use Invoke and marshal it onto the channel once and be done. + if len(conn.Targets) == 1 { + go func() { + out := &StoreManyResponse{ + Target: conn.Targets[0], + Index: 0, + Resp: &StoreResponse{}, + } + err := conn.Invoke(ctx, "/Mpa.Mpa/Store", in, out.Resp, opts...) + if err != nil { + out.Error = err + } + // Send and close. + ret <- out + close(ret) + }() + return ret, nil + } + manyRet, err := conn.InvokeOneMany(ctx, "/Mpa.Mpa/Store", in, opts...) + if err != nil { + return nil, err + } + // A goroutine to retrive untyped responses and convert them to typed ones. + go func() { + for { + typedResp := &StoreManyResponse{ + Resp: &StoreResponse{}, + } + + resp, ok := <-manyRet + if !ok { + // All done so we can shut down. + close(ret) + return + } + typedResp.Target = resp.Target + typedResp.Index = resp.Index + typedResp.Error = resp.Error + if resp.Error == nil { + if err := resp.Resp.UnmarshalTo(typedResp.Resp); err != nil { + typedResp.Error = fmt.Errorf("can't decode any response - %v. Original Error - %v", err, resp.Error) + } + } + ret <- typedResp + } + }() + + return ret, nil +} + +// ApproveManyResponse encapsulates a proxy data packet. +// It includes the target, index, response and possible error returned. +type ApproveManyResponse struct { + Target string + // As targets can be duplicated this is the index into the slice passed to proxy.Conn. + Index int + Resp *ApproveResponse + Error error +} + +// ApproveOneMany provides the same API as Approve but sends the same request to N destinations at once. +// N can be a single destination. +// +// NOTE: The returned channel must be read until it closes in order to avoid leaking goroutines. +func (c *mpaClientProxy) ApproveOneMany(ctx context.Context, in *ApproveRequest, opts ...grpc.CallOption) (<-chan *ApproveManyResponse, error) { + conn := c.cc.(*proxy.Conn) + ret := make(chan *ApproveManyResponse) + // If this is a single case we can just use Invoke and marshal it onto the channel once and be done. + if len(conn.Targets) == 1 { + go func() { + out := &ApproveManyResponse{ + Target: conn.Targets[0], + Index: 0, + Resp: &ApproveResponse{}, + } + err := conn.Invoke(ctx, "/Mpa.Mpa/Approve", in, out.Resp, opts...) + if err != nil { + out.Error = err + } + // Send and close. + ret <- out + close(ret) + }() + return ret, nil + } + manyRet, err := conn.InvokeOneMany(ctx, "/Mpa.Mpa/Approve", in, opts...) + if err != nil { + return nil, err + } + // A goroutine to retrive untyped responses and convert them to typed ones. + go func() { + for { + typedResp := &ApproveManyResponse{ + Resp: &ApproveResponse{}, + } + + resp, ok := <-manyRet + if !ok { + // All done so we can shut down. + close(ret) + return + } + typedResp.Target = resp.Target + typedResp.Index = resp.Index + typedResp.Error = resp.Error + if resp.Error == nil { + if err := resp.Resp.UnmarshalTo(typedResp.Resp); err != nil { + typedResp.Error = fmt.Errorf("can't decode any response - %v. Original Error - %v", err, resp.Error) + } + } + ret <- typedResp + } + }() + + return ret, nil +} + +// WaitForApprovalManyResponse encapsulates a proxy data packet. +// It includes the target, index, response and possible error returned. +type WaitForApprovalManyResponse struct { + Target string + // As targets can be duplicated this is the index into the slice passed to proxy.Conn. + Index int + Resp *WaitForApprovalResponse + Error error +} + +// WaitForApprovalOneMany provides the same API as WaitForApproval but sends the same request to N destinations at once. +// N can be a single destination. +// +// NOTE: The returned channel must be read until it closes in order to avoid leaking goroutines. +func (c *mpaClientProxy) WaitForApprovalOneMany(ctx context.Context, in *WaitForApprovalRequest, opts ...grpc.CallOption) (<-chan *WaitForApprovalManyResponse, error) { + conn := c.cc.(*proxy.Conn) + ret := make(chan *WaitForApprovalManyResponse) + // If this is a single case we can just use Invoke and marshal it onto the channel once and be done. + if len(conn.Targets) == 1 { + go func() { + out := &WaitForApprovalManyResponse{ + Target: conn.Targets[0], + Index: 0, + Resp: &WaitForApprovalResponse{}, + } + err := conn.Invoke(ctx, "/Mpa.Mpa/WaitForApproval", in, out.Resp, opts...) + if err != nil { + out.Error = err + } + // Send and close. + ret <- out + close(ret) + }() + return ret, nil + } + manyRet, err := conn.InvokeOneMany(ctx, "/Mpa.Mpa/WaitForApproval", in, opts...) + if err != nil { + return nil, err + } + // A goroutine to retrive untyped responses and convert them to typed ones. + go func() { + for { + typedResp := &WaitForApprovalManyResponse{ + Resp: &WaitForApprovalResponse{}, + } + + resp, ok := <-manyRet + if !ok { + // All done so we can shut down. + close(ret) + return + } + typedResp.Target = resp.Target + typedResp.Index = resp.Index + typedResp.Error = resp.Error + if resp.Error == nil { + if err := resp.Resp.UnmarshalTo(typedResp.Resp); err != nil { + typedResp.Error = fmt.Errorf("can't decode any response - %v. Original Error - %v", err, resp.Error) + } + } + ret <- typedResp + } + }() + + return ret, nil +} + +// ListManyResponse encapsulates a proxy data packet. +// It includes the target, index, response and possible error returned. +type ListManyResponse struct { + Target string + // As targets can be duplicated this is the index into the slice passed to proxy.Conn. + Index int + Resp *ListResponse + Error error +} + +// ListOneMany provides the same API as List but sends the same request to N destinations at once. +// N can be a single destination. +// +// NOTE: The returned channel must be read until it closes in order to avoid leaking goroutines. +func (c *mpaClientProxy) ListOneMany(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (<-chan *ListManyResponse, error) { + conn := c.cc.(*proxy.Conn) + ret := make(chan *ListManyResponse) + // If this is a single case we can just use Invoke and marshal it onto the channel once and be done. + if len(conn.Targets) == 1 { + go func() { + out := &ListManyResponse{ + Target: conn.Targets[0], + Index: 0, + Resp: &ListResponse{}, + } + err := conn.Invoke(ctx, "/Mpa.Mpa/List", in, out.Resp, opts...) + if err != nil { + out.Error = err + } + // Send and close. + ret <- out + close(ret) + }() + return ret, nil + } + manyRet, err := conn.InvokeOneMany(ctx, "/Mpa.Mpa/List", in, opts...) + if err != nil { + return nil, err + } + // A goroutine to retrive untyped responses and convert them to typed ones. + go func() { + for { + typedResp := &ListManyResponse{ + Resp: &ListResponse{}, + } + + resp, ok := <-manyRet + if !ok { + // All done so we can shut down. + close(ret) + return + } + typedResp.Target = resp.Target + typedResp.Index = resp.Index + typedResp.Error = resp.Error + if resp.Error == nil { + if err := resp.Resp.UnmarshalTo(typedResp.Resp); err != nil { + typedResp.Error = fmt.Errorf("can't decode any response - %v. Original Error - %v", err, resp.Error) + } + } + ret <- typedResp + } + }() + + return ret, nil +} + +// GetManyResponse encapsulates a proxy data packet. +// It includes the target, index, response and possible error returned. +type GetManyResponse struct { + Target string + // As targets can be duplicated this is the index into the slice passed to proxy.Conn. + Index int + Resp *GetResponse + Error error +} + +// GetOneMany provides the same API as Get but sends the same request to N destinations at once. +// N can be a single destination. +// +// NOTE: The returned channel must be read until it closes in order to avoid leaking goroutines. +func (c *mpaClientProxy) GetOneMany(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (<-chan *GetManyResponse, error) { + conn := c.cc.(*proxy.Conn) + ret := make(chan *GetManyResponse) + // If this is a single case we can just use Invoke and marshal it onto the channel once and be done. + if len(conn.Targets) == 1 { + go func() { + out := &GetManyResponse{ + Target: conn.Targets[0], + Index: 0, + Resp: &GetResponse{}, + } + err := conn.Invoke(ctx, "/Mpa.Mpa/Get", in, out.Resp, opts...) + if err != nil { + out.Error = err + } + // Send and close. + ret <- out + close(ret) + }() + return ret, nil + } + manyRet, err := conn.InvokeOneMany(ctx, "/Mpa.Mpa/Get", in, opts...) + if err != nil { + return nil, err + } + // A goroutine to retrive untyped responses and convert them to typed ones. + go func() { + for { + typedResp := &GetManyResponse{ + Resp: &GetResponse{}, + } + + resp, ok := <-manyRet + if !ok { + // All done so we can shut down. + close(ret) + return + } + typedResp.Target = resp.Target + typedResp.Index = resp.Index + typedResp.Error = resp.Error + if resp.Error == nil { + if err := resp.Resp.UnmarshalTo(typedResp.Resp); err != nil { + typedResp.Error = fmt.Errorf("can't decode any response - %v. Original Error - %v", err, resp.Error) + } + } + ret <- typedResp + } + }() + + return ret, nil +} + +// ClearManyResponse encapsulates a proxy data packet. +// It includes the target, index, response and possible error returned. +type ClearManyResponse struct { + Target string + // As targets can be duplicated this is the index into the slice passed to proxy.Conn. + Index int + Resp *ClearResponse + Error error +} + +// ClearOneMany provides the same API as Clear but sends the same request to N destinations at once. +// N can be a single destination. +// +// NOTE: The returned channel must be read until it closes in order to avoid leaking goroutines. +func (c *mpaClientProxy) ClearOneMany(ctx context.Context, in *ClearRequest, opts ...grpc.CallOption) (<-chan *ClearManyResponse, error) { + conn := c.cc.(*proxy.Conn) + ret := make(chan *ClearManyResponse) + // If this is a single case we can just use Invoke and marshal it onto the channel once and be done. + if len(conn.Targets) == 1 { + go func() { + out := &ClearManyResponse{ + Target: conn.Targets[0], + Index: 0, + Resp: &ClearResponse{}, + } + err := conn.Invoke(ctx, "/Mpa.Mpa/Clear", in, out.Resp, opts...) + if err != nil { + out.Error = err + } + // Send and close. + ret <- out + close(ret) + }() + return ret, nil + } + manyRet, err := conn.InvokeOneMany(ctx, "/Mpa.Mpa/Clear", in, opts...) + if err != nil { + return nil, err + } + // A goroutine to retrive untyped responses and convert them to typed ones. + go func() { + for { + typedResp := &ClearManyResponse{ + Resp: &ClearResponse{}, + } + + resp, ok := <-manyRet + if !ok { + // All done so we can shut down. + close(ret) + return + } + typedResp.Target = resp.Target + typedResp.Index = resp.Index + typedResp.Error = resp.Error + if resp.Error == nil { + if err := resp.Resp.UnmarshalTo(typedResp.Resp); err != nil { + typedResp.Error = fmt.Errorf("can't decode any response - %v. Original Error - %v", err, resp.Error) + } + } + ret <- typedResp + } + }() + + return ret, nil +} From be9144ea7491670c9ae48d007be5862f17948da4 Mon Sep 17 00:00:00 2001 From: Steven Rhodes Date: Wed, 25 Oct 2023 09:52:37 -0700 Subject: [PATCH 4/4] Add a hook for populating the principal in rpcauth input (#351) Many systems have the concept of an entity having an id and a set of groups, so the rpcauth input represents that in a first-class way. Until now, there was no method in this repo to populate the input. For multi-party-authentication, id and group will be an integral part so we'd benefit from having some way to represent them in tests for sansshell. PeerPrincipalFromCertHook populates principal based on common name and organizational units in a cert. This is meant much more as an example of how principal can be populated and not as an endorsement of how it should be populated. I've regenerated certs to have both fields. The upstream project we were using for generating certs has been deleted (a cached version lives on at https://pkg.go.dev/github.com/meterup/generate-cert) so I've switched to generating certs with openssl. This always feels a bit crufty to do but it has the benefit of using an extremely common tool that doesn't hide any of the details. Part of https://github.com/Snowflake-Labs/sansshell/issues/346 Co-authored-by: Edbert Linardi --- README.md | 66 +++++++++++++++++++---------------- auth/mtls/mtls_test.go | 57 +++++++++++++++++++++++++----- auth/mtls/testdata/README.md | 63 +++++++++++++++++++++++++++++++++ auth/mtls/testdata/client.pem | 22 ++++++------ auth/mtls/testdata/leaf.pem | 23 ++++++------ auth/mtls/testdata/root.pem | 16 ++++----- auth/opa/rpcauth/hooks.go | 16 +++++++++ cmd/proxy-server/main.go | 1 + cmd/sansshell-server/main.go | 1 + 9 files changed, 196 insertions(+), 69 deletions(-) create mode 100644 auth/mtls/testdata/README.md diff --git a/README.md b/README.md index ce504178..6ddc5a07 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A non-interactive daemon for host management ```mermaid flowchart LR; - + subgraph sanssh ["sansshell client (sanssh)"] cli; client; @@ -55,11 +55,12 @@ audited in advance or after the fact, and is ideally deterministic (for a given state of the local machine). sanssh is a simple CLI with a friendly API for dumping debugging state and -interacting with a remote machine. It also includes a set of convenient but +interacting with a remote machine. It also includes a set of convenient but perhaps-less-friendly subcommands to address the raw SansShell API endpoints. # Getting Started -How to set up, build and run locally for testing. All commands are relative to + +How to set up, build and run locally for testing. All commands are relative to the project root directory. Building SansShell requires a recent version of Go (check the go.mod file for @@ -74,6 +75,7 @@ $ cp -r auth/mtls/testdata ~/.sansshell ``` Then you can build and run the server, in separate terminal windows: + ``` $ go run ./cmd/sansshell-server $ go run ./cmd/sanssh --targets=localhost file read /etc/hosts @@ -96,6 +98,7 @@ buffer compiler (`protoc`) (version 3 or above) as well as the protoc plugins for Go and Go-GRPC On MacOS, the protocol buffer can be installed via homebrew using + ``` brew install protobuf ``` @@ -105,7 +108,7 @@ directly installing a release version from the [protocol buffers github][1] ## Environment setup : protoc plugins. -On any platform, Once protoc has been installed, you can install the required +On any platform, once protoc has been installed, you can install the required code generation plugins using `go install`. ``` @@ -126,17 +129,11 @@ $ go generate tools.go ## Creating your own certificates -As an alternative to copying auth/mtls/testdata, you can create your onwn example mTLS certs: -``` -$ go install github.com/meterup/generate-cert@latest -$ mkdir -m 0700 certs -$ cd certs -$ $(go env GOPATH)/bin/generate-cert --host=localhost,127.0.0.1,::1 -$ cd ../ -$ ln -s $(pwd)/certs ~/.sansshell -``` +As an alternative to copying auth/mtls/testdata, you can create your own example mTLS certs. See the +[mtls testdata readme](/auth/mtls/testdata/README.md) for steps. ## Debugging + Reflection is included in the RPC servers (proxy and sansshell-server) allowing for the use of [grpc_cli](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md). @@ -152,25 +149,29 @@ $ GRPC_DEFAULT_SSL_ROOTS_FILE_PATH=$HOME/.sansshell/root.pem grpc_cli \ NOTE: This connects to the proxy. Change to 50042 if you want to connect to the sansshell-server. # A tour of the codebase + SansShell is composed of 5 primary concepts: - 1. A series of services, which live in the `services/` directory. - 1. A server which wraps these services into a local host agent. - 1. A proxy server which can be used as an entry point to processing sansshell - RPCs by validating policy and then doing fanout to 1..N actual - sansshell servers. This can be done as a one to many RPC where - a single incoming RPC is replicated to N backend hosts in one RPC call. - 1. A reference server binary, which includes all of the services. - 1. A CLI, which serves as the reference implementation of how to use the - services via the agent. + +1. A series of services, which live in the `services/` directory. +1. A server which wraps these services into a local host agent. +1. A proxy server which can be used as an entry point to processing sansshell + RPCs by validating policy and then doing fanout to 1..N actual + sansshell servers. This can be done as a one to many RPC where + a single incoming RPC is replicated to N backend hosts in one RPC call. +1. A reference server binary, which includes all of the services. +1. A CLI, which serves as the reference implementation of how to use the + services via the agent. ## Services + Services implement at least one gRPC API endpoint, and expose it by calling -`RegisterSansShellService` from `init()`. The goal is to allow custom +`RegisterSansShellService` from `init()`. The goal is to allow custom implementations of the SansShell Server to easily import services they wish to use, and have zero overhead or risk from services they do not import at compile time. ### List of available Services: + 1. Ansible: Run a local ansible playbook and return output 1. Execute: Execute a command 1. HealthCheck @@ -180,29 +181,32 @@ time. 1. Process operations: List, Get stacks (native or Java), Get dumps (core or Java heap) 1. Service operations: List, Status, Start/stop/restart - TODO: Document service/.../client expectations. ## The Server class + Most of the logic of instantiating a local SansShell server lives in the -`server` directory. This instantiates a gRPC server, registers the imported +`server` directory. This instantiates a gRPC server, registers the imported services with that server, and constraints them with the supplied OPA policy. ## The reference Proxy Server binary + There is a reference implementation of a SansShell Proxy Server in `cmd/proxy-server`, which should be suitable as-written for many use cases. It's intentionally kept relatively short, so that it can be copied to another repository and customized by adjusting only the imported services. ## The reference Server binary + There is a reference implementation of a SansShell Server in `cmd/sansshell-server`, which should be suitable as-written for many use cases. It's intentionally kept relatively short, so that it can be copied to another repository and customized by adjusting only the imported services. ## The reference CLI client + There is a reference implementation of a SansShell CLI Client in -`cmd/sanssh`. It provides raw access to each gRPC endpoint, as well +`cmd/sanssh`. It provides raw access to each gRPC endpoint, as well as a way to implement "convenience" commands which chain together a series of actions. @@ -219,11 +223,12 @@ complete -C /path/to/sanssh -o dirnames sanssh ``` # Extending SansShell -SansShell is built on a principle of "Don't pay for what you don't use". This + +SansShell is built on a principle of "Don't pay for what you don't use". This is advantageous in both minimizing the resources of SansShell server (binary size, memory footprint, etc) as well as reducing the security risk of running -it. To accomplish that, all of the SansShell services are independent modules, -which can be optionally included at build time. The reference server and +it. To accomplish that, all of the SansShell services are independent modules, +which can be optionally included at build time. The reference server and client provide access to the features of all of the built-in modules, and come with exposure to all of their potential bugs and bloat. @@ -236,6 +241,7 @@ That same extensibility makes it easy to add additional functionality by implementing your own module. To quickly rebuild all binaries you can run: + ``` $ go generate build.go ``` @@ -244,7 +250,7 @@ and they will be placed in a bin directory (which is ignored by git). TODO: Add example client and server, building in different SansShell modules. -If you need to edit a proto file (to augment an existing service or +If you need to edit a proto file (to augment an existing service or create a new one) you'll need to generate proto outputs. ``` diff --git a/auth/mtls/mtls_test.go b/auth/mtls/mtls_test.go index 04efcdba..5b309daf 100644 --- a/auth/mtls/mtls_test.go +++ b/auth/mtls/mtls_test.go @@ -62,7 +62,7 @@ allow { input.peer.net.network = "something else" } ` - allowPeerSerialPolicy = ` + allowPeerCommonName = ` package sansshell.authz default allow = false @@ -70,10 +70,10 @@ default allow = false allow { input.type = "google.protobuf.Empty" input.method = "/HealthCheck.HealthCheck/Ok" - input.peer.cert.subject.SerialNumber = "18469474828185269013890292403630281187" + input.peer.cert.subject.CommonName = "sanssh" } ` - denyPeerSerialPolicy = ` + denyPeerCommonName = ` package sansshell.authz default allow = false @@ -81,7 +81,33 @@ default allow = false allow { input.type = "google.protobuf.Empty" input.method = "/HealthCheck.HealthCheck/Ok" - input.peer.cert.subject.SerialNumber = "12345" + input.peer.cert.subject.CommonName = "not-sanssh" +} +` + + allowPeerPrincipal = ` +package sansshell.authz + +default allow = false + +allow { + input.type = "google.protobuf.Empty" + input.method = "/HealthCheck.HealthCheck/Ok" + input.peer.principal.id = "sanssh" + input.peer.principal.groups[_] = "group2" +} +` + + denyPeerPrincipal = ` +package sansshell.authz + +default allow = false + +allow { + input.type = "google.protobuf.Empty" + input.method = "/HealthCheck.HealthCheck/Ok" + input.peer.principal.id = "sanssh" + input.peer.principal.groups[_]= "group3" } ` ) @@ -144,6 +170,7 @@ func serverWithPolicy(t *testing.T, policy string) (*bufconn.Listener, *grpc.Ser server.WithCredentials(creds), server.WithPolicy(policy), server.WithAuthzHook(rpcauth.HostNetHook(lis.Addr())), + server.WithAuthzHook(rpcauth.PeerPrincipalFromCertHook()), ) testutil.FatalOnErr("Could not build server", err, t) listening := make(chan struct{}) @@ -302,13 +329,23 @@ func TestHealthCheck(t *testing.T) { err: "OPA policy does not permit this request", }, { - name: "allowed peer by subject serial", - policy: allowPeerSerialPolicy, + name: "allowed peer by subject common name", + policy: allowPeerCommonName, + err: "", + }, + { + name: "denied peer by subject common name", + policy: denyPeerCommonName, + err: "OPA policy does not permit this request", + }, + { + name: "allowed peer by principal parsed from cert", + policy: allowPeerPrincipal, err: "", }, { - name: "denied peer by subject serial", - policy: denyPeerSerialPolicy, + name: "denied peer by principal parsed from cert", + policy: denyPeerPrincipal, err: "OPA policy does not permit this request", }, } { @@ -331,6 +368,10 @@ func TestHealthCheck(t *testing.T) { } return } + if err == nil && tc.err != "" { + t.Errorf("Passed, but expected error: %v", tc.err) + + } t.Logf("Response: %+v", resp) s.GracefulStop() }) diff --git a/auth/mtls/testdata/README.md b/auth/mtls/testdata/README.md new file mode 100644 index 00000000..5c801dcd --- /dev/null +++ b/auth/mtls/testdata/README.md @@ -0,0 +1,63 @@ +# mtls testdata + +This directory contains some test certificates. + +If you need to regenerate them, here's some helpful commands. Openssl + +## Generating private keys + +These commands will generate new private keys. + +```bash +openssl genrsa -out client.key +openssl genrsa -out leaf.key +openssl genrsa -out root.key +``` + +## Generating certificates + +These commands will generate new certificates. We need to generate certificate signing requests as part of generating certificates, but we can delete them afterwards. + +```bash +# CA cert +openssl req -new -key root.key -out root.csr -subj "/O=Acme Co" +openssl x509 -req -days 30000 -in root.csr -signkey root.key -out root.pem + +# Cert for the client +openssl req -new -key client.key -out client.csr -subj "/O=Acme Co/OU=group1/OU=group2/CN=sanssh" +openssl x509 -req -days 3000 -in client.csr -CA root.pem -CAkey root.key -out client.pem -extensions req_ext -extfile /dev/stdin <