Skip to content

Commit 222f0ac

Browse files
committed
bundle-server: add client cert verification
Add a '--client-ca' option to 'git-bundle-web-server' that, when specified, loads a PEM-formatted certificate file and uses the cert(s) within to verify client requests to the bundle web server. If the bundle server has been configured with this option and the client does not send an appropriate certificate, the TLS handshake will fail and the client will be unable to connect. Configuring both server TLS hosting (via the '--cert' and '--key' options) and client cert validation (via '--client-ca') sets up mutual TLS (mTLS) authentication on the bundle web server. Signed-off-by: Victoria Dye <[email protected]>
1 parent 3ac4b26 commit 222f0ac

File tree

6 files changed

+33
-9
lines changed

6 files changed

+33
-9
lines changed

cmd/git-bundle-server/web-server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (w *webServerCmd) startServer(ctx context.Context, args []string) error {
7777
parser.Visit(func(f *flag.Flag) {
7878
if webServerFlags.Lookup(f.Name) != nil {
7979
value := f.Value.String()
80-
if f.Name == "cert" || f.Name == "key" {
80+
if f.Name == "cert" || f.Name == "key" || f.Name == "client-ca" {
8181
// Need the absolute value of the path
8282
value, err = filepath.Abs(value)
8383
if err != nil {

cmd/git-bundle-web-server/bundle-server.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"context"
55
"crypto/tls"
6+
"crypto/x509"
67
"fmt"
78
"net/http"
89
"os"
@@ -32,7 +33,8 @@ func NewBundleWebServer(logger log.TraceLogger,
3233
port string,
3334
certFile string, keyFile string,
3435
tlsMinVersion uint16,
35-
) *bundleWebServer {
36+
clientCAFile string,
37+
) (*bundleWebServer, error) {
3638
bundleServer := &bundleWebServer{
3739
logger: logger,
3840
serverWaitGroup: &sync.WaitGroup{},
@@ -49,7 +51,7 @@ func NewBundleWebServer(logger log.TraceLogger,
4951
// No TLS configuration to be done, return
5052
if certFile == "" {
5153
bundleServer.listenAndServeFunc = func() error { return bundleServer.server.ListenAndServe() }
52-
return bundleServer
54+
return bundleServer, nil
5355
}
5456

5557
// Configure for TLS
@@ -59,7 +61,18 @@ func NewBundleWebServer(logger log.TraceLogger,
5961
bundleServer.server.TLSConfig = tlsConfig
6062
bundleServer.listenAndServeFunc = func() error { return bundleServer.server.ListenAndServeTLS(certFile, keyFile) }
6163

62-
return bundleServer
64+
if clientCAFile != "" {
65+
caBytes, err := os.ReadFile(clientCAFile)
66+
if err != nil {
67+
return nil, err
68+
}
69+
certPool := x509.NewCertPool()
70+
certPool.AppendCertsFromPEM(caBytes)
71+
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
72+
tlsConfig.ClientCAs = certPool
73+
}
74+
75+
return bundleServer, nil
6376
}
6477

6578
func (b *bundleWebServer) parseRoute(ctx context.Context, path string) (string, string, string, error) {

cmd/git-bundle-web-server/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,18 @@ func main() {
2727
cert := utils.GetFlagValue[string](parser, "cert")
2828
key := utils.GetFlagValue[string](parser, "key")
2929
tlsMinVersion := utils.GetFlagValue[uint16](parser, "tls-version")
30+
clientCA := utils.GetFlagValue[string](parser, "client-ca")
3031

3132
// Configure the server
32-
bundleServer := NewBundleWebServer(logger,
33+
bundleServer, err := NewBundleWebServer(logger,
3334
port,
3435
cert, key,
3536
tlsMinVersion,
37+
clientCA,
3638
)
39+
if err != nil {
40+
logger.Fatal(ctx, err)
41+
}
3742

3843
// Start the server asynchronously
3944
bundleServer.StartServerAsync(ctx)

cmd/utils/common-args.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func WebServerFlags(parser argParser) (*flag.FlagSet, func(context.Context)) {
8585
key := f.String("key", "", "The path to the certificate's private key")
8686
tlsVersion := tlsVersionValue(tls.VersionTLS12)
8787
f.Var(&tlsVersion, "tls-version", "The minimum TLS version the server will accept")
88+
f.String("client-ca", "", "The path to the client authentication certificate authority PEM")
8889

8990
// Function to call for additional arg validation (may exit with 'Usage()')
9091
validationFunc := func(ctx context.Context) {

docs/man/git-bundle-server.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,12 @@ Generate an incremental bundle with latest updates to rust-lang/rust:
163163
$ git-bundle-server update rust-lang/rust
164164
----
165165

166-
Start an HTTPS web server on port 443 with certificate 'server.crt' and private
167-
key 'server.key':
166+
Start an HTTPS web server with mTLS authentication (given files 'ca.pem',
167+
'server.crt', 'server.key'):
168168

169169
[source,console]
170170
----
171-
$ git-bundle-server web-server start --force --port 443 --cert server.crt --key server.key
171+
$ git-bundle-server web-server start --force --port 443 --client-ca ca.pem --cert server.crt --key server.key
172172
----
173173

174174
== SEE ALSO

docs/man/server-options.asc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@
2323
+
2424
These strings match those used in the *http.sslVersion* Git config setting
2525
(see man:git-config[1]). The default value is *tlsv1.2*. If the server is not
26-
configured for TLS, this option is a no-op.
26+
configured for TLS, this option is a no-op.
27+
28+
*--client-ca* _path_:::
29+
Require that requests to the bundle server include a client certificate that
30+
can be validated by the certificate authority file at the specified _path_.
31+
No-op if *--cert* and *--key* are not configured.

0 commit comments

Comments
 (0)