From ddc5ad1a3c69aff9e9b3377e5b9348fd6410a0da Mon Sep 17 00:00:00 2001 From: darkspir Date: Thu, 5 Dec 2024 12:58:02 +0100 Subject: [PATCH 1/5] handlers.go: Added parameter for TLS min version rest-server/main.go: Added parameter handling for TLS min version rest-server/main.go: Added crypto.tls, implemented and configured tlsConfig object --- cmd/rest-server/main.go | 32 ++++++++++++++++++++++++++++++++ handlers.go | 1 + 2 files changed, 33 insertions(+) diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index 0bdd1eb..bedd608 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -7,6 +7,7 @@ import ( "log" "net" "net/http" + "crypto/tls" "os" "os/signal" "path/filepath" @@ -47,6 +48,7 @@ func newRestServerApp() *restServerApp { Server: restserver.Server{ Path: filepath.Join(os.TempDir(), "restic"), Listen: ":8000", + TLSMinVer: "1.2", }, } rv.CmdRoot.RunE = rv.runRoot @@ -61,6 +63,7 @@ func newRestServerApp() *restServerApp { flags.BoolVar(&rv.Server.TLS, "tls", rv.Server.TLS, "turn on TLS support") flags.StringVar(&rv.Server.TLSCert, "tls-cert", rv.Server.TLSCert, "TLS certificate path") flags.StringVar(&rv.Server.TLSKey, "tls-key", rv.Server.TLSKey, "TLS key path") + flags.StringVar(&rv.Server.TLSMinVer, "tls-min-ver", rv.Server.TLSMinVer, "TLS min version (default: 1.2)") flags.BoolVar(&rv.Server.NoAuth, "no-auth", rv.Server.NoAuth, "disable .htpasswd authentication") flags.StringVar(&rv.Server.HtpasswdPath, "htpasswd-file", rv.Server.HtpasswdPath, "location of .htpasswd file (default: \"/.htpasswd)\"") flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload, @@ -162,8 +165,37 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error { app.listenerAddress = listener.Addr() app.listenerAddressMu.Unlock() + tlscfg := &tls.Config{ + MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }, + } + switch app.Server.TLSMinVer { + case "1.0": + // Only available with GODEBUG="tls10server=1" + tlscfg.MinVersion = tls.VersionTLS10 + case "1.1": + // Only available with GODEBUG="tls10server=1" + tlscfg.MinVersion = tls.VersionTLS11 + case "1.2": + tlscfg.MinVersion = tls.VersionTLS12 + case "1.3": + tlscfg.MinVersion = tls.VersionTLS13 + default: + tlscfg.MinVersion = tls.VersionTLS12 + } + + srv := &http.Server{ Handler: handler, + TLSConfig: tlscfg, } // run server in background diff --git a/handlers.go b/handlers.go index 8163ccf..7d78b61 100644 --- a/handlers.go +++ b/handlers.go @@ -22,6 +22,7 @@ type Server struct { CPUProfile string TLSKey string TLSCert string + TLSMinVer string TLS bool NoAuth bool AppendOnly bool From 250bcb5e2d7469ec88ffc777ee96aeeae618c3ea Mon Sep 17 00:00:00 2001 From: DarkSpir Date: Thu, 5 Dec 2024 13:23:57 +0100 Subject: [PATCH 2/5] tls min version parameter documentation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d4fa296..d09340a 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Flags: --tls turn on TLS support --tls-cert string TLS certificate path --tls-key string TLS key path + --tls-min-ver string TLS min version (default: 1.2) (default "1.2") -v, --version version for rest-server ``` @@ -68,7 +69,7 @@ If you want to disable authentication, you must add the `--no-auth` flag. If thi NOTE: In older versions of rest-server (up to 0.9.7), this flag does not exist and the server disables authentication if `.htpasswd` is missing or cannot be opened. -By default the server uses HTTP protocol. This is not very secure since with Basic Authentication, user name and passwords will be sent in clear text in every request. In order to enable TLS support just add the `--tls` argument and add a private and public key at the root of your persistence directory. You may also specify private and public keys by `--tls-cert` and `--tls-key`. +By default the server uses HTTP protocol. This is not very secure since with Basic Authentication, user name and passwords will be sent in clear text in every request. In order to enable TLS support just add the `--tls` argument and add a private and public key at the root of your persistence directory. You may also specify private and public keys by `--tls-cert` and `--tls-key` and set the minimum TLS version by `--tls-min-ver`. Signed certificate is normally required by the restic backend, but if you just want to test the feature you can generate password-less unsigned keys with the following command: From 3fa600b4d4c7488c9682fd4d9e085b99998e0f95 Mon Sep 17 00:00:00 2001 From: darkspir Date: Thu, 5 Dec 2024 13:53:34 +0100 Subject: [PATCH 3/5] Style correction of previous commits according to gofmt output --- cmd/rest-server/main.go | 17 ++++++++--------- handlers.go | 3 ++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index bedd608..df927ed 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -2,12 +2,12 @@ package main import ( "context" + "crypto/tls" "errors" "fmt" "log" "net" "net/http" - "crypto/tls" "os" "os/signal" "path/filepath" @@ -46,8 +46,8 @@ func newRestServerApp() *restServerApp { Version: fmt.Sprintf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH), }, Server: restserver.Server{ - Path: filepath.Join(os.TempDir(), "restic"), - Listen: ":8000", + Path: filepath.Join(os.TempDir(), "restic"), + Listen: ":8000", TLSMinVer: "1.2", }, } @@ -63,7 +63,7 @@ func newRestServerApp() *restServerApp { flags.BoolVar(&rv.Server.TLS, "tls", rv.Server.TLS, "turn on TLS support") flags.StringVar(&rv.Server.TLSCert, "tls-cert", rv.Server.TLSCert, "TLS certificate path") flags.StringVar(&rv.Server.TLSKey, "tls-key", rv.Server.TLSKey, "TLS key path") - flags.StringVar(&rv.Server.TLSMinVer, "tls-min-ver", rv.Server.TLSMinVer, "TLS min version (default: 1.2)") + flags.StringVar(&rv.Server.TLSMinVer, "tls-min-ver", rv.Server.TLSMinVer, "TLS min version (default: 1.2)") flags.BoolVar(&rv.Server.NoAuth, "no-auth", rv.Server.NoAuth, "disable .htpasswd authentication") flags.StringVar(&rv.Server.HtpasswdPath, "htpasswd-file", rv.Server.HtpasswdPath, "location of .htpasswd file (default: \"/.htpasswd)\"") flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload, @@ -166,9 +166,9 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error { app.listenerAddressMu.Unlock() tlscfg := &tls.Config{ - MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, - CipherSuites: []uint16{ + MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, @@ -192,9 +192,8 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error { tlscfg.MinVersion = tls.VersionTLS12 } - srv := &http.Server{ - Handler: handler, + Handler: handler, TLSConfig: tlscfg, } diff --git a/handlers.go b/handlers.go index 7d78b61..ddd4e4f 100644 --- a/handlers.go +++ b/handlers.go @@ -159,7 +159,8 @@ func join(base string, names ...string) (string, error) { // splitURLPath splits the URL path into a folderPath of the subrepo, and // a remainder that can be passed to repo.Handler. // Example: /foo/bar/locks/0123... will be split into: -// ["foo", "bar"] and "/locks/0123..." +// +// ["foo", "bar"] and "/locks/0123..." func splitURLPath(urlPath string, maxDepth int) (folderPath []string, remainder string) { if !strings.HasPrefix(urlPath, "/") { // Really should start with "/" From ba80de0de0517fccf6d85e3f4b2d4572d85f7be0 Mon Sep 17 00:00:00 2001 From: darkspir Date: Thu, 5 Dec 2024 14:02:29 +0100 Subject: [PATCH 4/5] Added changelog documentation --- changelog/unreleased/pull-315 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/pull-315 diff --git a/changelog/unreleased/pull-315 b/changelog/unreleased/pull-315 new file mode 100644 index 0000000..9b36679 --- /dev/null +++ b/changelog/unreleased/pull-315 @@ -0,0 +1,6 @@ +Enhancement: Hardened tls settings + +rest-server now uses a secure tls cipher suit set and the minimal TLS version +can be set with the option `--tls-min-ver` + +https://github.com/restic/rest-server/pull/315 From 99a88c3bf27b6c37fa618056fdea7c6a41450d3f Mon Sep 17 00:00:00 2001 From: darkspir Date: Sat, 8 Feb 2025 12:18:16 +0100 Subject: [PATCH 5/5] README.md: Fixed typo main.go: Added error for unknown TLS min versions main.go: Changed CurvePreferences in TLS config to Go default main.go: Removed handling for TLS min versions 1.0 and 1.1 Signed-off-by: darkspir --- README.md | 2 +- cmd/rest-server/main.go | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d09340a..12f6b18 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Flags: --tls turn on TLS support --tls-cert string TLS certificate path --tls-key string TLS key path - --tls-min-ver string TLS min version (default: 1.2) (default "1.2") + --tls-min-ver string TLS min version (default: 1.2) -v, --version version for rest-server ``` diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index df927ed..d1d2216 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -167,7 +167,6 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error { tlscfg := &tls.Config{ MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, @@ -178,18 +177,12 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error { }, } switch app.Server.TLSMinVer { - case "1.0": - // Only available with GODEBUG="tls10server=1" - tlscfg.MinVersion = tls.VersionTLS10 - case "1.1": - // Only available with GODEBUG="tls10server=1" - tlscfg.MinVersion = tls.VersionTLS11 case "1.2": tlscfg.MinVersion = tls.VersionTLS12 case "1.3": tlscfg.MinVersion = tls.VersionTLS13 default: - tlscfg.MinVersion = tls.VersionTLS12 + return fmt.Errorf("Unsupported TLS min version: %s", app.Server.TLSMinVer) } srv := &http.Server{