Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Cai <jcai@gitlab.com>2019-10-08 20:23:05 +0300
committerJohn Cai <jcai@gitlab.com>2019-10-08 20:23:05 +0300
commitd1bca34e27326dfd58738f8bab7a8e27a40f2990 (patch)
treef7619243567a2096c38c36bc42129ec50e1873e3
parent58ccfe7fdbde5a3f1d492d7a196ea24f8fab4e34 (diff)
parentad371133b96a7c9262269a1ffca5612c7eaeb9bc (diff)
Merge branch 'jc-praefect-auth' into 'master'
Adding auth to praefect Closes #1897 See merge request gitlab-org/gitaly!1517
-rw-r--r--changelogs/unreleased/jc-praefect-auth.yml5
-rw-r--r--cmd/praefect/main.go2
-rw-r--r--config.praefect.toml.example11
-rw-r--r--internal/auth/auth.go7
-rw-r--r--internal/config/auth.go20
-rw-r--r--internal/config/config.go12
-rw-r--r--internal/praefect/auth_test.go196
-rw-r--r--internal/praefect/config/config.go2
-rw-r--r--internal/praefect/conn/client_connections.go5
-rw-r--r--internal/praefect/conn/client_connections_test.go4
-rw-r--r--internal/praefect/coordinator_test.go2
-rw-r--r--internal/praefect/replicator_test.go6
-rw-r--r--internal/praefect/server.go3
-rw-r--r--internal/praefect/server_test.go24
-rw-r--r--internal/server/auth/auth.go61
-rw-r--r--internal/server/auth_test.go11
-rw-r--r--internal/server/server.go4
-rw-r--r--internal/service/repository/testhelper_test.go29
-rw-r--r--internal/service/server/info_test.go28
19 files changed, 346 insertions, 86 deletions
diff --git a/changelogs/unreleased/jc-praefect-auth.yml b/changelogs/unreleased/jc-praefect-auth.yml
new file mode 100644
index 000000000..b321ce013
--- /dev/null
+++ b/changelogs/unreleased/jc-praefect-auth.yml
@@ -0,0 +1,5 @@
+---
+title: Adding auth to praefect
+merge_request: 1517
+author:
+type: added
diff --git a/cmd/praefect/main.go b/cmd/praefect/main.go
index a7c9beddf..e5c434ae0 100644
--- a/cmd/praefect/main.go
+++ b/cmd/praefect/main.go
@@ -96,7 +96,7 @@ func run(listeners []net.Listener, conf config.Config) error {
clientConnections := conn.NewClientConnections()
for _, node := range conf.Nodes {
- if err := clientConnections.RegisterNode(node.Storage, node.Address); err != nil {
+ if err := clientConnections.RegisterNode(node.Storage, node.Address, node.Token); err != nil {
return fmt.Errorf("failed to register %s: %s", node.Address, err)
}
diff --git a/config.praefect.toml.example b/config.praefect.toml.example
index 368f9fed6..74e929f33 100644
--- a/config.praefect.toml.example
+++ b/config.praefect.toml.example
@@ -7,7 +7,6 @@ listen_addr = "127.0.0.1:2305"
# socket_path = "/home/git/gitlab/tmp/sockets/private/praefect.socket"
# # Optional: export metrics via Prometheus
# prometheus_listen_addr = "127.0.01:10101"
-
# # You can optionally configure Praefect to output JSON-formatted log messages to stdout
# [logging]
# format = "json"
@@ -15,7 +14,11 @@ listen_addr = "127.0.0.1:2305"
# # One of, in order: debug, info, warn, errror, fatal, panic
# # Defaults to "info"
# level = "warn"
-
+#
+# Optional: authenticate Gitaly requests using a shared secret. This token works the same way as a gitaly token
+# [auth]
+# token = 'abc123secret'
+#
# # One or more Gitaly servers need to be configured to be managed. The names
# of each server are used to link multiple nodes, or `gitaly_server`s together
# as shard. listen_addr should be unique for all nodes.
@@ -25,11 +28,15 @@ listen_addr = "127.0.0.1:2305"
storage = "praefect-git-0"
address = "tcp://praefect-git-0.internal"
primary = true
+ token = 'token1'
[[node]]
storage = "praefect-git-1"
address = "tcp://praefect-git-1.internal"
+ token = 'token2'
[[node]]
storage = "praefect-git-2"
address = "tcp://praefect-git-2.internal"
+ token = 'token3'
+
diff --git a/internal/auth/auth.go b/internal/auth/auth.go
new file mode 100644
index 000000000..1bf8de921
--- /dev/null
+++ b/internal/auth/auth.go
@@ -0,0 +1,7 @@
+package auth
+
+// Config is a struct for an authentication config
+type Config struct {
+ Transitioning bool `toml:"transitioning"`
+ Token string `toml:"token"`
+}
diff --git a/internal/config/auth.go b/internal/config/auth.go
deleted file mode 100644
index 98f192769..000000000
--- a/internal/config/auth.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package config
-
-import (
- log "github.com/sirupsen/logrus"
-)
-
-// Auth contains the authentication settings for this Gitaly process.
-type Auth struct {
- Transitioning bool `toml:"transitioning"`
- Token string `toml:"token"`
-}
-
-func validateToken() error {
- if !Config.Auth.Transitioning || len(Config.Auth.Token) == 0 {
- return nil
- }
-
- log.Warn("Authentication is enabled but not enforced because transitioning=true. Gitaly will accept unauthenticated requests.")
- return nil
-}
diff --git a/internal/config/config.go b/internal/config/config.go
index 372ab182f..98631a6ac 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -12,6 +12,7 @@ import (
"github.com/BurntSushi/toml"
"github.com/kelseyhightower/envconfig"
log "github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/gitaly/internal/auth"
)
const (
@@ -39,7 +40,7 @@ type Cfg struct {
Storages []Storage `toml:"storage" envconfig:"storage"`
Logging Logging `toml:"logging" envconfig:"logging"`
Prometheus Prometheus `toml:"prometheus"`
- Auth Auth `toml:"auth"`
+ Auth auth.Config `toml:"auth"`
TLS TLS `toml:"tls"`
Ruby Ruby `toml:"gitaly-ruby"`
GitlabShell GitlabShell `toml:"gitlab-shell"`
@@ -286,3 +287,12 @@ func validateBinDir() error {
Config.BinDir, err = filepath.Abs(Config.BinDir)
return err
}
+
+func validateToken() error {
+ if !Config.Auth.Transitioning || len(Config.Auth.Token) == 0 {
+ return nil
+ }
+
+ log.Warn("Authentication is enabled but not enforced because transitioning=true. Gitaly will accept unauthenticated requests.")
+ return nil
+}
diff --git a/internal/praefect/auth_test.go b/internal/praefect/auth_test.go
new file mode 100644
index 000000000..750515dbd
--- /dev/null
+++ b/internal/praefect/auth_test.go
@@ -0,0 +1,196 @@
+package praefect
+
+import (
+ "context"
+ "net"
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ gitalyauth "gitlab.com/gitlab-org/gitaly/auth"
+ "gitlab.com/gitlab-org/gitaly/internal/auth"
+ "gitlab.com/gitlab-org/gitaly/internal/log"
+ "gitlab.com/gitlab-org/gitaly/internal/praefect/config"
+ "gitlab.com/gitlab-org/gitaly/internal/praefect/conn"
+ "gitlab.com/gitlab-org/gitaly/internal/praefect/mock"
+ "gitlab.com/gitlab-org/gitaly/internal/praefect/models"
+ "gitlab.com/gitlab-org/gitaly/internal/praefect/protoregistry"
+ "gitlab.com/gitlab-org/gitaly/internal/testhelper"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+)
+
+func TestAuthFailures(t *testing.T) {
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ testCases := []struct {
+ desc string
+ opts []grpc.DialOption
+ code codes.Code
+ }{
+ {
+ desc: "no auth",
+ opts: nil,
+ code: codes.Unauthenticated,
+ },
+ {
+ desc: "invalid auth",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(brokenAuth{})},
+ code: codes.Unauthenticated,
+ },
+ {
+ desc: "wrong secret",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentials("foobar"))},
+ code: codes.PermissionDenied,
+ },
+ {
+ desc: "wrong secret new auth",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2("foobar"))},
+ code: codes.PermissionDenied,
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ srv, serverSocketPath, cleanup := runServer(t, "quxbaz", true)
+ defer srv.Shutdown(ctx)
+ defer cleanup()
+
+ connOpts := append(tc.opts, grpc.WithInsecure())
+ conn, err := dial(serverSocketPath, connOpts)
+ require.NoError(t, err, tc.desc)
+ defer conn.Close()
+
+ cli := mock.NewSimpleServiceClient(conn)
+
+ _, err = cli.SimpleUnaryUnary(ctx, &mock.SimpleRequest{
+ Value: 1,
+ })
+
+ testhelper.RequireGrpcError(t, err, tc.code)
+ })
+ }
+}
+
+func TestAuthSuccess(t *testing.T) {
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ token := "foobar"
+
+ testCases := []struct {
+ desc string
+ opts []grpc.DialOption
+ required bool
+ token string
+ }{
+ {desc: "no auth, not required"},
+ {
+ desc: "v1 incorrect auth, not required",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentials("incorrect"))},
+ token: token,
+ },
+ {
+ desc: "v1 correct auth, not required",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentials(token))},
+ token: token,
+ },
+ {
+ desc: "v1 correct auth, required",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentials(token))},
+ token: token,
+ required: true,
+ },
+ {
+ desc: "v2 correct new auth, not required",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2(token))},
+ token: token,
+ },
+ {
+ desc: "v2 incorrect auth, not required",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2("incorrect"))},
+ token: token,
+ },
+ {
+ desc: "v2 correct new auth, required",
+ opts: []grpc.DialOption{grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2(token))},
+ token: token,
+ required: true,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ srv, serverSocketPath, cleanup := runServer(t, tc.token, tc.required)
+ defer srv.Shutdown(ctx)
+ defer cleanup()
+
+ connOpts := append(tc.opts, grpc.WithInsecure())
+ conn, err := dial(serverSocketPath, connOpts)
+ require.NoError(t, err, tc.desc)
+ defer conn.Close()
+
+ cli := mock.NewSimpleServiceClient(conn)
+
+ _, err = cli.SimpleUnaryUnary(ctx, &mock.SimpleRequest{
+ Value: 1,
+ })
+
+ assert.NoError(t, err, tc.desc)
+ })
+ }
+}
+
+type brokenAuth struct{}
+
+func (brokenAuth) RequireTransportSecurity() bool { return false }
+func (brokenAuth) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
+ return map[string]string{"authorization": "Bearer blablabla"}, nil
+}
+
+func dial(serverSocketPath string, opts []grpc.DialOption) (*grpc.ClientConn, error) {
+ return grpc.Dial(serverSocketPath, opts...)
+}
+
+func runServer(t *testing.T, token string, required bool) (*Server, string, func()) {
+ backendToken := "abcxyz"
+ backend, cleanup := newMockDownstream(t, backendToken, callbackIncrement)
+
+ conf := config.Config{
+ Auth: auth.Config{Token: token, Transitioning: !required},
+ Nodes: []*models.Node{
+ &models.Node{
+ Storage: "praefect-internal-0",
+ DefaultPrimary: true,
+ Address: backend,
+ },
+ },
+ }
+
+ gz := proto.FileDescriptor("mock.proto")
+ fd, err := protoregistry.ExtractFileDescriptor(gz)
+ if err != nil {
+ panic(err)
+ }
+
+ logEntry := log.Default()
+ datastore := NewMemoryDatastore(conf)
+
+ clientConnections := conn.NewClientConnections()
+ clientConnections.RegisterNode("praefect-internal-0", backend, backendToken)
+
+ coordinator := NewCoordinator(logEntry, datastore, clientConnections, fd)
+
+ replMgr := NewReplMgr("praefect-internal-0", logEntry, datastore, clientConnections)
+
+ srv := NewServer(coordinator, replMgr, nil, logEntry, clientConnections, conf)
+
+ serverSocketPath := testhelper.GetTemporaryGitalySocketFileName()
+
+ listener, err := net.Listen("unix", serverSocketPath)
+ require.NoError(t, err)
+ go srv.Start(listener)
+
+ return srv, "unix://" + serverSocketPath, cleanup
+}
diff --git a/internal/praefect/config/config.go b/internal/praefect/config/config.go
index 633f57d1f..818acae32 100644
--- a/internal/praefect/config/config.go
+++ b/internal/praefect/config/config.go
@@ -6,6 +6,7 @@ import (
"github.com/BurntSushi/toml"
+ "gitlab.com/gitlab-org/gitaly/internal/auth"
"gitlab.com/gitlab-org/gitaly/internal/config"
"gitlab.com/gitlab-org/gitaly/internal/praefect/models"
)
@@ -19,6 +20,7 @@ type Config struct {
Logging config.Logging `toml:"logging"`
PrometheusListenAddr string `toml:"prometheus_listen_addr"`
+ Auth auth.Config `toml:"auth"`
}
// FromFile loads the config for the passed file path
diff --git a/internal/praefect/conn/client_connections.go b/internal/praefect/conn/client_connections.go
index 8d4f3a033..c3734937a 100644
--- a/internal/praefect/conn/client_connections.go
+++ b/internal/praefect/conn/client_connections.go
@@ -6,7 +6,6 @@ import (
gitalyauth "gitlab.com/gitlab-org/gitaly/auth"
"gitlab.com/gitlab-org/gitaly/client"
- gitalyconfig "gitlab.com/gitlab-org/gitaly/internal/config"
"gitlab.com/gitlab-org/gitaly/internal/praefect/grpc-proxy/proxy"
"google.golang.org/grpc"
)
@@ -32,11 +31,11 @@ func NewClientConnections() *ClientConnections {
// RegisterNode will direct traffic to the supplied downstream connection when the storage location
// is encountered.
-func (c *ClientConnections) RegisterNode(storageName, listenAddr string) error {
+func (c *ClientConnections) RegisterNode(storageName, listenAddr, token string) error {
conn, err := client.Dial(listenAddr,
[]grpc.DialOption{
grpc.WithDefaultCallOptions(grpc.CallCustomCodec(proxy.Codec())),
- grpc.WithPerRPCCredentials(gitalyauth.RPCCredentials(gitalyconfig.Config.Auth.Token)),
+ grpc.WithPerRPCCredentials(gitalyauth.RPCCredentials(token)),
},
)
if err != nil {
diff --git a/internal/praefect/conn/client_connections_test.go b/internal/praefect/conn/client_connections_test.go
index 9e2f832d1..2c5f77a54 100644
--- a/internal/praefect/conn/client_connections_test.go
+++ b/internal/praefect/conn/client_connections_test.go
@@ -15,12 +15,12 @@ func TestRegisterNode(t *testing.T) {
_, err := clientConn.GetConnection(storageName)
require.Equal(t, ErrConnectionNotFound, err)
- require.NoError(t, clientConn.RegisterNode(storageName, fmt.Sprintf("tcp://%s", tcpAddress)))
+ require.NoError(t, clientConn.RegisterNode(storageName, fmt.Sprintf("tcp://%s", tcpAddress), "token"))
conn, err := clientConn.GetConnection(storageName)
require.NoError(t, err)
require.Equal(t, tcpAddress, conn.Target())
- err = clientConn.RegisterNode(storageName, "tcp://some-other-address")
+ err = clientConn.RegisterNode(storageName, "tcp://some-other-address", "token")
require.Equal(t, ErrAlreadyRegistered, err)
}
diff --git a/internal/praefect/coordinator_test.go b/internal/praefect/coordinator_test.go
index 267ce9fbb..bcc9d9132 100644
--- a/internal/praefect/coordinator_test.go
+++ b/internal/praefect/coordinator_test.go
@@ -51,7 +51,7 @@ func TestStreamDirector(t *testing.T) {
address := "gitaly-primary.example.com"
clientConnections := conn.NewClientConnections()
- clientConnections.RegisterNode("praefect-internal-1", fmt.Sprintf("tcp://%s", address))
+ clientConnections.RegisterNode("praefect-internal-1", fmt.Sprintf("tcp://%s", address), "token")
coordinator := NewCoordinator(log.Default(), datastore, clientConnections)
require.NoError(t, coordinator.RegisterProtos(protoregistry.GitalyProtoFileDescriptors...))
diff --git a/internal/praefect/replicator_test.go b/internal/praefect/replicator_test.go
index 22b7ed024..a46b2140c 100644
--- a/internal/praefect/replicator_test.go
+++ b/internal/praefect/replicator_test.go
@@ -98,8 +98,8 @@ func TestProceessReplicationJob(t *testing.T) {
replicator.log = gitaly_log.Default()
clientCC := conn.NewClientConnections()
- clientCC.RegisterNode("default", srvSocketPath)
- clientCC.RegisterNode("backup", srvSocketPath)
+ clientCC.RegisterNode("default", srvSocketPath, gitaly_config.Config.Auth.Token)
+ clientCC.RegisterNode("backup", srvSocketPath, gitaly_config.Config.Auth.Token)
replMgr := &ReplMgr{
log: gitaly_log.Default(),
@@ -178,7 +178,7 @@ func TestMain(m *testing.M) {
func testMain(m *testing.M) int {
defer testhelper.MustHaveNoChildProcess()
- gitaly_config.Config.Auth = gitaly_config.Auth{Token: testhelper.RepositoryAuthToken}
+ gitaly_config.Config.Auth.Token = testhelper.RepositoryAuthToken
var err error
gitaly_config.Config.GitlabShell.Dir, err = filepath.Abs("testdata/gitlab-shell")
diff --git a/internal/praefect/server.go b/internal/praefect/server.go
index 7e2307e62..d91385d05 100644
--- a/internal/praefect/server.go
+++ b/internal/praefect/server.go
@@ -16,6 +16,7 @@ import (
"gitlab.com/gitlab-org/gitaly/internal/praefect/conn"
"gitlab.com/gitlab-org/gitaly/internal/praefect/grpc-proxy/proxy"
"gitlab.com/gitlab-org/gitaly/internal/praefect/service/server"
+ "gitlab.com/gitlab-org/gitaly/internal/server/auth"
"gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
grpccorrelation "gitlab.com/gitlab-org/labkit/correlation/grpc"
grpctracing "gitlab.com/gitlab-org/labkit/tracing/grpc"
@@ -40,6 +41,7 @@ func NewServer(c *Coordinator, repl ReplMgr, grpcOpts []grpc.ServerOption, l *lo
grpc_prometheus.StreamServerInterceptor,
cancelhandler.Stream, // Should be below LogHandler
grpctracing.StreamServerTracingInterceptor(),
+ auth.StreamServerInterceptor(conf.Auth),
// Panic handler should remain last so that application panics will be
// converted to errors and logged
panichandler.StreamPanicHandler,
@@ -50,6 +52,7 @@ func NewServer(c *Coordinator, repl ReplMgr, grpcOpts []grpc.ServerOption, l *lo
grpc_prometheus.UnaryServerInterceptor,
cancelhandler.Unary, // Should be below LogHandler
grpctracing.UnaryServerTracingInterceptor(),
+ auth.UnaryServerInterceptor(conf.Auth),
// Panic handler should remain last so that application panics will be
// converted to errors and logged
panichandler.UnaryPanicHandler,
diff --git a/internal/praefect/server_test.go b/internal/praefect/server_test.go
index 6545f3cdf..b7ce6e1b4 100644
--- a/internal/praefect/server_test.go
+++ b/internal/praefect/server_test.go
@@ -10,6 +10,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly/client"
+ internalauth "gitlab.com/gitlab-org/gitaly/internal/auth"
"gitlab.com/gitlab-org/gitaly/internal/log"
"gitlab.com/gitlab-org/gitaly/internal/praefect/config"
"gitlab.com/gitlab-org/gitaly/internal/praefect/conn"
@@ -68,10 +69,12 @@ func TestServerSimpleUnaryUnary(t *testing.T) {
ID: 1,
Storage: "praefect-internal-1",
DefaultPrimary: true,
+ Token: "abc",
},
&models.Node{
ID: 2,
Storage: "praefect-internal-2",
+ Token: "xyz",
}},
}
@@ -82,10 +85,10 @@ func TestServerSimpleUnaryUnary(t *testing.T) {
clientCC := conn.NewClientConnections()
for id, nodeStorage := range datastore.storageNodes.m {
- backend, cleanup := newMockDownstream(t, tt.callback)
+ backend, cleanup := newMockDownstream(t, nodeStorage.Token, tt.callback)
defer cleanup() // clean up mock downstream server resources
- clientCC.RegisterNode(nodeStorage.Storage, backend)
+ clientCC.RegisterNode(nodeStorage.Storage, backend, nodeStorage.Token)
nodeStorage.Address = backend
datastore.storageNodes.m[id] = nodeStorage
}
@@ -145,10 +148,12 @@ func TestGitalyServerInfo(t *testing.T) {
ID: 1,
Storage: "praefect-internal-1",
DefaultPrimary: true,
+ Token: "abc",
},
&models.Node{
ID: 2,
Storage: "praefect-internal-2",
+ Token: "xyz",
}},
}
cc, srv := runFullPraefectServer(t, conf)
@@ -175,9 +180,9 @@ func runFullPraefectServer(t *testing.T, conf config.Config) (*grpc.ClientConn,
clientCC := conn.NewClientConnections()
for id, nodeStorage := range datastore.storageNodes.m {
- _, backend := runInternalGitalyServer(t)
+ _, backend := runInternalGitalyServer(t, nodeStorage.Token)
- clientCC.RegisterNode(nodeStorage.Storage, backend)
+ clientCC.RegisterNode(nodeStorage.Storage, backend, nodeStorage.Token)
nodeStorage.Address = backend
datastore.storageNodes.m[id] = nodeStorage
}
@@ -215,9 +220,9 @@ func runFullPraefectServer(t *testing.T, conf config.Config) (*grpc.ClientConn,
return cc, prf
}
-func runInternalGitalyServer(t *testing.T) (*grpc.Server, string) {
- streamInt := []grpc.StreamServerInterceptor{auth.StreamServerInterceptor()}
- unaryInt := []grpc.UnaryServerInterceptor{auth.UnaryServerInterceptor()}
+func runInternalGitalyServer(t *testing.T, token string) (*grpc.Server, string) {
+ streamInt := []grpc.StreamServerInterceptor{auth.StreamServerInterceptor(internalauth.Config{Token: token})}
+ unaryInt := []grpc.UnaryServerInterceptor{auth.UnaryServerInterceptor(internalauth.Config{Token: token})}
server := testhelper.NewTestGrpcServer(t, streamInt, unaryInt)
serverSocketPath := testhelper.GetTemporaryGitalySocketFileName()
@@ -268,13 +273,14 @@ func dialLocalPort(tb testing.TB, port int, backend bool) *grpc.ClientConn {
}
// initializes and returns a client to downstream server, downstream server, and cleanup function
-func newMockDownstream(tb testing.TB, callback simpleUnaryUnaryCallback) (string, func()) {
+func newMockDownstream(tb testing.TB, token string, callback simpleUnaryUnaryCallback) (string, func()) {
// setup mock server
m := &mockSvc{
simpleUnaryUnary: callback,
}
- srv := grpc.NewServer()
+ srv := grpc.NewServer(grpc.UnaryInterceptor(auth.UnaryServerInterceptor(internalauth.Config{Token: token})))
+
mock.RegisterSimpleServiceServer(srv, m)
// client to backend service
diff --git a/internal/server/auth/auth.go b/internal/server/auth/auth.go
index 0ac688ba8..8ad2963d8 100644
--- a/internal/server/auth/auth.go
+++ b/internal/server/auth/auth.go
@@ -7,7 +7,7 @@ import (
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"github.com/prometheus/client_golang/prometheus"
gitalyauth "gitlab.com/gitlab-org/gitaly/auth"
- "gitlab.com/gitlab-org/gitaly/internal/config"
+ internalauth "gitlab.com/gitlab-org/gitaly/internal/auth"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -28,45 +28,44 @@ func init() {
}
// StreamServerInterceptor checks for Gitaly bearer tokens.
-func StreamServerInterceptor() grpc.StreamServerInterceptor {
- return grpc_auth.StreamServerInterceptor(check)
+func StreamServerInterceptor(conf internalauth.Config) grpc.StreamServerInterceptor {
+ return grpc_auth.StreamServerInterceptor(checkFunc(conf))
}
// UnaryServerInterceptor checks for Gitaly bearer tokens.
-func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
- return grpc_auth.UnaryServerInterceptor(check)
+func UnaryServerInterceptor(conf internalauth.Config) grpc.UnaryServerInterceptor {
+ return grpc_auth.UnaryServerInterceptor(checkFunc(conf))
}
-func check(ctx context.Context) (context.Context, error) {
- if len(config.Config.Auth.Token) == 0 {
- countStatus("server disabled authentication").Inc()
- return ctx, nil
- }
+func checkFunc(conf internalauth.Config) func(ctx context.Context) (context.Context, error) {
+ return func(ctx context.Context) (context.Context, error) {
+ if len(conf.Token) == 0 {
+ countStatus("server disabled authentication", conf.Transitioning).Inc()
+ return ctx, nil
+ }
- err := gitalyauth.CheckToken(ctx, config.Config.Auth.Token, time.Now())
- switch status.Code(err) {
- case codes.OK:
- countStatus(okLabel()).Inc()
- case codes.Unauthenticated:
- countStatus("unauthenticated").Inc()
- case codes.PermissionDenied:
- countStatus("denied").Inc()
- default:
- countStatus("invalid").Inc()
- }
+ err := gitalyauth.CheckToken(ctx, conf.Token, time.Now())
+ switch status.Code(err) {
+ case codes.OK:
+ countStatus(okLabel(conf.Transitioning), conf.Transitioning).Inc()
+ case codes.Unauthenticated:
+ countStatus("unauthenticated", conf.Transitioning).Inc()
+ case codes.PermissionDenied:
+ countStatus("denied", conf.Transitioning).Inc()
+ default:
+ countStatus("invalid", conf.Transitioning).Inc()
+ }
- return ctx, ifEnforced(err)
-}
+ if conf.Transitioning {
+ err = nil
+ }
-func ifEnforced(err error) error {
- if config.Config.Auth.Transitioning {
- return nil
+ return ctx, err
}
- return err
}
-func okLabel() string {
- if config.Config.Auth.Transitioning {
+func okLabel(transitioning bool) string {
+ if transitioning {
// This special value is an extra warning sign to administrators that
// authentication is currently not enforced.
return "would be ok"
@@ -74,9 +73,9 @@ func okLabel() string {
return "ok"
}
-func countStatus(status string) prometheus.Counter {
+func countStatus(status string, transitioning bool) prometheus.Counter {
enforced := "true"
- if config.Config.Auth.Transitioning {
+ if transitioning {
enforced = "false"
}
return authCount.WithLabelValues(enforced, status)
diff --git a/internal/server/auth_test.go b/internal/server/auth_test.go
index fdbf3d6b0..462f5d80d 100644
--- a/internal/server/auth_test.go
+++ b/internal/server/auth_test.go
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
gitalyauth "gitlab.com/gitlab-org/gitaly/auth"
+ "gitlab.com/gitlab-org/gitaly/internal/auth"
"gitlab.com/gitlab-org/gitaly/internal/config"
"gitlab.com/gitlab-org/gitaly/internal/testhelper"
"google.golang.org/grpc"
@@ -58,7 +59,7 @@ func TestTLSSanity(t *testing.T) {
}
func TestAuthFailures(t *testing.T) {
- defer func(oldAuth config.Auth) {
+ defer func(oldAuth auth.Config) {
config.Config.Auth = oldAuth
}(config.Config.Auth)
config.Config.Auth.Token = "quxbaz"
@@ -100,7 +101,7 @@ func TestAuthFailures(t *testing.T) {
}
func TestAuthSuccess(t *testing.T) {
- defer func(oldAuth config.Auth) {
+ defer func(oldAuth auth.Config) {
config.Config.Auth = oldAuth
}(config.Config.Auth)
@@ -148,10 +149,8 @@ func TestAuthSuccess(t *testing.T) {
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
- config.Config.Auth = config.Auth{
- Transitioning: !tc.required,
- Token: tc.token,
- }
+ config.Config.Auth.Token = tc.token
+ config.Config.Auth.Transitioning = !tc.required
srv, serverSocketPath := runServer(t)
defer srv.Stop()
diff --git a/internal/server/server.go b/internal/server/server.go
index 8be44a7ae..97013d48e 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -85,7 +85,7 @@ func createNewServer(rubyServer *rubyserver.Server, secure bool) *grpc.Server {
sentryhandler.StreamLogHandler,
cancelhandler.Stream, // Should be below LogHandler
lh.StreamInterceptor(),
- auth.StreamServerInterceptor(),
+ auth.StreamServerInterceptor(config.Config.Auth),
grpctracing.StreamServerTracingInterceptor(),
cache.StreamInvalidator(diskcache.LeaseKeyer{}, protoregistry.GitalyProtoPreregistered),
// Panic handler should remain last so that application panics will be
@@ -101,7 +101,7 @@ func createNewServer(rubyServer *rubyserver.Server, secure bool) *grpc.Server {
sentryhandler.UnaryLogHandler,
cancelhandler.Unary, // Should be below LogHandler
lh.UnaryInterceptor(),
- auth.UnaryServerInterceptor(),
+ auth.UnaryServerInterceptor(config.Config.Auth),
grpctracing.UnaryServerTracingInterceptor(),
cache.UnaryInvalidator(diskcache.LeaseKeyer{}, protoregistry.GitalyProtoPreregistered),
// Panic handler should remain last so that application panics will be
diff --git a/internal/service/repository/testhelper_test.go b/internal/service/repository/testhelper_test.go
index 245310ca6..e9be51311 100644
--- a/internal/service/repository/testhelper_test.go
+++ b/internal/service/repository/testhelper_test.go
@@ -16,6 +16,7 @@ import (
"gitlab.com/gitlab-org/gitaly/internal/testhelper"
"gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
"google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
"google.golang.org/grpc/reflection"
)
@@ -43,8 +44,8 @@ func newRepositoryClient(t *testing.T, serverSocketPath string) (gitalypb.Reposi
var NewRepositoryClient = newRepositoryClient
func runRepoServer(t *testing.T) (*grpc.Server, string) {
- streamInt := []grpc.StreamServerInterceptor{auth.StreamServerInterceptor()}
- unaryInt := []grpc.UnaryServerInterceptor{auth.UnaryServerInterceptor()}
+ streamInt := []grpc.StreamServerInterceptor{auth.StreamServerInterceptor(config.Config.Auth)}
+ unaryInt := []grpc.UnaryServerInterceptor{auth.UnaryServerInterceptor(config.Config.Auth)}
server := testhelper.NewTestGrpcServer(t, streamInt, unaryInt)
serverSocketPath := testhelper.GetTemporaryGitalySocketFileName()
@@ -62,6 +63,28 @@ func runRepoServer(t *testing.T) (*grpc.Server, string) {
return server, "unix://" + serverSocketPath
}
+func TestRepoNoAuth(t *testing.T) {
+ srv, path := runRepoServer(t)
+ defer srv.Stop()
+
+ connOpts := []grpc.DialOption{
+ grpc.WithInsecure(),
+ }
+
+ conn, err := grpc.Dial(path, connOpts...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ client := gitalypb.NewRepositoryServiceClient(conn)
+ _, err = client.CreateRepository(ctx, &gitalypb.CreateRepositoryRequest{Repository: &gitalypb.Repository{StorageName: "default", RelativePath: "new/project/path"}})
+
+ testhelper.RequireGrpcError(t, err, codes.Unauthenticated)
+}
+
func assertModTimeAfter(t *testing.T, afterTime time.Time, paths ...string) bool {
// NOTE: Since some filesystems don't have sub-second precision on `mtime`
// we're rounding the times to seconds
@@ -84,7 +107,7 @@ func TestMain(m *testing.M) {
func testMain(m *testing.M) int {
defer testhelper.MustHaveNoChildProcess()
- config.Config.Auth = config.Auth{Token: testhelper.RepositoryAuthToken}
+ config.Config.Auth.Token = testhelper.RepositoryAuthToken
var err error
config.Config.GitlabShell.Dir, err = filepath.Abs("testdata/gitlab-shell")
diff --git a/internal/service/server/info_test.go b/internal/service/server/info_test.go
index d1b287285..d708c8a03 100644
--- a/internal/service/server/info_test.go
+++ b/internal/service/server/info_test.go
@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require"
gitalyauth "gitlab.com/gitlab-org/gitaly/auth"
+ internalauth "gitlab.com/gitlab-org/gitaly/internal/auth"
"gitlab.com/gitlab-org/gitaly/internal/config"
"gitlab.com/gitlab-org/gitaly/internal/git"
"gitlab.com/gitlab-org/gitaly/internal/server/auth"
@@ -15,6 +16,7 @@ import (
"gitlab.com/gitlab-org/gitaly/internal/version"
"gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
"google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
"google.golang.org/grpc/reflection"
)
@@ -67,8 +69,9 @@ func TestGitalyServerInfo(t *testing.T) {
}
func runServer(t *testing.T) (*grpc.Server, string) {
- streamInt := []grpc.StreamServerInterceptor{auth.StreamServerInterceptor()}
- unaryInt := []grpc.UnaryServerInterceptor{auth.UnaryServerInterceptor()}
+ authConfig := internalauth.Config{Token: testhelper.RepositoryAuthToken}
+ streamInt := []grpc.StreamServerInterceptor{auth.StreamServerInterceptor(authConfig)}
+ unaryInt := []grpc.UnaryServerInterceptor{auth.UnaryServerInterceptor(authConfig)}
server := testhelper.NewTestGrpcServer(t, streamInt, unaryInt)
serverSocketPath := testhelper.GetTemporaryGitalySocketFileName()
@@ -85,6 +88,27 @@ func runServer(t *testing.T) (*grpc.Server, string) {
return server, "unix://" + serverSocketPath
}
+func TestServerNoAuth(t *testing.T) {
+ srv, path := runServer(t)
+ defer srv.Stop()
+
+ connOpts := []grpc.DialOption{
+ grpc.WithInsecure(),
+ }
+
+ conn, err := grpc.Dial(path, connOpts...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ client := gitalypb.NewServerServiceClient(conn)
+ _, err = client.ServerInfo(ctx, &gitalypb.ServerInfoRequest{})
+
+ testhelper.RequireGrpcError(t, err, codes.Unauthenticated)
+}
func newServerClient(t *testing.T, serverSocketPath string) (gitalypb.ServerServiceClient, *grpc.ClientConn) {
connOpts := []grpc.DialOption{