This commit is contained in:
DarkSpir
2025-02-08 12:18:32 +01:00
committed by GitHub
4 changed files with 38 additions and 5 deletions

View File

@@ -49,6 +49,7 @@ Flags:
--tls turn on TLS support --tls turn on TLS support
--tls-cert string TLS certificate path --tls-cert string TLS certificate path
--tls-key string TLS key path --tls-key string TLS key path
--tls-min-ver string TLS min version (default: 1.2)
-v, --version version for rest-server -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. 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: 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:

View File

@@ -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

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"log" "log"
@@ -45,8 +46,9 @@ func newRestServerApp() *restServerApp {
Version: fmt.Sprintf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH), Version: fmt.Sprintf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH),
}, },
Server: restserver.Server{ Server: restserver.Server{
Path: filepath.Join(os.TempDir(), "restic"), Path: filepath.Join(os.TempDir(), "restic"),
Listen: ":8000", Listen: ":8000",
TLSMinVer: "1.2",
}, },
} }
rv.CmdRoot.RunE = rv.runRoot 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.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.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.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.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: \"<data directory>/.htpasswd)\"") flags.StringVar(&rv.Server.HtpasswdPath, "htpasswd-file", rv.Server.HtpasswdPath, "location of .htpasswd file (default: \"<data directory>/.htpasswd)\"")
flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload, flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload,
@@ -162,8 +165,29 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error {
app.listenerAddress = listener.Addr() app.listenerAddress = listener.Addr()
app.listenerAddressMu.Unlock() app.listenerAddressMu.Unlock()
tlscfg := &tls.Config{
MinVersion: tls.VersionTLS12,
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.2":
tlscfg.MinVersion = tls.VersionTLS12
case "1.3":
tlscfg.MinVersion = tls.VersionTLS13
default:
return fmt.Errorf("Unsupported TLS min version: %s", app.Server.TLSMinVer)
}
srv := &http.Server{ srv := &http.Server{
Handler: handler, Handler: handler,
TLSConfig: tlscfg,
} }
// run server in background // run server in background

View File

@@ -22,6 +22,7 @@ type Server struct {
CPUProfile string CPUProfile string
TLSKey string TLSKey string
TLSCert string TLSCert string
TLSMinVer string
TLS bool TLS bool
NoAuth bool NoAuth bool
AppendOnly bool AppendOnly bool
@@ -158,7 +159,8 @@ func join(base string, names ...string) (string, error) {
// splitURLPath splits the URL path into a folderPath of the subrepo, and // splitURLPath splits the URL path into a folderPath of the subrepo, and
// a remainder that can be passed to repo.Handler. // a remainder that can be passed to repo.Handler.
// Example: /foo/bar/locks/0123... will be split into: // 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) { func splitURLPath(urlPath string, maxDepth int) (folderPath []string, remainder string) {
if !strings.HasPrefix(urlPath, "/") { if !strings.HasPrefix(urlPath, "/") {
// Really should start with "/" // Really should start with "/"