mirror of
https://github.com/restic/rest-server.git
synced 2025-12-07 09:36:13 -08:00
Compare commits
11 Commits
show
...
bcbd35bf4c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcbd35bf4c | ||
|
|
f15b03ae9d | ||
|
|
2513a698f3 | ||
|
|
92216a10ba | ||
|
|
bdfb047edf | ||
|
|
e35c6e39d9 | ||
|
|
da5bb66030 | ||
|
|
664d997006 | ||
|
|
5d7b581db6 | ||
|
|
37b4327012 | ||
|
|
238eceb2a1 |
@@ -79,13 +79,13 @@ archives:
|
|||||||
# add these files to all archives
|
# add these files to all archives
|
||||||
files:
|
files:
|
||||||
- src: LICENSE
|
- src: LICENSE
|
||||||
dst: .
|
dst: LICENSE
|
||||||
info: *archive_file_info
|
info: *archive_file_info
|
||||||
- src: README.md
|
- src: README.md
|
||||||
dst: .
|
dst: README.md
|
||||||
info: *archive_file_info
|
info: *archive_file_info
|
||||||
- src: CHANGELOG.md
|
- src: CHANGELOG.md
|
||||||
dst: .
|
dst: CHANGELOG.md
|
||||||
info: *archive_file_info
|
info: *archive_file_info
|
||||||
|
|
||||||
# also build an archive of the source code
|
# also build an archive of the source code
|
||||||
|
|||||||
285
CHANGELOG.md
285
CHANGELOG.md
@@ -1,3 +1,65 @@
|
|||||||
|
Changelog for rest-server 0.13.0 (2024-07-26)
|
||||||
|
============================================
|
||||||
|
|
||||||
|
The following sections list the changes in rest-server 0.13.0 relevant
|
||||||
|
to users. The changes are ordered by importance.
|
||||||
|
|
||||||
|
Summary
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Chg #267: Update dependencies and require Go 1.18 or newer
|
||||||
|
* Chg #273: Shut down cleanly on TERM and INT signals
|
||||||
|
* Enh #271: Print listening address after start-up
|
||||||
|
* Enh #272: Support listening on a unix socket
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Change #267: Update dependencies and require Go 1.18 or newer
|
||||||
|
|
||||||
|
Most dependencies have been updated. Since some libraries require newer language
|
||||||
|
features, support for Go 1.17 has been dropped, which means that rest-server now
|
||||||
|
requires at least Go 1.18 to build.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/267
|
||||||
|
|
||||||
|
* Change #273: Shut down cleanly on TERM and INT signals
|
||||||
|
|
||||||
|
Rest-server now listens for TERM and INT signals and cleanly closes down the
|
||||||
|
http.Server and listener when receiving either of them.
|
||||||
|
|
||||||
|
This is particularly useful when listening on a unix socket, as the server will
|
||||||
|
now remove the socket file when it shuts down.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/273
|
||||||
|
|
||||||
|
* Enhancement #271: Print listening address after start-up
|
||||||
|
|
||||||
|
When started with `--listen :0`, rest-server would print `start server on :0`
|
||||||
|
|
||||||
|
The message now also includes the actual address listened on, for example `start
|
||||||
|
server on 0.0.0.0:37333`. This is useful when starting a server with an
|
||||||
|
auto-allocated free port number (port 0).
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/271
|
||||||
|
|
||||||
|
* Enhancement #272: Support listening on a unix socket
|
||||||
|
|
||||||
|
It is now possible to make rest-server listen on a unix socket by prefixing the
|
||||||
|
socket filename with `unix:` and passing it to the `--listen` option, for
|
||||||
|
example `--listen unix:/tmp/foo`.
|
||||||
|
|
||||||
|
This is useful in combination with remote port forwarding to enable a remote
|
||||||
|
server to backup locally, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
|
rest-server --listen unix:/tmp/foo &
|
||||||
|
ssh -R /tmp/foo:/tmp/foo user@host restic -r rest:http+unix:///tmp/foo:/repo backup
|
||||||
|
```
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/272
|
||||||
|
|
||||||
|
|
||||||
Changelog for rest-server 0.12.1 (2023-07-09)
|
Changelog for rest-server 0.12.1 (2023-07-09)
|
||||||
============================================
|
============================================
|
||||||
|
|
||||||
@@ -16,33 +78,34 @@ Details
|
|||||||
|
|
||||||
* Bugfix #230: Fix erroneous warnings about unsupported fsync
|
* Bugfix #230: Fix erroneous warnings about unsupported fsync
|
||||||
|
|
||||||
Due to a regression in rest-server 0.12.0, it continuously printed `WARNING: fsync is not
|
Due to a regression in rest-server 0.12.0, it continuously printed `WARNING:
|
||||||
supported by the data storage. This can lead to data loss, if the system crashes or the storage is
|
fsync is not supported by the data storage. This can lead to data loss, if the
|
||||||
unexpectedly disconnected.` for systems that support fsync. We have fixed the warning.
|
system crashes or the storage is unexpectedly disconnected.` for systems that
|
||||||
|
support fsync. We have fixed the warning.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/230
|
https://github.com/restic/rest-server/issues/230
|
||||||
https://github.com/restic/rest-server/pull/231
|
https://github.com/restic/rest-server/pull/231
|
||||||
|
|
||||||
* Bugfix #238: API: Return empty array when listing empty folders
|
* Bugfix #238: API: Return empty array when listing empty folders
|
||||||
|
|
||||||
Rest-server returned `null` when listing an empty folder. This has been changed to returning
|
Rest-server returned `null` when listing an empty folder. This has been changed
|
||||||
an empty array in accordance with the REST protocol specification. This change has no impact on
|
to returning an empty array in accordance with the REST protocol specification.
|
||||||
restic users.
|
This change has no impact on restic users.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/238
|
https://github.com/restic/rest-server/issues/238
|
||||||
https://github.com/restic/rest-server/pull/239
|
https://github.com/restic/rest-server/pull/239
|
||||||
|
|
||||||
* Enhancement #217: Log to stdout using the `--log -` option
|
* Enhancement #217: Log to stdout using the `--log -` option
|
||||||
|
|
||||||
Logging to stdout was possible using `--log /dev/stdout`. However, when the rest server is run
|
Logging to stdout was possible using `--log /dev/stdout`. However, when the rest
|
||||||
as a different user, for example, using
|
server is run as a different user, for example, using
|
||||||
|
|
||||||
`sudo -u restic rest-server [...] --log /dev/stdout`
|
`sudo -u restic rest-server [...] --log /dev/stdout`
|
||||||
|
|
||||||
This did not work due to permission issues.
|
This did not work due to permission issues.
|
||||||
|
|
||||||
For logging to stdout, the `--log` option now supports the special filename `-` which also
|
For logging to stdout, the `--log` option now supports the special filename `-`
|
||||||
works in these cases.
|
which also works in these cases.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/217
|
https://github.com/restic/rest-server/pull/217
|
||||||
|
|
||||||
@@ -69,68 +132,69 @@ Details
|
|||||||
|
|
||||||
* Bugfix #183: Allow usernames containing underscore and more
|
* Bugfix #183: Allow usernames containing underscore and more
|
||||||
|
|
||||||
The security fix in rest-server 0.11.0 (#131) disallowed usernames containing and
|
The security fix in rest-server 0.11.0 (#131) disallowed usernames containing
|
||||||
underscore "_". The list of allowed characters has now been changed to include Unicode
|
and underscore "_". The list of allowed characters has now been changed to
|
||||||
characters, numbers, "_", "-", "." and "@".
|
include Unicode characters, numbers, "_", "-", "." and "@".
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/183
|
https://github.com/restic/rest-server/issues/183
|
||||||
https://github.com/restic/rest-server/pull/184
|
https://github.com/restic/rest-server/pull/184
|
||||||
|
|
||||||
* Bugfix #219: Ignore unexpected files in the data/ folder
|
* Bugfix #219: Ignore unexpected files in the data/ folder
|
||||||
|
|
||||||
If the data folder of a repository contained files, this would prevent restic from retrieving a
|
If the data folder of a repository contained files, this would prevent restic
|
||||||
list of file data files. This has been fixed. As a workaround remove the files that are directly
|
from retrieving a list of file data files. This has been fixed. As a workaround
|
||||||
contained in the data folder (e.g., `.DS_Store` files).
|
remove the files that are directly contained in the data folder (e.g.,
|
||||||
|
`.DS_Store` files).
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/219
|
https://github.com/restic/rest-server/issues/219
|
||||||
https://github.com/restic/rest-server/pull/221
|
https://github.com/restic/rest-server/pull/221
|
||||||
|
|
||||||
* Bugfix #1871: Return 500 "Internal server error" if files cannot be read
|
* Bugfix #1871: Return 500 "Internal server error" if files cannot be read
|
||||||
|
|
||||||
When files in a repository cannot be read by rest-server, for example after running `restic
|
When files in a repository cannot be read by rest-server, for example after
|
||||||
prune` directly on the server hosting the repositories in a way that causes filesystem
|
running `restic prune` directly on the server hosting the repositories in a way
|
||||||
permissions to be wrong, rest-server previously returned 404 "Not Found" as status code. This
|
that causes filesystem permissions to be wrong, rest-server previously returned
|
||||||
was causing confusing for users.
|
404 "Not Found" as status code. This was causing confusing for users.
|
||||||
|
|
||||||
The error handling has now been fixed to only return 404 "Not Found" if the file actually does not
|
The error handling has now been fixed to only return 404 "Not Found" if the file
|
||||||
exist. Otherwise a 500 "Internal server error" is reported to the client and the underlying
|
actually does not exist. Otherwise a 500 "Internal server error" is reported to
|
||||||
error is logged at the server side.
|
the client and the underlying error is logged at the server side.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/1871
|
https://github.com/restic/rest-server/issues/1871
|
||||||
https://github.com/restic/rest-server/pull/195
|
https://github.com/restic/rest-server/pull/195
|
||||||
|
|
||||||
* Change #207: Return error if command-line arguments are specified
|
* Change #207: Return error if command-line arguments are specified
|
||||||
|
|
||||||
Command line arguments are ignored by rest-server, but there was previously no indication of
|
Command line arguments are ignored by rest-server, but there was previously no
|
||||||
this when they were supplied anyway.
|
indication of this when they were supplied anyway.
|
||||||
|
|
||||||
To prevent usage errors an error is now printed when command line arguments are supplied,
|
To prevent usage errors an error is now printed when command line arguments are
|
||||||
instead of them being silently ignored.
|
supplied, instead of them being silently ignored.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/207
|
https://github.com/restic/rest-server/pull/207
|
||||||
|
|
||||||
* Change #208: Update dependencies and require Go 1.17 or newer
|
* Change #208: Update dependencies and require Go 1.17 or newer
|
||||||
|
|
||||||
Most dependencies have been updated. Since some libraries require newer language features,
|
Most dependencies have been updated. Since some libraries require newer language
|
||||||
support for Go 1.15-1.16 has been dropped, which means that rest-server now requires at least
|
features, support for Go 1.15-1.16 has been dropped, which means that
|
||||||
Go 1.17 to build.
|
rest-server now requires at least Go 1.17 to build.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/208
|
https://github.com/restic/rest-server/pull/208
|
||||||
|
|
||||||
* Enhancement #133: Cache basic authentication credentials
|
* Enhancement #133: Cache basic authentication credentials
|
||||||
|
|
||||||
To speed up the verification of basic auth credentials, rest-server now caches passwords for a
|
To speed up the verification of basic auth credentials, rest-server now caches
|
||||||
minute in memory. That way the expensive verification of basic auth credentials can be skipped
|
passwords for a minute in memory. That way the expensive verification of basic
|
||||||
for most requests issued by a single restic run. The password is kept in memory in a hashed form
|
auth credentials can be skipped for most requests issued by a single restic run.
|
||||||
and not as plaintext.
|
The password is kept in memory in a hashed form and not as plaintext.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/133
|
https://github.com/restic/rest-server/issues/133
|
||||||
https://github.com/restic/rest-server/pull/138
|
https://github.com/restic/rest-server/pull/138
|
||||||
|
|
||||||
* Enhancement #187: Allow configurable location for `.htpasswd` file
|
* Enhancement #187: Allow configurable location for `.htpasswd` file
|
||||||
|
|
||||||
It is now possible to specify the location of the `.htpasswd` file using the `--htpasswd-file`
|
It is now possible to specify the location of the `.htpasswd` file using the
|
||||||
option.
|
`--htpasswd-file` option.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/187
|
https://github.com/restic/rest-server/issues/187
|
||||||
https://github.com/restic/rest-server/pull/188
|
https://github.com/restic/rest-server/pull/188
|
||||||
@@ -161,11 +225,12 @@ Details
|
|||||||
|
|
||||||
* Security #131: Prevent loading of usernames containing a slash
|
* Security #131: Prevent loading of usernames containing a slash
|
||||||
|
|
||||||
"/" is valid char in HTTP authorization headers, but is also used in rest-server to map
|
"/" is valid char in HTTP authorization headers, but is also used in rest-server
|
||||||
usernames to private repos.
|
to map usernames to private repos.
|
||||||
|
|
||||||
This commit prevents loading maliciously composed usernames like "/foo/config" by
|
This commit prevents loading maliciously composed usernames like "/foo/config"
|
||||||
restricting the allowed characters to the unicode character class, numbers, "-", "." and "@".
|
by restricting the allowed characters to the unicode character class, numbers,
|
||||||
|
"-", "." and "@".
|
||||||
|
|
||||||
This prevents requests to other users files like:
|
This prevents requests to other users files like:
|
||||||
|
|
||||||
@@ -177,64 +242,71 @@ Details
|
|||||||
|
|
||||||
* Bugfix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION`
|
* Bugfix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION`
|
||||||
|
|
||||||
Rest-server 0.10.0 introduced a regression which caused the `DISABLE_AUTHENTICATION`
|
Rest-server 0.10.0 introduced a regression which caused the
|
||||||
environment variable to stop working for the Docker container. This has been fixed by
|
`DISABLE_AUTHENTICATION` environment variable to stop working for the Docker
|
||||||
automatically setting the option `--no-auth` to disable authentication.
|
container. This has been fixed by automatically setting the option `--no-auth`
|
||||||
|
to disable authentication.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/119
|
https://github.com/restic/rest-server/issues/119
|
||||||
https://github.com/restic/rest-server/pull/124
|
https://github.com/restic/rest-server/pull/124
|
||||||
|
|
||||||
* Bugfix #142: Fix possible data loss due to interrupted network connections
|
* Bugfix #142: Fix possible data loss due to interrupted network connections
|
||||||
|
|
||||||
When rest-server was run without `--append-only` it was possible to lose uploaded files in a
|
When rest-server was run without `--append-only` it was possible to lose
|
||||||
specific scenario in which a network connection was interrupted.
|
uploaded files in a specific scenario in which a network connection was
|
||||||
|
interrupted.
|
||||||
|
|
||||||
For the data loss to occur a file upload by restic would have to be interrupted such that restic
|
For the data loss to occur a file upload by restic would have to be interrupted
|
||||||
notices the interrupted network connection before the rest-server. Then restic would have to
|
such that restic notices the interrupted network connection before the
|
||||||
retry the file upload and finish it before the rest-server notices that the initial upload has
|
rest-server. Then restic would have to retry the file upload and finish it
|
||||||
failed. Then the uploaded file would be accidentally removed by rest-server when trying to
|
before the rest-server notices that the initial upload has failed. Then the
|
||||||
|
uploaded file would be accidentally removed by rest-server when trying to
|
||||||
cleanup the failed upload.
|
cleanup the failed upload.
|
||||||
|
|
||||||
This has been fixed by always uploading to a temporary file first which is moved in position only
|
This has been fixed by always uploading to a temporary file first which is moved
|
||||||
once it was uploaded completely.
|
in position only once it was uploaded completely.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/142
|
https://github.com/restic/rest-server/pull/142
|
||||||
|
|
||||||
* Bugfix #155: Reply "insufficient storage" on disk full or over-quota
|
* Bugfix #155: Reply "insufficient storage" on disk full or over-quota
|
||||||
|
|
||||||
When there was no space left on disk, or any other write-related error occurred, rest-server
|
When there was no space left on disk, or any other write-related error occurred,
|
||||||
replied with HTTP status code 400 (Bad request). This is misleading (restic client will dump
|
rest-server replied with HTTP status code 400 (Bad request). This is misleading
|
||||||
the status code to the user).
|
(restic client will dump the status code to the user).
|
||||||
|
|
||||||
Rest-server now replies with two different status codes in these situations: * HTTP 507
|
Rest-server now replies with two different status codes in these situations: *
|
||||||
"Insufficient storage" is the status on disk full or repository over-quota * HTTP 500
|
HTTP 507 "Insufficient storage" is the status on disk full or repository
|
||||||
"Internal server error" is used for other disk-related errors
|
over-quota * HTTP 500 "Internal server error" is used for other disk-related
|
||||||
|
errors
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/155
|
https://github.com/restic/rest-server/issues/155
|
||||||
https://github.com/restic/rest-server/pull/160
|
https://github.com/restic/rest-server/pull/160
|
||||||
|
|
||||||
* Bugfix #157: Use platform-specific temporary directory as default data directory
|
* Bugfix #157: Use platform-specific temporary directory as default data directory
|
||||||
|
|
||||||
If no data directory is specificed, then rest-server now uses the Go standard library
|
If no data directory is specificed, then rest-server now uses the Go standard
|
||||||
functions to retrieve the standard temporary directory path for the current platform.
|
library functions to retrieve the standard temporary directory path for the
|
||||||
|
current platform.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/157
|
https://github.com/restic/rest-server/issues/157
|
||||||
https://github.com/restic/rest-server/pull/158
|
https://github.com/restic/rest-server/pull/158
|
||||||
|
|
||||||
* Change #112: Add subrepo support and refactor server code
|
* Change #112: Add subrepo support and refactor server code
|
||||||
|
|
||||||
Support for multi-level repositories has been added, so now each user can have its own
|
Support for multi-level repositories has been added, so now each user can have
|
||||||
subrepositories. This feature is always enabled.
|
its own subrepositories. This feature is always enabled.
|
||||||
|
|
||||||
Authentication for the Prometheus /metrics endpoint can now be disabled with the new
|
Authentication for the Prometheus /metrics endpoint can now be disabled with the
|
||||||
`--prometheus-no-auth` flag.
|
new `--prometheus-no-auth` flag.
|
||||||
|
|
||||||
We have split out all HTTP handling to a separate `repo` subpackage to cleanly separate the
|
We have split out all HTTP handling to a separate `repo` subpackage to cleanly
|
||||||
server code from the code that handles a single repository. The new RepoHandler also makes it
|
separate the server code from the code that handles a single repository. The new
|
||||||
easier to reuse rest-server as a Go component in any other HTTP server.
|
RepoHandler also makes it easier to reuse rest-server as a Go component in any
|
||||||
|
other HTTP server.
|
||||||
|
|
||||||
The refactoring makes the code significantly easier to follow and understand, which in turn
|
The refactoring makes the code significantly easier to follow and understand,
|
||||||
makes it easier to add new features, audit for security and debug issues.
|
which in turn makes it easier to add new features, audit for security and debug
|
||||||
|
issues.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/109
|
https://github.com/restic/rest-server/issues/109
|
||||||
https://github.com/restic/rest-server/issues/107
|
https://github.com/restic/rest-server/issues/107
|
||||||
@@ -242,27 +314,28 @@ Details
|
|||||||
|
|
||||||
* Change #146: Build rest-server at docker container build time
|
* Change #146: Build rest-server at docker container build time
|
||||||
|
|
||||||
The Dockerfile now includes a build stage such that the latest rest-server is always built and
|
The Dockerfile now includes a build stage such that the latest rest-server is
|
||||||
packaged. This is done in a standard golang container to ensure a clean build environment and
|
always built and packaged. This is done in a standard golang container to ensure
|
||||||
only the final binary is shipped rather than the whole build environment.
|
a clean build environment and only the final binary is shipped rather than the
|
||||||
|
whole build environment.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/146
|
https://github.com/restic/rest-server/issues/146
|
||||||
https://github.com/restic/rest-server/pull/145
|
https://github.com/restic/rest-server/pull/145
|
||||||
|
|
||||||
* Enhancement #122: Verify uploaded files
|
* Enhancement #122: Verify uploaded files
|
||||||
|
|
||||||
The rest-server now by default verifies that the hash of content of uploaded files matches
|
The rest-server now by default verifies that the hash of content of uploaded
|
||||||
their filename. This ensures that transmission errors are detected and forces restic to retry
|
files matches their filename. This ensures that transmission errors are detected
|
||||||
the upload. On low-power devices it can make sense to disable this check by passing the
|
and forces restic to retry the upload. On low-power devices it can make sense to
|
||||||
`--no-verify-upload` flag.
|
disable this check by passing the `--no-verify-upload` flag.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/122
|
https://github.com/restic/rest-server/issues/122
|
||||||
https://github.com/restic/rest-server/pull/130
|
https://github.com/restic/rest-server/pull/130
|
||||||
|
|
||||||
* Enhancement #126: Allow running rest-server via systemd socket activation
|
* Enhancement #126: Allow running rest-server via systemd socket activation
|
||||||
|
|
||||||
We've added the option to have systemd create the listening socket and start the rest-server on
|
We've added the option to have systemd create the listening socket and start the
|
||||||
demand.
|
rest-server on demand.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/126
|
https://github.com/restic/rest-server/issues/126
|
||||||
https://github.com/restic/rest-server/pull/151
|
https://github.com/restic/rest-server/pull/151
|
||||||
@@ -270,9 +343,9 @@ Details
|
|||||||
|
|
||||||
* Enhancement #148: Expand use of security features in example systemd unit file
|
* Enhancement #148: Expand use of security features in example systemd unit file
|
||||||
|
|
||||||
The example systemd unit file now enables additional systemd features to mitigate potential
|
The example systemd unit file now enables additional systemd features to
|
||||||
security vulnerabilities in rest-server and the various packages and operating system
|
mitigate potential security vulnerabilities in rest-server and the various
|
||||||
components which it relies upon.
|
packages and operating system components which it relies upon.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/148
|
https://github.com/restic/rest-server/issues/148
|
||||||
https://github.com/restic/rest-server/pull/149
|
https://github.com/restic/rest-server/pull/149
|
||||||
@@ -298,49 +371,53 @@ Details
|
|||||||
|
|
||||||
* Security #60: Require auth by default, add --no-auth flag
|
* Security #60: Require auth by default, add --no-auth flag
|
||||||
|
|
||||||
In order to prevent users from accidentally exposing rest-server without authentication,
|
In order to prevent users from accidentally exposing rest-server without
|
||||||
rest-server now defaults to requiring a .htpasswd. If you want to disable authentication, you
|
authentication, rest-server now defaults to requiring a .htpasswd. If you want
|
||||||
need to explicitly pass the new --no-auth flag.
|
to disable authentication, you need to explicitly pass the new --no-auth flag.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/60
|
https://github.com/restic/rest-server/issues/60
|
||||||
https://github.com/restic/rest-server/pull/61
|
https://github.com/restic/rest-server/pull/61
|
||||||
|
|
||||||
* Security #64: Refuse overwriting config file in append-only mode
|
* Security #64: Refuse overwriting config file in append-only mode
|
||||||
|
|
||||||
While working on the `rclone serve restic` command we noticed that is currently possible to
|
While working on the `rclone serve restic` command we noticed that is currently
|
||||||
overwrite the config file in a repo even if `--append-only` is specified. The first commit adds
|
possible to overwrite the config file in a repo even if `--append-only` is
|
||||||
proper tests, and the second commit fixes the issue.
|
specified. The first commit adds proper tests, and the second commit fixes the
|
||||||
|
issue.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/pull/64
|
https://github.com/restic/rest-server/pull/64
|
||||||
|
|
||||||
* Security #117: Stricter path sanitization
|
* Security #117: Stricter path sanitization
|
||||||
|
|
||||||
The framework we're using in rest-server to decode paths to repositories allowed specifying
|
The framework we're using in rest-server to decode paths to repositories allowed
|
||||||
URL-encoded characters in paths, including sensitive characters such as `/` (encoded as
|
specifying URL-encoded characters in paths, including sensitive characters such
|
||||||
`%2F`).
|
as `/` (encoded as `%2F`).
|
||||||
|
|
||||||
We've changed this unintended behavior, such that rest-server now rejects such paths. In
|
We've changed this unintended behavior, such that rest-server now rejects such
|
||||||
particular, it is no longer possible to specify sub-repositories for users by encoding the
|
paths. In particular, it is no longer possible to specify sub-repositories for
|
||||||
path with `%2F`, such as `http://localhost:8000/foo%2Fbar`, which means that this will
|
users by encoding the path with `%2F`, such as
|
||||||
unfortunately be a breaking change in that case.
|
`http://localhost:8000/foo%2Fbar`, which means that this will unfortunately be a
|
||||||
|
breaking change in that case.
|
||||||
|
|
||||||
If using sub-repositories for users is important to you, please let us know in the forum, so we
|
If using sub-repositories for users is important to you, please let us know in
|
||||||
can learn about your use case and implement this properly. As it currently stands, the ability
|
the forum, so we can learn about your use case and implement this properly. As
|
||||||
to use sub-repositories was an unintentional feature made possible by the URL decoding
|
it currently stands, the ability to use sub-repositories was an unintentional
|
||||||
framework used, and hence never meant to be supported in the first place. If we wish to have this
|
feature made possible by the URL decoding framework used, and hence never meant
|
||||||
feature in rest-server, we'd like to have it implemented properly and intentionally.
|
to be supported in the first place. If we wish to have this feature in
|
||||||
|
rest-server, we'd like to have it implemented properly and intentionally.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/117
|
https://github.com/restic/rest-server/issues/117
|
||||||
|
|
||||||
* Change #102: Remove vendored dependencies
|
* Change #102: Remove vendored dependencies
|
||||||
|
|
||||||
We've removed the vendored dependencies (in the subdir `vendor/`) similar to what we did for
|
We've removed the vendored dependencies (in the subdir `vendor/`) similar to
|
||||||
`restic` itself. When building restic, the Go compiler automatically fetches the
|
what we did for `restic` itself. When building restic, the Go compiler
|
||||||
dependencies. It will also cryptographically verify that the correct code has been fetched by
|
automatically fetches the dependencies. It will also cryptographically verify
|
||||||
using the hashes in `go.sum` (see the link to the documentation below).
|
that the correct code has been fetched by using the hashes in `go.sum` (see the
|
||||||
|
link to the documentation below).
|
||||||
|
|
||||||
Building the rest-server now requires Go 1.11 or newer, since we're using Go Modules for
|
Building the rest-server now requires Go 1.11 or newer, since we're using Go
|
||||||
dependency management. Older Go versions are not supported any more.
|
Modules for dependency management. Older Go versions are not supported any more.
|
||||||
|
|
||||||
https://github.com/restic/rest-server/issues/102
|
https://github.com/restic/rest-server/issues/102
|
||||||
https://golang.org/cmd/go/#hdr-Module_downloading_and_verification
|
https://golang.org/cmd/go/#hdr-Module_downloading_and_verification
|
||||||
|
|||||||
5
changelog/unreleased/pull-295
Normal file
5
changelog/unreleased/pull-295
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Enhancement: Output status of append only mode on startup
|
||||||
|
|
||||||
|
Rest-server now outputs whether append only mode has been enabled on startup.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/pull/295
|
||||||
@@ -69,11 +69,12 @@ func newRestServerApp() *restServerApp {
|
|||||||
flags.BoolVar(&rv.Server.PrivateRepos, "private-repos", rv.Server.PrivateRepos, "users can only access their private repo")
|
flags.BoolVar(&rv.Server.PrivateRepos, "private-repos", rv.Server.PrivateRepos, "users can only access their private repo")
|
||||||
flags.BoolVar(&rv.Server.Prometheus, "prometheus", rv.Server.Prometheus, "enable Prometheus metrics")
|
flags.BoolVar(&rv.Server.Prometheus, "prometheus", rv.Server.Prometheus, "enable Prometheus metrics")
|
||||||
flags.BoolVar(&rv.Server.PrometheusNoAuth, "prometheus-no-auth", rv.Server.PrometheusNoAuth, "disable auth for Prometheus /metrics endpoint")
|
flags.BoolVar(&rv.Server.PrometheusNoAuth, "prometheus-no-auth", rv.Server.PrometheusNoAuth, "disable auth for Prometheus /metrics endpoint")
|
||||||
|
flags.BoolVar(&rv.Server.GroupAccessibleRepos, "group-accessible-repos", rv.Server.GroupAccessibleRepos, "let filesystem group be able to access repo files")
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
}
|
}
|
||||||
|
|
||||||
var version = "0.12.1-dev"
|
var version = "0.13.0"
|
||||||
|
|
||||||
func (app *restServerApp) tlsSettings() (bool, string, string, error) {
|
func (app *restServerApp) tlsSettings() (bool, string, string, error) {
|
||||||
var key, cert string
|
var key, cert string
|
||||||
@@ -135,12 +136,24 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error {
|
|||||||
log.Fatalf("error: %v", err)
|
log.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.Server.AppendOnly {
|
||||||
|
log.Println("Append only mode enabled")
|
||||||
|
} else {
|
||||||
|
log.Println("Append only mode disabled")
|
||||||
|
}
|
||||||
|
|
||||||
if app.Server.PrivateRepos {
|
if app.Server.PrivateRepos {
|
||||||
log.Println("Private repositories enabled")
|
log.Println("Private repositories enabled")
|
||||||
} else {
|
} else {
|
||||||
log.Println("Private repositories disabled")
|
log.Println("Private repositories disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.Server.GroupAccessibleRepos {
|
||||||
|
log.Println("Group accessible repos enabled")
|
||||||
|
} else {
|
||||||
|
log.Println("Group accessible repos disabled")
|
||||||
|
}
|
||||||
|
|
||||||
enabledTLS, privateKey, publicKey, err := app.tlsSettings()
|
enabledTLS, privateKey, publicKey, err := app.tlsSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -9,7 +9,7 @@ require (
|
|||||||
github.com/miolini/datacounter v1.0.3
|
github.com/miolini/datacounter v1.0.3
|
||||||
github.com/prometheus/client_golang v1.18.0
|
github.com/prometheus/client_golang v1.18.0
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
golang.org/x/crypto v0.25.0
|
golang.org/x/crypto v0.27.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -23,6 +23,6 @@ require (
|
|||||||
github.com/prometheus/common v0.45.0 // indirect
|
github.com/prometheus/common v0.45.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -35,11 +35,11 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
|||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
51
handlers.go
51
handlers.go
@@ -15,23 +15,24 @@ import (
|
|||||||
|
|
||||||
// Server encapsulates the rest-server's settings and repo management logic
|
// Server encapsulates the rest-server's settings and repo management logic
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Path string
|
Path string
|
||||||
HtpasswdPath string
|
HtpasswdPath string
|
||||||
Listen string
|
Listen string
|
||||||
Log string
|
Log string
|
||||||
CPUProfile string
|
CPUProfile string
|
||||||
TLSKey string
|
TLSKey string
|
||||||
TLSCert string
|
TLSCert string
|
||||||
TLS bool
|
TLS bool
|
||||||
NoAuth bool
|
NoAuth bool
|
||||||
AppendOnly bool
|
AppendOnly bool
|
||||||
PrivateRepos bool
|
PrivateRepos bool
|
||||||
Prometheus bool
|
Prometheus bool
|
||||||
PrometheusNoAuth bool
|
PrometheusNoAuth bool
|
||||||
Debug bool
|
Debug bool
|
||||||
MaxRepoSize int64
|
MaxRepoSize int64
|
||||||
PanicOnError bool
|
PanicOnError bool
|
||||||
NoVerifyUpload bool
|
NoVerifyUpload bool
|
||||||
|
GroupAccessibleRepos bool
|
||||||
|
|
||||||
htpasswdFile *HtpasswdFile
|
htpasswdFile *HtpasswdFile
|
||||||
quotaManager *quota.Manager
|
quotaManager *quota.Manager
|
||||||
@@ -88,12 +89,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Pass the request to the repo.Handler
|
// Pass the request to the repo.Handler
|
||||||
opt := repo.Options{
|
opt := repo.Options{
|
||||||
AppendOnly: s.AppendOnly,
|
AppendOnly: s.AppendOnly,
|
||||||
Debug: s.Debug,
|
Debug: s.Debug,
|
||||||
QuotaManager: s.quotaManager, // may be nil
|
QuotaManager: s.quotaManager, // may be nil
|
||||||
PanicOnError: s.PanicOnError,
|
PanicOnError: s.PanicOnError,
|
||||||
NoVerifyUpload: s.NoVerifyUpload,
|
NoVerifyUpload: s.NoVerifyUpload,
|
||||||
FsyncWarning: &s.fsyncWarning,
|
FsyncWarning: &s.fsyncWarning,
|
||||||
|
GroupAccessible: s.GroupAccessibleRepos,
|
||||||
}
|
}
|
||||||
if s.Prometheus {
|
if s.Prometheus {
|
||||||
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)
|
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)
|
||||||
@@ -158,7 +160,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 "/"
|
||||||
|
|||||||
42
repo/repo.go
42
repo/repo.go
@@ -29,8 +29,6 @@ import (
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
AppendOnly bool // if set, delete actions are not allowed
|
AppendOnly bool // if set, delete actions are not allowed
|
||||||
Debug bool
|
Debug bool
|
||||||
DirMode os.FileMode
|
|
||||||
FileMode os.FileMode
|
|
||||||
NoVerifyUpload bool
|
NoVerifyUpload bool
|
||||||
|
|
||||||
// If set, we will panic when an internal server error happens. This
|
// If set, we will panic when an internal server error happens. This
|
||||||
@@ -40,6 +38,13 @@ type Options struct {
|
|||||||
BlobMetricFunc BlobMetricFunc
|
BlobMetricFunc BlobMetricFunc
|
||||||
QuotaManager *quota.Manager
|
QuotaManager *quota.Manager
|
||||||
FsyncWarning *sync.Once
|
FsyncWarning *sync.Once
|
||||||
|
|
||||||
|
// If set makes files group accessible
|
||||||
|
GroupAccessible bool
|
||||||
|
|
||||||
|
// Defaults dir and file mode
|
||||||
|
dirMode os.FileMode
|
||||||
|
fileMode os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultDirMode is the file mode used for directory creation if not
|
// DefaultDirMode is the file mode used for directory creation if not
|
||||||
@@ -50,6 +55,12 @@ const DefaultDirMode os.FileMode = 0700
|
|||||||
// overridden in the Options
|
// overridden in the Options
|
||||||
const DefaultFileMode os.FileMode = 0600
|
const DefaultFileMode os.FileMode = 0600
|
||||||
|
|
||||||
|
// File mode used for directory creation when group access is enabled
|
||||||
|
const GroupAccessibleDirMode os.FileMode = 0770
|
||||||
|
|
||||||
|
// File mode used for file creation when group access is enabled
|
||||||
|
const GroupAccessibleFileMode os.FileMode = 0660
|
||||||
|
|
||||||
// New creates a new Handler for a single Restic backup repo.
|
// New creates a new Handler for a single Restic backup repo.
|
||||||
// path is the full filesystem path to this repo directory.
|
// path is the full filesystem path to this repo directory.
|
||||||
// opt is a set of options.
|
// opt is a set of options.
|
||||||
@@ -57,12 +68,15 @@ func New(path string, opt Options) (*Handler, error) {
|
|||||||
if path == "" {
|
if path == "" {
|
||||||
return nil, fmt.Errorf("path is required")
|
return nil, fmt.Errorf("path is required")
|
||||||
}
|
}
|
||||||
if opt.DirMode == 0 {
|
|
||||||
opt.DirMode = DefaultDirMode
|
opt.dirMode = DefaultDirMode
|
||||||
}
|
opt.fileMode = DefaultFileMode
|
||||||
if opt.FileMode == 0 {
|
|
||||||
opt.FileMode = DefaultFileMode
|
if opt.GroupAccessible {
|
||||||
|
opt.dirMode = GroupAccessibleDirMode
|
||||||
|
opt.fileMode = GroupAccessibleFileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
h := Handler{
|
h := Handler{
|
||||||
path: path,
|
path: path,
|
||||||
opt: opt,
|
opt: opt,
|
||||||
@@ -289,7 +303,7 @@ func (h *Handler) saveConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
cfg := h.getSubPath("config")
|
cfg := h.getSubPath("config")
|
||||||
|
|
||||||
f, err := os.OpenFile(cfg, os.O_CREATE|os.O_WRONLY|os.O_EXCL, h.opt.FileMode)
|
f, err := os.OpenFile(cfg, os.O_CREATE|os.O_WRONLY|os.O_EXCL, h.opt.fileMode)
|
||||||
if err != nil && os.IsExist(err) {
|
if err != nil && os.IsExist(err) {
|
||||||
if h.opt.Debug {
|
if h.opt.Debug {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
@@ -545,15 +559,15 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmpFn := filepath.Join(filepath.Dir(path), objectID+".rest-server-temp")
|
tmpFn := filepath.Join(filepath.Dir(path), objectID+".rest-server-temp")
|
||||||
tf, err := tempFile(tmpFn, h.opt.FileMode)
|
tf, err := tempFile(tmpFn, h.opt.fileMode)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// the error is caused by a missing directory, create it and retry
|
// the error is caused by a missing directory, create it and retry
|
||||||
mkdirErr := os.MkdirAll(filepath.Dir(path), h.opt.DirMode)
|
mkdirErr := os.MkdirAll(filepath.Dir(path), h.opt.dirMode)
|
||||||
if mkdirErr != nil {
|
if mkdirErr != nil {
|
||||||
log.Print(mkdirErr)
|
log.Print(mkdirErr)
|
||||||
} else {
|
} else {
|
||||||
// try again
|
// try again
|
||||||
tf, err = tempFile(tmpFn, h.opt.FileMode)
|
tf, err = tempFile(tmpFn, h.opt.fileMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -750,13 +764,13 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
log.Printf("Creating repository directories in %s\n", h.path)
|
log.Printf("Creating repository directories in %s\n", h.path)
|
||||||
|
|
||||||
if err := os.MkdirAll(h.path, h.opt.DirMode); err != nil {
|
if err := os.MkdirAll(h.path, h.opt.dirMode); err != nil {
|
||||||
h.internalServerError(w, err)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range ObjectTypes {
|
for _, d := range ObjectTypes {
|
||||||
if err := os.Mkdir(filepath.Join(h.path, d), h.opt.DirMode); err != nil && !os.IsExist(err) {
|
if err := os.Mkdir(filepath.Join(h.path, d), h.opt.dirMode); err != nil && !os.IsExist(err) {
|
||||||
h.internalServerError(w, err)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -764,7 +778,7 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", i))
|
dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", i))
|
||||||
if err := os.Mkdir(dirPath, h.opt.DirMode); err != nil && !os.IsExist(err) {
|
if err := os.Mkdir(dirPath, h.opt.dirMode); err != nil && !os.IsExist(err) {
|
||||||
h.internalServerError(w, err)
|
h.internalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user