Compare commits

...

11 Commits

Author SHA1 Message Date
Justin Dalrymple bc444dd7d4
Merge 84ac164557 into a7134c1c90 2024-02-21 00:21:59 -07:00
Ignacio Gómez a7134c1c90
Merge pull request #317 from mbv/bump-mosquitto-version
Bump mosquitto versions to 1.6.15 & 2.0.18
2024-02-11 13:09:17 -03:00
Ignacio Gómez aaf33cdd8f
Merge pull request #318 from iegomez/upgrade-net-crypto-grpc
Update net, crypto and grpc modules.
2024-02-11 13:08:57 -03:00
Ignacio Gómez 73c5e40843 Update net, crypto and grpc modules. 2024-02-11 13:00:46 -03:00
Konstantin Terekhov 0ea881ca9a Bump mosquitto versions to 1.6.15 & 2.0.18 2024-02-11 16:44:40 +01:00
Ignacio Gómez 8ad80ee6b0
Merge pull request #316 from iegomez/actions-run-tests-on-fork-pr
Run tests on PRs.
2024-02-11 11:53:31 -03:00
Ignacio Gómez f5b35cbde0 Run tests on PRs. 2024-02-11 11:45:37 -03:00
Ignacio Gómez 5b4a46e114
Merge pull request #315 from mbv/fix-tests-bookworm
Fixed test after debian-stable is bookworm
2024-02-11 11:43:54 -03:00
Konstantin Terekhov 72e437cafe Fixed test after debian-stable is bookworm
* updated mongo to 7.0, repo mongo contains only 7.0 for debian bookworm
 * installed redis 6.*, redis 7.* is not supported by `go-redis/redis/v8`
2024-02-11 11:37:50 +01:00
Justin Dalrymple 84ac164557
Update jwt_local.go 2023-05-03 21:33:32 -04:00
Justin Dalrymple e90423f7fd
Update jwt_local.go
Adding prelim updates for mongo support in JWT backend
2023-05-03 21:27:34 -04:00
8 changed files with 105 additions and 78 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
.github

View File

@ -6,10 +6,10 @@ on:
release:
types: [published]
env:
MOSQUITTO_VERSION_1: 1.6.14
MOSQUITTO_VERSION_2: 2.0.15
MOSQUITTO_VERSION_1: 1.6.15
MOSQUITTO_VERSION_2: 2.0.18
MOSQUITTO_VERSION_SUFFIX: -mosquitto_
DOCKERFILE_MOSQUITTO_VERSION: 1.6.14
DOCKERFILE_MOSQUITTO_VERSION: 2.0.18
DOCKERHUB_REPO: mosquitto-go-auth
jobs:
mosq_1:

View File

@ -1,10 +1,13 @@
name: Test
on:
push
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
MOSQUITTO_VERSION_1: 1.6.14
MOSQUITTO_VERSION_2: 2.0.15
MOSQUITTO_VERSION_1: 1.6.15
MOSQUITTO_VERSION_2: 2.0.18
DOCKERFILE_MOSQUITTO_VERSION: 1.6.14
DOCKERHUB_REPO: mosquitto-go-auth
jobs:
@ -12,17 +15,13 @@ jobs:
name: Test with Mosquitto version 1.x
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Set Mosquitto version
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set Mosquitto version
run: sed -i 's/ARG MOSQUITTO_VERSION=${{ env.DOCKERFILE_MOSQUITTO_VERSION }}/ARG MOSQUITTO_VERSION=${{ env.MOSQUITTO_VERSION_1 }}/' Dockerfile.runtest
-
name: Test
- name: Test
run: |
docker build -t mosquitto-go-auth.test -f Dockerfile.runtest .
docker run --rm mosquitto-go-auth.test ./run-test-in-docker.sh
@ -30,20 +29,13 @@ jobs:
name: Test with Mosquitto version 2.x
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set Mosquitto version
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set Mosquitto version
run: sed -i 's/ARG MOSQUITTO_VERSION=${{ env.DOCKERFILE_MOSQUITTO_VERSION }}/ARG MOSQUITTO_VERSION=${{ env.MOSQUITTO_VERSION_2 }}/' Dockerfile.runtest
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Test
- name: Test
run: |
docker build -t mosquitto-go-auth.test -f Dockerfile.runtest .
docker run --rm mosquitto-go-auth.test ./run-test-in-docker.sh

View File

@ -1,6 +1,6 @@
# Define Mosquitto version, see also .github/workflows/build_and_push_docker_images.yml for
# the automatically built images
ARG MOSQUITTO_VERSION=2.0.15
ARG MOSQUITTO_VERSION=2.0.18
# Define libwebsocket version
ARG LWS_VERSION=4.2.2

View File

