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
commit13839ed64f05c62cfe486424a8cd14279b51ae19 (patch)
tree839166a61e8f8896af61068291d63a0f7066ad24
parent381df30b1b49b9fcfbc1e4107a106a70f1403c7d (diff)
Inject entire config to GitLab API instantiation
To support configuring client certificates in TLS connections to the GitLab internal API, the entire configuration is needed.
-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)