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:
authorPaul Okstad <pokstad@gitlab.com>2020-11-17 21:24:52 +0300
committerPaul Okstad <pokstad@gitlab.com>2020-11-17 21:24:52 +0300
commit3464eea3b9c7c4cb195c1bb9714291c94524d2f5 (patch)
tree839166a61e8f8896af61068291d63a0f7066ad24
parent381df30b1b49b9fcfbc1e4107a106a70f1403c7d (diff)
parent13839ed64f05c62cfe486424a8cd14279b51ae19 (diff)
Merge branch 'po-client-certs-internal-gitlab-api' into 'master'
Configure the GitLab internal API client to provide client certificates when using TLS Closes #3160 See merge request gitlab-org/gitaly!2794
-rw-r--r--changelogs/unreleased/po-client-certs-internal-gitlab-api.yml6
-rw-r--r--cmd/gitaly-hooks/hooks.go2
-rw-r--r--cmd/gitaly-hooks/hooks_test.go6
-rw-r--r--cmd/gitaly-lfs-smudge/lfs_smudge.go29
-rw-r--r--cmd/gitaly-lfs-smudge/lfs_smudge_test.go35
-rw-r--r--cmd/gitaly/main.go2
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--internal/gitaly/hook/access.go17
-rw-r--r--internal/gitaly/hook/access_test.go18
-rw-r--r--internal/gitaly/hook/testdata/certs/server.crt34
-rw-r--r--internal/gitaly/hook/testdata/certs/server.key52
-rw-r--r--internal/gitaly/service/hook/post_receive_test.go4
-rw-r--r--internal/gitaly/service/hook/pre_receive_test.go8
-rw-r--r--internal/gitaly/service/repository/archive.go8
-rw-r--r--internal/gitaly/service/repository/archive_test.go4
-rw-r--r--internal/testhelper/testserver.go44
17 files changed, 236 insertions, 39 deletions
diff --git a/changelogs/unreleased/po-client-certs-internal-gitlab-api.yml b/changelogs/unreleased/po-client-certs-internal-gitlab-api.yml
new file mode 100644
index 000000000..7c6051639
--- /dev/null
+++ b/changelogs/unreleased/po-client-certs-internal-gitlab-api.yml
@@ -0,0 +1,6 @@
+---
+title: Configure the GitLab internal API client to provide client certificates when
+ using TLS
+merge_request: 2794
+author:
+type: security
diff --git a/cmd/gitaly-hooks/hooks.go b/cmd/gitaly-hooks/hooks.go
index c4cbf3bef..80b000c90 100644
--- a/cmd/gitaly-hooks/hooks.go
+++ b/cmd/gitaly-hooks/hooks.go
@@ -331,7 +331,7 @@ func check(configPath string) (*hook.CheckInfo, error) {
return nil, err
}
- gitlabAPI, err := hook.NewGitlabAPI(config.Config.Gitlab)
+ gitlabAPI, err := hook.NewGitlabAPI(config.Config.Gitlab, config.Config.TLS)
if err != nil {
return nil, err
}
diff --git a/cmd/gitaly-hooks/hooks_test.go b/cmd/gitaly-hooks/hooks_test.go
index 7c9d50bb9..bb81ec32a 100644
--- a/cmd/gitaly-hooks/hooks_test.go
+++ b/cmd/gitaly-hooks/hooks_test.go
@@ -156,7 +156,7 @@ func testHooksPrePostReceive(t *testing.T) {
config.Config.Gitlab.HTTPSettings.User = gitlabUser
config.Config.Gitlab.HTTPSettings.Password = gitlabPassword
- gitlabAPI, err := gitalyhook.NewGitlabAPI(config.Config.Gitlab)
+ gitlabAPI, err := gitalyhook.NewGitlabAPI(config.Config.Gitlab, config.Config.TLS)
require.NoError(t, err)
socket, stop := runHookServiceServerWithAPI(t, token, gitlabAPI)
@@ -362,7 +362,7 @@ func TestHooksPostReceiveFailed(t *testing.T) {
token := "abc123"
- gitlabAPI, err := gitalyhook.NewGitlabAPI(config.Config.Gitlab)
+ gitlabAPI, err := gitalyhook.NewGitlabAPI(config.Config.Gitlab, config.Config.TLS)
require.NoError(t, err)
socket, stop := runHookServiceServerWithAPI(t, token, gitlabAPI)
@@ -488,7 +488,7 @@ func TestHooksNotAllowed(t *testing.T) {
token := "abc123"
- gitlabAPI, err := gitalyhook.NewGitlabAPI(config.Config.Gitlab)
+ gitlabAPI, err := gitalyhook.NewGitlabAPI(config.Config.Gitlab, config.Config.TLS)
require.NoError(t, err)
socket, stop := runHookServiceServerWithAPI(t, token, gitlabAPI)
diff --git a/cmd/gitaly-lfs-smudge/lfs_smudge.go b/cmd/gitaly-lfs-smudge/lfs_smudge.go
index d08716404..07b63ca23 100644
--- a/cmd/gitaly-lfs-smudge/lfs_smudge.go
+++ b/cmd/gitaly-lfs-smudge/lfs_smudge.go
@@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"io"
"path/filepath"
@@ -67,14 +68,16 @@ func handleSmudge(to io.Writer, from io.Reader, config configProvider) (io.Reade
logger.WithField("oid", ptr.Oid).Debug("decoded LFS OID")
- cfg, glRepository, err := loadConfig(config)
+ glCfg, tlsCfg, glRepository, err := loadConfig(config)
if err != nil {
return contents, err
}
- logger.WithField("gitlab_config", cfg).Debug("loaded GitLab API config")
+ logger.WithField("gitlab_config", glCfg).
+ WithField("gitaly_tls_config", tlsCfg).
+ Debug("loaded GitLab API config")
- client, err := hook.NewGitlabNetClient(cfg)
+ client, err := hook.NewGitlabNetClient(glCfg, tlsCfg)
if err != nil {
return contents, err
}
@@ -92,22 +95,32 @@ func handleSmudge(to io.Writer, from io.Reader, config configProvider) (io.Reade
return contents, nil
}
-func loadConfig(cfgProvider configProvider) (config.Gitlab, string, error) {
+func loadConfig(cfgProvider configProvider) (config.Gitlab, config.TLS, string, error) {
var cfg config.Gitlab
+ var tlsCfg config.TLS
glRepository := cfgProvider.Get("GL_REPOSITORY")
if glRepository == "" {
- return cfg, "", fmt.Errorf("error loading project: GL_REPOSITORY is not defined")
+ return cfg, tlsCfg, "", fmt.Errorf("error loading project: GL_REPOSITORY is not defined")
}
u := cfgProvider.Get("GL_INTERNAL_CONFIG")
if u == "" {
- return cfg, glRepository, fmt.Errorf("unable to retrieve GL_INTERNAL_CONFIG")
+ return cfg, tlsCfg, glRepository, fmt.Errorf("unable to retrieve GL_INTERNAL_CONFIG")
}
if err := json.Unmarshal([]byte(u), &cfg); err != nil {
- return cfg, glRepository, fmt.Errorf("unable to unmarshal GL_INTERNAL_CONFIG: %v", err)
+ return cfg, tlsCfg, glRepository, fmt.Errorf("unable to unmarshal GL_INTERNAL_CONFIG: %v", err)
}
- return cfg, glRepository, nil
+ u = cfgProvider.Get("GITALY_TLS")
+ if u == "" {
+ return cfg, tlsCfg, glRepository, errors.New("unable to retrieve GITALY_TLS")
+ }
+
+ if err := json.Unmarshal([]byte(u), &tlsCfg); err != nil {
+ return cfg, tlsCfg, glRepository, fmt.Errorf("unable to unmarshal GITALY_TLS: %w", err)
+ }
+
+ return cfg, tlsCfg, glRepository, nil
}
diff --git a/cmd/gitaly-lfs-smudge/lfs_smudge_test.go b/cmd/gitaly-lfs-smudge/lfs_smudge_test.go
index 8ebd8afb6..244cd16d5 100644
--- a/cmd/gitaly-lfs-smudge/lfs_smudge_test.go
+++ b/cmd/gitaly-lfs-smudge/lfs_smudge_test.go
@@ -24,14 +24,19 @@ size 177735
glRepository = "project-1"
secretToken = "topsecret"
testData = "hello world"
+ certPath = "../../internal/gitaly/hook/testdata/certs/server.crt"
+ keyPath = "../../internal/gitaly/hook/testdata/certs/server.key"
)
var (
defaultOptions = testhelper.GitlabTestServerOptions{
- SecretToken: secretToken,
- LfsBody: testData,
- LfsOid: lfsOid,
- GlRepository: glRepository,
+ SecretToken: secretToken,
+ LfsBody: testData,
+ LfsOid: lfsOid,
+ GlRepository: glRepository,
+ ClientCACertPath: certPath,
+ ServerCertPath: certPath,
+ ServerKeyPath: keyPath,
}
)
@@ -62,7 +67,7 @@ func runTestServer(t *testing.T, options testhelper.GitlabTestServerOptions) (co
serverURL, serverCleanup := testhelper.NewGitlabTestServer(t, options)
- c := config.Gitlab{URL: serverURL, SecretFile: secretFilePath}
+ c := config.Gitlab{URL: serverURL, SecretFile: secretFilePath, HTTPSettings: config.HTTPSettings{CAFile: certPath}}
return c, func() {
cleanup()
@@ -80,6 +85,12 @@ func TestSuccessfulLfsSmudge(t *testing.T) {
cfg, err := json.Marshal(c)
require.NoError(t, err)
+ tlsCfg, err := json.Marshal(config.TLS{
+ CertPath: certPath,
+ KeyPath: keyPath,
+ })
+ require.NoError(t, err)
+
tmpDir, err := ioutil.TempDir("", "")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
@@ -88,6 +99,7 @@ func TestSuccessfulLfsSmudge(t *testing.T) {
"GL_REPOSITORY": "project-1",
"GL_INTERNAL_CONFIG": string(cfg),
"GITALY_LOG_DIR": tmpDir,
+ "GITALY_TLS": string(tlsCfg),
}
cfgProvider := &mapConfig{env: env}
initLogging(cfgProvider)
@@ -113,9 +125,11 @@ func TestUnsuccessfulLfsSmudge(t *testing.T) {
desc string
data string
missingEnv string
+ tlsCfg config.TLS
expectedError bool
options testhelper.GitlabTestServerOptions
expectedLogMessage string
+ expectedGitalyTLS string
}{
{
desc: "bad LFS pointer",
@@ -152,6 +166,13 @@ func TestUnsuccessfulLfsSmudge(t *testing.T) {
expectedError: true,
expectedLogMessage: "error loading LFS object",
},
+ {
+ desc: "invalid TLS paths",
+ data: lfsPointer,
+ options: defaultOptions,
+ tlsCfg: config.TLS{CertPath: "fake-path", KeyPath: "not-real"},
+ expectedError: true,
+ },
}
for _, tc := range testCases {
@@ -162,6 +183,9 @@ func TestUnsuccessfulLfsSmudge(t *testing.T) {
cfg, err := json.Marshal(c)
require.NoError(t, err)
+ tlsCfg, err := json.Marshal(tc.tlsCfg)
+ require.NoError(t, err)
+
tmpDir, err := ioutil.TempDir("", "")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
@@ -170,6 +194,7 @@ func TestUnsuccessfulLfsSmudge(t *testing.T) {
"GL_REPOSITORY": "project-1",
"GL_INTERNAL_CONFIG": string(cfg),
"GITALY_LOG_DIR": tmpDir,
+ "GITALY_TLS": string(tlsCfg),
}
if tc.missingEnv != "" {
diff --git a/cmd/gitaly/main.go b/cmd/gitaly/main.go
index 3e4423f63..b47f04658 100644
--- a/cmd/gitaly/main.go
+++ b/cmd/gitaly/main.go
@@ -113,7 +113,7 @@ func run(b *bootstrap.Bootstrap) error {
if config.SkipHooks() {
log.Warn("skipping GitLab API client creation since hooks are bypassed via GITALY_TESTING_NO_GIT_HOOKS")
} else {
- gitlabAPI, err = hook.NewGitlabAPI(config.Config.Gitlab)
+ gitlabAPI, err = hook.NewGitlabAPI(config.Config.Gitlab, config.Config.TLS)
if err != nil {
log.Fatalf("could not create GitLab API client: %v", err)
}
diff --git a/go.mod b/go.mod
index bd191a41b..28d02380a 100644
--- a/go.mod
+++ b/go.mod
@@ -25,7 +25,7 @@ require (
github.com/sirupsen/logrus v1.7.0
github.com/stretchr/testify v1.6.1
github.com/uber/jaeger-client-go v2.15.0+incompatible
- gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201106143703-da924afd346d
+ gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc
gitlab.com/gitlab-org/labkit v0.0.0-20201113080642-40dcf811328c
go.uber.org/atomic v1.4.0 // indirect
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
diff --git a/go.sum b/go.sum
index c30234780..4bfb00523 100644
--- a/go.sum
+++ b/go.sum
@@ -421,8 +421,8 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
gitlab.com/gitlab-org/gitaly v1.68.0/go.mod h1:/pCsB918Zu5wFchZ9hLYin9WkJ2yQqdVNz0zlv5HbXg=
-gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201106143703-da924afd346d h1:C5BHPkyOWx6mDFz2LA3O/3o2ixSGsQBnaLSSQV7KOj8=
-gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201106143703-da924afd346d/go.mod h1:5QSTbpAHY2v0iIH5uHh2KA9w7sPUqPmnLjDApI/sv1U=
+gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc h1:ENBJqb2gK3cZFHe8dbEM/TPXCwkAxDuxGK255ux4XPg=
+gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc/go.mod h1:5QSTbpAHY2v0iIH5uHh2KA9w7sPUqPmnLjDApI/sv1U=
gitlab.com/gitlab-org/labkit v0.0.0-20190221122536-0c3fc7cdd57c/go.mod h1:rYhLgfrbEcyfinG+R3EvKu6bZSsmwQqcXzLfHWSfUKM=
gitlab.com/gitlab-org/labkit v0.0.0-20190221122536-0c3fc7cdd57c/go.mod h1:rYhLgfrbEcyfinG+R3EvKu6bZSsmwQqcXzLfHWSfUKM=
gitlab.com/gitlab-org/labkit v0.0.0-20200908084045-45895e129029 h1:L7b9YLsU3zBfTShAPl4fjhgFdfSvuo9tu4VobJdcKDs=
diff --git a/internal/gitaly/hook/access.go b/internal/gitaly/hook/access.go
index 152e8c0e8..4cb326998 100644
--- a/internal/gitaly/hook/access.go
+++ b/internal/gitaly/hook/access.go
@@ -71,20 +71,29 @@ type gitlabAPI struct {
}
// NewGitlabNetClient creates an HTTP client to talk to the Rails internal API
-func NewGitlabNetClient(gitlabCfg config.Gitlab) (*client.GitlabNetClient, error) {
+func NewGitlabNetClient(gitlabCfg config.Gitlab, tlsCfg config.TLS) (*client.GitlabNetClient, error) {
url, err := url.PathUnescape(gitlabCfg.URL)
if err != nil {
return nil, err
}
- httpClient := client.NewHTTPClient(
+ var opts []client.HTTPClientOpt
+ if tlsCfg.CertPath != "" && tlsCfg.KeyPath != "" {
+ opts = append(opts, client.WithClientCert(tlsCfg.CertPath, tlsCfg.KeyPath))
+ }
+
+ httpClient, err := client.NewHTTPClientWithOpts(
url,
gitlabCfg.RelativeURLRoot,
gitlabCfg.HTTPSettings.CAFile,
gitlabCfg.HTTPSettings.CAPath,
gitlabCfg.HTTPSettings.SelfSigned,
uint64(gitlabCfg.HTTPSettings.ReadTimeout),
+ opts,
)
+ if err != nil {
+ return nil, fmt.Errorf("building new HTTP client for GitLab API: %w", err)
+ }
if httpClient == nil {
return nil, fmt.Errorf("%s is not a valid url", gitlabCfg.URL)
@@ -106,8 +115,8 @@ func NewGitlabNetClient(gitlabCfg config.Gitlab) (*client.GitlabNetClient, error
}
// NewGitlabAPI creates a GitLabAPI to talk to the Rails internal API
-func NewGitlabAPI(gitlabCfg config.Gitlab) (GitlabAPI, error) {
- client, err := NewGitlabNetClient(gitlabCfg)
+func NewGitlabAPI(gitlabCfg config.Gitlab, tlsCfg config.TLS) (GitlabAPI, error) {
+ client, err := NewGitlabNetClient(gitlabCfg, tlsCfg)
if err != nil {
return nil, err
}
diff --git a/internal/gitaly/hook/access_test.go b/internal/gitaly/hook/access_test.go
index deb17bfcb..fab76522d 100644
--- a/internal/gitaly/hook/access_test.go
+++ b/internal/gitaly/hook/access_test.go
@@ -23,6 +23,9 @@ type postReceiveRequest struct {
PushOptions []string `json:"push_options,omitempty"`
}
+// TestAllowedVerifyParams uses client cert fixtures to test TLS connections. To
+// regenerate these certs, run `go generate access_test.go`.
+//go:generate openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 -out testdata/certs/server.crt -keyout testdata/certs/server.key -subj "/C=US/ST=California/L=San Francisco/O=GitLab/OU=GitLab-Shell/CN=localhost" -addext "subjectAltName = IP:127.0.0.1"
func TestAllowedVerifyParams(t *testing.T) {
user, password := "user", "password"
secretToken := "topsecret"
@@ -62,6 +65,9 @@ func TestAllowedVerifyParams(t *testing.T) {
GitObjectDir: gitObjectDirFull,
GitAlternateObjectDirs: gitAlternateObjectDirsFull,
RepoPath: testRepoPath,
+ ClientCACertPath: "testdata/certs/server.crt",
+ ServerCertPath: "testdata/certs/server.crt",
+ ServerKeyPath: "testdata/certs/server.key",
})
defer cleanup()
@@ -71,7 +77,11 @@ func TestAllowedVerifyParams(t *testing.T) {
HTTPSettings: config.HTTPSettings{
User: user,
Password: password,
+ CAFile: "testdata/certs/server.crt",
},
+ }, config.TLS{
+ CertPath: "testdata/certs/server.crt",
+ KeyPath: "testdata/certs/server.key",
})
require.NoError(t, err)
@@ -198,7 +208,7 @@ func TestEscapedAndRelativeURLs(t *testing.T) {
User: user,
Password: password,
},
- })
+ }, config.TLS{})
require.NoError(t, err)
allowed, _, err := c.Allowed(context.Background(), testRepo, glRepository, glID, protocol, changes)
require.NoError(t, err)
@@ -330,7 +340,7 @@ func TestAllowedResponseHandling(t *testing.T) {
c, err := hook.NewGitlabAPI(config.Gitlab{
URL: server.URL,
SecretFile: secretFilePath,
- })
+ }, config.TLS{})
require.NoError(t, err)
allowed, message, err := c.Allowed(context.Background(), testRepo, "repo-1", "key-123", "http", "a\nb\nc\nd")
@@ -418,7 +428,7 @@ func TestPrereceive(t *testing.T) {
c, err := hook.NewGitlabAPI(config.Gitlab{
URL: server.URL,
SecretFile: secretFilePath,
- })
+ }, config.TLS{})
require.NoError(t, err)
success, err := c.PreReceive(context.Background(), "key-123")
@@ -494,7 +504,7 @@ func TestPostReceive(t *testing.T) {
c, err := hook.NewGitlabAPI(config.Gitlab{
URL: server.URL,
SecretFile: secretFilePath,
- })
+ }, config.TLS{})
require.NoError(t, err)
repositoryID := "project-123"
diff --git a/internal/gitaly/hook/testdata/certs/server.crt b/internal/gitaly/hook/testdata/certs/server.crt
new file mode 100644
index 000000000..af0103e8e
--- /dev/null
+++ b/internal/gitaly/hook/testdata/certs/server.crt
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+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
+bjogyV1O8cL8dTQLHxeQG9oC
+-----END CERTIFICATE-----
diff --git a/internal/gitaly/hook/testdata/certs/server.key b/internal/gitaly/hook/testdata/certs/server.key
new file mode 100644
index 000000000..f343b2be7
--- /dev/null
+++ b/internal/gitaly/hook/testdata/certs/server.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+QY02aq2BcpobB0bNeQ8o8RipNcKKF9U=
+-----END PRIVATE KEY-----
diff --git a/internal/gitaly/service/hook/post_receive_test.go b/internal/gitaly/service/hook/post_receive_test.go
index bd09c2d00..726798e5c 100644
--- a/internal/gitaly/service/hook/post_receive_test.go
+++ b/internal/gitaly/service/hook/post_receive_test.go
@@ -90,7 +90,7 @@ func TestHooksMissingStdin(t *testing.T) {
config.Config.Gitlab = gitlabConfig
- api, err := gitalyhook.NewGitlabAPI(gitlabConfig)
+ api, err := gitalyhook.NewGitlabAPI(gitlabConfig, config.Config.TLS)
require.NoError(t, err)
testCases := []struct {
@@ -230,7 +230,7 @@ To create a merge request for okay, visit:
config.Config.Gitlab = gitlabConfig
- api, err := gitalyhook.NewGitlabAPI(gitlabConfig)
+ api, err := gitalyhook.NewGitlabAPI(gitlabConfig, config.Config.TLS)
require.NoError(t, err)
serverSocketPath, stop := runHooksServerWithAPI(t, api, config.Config)
diff --git a/internal/gitaly/service/hook/pre_receive_test.go b/internal/gitaly/service/hook/pre_receive_test.go
index 2e706a55a..b97e7e6c6 100644
--- a/internal/gitaly/service/hook/pre_receive_test.go
+++ b/internal/gitaly/service/hook/pre_receive_test.go
@@ -132,7 +132,7 @@ func TestPreReceiveHook_GitlabAPIAccess(t *testing.T) {
SecretFile: secretFilePath,
}
- gitlabAPI, err := gitalyhook.NewGitlabAPI(gitlabConfig)
+ gitlabAPI, err := gitalyhook.NewGitlabAPI(gitlabConfig, config.Config.TLS)
require.NoError(t, err)
serverSocketPath, stop := runHooksServerWithAPI(t, gitlabAPI, config.Cfg{})
@@ -235,7 +235,7 @@ func TestPreReceive_APIErrors(t *testing.T) {
SecretFile: secretFilePath,
}
- gitlabAPI, err := gitalyhook.NewGitlabAPI(gitlabConfig)
+ gitlabAPI, err := gitalyhook.NewGitlabAPI(gitlabConfig, config.Config.TLS)
require.NoError(t, err)
serverSocketPath, stop := runHooksServerWithAPI(t, gitlabAPI, config.Cfg{})
@@ -295,7 +295,7 @@ exit %d
SecretFile: secretFilePath,
}
- gitlabAPI, err := gitalyhook.NewGitlabAPI(gitlabConfig)
+ gitlabAPI, err := gitalyhook.NewGitlabAPI(gitlabConfig, config.Config.TLS)
require.NoError(t, err)
serverSocketPath, stop := runHooksServerWithAPI(t, gitlabAPI, config.Cfg{})
@@ -413,7 +413,7 @@ func TestPreReceiveHook_Primary(t *testing.T) {
gitlabAPI, err := gitalyhook.NewGitlabAPI(config.Gitlab{
URL: srv.URL,
SecretFile: secretFilePath,
- })
+ }, config.Config.TLS)
require.NoError(t, err)
serverSocketPath, stop := runHooksServerWithAPI(t, gitlabAPI, config.Cfg{})
diff --git a/internal/gitaly/service/repository/archive.go b/internal/gitaly/service/repository/archive.go
index bce628c89..49614c73d 100644
--- a/internal/gitaly/service/repository/archive.go
+++ b/internal/gitaly/service/repository/archive.go
@@ -31,6 +31,7 @@ type archiveParams struct {
archivePath string
exclude []string
internalCfg []byte
+ tlsCfg []byte
binDir string
loggingDir string
}
@@ -86,6 +87,11 @@ func (s *server) GetArchive(in *gitalypb.GetArchiveRequest, stream gitalypb.Repo
return err
}
+ tlsCfg, err := json.Marshal(s.cfg.TLS)
+ if err != nil {
+ return err
+ }
+
return handleArchive(archiveParams{
ctx: ctx,
writer: writer,
@@ -95,6 +101,7 @@ func (s *server) GetArchive(in *gitalypb.GetArchiveRequest, stream gitalypb.Repo
archivePath: path,
exclude: exclude,
internalCfg: gitlabConfig,
+ tlsCfg: tlsCfg,
binDir: s.binDir,
loggingDir: s.loggingCfg.Dir,
})
@@ -188,6 +195,7 @@ func handleArchive(p archiveParams) error {
fmt.Sprintf("GL_REPOSITORY=%s", p.in.GetRepository().GetGlRepository()),
fmt.Sprintf("GL_PROJECT_PATH=%s", p.in.GetRepository().GetGlProjectPath()),
fmt.Sprintf("GL_INTERNAL_CONFIG=%s", p.internalCfg),
+ fmt.Sprintf("GITALY_TLS=%s", p.tlsCfg),
fmt.Sprintf("CORRELATION_ID=%s", correlation.ExtractFromContext(p.ctx)),
fmt.Sprintf("%s=%s", log.GitalyLogDirEnvKey, p.loggingDir),
}
diff --git a/internal/gitaly/service/repository/archive_test.go b/internal/gitaly/service/repository/archive_test.go
index 26b872e38..1188d3344 100644
--- a/internal/gitaly/service/repository/archive_test.go
+++ b/internal/gitaly/service/repository/archive_test.go
@@ -531,6 +531,9 @@ env | grep -E "^GL_|CORRELATION|GITALY_"`))
cfgData, err := json.Marshal(config.Config.Gitlab)
require.NoError(t, err)
+ tlsCfgData, err := json.Marshal(config.Config.TLS)
+ require.NoError(t, err)
+
stream, err := client.GetArchive(ctx, req)
require.NoError(t, err)
@@ -539,6 +542,7 @@ env | grep -E "^GL_|CORRELATION|GITALY_"`))
require.Contains(t, string(data), "GL_REPOSITORY="+testhelper.GlRepository)
require.Contains(t, string(data), "GL_PROJECT_PATH="+testhelper.GlProjectPath)
require.Contains(t, string(data), "GL_INTERNAL_CONFIG="+string(cfgData))
+ require.Contains(t, string(data), "GITALY_TLS="+string(tlsCfgData))
require.Contains(t, string(data), "CORRELATION_ID="+correlationID)
require.Contains(t, string(data), "GITALY_LOG_DIR="+config.Config.Logging.Dir)
}
diff --git a/internal/testhelper/testserver.go b/internal/testhelper/testserver.go
index b6dab7ed9..b20415bc8 100644
--- a/internal/testhelper/testserver.go
+++ b/internal/testhelper/testserver.go
@@ -2,6 +2,8 @@ package testhelper
import (
"context"
+ "crypto/tls"
+ "crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
@@ -798,6 +800,9 @@ type GitlabTestServerOptions struct {
RepoPath string
RelativeURLRoot string
GlRepository string
+ ClientCACertPath string // used to verify client certs are valid
+ ServerCertPath string
+ ServerKeyPath string
}
// NewGitlabTestServer returns a mock gitlab server that responds to the hook api endpoints
@@ -810,15 +815,45 @@ func NewGitlabTestServer(t FatalLogger, options GitlabTestServerOptions) (url st
mux.Handle(prefix+"/check", http.HandlerFunc(handleCheck(options)))
mux.Handle(prefix+"/lfs", http.HandlerFunc(handleLfs(options)))
+ var tlsCfg *tls.Config
+ if options.ClientCACertPath != "" {
+ caCertPEM, err := ioutil.ReadFile(options.ClientCACertPath)
+ if err != nil {
+ t.Fatalf("reading client CA file: %v", err)
+ }
+
+ certPool := x509.NewCertPool()
+ if !certPool.AppendCertsFromPEM(caCertPEM) {
+ t.Fatalf("unable to add client CA cert to pool")
+ }
+
+ serverCert, err := tls.LoadX509KeyPair(options.ServerCertPath, options.ServerKeyPath)
+ if err != nil {
+ t.Fatalf("unable to load x509 key pair: %v", err)
+ }
+ tlsCfg = &tls.Config{
+ ClientCAs: certPool,
+ ClientAuth: tls.RequireAndVerifyClientCert,
+ Certificates: []tls.Certificate{serverCert},
+ }
+ }
+
if options.UnixSocket {
- return startSocketHTTPServer(t, mux)
+ return startSocketHTTPServer(t, mux, tlsCfg)
} else {
- server := httptest.NewServer(mux)
+ var server *httptest.Server
+ if tlsCfg == nil {
+ server = httptest.NewServer(mux)
+ } else {
+ server = httptest.NewUnstartedServer(mux)
+ server.TLS = tlsCfg
+ server.StartTLS()
+ }
return server.URL, server.Close
}
}
-func startSocketHTTPServer(t FatalLogger, mux *http.ServeMux) (string, func()) {
+func startSocketHTTPServer(t FatalLogger, mux *http.ServeMux, tlsCfg *tls.Config) (string, func()) {
tmpFile, err := ioutil.TempFile(os.TempDir(), "http-test-server")
if err != nil {
t.Fatalf("Cannot create temporary file", err)
@@ -833,7 +868,8 @@ func startSocketHTTPServer(t FatalLogger, mux *http.ServeMux) (string, func()) {
}
server := http.Server{
- Handler: mux,
+ Handler: mux,
+ TLSConfig: tlsCfg,
}
go server.Serve(socketListener)