@ -18,7 +18,7 @@ ENV MOSQUITTO_GO_AUTH_TEST_RUNNING_IN_A_CONTAINER=true
WORKDIR /app
#Get mosquitto build dependencies.
RUN apt-get update && apt-get install -y libc-ares2 libc-ares-dev cmake libssl-dev uuid uuid-dev wget build-essential git libcjson-dev
RUN apt-get update && apt-get install -y libc-ares2 libc-ares-dev cmake libssl-dev uuid uuid-dev wget build-essential git libcjson-dev gnupg lsb-release
# Get libwebsocket. Debian's libwebsockets is too old for Mosquitto version > 2.x so it gets built from source.
RUN set -ex; \
@ -67,16 +67,22 @@ RUN export PATH=$PATH:/usr/local/go/bin && export CGO_CFLAGS="-I/usr/local/inclu
## Everything above, is the same as Dockerfile
RUN apt-get update && apt-get install --no-install-recommends -y mariadb-server postgresql redis-server sudo
RUN apt-get update && apt-get install --no-install-recommends -y mariadb-server postgresql sudo
RUN wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add - && \
echo "deb http://repo.mongodb.org/apt/debian buster/mongodb-org/4.4 main" > /etc/apt/sources.list.d/mongodb-org-4.4.list && \
RUN wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor && \
echo "deb [signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg] https://repo.mongodb.org/apt/debian $(lsb_release -cs)/mongodb-org/7.0 main" | tee /etc/apt/sources.list.d/mongodb-org-7.0.list && \
apt-get update && \
# starting with MongoDB 4.3, the postinst for server includes "systemctl daemon-reload" (and we don't have "systemctl")
# starting with MongoDB 7.0, the postinst for server includes "systemctl daemon-reload" (and we don't have "systemctl")
ln -s /bin/true /usr/bin/systemctl && \
apt-get install -y mongodb-org && \
rm -f /usr/bin/systemctl
#Install redis 6.2
RUN wget -qO - https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/redis.list && \
apt-get update && \
apt-get install -y redis-tools=6:6.2.* redis-server=6:6.2.*
# Install CFSSL to generate test certificates required for tests
RUN export PATH=$PATH:/usr/local/go/bin && go install github.com/cloudflare/cfssl/cmd/cfssl@v1.6.2 && cp ~/go/bin/cfssl /usr/local/bin
RUN export PATH=$PATH:/usr/local/go/bin && go install github.com/cloudflare/cfssl/cmd/cfssljson@v1.6.2 && cp ~/go/bin/cfssljson /usr/local/bin

View File

