From f3fe792d885fe44304f437b4f578c6e276723e2b Mon Sep 17 00:00:00 2001 From: Gordon Bleux <33967640+UiP9AV6Y@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:19:41 +0100 Subject: [PATCH] add RPC-based AUTHN and AUTHZ support this change adds both a new authenticator as well as an authorizer implementation using RPC communication. the advantage over "External Authentication" is that those new plugins are only spawned once upon server start instead of with each auth request. this reduces resource problems in high request scenarios where the process IDs the system has available dwindle due to too many processes being created. the advantage over "Plugin Authentication" is that it works on all platforms. example plugins for each aspect (AUTHN/AUTHZ) have been implemented to demonstrate the feature as well as a simple test suite to verify the functionality. relates to #337 --- .github/workflows/go_test.yml | 30 +++---- auth_server/authn/ldap_auth.go | 14 +-- auth_server/authn/mongo_auth.go | 3 +- auth_server/authn/rpc_auth.go | 115 ++++++++++++++++++++++++ auth_server/authn/tokendb_gcs.go | 4 +- auth_server/authn/tokendb_level.go | 4 +- auth_server/authn/tokendb_redis.go | 5 +- auth_server/authn/xorm_sqlite_authn.go | 3 +- auth_server/authz/acl_xorm_sqlite.go | 3 +- auth_server/authz/rpc_auth.go | 120 +++++++++++++++++++++++++ auth_server/gen_version.go | 3 +- auth_server/go.mod | 36 +++++--- auth_server/go.sum | 46 ++++++++++ auth_server/plugin/authn/contract.go | 35 ++++++++ auth_server/plugin/authn/doc.go | 18 ++++ auth_server/plugin/authn/handshake.go | 38 ++++++++ auth_server/plugin/authn/rpc.go | 86 ++++++++++++++++++ auth_server/plugin/authz/contract.go | 42 +++++++++ auth_server/plugin/authz/doc.go | 18 ++++ auth_server/plugin/authz/handshake.go | 38 ++++++++ auth_server/plugin/authz/rpc.go | 86 ++++++++++++++++++ auth_server/plugin/doc.go | 19 ++++ auth_server/plugin/errors.go | 27 ++++++ auth_server/plugin/go.mod | 3 + auth_server/server/config.go | 16 +++- auth_server/server/server.go | 16 +++- examples/reference.yml | 87 ++++++++++-------- examples/rpc/.gitignore | 1 + examples/rpc/Makefile | 49 ++++++++++ examples/rpc/auth_server.yml | 19 ++++ examples/rpc/authn/authn.go | 64 +++++++++++++ examples/rpc/authn/doc.go | 18 ++++ examples/rpc/authn/go.mod | 26 ++++++ examples/rpc/authn/go.sum | 54 +++++++++++ examples/rpc/authn/main.go | 54 +++++++++++ examples/rpc/authz/authz.go | 59 ++++++++++++ examples/rpc/authz/doc.go | 18 ++++ examples/rpc/authz/go.mod | 26 ++++++ examples/rpc/authz/go.sum | 54 +++++++++++ examples/rpc/authz/main.go | 54 +++++++++++ examples/rpc/script/e2e.sh | 53 +++++++++++ 41 files changed, 1377 insertions(+), 87 deletions(-) create mode 100644 auth_server/authn/rpc_auth.go create mode 100644 auth_server/authz/rpc_auth.go create mode 100644 auth_server/plugin/authn/contract.go create mode 100644 auth_server/plugin/authn/doc.go create mode 100644 auth_server/plugin/authn/handshake.go create mode 100644 auth_server/plugin/authn/rpc.go create mode 100644 auth_server/plugin/authz/contract.go create mode 100644 auth_server/plugin/authz/doc.go create mode 100644 auth_server/plugin/authz/handshake.go create mode 100644 auth_server/plugin/authz/rpc.go create mode 100644 auth_server/plugin/doc.go create mode 100644 auth_server/plugin/errors.go create mode 100644 auth_server/plugin/go.mod create mode 100644 examples/rpc/.gitignore create mode 100644 examples/rpc/Makefile create mode 100644 examples/rpc/auth_server.yml create mode 100644 examples/rpc/authn/authn.go create mode 100644 examples/rpc/authn/doc.go create mode 100644 examples/rpc/authn/go.mod create mode 100644 examples/rpc/authn/go.sum create mode 100644 examples/rpc/authn/main.go create mode 100644 examples/rpc/authz/authz.go create mode 100644 examples/rpc/authz/doc.go create mode 100644 examples/rpc/authz/go.mod create mode 100644 examples/rpc/authz/go.sum create mode 100644 examples/rpc/authz/main.go create mode 100755 examples/rpc/script/e2e.sh diff --git a/.github/workflows/go_test.yml b/.github/workflows/go_test.yml index 50c4821b..8bfefef3 100644 --- a/.github/workflows/go_test.yml +++ b/.github/workflows/go_test.yml @@ -4,21 +4,21 @@ jobs: test: strategy: matrix: - go-version: [1.23.x,1.24.x] + go-version: [1.24.x, 1.25.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - - name: Install Go - uses: actions/setup-go@v5 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v4 - - name: Test - run: | - cd auth_server - go test ./... - - name: Build - run: | - cd auth_server - make + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v4 + - name: Test + run: | + cd auth_server + go test ./... + - name: Build + run: | + cd auth_server + make diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go index cc837cd9..f7f85777 100644 --- a/auth_server/authn/ldap_auth.go +++ b/auth_server/authn/ldap_auth.go @@ -61,7 +61,7 @@ func NewLDAPAuth(c *LDAPAuthConfig) (*LDAPAuth, error) { }, nil } -//How to authenticate user, please refer to https://github.com/go-ldap/ldap/blob/master/example_test.go#L166 +// How to authenticate user, please refer to https://github.com/go-ldap/ldap/blob/master/example_test.go#L166 func (la *LDAPAuth) Authenticate(account string, password api.PasswordString) (bool, api.Labels, error) { if account == "" || password == "" { return false, nil, api.NoMatch @@ -160,10 +160,10 @@ func (la *LDAPAuth) bindInitialAsUser(l *ldap.Conn, account string, password api return nil } -//To prevent LDAP injection, some characters must be escaped for searching -//e.g. char '\' will be replaced by hex '\5c' -//Filter meta chars are choosen based on filter complier code -//https://github.com/go-ldap/ldap/blob/master/filter.go#L159 +// To prevent LDAP injection, some characters must be escaped for searching +// e.g. char '\' will be replaced by hex '\5c' +// Filter meta chars are choosen based on filter complier code +// https://github.com/go-ldap/ldap/blob/master/filter.go#L159 func (la *LDAPAuth) escapeAccountInput(account string) string { r := strings.NewReplacer( `\`, `\5c`, @@ -229,8 +229,8 @@ func (la *LDAPAuth) getFilter(account string) string { return filter } -//ldap search and return required attributes' value from searched entries -//default return entry's DN value if you leave attrs array empty +// ldap search and return required attributes' value from searched entries +// default return entry's DN value if you leave attrs array empty func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, map[string][]string, error) { if l == nil { return "", nil, fmt.Errorf("No ldap connection!") diff --git a/auth_server/authn/mongo_auth.go b/auth_server/authn/mongo_auth.go index db546be4..3291ac97 100644 --- a/auth_server/authn/mongo_auth.go +++ b/auth_server/authn/mongo_auth.go @@ -102,8 +102,7 @@ func (mauth *MongoAuth) authenticate(account string, password api.PasswordString var dbUserRecord authUserEntry collection := mauth.session.Database(mauth.config.MongoConfig.DialInfo.Database).Collection(mauth.config.Collection) - - filter := bson.D{{"username", account}} + filter := bson.D{{"username", account}} err := collection.FindOne(context.TODO(), filter).Decode(&dbUserRecord) // If we connect and get no results we return a NoMatch so auth can fall-through diff --git a/auth_server/authn/rpc_auth.go b/auth_server/authn/rpc_auth.go new file mode 100644 index 00000000..ccf3460d --- /dev/null +++ b/auth_server/authn/rpc_auth.go @@ -0,0 +1,115 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authn + +import ( + "fmt" + "os/exec" + + "github.com/cesanta/glog" + rpc "github.com/hashicorp/go-plugin" + + "github.com/cesanta/docker_auth/auth_server/api" + shared "github.com/cesanta/docker_auth/auth_server/plugin" + plugin "github.com/cesanta/docker_auth/auth_server/plugin/authn" +) + +type RPCAuthnConfig struct { + Command string `yaml:"command"` + Args []string `yaml:"args"` +} + +func (c *RPCAuthnConfig) Validate() error { + if c.Command == "" { + return fmt.Errorf("command is not set") + } + + if _, err := exec.LookPath(c.Command); err != nil { + return fmt.Errorf("no such command: %s: %w", c.Command, err) + } + + return nil +} + +type RPCAuthn struct { + client *rpc.Client + impl plugin.Authenticator +} + +func (c *RPCAuthn) Authenticate(username string, password api.PasswordString) (bool, api.Labels, error) { + req := &plugin.AuthenticateRequest{ + Username: username, + Password: string(password), + } + resp, err := c.impl.Authenticate(req) + switch { + case err == nil: + return true, api.Labels(resp), nil + case shared.IsError(err, shared.ErrUnauthorized): + return false, nil, nil + case shared.IsError(err, shared.ErrUnacceptable): + return false, nil, api.NoMatch + default: + return false, nil, err + } +} + +func (c *RPCAuthn) Stop() { + if c.client != nil { + c.client.Kill() + } +} + +func (c *RPCAuthn) Name() string { + return "rpc" +} + +func NewRPCAuthn(cfg *RPCAuthnConfig) (*RPCAuthn, error) { + glog.Infof("RPC authenticator: %s", cfg) + + conn := &rpc.ClientConfig{ + HandshakeConfig: plugin.Handshake, + Plugins: plugin.PluginMap, + Cmd: exec.Command(cfg.Command, cfg.Args...), + } + client := rpc.NewClient(conn) + + rpcClient, err := client.Client() + if err != nil { + client.Kill() + return nil, err + } + + raw, err := rpcClient.Dispense(plugin.PluginNetRPC) + if err != nil { + client.Kill() + return nil, err + } + + impl, ok := raw.(plugin.Authenticator) + if !ok { + client.Kill() + return nil, fmt.Errorf("no authenticator plugin provided: %T", impl) + } + + result := &RPCAuthn{ + client: client, + impl: impl, + } + + return result, nil +} diff --git a/auth_server/authn/tokendb_gcs.go b/auth_server/authn/tokendb_gcs.go index 53a0d278..bc2ccc91 100644 --- a/auth_server/authn/tokendb_gcs.go +++ b/auth_server/authn/tokendb_gcs.go @@ -51,8 +51,8 @@ func NewGCSTokenDB(options *GCSStoreConfig) (TokenDB, error) { } type gcsTokenDB struct { - gcs *storage.Client - bucket string + gcs *storage.Client + bucket string tokenHashCost int } diff --git a/auth_server/authn/tokendb_level.go b/auth_server/authn/tokendb_level.go index 66d43444..f13c71a2 100644 --- a/auth_server/authn/tokendb_level.go +++ b/auth_server/authn/tokendb_level.go @@ -37,8 +37,8 @@ const ( var ExpiredToken = errors.New("expired token") type LevelDBStoreConfig struct { - Path string `yaml:"path,omitempty"` - TokenHashCost int `yaml:"token_hash_cost,omitempty"` + Path string `yaml:"path,omitempty"` + TokenHashCost int `yaml:"token_hash_cost,omitempty"` } // TokenDB stores tokens using LevelDB diff --git a/auth_server/authn/tokendb_redis.go b/auth_server/authn/tokendb_redis.go index 39a4f10a..5288f635 100644 --- a/auth_server/authn/tokendb_redis.go +++ b/auth_server/authn/tokendb_redis.go @@ -42,7 +42,6 @@ type RedisClient interface { } // NewRedisTokenDB returns a new TokenDB structure which uses Redis as the storage backend. -// func NewRedisTokenDB(options *RedisStoreConfig) (TokenDB, error) { var client RedisClient if options.ClusterOptions != nil { @@ -58,11 +57,11 @@ func NewRedisTokenDB(options *RedisStoreConfig) (TokenDB, error) { tokenHashCost = bcrypt.DefaultCost } - return &redisTokenDB{client,tokenHashCost}, nil + return &redisTokenDB{client, tokenHashCost}, nil } type redisTokenDB struct { - client RedisClient + client RedisClient tokenHashCost int } diff --git a/auth_server/authn/xorm_sqlite_authn.go b/auth_server/authn/xorm_sqlite_authn.go index f1a39ccc..e0cd4687 100644 --- a/auth_server/authn/xorm_sqlite_authn.go +++ b/auth_server/authn/xorm_sqlite_authn.go @@ -1,4 +1,5 @@ -//+build sqlite +//go:build sqlite +// +build sqlite /* Copyright 2020 Cesanta Software Ltd. diff --git a/auth_server/authz/acl_xorm_sqlite.go b/auth_server/authz/acl_xorm_sqlite.go index cdf5b81d..a9ce34f8 100644 --- a/auth_server/authz/acl_xorm_sqlite.go +++ b/auth_server/authz/acl_xorm_sqlite.go @@ -1,4 +1,5 @@ -//+build sqlite +//go:build sqlite +// +build sqlite /* Copyright 2020 Cesanta Software Ltd. diff --git a/auth_server/authz/rpc_auth.go b/auth_server/authz/rpc_auth.go new file mode 100644 index 00000000..305bf3a0 --- /dev/null +++ b/auth_server/authz/rpc_auth.go @@ -0,0 +1,120 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authz + +import ( + "fmt" + "os/exec" + + "github.com/cesanta/glog" + rpc "github.com/hashicorp/go-plugin" + + "github.com/cesanta/docker_auth/auth_server/api" + shared "github.com/cesanta/docker_auth/auth_server/plugin" + plugin "github.com/cesanta/docker_auth/auth_server/plugin/authz" +) + +type RPCAuthzConfig struct { + Command string `yaml:"command"` + Args []string `yaml:"args"` +} + +func (c *RPCAuthzConfig) Validate() error { + if c.Command == "" { + return fmt.Errorf("command is not set") + } + + if _, err := exec.LookPath(c.Command); err != nil { + return fmt.Errorf("no such command: %s: %w", c.Command, err) + } + + return nil +} + +type RPCAuthz struct { + client *rpc.Client + impl plugin.Authorizer +} + +func (c *RPCAuthz) Authorize(ai *api.AuthRequestInfo) ([]string, error) { + req := &plugin.AuthorizeRequest{ + Account: ai.Account, + Type: ai.Type, + Name: ai.Name, + Service: ai.Service, + IP: ai.IP, + Actions: ai.Actions, + Labels: ai.Labels, + } + resp, err := c.impl.Authorize(req) + switch { + case err == nil: + return resp, nil + case shared.IsError(err, shared.ErrForbidden): + return []string{}, nil + case shared.IsError(err, shared.ErrUnacceptable): + return nil, api.NoMatch + default: + return nil, err + } +} + +func (c *RPCAuthz) Stop() { + if c.client != nil { + c.client.Kill() + } +} + +func (c *RPCAuthz) Name() string { + return "rpc" +} + +func NewRPCAuthz(cfg *RPCAuthzConfig) (*RPCAuthz, error) { + glog.Infof("RPC authorizer: %s", cfg) + + conn := &rpc.ClientConfig{ + HandshakeConfig: plugin.Handshake, + Plugins: plugin.PluginMap, + Cmd: exec.Command(cfg.Command, cfg.Args...), + } + client := rpc.NewClient(conn) + + rpcClient, err := client.Client() + if err != nil { + client.Kill() + return nil, err + } + + raw, err := rpcClient.Dispense(plugin.PluginNetRPC) + if err != nil { + client.Kill() + return nil, err + } + + impl, ok := raw.(plugin.Authorizer) + if !ok { + client.Kill() + return nil, fmt.Errorf("no authorizer plugin provided: %T", impl) + } + + result := &RPCAuthz{ + client: client, + impl: impl, + } + + return result, nil +} diff --git a/auth_server/gen_version.go b/auth_server/gen_version.go index 65c86bda..04d7a998 100644 --- a/auth_server/gen_version.go +++ b/auth_server/gen_version.go @@ -1,4 +1,5 @@ -//+build ignore +//go:build ignore +// +build ignore /* Copyright 2021 Cesanta Software Ltd. diff --git a/auth_server/go.mod b/auth_server/go.mod index 498b1966..4817a91d 100644 --- a/auth_server/go.mod +++ b/auth_server/go.mod @@ -2,8 +2,10 @@ module github.com/cesanta/docker_auth/auth_server go 1.24.0 +replace github.com/cesanta/docker_auth/auth_server/plugin => ./plugin + require ( - cloud.google.com/go/storage v1.29.0 + cloud.google.com/go/storage v1.30.1 github.com/casbin/casbin/v2 v2.55.1 github.com/cesanta/glog v0.0.0-20150527111657-22eb27a0ae19 github.com/coreos/go-oidc/v3 v3.9.0 @@ -21,7 +23,7 @@ require ( golang.org/x/crypto v0.45.0 golang.org/x/net v0.47.0 golang.org/x/oauth2 v0.27.0 - google.golang.org/api v0.126.0 + google.golang.org/api v0.149.0 gopkg.in/fsnotify.v1 v1.4.7 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 gopkg.in/yaml.v2 v2.4.0 @@ -29,10 +31,12 @@ require ( ) require ( - cloud.google.com/go v0.110.2 // indirect + cloud.google.com/go v0.110.10 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect - cloud.google.com/go/iam v0.13.0 // indirect + cloud.google.com/go/iam v1.1.5 // indirect github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect + github.com/cesanta/docker_auth/auth_server/plugin v0.0.0-00010101000000-000000000000 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/go-jose/go-jose/v3 v3.0.4 // indirect github.com/goccy/go-json v0.9.11 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -40,17 +44,23 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/s2a-go v0.1.4 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.11.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-plugin v1.7.0 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/kr/pretty v0.3.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.6.6 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/sirupsen/logrus v1.9.1 // indirect @@ -64,11 +74,11 @@ require ( golang.org/x/text v0.31.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/grpc v1.56.3 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/grpc v1.61.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect lukechampine.com/uint128 v1.2.0 // indirect diff --git a/auth_server/go.sum b/auth_server/go.sum index bebecbfc..ad701cb5 100644 --- a/auth_server/go.sum +++ b/auth_server/go.sum @@ -2,12 +2,18 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= @@ -93,6 +99,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -173,14 +181,22 @@ github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -196,9 +212,13 @@ github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoP github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -212,6 +232,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= @@ -306,6 +328,9 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -313,8 +338,11 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= @@ -348,6 +376,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= @@ -440,6 +470,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -595,8 +626,11 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= @@ -660,6 +694,8 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNq google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -674,10 +710,16 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -695,6 +737,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -708,6 +752,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= diff --git a/auth_server/plugin/authn/contract.go b/auth_server/plugin/authn/contract.go new file mode 100644 index 00000000..d315ff62 --- /dev/null +++ b/auth_server/plugin/authn/contract.go @@ -0,0 +1,35 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authn + +// AuthenticateResponse contains information associated with +// the authenticated principal. +type AuthenticateResponse map[string][]string + +// AuthenticateRequest represents the input query for authentication requests. +type AuthenticateRequest struct { + // Username is the authentication principal + Username string + // Password is the authentication secret + Password string +} + +// Authenticator is the contract plugin implementations must fulfill +// in order to be used for authentication purposes. +type Authenticator interface { + Authenticate(*AuthenticateRequest) (AuthenticateResponse, error) +} diff --git a/auth_server/plugin/authn/doc.go b/auth_server/plugin/authn/doc.go new file mode 100644 index 00000000..d8d1d4bf --- /dev/null +++ b/auth_server/plugin/authn/doc.go @@ -0,0 +1,18 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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. +*/ + +// Shared interfaces and constants for authentication requests +package authn diff --git a/auth_server/plugin/authn/handshake.go b/auth_server/plugin/authn/handshake.go new file mode 100644 index 00000000..4bd5c467 --- /dev/null +++ b/auth_server/plugin/authn/handshake.go @@ -0,0 +1,38 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authn + +import ( + plugin "github.com/hashicorp/go-plugin" +) + +const ( + // PluginNetRPC is the plugin identifier for the net/rpc implementation + PluginNetRPC = "authenticator" +) + +// PluginMap is the map of plugins we can dispense. +var PluginMap = map[string]plugin.Plugin{ + PluginNetRPC: &RPCPlugin{}, +} + +// Handshake is the plugin contract between the host and its plugins. +var Handshake = plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "AUTHN_PLUGIN", + MagicCookieValue: "docker_auth", +} diff --git a/auth_server/plugin/authn/rpc.go b/auth_server/plugin/authn/rpc.go new file mode 100644 index 00000000..6d814b2d --- /dev/null +++ b/auth_server/plugin/authn/rpc.go @@ -0,0 +1,86 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authn + +import ( + "net/rpc" + + plugin "github.com/hashicorp/go-plugin" +) + +// RPCPlugin implements [plugin.Plugin] +// using net/rpc as transport implementation. +type RPCPlugin struct { + impl Authenticator +} + +func NewRPCPlugin(a Authenticator) *RPCPlugin { + result := &RPCPlugin{ + impl: a, + } + + return result +} + +func (p *RPCPlugin) Server(_ *plugin.MuxBroker) (interface{}, error) { + return NewRPCServer(p.impl), nil +} + +func (*RPCPlugin) Client(_ *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { + return NewRPCClient(c), nil +} + +// RPCClient implements [Authenticator] using an [rpc.Client] +// for communication. +type RPCClient struct { + client *rpc.Client +} + +func NewRPCClient(c *rpc.Client) *RPCClient { + result := &RPCClient{ + client: c, + } + + return result +} + +func (c *RPCClient) Authenticate(req *AuthenticateRequest) (resp AuthenticateResponse, err error) { + err = c.client.Call("Plugin.Authenticate", req, &resp) + + return +} + +// RPCServer is the server side of the communication with RPCClient, conforming to +// the requirements of net/rpc +type RPCServer struct { + impl Authenticator +} + +func NewRPCServer(a Authenticator) *RPCServer { + result := &RPCServer{ + impl: a, + } + + return result +} + +func (s *RPCServer) Authenticate(req *AuthenticateRequest, resp *AuthenticateResponse) error { + v, err := s.impl.Authenticate(req) + *resp = v + + return err +} diff --git a/auth_server/plugin/authz/contract.go b/auth_server/plugin/authz/contract.go new file mode 100644 index 00000000..150f9ce7 --- /dev/null +++ b/auth_server/plugin/authz/contract.go @@ -0,0 +1,42 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authz + +import ( + "net" +) + +// AuthorizeResponse contains information associated with +// the authorized principal. +type AuthorizeResponse []string + +// AuthorizeRequest represents the input query for authorization requests. +type AuthorizeRequest struct { + Account string + Type string + Name string + Service string + IP net.IP + Actions []string + Labels map[string][]string +} + +// Authorizer is the contract plugin implementations must fulfill +// in order to be used for authorization purposes. +type Authorizer interface { + Authorize(*AuthorizeRequest) (AuthorizeResponse, error) +} diff --git a/auth_server/plugin/authz/doc.go b/auth_server/plugin/authz/doc.go new file mode 100644 index 00000000..a6d29588 --- /dev/null +++ b/auth_server/plugin/authz/doc.go @@ -0,0 +1,18 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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. +*/ + +// Shared interfaces and constants for authorization requests +package authz diff --git a/auth_server/plugin/authz/handshake.go b/auth_server/plugin/authz/handshake.go new file mode 100644 index 00000000..da5f4b7d --- /dev/null +++ b/auth_server/plugin/authz/handshake.go @@ -0,0 +1,38 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authz + +import ( + plugin "github.com/hashicorp/go-plugin" +) + +const ( + // PluginNetRPC is the plugin identifier for the net/rpc implementation + PluginNetRPC = "authorizer" +) + +// PluginMap is the map of plugins we can dispense. +var PluginMap = map[string]plugin.Plugin{ + PluginNetRPC: &RPCPlugin{}, +} + +// Handshake is the plugin contract between the host and its plugins. +var Handshake = plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "AUTHZ_PLUGIN", + MagicCookieValue: "docker_auth", +} diff --git a/auth_server/plugin/authz/rpc.go b/auth_server/plugin/authz/rpc.go new file mode 100644 index 00000000..6e47a579 --- /dev/null +++ b/auth_server/plugin/authz/rpc.go @@ -0,0 +1,86 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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 authz + +import ( + "net/rpc" + + plugin "github.com/hashicorp/go-plugin" +) + +// RPCPlugin implements [plugin.Plugin] +// using net/rpc as transport implementation. +type RPCPlugin struct { + impl Authorizer +} + +func NewRPCPlugin(a Authorizer) *RPCPlugin { + result := &RPCPlugin{ + impl: a, + } + + return result +} + +func (p *RPCPlugin) Server(_ *plugin.MuxBroker) (interface{}, error) { + return NewRPCServer(p.impl), nil +} + +func (*RPCPlugin) Client(_ *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { + return NewRPCClient(c), nil +} + +// RPCClient implements [Authorizer] using an [rpc.Client] +// for communication. +type RPCClient struct { + client *rpc.Client +} + +func NewRPCClient(c *rpc.Client) *RPCClient { + result := &RPCClient{ + client: c, + } + + return result +} + +func (c *RPCClient) Authorize(req *AuthorizeRequest) (resp AuthorizeResponse, err error) { + err = c.client.Call("Plugin.Authorize", req, &resp) + + return +} + +// RPCServer is the server side of the communication with RPCClient, conforming to +// the requirements of net/rpc +type RPCServer struct { + impl Authorizer +} + +func NewRPCServer(a Authorizer) *RPCServer { + result := &RPCServer{ + impl: a, + } + + return result +} + +func (s *RPCServer) Authorize(req *AuthorizeRequest, resp *AuthorizeResponse) error { + v, err := s.impl.Authorize(req) + *resp = v + + return err +} diff --git a/auth_server/plugin/doc.go b/auth_server/plugin/doc.go new file mode 100644 index 00000000..066c5ce2 --- /dev/null +++ b/auth_server/plugin/doc.go @@ -0,0 +1,19 @@ +/* + Copyright 2019 Cesanta Software Ltd. + + 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 + + https://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. +*/ + +// Shared constants, interfaces and other parts for implementing +// authentication/authorization plugins via RPC +package plugin diff --git a/auth_server/plugin/errors.go b/auth_server/plugin/errors.go new file mode 100644 index 00000000..174569a0 --- /dev/null +++ b/auth_server/plugin/errors.go @@ -0,0 +1,27 @@ +package plugin + +import ( + "errors" +) + +var ( + ErrForbidden = errors.New("authorization has been refused") + ErrUnauthorized = errors.New("authentication has been refused") + ErrUnacceptable = errors.New("plugin is not applicable to the provided input") +) + +// IsError is a helper method to compare errors that have +// been serialized and transmitted via RPC. the recommended +// way to compare errors using [errors#Is] does not work as +// the deserialized error and the comparison input are two +// distinct error instances (presumably created via [errors.New]) +// that happen to have the same error message but have different +// pointer addresses. IsError compares the message of the +// provided errors without unwrapping either of them. +func IsError(err, target error) bool { + if err == nil || target == nil { + return err == target + } + + return err.Error() == target.Error() +} diff --git a/auth_server/plugin/go.mod b/auth_server/plugin/go.mod new file mode 100644 index 00000000..a1bfa4bc --- /dev/null +++ b/auth_server/plugin/go.mod @@ -0,0 +1,3 @@ +module github.com/cesanta/docker_auth/auth_server/plugin + +go 1.23.0 diff --git a/auth_server/server/config.go b/auth_server/server/config.go index 13c610b7..de524e0b 100644 --- a/auth_server/server/config.go +++ b/auth_server/server/config.go @@ -51,11 +51,13 @@ type Config struct { MongoAuth *authn.MongoAuthConfig `yaml:"mongo_auth,omitempty"` XormAuthn *authn.XormAuthnConfig `yaml:"xorm_auth,omitempty"` ExtAuth *authn.ExtAuthConfig `yaml:"ext_auth,omitempty"` + RPCAuthn *authn.RPCAuthnConfig `yaml:"rpc_authn,omitempty"` PluginAuthn *authn.PluginAuthnConfig `yaml:"plugin_authn,omitempty"` ACL authz.ACL `yaml:"acl,omitempty"` ACLMongo *authz.ACLMongoConfig `yaml:"acl_mongo,omitempty"` ACLXorm *authz.XormAuthzConfig `yaml:"acl_xorm,omitempty"` ExtAuthz *authz.ExtAuthzConfig `yaml:"ext_authz,omitempty"` + RPCAuthz *authz.RPCAuthzConfig `yaml:"rpc_authz,omitempty"` PluginAuthz *authz.PluginAuthzConfig `yaml:"plugin_authz,omitempty"` CasbinAuthz *authz.CasbinAuthzConfig `yaml:"casbin_authz,omitempty"` } @@ -180,7 +182,7 @@ func validate(c *Config) error { if c.Token.Expiration <= 0 { return fmt.Errorf("expiration must be positive, got %d", c.Token.Expiration) } - if c.Users == nil && c.ExtAuth == nil && c.GoogleAuth == nil && c.GitHubAuth == nil && c.GitlabAuth == nil && c.OIDCAuth == nil && c.LDAPAuth == nil && c.MongoAuth == nil && c.XormAuthn == nil && c.PluginAuthn == nil { + if c.Users == nil && c.ExtAuth == nil && c.RPCAuthn == nil && c.GoogleAuth == nil && c.GitHubAuth == nil && c.GitlabAuth == nil && c.OIDCAuth == nil && c.LDAPAuth == nil && c.MongoAuth == nil && c.XormAuthn == nil && c.PluginAuthn == nil { return errors.New("no auth methods are configured, this is probably a mistake. Use an empty user map if you really want to deny everyone") } if c.MongoAuth != nil { @@ -308,7 +310,12 @@ func validate(c *Config) error { return fmt.Errorf("bad ext_auth config: %s", err) } } - if c.ACL == nil && c.ACLXorm == nil && c.ACLMongo == nil && c.ExtAuthz == nil && c.PluginAuthz == nil { + if c.RPCAuthn != nil { + if err := c.RPCAuthn.Validate(); err != nil { + return fmt.Errorf("bad rpc_authn config: %s", err) + } + } + if c.ACL == nil && c.ACLXorm == nil && c.ACLMongo == nil && c.ExtAuthz == nil && c.RPCAuthz == nil && c.PluginAuthz == nil { return errors.New("ACL is empty, this is probably a mistake. Use an empty list if you really want to deny all actions") } @@ -332,6 +339,11 @@ func validate(c *Config) error { return err } } + if c.RPCAuthz != nil { + if err := c.RPCAuthz.Validate(); err != nil { + return fmt.Errorf("bad rpc_authz config: %w", err) + } + } if c.PluginAuthn != nil { if err := c.PluginAuthn.Validate(); err != nil { return fmt.Errorf("bad plugin_authn config: %s", err) diff --git a/auth_server/server/server.go b/auth_server/server/server.go index ae7abd82..dadccffe 100644 --- a/auth_server/server/server.go +++ b/auth_server/server/server.go @@ -82,12 +82,26 @@ func NewAuthServer(c *Config) (*AuthServer, error) { extAuthorizer := authz.NewExtAuthzAuthorizer(c.ExtAuthz) as.authorizers = append(as.authorizers, extAuthorizer) } + if c.RPCAuthz != nil { + ra, err := authz.NewRPCAuthz(c.RPCAuthz) + if err != nil { + return nil, err + } + as.authorizers = append(as.authorizers, ra) + } if c.Users != nil { as.authenticators = append(as.authenticators, authn.NewStaticUserAuth(c.Users)) } if c.ExtAuth != nil { as.authenticators = append(as.authenticators, authn.NewExtAuth(c.ExtAuth)) } + if c.RPCAuthn != nil { + ra, err := authn.NewRPCAuthn(c.RPCAuthn) + if err != nil { + return nil, err + } + as.authenticators = append(as.authenticators, ra) + } if c.GoogleAuth != nil { ga, err := authn.NewGoogleAuth(c.GoogleAuth) if err != nil { @@ -437,7 +451,7 @@ func (as *AuthServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { case req.URL.Path == path_prefix+"/auth": as.doAuth(rw, req) case req.URL.Path == path_prefix+"/auth/token": - as.doAuth(rw, req) + as.doAuth(rw, req) case req.URL.Path == path_prefix+"/google_auth" && as.ga != nil: as.ga.DoGoogleAuth(rw, req) case req.URL.Path == path_prefix+"/github_auth" && as.gha != nil: diff --git a/examples/reference.yml b/examples/reference.yml index 31de9ee7..54c13558 100644 --- a/examples/reference.yml +++ b/examples/reference.yml @@ -10,7 +10,7 @@ # autoredirect: false # rootcertbundle: "/path/to/server.pem" -server: # Server settings. +server: # Server settings. # Address to listen on. # Can be HOST:PORT for TCP or file path (e.g. /run/docker_auth.sock) for Unix socket. addr: ":5001" @@ -76,8 +76,8 @@ server: # Server settings. # end of addresses. # real_ip_pos: -2 -token: # Settings for the tokens. - issuer: "Acme auth server" # Must match issuer in the Registry config. +token: # Settings for the tokens. + issuer: "Acme auth server" # Must match issuer in the Registry config. expiration: 900 # Token must be signed by a certificate that registry trusts, i.e. by a certificate to which a trust chain # can be constructed from one of the certificates in registry's auth.token.rootcertbundle. @@ -98,10 +98,10 @@ token: # Settings for the tokens. users: # Password is specified as a BCrypt hash. Use `htpasswd -nB USERNAME` to generate. "admin": - password: "$2y$05$LO.vzwpWC5LZGqThvEfznu8qhb5SGqvBSWY1J3yZ4AxtMRZ3kN5jC" # badmin + password: "$2y$05$LO.vzwpWC5LZGqThvEfznu8qhb5SGqvBSWY1J3yZ4AxtMRZ3kN5jC" # badmin "test": - password: "$2y$05$WuwBasGDAgr.QCbGIjKJaep4dhxeai9gNZdmBnQXqpKly57oNutya" # 123 - "": {} # Allow anonymous (no "docker login") access. + password: "$2y$05$WuwBasGDAgr.QCbGIjKJaep4dhxeai9gNZdmBnQXqpKly57oNutya" # 123 + "": {} # Allow anonymous (no "docker login") access. # Google authentication. # ==! NB: DO NOT ENTER YOUR GOOGLE PASSWORD AT "docker login". IT WILL NOT WORK. @@ -109,7 +109,7 @@ users: # Go to the server's port as HTTPS with your browser and follow the "Login with Google account" link. # Once signed in, you will get a throw-away password which you can use for Docker login. google_auth: - domain: "example.com" # Optional. If set, only logins from this domain are accepted. + domain: "example.com" # Optional. If set, only logins from this domain are accepted. # client_id and client_secret for API access. Required. # Follow instructions here: https://developers.google.com/identity/sign-in/web/devconsole-project # NB: Make sure JavaScript origins are configured correctly, and that third-party @@ -133,7 +133,7 @@ google_auth: # Go to the server's port as HTTPS with your browser and follow the "Login with GitHub account" link. # Once signed in, you will get a throw-away password which you can use for Docker login. github_auth: - organization: "acme" # Optional. If set, only logins from this organization are accepted. + organization: "acme" # Optional. If set, only logins from this organization are accepted. # client_id and client_secret for API access. Required. # You can register a new application here: https://github.com/settings/developers # NB: Make sure JavaScript origins are configured correctly, and that third-party @@ -155,11 +155,11 @@ github_auth: # or Redis, redis_token_db: redis_options: - # with a single instance, - addr: localhost:6379 + # with a single instance, + addr: localhost:6379 redis_cluster_options: - # or in the cluster mode. - addrs: ["localhost:7000"] + # or in the cluster mode. + addrs: ["localhost:7000"] # How long to wait when talking to GitHub servers. Optional. http_timeout: "10s" # How long to wait before revalidating the GitHub token. Optional. @@ -212,7 +212,6 @@ oidc_auth: - openid - email - # Gitlab authentication. # ==! NB: DO NOT ENTER YOUR Gitlab PASSWORD AT "docker login". IT WILL NOT WORK. # Instead, Auth server maintains a database of Gitlab authentication tokens. @@ -327,9 +326,17 @@ xorm_auth: # The "labels" key may contain labels to be passed down to authz, where they can # be used in matching. See ext_auth.sh for an example. ext_auth: - command: "/usr/local/bin/my_auth" # Can be a relative path too; $PATH works. + command: "/usr/local/bin/my_auth" # Can be a relative path too; $PATH works. args: ["--flag", "--more", "--flags"] +# External authentication via RPC calls. Plugins are started alongside the server and +# are queried via RPC calls. Only a single method must be implemented which responds +# with either a map of labels to further process during the authorization phase or an +# error. +rpc_authn: + command: "/usr/local/libexec/docker_auth/my_authn" + args: ["--log.level", "info"] + # User written authentication plugin - call a user written program to authenticate user. # Username of type string and password of authn.PasswordString is passed to the plugin # Expects a boolean value whether the user is authenticate or not, authn.Labels, error @@ -369,58 +376,58 @@ plugin_authn: # * ${name} - the name of the repository (i.e. image), e.g. centos. # * ${labels: