diff --git a/README.md b/README.md index dc27d7c..ef8e96e 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Usage: rest-server [flags] Flags: + --append-only Enable append only mode --cpuprofile string write CPU profile to file --debug output debug messages -h, --help help for rest-server @@ -88,6 +89,9 @@ keys with the following commands: % openssl req -new -x509 -key private_key -out public_key -days 365 ``` +Append only mode allows creation of new backups but prevents deletion and modification of existing backups. This +can be useful when backing up systems that have a potential of being hacked. + Rest Server uses exactly the same directory structure as local backend, so you should be able to access it both locally and via HTTP, even simultaneously. diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index c18523b..6cf0da0 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -29,6 +29,7 @@ func init() { flags.StringVar(&restserver.Config.Log, "log", restserver.Config.Log, "log HTTP requests in the combined log format") flags.StringVar(&restserver.Config.Path, "path", restserver.Config.Path, "data directory") flags.BoolVar(&restserver.Config.TLS, "tls", restserver.Config.TLS, "turn on TLS support") + flags.BoolVar(&restserver.Config.AppendOnly, "append-only", restserver.Config.AppendOnly, "Enable append only mode") } var version = "manually" diff --git a/handlers.go b/handlers.go index d56b966..4aceb4a 100644 --- a/handlers.go +++ b/handlers.go @@ -194,6 +194,12 @@ func DeleteConfig(w http.ResponseWriter, r *http.Request) { if Config.Debug { log.Println("DeleteConfig()") } + + if Config.AppendOnly { + http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) + return + } + cfg, err := getPath(r, "config") if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -372,6 +378,11 @@ func DeleteBlob(w http.ResponseWriter, r *http.Request) { log.Println("DeleteBlob()") } + if Config.AppendOnly && pat.Param(r, "type") != "locks" { + http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) + return + } + path, err := getFilePath(r, pat.Param(r, "type"), pat.Param(r, "name")) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/mux.go b/mux.go index f67397f..7a916c2 100644 --- a/mux.go +++ b/mux.go @@ -18,9 +18,11 @@ var Config = struct { Log string CPUProfile string Debug bool + AppendOnly bool }{ Path: "/tmp/restic", Listen: ":8000", + AppendOnly: false, } func debugHandler(next http.Handler) http.Handler {