58 Commits

Author SHA1 Message Date
Michael Eischer
654fa16cb2 Merge pull request #359 from restic/dependabot/github_actions/actions/setup-go-6
Some checks failed
test / Linux Go 1.23.x (push) Has been cancelled
test / Linux (race) Go 1.24.x (push) Has been cancelled
test / Linux Go 1.24.x (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
Bump actions/setup-go from 5 to 6
2025-10-04 16:51:20 +02:00
Michael Eischer
cc352125b8 Merge pull request #354 from restic/dependabot/github_actions/actions/checkout-5
Bump actions/checkout from 4 to 5
2025-10-04 16:51:00 +02:00
dependabot[bot]
822a8dca64 Bump actions/setup-go from 5 to 6
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 19:32:23 +00:00
dependabot[bot]
334ddf15ea Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 05:42:05 +00:00
Alexander Neumann
2f31e10ceb Update version for development
Some checks failed
test / Linux Go 1.23.x (push) Has been cancelled
test / Linux (race) Go 1.24.x (push) Has been cancelled
test / Linux Go 1.24.x (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
2025-05-31 22:32:39 +02:00
Alexander Neumann
ad130de021 Generate CHANGELOG.md for 0.14.0 2025-05-31 22:21:45 +02:00
Alexander Neumann
2aaa048aba Move changelog files for 0.14.0 2025-05-31 22:21:33 +02:00
Alexander Neumann
b6ec6f45cc Update VERSION files for 0.14.0 2025-05-31 22:20:43 +02:00
rawtaz
2a77536ce5 Merge pull request #348 from restic/clarify-umask-for-group-accessible
Some checks failed
test / Linux Go 1.23.x (push) Has been cancelled
test / Linux (race) Go 1.24.x (push) Has been cancelled
test / Linux Go 1.24.x (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
Improve description of group-accessible option
2025-05-27 20:58:05 +02:00
Michael Eischer
0adcfa2619 Improve description of group-accessible option 2025-05-27 19:43:24 +02:00
Michael Eischer
9f8bb0c87c Merge pull request #347 from restic/polish-changelogs
Some checks failed
test / Linux (race) Go 1.24.x (push) Has been cancelled
test / Linux Go 1.23.x (push) Has been cancelled
test / Linux Go 1.24.x (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
Polish changelogs
2025-05-21 19:01:27 +02:00
Leo R. Lundgren
5faeedf050 Polish changelogs 2025-05-20 21:56:28 +02:00
Michael Eischer
7294612990 Merge pull request #346 from MichaelEischer/zip-for-windows
Some checks are pending
test / lint (push) Waiting to run
test / Analyze results (push) Blocked by required conditions
test / Linux Go 1.23.x (push) Waiting to run
test / Linux (race) Go 1.24.x (push) Waiting to run
test / Linux Go 1.24.x (push) Waiting to run
Build zip files for windows release binaries
2025-05-20 19:09:48 +02:00
Michael Eischer
25066228ee add changelog for windows zip binaries 2025-05-15 21:03:05 +02:00
Michael Eischer
72a7319fae limit build parallelism 2025-05-15 20:58:23 +02:00
Michael Eischer
df5330773f also generate zip files for windows 2025-05-15 20:58:23 +02:00
Michael Eischer
2bb4d251e2 autoformat goreleaser yaml 2025-05-15 20:58:23 +02:00
Michael Eischer
f018e99109 Merge pull request #340 from MichaelEischer/limit-htpasswd-perms
Some checks failed
test / Linux Go 1.23.x (push) Has been cancelled
test / Linux (race) Go 1.24.x (push) Has been cancelled
test / Linux Go 1.24.x (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
Limit htpasswd perms
2025-05-15 20:20:16 +02:00
Michael Eischer
95538fe956 restrict umask of htpasswd file 2025-05-15 19:56:53 +02:00
Michael Eischer
4e6193ceee Merge pull request #339 from MichaelEischer/polish-changelogs
Some checks failed
test / Linux Go 1.23.x (push) Has been cancelled
test / Linux (race) Go 1.24.x (push) Has been cancelled
test / Linux Go 1.24.x (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
Polish changelogs
2025-05-12 19:00:34 +02:00
Michael Eischer
4c368ae1fb polish changelogs 2025-05-12 18:50:47 +02:00
Michael Eischer
0ed9de379e Merge pull request #341 from restic/dependabot/go_modules/github.com/prometheus/client_golang-1.22.0
Bump github.com/prometheus/client_golang from 1.21.1 to 1.22.0
2025-05-12 18:47:15 +02:00
Michael Eischer
451c4831f9 Merge pull request #345 from restic/dependabot/go_modules/golang.org/x/crypto-0.38.0
Bump golang.org/x/crypto from 0.37.0 to 0.38.0
2025-05-12 18:47:00 +02:00
dependabot[bot]
1610cf6cef Bump golang.org/x/crypto from 0.37.0 to 0.38.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/crypto/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 19:51:08 +00:00
dependabot[bot]
3d35116b3c Bump github.com/prometheus/client_golang from 1.21.1 to 1.22.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.21.1 to 1.22.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.21.1...v1.22.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 20:42:16 +00:00
dependabot[bot]
eee73d3bc1 Bump golang.org/x/crypto from 0.33.0 to 0.37.0 (#337)
Some checks failed
test / Linux Go 1.23.x (push) Has been cancelled
test / Linux (race) Go 1.24.x (push) Has been cancelled
test / Linux Go 1.24.x (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.33.0 to 0.37.0.
- [Commits](https://github.com/golang/crypto/compare/v0.33.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 19:16:53 +00:00
Michael Eischer
df7b13e18f Merge pull request #338 from MichaelEischer/bump-go1.23
Bump minimum Go version to 1.23
2025-04-14 21:13:59 +02:00
Michael Eischer
2d3e02017b CI: add Go 1.24 and reduce diff to config used by restic 2025-04-14 21:11:57 +02:00
DarkSpir
2b6f0b39fc Hardened tls cipher suits and added option for tls min version (#315)
* handlers.go: Added parameter for TLS min version

rest-server/main.go: Added parameter handling for TLS min version

rest-server/main.go: Added crypto.tls, implemented and configured tlsConfig object

* tls min version parameter documentation

* Added changelog documentation

* README.md: Fixed typo

main.go: Added error for unknown TLS min versions

main.go: Changed CurvePreferences in TLS config to Go default

main.go: Removed handling for TLS min versions 1.0 and 1.1

Signed-off-by: darkspir <forgejo.darkspir@teemitmil.ch>

* main.go: Improved TLSMinVer parameter documentation

* README.md: Improved --tls-min-ver parameter documentation

* main.go: Changed --tls-min-ver parameter documentation again

* main.go: Added allowed versions in Error Unsupported TLS min version

* update rest-server help output in readme

---------

Signed-off-by: darkspir <forgejo.darkspir@teemitmil.ch>
Co-authored-by: Michael Eischer <michael.eischer@fau.de>
2025-04-14 19:09:57 +00:00
Michael Eischer
dbf5253ac2 Merge pull request #307 from akmet/master
Add support for proxy-based authentication
2025-04-14 21:01:11 +02:00
Michael Eischer
fa15677855 Merge pull request #334 from stemid/patch-1
Support passwords with spaces in docker container
2025-04-14 20:52:01 +02:00
Michael Eischer
19aa0845c0 create_user/delete_user: quote user and filename 2025-04-14 20:48:11 +02:00
Michael Eischer
04b52b0cee document how to use group-accessible repos with systemd (#327) 2025-04-14 20:46:20 +02:00
dependabot[bot]
f2d406ff2e Bump github.com/prometheus/client_golang from 1.20.5 to 1.21.1 (#331)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.5 to 1.21.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.20.5...v1.21.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 20:45:54 +02:00
Michael Eischer
8ad7cfa60a bump minimum go version to 1.23 2025-04-14 20:44:28 +02:00
Stefan Midjich
4f17744d6c Support passwords with spaces
You must quote $2 in order to support passwords with spaces.
2025-03-13 14:40:59 +01:00
Michael Eischer
68ae5d1c0b check that proxy header is ignored if not enabled 2025-02-17 22:36:03 +01:00
Michael Eischer
0dfc772cdb document proxy auth in readme 2025-02-17 22:32:49 +01:00
akmet
b0a9a0452e Add support for proxy-based authentication 2025-02-17 22:25:25 +01:00
Massimo Lusetti
f053e33486 Add group-accessible-repos option (#308)
Some checks failed
test / Go ${{ matrix.go }} (1.22.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.23.x) (push) Has been cancelled
test / lint (push) Has been cancelled
test / Analyze results (push) Has been cancelled
* Add group-accessible-repos option

The group-accessible-repos option will let filesystem group id
be able to access files and dir within the restic repo
Default stick with old behaviour to be owner restricted
While here make dirMode and fileMode within Options struct
private

---------

Co-authored-by: Michael Eischer <michael.eischer@fau.de>
2025-02-17 22:17:54 +01:00
Michael Eischer
10a06dcbf1 Merge pull request #325 from restic/dependabot/go_modules/golang.org/x/crypto-0.33.0
Bump golang.org/x/crypto from 0.32.0 to 0.33.0
2025-02-17 21:46:52 +01:00
dependabot[bot]
b05b44cb2c Bump golang.org/x/crypto from 0.27.0 to 0.33.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.33.0.
- [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 20:44:14 +00:00
Michael Eischer
a976a2145b Merge pull request #326 from restic/dependabot/go_modules/github.com/spf13/cobra-1.9.1
Bump github.com/spf13/cobra from 1.8.1 to 1.9.1
2025-02-17 21:43:11 +01:00
dependabot[bot]
751d2841f3 Bump github.com/spf13/cobra from 1.8.1 to 1.9.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.1 to 1.9.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 20:38:50 +00:00
Michael Eischer
5938f9aacd Merge pull request #323 from MichaelEischer/update-issue-templates
Sync issue and pr templates with restic
2025-02-17 21:38:35 +01:00
Michael Eischer
376392a89c Merge pull request #322 from MichaelEischer/bump-go-version
Bump minimum go version to 1.22
2025-02-17 21:37:02 +01:00
Michael Eischer
13f461740d sync issue and pr template with restic 2025-02-07 23:47:46 +01:00
Michael Eischer
e9e6529345 update dependencies 2025-02-07 22:44:58 +01:00
Michael Eischer
a0110bb902 add changelog 2025-02-07 22:44:58 +01:00
Michael Eischer
82c5a314f9 CI: sync pipeline with restic 2025-02-07 22:44:58 +01:00
Michael Eischer
9fc5066fc4 fix most linter errors 2025-02-07 22:34:40 +01:00
Michael Eischer
3e0737a8bd sync golangci config with restic 2025-02-07 22:34:40 +01:00
Michael Eischer
9195526406 bump minimum go version to 1.22 2025-02-02 22:32:12 +01:00
Michael Eischer
2513a698f3 Merge pull request #302 from restic/dependabot/go_modules/golang.org/x/crypto-0.27.0
Some checks failed
test / Go ${{ matrix.go }} (1.18.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.19.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.20.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.21.x) (push) Has been cancelled
test / lint (push) Has been cancelled
Bump golang.org/x/crypto from 0.25.0 to 0.27.0
2024-12-01 17:59:35 +01:00
dependabot[bot]
bdfb047edf Bump golang.org/x/crypto from 0.25.0 to 0.27.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.25.0 to 0.27.0.
- [Commits](https://github.com/golang/crypto/compare/v0.25.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 19:18:23 +00:00
Michael Eischer
e35c6e39d9 Merge pull request #295 from restic/append-only-status
Some checks failed
test / Go ${{ matrix.go }} (1.18.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.19.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.20.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.21.x) (push) Has been cancelled
test / lint (push) Has been cancelled
Output status of append only mode on startup
2024-07-29 19:36:59 +02:00
Leo R. Lundgren
da5bb66030 Output status of append only mode on startup 2024-07-29 01:20:10 +02:00
Alexander Neumann
664d997006 Fix goreleaser config
Some checks failed
test / Go ${{ matrix.go }} (1.18.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.19.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.20.x) (push) Has been cancelled
test / Go ${{ matrix.go }} (1.21.x) (push) Has been cancelled
test / lint (push) Has been cancelled
2024-07-26 14:55:06 +02:00
34 changed files with 688 additions and 291 deletions

View File

@@ -3,8 +3,8 @@ name: Bug report
about: Report a problem with rest-server to help us resolve it and improve about: Report a problem with rest-server to help us resolve it and improve
--- ---
<!-- <!--
Welcome! - We kindly ask that you: Welcome! - We kindly ask that you:
1. Fill out the issue template below - not doing so needs a good reason. 1. Fill out the issue template below - not doing so needs a good reason.
@@ -24,6 +24,7 @@ for tracking bugs and feature requests directly relating to the development of
the software itself, rather than the project. the software itself, rather than the project.
Thanks for understanding, and for contributing to the project! Thanks for understanding, and for contributing to the project!
--> -->
@@ -31,24 +32,26 @@ Output of `rest-server --version` <!-- If using docker, output of `docker images
--------------------------------- ---------------------------------
How did you run rest-server exactly?
------------------------------------ Problem description / Steps to reproduce
----------------------------------------
<!-- <!--
This section should include at least: This section should include at least:
* A description of the problem you are having with rest-server.
* The complete command line and any environment variables you used to * The complete command line and any environment variables you used to
configure rest-server's backend access. Make sure to replace sensitive values! configure rest-server. Make sure to replace sensitive values!
* The output of the commands, what rest-server prints gives may give us much * The output of the commands, what rest-server prints gives may give us much
information to diagnose the problem! information to diagnose the problem!
* The more time you spend describing an easy way to reproduce the behavior (if
this is possible), the easier it is for the project developers to fix it!
--> -->
What backend/server/service did you use to store the repository?
----------------------------------------------------------------
Expected behavior Expected behavior
----------------- -----------------
@@ -56,30 +59,20 @@ Expected behavior
Describe what you'd like rest-server to do differently. Describe what you'd like rest-server to do differently.
--> -->
Actual behavior Actual behavior
--------------- ---------------
<!-- <!--
Please try to concentrate on observations, so only describe what you observed directly. In this section, please try to concentrate on observations, so only describe
what you observed directly.
--> -->
Steps to reproduce the behavior
-------------------------------
<!--
The more time you spend describing an easy way to reproduce the behavior (if
this is possible), the easier it is for the project developers to fix it!
-->
Do you have any idea what may have caused this? Do you have any idea what may have caused this?
----------------------------------------------- -----------------------------------------------
<!--
Do you have an idea how to solve the issue? Did something noteworthy happen on your system, Internet connection, backend services, etc?
------------------------------------------- -->
Did rest-server help you today? Did it make you happy in any way? Did rest-server help you today? Did it make you happy in any way?

View File

@@ -1,10 +1,10 @@
--- ---
name: Feature request/enhancement name: Feature request
about: Suggest a new feature or enhancement for rest-server about: Suggest a new feature or enhancement for rest-server
--- ---
<!-- <!--
Welcome! - We kindly ask that you: Welcome! - We kindly ask that you:
1. Fill out the issue template below - not doing so needs a good reason. 1. Fill out the issue template below - not doing so needs a good reason.
@@ -18,6 +18,7 @@ for tracking bugs and feature requests directly relating to the development of
the software itself, rather than the project. the software itself, rather than the project.
Thanks for understanding, and for contributing to the project! Thanks for understanding, and for contributing to the project!
--> -->
@@ -30,24 +31,22 @@ later to see what has changed in rest-server when we revisit this issue after so
time. time.
--> -->
What should rest-server do differently? Which functionality do you think we should add?
What should rest-server do differently? ---------------------------------------------------------------------------------------
---------------------------------------
<!-- <!--
Please describe the feature you'd like to see added or changed here. Please describe the feature you'd like us to add here.
--> -->
What are you trying to do? What is your use case? What are you trying to do? What problem would this solve?
------------------------------------------------- ---------------------------------------------------------
<!-- <!--
This section should contain a brief description what you're trying to do, which This section should contain a brief description what you're trying to do, which
would be possible after implementing the new feature. would be possible after implementing the new feature.
--> -->
Did rest-server help you today? Did it make you happy in any way? Did rest-server help you today? Did it make you happy in any way?
----------------------------------------------------------------- -----------------------------------------------------------------

View File

@@ -1,42 +1,41 @@
<!-- <!--
Thank you very much for contributing code or documentation to rest-server! Thank you very much for contributing code or documentation to rest-server! Please
fill out the following questions to make it easier for us to review your
Please note that each PR should be preceded by an issue where the suggested changes.
change can be discussed in general and without focus on specific code. That
way, work done in the PR will better match what's been agreed in the issue.
Please fill out the following questions to make it easier for us to review
your changes. You don't have to check all the checkboxes at once, instead
feel free to add more commits over time.
--> -->
What does this PR change? What problem does it solve?
What is the purpose of this change? What does it change? -----------------------------------------------------
--------------------------------------------------------
<!-- <!--
Describe the changes here, as detailed as needed. Describe the changes and their purpose here, as detailed as needed.
--> -->
Was the change previously discussed in an issue or on the forum?
Was the change discussed in an issue or in the forum before? ----------------------------------------------------------------
------------------------------------------------------------
<!-- <!--
Link issues and relevant forum posts here. Link issues and relevant forum posts here.
If this PR resolves an issue on GitHub, write "Closes #1234" such If this PR resolves an issue on GitHub, use "Closes #1234" so that the issue
that the issue is closed automatically when this PR is merged. is closed automatically when this PR is merged.
--> -->
Checklist Checklist
--------- ---------
- [ ] I have enabled [maintainer edits for this PR](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) <!--
- [ ] I have added tests for all changes in this PR You do not need to check all the boxes below all at once. Feel free to take
- [ ] I have added documentation for the changes (in the manual) your time and add more commits. If you're done and ready for review, please
- [ ] There's a new file in `changelog/unreleased/` that describes the changes for our users (template [here](https://github.com/restic/rest-server/blob/master/changelog/TEMPLATE)) check the last box. Enable a checkbox by replacing [ ] with [x].
- [ ] I have run `gofmt` on the code in all commits
- [ ] All commit messages are formatted in the same style as [the other commits in the repo](https://github.com/restic/rest-server/commits/master) Please always follow these steps:
- [ ] I'm done, this Pull Request is ready for review - Enable [maintainer edits](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork).
- Run `gofmt` on the code in all commits.
- Format all commit messages in the same style as [the other commits in the repository](https://github.com/restic/rest-server/blob/master/CONTRIBUTING.md#git-commits).
-->
- [ ] I have added tests for all code changes.
- [ ] I have added documentation for relevant changes (in the manual).
- [ ] There's a new file in `changelog/unreleased/` that describes the changes for our users (see [template](https://github.com/restic/rest-server/blob/master/changelog/TEMPLATE)).
- [ ] I'm done! This pull request is ready for review.

View File

@@ -7,48 +7,58 @@ on:
# run tests for all pull requests # run tests for all pull requests
pull_request: pull_request:
merge_group:
permissions:
contents: read
env: env:
latest_go: "1.21.x" latest_go: "1.24.x"
GO111MODULE: on GO111MODULE: on
jobs: jobs:
test: test:
strategy: strategy:
matrix: matrix:
go: include:
- 1.18.x - job_name: Linux
- 1.19.x go: 1.24.x
- 1.20.x os: ubuntu-latest
- 1.21.x check_changelog: true
runs-on: ubuntu-latest
name: Go ${{ matrix.go }} - job_name: Linux (race)
go: 1.24.x
os: ubuntu-latest
test_opts: "-race"
- job_name: Linux
go: 1.23.x
os: ubuntu-latest
name: ${{ matrix.job_name }} Go ${{ matrix.go }}
runs-on: ${{ matrix.os }}
env: env:
GOPROXY: https://proxy.golang.org GOPROXY: https://proxy.golang.org
steps: steps:
- name: Check out code
uses: actions/checkout@v5
- name: Set up Go ${{ matrix.go }} - name: Set up Go ${{ matrix.go }}
uses: actions/setup-go@v5 uses: actions/setup-go@v6
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Check out code
uses: actions/checkout@v4
- name: Build
run: |
go build ./cmd/rest-server
- name: Build with build.go - name: Build with build.go
run: | run: |
go run build.go --goos linux go run build.go --goos linux
go run build.go --goos windows go run build.go --goos windows
go run build.go --goos darwin go run build.go --goos darwin
- name: Run tests - name: Run local Tests
run: | run: |
go test ./... go test -cover ${{matrix.test_opts}} ./...
- name: Check changelog files with calens - name: Check changelog files with calens
run: | run: |
@@ -57,27 +67,30 @@ jobs:
echo "check changelog files" echo "check changelog files"
calens calens
if: matrix.check_changelog
lint: lint:
name: lint name: lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
# allow annotating code in the PR
checks: write
steps: steps:
- name: Check out code
uses: actions/checkout@v5
- name: Set up Go ${{ env.latest_go }} - name: Set up Go ${{ env.latest_go }}
uses: actions/setup-go@v5 uses: actions/setup-go@v6
with: with:
go-version: ${{ env.latest_go }} go-version: ${{ env.latest_go }}
- name: Check out code
uses: actions/checkout@v4
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.51 version: v1.64.8
# Optional: show only new issues if it's a pull request. The default value is `false`. args: --verbose --timeout 5m
only-new-issues: true
args: --verbose --timeout 10m
# only run golangci-lint for pull requests, otherwise ALL hints get # only run golangci-lint for pull requests, otherwise ALL hints get
# reported. We need to slowly address all issues until we can enable # reported. We need to slowly address all issues until we can enable
@@ -89,3 +102,18 @@ jobs:
echo "check if go.mod and go.sum are up to date" echo "check if go.mod and go.sum are up to date"
go mod tidy go mod tidy
git diff --exit-code go.mod go.sum git diff --exit-code go.mod go.sum
analyze:
name: Analyze results
needs: [test, lint]
if: always()
permissions: # no need to access code
contents: none
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe
with:
jobs: ${{ toJSON(needs) }}

View File

@@ -10,13 +10,10 @@ linters:
# make sure all errors returned by functions are handled # make sure all errors returned by functions are handled
- errcheck - errcheck
# find unused code
- deadcode
# show how code can be simplified # show how code can be simplified
- gosimple - gosimple
# # make sure code is formatted # make sure code is formatted
- gofmt - gofmt
# examine code and report suspicious constructs, such as Printf calls whose # examine code and report suspicious constructs, such as Printf calls whose
@@ -35,15 +32,14 @@ linters:
# find unused variables, functions, structs, types, etc. # find unused variables, functions, structs, types, etc.
- unused - unused
# find unused struct fields
- structcheck
# find unused global variables
- varcheck
# parse and typecheck code # parse and typecheck code
- typecheck - typecheck
# ensure that http response bodies are closed
- bodyclose
- importas
issues: issues:
# don't use the default exclude rules, this hides (among others) ignored # don't use the default exclude rules, this hides (among others) ignored
# errors from Close() calls # errors from Close() calls
@@ -55,3 +51,6 @@ issues:
- exported (function|method|var|type|const) .* should have comment or be unexported - exported (function|method|var|type|const) .* should have comment or be unexported
# revive: ignore constants in all caps # revive: ignore constants in all caps
- don't use ALL_CAPS in Go names; use CamelCase - don't use ALL_CAPS in Go names; use CamelCase
# revive: lots of packages don't have such a comment
- "package-comments: should have a package comment"
- "redefines-builtin-id:"

View File

@@ -21,29 +21,27 @@ before:
# build a single binary # build a single binary
builds: builds:
- - id: default
# make sure everything is statically linked by disabling cgo altogether # make sure everything is statically linked by disabling cgo altogether
env: env: &build_env
- CGO_ENABLED=0 - CGO_ENABLED=0
# set the package for the main binary # set the package for the main binary
main: ./cmd/rest-server main: ./cmd/rest-server
flags: flags:
# don't include any paths to source files in the resulting binary &build_flags # don't include any paths to source files in the resulting binary
- -trimpath - -trimpath
mod_timestamp: '{{ .CommitTimestamp }}' mod_timestamp: "{{ .CommitTimestamp }}"
ldflags: ldflags: &build_ldflags # set the version variable in the main package
# set the version variable in the main package
- "-s -w -X main.version={{ .Version }}" - "-s -w -X main.version={{ .Version }}"
# list all operating systems and architectures we build binaries for # list all operating systems and architectures we build binaries for
goos: goos:
- linux - linux
- darwin - darwin
- windows
- freebsd - freebsd
- netbsd - netbsd
- openbsd - openbsd
@@ -52,7 +50,7 @@ builds:
goarch: goarch:
- amd64 - amd64
- 386 - "386"
- arm - arm
- arm64 - arm64
- mips - mips
@@ -61,40 +59,63 @@ builds:
- ppc64 - ppc64
- ppc64le - ppc64le
goarm: goarm:
- 6 - "6"
- 7 - "7"
- id: windows-only
env: *build_env
main: ./cmd/rest-server
flags: *build_flags
mod_timestamp: "{{ .CommitTimestamp }}"
ldflags: *build_ldflags
goos:
- windows
goarch:
- amd64
- "386"
- arm
- arm64
# configure the resulting archives to create # configure the resulting archives to create
archives: archives:
- - id: default
builds: [default, windows-only]
format: tar.gz
# package a directory which contains the source file # package a directory which contains the source file
wrap_in_directory: true wrap_in_directory: true
builds_info: &archive_file_info builds_info: &archive_file_info
owner: root owner: root
group: root group: root
mtime: '{{ .CommitDate }}' mtime: "{{ .CommitDate }}"
mode: 0644 mode: 0644
# add these files to all archives # add these files to all archives
files: files: &archive_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
- id: windows-only
builds: [windows-only]
formats: [zip]
wrap_in_directory: true
builds_info: *archive_file_info
files: *archive_files
# also build an archive of the source code # also build an archive of the source code
source: source:
enabled: true enabled: true
# build a file containing the SHA256 hashes # build a file containing the SHA256 hashes
checksum: checksum:
name_template: 'SHA256SUMS' name_template: "SHA256SUMS"
# sign the checksum file # sign the checksum file
signs: signs:
@@ -128,7 +149,7 @@ dockers:
- docker/entrypoint.sh - docker/entrypoint.sh
- image_templates: - image_templates:
- restic/rest-server:{{ .Version }}-i386 - restic/rest-server:{{ .Version }}-i386
goarch: 386 goarch: "386"
build_flag_templates: build_flag_templates:
- "--platform=linux/386" - "--platform=linux/386"
- "--pull" - "--pull"
@@ -204,21 +225,20 @@ dockers:
dockerfile: "Dockerfile.goreleaser" dockerfile: "Dockerfile.goreleaser"
extra_files: *extra_files extra_files: *extra_files
docker_manifests: docker_manifests:
- name_template: "restic/rest-server:{{ .Version }}" - name_template: "restic/rest-server:{{ .Version }}"
image_templates: image_templates:
- "restic/rest-server:{{ .Version }}-amd64" - "restic/rest-server:{{ .Version }}-amd64"
- "restic/rest-server:{{ .Version }}-i386" - "restic/rest-server:{{ .Version }}-i386"
- "restic/rest-server:{{ .Version }}-arm32v6" - "restic/rest-server:{{ .Version }}-arm32v6"
- "restic/rest-server:{{ .Version }}-arm32v7" - "restic/rest-server:{{ .Version }}-arm32v7"
- "restic/rest-server:{{ .Version }}-arm64v8" - "restic/rest-server:{{ .Version }}-arm64v8"
- "restic/rest-server:{{ .Version }}-ppc64le" - "restic/rest-server:{{ .Version }}-ppc64le"
- name_template: "restic/rest-server:latest" - name_template: "restic/rest-server:latest"
image_templates: image_templates:
- "restic/rest-server:{{ .Version }}-amd64" - "restic/rest-server:{{ .Version }}-amd64"
- "restic/rest-server:{{ .Version }}-i386" - "restic/rest-server:{{ .Version }}-i386"
- "restic/rest-server:{{ .Version }}-arm32v6" - "restic/rest-server:{{ .Version }}-arm32v6"
- "restic/rest-server:{{ .Version }}-arm32v7" - "restic/rest-server:{{ .Version }}-arm32v7"
- "restic/rest-server:{{ .Version }}-arm64v8" - "restic/rest-server:{{ .Version }}-arm64v8"
- "restic/rest-server:{{ .Version }}-ppc64le" - "restic/rest-server:{{ .Version }}-ppc64le"

View File

@@ -1,3 +1,98 @@
Changelog for rest-server 0.14.0 (2025-05-31)
============================================
The following sections list the changes in rest-server 0.14.0 relevant
to users. The changes are ordered by importance.
Summary
-------
* Sec #318: Fix world-readable permissions on new `.htpasswd` files
* Chg #322: Update dependencies and require Go 1.23 or newer
* Enh #174: Support proxy-based authentication
* Enh #189: Support group accessible repositories
* Enh #295: Output status of append-only mode on startup
* Enh #315: Hardened tls settings
* Enh #321: Add zip archive format for Windows releases
Details
-------
* Security #318: Fix world-readable permissions on new `.htpasswd` files
On startup the rest-server Docker container creates an empty `.htpasswd` file if
none exists yet. This file was world-readable by default, which can be a
security risk, even though the file only contains hashed passwords.
This has been fixed such that new `.htpasswd` files are no longer
world-readabble.
The permissions of existing `.htpasswd` files must be manually changed if
relevant in your setup.
https://github.com/restic/rest-server/issues/318
https://github.com/restic/rest-server/pull/340
* Change #322: Update dependencies and require Go 1.23 or newer
All dependencies have been updated. Rest-server now requires Go 1.23 or newer to
build.
This also disables support for TLS versions older than TLS 1.2. On Windows,
rest-server now requires at least Windows 10 or Windows Server 2016. On macOS,
rest-server now requires at least macOS 11 Big Sur.
https://github.com/restic/rest-server/pull/322
https://github.com/restic/rest-server/pull/338
* Enhancement #174: Support proxy-based authentication
Rest-server now supports authentication via HTTP proxy headers. This feature can
be enabled by specifying the username header using the `--proxy-auth-username`
option (e.g., `--proxy-auth-username=X-Forwarded-User`).
When enabled, the server authenticates users based on the specified header and
disables Basic Auth. Note that proxy authentication is disabled when `--no-auth`
is set.
https://github.com/restic/rest-server/issues/174
https://github.com/restic/rest-server/pull/307
* Enhancement #189: Support group accessible repositories
Rest-server now supports making repositories accessible to the filesystem group
by setting the `--group-accessible-repos` option. Note that permissions of
existing files are not modified. To allow the group to read and write file, use
a umask of `007`. To only grant read access use `027`. To make an existing
repository group-accessible, use `chmod -R g+rwX /path/to/repo`.
https://github.com/restic/rest-server/issues/189
https://github.com/restic/rest-server/pull/308
* Enhancement #295: Output status of append-only mode on startup
Rest-server now displays the status of append-only mode during startup.
https://github.com/restic/rest-server/pull/295
* Enhancement #315: Hardened tls settings
Rest-server now uses a secure TLS cipher suite set by default. The minimum TLS
version is now TLS 1.2 and can be further increased using the new
`--tls-min-ver` option, allowing users to enforce stricter security
requirements.
https://github.com/restic/rest-server/pull/315
* Enhancement #321: Add zip archive format for Windows releases
Windows users can now download rest-server binaries in zip archive format (.zip)
in addition to the existing tar.gz archives.
https://github.com/restic/rest-server/issues/321
https://github.com/restic/rest-server/pull/346
Changelog for rest-server 0.13.0 (2024-07-26) Changelog for rest-server 0.13.0 (2024-07-26)
============================================ ============================================

View File

@@ -11,7 +11,7 @@ Rest Server is a high performance HTTP server that implements restic's [REST bac
## Requirements ## Requirements
Rest Server requires Go 1.18 or higher to build. The only tested compiler is the official Go compiler. Building server with `gccgo` may work, but is not supported. Rest Server requires Go 1.23 or higher to build. The only tested compiler is the official Go compiler.
The required version of restic backup client to use with `rest-server` is [v0.7.1](https://github.com/restic/restic/releases/tag/v0.7.1) or higher. The required version of restic backup client to use with `rest-server` is [v0.7.1](https://github.com/restic/restic/releases/tag/v0.7.1) or higher.
@@ -32,24 +32,27 @@ Usage:
rest-server [flags] rest-server [flags]
Flags: Flags:
--append-only enable append only mode --append-only enable append only mode
--cpu-profile string write CPU profile to file --cpu-profile string write CPU profile to file
--debug output debug messages --debug output debug messages
-h, --help help for rest-server --group-accessible-repos let filesystem group be able to access repo files
--htpasswd-file string location of .htpasswd file (default: "<data directory>/.htpasswd") -h, --help help for rest-server
--listen string listen address (default ":8000") --htpasswd-file string location of .htpasswd file (default: "<data directory>/.htpasswd)"
--log filename write HTTP requests in the combined log format to the specified filename --listen string listen address (default ":8000")
--max-size int the maximum size of the repository in bytes --log filename write HTTP requests in the combined log format to the specified filename (use "-" for logging to stdout)
--no-auth disable .htpasswd authentication --max-size int the maximum size of the repository in bytes
--no-verify-upload do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device --no-auth disable .htpasswd authentication
--path string data directory (default "/tmp/restic") --no-verify-upload do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device
--private-repos users can only access their private repo --path string data directory (default "/tmp/restic")
--prometheus enable Prometheus metrics --private-repos users can only access their private repo
--prometheus-no-auth disable auth for Prometheus /metrics endpoint --prometheus enable Prometheus metrics
--tls turn on TLS support --prometheus-no-auth disable auth for Prometheus /metrics endpoint
--tls-cert string TLS certificate path --proxy-auth-username string specifies the HTTP header containing the username for proxy-based authentication
--tls-key string TLS key path --tls turn on TLS support
-v, --version version for rest-server --tls-cert string TLS certificate path
--tls-key string TLS key path
--tls-min-ver string TLS min version, one of (1.2|1.3) (default "1.2")
-v, --version version for rest-server
``` ```
By default the server persists backup data in the OS temporary directory (`/tmp/restic` on Linux/BSD and others, in `%TEMP%\\restic` in Windows, etc). **If `rest-server` is launched using the default path, all backups will be lost**. To start the server with a custom persistence directory and with authentication disabled: By default the server persists backup data in the OS temporary directory (`/tmp/restic` on Linux/BSD and others, in `%TEMP%\\restic` in Windows, etc). **If `rest-server` is launched using the default path, all backups will be lost**. To start the server with a custom persistence directory and with authentication disabled:
@@ -68,7 +71,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 to 1.3 using `--tls-min-ver 1.3`.
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:
@@ -139,6 +142,16 @@ docker exec -it rest_server create_user myuser mypassword
docker exec -it rest_server delete_user myuser docker exec -it rest_server delete_user myuser
``` ```
## Proxy Authentication
See above for no authentication (`--no-auth`) and basic authentication.
To delegate authentication to a proxy, use the `--proxy-auth-username` flag. The specified header name, for example `X-Forwarded-User`,
must be present in the request headers and specifies the username. Basic authentication is disabled when this flag is set.
Warning: rest-server trusts the username in the header. It is the responsibility of the proxy
to ensure that the username is correct and cannot be forged by an attacker.
## Prometheus support and Grafana dashboard ## Prometheus support and Grafana dashboard
@@ -147,6 +160,10 @@ The server can be started with `--prometheus` to expose [Prometheus](https://pro
This repository contains an example full stack Docker Compose setup with a Grafana dashboard in [examples/compose-with-grafana/](examples/compose-with-grafana/). This repository contains an example full stack Docker Compose setup with a Grafana dashboard in [examples/compose-with-grafana/](examples/compose-with-grafana/).
## Group-accessible Repositories
Rest-server supports making repositories accessible to the filesystem group by setting the `--group-accessible-repos` option. Note that permissions of existing files are not modified. To allow the group to read and write file, use a umask of `007`. To only grant read access use `027`. To make an existing repository group-accessible, use `chmod -R g+rwX /path/to/repo`.
## Why use Rest Server? ## Why use Rest Server?
Compared to the SFTP backend, the REST backend has better performance, especially so if you can skip additional crypto overhead by using plain HTTP transport (restic already properly encrypts all data it sends, so using HTTPS is mostly about authentication). Compared to the SFTP backend, the REST backend has better performance, especially so if you can skip additional crypto overhead by using plain HTTP transport (restic already properly encrypts all data it sends, so using HTTPS is mostly about authentication).

View File

@@ -34,7 +34,7 @@
use another config file): use another config file):
goreleaser \ goreleaser \
release \ release --parallelism 4 \
--release-notes <(calens --template changelog/CHANGELOG-GitHub.tmpl --version "${VERSION}") --release-notes <(calens --template changelog/CHANGELOG-GitHub.tmpl --version "${VERSION}")
7. Set a new version in `main.go` and commit the result: 7. Set a new version in `main.go` and commit the result:

View File

@@ -1 +1 @@
0.13.0 0.14.0

View File

@@ -58,7 +58,7 @@ var config = Config{
Namespace: "github.com/restic/rest-server", // subdir of GOPATH, e.g. "github.com/foo/bar" Namespace: "github.com/restic/rest-server", // subdir of GOPATH, e.g. "github.com/foo/bar"
Main: "github.com/restic/rest-server/cmd/rest-server", // package name for the main package Main: "github.com/restic/rest-server/cmd/rest-server", // package name for the main package
Tests: []string{"./..."}, // tests to run Tests: []string{"./..."}, // tests to run
MinVersion: GoVersion{Major: 1, Minor: 15, Patch: 0}, // minimum Go version supported MinVersion: GoVersion{Major: 1, Minor: 23, Patch: 0}, // minimum Go version supported
} }
// Config configures the build. // Config configures the build.

View File

@@ -0,0 +1,10 @@
Enhancement: Support group accessible repositories
Rest-server now supports making repositories accessible to the filesystem group
by setting the `--group-accessible-repos` option. Note that permissions of
existing files are not modified. To allow the group to read and write file,
use a umask of `007`. To only grant read access use `027`. To make an existing
repository group-accessible, use `chmod -R g+rwX /path/to/repo`.
https://github.com/restic/rest-server/issues/189
https://github.com/restic/rest-server/pull/308

View File

@@ -0,0 +1,13 @@
Security: Fix world-readable permissions on new `.htpasswd` files
On startup the rest-server Docker container creates an empty `.htpasswd` file
if none exists yet. This file was world-readable by default, which can be
a security risk, even though the file only contains hashed passwords.
This has been fixed such that new `.htpasswd` files are no longer world-readabble.
The permissions of existing `.htpasswd` files must be manually changed if
relevant in your setup.
https://github.com/restic/rest-server/issues/318
https://github.com/restic/rest-server/pull/340

View File

@@ -0,0 +1,7 @@
Enhancement: Add zip archive format for Windows releases
Windows users can now download rest-server binaries in zip archive format (.zip)
in addition to the existing tar.gz archives.
https://github.com/restic/rest-server/issues/321
https://github.com/restic/rest-server/pull/346

View File

@@ -0,0 +1,5 @@
Enhancement: Output status of append-only mode on startup
Rest-server now displays the status of append-only mode during startup.
https://github.com/restic/rest-server/pull/295

View File

@@ -0,0 +1,12 @@
Enhancement: Support proxy-based authentication
Rest-server now supports authentication via HTTP proxy headers. This feature can
be enabled by specifying the username header using the `--proxy-auth-username`
option (e.g., `--proxy-auth-username=X-Forwarded-User`).
When enabled, the server authenticates users based on the specified header and
disables Basic Auth. Note that proxy authentication is disabled when `--no-auth`
is set.
https://github.com/restic/rest-server/issues/174
https://github.com/restic/rest-server/pull/307

View File

@@ -0,0 +1,7 @@
Enhancement: Hardened tls settings
Rest-server now uses a secure TLS cipher suite set by default. The minimum TLS
version is now TLS 1.2 and can be further increased using the new `--tls-min-ver`
option, allowing users to enforce stricter security requirements.
https://github.com/restic/rest-server/pull/315

View File

@@ -0,0 +1,11 @@
Change: Update dependencies and require Go 1.23 or newer
All dependencies have been updated. Rest-server now requires Go 1.23 or newer
to build.
This also disables support for TLS versions older than TLS 1.2. On Windows,
rest-server now requires at least Windows 10 or Windows Server 2016. On macOS,
rest-server now requires at least macOS 11 Big Sur.
https://github.com/restic/rest-server/pull/322
https://github.com/restic/rest-server/pull/338

View File

@@ -61,7 +61,10 @@ func TestUnixSocket(t *testing.T) {
if err != nil { if err != nil {
return err return err
} }
resp.Body.Close() err = resp.Body.Close()
if err != nil {
return err
}
if resp.StatusCode != test.StatusCode { if resp.StatusCode != test.StatusCode {
return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path) return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path)
} }

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"log" "log"
@@ -22,7 +23,7 @@ import (
type restServerApp struct { type restServerApp struct {
CmdRoot *cobra.Command CmdRoot *cobra.Command
Server restserver.Server Server restserver.Server
CpuProfile string CPUProfile string
listenerAddressMu sync.Mutex listenerAddressMu sync.Mutex
listenerAddress net.Addr // set after startup listenerAddress net.Addr // set after startup
@@ -36,7 +37,7 @@ func newRestServerApp() *restServerApp {
Short: "Run a REST server for use with restic", Short: "Run a REST server for use with restic",
SilenceErrors: true, SilenceErrors: true,
SilenceUsage: true, SilenceUsage: true,
Args: func(cmd *cobra.Command, args []string) error { Args: func(_ *cobra.Command, args []string) error {
if len(args) != 0 { if len(args) != 0 {
return fmt.Errorf("rest-server expects no arguments - unknown argument: %s", args[0]) return fmt.Errorf("rest-server expects no arguments - unknown argument: %s", args[0])
} }
@@ -45,14 +46,15 @@ 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
flags := rv.CmdRoot.Flags() flags := rv.CmdRoot.Flags()
flags.StringVar(&rv.CpuProfile, "cpu-profile", rv.CpuProfile, "write CPU profile to file") flags.StringVar(&rv.CPUProfile, "cpu-profile", rv.CPUProfile, "write CPU profile to file")
flags.BoolVar(&rv.Server.Debug, "debug", rv.Server.Debug, "output debug messages") flags.BoolVar(&rv.Server.Debug, "debug", rv.Server.Debug, "output debug messages")
flags.StringVar(&rv.Server.Listen, "listen", rv.Server.Listen, "listen address") flags.StringVar(&rv.Server.Listen, "listen", rv.Server.Listen, "listen address")
flags.StringVar(&rv.Server.Log, "log", rv.Server.Log, "write HTTP requests in the combined log format to the specified `filename` (use \"-\" for logging to stdout)") flags.StringVar(&rv.Server.Log, "log", rv.Server.Log, "write HTTP requests in the combined log format to the specified `filename` (use \"-\" for logging to stdout)")
@@ -61,19 +63,22 @@ 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.BoolVar(&rv.Server.NoAuth, "no-auth", rv.Server.NoAuth, "disable .htpasswd authentication") flags.StringVar(&rv.Server.TLSMinVer, "tls-min-ver", rv.Server.TLSMinVer, "TLS min version, one of (1.2|1.3)")
flags.BoolVar(&rv.Server.NoAuth, "no-auth", rv.Server.NoAuth, "disable 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.StringVar(&rv.Server.ProxyAuthUsername, "proxy-auth-username", rv.Server.ProxyAuthUsername, "specifies the HTTP header containing the username for proxy-based authentication")
flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload, flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload,
"do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device") "do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device")
flags.BoolVar(&rv.Server.AppendOnly, "append-only", rv.Server.AppendOnly, "enable append only mode") flags.BoolVar(&rv.Server.AppendOnly, "append-only", rv.Server.AppendOnly, "enable append only mode")
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.13.0" var version = "0.14.0-dev"
func (app *restServerApp) tlsSettings() (bool, string, string, error) { func (app *restServerApp) tlsSettings() (bool, string, string, error) {
var key, cert string var key, cert string
@@ -103,17 +108,19 @@ func (app *restServerApp) ListenerAddress() net.Addr {
return app.listenerAddress return app.listenerAddress
} }
func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error { func (app *restServerApp) runRoot(_ *cobra.Command, _ []string) error {
log.SetFlags(0) log.SetFlags(0)
log.Printf("Data directory: %s", app.Server.Path) log.Printf("Data directory: %s", app.Server.Path)
if app.CpuProfile != "" { if app.CPUProfile != "" {
f, err := os.Create(app.CpuProfile) f, err := os.Create(app.CPUProfile)
if err != nil { if err != nil {
return err return err
} }
defer f.Close() defer func() {
_ = f.Close()
}()
if err := pprof.StartCPUProfile(f); err != nil { if err := pprof.StartCPUProfile(f); err != nil {
return err return err
@@ -127,7 +134,11 @@ func (app *restServerApp) runRoot(cmd *cobra.Command, args []string) error {
if app.Server.NoAuth { if app.Server.NoAuth {
log.Println("Authentication disabled") log.Println("Authentication disabled")
} else { } else {
log.Println("Authentication enabled") if app.Server.ProxyAuthUsername == "" {
log.Println("Authentication enabled")
} else {
log.Println("Proxy Authentication enabled.")
}
} }
handler, err := restserver.NewHandler(&app.Server) handler, err := restserver.NewHandler(&app.Server)
@@ -135,12 +146,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
@@ -156,8 +179,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. Allowed versions are 1.2 or 1.3", 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

@@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@@ -94,7 +93,7 @@ func TestTLSSettings(t *testing.T) {
} }
func TestGetHandler(t *testing.T) { func TestGetHandler(t *testing.T) {
dir, err := ioutil.TempDir("", "rest-server-test") dir, err := os.MkdirTemp("", "rest-server-test")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -119,8 +118,14 @@ func TestGetHandler(t *testing.T) {
t.Errorf("NoAuth=true: expected no error, got %v", err) t.Errorf("NoAuth=true: expected no error, got %v", err)
} }
// With NoAuth = false, no .htpasswd and ProxyAuth = X-Remote-User
_, err = getHandler(&restserver.Server{Path: dir, ProxyAuthUsername: "X-Remote-User"})
if err != nil {
t.Errorf("NoAuth=false, ProxyAuthUsername = X-Remote-User: expected no error, got %v", err)
}
// With NoAuth = false and custom .htpasswd // With NoAuth = false and custom .htpasswd
htpFile, err := ioutil.TempFile(dir, "custom") htpFile, err := os.CreateTemp(dir, "custom")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -137,7 +142,7 @@ func TestGetHandler(t *testing.T) {
// Create .htpasswd // Create .htpasswd
htpasswd := filepath.Join(dir, ".htpasswd") htpasswd := filepath.Join(dir, ".htpasswd")
err = ioutil.WriteFile(htpasswd, []byte(""), 0644) err = os.WriteFile(htpasswd, []byte(""), 0644)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -262,7 +267,10 @@ func TestHttpListen(t *testing.T) {
if err != nil { if err != nil {
return err return err
} }
resp.Body.Close() err = resp.Body.Close()
if err != nil {
return err
}
if resp.StatusCode != test.StatusCode { if resp.StatusCode != test.StatusCode {
return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path) return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path)
} }

View File

@@ -9,8 +9,8 @@ fi
if [ -z "$2" ]; then if [ -z "$2" ]; then
# password from prompt # password from prompt
htpasswd -B $PASSWORD_FILE $1 htpasswd -B "$PASSWORD_FILE" "$1"
else else
# read password from command line # read password from command line
htpasswd -B -b $PASSWORD_FILE $1 $2 htpasswd -B -b "$PASSWORD_FILE" "$1" "$2"
fi fi

View File

@@ -5,4 +5,4 @@ if [ -z "$1" ]; then
exit 1 exit 1
fi fi
htpasswd -D $PASSWORD_FILE $1 htpasswd -D "$PASSWORD_FILE" "$1"

View File

@@ -6,7 +6,7 @@ if [ -n "$DISABLE_AUTHENTICATION" ]; then
OPTIONS="--no-auth $OPTIONS" OPTIONS="--no-auth $OPTIONS"
else else
if [ ! -f "$PASSWORD_FILE" ]; then if [ ! -f "$PASSWORD_FILE" ]; then
touch "$PASSWORD_FILE" ( umask 027 && touch "$PASSWORD_FILE" )
fi fi
if [ ! -s "$PASSWORD_FILE" ]; then if [ ! -s "$PASSWORD_FILE" ]; then

View File

@@ -26,8 +26,10 @@ RestartSec=5
# The following line must be customised to your individual requirements. # The following line must be customised to your individual requirements.
ReadWritePaths=/path/to/backups ReadWritePaths=/path/to/backups
# Makes created files group-readable, but inaccessible by others # Files in the data repository are only user accessible by default. Default to
UMask=027 # `UMask=077` for consistency. To make created files group-readable, set to
# `UMask=007` and pass `--group-accessible-repos` to rest-server via `ExecStart`.
UMask=077
# If your system doesn't support all of the features below (e.g. because of # If your system doesn't support all of the features below (e.g. because of
# the use of an older version of systemd), you may wish to comment-out # the use of an older version of systemd), you may wish to comment-out

28
go.mod
View File

@@ -1,28 +1,28 @@
module github.com/restic/rest-server module github.com/restic/rest-server
go 1.18 go 1.23.0
require ( require (
github.com/coreos/go-systemd/v22 v22.5.0 github.com/coreos/go-systemd/v22 v22.5.0
github.com/gorilla/handlers v1.5.2 github.com/gorilla/handlers v1.5.2
github.com/minio/sha256-simd v1.0.1 github.com/minio/sha256-simd v1.0.1
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.22.0
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.9.1
golang.org/x/crypto v0.25.0 golang.org/x/crypto v0.38.0
) )
require ( require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.6 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.33.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect google.golang.org/protobuf v1.36.5 // indirect
) )

68
go.sum
View File

@@ -1,46 +1,56 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA= github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
github.com/miolini/datacounter v1.0.3/go.mod h1:C45dc2hBumHjDpEU64IqPwR6TDyPVpzOqqRTN7zmBUA= github.com/miolini/datacounter v1.0.3/go.mod h1:C45dc2hBumHjDpEU64IqPwR6TDyPVpzOqqRTN7zmBUA=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -15,23 +15,26 @@ 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 TLSMinVer string
NoAuth bool TLS bool
AppendOnly bool NoAuth bool
PrivateRepos bool ProxyAuthUsername string
Prometheus bool AppendOnly bool
PrometheusNoAuth bool PrivateRepos bool
Debug bool Prometheus bool
MaxRepoSize int64 PrometheusNoAuth bool
PanicOnError bool Debug bool
NoVerifyUpload bool MaxRepoSize int64
PanicOnError bool
NoVerifyUpload bool
GroupAccessibleRepos bool
htpasswdFile *HtpasswdFile htpasswdFile *HtpasswdFile
quotaManager *quota.Manager quotaManager *quota.Manager
@@ -88,12 +91,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 +162,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 "/"

View File

@@ -6,7 +6,6 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
@@ -165,7 +164,7 @@ func createOverwriteDeleteSeq(t testing.TB, path string, data string) []TestRequ
return req return req
} }
func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string, string, func()) { func createTestHandler(t *testing.T, conf *Server) (http.Handler, string, string, string, func()) {
buf := make([]byte, 32) buf := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, buf) _, err := io.ReadFull(rand.Reader, buf)
if err != nil { if err != nil {
@@ -176,7 +175,7 @@ func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string,
fileID := hex.EncodeToString(dataHash[:]) fileID := hex.EncodeToString(dataHash[:])
// setup the server with a local backend in a temporary directory // setup the server with a local backend in a temporary directory
tempdir, err := ioutil.TempDir("", "rest-server-test-") tempdir, err := os.MkdirTemp("", "rest-server-test-")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -190,7 +189,7 @@ func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string,
} }
conf.Path = tempdir conf.Path = tempdir
mux, err := NewHandler(&conf) mux, err := NewHandler(conf)
if err != nil { if err != nil {
t.Fatalf("error from NewHandler: %v", err) t.Fatalf("error from NewHandler: %v", err)
} }
@@ -199,7 +198,7 @@ func createTestHandler(t *testing.T, conf Server) (http.Handler, string, string,
// TestResticAppendOnlyHandler runs tests on the restic handler code, especially in append-only mode. // TestResticAppendOnlyHandler runs tests on the restic handler code, especially in append-only mode.
func TestResticAppendOnlyHandler(t *testing.T) { func TestResticAppendOnlyHandler(t *testing.T) {
mux, data, fileID, _, cleanup := createTestHandler(t, Server{ mux, data, fileID, _, cleanup := createTestHandler(t, &Server{
AppendOnly: true, AppendOnly: true,
NoAuth: true, NoAuth: true,
Debug: true, Debug: true,
@@ -300,7 +299,7 @@ func createIdempotentDeleteSeq(t testing.TB, path string, data string) []TestReq
// TestResticHandler runs tests on the restic handler code, especially in append-only mode. // TestResticHandler runs tests on the restic handler code, especially in append-only mode.
func TestResticHandler(t *testing.T) { func TestResticHandler(t *testing.T) {
mux, data, fileID, _, cleanup := createTestHandler(t, Server{ mux, data, fileID, _, cleanup := createTestHandler(t, &Server{
NoAuth: true, NoAuth: true,
Debug: true, Debug: true,
PanicOnError: true, PanicOnError: true,
@@ -331,7 +330,7 @@ func TestResticHandler(t *testing.T) {
// TestResticErrorHandler runs tests on the restic handler error handling. // TestResticErrorHandler runs tests on the restic handler error handling.
func TestResticErrorHandler(t *testing.T) { func TestResticErrorHandler(t *testing.T) {
mux, _, _, tempdir, cleanup := createTestHandler(t, Server{ mux, _, _, tempdir, cleanup := createTestHandler(t, &Server{
AppendOnly: true, AppendOnly: true,
NoAuth: true, NoAuth: true,
Debug: true, Debug: true,
@@ -380,7 +379,7 @@ func TestResticErrorHandler(t *testing.T) {
} }
func TestEmptyList(t *testing.T) { func TestEmptyList(t *testing.T) {
mux, _, _, _, cleanup := createTestHandler(t, Server{ mux, _, _, _, cleanup := createTestHandler(t, &Server{
AppendOnly: true, AppendOnly: true,
NoAuth: true, NoAuth: true,
Debug: true, Debug: true,
@@ -404,7 +403,7 @@ func TestEmptyList(t *testing.T) {
} }
func TestListWithUnexpectedFiles(t *testing.T) { func TestListWithUnexpectedFiles(t *testing.T) {
mux, _, _, tempdir, cleanup := createTestHandler(t, Server{ mux, _, _, tempdir, cleanup := createTestHandler(t, &Server{
AppendOnly: true, AppendOnly: true,
NoAuth: true, NoAuth: true,
Debug: true, Debug: true,
@@ -510,7 +509,7 @@ func newDelayedErrorReader(err error) *delayErrorReader {
} }
} }
func (d *delayErrorReader) Read(p []byte) (int, error) { func (d *delayErrorReader) Read(_ []byte) (int, error) {
d.firstReadOnce.Do(func() { d.firstReadOnce.Do(func() {
// close the channel to signal that the first read has happened // close the channel to signal that the first read has happened
close(d.FirstRead) close(d.FirstRead)
@@ -522,7 +521,7 @@ func (d *delayErrorReader) Read(p []byte) (int, error) {
// TestAbortedRequest runs tests with concurrent upload requests for the same file. // TestAbortedRequest runs tests with concurrent upload requests for the same file.
func TestAbortedRequest(t *testing.T) { func TestAbortedRequest(t *testing.T) {
// the race condition doesn't happen for append-only repositories // the race condition doesn't happen for append-only repositories
mux, _, _, _, cleanup := createTestHandler(t, Server{ mux, _, _, _, cleanup := createTestHandler(t, &Server{
NoAuth: true, NoAuth: true,
Debug: true, Debug: true,
PanicOnError: true, PanicOnError: true,

View File

@@ -1,7 +1,6 @@
package restserver package restserver
import ( import (
"io/ioutil"
"os" "os"
"testing" "testing"
) )
@@ -12,7 +11,7 @@ func TestValidate(t *testing.T) {
rawPwd := "test" rawPwd := "test"
wrongPwd := "wrong" wrongPwd := "wrong"
tmpfile, err := ioutil.TempFile("", "rest-validate-") tmpfile, err := os.CreateTemp("", "rest-validate-")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

17
mux.go
View File

@@ -41,10 +41,17 @@ func (s *Server) checkAuth(r *http.Request) (username string, ok bool) {
if s.NoAuth { if s.NoAuth {
return username, true return username, true
} }
var password string if s.ProxyAuthUsername != "" {
username, password, ok = r.BasicAuth() username = r.Header.Get(s.ProxyAuthUsername)
if !ok || !s.htpasswdFile.Validate(username, password) { if username == "" {
return "", false return "", false
}
} else {
var password string
username, password, ok = r.BasicAuth()
if !ok || !s.htpasswdFile.Validate(username, password) {
return "", false
}
} }
return username, true return username, true
} }
@@ -66,7 +73,7 @@ func (s *Server) wrapMetricsAuth(f http.HandlerFunc) http.HandlerFunc {
// NewHandler returns the master HTTP multiplexer/router. // NewHandler returns the master HTTP multiplexer/router.
func NewHandler(server *Server) (http.Handler, error) { func NewHandler(server *Server) (http.Handler, error) {
if !server.NoAuth { if !server.NoAuth && server.ProxyAuthUsername == "" {
var err error var err error
if server.HtpasswdPath == "" { if server.HtpasswdPath == "" {
server.HtpasswdPath = filepath.Join(server.Path, ".htpasswd") server.HtpasswdPath = filepath.Join(server.Path, ".htpasswd")

80
mux_test.go Normal file
View File

@@ -0,0 +1,80 @@
package restserver
import (
"net/http/httptest"
"testing"
)
func TestCheckAuth(t *testing.T) {
tests := []struct {
name string
server *Server
requestHeaders map[string]string
basicAuth bool
basicUser string
basicPassword string
expectedUser string
expectedOk bool
}{
{
name: "NoAuth enabled",
server: &Server{
NoAuth: true,
},
expectedOk: true,
},
{
name: "Proxy Auth successful",
server: &Server{
ProxyAuthUsername: "X-Remote-User",
},
requestHeaders: map[string]string{
"X-Remote-User": "restic",
},
expectedUser: "restic",
expectedOk: true,
},
{
name: "Proxy Auth empty header",
server: &Server{
ProxyAuthUsername: "X-Remote-User",
},
requestHeaders: map[string]string{
"X-Remote-User": "",
},
expectedOk: false,
},
{
name: "Proxy Auth missing header",
server: &Server{
ProxyAuthUsername: "X-Remote-User",
},
expectedOk: false,
},
{
name: "Proxy Auth send but not enabled",
server: &Server{},
requestHeaders: map[string]string{
"X-Remote-User": "restic",
},
expectedOk: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
for header, value := range tt.requestHeaders {
req.Header.Set(header, value)
}
if tt.basicAuth {
req.SetBasicAuth(tt.basicUser, tt.basicPassword)
}
username, ok := tt.server.checkAuth(req)
if username != tt.expectedUser || ok != tt.expectedOk {
t.Errorf("expected (%v, %v), got (%v, %v)", tt.expectedUser, tt.expectedOk, username, ok)
}
})
}
}

View File

@@ -113,7 +113,7 @@ func tallySize(path string) (int64, error) {
path = "." path = "."
} }
var size int64 var size int64
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }

View File

@@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"math/rand" "math/rand"
"net/http" "net/http"
@@ -29,8 +28,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 +37,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 +54,14 @@ const DefaultDirMode os.FileMode = 0700
// overridden in the Options // overridden in the Options
const DefaultFileMode os.FileMode = 0600 const DefaultFileMode os.FileMode = 0600
// GroupAccessibleDirMode is the file mode used for directory creation when
// group access is enabled
const GroupAccessibleDirMode os.FileMode = 0770
// GroupAccessibleFileMode is the 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 +69,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,
@@ -251,7 +266,7 @@ func (h *Handler) wrapFileWriter(r *http.Request, w io.Writer) (io.Writer, int,
} }
// checkConfig checks whether a configuration exists. // checkConfig checks whether a configuration exists.
func (h *Handler) checkConfig(w http.ResponseWriter, r *http.Request) { func (h *Handler) checkConfig(w http.ResponseWriter, _ *http.Request) {
if h.opt.Debug { if h.opt.Debug {
log.Println("checkConfig()") log.Println("checkConfig()")
} }
@@ -267,13 +282,13 @@ func (h *Handler) checkConfig(w http.ResponseWriter, r *http.Request) {
} }
// getConfig allows for a config to be retrieved. // getConfig allows for a config to be retrieved.
func (h *Handler) getConfig(w http.ResponseWriter, r *http.Request) { func (h *Handler) getConfig(w http.ResponseWriter, _ *http.Request) {
if h.opt.Debug { if h.opt.Debug {
log.Println("getConfig()") log.Println("getConfig()")
} }
cfg := h.getSubPath("config") cfg := h.getSubPath("config")
bytes, err := ioutil.ReadFile(cfg) bytes, err := os.ReadFile(cfg)
if err != nil { if err != nil {
h.fileAccessError(w, err) h.fileAccessError(w, err)
return return
@@ -289,7 +304,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)
@@ -314,7 +329,7 @@ func (h *Handler) saveConfig(w http.ResponseWriter, r *http.Request) {
} }
// deleteConfig removes a config. // deleteConfig removes a config.
func (h *Handler) deleteConfig(w http.ResponseWriter, r *http.Request) { func (h *Handler) deleteConfig(w http.ResponseWriter, _ *http.Request) {
if h.opt.Debug { if h.opt.Debug {
log.Println("deleteConfig()") log.Println("deleteConfig()")
} }
@@ -369,7 +384,7 @@ func (h *Handler) listBlobsV1(w http.ResponseWriter, r *http.Request) {
} }
path := h.getSubPath(objectType) path := h.getSubPath(objectType)
items, err := ioutil.ReadDir(path) items, err := os.ReadDir(path)
if err != nil { if err != nil {
h.fileAccessError(w, err) h.fileAccessError(w, err)
return return
@@ -383,8 +398,8 @@ func (h *Handler) listBlobsV1(w http.ResponseWriter, r *http.Request) {
continue continue
} }
subpath := filepath.Join(path, i.Name()) subpath := filepath.Join(path, i.Name())
var subitems []os.FileInfo var subitems []os.DirEntry
subitems, err = ioutil.ReadDir(subpath) subitems, err = os.ReadDir(subpath)
if err != nil { if err != nil {
h.fileAccessError(w, err) h.fileAccessError(w, err)
return return
@@ -428,7 +443,7 @@ func (h *Handler) listBlobsV2(w http.ResponseWriter, r *http.Request) {
} }
path := h.getSubPath(objectType) path := h.getSubPath(objectType)
items, err := ioutil.ReadDir(path) items, err := os.ReadDir(path)
if err != nil { if err != nil {
h.fileAccessError(w, err) h.fileAccessError(w, err)
return return
@@ -442,17 +457,27 @@ func (h *Handler) listBlobsV2(w http.ResponseWriter, r *http.Request) {
continue continue
} }
subpath := filepath.Join(path, i.Name()) subpath := filepath.Join(path, i.Name())
var subitems []os.FileInfo var subitems []os.DirEntry
subitems, err = ioutil.ReadDir(subpath) subitems, err = os.ReadDir(subpath)
if err != nil { if err != nil {
h.fileAccessError(w, err) h.fileAccessError(w, err)
return return
} }
for _, f := range subitems { for _, f := range subitems {
blobs = append(blobs, Blob{Name: f.Name(), Size: f.Size()}) fi, err := f.Info()
if err != nil {
h.fileAccessError(w, err)
return
}
blobs = append(blobs, Blob{Name: f.Name(), Size: fi.Size()})
} }
} else { } else {
blobs = append(blobs, Blob{Name: i.Name(), Size: i.Size()}) fi, err := i.Info()
if err != nil {
h.fileAccessError(w, err)
return
}
blobs = append(blobs, Blob{Name: i.Name(), Size: fi.Size()})
} }
} }
@@ -545,15 +570,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 {
@@ -652,7 +677,7 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
h.sendMetric(objectType, BlobWrite, uint64(written)) h.sendMetric(objectType, BlobWrite, uint64(written))
} }
// tempFile implements a custom version of ioutil.TempFile which allows modifying the file permissions // tempFile implements a custom version of os.CreateTemp which allows modifying the file permissions
func tempFile(fn string, perm os.FileMode) (f *os.File, err error) { func tempFile(fn string, perm os.FileMode) (f *os.File, err error) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
name := fn + strconv.FormatInt(rand.Int63(), 10) name := fn + strconv.FormatInt(rand.Int63(), 10)
@@ -750,13 +775,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 +789,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
} }