@ -3,7 +3,8 @@ package backends
import (
"database/sql"
"strings"
"context"
"github.com/iegomez/mosquitto-go-auth/hashing"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@ -13,6 +14,7 @@ type localJWTChecker struct {
db string
postgres Postgres
mysql Mysql
mongo Mongo
userQuery string
hasher hashing.HashComparer
options tokenOptions
@ -21,10 +23,11 @@ type localJWTChecker struct {
const (
mysqlDB = "mysql"
postgresDB = "postgres"
mongoDB = "mongo"
)
// NewLocalJWTChecker initializes a checker with a local DB.
func NewLocalJWTChecker(authOpts map[string]string, logLevel log.Level, hasher hashing.HashComparer, options tokenOptions) (jwtChecker, error) {
func NewLocalJWTChecker(authOpts map[string]string, logLevel log.Level, hasher hashing.HashComparer, options tokenOptions) (JWTChecker, error) {
checker := &localJWTChecker{
hasher: hasher,
db: postgresDB,
@ -35,7 +38,7 @@ func NewLocalJWTChecker(authOpts map[string]string, logLevel log.Level, hasher h
localOk := true
if options.secret == "" {
return nil, errors.New("JWT backend error: missing jwt secret")
return nil, errors.New("JWT backend error: missing JWT secret")
}
if db, ok := authOpts["jwt_db"]; ok {
@ -59,21 +62,28 @@ func NewLocalJWTChecker(authOpts map[string]string, logLevel log.Level, hasher h
if checker.db == mysqlDB {
mysql, err := NewMysql(dbAuthOpts, logLevel, hasher)
if err != nil {
return nil, errors.Errorf("JWT backend error: couldn't create mysql connector for local jwt: %s", err)
return nil, errors.Errorf("JWT backend error: couldn't create mysql connector for local JWT: %s", err)
}
checker.mysql = mysql
} else if checker.db == mongoDB {
mongodb, err := NewMongo(dbAuthOpts, logLevel, hasher)
return checker, nil
if err != nil {
return nil, errors.Errorf("JWT backend error: couldn't create mysql connector for local JWT: %s", err)
}
checker.mongo = mongodb
} else {
postgres, err := NewPostgres(dbAuthOpts, logLevel, hasher)
checker.postgres = postgres
}
postgres, err := NewPostgres(dbAuthOpts, logLevel, hasher)
if err != nil {
return nil, errors.Errorf("JWT backend error: couldn't create postgres connector for local jwt: %s", err)
return nil, errors.Errorf("JWT backend error: couldn't create postgres connector for local JWT: %s", err)
}
checker.postgres = postgres
return checker, nil
}
@ -81,7 +91,7 @@ func (o *localJWTChecker) GetUser(token string) (bool, error) {
username, err := getUsernameForToken(o.options, token, o.options.skipUserExpiration)
if err != nil {
log.Printf("jwt local get user error: %s", err)
log.Printf("JWT local get user error: %s", err)
return false, err
}
@ -92,43 +102,47 @@ func (o *localJWTChecker) GetSuperuser(token string) (bool, error) {
username, err := getUsernameForToken(o.options, token, o.options.skipUserExpiration)
if err != nil {
log.Printf("jwt local get superuser error: %s", err)
log.Printf("JWT local get superuser error: %s", err)
return false, err
}
if o.db == mysqlDB {
return o.mysql.GetSuperuser(username)
} else if o.db == mongoDB {
return o.mongo.GetSuperuser(username)
} else {
return o.postgres.GetSuperuser(username)
}
return o.postgres.GetSuperuser(username)
}
func (o *localJWTChecker) CheckAcl(token, topic, clientid string, acc int32) (bool, error) {
username, err := getUsernameForToken(o.options, token, o.options.skipACLExpiration)
if err != nil {
log.Printf("jwt local check acl error: %s", err)
log.Printf("JWT local check acl error: %s", err)
return false, err
}
if o.db == mysqlDB {
return o.mysql.CheckAcl(username, topic, clientid, acc)
} else if o.db == mongoDB {
return o.mongo.CheckAcl(username)
} else {
return o.postgres.CheckAcl(username)
}
return o.postgres.CheckAcl(username, topic, clientid, acc)
}
func (o *localJWTChecker) Halt() {
if o.postgres != (Postgres{}) && o.postgres.DB != nil {
err := o.postgres.DB.Close()
if err != nil {
log.Errorf("JWT cleanup error: %s", err)
}
} else if o.mysql != (Mysql{}) && o.mysql.DB != nil {
err := o.mysql.DB.Close()
if err != nil {
log.Errorf("JWT cleanup error: %s", err)
}
} else if o.mongo != (Mongo{}) && o.mongo.Conn != nil {
err := o.mongo.Conn.Disconnect(context.TODO())
}
if err != nil {
log.Errorf("JWT cleanup error: %s", err)
}
}
@ -137,25 +151,36 @@ func (o *localJWTChecker) getLocalUser(username string) (bool, error) {
return false, nil
}
var count sql.NullInt64
var err error
var sqlCount sql.NullInt64
var count Int64
var valid boolean
if o.db == mysqlDB {
err = o.mysql.DB.Get(&count, o.userQuery, username)
valid = sqlCount.Valid
count = sqlCount.Int64
} else if o.db == mongoDB {
var uc := o.mongo.Conn.Database(o.mongo.DBName).Collection(o.mongo.UsersCollection)
count, err := uc.CountDocuments(context.TODO(), bson.M{"username": username})
} else {
err = o.postgres.DB.Get(&count, o.userQuery, username)
}
valid = sqlCount.Valid
count = sqlCount.Int64
}
if err != nil {
log.Debugf("local JWT get user error: %s", err)
return false, err
}
if !count.Valid {
if !valid {
log.Debugf("local JWT get user error: user %s not found", username)
return false, nil
}
if count.Int64 > 0 {
if count > 0 {
return true, nil
}
@ -165,9 +190,12 @@ func (o *localJWTChecker) getLocalUser(username string) (bool, error) {
func extractOpts(authOpts map[string]string, db string) map[string]string {
dbAuthOpts := make(map[string]string)
dbPrefix := "pg"
if db == mysqlDB {
dbPrefix = mysqlDB
} else if db == mongoDB {
dbPrefix = mongoDB
} else {
dbPrefix := "pg"
}
prefix := "jwt_" + dbPrefix

10
go.mod
View File

@ -18,8 +18,8 @@ require (
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a
github.com/stretchr/testify v1.8.1
go.mongodb.org/mongo-driver v1.11.6
golang.org/x/crypto v0.9.0
google.golang.org/grpc v1.55.0
golang.org/x/crypto v0.17.0
google.golang.org/grpc v1.56.3
)
require (
@ -37,10 +37,10 @@ require (
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect

20
go.sum
View File

@ -135,8 +135,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -156,8 +156,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
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=
@ -180,8 +180,8 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -189,8 +189,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -218,8 +218,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=