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.
This commit is contained in:
Konrad Wojas 2018-03-24 20:13:13 +08:00 committed by Zlatko Čalušić
parent 02196a18d8
commit 4d2493388a
4 changed files with 72 additions and 13 deletions

View file

@ -67,6 +67,7 @@ Flags:
-h, --help help for rest-server -h, --help help for rest-server
--listen string listen address (default ":8000") --listen string listen address (default ":8000")
--log string log HTTP requests in the combined log format --log string log HTTP requests in the combined log format
--no-auth disable .htpasswd authentication
--path string data directory (default "/tmp/restic") --path string data directory (default "/tmp/restic")
--private-repos users can only access their private repo --private-repos users can only access their private repo
--prometheus enable Prometheus metrics --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. 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 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`. 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: 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:

View file

@ -35,6 +35,7 @@ func init() {
flags.BoolVar(&restserver.Config.TLS, "tls", restserver.Config.TLS, "turn on TLS support") 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.TLSCert, "tls-cert", restserver.Config.TLSCert, "TLS certificate path")
flags.StringVar(&restserver.Config.TLSKey, "tls-key", restserver.Config.TLSKey, "TLS key 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.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.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") 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 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 { func runRoot(cmd *cobra.Command, args []string) error {
if restserver.Config.Version { if restserver.Config.Version {
fmt.Printf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH) 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() defer pprof.StopCPUProfile()
} }
mux := restserver.NewMux() handler, err := getHandler()
var handler http.Handler
htpasswdFile, err := restserver.NewHtpasswdFromFile(filepath.Join(restserver.Config.Path, ".htpasswd"))
if err != nil { if err != nil {
handler = mux log.Fatalf("error: %v", err)
log.Println("Authentication disabled")
} else {
handler = restserver.AuthHandler(htpasswdFile, mux)
log.Println("Authentication enabled")
} }
if restserver.Config.PrivateRepos { if restserver.Config.PrivateRepos {
@ -122,6 +131,7 @@ func runRoot(cmd *cobra.Command, args []string) error {
return err return err
} }
func main() { func main() {
if err := cmdRoot.Execute(); err != nil { if err := cmdRoot.Execute(); err != nil {
log.Fatalf("error: %v", err) log.Fatalf("error: %v", err)

View file

@ -1,6 +1,9 @@
package main package main
import ( import (
"io/ioutil"
"os"
"path/filepath"
"testing" "testing"
restserver "github.com/restic/rest-server" 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)
}
}

1
mux.go
View file

@ -21,6 +21,7 @@ var Config = struct {
TLSKey string TLSKey string
TLSCert string TLSCert string
TLS bool TLS bool
NoAuth bool
AppendOnly bool AppendOnly bool
PrivateRepos bool PrivateRepos bool
Prometheus bool Prometheus bool