From 0626d79fcd2f118e7818b1e8233ac624d7665fb0 Mon Sep 17 00:00:00 2001 From: Konrad Wojas Date: Sat, 24 Mar 2018 20:13:13 +0800 Subject: [PATCH] Require auth by default, add --no-auth flag In order to prevent users from accidentally exposing rest-server without authentication, rest-server now defaults to requiring a .htpasswd. If you want to disable authentication, you need to explicitly pass the new --no-auth flag. --- README.md | 11 +++++---- cmd/rest-server/main.go | 28 ++++++++++++++-------- cmd/rest-server/main_test.go | 45 ++++++++++++++++++++++++++++++++++++ mux.go | 1 + 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 37318251..a5c2a8b1 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Flags: -h, --help help for rest-server --listen string listen address (default ":8000") --log string log HTTP requests in the combined log format + --no-auth disable .htpasswd authentication --path string data directory (default "/tmp/restic") --private-repos users can only access their private repo --prometheus enable Prometheus metrics @@ -77,20 +78,22 @@ Flags: ``` -By default the server persists backup data in `/tmp/restic`. Start the server with a custom persistence directory: +By default the server persists backup data in `/tmp/restic`. To start the server with a custom persistence directory and with authentication disabled: ``` -rest-server --path /user/home/backup +rest-server --path /user/home/backup --no-auth ``` To authenticate users (for access to the rest-server), the server supports using a `.htpasswd` file to specify users. You can create such a file at the root of the persistence directory by executing the following command (note that you need the `htpasswd` program from Apache's http-tools). In order to append new user to the file, just omit the `-c` argument. Only bcrypt and SHA encryption methods are supported, so use -B (very secure) or -s (insecure by today's standards) when adding/changing passwords. -NOTE: Without a valid `.htaccess` file, the server will not authenticate users (it prints "Authentication disabled upon startup"), in which case anyone who can access the server will be able to back up to it. - ``` htpasswd -B -c .htpasswd username ``` +If you want to disable authentication, you must add the `--no-auth` flag. If this flag is not specified and the `.htpasswd` cannot be opened, rest-server will refuse to start. + +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, username and passwords will travel in cleartext 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`. Signed certificate is required by the restic backend, but if you just want to test the feature you can generate unsigned keys with the following commands: diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index c1833b46..3b3b887e 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -35,6 +35,7 @@ func init() { flags.BoolVar(&restserver.Config.TLS, "tls", restserver.Config.TLS, "turn on TLS support") flags.StringVar(&restserver.Config.TLSCert, "tls-cert", restserver.Config.TLSCert, "TLS certificate path") flags.StringVar(&restserver.Config.TLSKey, "tls-key", restserver.Config.TLSKey, "TLS key path") + flags.BoolVar(&restserver.Config.NoAuth, "no-auth", restserver.Config.NoAuth, "disable .htpasswd authentication") flags.BoolVar(&restserver.Config.AppendOnly, "append-only", restserver.Config.AppendOnly, "enable append only mode") flags.BoolVar(&restserver.Config.PrivateRepos, "private-repos", restserver.Config.PrivateRepos, "users can only access their private repo") flags.BoolVar(&restserver.Config.Prometheus, "prometheus", restserver.Config.Prometheus, "enable Prometheus metrics") @@ -64,6 +65,21 @@ func tlsSettings() (bool, string, string, error) { return enabledTLS, key, cert, nil } +func getHandler() (http.Handler, error) { + mux := restserver.NewMux() + if restserver.Config.NoAuth { + log.Println("Authentication disabled") + return mux, nil + } + + log.Println("Authentication enabled") + htpasswdFile, err := restserver.NewHtpasswdFromFile(filepath.Join(restserver.Config.Path, ".htpasswd")) + if err != nil { + return nil, fmt.Errorf("cannot load .htpasswd (use --no-auth to disable): %v", err) + } + return restserver.AuthHandler(htpasswdFile, mux), nil +} + func runRoot(cmd *cobra.Command, args []string) error { if restserver.Config.Version { fmt.Printf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH) @@ -86,16 +102,9 @@ func runRoot(cmd *cobra.Command, args []string) error { defer pprof.StopCPUProfile() } - mux := restserver.NewMux() - - var handler http.Handler - htpasswdFile, err := restserver.NewHtpasswdFromFile(filepath.Join(restserver.Config.Path, ".htpasswd")) + handler, err := getHandler() if err != nil { - handler = mux - log.Println("Authentication disabled") - } else { - handler = restserver.AuthHandler(htpasswdFile, mux) - log.Println("Authentication enabled") + log.Fatalf("error: %v", err) } if restserver.Config.PrivateRepos { @@ -122,6 +131,7 @@ func runRoot(cmd *cobra.Command, args []string) error { return err } + func main() { if err := cmdRoot.Execute(); err != nil { log.Fatalf("error: %v", err) diff --git a/cmd/rest-server/main_test.go b/cmd/rest-server/main_test.go index b2a71e11..b379b1a3 100644 --- a/cmd/rest-server/main_test.go +++ b/cmd/rest-server/main_test.go @@ -1,6 +1,9 @@ package main import ( + "io/ioutil" + "os" + "path/filepath" "testing" restserver "github.com/restic/rest-server" @@ -71,3 +74,45 @@ func TestTLSSettings(t *testing.T) { }) } } + +func TestGetHandler(t *testing.T) { + // Save and restore config + defaultConfig := restserver.Config + defer func() { restserver.Config = defaultConfig }() + + dir, err := ioutil.TempDir("", "rest-server-test") + if err != nil { + t.Fatal(err) + } + defer os.Remove(dir) + restserver.Config.Path = dir + + // With NoAuth = false and no .htpasswd + restserver.Config.NoAuth = false // default + _, err = getHandler() + if err == nil { + t.Errorf("NoAuth=false: expected error, got nil") + } + + // With NoAuth = true and no .htpasswd + restserver.Config.NoAuth = true + _, err = getHandler() + if err != nil { + t.Errorf("NoAuth=true: expected no error, got %v", err) + } + + // Create .htpasswd + htpasswd := filepath.Join(dir, ".htpasswd") + err = ioutil.WriteFile(htpasswd, []byte(""), 0644) + if err != nil { + t.Fatal(err) + } + defer os.Remove(htpasswd) + + // With NoAuth = false and with .htpasswd + restserver.Config.NoAuth = false // default + _, err = getHandler() + if err != nil { + t.Errorf("NoAuth=false with .htpasswd: expected no error, got %v", err) + } +} diff --git a/mux.go b/mux.go index e122a619..ae2da649 100644 --- a/mux.go +++ b/mux.go @@ -21,6 +21,7 @@ var Config = struct { TLSKey string TLSCert string TLS bool + NoAuth bool AppendOnly bool PrivateRepos bool Prometheus bool