231 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
Alexander Neumann
5d7b581db6 Generate CHANGELOG.md for 0.13.0 2024-07-26 14:47:50 +02:00
Alexander Neumann
37b4327012 Move changelog files for 0.13.0 2024-07-26 14:47:23 +02:00
Alexander Neumann
238eceb2a1 Update VERSION files for 0.13.0 2024-07-26 14:47:04 +02:00
Alexander Neumann
9fb75a71cb Udpate goreleaser config 2024-07-26 12:03:27 +02:00
rawtaz
38f29da143 Merge pull request #294 from restic/polish-changelogs
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
doc: Polish unreleased changelogs
2024-07-24 21:03:55 +02:00
Leo R. Lundgren
eb9f8cfa1f doc: Polish unreleased changelogs 2024-07-24 21:01:16 +02:00
Michael Eischer
e8a9fbc88f Merge pull request #290 from MichaelEischer/bump-dependencies
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 dependencies and replace deprecated goreleaser option
2024-07-10 21:11:49 +02:00
Michael Eischer
7b2639b21e replace deprecated goreleaser options 2024-07-06 17:40:45 +02:00
Michael Eischer
40fd34e7c8 bump dependencies 2024-07-06 17:40:45 +02:00
Michael Eischer
a6323b5e98 Merge pull request #288 from restic/dependabot/go_modules/github.com/spf13/cobra-1.8.1
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 github.com/spf13/cobra from 1.8.0 to 1.8.1
2024-06-26 19:26:28 +02:00
dependabot[bot]
318f0c6a89 Bump github.com/spf13/cobra from 1.8.0 to 1.8.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.0...v1.8.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 19:47:55 +00:00
Michael Eischer
add9b066f9 Merge pull request #285 from restic/dependabot/github_actions/golangci/golangci-lint-action-6
Bump golangci/golangci-lint-action from 5 to 6
2024-06-11 22:19:04 +02:00
Michael Eischer
e35f649f07 Merge pull request #287 from restic/dependabot/go_modules/golang.org/x/crypto-0.24.0
Bump golang.org/x/crypto from 0.22.0 to 0.24.0
2024-06-11 22:15:24 +02:00
dependabot[bot]
e309a92895 Bump golang.org/x/crypto from 0.22.0 to 0.24.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.22.0 to 0.24.0.
- [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.24.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-06-10 19:31:28 +00:00
dependabot[bot]
848e75c753 Bump golangci/golangci-lint-action from 5 to 6
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 5 to 6.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 19:32:17 +00:00
Michael Eischer
d3fedbefe5 Merge pull request #281 from restic/dependabot/github_actions/golangci/golangci-lint-action-5
Bump golangci/golangci-lint-action from 4 to 5
2024-05-06 21:22:34 +02:00
Michael Eischer
d8cbfd1f63 Merge pull request #280 from restic/dependabot/go_modules/golang.org/x/crypto-0.22.0
Bump golang.org/x/crypto from 0.21.0 to 0.22.0
2024-05-06 21:22:20 +02:00
dependabot[bot]
154efcee43 Bump golangci/golangci-lint-action from 4 to 5
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4 to 5.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 19:32:25 +00:00
dependabot[bot]
9d5b956858 Bump golang.org/x/crypto from 0.21.0 to 0.22.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.21.0 to 0.22.0.
- [Commits](https://github.com/golang/crypto/compare/v0.21.0...v0.22.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-04-08 19:41:12 +00:00
Michael Eischer
705c83a714 Merge pull request #278 from benjamonnguyen/update-compose-with-grafana-example
fix docker-compose.yml for compose-with-grafana example
2024-03-27 18:59:33 +01:00
Michael Eischer
421c542e31 Merge pull request #279 from restic/dependabot/go_modules/google.golang.org/protobuf-1.33.0
Bump google.golang.org/protobuf from 1.31.0 to 1.33.0
2024-03-27 18:49:14 +01:00
dependabot[bot]
c510ac2e41 Bump google.golang.org/protobuf from 1.31.0 to 1.33.0
Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 22:57:14 +00:00
Benjamin Nguyen
fed9c4b1e3 fix docker-compose.yml for compose-with-grafana example 2024-03-07 14:28:57 -08:00
Michael Eischer
a2b39539a3 Merge pull request #277 from restic/dependabot/go_modules/golang.org/x/crypto-0.21.0
Bump golang.org/x/crypto from 0.19.0 to 0.21.0
2024-03-07 13:31:29 +01:00
dependabot[bot]
16262def42 Bump golang.org/x/crypto from 0.19.0 to 0.21.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.19.0 to 0.21.0.
- [Commits](https://github.com/golang/crypto/compare/v0.19.0...v0.21.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-03-04 19:03:57 +00:00
Michael Eischer
0c735187ba Merge pull request #272 from ae-govau/unixsocket
Enhancement: add support for unix socket listen
2024-02-23 18:39:47 +01:00
Adam Eijdenberg
b4430d3607 address feedback, fix typo in example command 2024-02-23 15:26:06 +11:00
Adam Eijdenberg
e0674c6150 Enhancement: can now listen on a unix socket 2024-02-19 14:47:43 +11:00
Michael Eischer
55f43b815c Merge pull request #274 from restic/dependabot/go_modules/golang.org/x/crypto-0.19.0
Bump golang.org/x/crypto from 0.18.0 to 0.19.0
2024-02-18 12:11:33 +01:00
Michael Eischer
b3522ae2ac Merge pull request #275 from restic/dependabot/github_actions/golangci/golangci-lint-action-4
Bump golangci/golangci-lint-action from 3 to 4
2024-02-18 12:11:27 +01:00
dependabot[bot]
6faaaa713e Bump golangci/golangci-lint-action from 3 to 4
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3 to 4.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 19:37:48 +00:00
dependabot[bot]
a4f2823515 Bump golang.org/x/crypto from 0.18.0 to 0.19.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/crypto/compare/v0.18.0...v0.19.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-02-12 19:12:57 +00:00
Michael Eischer
13083acc1a Merge pull request #273 from ae-govau/gracefulshutdown
fix: shutdown gracefully on TERM or INT signals
2024-02-08 19:30:39 +01:00
Adam Eijdenberg
ce15402f74 fix: increase test timeout to 10s, listen on 127.0.0.1 only
This is to try to fix test reliability in GitHub actions.
2024-02-06 10:14:40 +11:00
Michael Eischer
b28bea1de4 Merge pull request #271 from ae-govau/printlistenaddr
fix: print the actual address listened on
2024-02-05 20:52:01 +01:00
Michael Eischer
4bfb2eaa81 simplify changelog for listen address 2024-02-05 20:50:09 +01:00
Adam Eijdenberg
8becd574cb fix: shutdown gracefully on TERM or INT signals
This allows server listen resources to be cleaned up appropriately.
2024-02-05 10:48:58 +11:00
Adam Eijdenberg
03ad2420db fix: print the actual address listened on
This is useful when the server is started with an ephemeral port, e.g.:

./rest-server --no-auth --listen "127.0.0.1:0"

...

start server on 127.0.0.1:46015
2024-02-05 10:44:25 +11:00
Michael Eischer
3ce6aaf2b6 Merge pull request #268 from restic/dependabot/go_modules/golang.org/x/crypto-0.18.0
Bump golang.org/x/crypto from 0.17.0 to 0.18.0
2024-01-13 19:47:30 +01:00
Michael Eischer
e388fcddde Merge pull request #270 from pagdot/patch-1
Update rest-server.socket with correct default rest-server port
2024-01-13 19:47:14 +01:00
pagdot
2d456efbe8 Update rest-server.socket with correct default rest-server port 2024-01-09 21:52:12 +01:00
dependabot[bot]
14f70457cf Bump golang.org/x/crypto from 0.17.0 to 0.18.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.18.0.
- [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.18.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-01-08 19:33:07 +00:00
Michael Eischer
29753193ac Merge pull request #266 from restic/dependabot/go_modules/github.com/prometheus/client_golang-1.18.0
Bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0
2024-01-06 22:20:52 +01:00
dependabot[bot]
e2d5251cbf Bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.17.0 to 1.18.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.17.0...v1.18.0)

---
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>
2024-01-06 21:16:41 +00:00
Michael Eischer
f36b6504fc Merge pull request #267 from MichaelEischer/require-go-1.18
Bump require go version to 1.18
2024-01-06 22:15:52 +01:00
Michael Eischer
04418c721f require go 1.18 2024-01-06 22:11:49 +01:00
Michael Eischer
c615b9dbdd Merge pull request #264 from restic/dependabot/github_actions/actions/setup-go-5
Bump actions/setup-go from 4 to 5
2024-01-06 22:00:39 +01:00
Michael Eischer
292556a8bc Merge pull request #265 from restic/dependabot/go_modules/golang.org/x/crypto-0.17.0
Bump golang.org/x/crypto from 0.15.0 to 0.17.0
2024-01-06 21:56:30 +01:00
dependabot[bot]
ef8998a1cc Bump golang.org/x/crypto from 0.15.0 to 0.17.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.15.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.15.0...v0.17.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>
2023-12-18 19:51:11 +00:00
dependabot[bot]
0c905fd64c Bump actions/setup-go from 4 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 19:26:37 +00:00
Michael Eischer
45f71173c8 Merge pull request #262 from restic/dependabot/go_modules/golang.org/x/crypto-0.15.0
Bump golang.org/x/crypto from 0.14.0 to 0.15.0
2023-11-13 22:08:05 +01:00
dependabot[bot]
ce63a80646 Bump golang.org/x/crypto from 0.14.0 to 0.15.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.15.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>
2023-11-13 19:23:42 +00:00
Michael Eischer
6f91a8638d Merge pull request #261 from restic/dependabot/go_modules/github.com/spf13/cobra-1.8.0
Bump github.com/spf13/cobra from 1.7.0 to 1.8.0
2023-11-11 21:45:21 +01:00
Michael Eischer
59d9e20cbd Merge pull request #260 from restic/dependabot/go_modules/github.com/gorilla/handlers-1.5.2
Bump github.com/gorilla/handlers from 1.5.1 to 1.5.2
2023-11-11 21:32:21 +01:00
dependabot[bot]
6aa3c966b6 Bump github.com/spf13/cobra from 1.7.0 to 1.8.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.7.0...v1.8.0)

---
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>
2023-11-06 19:52:35 +00:00
dependabot[bot]
228082e5ea Bump github.com/gorilla/handlers from 1.5.1 to 1.5.2
Bumps [github.com/gorilla/handlers](https://github.com/gorilla/handlers) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/handlers/releases)
- [Commits](https://github.com/gorilla/handlers/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/handlers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 19:52:31 +00:00
dependabot[bot]
8523f0c968 Merge pull request #255 from restic/dependabot/go_modules/github.com/prometheus/client_golang-1.17.0 2023-10-14 22:25:17 +00:00
dependabot[bot]
74b74ed325 Bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.16.0 to 1.17.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.16.0...v1.17.0)

---
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>
2023-10-14 22:23:04 +00:00
Michael Eischer
2dbad90967 Merge pull request #257 from restic/dependabot/go_modules/golang.org/x/crypto-0.14.0
Bump golang.org/x/crypto from 0.13.0 to 0.14.0
2023-10-15 00:22:26 +02:00
dependabot[bot]
04eacee642 Bump golang.org/x/crypto from 0.13.0 to 0.14.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/crypto/compare/v0.13.0...v0.14.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>
2023-10-09 19:10:29 +00:00
dependabot[bot]
1b1b127490 Merge pull request #254 from restic/dependabot/go_modules/golang.org/x/crypto-0.13.0 2023-09-11 19:43:10 +00:00
dependabot[bot]
f8669a2eba Bump golang.org/x/crypto from 0.12.0 to 0.13.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/crypto/compare/v0.12.0...v0.13.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>
2023-09-11 19:39:59 +00:00
dependabot[bot]
fb5d49c631 Merge pull request #252 from restic/dependabot/github_actions/actions/checkout-4 2023-09-06 20:31:03 +00:00
dependabot[bot]
e6efaaf65f Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [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/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 19:34:51 +00:00
Michael Eischer
8b90047951 Merge pull request #250 from restic/dependabot/go_modules/golang.org/x/crypto-0.12.0
Bump golang.org/x/crypto from 0.11.0 to 0.12.0
2023-08-13 18:59:15 +02:00
dependabot[bot]
29fea36169 Bump golang.org/x/crypto from 0.11.0 to 0.12.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.11.0 to 0.12.0.
- [Commits](https://github.com/golang/crypto/compare/v0.11.0...v0.12.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>
2023-08-07 19:14:27 +00:00
Michael Eischer
0bb8cd41d1 Merge pull request #246 from eriksjolund/adjust_restrict_address_families
Improve security of systemd service rest-server.service by restricting network access
2023-07-23 12:16:34 +02:00
Erik Sjölund
ec2ce8cd27 Improve security of rest-server.service by restricting network access
This patch improves the overall security assessment score given by
`systemd-analyze security rest-server.service` from "1.3 OK" to "0.6 SAFE"
(when using systemd-analyze version 253)

* Remove `AF_INET AF_INET6` from RestrictAddressFamilies.
  Sockets originating from socket activation are not affected by the
  systemd directive RestrictAddressFamilies.
  See systemd.exec man page.

* Add `PrivateNetwork=yes`
  as recommended for socket-activated services in the systemd.socket man page

* Add dependency on rest-server.socket

Signed-off-by: Erik Sjölund <erik.sjolund@gmail.com>
2023-07-17 08:52:26 +02:00
Michael Eischer
c38e18b708 Let goreleaser use custom release notes
If the changelog generation is disabled in goreleaser, this also ignores
custom release notes passed in via the command line. Thus keep the
default changelog configuration.
2023-07-09 21:32:35 +02:00
Alexander Neumann
76759aa52e Update version for development 2023-07-09 17:21:54 +02:00
Alexander Neumann
4231ef6f20 Generate CHANGELOG.md for 0.12.1 2023-07-09 17:17:10 +02:00
Alexander Neumann
5b288d488a Move changelog files for 0.12.1 2023-07-09 17:16:24 +02:00
Alexander Neumann
46e3c7248b Set version 2023-07-09 17:15:15 +02:00
Michael Eischer
fdf65f66e7 Merge pull request #243 from MichaelEischer/upgrade-dependencies
upgrade dependencies
2023-07-09 14:22:35 +02:00
Michael Eischer
32ab845151 upgrade dependencies 2023-07-09 14:12:47 +02:00
Michael Eischer
4bf4b62379 Merge pull request #242 from restic/dependabot/go_modules/github.com/prometheus/client_golang-1.16.0
Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0
2023-07-01 18:40:04 +02:00
dependabot[bot]
bb99f5a426 Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.1 to 1.16.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.15.1...v1.16.0)

---
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>
2023-07-01 16:14:06 +00:00
Michael Eischer
38b712c714 Merge pull request #241 from restic/dependabot/go_modules/golang.org/x/crypto-0.10.0
Bump golang.org/x/crypto from 0.9.0 to 0.10.0
2023-07-01 18:13:27 +02:00
dependabot[bot]
5f3faad48d Bump golang.org/x/crypto from 0.9.0 to 0.10.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/crypto/compare/v0.9.0...v0.10.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>
2023-06-19 20:02:25 +00:00
Michael Eischer
d6a268ca01 Merge pull request #240 from MichaelEischer/update-changelog-template
Update changelog template with changes from restic
2023-06-17 22:15:39 +02:00
Michael Eischer
d890dbad69 update changelog template with changes from restic 2023-06-17 22:08:58 +02:00
Michael Eischer
3130a4bcdf Merge pull request #239 from MichaelEischer/empty-list
Return empty array if there are no objects to list
2023-06-17 22:07:57 +02:00
Michael Eischer
ff81311a98 add changelog 2023-06-17 22:03:01 +02:00
Michael Eischer
9557efad55 Fix TestListWithUnexpectedFiles test
The test should test both API versions.
2023-06-17 21:57:58 +02:00
Michael Eischer
84a8b210f5 return empty array if there are no objects to list
Previously "null" was returned, which does not match the REST backend
specification.
2023-06-17 21:57:41 +02:00
Michael Eischer
ab45fb59ff Merge pull request #236 from restic/dependabot/go_modules/github.com/minio/sha256-simd-1.0.1
Bump github.com/minio/sha256-simd from 1.0.0 to 1.0.1
2023-05-30 23:13:31 +02:00
dependabot[bot]
3284c4ab73 Bump github.com/minio/sha256-simd from 1.0.0 to 1.0.1
Bumps [github.com/minio/sha256-simd](https://github.com/minio/sha256-simd) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/minio/sha256-simd/releases)
- [Commits](https://github.com/minio/sha256-simd/compare/v1.0.0...v1.0.1)

---
updated-dependencies:
- dependency-name: github.com/minio/sha256-simd
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 20:01:27 +00:00
Michael Eischer
0cd077f4ab Merge pull request #231 from MichaelEischer/fix-fsync-warning
Fix inverted condition for fsync warning
2023-05-18 16:36:04 +02:00
Michael Eischer
64ab92761c Merge pull request #234 from restic/dependabot/go_modules/golang.org/x/crypto-0.9.0
Bump golang.org/x/crypto from 0.8.0 to 0.9.0
2023-05-18 16:33:54 +02:00
dependabot[bot]
8ec316cea3 Bump golang.org/x/crypto from 0.8.0 to 0.9.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.8.0 to 0.9.0.
- [Commits](https://github.com/golang/crypto/compare/v0.8.0...v0.9.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>
2023-05-15 20:00:07 +00:00
Michael Eischer
420c4c6683 Add changelog for fsync warning 2023-05-13 22:05:18 +02:00
Michael Eischer
be14687a9c Print fsync warning only once
The repo.Handler is freshly instantiated for every request such that it
forget that the fsync warning was already printed. Use a single instance
in the Server instead.
2023-05-13 21:50:39 +02:00
Michael Eischer
dbb2d4690c Merge pull request #232 from restic/dependabot/go_modules/github.com/prometheus/client_golang-1.15.1
Bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1
2023-05-13 21:43:53 +02:00
Michael Eischer
0a09c8d633 Improve reproducibility of built archives
The binaries and generate archives should be reproducible now. Note that
this does not apply to the source tarball.
2023-05-10 06:42:01 +02:00
Michael Eischer
dedcb846df Enable multi-platform container builds in goreleaser
This requires `docker-buildx` and `qemu-user-static-binfmt`
2023-05-10 06:42:01 +02:00
dependabot[bot]
781d2241e0 Bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.0 to 1.15.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.15.0...v1.15.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 20:01:25 +00:00
Michael Eischer
b2e8044fbd Fix inverted condition for fsync warning
The warning should only be printed if fsync is _not_ supported and not
the other way around.
2023-05-08 21:40:23 +02:00
Michael Eischer
30ec84fcb7 Merge pull request #228 from MichaelEischer/update-ci
Update CI configuration and verify changelog entries
2023-05-05 23:05:22 +02:00
Michael Eischer
2a3bca1633 Fix type of changelog entry 2023-04-30 15:21:16 +02:00
Michael Eischer
aeb5e2982f CI: Automatically check changelog entries 2023-04-30 15:18:44 +02:00
Michael Eischer
20c4cdedfc CI: Sync tests with restic 2023-04-30 15:18:28 +02:00
Michael Eischer
c064e4c1ed Merge pull request #217 from HeikoSchlittermann/feat-log-to-stdout
feat: allow logging to stdout, stderr
2023-04-30 14:56:29 +02:00
Michael Eischer
66fe4afb7d Make changelog less technical 2023-04-30 14:51:46 +02:00
Michael Eischer
4576e1bc12 Fix docker container setup for goreleaser
The expected approach is that the binary built by goreleaser is added to
the docker container.
2023-04-28 20:41:30 +02:00
Michael Eischer
e8a839673f Set development version for 0.12.0 2023-04-27 21:41:32 +02:00
Michael Eischer
b34b9f0780 create folder for unreleased changelogs 2023-04-26 21:09:55 +02:00
Alexander Neumann
543d313f7e Fix Changelog file 2023-04-26 21:08:58 +02:00
Alexander Neumann
a98ed25c7b Rename changelog 2023-04-24 21:02:21 +02:00
Alexander Neumann
efe070c66d Add changelog, fix VERSION 2023-04-24 20:59:03 +02:00
Leo R. Lundgren
834a3378e9 doc: Polish changelogs 2023-04-24 13:15:03 +02:00
Heiko Schlittermann (HS12-RIPE)
9f074d8b3a feat: allow logging to stdout
The --log option accepts "-" as filename. This prevents rest-server from
opening the log file, it simply writes to the STDOUT stream provided by
the caller.

**BREAKING** in case use really used "-" to specify a file named "-"
you'll need to update your rest-server invocation to use "./-".
2023-04-24 11:15:38 +02:00
Leo R. Lundgren
94d5861c50 doc: Polish changelogs 2023-04-23 22:28:14 +02:00
Michael Eischer
253bebb096 Merge pull request #225 from restic/dependabot/go_modules/github.com/prometheus/client_golang-1.15.0
Bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0
2023-04-22 11:23:51 +02:00
Michael Eischer
337035c414 Merge pull request #221 from MichaelEischer/skip-files-in-intermediate-directories
Ignore unexpected files in intermediate directories
2023-04-22 11:20:11 +02:00
dependabot[bot]
399f7f1d49 Bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.14.0 to 1.15.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.14.0...v1.15.0)

---
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>
2023-04-17 20:04:17 +00:00
Michael Eischer
afcdb2f312 Merge pull request #224 from restic/dependabot/go_modules/github.com/spf13/cobra-1.7.0
Bump github.com/spf13/cobra from 1.6.1 to 1.7.0
2023-04-11 22:42:58 +02:00
dependabot[bot]
f97c48d92e Bump github.com/spf13/cobra from 1.6.1 to 1.7.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.6.1...v1.7.0)

---
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>
2023-04-11 20:39:21 +00:00
Michael Eischer
da9ce53a95 Merge pull request #223 from restic/dependabot/go_modules/golang.org/x/crypto-0.8.0
Bump golang.org/x/crypto from 0.7.0 to 0.8.0
2023-04-11 22:38:43 +02:00
dependabot[bot]
d795b70b9f Bump golang.org/x/crypto from 0.7.0 to 0.8.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.7.0 to 0.8.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.7.0...v0.8.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>
2023-04-10 20:04:10 +00:00
Michael Eischer
3a23555594 Merge pull request #202 from deajan/patch-1
Make dashboard portable
2023-04-08 21:22:22 +02:00
Michael Eischer
22a6412b81 Ignore unexpected files in intermediate directories
Listing the data/ folder in a repository no longer fails if it contains
files in the data/ folder. This also ignore .DS_Store files created by
macOS.
2023-04-08 20:17:44 +02:00
Michael Eischer
11c5a548e8 Merge pull request #216 from restic/dependabot/go_modules/golang.org/x/crypto-0.7.0
Bump golang.org/x/crypto from 0.6.0 to 0.7.0
2023-04-08 19:34:33 +02:00
Michael Eischer
c3d3bfbc12 Merge pull request #218 from restic/dependabot/github_actions/actions/setup-go-4
Bump actions/setup-go from 3 to 4
2023-04-07 12:35:13 +02:00
dependabot[bot]
f2f2d29064 Bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 20:04:09 +00:00
dependabot[bot]
dd0f03c9bb Bump golang.org/x/crypto from 0.6.0 to 0.7.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.6.0...v0.7.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>
2023-03-13 20:06:54 +00:00
Michael Eischer
ea461a2d76 Merge pull request #214 from restic/dependabot/go_modules/golang.org/x/crypto-0.6.0
Bump golang.org/x/crypto from 0.5.0 to 0.6.0
2023-02-19 13:54:43 +01:00
dependabot[bot]
40ad68bc5f Bump golang.org/x/crypto from 0.5.0 to 0.6.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.5.0...v0.6.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>
2023-02-13 20:09:40 +00:00
Michael Eischer
8f3b92325f Merge pull request #213 from 0xpr03/patch-1
warn about SystemCallArchitectures in the systemd service
2023-02-10 21:44:18 +01:00
Aron Heinecke
fb6a3a62cf warn about SystemCallArchitectures
Running 32bit on a 64bit host with SystemCallArchitectures will fail without much information
2023-02-01 00:10:46 +01:00
Michael Eischer
667ce6e26b Merge pull request #210 from ph818/patch-1
add MemoryHigh to systemd example
2023-01-26 22:36:29 +01:00
ph818
091e70d903 add MemoryHigh to systemd example 2023-01-21 21:43:56 -05:00
Michael Eischer
f5c06a2e45 Merge pull request #208 from MichaelEischer/upgrade-dependencies
Upgrade dependencies
2023-01-20 22:42:58 +01:00
Michael Eischer
40e2a8b1e4 add changelog and update readme 2023-01-20 22:36:40 +01:00
Michael Eischer
fa795cc66a upgrade go-systemd to latest major version 2023-01-14 21:00:32 +01:00
Michael Eischer
99ec5e2dbb CI: update and sync ci tasks with restic 2023-01-14 20:39:32 +01:00
Michael Eischer
f299c735df bump minimum go version to 1.17 2023-01-14 19:22:08 +01:00
Michael Eischer
8ce88b24e7 enable dependabot 2023-01-14 19:18:10 +01:00
Michael Eischer
2eae8c9266 update transitive dependencies 2023-01-14 19:17:53 +01:00
Michael Eischer
30fbd043b9 update direct dependencies 2023-01-14 19:09:42 +01:00
Michael Eischer
7f29dcbd69 Merge pull request #207 from MichaelEischer/err-on-args
Error out on unexpected command line argument
2023-01-12 22:00:36 +01:00
Michael Eischer
43c96fb6f2 Error out on unexpected command line argument
rest-server doesn't accept arguments. Thus, error out to prevent wrong
usage.
2023-01-11 21:47:25 +01:00
Orsiris de Jong
c1c48c62e9 Make dashboard portable
Allows to select datasource when first importing into grafana
2022-12-28 19:44:32 +01:00
Michael Eischer
a8cd3f218d Merge pull request #199 from MichaelEischer/support-macos-ventura
Ignore fsync errors
2022-12-16 23:13:43 +01:00
Michael Eischer
80babf98e7 add fsync warning 2022-12-02 21:08:15 +01:00
Michael Eischer
408dcab92e Port fsync error handling from restic
This ignores several different combinations of errnos which are returned
if the storage destination is not able to fsync correctly.

See also https://github.com/restic/restic/pull/4021
2022-11-11 22:37:02 +01:00
Michael Eischer
2dd87ced0a Merge pull request #195 from MichaelEischer/internal-error-for-inaccessible-files
Return "internal server error" if files cannot be read
2022-10-07 22:53:41 +02:00
Michael Eischer
f763db8934 Deduplicate handler initialization in tests 2022-09-02 23:41:05 +02:00
Michael Eischer
a8fdca3b9f make deleting idempotent 2022-09-02 23:32:52 +02:00
Michael Eischer
b562edefd1 add changelog for not found error handling 2022-08-31 22:29:14 +02:00
Michael Eischer
cb2afaa4c0 test that inaccessible files result in status 'internal error' 2022-08-31 22:29:14 +02:00
Michael Eischer
991c459be3 Only return "Not Found" status if file does not exist
All other errors are now turned into a internal server error and are
logged.
2022-08-31 22:29:14 +02:00
MichaelEischer
9f4fba2c21 Merge pull request #138 from MichaelEischer/cache-basic-auth
Cache basic auth credentials
2022-07-02 21:19:46 +02:00
Michael Eischer
65fd8be3f8 Add changelog for cached basic auth 2022-07-02 21:17:57 +02:00
Michael Eischer
274f29fee8 refactor password check into separate function 2022-06-21 00:18:05 +02:00
Michael Eischer
98f0aaca1c convert htpasswd regexes to globals 2022-06-21 00:18:05 +02:00
Michael Eischer
5a6ed2ffdf use constant time comparison for sha1 password hash 2022-06-21 00:18:05 +02:00
Michael Eischer
46b020fd9e Add basic test 2022-06-21 00:18:05 +02:00
Michael Eischer
df9eb337d3 Extend htpasswd auth cache entry expiry on use 2022-06-21 00:18:05 +02:00
Michael Eischer
1eeca53812 Try to zero htpasswd cache entries before deletion 2022-06-21 00:18:05 +02:00
Michael Eischer
0bdc420e75 Cache successful basic auth credentials for a minute
This stores a hash of the username + password in map which is indexed by
the username. Indexing by username avoids accidentally introducing a
timing side-channel as a successful/failed lookup only provides
information on whether a cache entry exists for a username or not.

Hashing the username and password together makes it simple to get a
constant-time string comparison as we no longer have to worry about
string length differences.

Expriy is done by a goroutine which every few seconds checks for expired
cache entries and removes those.
2022-06-21 00:18:05 +02:00
Michael Eischer
b0036d006b Unexport users map in htpasswd struct 2022-06-21 00:18:05 +02:00
rawtaz
6bc87b8e95 Merge pull request #188 from dwmunster/f-config-htpasswd
Add configurable htpasswd file location
2022-06-20 23:40:49 +02:00
Alexander Neumann
d24ffc13d8 Fix tests 2022-04-15 09:38:23 +02:00
Alexander Neumann
a87a50ad11 Sync settinsg with restic, require Go >= 1.15 2022-04-15 09:38:23 +02:00
Alexander Neumann
8538ce7859 Copy golangci-lint settings from restic 2022-04-15 09:28:56 +02:00
Drayton Munster
87cef8f159 Clarify file accessibility for PASSWORD_FILE in docker 2022-03-25 22:36:54 -04:00
Drayton Munster
bc1545c717 Clarifying descriptions as suggested in PR review 2022-03-25 22:25:48 -04:00
Drayton Munster
3903ed000c Add configurable htpasswd location 2022-03-25 15:39:20 -04:00
MichaelEischer
cb85fb38c0 Merge pull request #184 from lgommans/patch-1
Be more clear in --help what the value of --log is supposed to be
2022-02-18 21:04:07 +01:00
Luc Gommans
1fd9538f73 Reword --log option in --help output 2022-02-17 23:33:42 +01:00
rawtaz
af36b77ece Merge pull request #183 from MichaelEischer/allow-underscore
Allow underscores in usernames
2022-02-12 21:18:05 +01:00
Michael Eischer
48067dc896 htpasswd: allow underscores in usernames 2022-02-12 21:13:40 +01:00
Alexander Neumann
096ac5a9c8 Remove plan9, rest-server fails to build 2022-02-10 20:02:10 +01:00
60 changed files with 2139 additions and 1343 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.

13
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
version: 2
updates:
# Dependencies listed in go.mod
- package-ecosystem: "gomod"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
# Dependencies listed in .github/workflows/*.yml
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@@ -7,61 +7,113 @@ on:
# run tests for all pull requests # run tests for all pull requests
pull_request: pull_request:
merge_group:
permissions:
contents: read
env:
latest_go: "1.24.x"
GO111MODULE: on
jobs: jobs:
test: test:
strategy: strategy:
matrix: matrix:
go: include:
- 1.14.x - job_name: Linux
- 1.15.x go: 1.24.x
- 1.16.x os: ubuntu-latest
- 1.17.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@v2 uses: actions/setup-go@v6
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Check out code
uses: actions/checkout@v2
- 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
run: |
echo "install calens"
go install github.com/restic/calens@latest
echo "check changelog files"
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 - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v5
- name: Set up Go ${{ env.latest_go }}
uses: actions/setup-go@v6
with:
go-version: ${{ env.latest_go }}
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v2 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.41 version: v1.64.8
args: --verbose --timeout 5m args: --verbose --timeout 5m
# only run golangci-lint for pull requests, otherwise ALL hints get
# reported. We need to slowly address all issues until we can enable
# linting the master branch :)
if: github.event_name == 'pull_request'
- name: Check go.mod/go.sum - name: Check go.mod/go.sum
run: | run: |
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
@@ -51,7 +47,10 @@ issues:
# list of things to not warn about # list of things to not warn about
exclude: exclude:
# golint: do not warn about missing comments for exported stuff # revive: do not warn about missing comments for exported stuff
- exported (function|method|var|type|const) `.*` should have comment or be unexported - exported (function|method|var|type|const) .* should have comment or be unexported
# golint: 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

@@ -1,4 +1,6 @@
--- ---
version: 2
before: before:
# Run a few commands to check the state of things. When anything is changed # Run a few commands to check the state of things. When anything is changed
# in files commited to the repo, goreleaser will abort before building # in files commited to the repo, goreleaser will abort before building
@@ -19,37 +21,36 @@ 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
ldflags: mod_timestamp: "{{ .CommitTimestamp }}"
# set the version variable in the main package
ldflags: &build_ldflags # 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
- dragonfly - dragonfly
- plan9
- solaris - solaris
goarch: goarch:
- amd64 - amd64
- 386 - "386"
- arm - arm
- arm64 - arm64
- mips - mips
@@ -58,20 +59,55 @@ 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
owner: root
group: root
mtime: "{{ .CommitDate }}"
mode: 0644
# add these files to all archives # add these files to all archives
files: files: &archive_files
- LICENSE - src: LICENSE
- README.md dst: LICENSE
- CHANGELOG.md info: *archive_file_info
- src: README.md
dst: README.md
info: *archive_file_info
- src: CHANGELOG.md
dst: CHANGELOG.md
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:
@@ -79,7 +115,7 @@ source:
# 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:
@@ -92,18 +128,117 @@ signs:
- "--detach-sign" - "--detach-sign"
- "${artifact}" - "${artifact}"
# do not generate a changelog file, we're using calens for that
changelog:
skip: true
# configure building the rest-server docker image # configure building the rest-server docker image
dockers: dockers:
- image_templates: - image_templates:
- restic/rest-server:latest - restic/rest-server:{{ .Version }}-amd64
- restic/rest-server:{{ .Version }}
build_flag_templates: build_flag_templates:
- "--platform=linux/amd64"
- "--pull" - "--pull"
extra_files: - "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.source=https://github.com/restic/{{ .ProjectName }}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.licenses=BSD-2-Clause"
use: buildx
dockerfile: "Dockerfile.goreleaser"
extra_files: &extra_files
- docker/create_user - docker/create_user
- docker/delete_user - docker/delete_user
- docker/entrypoint.sh - docker/entrypoint.sh
- image_templates:
- restic/rest-server:{{ .Version }}-i386
goarch: "386"
build_flag_templates:
- "--platform=linux/386"
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.source=https://github.com/restic/{{ .ProjectName }}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.licenses=BSD-2-Clause"
use: buildx
dockerfile: "Dockerfile.goreleaser"
extra_files: *extra_files
- image_templates:
- restic/rest-server:{{ .Version }}-arm32v6
goarch: arm
goarm: 6
build_flag_templates:
- "--platform=linux/arm/v6"
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.source=https://github.com/restic/{{ .ProjectName }}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.licenses=BSD-2-Clause"
use: buildx
dockerfile: "Dockerfile.goreleaser"
extra_files: *extra_files
- image_templates:
- restic/rest-server:{{ .Version }}-arm32v7
goarch: arm
goarm: 7
build_flag_templates:
- "--platform=linux/arm/v7"
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.source=https://github.com/restic/{{ .ProjectName }}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.licenses=BSD-2-Clause"
use: buildx
dockerfile: "Dockerfile.goreleaser"
extra_files: *extra_files
- image_templates:
- restic/rest-server:{{ .Version }}-arm64v8
goarch: arm64
build_flag_templates:
- "--platform=linux/arm64/v8"
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.source=https://github.com/restic/{{ .ProjectName }}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.licenses=BSD-2-Clause"
use: buildx
dockerfile: "Dockerfile.goreleaser"
extra_files: *extra_files
- image_templates:
- restic/rest-server:{{ .Version }}-ppc64le
goarch: ppc64le
build_flag_templates:
- "--platform=linux/ppc64le"
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.source=https://github.com/restic/{{ .ProjectName }}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.licenses=BSD-2-Clause"
use: buildx
dockerfile: "Dockerfile.goreleaser"
extra_files: *extra_files
docker_manifests:
- name_template: "restic/rest-server:{{ .Version }}"
image_templates:
- "restic/rest-server:{{ .Version }}-amd64"
- "restic/rest-server:{{ .Version }}-i386"
- "restic/rest-server:{{ .Version }}-arm32v6"
- "restic/rest-server:{{ .Version }}-arm32v7"
- "restic/rest-server:{{ .Version }}-arm64v8"
- "restic/rest-server:{{ .Version }}-ppc64le"
- name_template: "restic/rest-server:latest"
image_templates:
- "restic/rest-server:{{ .Version }}-amd64"
- "restic/rest-server:{{ .Version }}-i386"
- "restic/rest-server:{{ .Version }}-arm32v6"
- "restic/rest-server:{{ .Version }}-arm32v7"
- "restic/rest-server:{{ .Version }}-arm64v8"
- "restic/rest-server:{{ .Version }}-ppc64le"

View File

@@ -1,3 +1,300 @@
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)
============================================
The following sections list the changes in rest-server 0.13.0 relevant
to users. The changes are ordered by importance.
Summary
-------
* Chg #267: Update dependencies and require Go 1.18 or newer
* Chg #273: Shut down cleanly on TERM and INT signals
* Enh #271: Print listening address after start-up
* Enh #272: Support listening on a unix socket
Details
-------
* Change #267: Update dependencies and require Go 1.18 or newer
Most dependencies have been updated. Since some libraries require newer language
features, support for Go 1.17 has been dropped, which means that rest-server now
requires at least Go 1.18 to build.
https://github.com/restic/rest-server/pull/267
* Change #273: Shut down cleanly on TERM and INT signals
Rest-server now listens for TERM and INT signals and cleanly closes down the
http.Server and listener when receiving either of them.
This is particularly useful when listening on a unix socket, as the server will
now remove the socket file when it shuts down.
https://github.com/restic/rest-server/pull/273
* Enhancement #271: Print listening address after start-up
When started with `--listen :0`, rest-server would print `start server on :0`
The message now also includes the actual address listened on, for example `start
server on 0.0.0.0:37333`. This is useful when starting a server with an
auto-allocated free port number (port 0).
https://github.com/restic/rest-server/pull/271
* Enhancement #272: Support listening on a unix socket
It is now possible to make rest-server listen on a unix socket by prefixing the
socket filename with `unix:` and passing it to the `--listen` option, for
example `--listen unix:/tmp/foo`.
This is useful in combination with remote port forwarding to enable a remote
server to backup locally, e.g.:
```
rest-server --listen unix:/tmp/foo &
ssh -R /tmp/foo:/tmp/foo user@host restic -r rest:http+unix:///tmp/foo:/repo backup
```
https://github.com/restic/rest-server/pull/272
Changelog for rest-server 0.12.1 (2023-07-09)
============================================
The following sections list the changes in rest-server 0.12.1 relevant
to users. The changes are ordered by importance.
Summary
-------
* Fix #230: Fix erroneous warnings about unsupported fsync
* Fix #238: API: Return empty array when listing empty folders
* Enh #217: Log to stdout using the `--log -` option
Details
-------
* Bugfix #230: Fix erroneous warnings about unsupported fsync
Due to a regression in rest-server 0.12.0, it continuously printed `WARNING:
fsync is not supported by the data storage. This can lead to data loss, if the
system crashes or the storage is unexpectedly disconnected.` for systems that
support fsync. We have fixed the warning.
https://github.com/restic/rest-server/issues/230
https://github.com/restic/rest-server/pull/231
* Bugfix #238: API: Return empty array when listing empty folders
Rest-server returned `null` when listing an empty folder. This has been changed
to returning an empty array in accordance with the REST protocol specification.
This change has no impact on restic users.
https://github.com/restic/rest-server/issues/238
https://github.com/restic/rest-server/pull/239
* Enhancement #217: Log to stdout using the `--log -` option
Logging to stdout was possible using `--log /dev/stdout`. However, when the rest
server is run as a different user, for example, using
`sudo -u restic rest-server [...] --log /dev/stdout`
This did not work due to permission issues.
For logging to stdout, the `--log` option now supports the special filename `-`
which also works in these cases.
https://github.com/restic/rest-server/pull/217
Changelog for rest-server 0.12.0 (2023-04-24)
============================================
The following sections list the changes in rest-server 0.12.0 relevant
to users. The changes are ordered by importance.
Summary
-------
* Fix #183: Allow usernames containing underscore and more
* Fix #219: Ignore unexpected files in the data/ folder
* Fix #1871: Return 500 "Internal server error" if files cannot be read
* Chg #207: Return error if command-line arguments are specified
* Chg #208: Update dependencies and require Go 1.17 or newer
* Enh #133: Cache basic authentication credentials
* Enh #187: Allow configurable location for `.htpasswd` file
Details
-------
* Bugfix #183: Allow usernames containing underscore and more
The security fix in rest-server 0.11.0 (#131) disallowed usernames containing
and underscore "_". The list of allowed characters has now been changed to
include Unicode characters, numbers, "_", "-", "." and "@".
https://github.com/restic/rest-server/issues/183
https://github.com/restic/rest-server/pull/184
* Bugfix #219: Ignore unexpected files in the data/ folder
If the data folder of a repository contained files, this would prevent restic
from retrieving a list of file data files. This has been fixed. As a workaround
remove the files that are directly contained in the data folder (e.g.,
`.DS_Store` files).
https://github.com/restic/rest-server/issues/219
https://github.com/restic/rest-server/pull/221
* Bugfix #1871: Return 500 "Internal server error" if files cannot be read
When files in a repository cannot be read by rest-server, for example after
running `restic prune` directly on the server hosting the repositories in a way
that causes filesystem permissions to be wrong, rest-server previously returned
404 "Not Found" as status code. This was causing confusing for users.
The error handling has now been fixed to only return 404 "Not Found" if the file
actually does not exist. Otherwise a 500 "Internal server error" is reported to
the client and the underlying error is logged at the server side.
https://github.com/restic/rest-server/issues/1871
https://github.com/restic/rest-server/pull/195
* Change #207: Return error if command-line arguments are specified
Command line arguments are ignored by rest-server, but there was previously no
indication of this when they were supplied anyway.
To prevent usage errors an error is now printed when command line arguments are
supplied, instead of them being silently ignored.
https://github.com/restic/rest-server/pull/207
* Change #208: Update dependencies and require Go 1.17 or newer
Most dependencies have been updated. Since some libraries require newer language
features, support for Go 1.15-1.16 has been dropped, which means that
rest-server now requires at least Go 1.17 to build.
https://github.com/restic/rest-server/pull/208
* Enhancement #133: Cache basic authentication credentials
To speed up the verification of basic auth credentials, rest-server now caches
passwords for a minute in memory. That way the expensive verification of basic
auth credentials can be skipped for most requests issued by a single restic run.
The password is kept in memory in a hashed form and not as plaintext.
https://github.com/restic/rest-server/issues/133
https://github.com/restic/rest-server/pull/138
* Enhancement #187: Allow configurable location for `.htpasswd` file
It is now possible to specify the location of the `.htpasswd` file using the
`--htpasswd-file` option.
https://github.com/restic/rest-server/issues/187
https://github.com/restic/rest-server/pull/188
Changelog for rest-server 0.11.0 (2022-02-10) Changelog for rest-server 0.11.0 (2022-02-10)
============================================ ============================================
@@ -10,10 +307,10 @@ Summary
* Sec #131: Prevent loading of usernames containing a slash * Sec #131: Prevent loading of usernames containing a slash
* Fix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION` * Fix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION`
* Fix #142: Fix possible data loss due to interrupted network connections * Fix #142: Fix possible data loss due to interrupted network connections
* Fix #157: Use platform-specific temporary directory as default data directory
* Fix #155: Reply "insufficient storage" on disk full or over-quota * Fix #155: Reply "insufficient storage" on disk full or over-quota
* Chg #146: Build rest-server at docker container build time * Fix #157: Use platform-specific temporary directory as default data directory
* Chg #112: Add subrepo support and refactor server code * Chg #112: Add subrepo support and refactor server code
* Chg #146: Build rest-server at docker container build time
* Enh #122: Verify uploaded files * Enh #122: Verify uploaded files
* Enh #126: Allow running rest-server via systemd socket activation * Enh #126: Allow running rest-server via systemd socket activation
* Enh #148: Expand use of security features in example systemd unit file * Enh #148: Expand use of security features in example systemd unit file
@@ -23,11 +320,12 @@ Details
* Security #131: Prevent loading of usernames containing a slash * Security #131: Prevent loading of usernames containing a slash
"/" is valid char in HTTP authorization headers, but is also used in rest-server to map "/" is valid char in HTTP authorization headers, but is also used in rest-server
usernames to private repos. to map usernames to private repos.
This commit prevents loading maliciously composed usernames like "/foo/config" by This commit prevents loading maliciously composed usernames like "/foo/config"
restricting the allowed characters to the unicode character class, numbers, "-", "." and "@". by restricting the allowed characters to the unicode character class, numbers,
"-", "." and "@".
This prevents requests to other users files like: This prevents requests to other users files like:
@@ -39,92 +337,100 @@ Details
* Bugfix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION` * Bugfix #119: Fix Docker configuration for `DISABLE_AUTHENTICATION`
Rest-server 0.10.0 introduced a regression which caused the `DISABLE_AUTHENTICATION` Rest-server 0.10.0 introduced a regression which caused the
environment variable to stop working for the Docker container. This has been fixed by `DISABLE_AUTHENTICATION` environment variable to stop working for the Docker
automatically setting the option `--no-auth` to disable authentication. container. This has been fixed by automatically setting the option `--no-auth`
to disable authentication.
https://github.com/restic/rest-server/issues/119 https://github.com/restic/rest-server/issues/119
https://github.com/restic/rest-server/pull/124 https://github.com/restic/rest-server/pull/124
* Bugfix #142: Fix possible data loss due to interrupted network connections * Bugfix #142: Fix possible data loss due to interrupted network connections
When rest-server was run without `--append-only` it was possible to lose uploaded files in a When rest-server was run without `--append-only` it was possible to lose
specific scenario in which a network connection was interrupted. uploaded files in a specific scenario in which a network connection was
interrupted.
For the data loss to occur a file upload by restic would have to be interrupted such that restic For the data loss to occur a file upload by restic would have to be interrupted
notices the interrupted network connection before the rest-server. Then restic would have to such that restic notices the interrupted network connection before the
retry the file upload and finish it before the rest-server notices that the initial upload has rest-server. Then restic would have to retry the file upload and finish it
failed. Then the uploaded file would be accidentally removed by rest-server when trying to before the rest-server notices that the initial upload has failed. Then the
uploaded file would be accidentally removed by rest-server when trying to
cleanup the failed upload. cleanup the failed upload.
This has been fixed by always uploading to a temporary file first which is moved in position only This has been fixed by always uploading to a temporary file first which is moved
once it was uploaded completely. in position only once it was uploaded completely.
https://github.com/restic/rest-server/pull/142 https://github.com/restic/rest-server/pull/142
* Bugfix #157: Use platform-specific temporary directory as default data directory
If no data directory is specificed, then rest-server now uses the Go standard library
functions to retrieve the standard temporary directory path for the current platform.
https://github.com/restic/rest-server/issues/157
https://github.com/restic/rest-server/pull/158
* Bugfix #155: Reply "insufficient storage" on disk full or over-quota * Bugfix #155: Reply "insufficient storage" on disk full or over-quota
When there was no space left on disk, or any other write-related error occurred, rest-server When there was no space left on disk, or any other write-related error occurred,
replied with HTTP status code 400 (Bad request). This is misleading (restic client will dump rest-server replied with HTTP status code 400 (Bad request). This is misleading
the status code to the user). (restic client will dump the status code to the user).
Rest-server now replies with two different status codes in these situations: * HTTP 507 Rest-server now replies with two different status codes in these situations: *
"Insufficient storage" is the status on disk full or repository over-quota * HTTP 500 HTTP 507 "Insufficient storage" is the status on disk full or repository
"Internal server error" is used for other disk-related errors over-quota * HTTP 500 "Internal server error" is used for other disk-related
errors
https://github.com/restic/rest-server/issues/155 https://github.com/restic/rest-server/issues/155
https://github.com/restic/rest-server/pull/160 https://github.com/restic/rest-server/pull/160
* Change #146: Build rest-server at docker container build time * Bugfix #157: Use platform-specific temporary directory as default data directory
The Dockerfile now includes a build stage such that the latest rest-server is always built and If no data directory is specificed, then rest-server now uses the Go standard
packaged. This is done in a standard golang container to ensure a clean build environment and library functions to retrieve the standard temporary directory path for the
only the final binary is shipped rather than the whole build environment. current platform.
https://github.com/restic/rest-server/issues/146 https://github.com/restic/rest-server/issues/157
https://github.com/restic/rest-server/pull/145 https://github.com/restic/rest-server/pull/158
* Change #112: Add subrepo support and refactor server code * Change #112: Add subrepo support and refactor server code
Support for multi-level repositories has been added, so now each user can have its own Support for multi-level repositories has been added, so now each user can have
subrepositories. This feature is always enabled. its own subrepositories. This feature is always enabled.
Authentication for the Prometheus /metrics endpoint can now be disabled with the new Authentication for the Prometheus /metrics endpoint can now be disabled with the
`--prometheus-no-auth` flag. new `--prometheus-no-auth` flag.
We have split out all HTTP handling to a separate `repo` subpackage to cleanly separate the We have split out all HTTP handling to a separate `repo` subpackage to cleanly
server code from the code that handles a single repository. The new RepoHandler also makes it separate the server code from the code that handles a single repository. The new
easier to reuse rest-server as a Go component in any other HTTP server. RepoHandler also makes it easier to reuse rest-server as a Go component in any
other HTTP server.
The refactoring makes the code significantly easier to follow and understand, which in turn The refactoring makes the code significantly easier to follow and understand,
makes it easier to add new features, audit for security and debug issues. which in turn makes it easier to add new features, audit for security and debug
issues.
https://github.com/restic/rest-server/issues/109 https://github.com/restic/rest-server/issues/109
https://github.com/restic/rest-server/issues/107 https://github.com/restic/rest-server/issues/107
https://github.com/restic/rest-server/pull/112 https://github.com/restic/rest-server/pull/112
* Change #146: Build rest-server at docker container build time
The Dockerfile now includes a build stage such that the latest rest-server is
always built and packaged. This is done in a standard golang container to ensure
a clean build environment and only the final binary is shipped rather than the
whole build environment.
https://github.com/restic/rest-server/issues/146
https://github.com/restic/rest-server/pull/145
* Enhancement #122: Verify uploaded files * Enhancement #122: Verify uploaded files
The rest-server now by default verifies that the hash of content of uploaded files matches The rest-server now by default verifies that the hash of content of uploaded
their filename. This ensures that transmission errors are detected and forces restic to retry files matches their filename. This ensures that transmission errors are detected
the upload. On low-power devices it can make sense to disable this check by passing the and forces restic to retry the upload. On low-power devices it can make sense to
`--no-verify-upload` flag. disable this check by passing the `--no-verify-upload` flag.
https://github.com/restic/rest-server/issues/122 https://github.com/restic/rest-server/issues/122
https://github.com/restic/rest-server/pull/130 https://github.com/restic/rest-server/pull/130
* Enhancement #126: Allow running rest-server via systemd socket activation * Enhancement #126: Allow running rest-server via systemd socket activation
We've added the option to have systemd create the listening socket and start the rest-server on We've added the option to have systemd create the listening socket and start the
demand. rest-server on demand.
https://github.com/restic/rest-server/issues/126 https://github.com/restic/rest-server/issues/126
https://github.com/restic/rest-server/pull/151 https://github.com/restic/rest-server/pull/151
@@ -132,9 +438,9 @@ Details
* Enhancement #148: Expand use of security features in example systemd unit file * Enhancement #148: Expand use of security features in example systemd unit file
The example systemd unit file now enables additional systemd features to mitigate potential The example systemd unit file now enables additional systemd features to
security vulnerabilities in rest-server and the various packages and operating system mitigate potential security vulnerabilities in rest-server and the various
components which it relies upon. packages and operating system components which it relies upon.
https://github.com/restic/rest-server/issues/148 https://github.com/restic/rest-server/issues/148
https://github.com/restic/rest-server/pull/149 https://github.com/restic/rest-server/pull/149
@@ -149,60 +455,64 @@ to users. The changes are ordered by importance.
Summary Summary
------- -------
* Sec #117: Stricter path sanitization
* Sec #60: Require auth by default, add --no-auth flag * Sec #60: Require auth by default, add --no-auth flag
* Sec #64: Refuse overwriting config file in append-only mode * Sec #64: Refuse overwriting config file in append-only mode
* Sec #117: Stricter path sanitization
* Chg #102: Remove vendored dependencies * Chg #102: Remove vendored dependencies
* Enh #44: Add changelog file * Enh #44: Add changelog file
Details Details
------- -------
* Security #117: Stricter path sanitization
The framework we're using in rest-server to decode paths to repositories allowed specifying
URL-encoded characters in paths, including sensitive characters such as `/` (encoded as
`%2F`).
We've changed this unintended behavior, such that rest-server now rejects such paths. In
particular, it is no longer possible to specify sub-repositories for users by encoding the
path with `%2F`, such as `http://localhost:8000/foo%2Fbar`, which means that this will
unfortunately be a breaking change in that case.
If using sub-repositories for users is important to you, please let us know in the forum, so we
can learn about your use case and implement this properly. As it currently stands, the ability
to use sub-repositories was an unintentional feature made possible by the URL decoding
framework used, and hence never meant to be supported in the first place. If we wish to have this
feature in rest-server, we'd like to have it implemented properly and intentionally.
https://github.com/restic/rest-server/issues/117
* Security #60: Require auth by default, add --no-auth flag * Security #60: Require auth by default, add --no-auth flag
In order to prevent users from accidentally exposing rest-server without authentication, In order to prevent users from accidentally exposing rest-server without
rest-server now defaults to requiring a .htpasswd. If you want to disable authentication, you authentication, rest-server now defaults to requiring a .htpasswd. If you want
need to explicitly pass the new --no-auth flag. to disable authentication, you need to explicitly pass the new --no-auth flag.
https://github.com/restic/rest-server/issues/60 https://github.com/restic/rest-server/issues/60
https://github.com/restic/rest-server/pull/61 https://github.com/restic/rest-server/pull/61
* Security #64: Refuse overwriting config file in append-only mode * Security #64: Refuse overwriting config file in append-only mode
While working on the `rclone serve restic` command we noticed that is currently possible to While working on the `rclone serve restic` command we noticed that is currently
overwrite the config file in a repo even if `--append-only` is specified. The first commit adds possible to overwrite the config file in a repo even if `--append-only` is
proper tests, and the second commit fixes the issue. specified. The first commit adds proper tests, and the second commit fixes the
issue.
https://github.com/restic/rest-server/pull/64 https://github.com/restic/rest-server/pull/64
* Security #117: Stricter path sanitization
The framework we're using in rest-server to decode paths to repositories allowed
specifying URL-encoded characters in paths, including sensitive characters such
as `/` (encoded as `%2F`).
We've changed this unintended behavior, such that rest-server now rejects such
paths. In particular, it is no longer possible to specify sub-repositories for
users by encoding the path with `%2F`, such as
`http://localhost:8000/foo%2Fbar`, which means that this will unfortunately be a
breaking change in that case.
If using sub-repositories for users is important to you, please let us know in
the forum, so we can learn about your use case and implement this properly. As
it currently stands, the ability to use sub-repositories was an unintentional
feature made possible by the URL decoding framework used, and hence never meant
to be supported in the first place. If we wish to have this feature in
rest-server, we'd like to have it implemented properly and intentionally.
https://github.com/restic/rest-server/issues/117
* Change #102: Remove vendored dependencies * Change #102: Remove vendored dependencies
We've removed the vendored dependencies (in the subdir `vendor/`) similar to what we did for We've removed the vendored dependencies (in the subdir `vendor/`) similar to
`restic` itself. When building restic, the Go compiler automatically fetches the what we did for `restic` itself. When building restic, the Go compiler
dependencies. It will also cryptographically verify that the correct code has been fetched by automatically fetches the dependencies. It will also cryptographically verify
using the hashes in `go.sum` (see the link to the documentation below). that the correct code has been fetched by using the hashes in `go.sum` (see the
link to the documentation below).
Building the rest-server now requires Go 1.11 or newer, since we're using Go Modules for Building the rest-server now requires Go 1.11 or newer, since we're using Go
dependency management. Older Go versions are not supported any more. Modules for dependency management. Older Go versions are not supported any more.
https://github.com/restic/rest-server/issues/102 https://github.com/restic/rest-server/issues/102
https://golang.org/cmd/go/#hdr-Module_downloading_and_verification https://golang.org/cmd/go/#hdr-Module_downloading_and_verification

16
Dockerfile.goreleaser Normal file
View File

@@ -0,0 +1,16 @@
FROM alpine
ENV DATA_DIRECTORY /data
ENV PASSWORD_FILE /data/.htpasswd
RUN apk add --no-cache --update apache2-utils
COPY docker/create_user /usr/bin/
COPY docker/delete_user /usr/bin/
COPY docker/entrypoint.sh /entrypoint.sh
COPY rest-server /usr/bin
VOLUME /data
EXPOSE 8000
CMD [ "/entrypoint.sh" ]

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.14 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.
@@ -35,9 +35,11 @@ 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
--group-accessible-repos let filesystem group be able to access repo files
-h, --help help for rest-server -h, --help help for rest-server
--htpasswd-file string location of .htpasswd file (default: "<data directory>/.htpasswd)"
--listen string listen address (default ":8000") --listen string listen address (default ":8000")
--log string log HTTP requests in the combined log format --log filename write HTTP requests in the combined log format to the specified filename (use "-" for logging to stdout)
--max-size int the maximum size of the repository in bytes --max-size int the maximum size of the repository in bytes
--no-auth disable .htpasswd authentication --no-auth disable .htpasswd authentication
--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-verify-upload do not verify the integrity of uploaded data. DO NOT enable unless the rest-server runs on a very low-power device
@@ -45,9 +47,11 @@ Flags:
--private-repos users can only access their private repo --private-repos users can only access their private repo
--prometheus enable Prometheus metrics --prometheus enable Prometheus metrics
--prometheus-no-auth disable auth for Prometheus /metrics endpoint --prometheus-no-auth disable auth for Prometheus /metrics endpoint
--proxy-auth-username string specifies the HTTP header containing the username for proxy-based authentication
--tls turn on TLS support --tls turn on TLS support
--tls-cert string TLS certificate path --tls-cert string TLS certificate path
--tls-key string TLS key 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 -v, --version version for rest-server
``` ```
@@ -57,7 +61,7 @@ By default the server persists backup data in the OS temporary directory (`/tmp/
rest-server --path /user/home/backup --no-auth rest-server --path /user/home/backup --no-auth
``` ```
To authenticate users (for access to the rest-server), the server supports using a `.htpasswd` file to specify users. You can create such a file at the root of the persistence directory by executing the following command (note that you need the `htpasswd` program from Apache's http-tools). In order to append new user to the file, just omit the `-c` argument. Only bcrypt and SHA encryption methods are supported, so use -B (very secure) or -s (insecure by today's standards) when adding/changing passwords. To authenticate users (for access to the rest-server), the server supports using a `.htpasswd` file to specify users. By default, the server looks for this file at the root of the persistence directory, but this can be changed using the `--htpasswd-file` option. You can create such a file by executing the following command (note that you need the `htpasswd` program from Apache's http-tools). In order to append new user to the file, just omit the `-c` argument. Only bcrypt and SHA encryption methods are supported, so use -B (very secure) or -s (insecure by today's standards) when adding/changing passwords.
```sh ```sh
htpasswd -B -c .htpasswd username htpasswd -B -c .htpasswd username
@@ -67,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:
@@ -104,6 +108,7 @@ Note that:
- **contrary to the defaults** of `rest-server`, the persistent data volume is located to `/data`. - **contrary to the defaults** of `rest-server`, the persistent data volume is located to `/data`.
- By default, the image uses authentication. To turn it off, set environment variable `DISABLE_AUTHENTICATION` to any value. - By default, the image uses authentication. To turn it off, set environment variable `DISABLE_AUTHENTICATION` to any value.
- By default, the image loads the `.htpasswd` file from the persistent data volume (i.e. from `/data/.htpasswd`). To change the location of this file, set the environment variable `PASSWORD_FILE` to the path of the `.htpasswd` file. Please note that this path must be accessible from inside the container and should be persisted. This is normally done by bind-mounting a path into the container or with another docker volume.
- It's suggested to set a container name to more easily manage users (`--name` parameter to `docker run`). - It's suggested to set a container name to more easily manage users (`--name` parameter to `docker run`).
- You can set environment variable `OPTIONS` to any extra flags you'd like to pass to rest-server. - You can set environment variable `OPTIONS` to any extra flags you'd like to pass to rest-server.
@@ -137,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
@@ -145,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

@@ -30,11 +30,11 @@
git tag -a -s -m "v${VERSION}" "v${VERSION}" git tag -a -s -m "v${VERSION}" "v${VERSION}"
git push --tags git push --tags
6. Build the project (use `--skip-publish` for testing, or pass `--config` to 6. Build the project (use `--snapshot` for testing, or pass `--config` to
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.11.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: 14, 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,9 @@
Enhancement: Cache basic authentication credentials
To speed up the verification of basic auth credentials, rest-server now caches
passwords for a minute in memory. That way the expensive verification of basic
auth credentials can be skipped for most requests issued by a single restic
run. The password is kept in memory in a hashed form and not as plaintext.
https://github.com/restic/rest-server/issues/133
https://github.com/restic/rest-server/pull/138

View File

@@ -0,0 +1,8 @@
Bugfix: Allow usernames containing underscore and more
The security fix in rest-server 0.11.0 (#131) disallowed usernames containing
and underscore "_". The list of allowed characters has now been changed to
include Unicode characters, numbers, "_", "-", "." and "@".
https://github.com/restic/restic/issues/183
https://github.com/restic/restic/pull/184

View File

@@ -0,0 +1,7 @@
Enhancement: Allow configurable location for `.htpasswd` file
It is now possible to specify the location of the `.htpasswd`
file using the `--htpasswd-file` option.
https://github.com/restic/restic/issues/187
https://github.com/restic/restic/pull/188

View File

@@ -0,0 +1,9 @@
Bugfix: Ignore unexpected files in the data/ folder
If the data folder of a repository contained files, this would prevent restic
from retrieving a list of file data files. This has been fixed. As a workaround
remove the files that are directly contained in the data folder (e.g.,
`.DS_Store` files).
https://github.com/restic/rest-server/issues/219
https://github.com/restic/rest-server/pull/221

View File

@@ -0,0 +1,13 @@
Bugfix: Return 500 "Internal server error" if files cannot be read
When files in a repository cannot be read by rest-server, for example after
running `restic prune` directly on the server hosting the repositories in a
way that causes filesystem permissions to be wrong, rest-server previously
returned 404 "Not Found" as status code. This was causing confusing for users.
The error handling has now been fixed to only return 404 "Not Found" if the
file actually does not exist. Otherwise a 500 "Internal server error" is
reported to the client and the underlying error is logged at the server side.
https://github.com/restic/restic/issues/1871
https://github.com/restic/rest-server/pull/195

View File

@@ -0,0 +1,9 @@
Change: Return error if command-line arguments are specified
Command line arguments are ignored by rest-server, but there was previously
no indication of this when they were supplied anyway.
To prevent usage errors an error is now printed when command line arguments
are supplied, instead of them being silently ignored.
https://github.com/restic/rest-server/pull/207

View File

@@ -0,0 +1,7 @@
Change: Update dependencies and require Go 1.17 or newer
Most dependencies have been updated. Since some libraries require newer language
features, support for Go 1.15-1.16 has been dropped, which means that rest-server
now requires at least Go 1.17 to build.
https://github.com/restic/rest-server/pull/208

View File

@@ -0,0 +1,9 @@
Bugfix: Fix erroneous warnings about unsupported fsync
Due to a regression in rest-server 0.12.0, it continuously printed
`WARNING: fsync is not supported by the data storage. This can lead to data loss,
if the system crashes or the storage is unexpectedly disconnected.` for systems
that support fsync. We have fixed the warning.
https://github.com/restic/rest-server/issues/230
https://github.com/restic/rest-server/pull/231

View File

@@ -0,0 +1,8 @@
Bugfix: API: Return empty array when listing empty folders
Rest-server returned `null` when listing an empty folder. This has been changed
to returning an empty array in accordance with the REST protocol specification.
This change has no impact on restic users.
https://github.com/restic/rest-server/issues/238
https://github.com/restic/rest-server/pull/239

View File

@@ -0,0 +1,13 @@
Enhancement: Log to stdout using the `--log -` option
Logging to stdout was possible using `--log /dev/stdout`. However,
when the rest server is run as a different user, for example, using
`sudo -u restic rest-server [...] --log /dev/stdout`
this did not work due to permission issues.
For logging to stdout, the `--log` option now supports the special
filename `-` which also works in these cases.
https://github.com/restic/rest-server/pull/217

View File

@@ -0,0 +1,7 @@
Change: Update dependencies and require Go 1.18 or newer
Most dependencies have been updated. Since some libraries require newer language
features, support for Go 1.17 has been dropped, which means that rest-server
now requires at least Go 1.18 to build.
https://github.com/restic/rest-server/pull/267

View File

@@ -0,0 +1,9 @@
Enhancement: Print listening address after start-up
When started with `--listen :0`, rest-server would print `start server on :0`
The message now also includes the actual address listened on, for example
`start server on 0.0.0.0:37333`. This is useful when starting a server with
an auto-allocated free port number (port 0).
https://github.com/restic/rest-server/pull/271

View File

@@ -0,0 +1,15 @@
Enhancement: Support listening on a unix socket
It is now possible to make rest-server listen on a unix socket by prefixing
the socket filename with `unix:` and passing it to the `--listen` option,
for example `--listen unix:/tmp/foo`.
This is useful in combination with remote port forwarding to enable a remote
server to backup locally, e.g.:
```
rest-server --listen unix:/tmp/foo &
ssh -R /tmp/foo:/tmp/foo user@host restic -r rest:http+unix:///tmp/foo:/repo backup
```
https://github.com/restic/rest-server/pull/272

View File

@@ -0,0 +1,9 @@
Change: Shut down cleanly on TERM and INT signals
Rest-server now listens for TERM and INT signals and cleanly closes down the
http.Server and listener when receiving either of them.
This is particularly useful when listening on a unix socket, as the server
will now remove the socket file when it shuts down.
https://github.com/restic/rest-server/pull/273

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

@@ -1,12 +1,20 @@
# The first line must start with Bugfix:, Enhancement: or Change:,
# including the colon. Use present tense. Remove lines starting with '#'
# from this template.
Bugfix: Fix behavior for foobar (in present tense) Bugfix: Fix behavior for foobar (in present tense)
# Describe the problem in the past tense, the new behavior in the present
# tense. Mention the affected commands, backends, operating systems, etc.
# Focus on user-facing behavior, not the implementation.
We've fixed the behavior for foobar, a long-standing annoyance for rest-server We've fixed the behavior for foobar, a long-standing annoyance for rest-server
users. users.
The text in the paragraphs is written in past tense. The last section is a list # The last section is a list of issue, PR and forum URLs.
of issue URLs, PR URLs and other URLs. The first issue ID (or the first PR ID, # The first issue ID determines the filename for the changelog entry:
in case there aren't any issue links) is used as the primary ID. # changelog/unreleased/issue-1234. If there are no relevant issue links,
# use the PR ID and call the file pull-55555.
https://github.com/restic/restic/issues/1234 https://github.com/restic/rest-server/issues/1234
https://github.com/restic/restic/pull/55555 https://github.com/restic/rest-server/pull/55555
https://forum.restic/.net/foo/bar/baz https://forum.restic.net/foo/bar/baz

View File

@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package main package main
@@ -6,8 +7,9 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"strings"
"github.com/coreos/go-systemd/activation" "github.com/coreos/go-systemd/v22/activation"
) )
// findListener tries to find a listener via systemd socket activation. If that // findListener tries to find a listener via systemd socket activation. If that
@@ -22,12 +24,23 @@ func findListener(addr string) (listener net.Listener, err error) {
switch len(listeners) { switch len(listeners) {
case 0: case 0:
// no listeners found, listen manually // no listeners found, listen manually
if strings.HasPrefix(addr, "unix:") { // if we want to listen on a unix socket
unixAddr, err := net.ResolveUnixAddr("unix", strings.TrimPrefix(addr, "unix:"))
if err != nil {
return nil, fmt.Errorf("unable to understand unix address %s: %w", addr, err)
}
listener, err = net.ListenUnix("unix", unixAddr)
if err != nil {
return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
}
} else { // assume tcp
listener, err = net.Listen("tcp", addr) listener, err = net.Listen("tcp", addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("listen on %v failed: %w", addr, err) return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
} }
}
log.Printf("start server on %v", addr) log.Printf("start server on %v", listener.Addr())
return listener, nil return listener, nil
case 1: case 1:

View File

@@ -0,0 +1,78 @@
//go:build !windows
// +build !windows
package main
import (
"context"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"testing"
"time"
)
func TestUnixSocket(t *testing.T) {
td := t.TempDir()
// this is the socket we'll listen on and connect to
tempSocket := filepath.Join(td, "sock")
// create some content and parent dirs
if err := os.MkdirAll(filepath.Join(td, "data", "repo1"), 0700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(td, "data", "repo1", "config"), []byte("foo"), 0700); err != nil {
t.Fatal(err)
}
// run the following twice, to test that the server will
// cleanup its socket file when quitting, which won't happen
// if it doesn't exit gracefully
for i := 0; i < 2; i++ {
err := testServerWithArgs([]string{
"--no-auth",
"--path", filepath.Join(td, "data"),
"--listen", fmt.Sprintf("unix:%s", tempSocket),
}, time.Second, func(ctx context.Context, _ *restServerApp) error {
// custom client that will talk HTTP to unix socket
client := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", tempSocket)
},
},
}
for _, test := range []struct {
Path string
StatusCode int
}{
{"/repo1/", http.StatusMethodNotAllowed},
{"/repo1/config", http.StatusOK},
{"/repo2/config", http.StatusNotFound},
} {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://ignored"+test.Path, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
err = resp.Body.Close()
if err != nil {
return err
}
if resp.StatusCode != test.StatusCode {
return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path)
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
}

View File

@@ -14,6 +14,6 @@ func findListener(addr string) (listener net.Listener, err error) {
return nil, fmt.Errorf("listen on %v failed: %w", addr, err) return nil, fmt.Errorf("listen on %v failed: %w", addr, err)
} }
log.Printf("start server on %v", addr) log.Printf("start server on %v", listener.Addr())
return listener, nil return listener, nil
} }

View File

@@ -1,152 +1,240 @@
package main package main
import ( import (
"context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"log" "log"
"net"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"sync"
"syscall" "syscall"
restserver "github.com/restic/rest-server" restserver "github.com/restic/rest-server"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type restServerApp struct {
CmdRoot *cobra.Command
Server restserver.Server
CPUProfile string
listenerAddressMu sync.Mutex
listenerAddress net.Addr // set after startup
}
// cmdRoot is the base command when no other command has been specified. // cmdRoot is the base command when no other command has been specified.
var cmdRoot = &cobra.Command{ func newRestServerApp() *restServerApp {
rv := &restServerApp{
CmdRoot: &cobra.Command{
Use: "rest-server", Use: "rest-server",
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,
RunE: runRoot, Args: func(_ *cobra.Command, args []string) error {
Version: fmt.Sprintf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH), if len(args) != 0 {
return fmt.Errorf("rest-server expects no arguments - unknown argument: %s", args[0])
} }
return nil
var server = restserver.Server{ },
Version: fmt.Sprintf("rest-server %s compiled with %v on %v/%v\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH),
},
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
flags := rv.CmdRoot.Flags()
var ( flags.StringVar(&rv.CPUProfile, "cpu-profile", rv.CPUProfile, "write CPU profile to file")
cpuProfile string 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.Log, "log", rv.Server.Log, "write HTTP requests in the combined log format to the specified `filename` (use \"-\" for logging to stdout)")
func init() { flags.Int64Var(&rv.Server.MaxRepoSize, "max-size", rv.Server.MaxRepoSize, "the maximum size of the repository in bytes")
flags := cmdRoot.Flags() flags.StringVar(&rv.Server.Path, "path", rv.Server.Path, "data directory")
flags.StringVar(&cpuProfile, "cpu-profile", cpuProfile, "write CPU profile to file") flags.BoolVar(&rv.Server.TLS, "tls", rv.Server.TLS, "turn on TLS support")
flags.BoolVar(&server.Debug, "debug", server.Debug, "output debug messages") flags.StringVar(&rv.Server.TLSCert, "tls-cert", rv.Server.TLSCert, "TLS certificate path")
flags.StringVar(&server.Listen, "listen", server.Listen, "listen address") flags.StringVar(&rv.Server.TLSKey, "tls-key", rv.Server.TLSKey, "TLS key path")
flags.StringVar(&server.Log, "log", server.Log, "log HTTP requests in the combined log format") flags.StringVar(&rv.Server.TLSMinVer, "tls-min-ver", rv.Server.TLSMinVer, "TLS min version, one of (1.2|1.3)")
flags.Int64Var(&server.MaxRepoSize, "max-size", server.MaxRepoSize, "the maximum size of the repository in bytes") flags.BoolVar(&rv.Server.NoAuth, "no-auth", rv.Server.NoAuth, "disable authentication")
flags.StringVar(&server.Path, "path", server.Path, "data directory") flags.StringVar(&rv.Server.HtpasswdPath, "htpasswd-file", rv.Server.HtpasswdPath, "location of .htpasswd file (default: \"<data directory>/.htpasswd)\"")
flags.BoolVar(&server.TLS, "tls", server.TLS, "turn on TLS support") flags.StringVar(&rv.Server.ProxyAuthUsername, "proxy-auth-username", rv.Server.ProxyAuthUsername, "specifies the HTTP header containing the username for proxy-based authentication")
flags.StringVar(&server.TLSCert, "tls-cert", server.TLSCert, "TLS certificate path") flags.BoolVar(&rv.Server.NoVerifyUpload, "no-verify-upload", rv.Server.NoVerifyUpload,
flags.StringVar(&server.TLSKey, "tls-key", server.TLSKey, "TLS key path")
flags.BoolVar(&server.NoAuth, "no-auth", server.NoAuth, "disable .htpasswd authentication")
flags.BoolVar(&server.NoVerifyUpload, "no-verify-upload", 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(&server.AppendOnly, "append-only", server.AppendOnly, "enable append only mode") flags.BoolVar(&rv.Server.AppendOnly, "append-only", rv.Server.AppendOnly, "enable append only mode")
flags.BoolVar(&server.PrivateRepos, "private-repos", 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(&server.Prometheus, "prometheus", server.Prometheus, "enable Prometheus metrics") flags.BoolVar(&rv.Server.Prometheus, "prometheus", rv.Server.Prometheus, "enable Prometheus metrics")
flags.BoolVar(&server.PrometheusNoAuth, "prometheus-no-auth", 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
} }
var version = "0.11.0" var version = "0.14.0-dev"
func tlsSettings() (bool, string, string, error) { func (app *restServerApp) tlsSettings() (bool, string, string, error) {
var key, cert string var key, cert string
if !server.TLS && (server.TLSKey != "" || server.TLSCert != "") { if !app.Server.TLS && (app.Server.TLSKey != "" || app.Server.TLSCert != "") {
return false, "", "", errors.New("requires enabled TLS") return false, "", "", errors.New("requires enabled TLS")
} else if !server.TLS { } else if !app.Server.TLS {
return false, "", "", nil return false, "", "", nil
} }
if server.TLSKey != "" { if app.Server.TLSKey != "" {
key = server.TLSKey key = app.Server.TLSKey
} else { } else {
key = filepath.Join(server.Path, "private_key") key = filepath.Join(app.Server.Path, "private_key")
} }
if server.TLSCert != "" { if app.Server.TLSCert != "" {
cert = server.TLSCert cert = app.Server.TLSCert
} else { } else {
cert = filepath.Join(server.Path, "public_key") cert = filepath.Join(app.Server.Path, "public_key")
} }
return server.TLS, key, cert, nil return app.Server.TLS, key, cert, nil
} }
func runRoot(cmd *cobra.Command, args []string) error { // returns the address that the app is listening on.
// returns nil if the application hasn't finished starting yet
func (app *restServerApp) ListenerAddress() net.Addr {
app.listenerAddressMu.Lock()
defer app.listenerAddressMu.Unlock()
return app.listenerAddress
}
func (app *restServerApp) runRoot(_ *cobra.Command, _ []string) error {
log.SetFlags(0) log.SetFlags(0)
log.Printf("Data directory: %s", server.Path) log.Printf("Data directory: %s", app.Server.Path)
if cpuProfile != "" { if app.CPUProfile != "" {
f, err := os.Create(cpuProfile) f, err := os.Create(app.CPUProfile)
if err != nil { if err != nil {
return err return err
} }
defer func() {
_ = f.Close()
}()
if err := pprof.StartCPUProfile(f); err != nil { if err := pprof.StartCPUProfile(f); err != nil {
return err return err
} }
defer pprof.StopCPUProfile()
log.Println("CPU profiling enabled") log.Println("CPU profiling enabled")
defer log.Println("Stopped CPU profiling")
// clean profiling shutdown on sigint
sigintCh := make(chan os.Signal, 1)
go func() {
for range sigintCh {
pprof.StopCPUProfile()
log.Println("Stopped CPU profiling")
err := f.Close()
if err != nil {
log.Printf("error closing CPU profile file: %v", err)
}
os.Exit(130)
}
}()
signal.Notify(sigintCh, syscall.SIGINT)
} }
if server.NoAuth { if app.Server.NoAuth {
log.Println("Authentication disabled") log.Println("Authentication disabled")
} else { } else {
if app.Server.ProxyAuthUsername == "" {
log.Println("Authentication enabled") log.Println("Authentication enabled")
} else {
log.Println("Proxy Authentication enabled.")
}
} }
handler, err := restserver.NewHandler(&server) handler, err := restserver.NewHandler(&app.Server)
if err != nil { if err != nil {
log.Fatalf("error: %v", err) log.Fatalf("error: %v", err)
} }
if server.PrivateRepos { if app.Server.AppendOnly {
log.Println("Append only mode enabled")
} else {
log.Println("Append only mode disabled")
}
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")
} }
enabledTLS, privateKey, publicKey, err := tlsSettings() if app.Server.GroupAccessibleRepos {
log.Println("Group accessible repos enabled")
} else {
log.Println("Group accessible repos disabled")
}
enabledTLS, privateKey, publicKey, err := app.tlsSettings()
if err != nil { if err != nil {
return err return err
} }
listener, err := findListener(server.Listen) listener, err := findListener(app.Server.Listen)
if err != nil { if err != nil {
return fmt.Errorf("unable to listen: %w", err) return fmt.Errorf("unable to listen: %w", err)
} }
if !enabledTLS { // set listener address, this is useful for tests
err = http.Serve(listener, handler) app.listenerAddressMu.Lock()
} else { app.listenerAddress = listener.Addr()
log.Printf("TLS enabled, private key %s, pubkey %v", privateKey, publicKey) app.listenerAddressMu.Unlock()
err = http.ServeTLS(listener, handler, publicKey, privateKey)
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)
} }
return err srv := &http.Server{
Handler: handler,
TLSConfig: tlscfg,
}
// run server in background
go func() {
if !enabledTLS {
err = srv.Serve(listener)
} else {
log.Printf("TLS enabled, private key %s, pubkey %v", privateKey, publicKey)
err = srv.ServeTLS(listener, publicKey, privateKey)
}
if err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listen and serve returned err: %v", err)
}
}()
// wait until done
<-app.CmdRoot.Context().Done()
// gracefully shutdown server
if err := srv.Shutdown(context.Background()); err != nil {
return fmt.Errorf("server shutdown returned an err: %w", err)
}
log.Println("shutdown cleanly")
return nil
} }
func main() { func main() {
if err := cmdRoot.Execute(); err != nil { // create context to be notified on interrupt or term signal so that we can shutdown cleanly
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
if err := newRestServerApp().CmdRoot.ExecuteContext(ctx); err != nil {
log.Fatalf("error: %v", err) log.Fatalf("error: %v", err)
} }
} }

View File

@@ -1,10 +1,17 @@
package main package main
import ( import (
"io/ioutil" "context"
"errors"
"fmt"
"net/http"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"sync"
"testing" "testing"
"time"
restserver "github.com/restic/rest-server" restserver "github.com/restic/rest-server"
) )
@@ -27,26 +34,37 @@ func TestTLSSettings(t *testing.T) {
expected expected expected expected
}{ }{
{passed{TLS: false}, expected{"", "", false}}, {passed{TLS: false}, expected{"", "", false}},
{passed{TLS: true}, expected{"/tmp/restic/private_key", "/tmp/restic/public_key", false}}, {passed{TLS: true}, expected{
{passed{Path: "/tmp", TLS: true}, expected{"/tmp/private_key", "/tmp/public_key", false}}, filepath.Join(os.TempDir(), "restic/private_key"),
{passed{Path: "/tmp", TLS: true, TLSKey: "/etc/restic/key", TLSCert: "/etc/restic/cert"}, expected{"/etc/restic/key", "/etc/restic/cert", false}}, filepath.Join(os.TempDir(), "restic/public_key"),
{passed{Path: "/tmp", TLS: false, TLSKey: "/etc/restic/key", TLSCert: "/etc/restic/cert"}, expected{"", "", true}}, false,
{passed{Path: "/tmp", TLS: false, TLSKey: "/etc/restic/key"}, expected{"", "", true}}, }},
{passed{Path: "/tmp", TLS: false, TLSCert: "/etc/restic/cert"}, expected{"", "", true}}, {passed{
Path: os.TempDir(),
TLS: true,
}, expected{
filepath.Join(os.TempDir(), "private_key"),
filepath.Join(os.TempDir(), "public_key"),
false,
}},
{passed{Path: os.TempDir(), TLS: true, TLSKey: "/etc/restic/key", TLSCert: "/etc/restic/cert"}, expected{"/etc/restic/key", "/etc/restic/cert", false}},
{passed{Path: os.TempDir(), TLS: false, TLSKey: "/etc/restic/key", TLSCert: "/etc/restic/cert"}, expected{"", "", true}},
{passed{Path: os.TempDir(), TLS: false, TLSKey: "/etc/restic/key"}, expected{"", "", true}},
{passed{Path: os.TempDir(), TLS: false, TLSCert: "/etc/restic/cert"}, expected{"", "", true}},
} }
for _, test := range tests { for _, test := range tests {
app := newRestServerApp()
t.Run("", func(t *testing.T) { t.Run("", func(t *testing.T) {
// defer func() { restserver.Server = defaultConfig }() // defer func() { restserver.Server = defaultConfig }()
if test.passed.Path != "" { if test.passed.Path != "" {
server.Path = test.passed.Path app.Server.Path = test.passed.Path
} }
server.TLS = test.passed.TLS app.Server.TLS = test.passed.TLS
server.TLSKey = test.passed.TLSKey app.Server.TLSKey = test.passed.TLSKey
server.TLSCert = test.passed.TLSCert app.Server.TLSCert = test.passed.TLSCert
gotTLS, gotKey, gotCert, err := tlsSettings() gotTLS, gotKey, gotCert, err := app.tlsSettings()
if err != nil && !test.expected.Error { if err != nil && !test.expected.Error {
t.Fatalf("tls_settings returned err (%v)", err) t.Fatalf("tls_settings returned err (%v)", err)
} }
@@ -75,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)
} }
@@ -100,9 +118,31 @@ 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
htpFile, err := os.CreateTemp(dir, "custom")
if err != nil {
t.Fatal(err)
}
defer func() {
err := os.Remove(htpFile.Name())
if err != nil {
t.Fatal(err)
}
}()
_, err = getHandler(&restserver.Server{HtpasswdPath: htpFile.Name()})
if err != nil {
t.Errorf("NoAuth=false with custom htpasswd: expected no error, got %v", err)
}
// 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)
} }
@@ -119,3 +159,126 @@ func TestGetHandler(t *testing.T) {
t.Errorf("NoAuth=false with .htpasswd: expected no error, got %v", err) t.Errorf("NoAuth=false with .htpasswd: expected no error, got %v", err)
} }
} }
// helper method to test the app. Starts app with passed arguments,
// then will call the callback function which can make requests against
// the application. If the callback function fails due to errors returned
// by http.Do() (i.e. *url.Error), then it will be retried until successful,
// or the passed timeout passes.
func testServerWithArgs(args []string, timeout time.Duration, cb func(context.Context, *restServerApp) error) error {
// create the app with passed args
app := newRestServerApp()
app.CmdRoot.SetArgs(args)
// create context that will timeout
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// wait group for our client and server tasks
jobs := &sync.WaitGroup{}
jobs.Add(2)
// run the server, saving the error
var serverErr error
go func() {
defer jobs.Done()
defer cancel() // if the server is stopped, no point keep the client alive
serverErr = app.CmdRoot.ExecuteContext(ctx)
}()
// run the client, saving the error
var clientErr error
go func() {
defer jobs.Done()
defer cancel() // once the client is done, stop the server
var urlError *url.Error
// execute in loop, as we will retry for network errors
// (such as the server hasn't started yet)
for {
clientErr = cb(ctx, app)
switch {
case clientErr == nil:
return // success, we're done
case errors.As(clientErr, &urlError):
// if a network error (url.Error), then wait and retry
// as server may not be ready yet
select {
case <-time.After(time.Millisecond * 100):
continue
case <-ctx.Done(): // unless we run out of time first
clientErr = context.Canceled
return
}
default:
return // other error type, we're done
}
}
}()
// wait for both to complete
jobs.Wait()
// report back if either failed
if clientErr != nil || serverErr != nil {
return fmt.Errorf("client or server error, client: %v, server: %v", clientErr, serverErr)
}
return nil
}
func TestHttpListen(t *testing.T) {
td := t.TempDir()
// create some content and parent dirs
if err := os.MkdirAll(filepath.Join(td, "data", "repo1"), 0700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(td, "data", "repo1", "config"), []byte("foo"), 0700); err != nil {
t.Fatal(err)
}
for _, args := range [][]string{
{"--no-auth", "--path", filepath.Join(td, "data"), "--listen", "127.0.0.1:0"}, // test emphemeral port
{"--no-auth", "--path", filepath.Join(td, "data"), "--listen", "127.0.0.1:9000"}, // test "normal" port
{"--no-auth", "--path", filepath.Join(td, "data"), "--listen", "127.0.0.1:9000"}, // test that server was shutdown cleanly and that we can re-use that port
} {
err := testServerWithArgs(args, time.Second*10, func(ctx context.Context, app *restServerApp) error {
for _, test := range []struct {
Path string
StatusCode int
}{
{"/repo1/", http.StatusMethodNotAllowed},
{"/repo1/config", http.StatusOK},
{"/repo2/config", http.StatusNotFound},
} {
listenAddr := app.ListenerAddress()
if listenAddr == nil {
return &url.Error{} // return this type of err, as we know this will retry
}
port := strings.Split(listenAddr.String(), ":")[1]
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("http://localhost:%s%s", port, test.Path), nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
err = resp.Body.Close()
if err != nil {
return err
}
if resp.StatusCode != test.StatusCode {
return fmt.Errorf("expected %d from server, instead got %d (path %s)", test.StatusCode, resp.StatusCode, test.Path)
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
}

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
@@ -16,4 +16,4 @@ else
fi fi
fi fi
exec rest-server --path "$DATA_DIRECTORY" $OPTIONS exec rest-server --path "$DATA_DIRECTORY" --htpasswd-file "$PASSWORD_FILE" $OPTIONS

View File

@@ -1,4 +1,14 @@
{ {
"__inputs": [
{
"name": "DS_PROMETHEUS-INFRA",
"label": "prometheus-infra",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"__requires": [ "__requires": [
{ {
"type": "grafana", "type": "grafana",
@@ -49,7 +59,7 @@
"bars": false, "bars": false,
"dashLength": 10, "dashLength": 10,
"dashes": false, "dashes": false,
"datasource": "prometheus", "datasource": "${DS_PROMETHEUS-INFRA}",
"fill": 1, "fill": 1,
"id": 1, "id": 1,
"legend": { "legend": {
@@ -125,7 +135,7 @@
"bars": false, "bars": false,
"dashLength": 10, "dashLength": 10,
"dashes": false, "dashes": false,
"datasource": "prometheus", "datasource": "${DS_PROMETHEUS-INFRA}",
"fill": 1, "fill": 1,
"id": 4, "id": 4,
"legend": { "legend": {
@@ -213,7 +223,7 @@
"bars": false, "bars": false,
"dashLength": 10, "dashLength": 10,
"dashes": false, "dashes": false,
"datasource": "prometheus", "datasource": "${DS_PROMETHEUS-INFRA}",
"fill": 1, "fill": 1,
"id": 2, "id": 2,
"legend": { "legend": {
@@ -289,7 +299,7 @@
"bars": false, "bars": false,
"dashLength": 10, "dashLength": 10,
"dashes": false, "dashes": false,
"datasource": "prometheus", "datasource": "${DS_PROMETHEUS-INFRA}",
"fill": 1, "fill": 1,
"id": 5, "id": 5,
"legend": { "legend": {
@@ -377,7 +387,7 @@
"bars": false, "bars": false,
"dashLength": 10, "dashLength": 10,
"dashes": false, "dashes": false,
"datasource": "prometheus", "datasource": "${DS_PROMETHEUS-INFRA}",
"fill": 1, "fill": 1,
"id": 3, "id": 3,
"legend": { "legend": {
@@ -453,7 +463,7 @@
"bars": false, "bars": false,
"dashLength": 10, "dashLength": 10,
"dashes": false, "dashes": false,
"datasource": "prometheus", "datasource": "${DS_PROMETHEUS-INFRA}",
"fill": 1, "fill": 1,
"id": 6, "id": 6,
"legend": { "legend": {
@@ -541,7 +551,7 @@
{ {
"allValue": null, "allValue": null,
"current": {}, "current": {},
"datasource": "prometheus", "datasource": "${DS_PROMETHEUS-INFRA}",
"hide": 0, "hide": 0,
"includeAll": false, "includeAll": false,
"label": "Instance", "label": "Instance",

View File

@@ -24,7 +24,7 @@ services:
- "127.0.0.1:8020:9090" - "127.0.0.1:8020:9090"
volumes: volumes:
- prometheusdata:/prometheus - prometheusdata:/prometheus
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./prometheus:/etc/prometheus:ro
depends_on: depends_on:
- restserver - restserver
networks: networks:

View File

@@ -2,9 +2,8 @@
Description=Rest Server Description=Rest Server
After=syslog.target After=syslog.target
After=network.target After=network.target
Requires=rest-server.socket
# if you want to use socket activation, make sure to require the socket here After=rest-server.socket
#Requires=rest-server.socket
[Service] [Service]
Type=simple Type=simple
@@ -27,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
@@ -37,6 +38,11 @@ CapabilityBoundingSet=
LockPersonality=true LockPersonality=true
MemoryDenyWriteExecute=true MemoryDenyWriteExecute=true
NoNewPrivileges=yes NoNewPrivileges=yes
# As the listen socket is created by systemd via the rest-server.socket unit, it is
# no longer necessary for rest-server to have access to the host network namespace.
PrivateNetwork=yes
PrivateTmp=yes PrivateTmp=yes
PrivateDevices=true PrivateDevices=true
PrivateUsers=true PrivateUsers=true
@@ -51,9 +57,10 @@ ProtectProc=invisible
ProtectHostname=true ProtectHostname=true
RemoveIPC=true RemoveIPC=true
RestrictNamespaces=true RestrictNamespaces=true
RestrictAddressFamilies=AF_INET AF_INET6 RestrictAddressFamilies=none
RestrictSUIDSGID=true RestrictSUIDSGID=true
RestrictRealtime=true RestrictRealtime=true
# if your service crashes with "code=killed, status=31/SYS", you probably tried to run linux_i386 (32bit) binary on a amd64 host
SystemCallArchitectures=native SystemCallArchitectures=native
SystemCallFilter=@system-service SystemCallFilter=@system-service
@@ -62,6 +69,7 @@ SystemCallFilter=@system-service
# network I/O that the rest-server is permitted to consume according to the # network I/O that the rest-server is permitted to consume according to the
# individual requirements of your installation. # individual requirements of your installation.
#CPUQuota=25% #CPUQuota=25%
#MemoryHigh=bytes
#MemoryMax=bytes #MemoryMax=bytes
#MemorySwapMax=bytes #MemorySwapMax=bytes
#TasksMax=N #TasksMax=N

View File

@@ -1,5 +1,5 @@
[Socket] [Socket]
ListenStream = 8080 ListenStream = 8000
[Install] [Install]
WantedBy = sockets.target WantedBy = sockets.target

31
go.mod
View File

@@ -1,13 +1,28 @@
module github.com/restic/rest-server module github.com/restic/rest-server
go 1.14 go 1.23.0
require ( require (
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/coreos/go-systemd/v22 v22.5.0
github.com/gorilla/handlers v1.5.1 github.com/gorilla/handlers v1.5.2
github.com/minio/sha256-simd v1.0.0 github.com/minio/sha256-simd v1.0.1
github.com/miolini/datacounter v1.0.2 github.com/miolini/datacounter v1.0.3
github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_golang v1.22.0
github.com/spf13/cobra v1.3.0 github.com/spf13/cobra v1.9.1
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab golang.org/x/crypto v0.38.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
golang.org/x/sys v0.33.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
) )

855
go.sum
View File

@@ -1,813 +1,56 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/miolini/datacounter v1.0.3/go.mod h1:C45dc2hBumHjDpEU64IqPwR6TDyPVpzOqqRTN7zmBUA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/miolini/datacounter v1.0.2 h1:mGTL0vqEAtH7mwNJS1JIpd6jwTAP6cBQQ2P8apaCIm8=
github.com/miolini/datacounter v1.0.2/go.mod h1:C45dc2hBumHjDpEU64IqPwR6TDyPVpzOqqRTN7zmBUA=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab h1:lnZ4LoV0UMdibeCUfIB2a4uFwRu491WX/VB2reB8xNc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -7,6 +7,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"github.com/restic/rest-server/quota" "github.com/restic/rest-server/quota"
"github.com/restic/rest-server/repo" "github.com/restic/rest-server/repo"
@@ -15,13 +16,16 @@ 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
Listen string Listen string
Log string Log string
CPUProfile string CPUProfile string
TLSKey string TLSKey string
TLSCert string TLSCert string
TLSMinVer string
TLS bool TLS bool
NoAuth bool NoAuth bool
ProxyAuthUsername string
AppendOnly bool AppendOnly bool
PrivateRepos bool PrivateRepos bool
Prometheus bool Prometheus bool
@@ -30,9 +34,11 @@ type Server struct {
MaxRepoSize int64 MaxRepoSize int64
PanicOnError bool PanicOnError bool
NoVerifyUpload bool NoVerifyUpload bool
GroupAccessibleRepos bool
htpasswdFile *HtpasswdFile htpasswdFile *HtpasswdFile
quotaManager *quota.Manager quotaManager *quota.Manager
fsyncWarning sync.Once
} }
// MaxFolderDepth is the maxDepth param passed to splitURLPath. // MaxFolderDepth is the maxDepth param passed to splitURLPath.
@@ -90,6 +96,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
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,
GroupAccessible: s.GroupAccessibleRepos,
} }
if s.Prometheus { if s.Prometheus {
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath) opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)
@@ -154,6 +162,7 @@ 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, "/") {

View File

@@ -6,10 +6,10 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings" "strings"
@@ -164,8 +164,7 @@ func createOverwriteDeleteSeq(t testing.TB, path string, data string) []TestRequ
return req return req
} }
// TestResticHandler runs tests on the restic handler code, especially in append-only mode. func createTestHandler(t *testing.T, conf *Server) (http.Handler, string, string, string, func()) {
func TestResticHandler(t *testing.T) {
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 {
@@ -175,6 +174,38 @@ func TestResticHandler(t *testing.T) {
dataHash := sha256.Sum256([]byte(data)) dataHash := sha256.Sum256([]byte(data))
fileID := hex.EncodeToString(dataHash[:]) fileID := hex.EncodeToString(dataHash[:])
// setup the server with a local backend in a temporary directory
tempdir, err := os.MkdirTemp("", "rest-server-test-")
if err != nil {
t.Fatal(err)
}
// make sure the tempdir is properly removed
cleanup := func() {
err := os.RemoveAll(tempdir)
if err != nil {
t.Fatal(err)
}
}
conf.Path = tempdir
mux, err := NewHandler(conf)
if err != nil {
t.Fatalf("error from NewHandler: %v", err)
}
return mux, data, fileID, tempdir, cleanup
}
// TestResticAppendOnlyHandler runs tests on the restic handler code, especially in append-only mode.
func TestResticAppendOnlyHandler(t *testing.T) {
mux, data, fileID, _, cleanup := createTestHandler(t, &Server{
AppendOnly: true,
NoAuth: true,
Debug: true,
PanicOnError: true,
})
defer cleanup()
var tests = []struct { var tests = []struct {
seq []TestRequest seq []TestRequest
}{ }{
@@ -226,32 +257,6 @@ func TestResticHandler(t *testing.T) {
{createOverwriteDeleteSeq(t, "/parent2/data/"+fileID, data)}, {createOverwriteDeleteSeq(t, "/parent2/data/"+fileID, data)},
} }
// setup the server with a local backend in a temporary directory
tempdir, err := ioutil.TempDir("", "rest-server-test-")
if err != nil {
t.Fatal(err)
}
// make sure the tempdir is properly removed
defer func() {
err := os.RemoveAll(tempdir)
if err != nil {
t.Fatal(err)
}
}()
// set append-only mode and configure path
mux, err := NewHandler(&Server{
AppendOnly: true,
Path: tempdir,
NoAuth: true,
Debug: true,
PanicOnError: true,
})
if err != nil {
t.Fatalf("error from NewHandler: %v", err)
}
// create the repos // create the repos
for _, path := range []string{"/", "/parent1/sub1/", "/parent1/", "/parent2/"} { for _, path := range []string{"/", "/parent1/sub1/", "/parent1/", "/parent2/"} {
checkRequest(t, mux.ServeHTTP, checkRequest(t, mux.ServeHTTP,
@@ -269,6 +274,162 @@ func TestResticHandler(t *testing.T) {
} }
} }
// createOverwriteDeleteSeq returns a sequence which will create a new file at
// path, and then deletes it twice.
func createIdempotentDeleteSeq(t testing.TB, path string, data string) []TestRequest {
return []TestRequest{
{
req: newRequest(t, "POST", path, strings.NewReader(data)),
want: []wantFunc{wantCode(http.StatusOK)},
},
{
req: newRequest(t, "DELETE", path, nil),
want: []wantFunc{wantCode(http.StatusOK)},
},
{
req: newRequest(t, "GET", path, nil),
want: []wantFunc{wantCode(http.StatusNotFound)},
},
{
req: newRequest(t, "DELETE", path, nil),
want: []wantFunc{wantCode(http.StatusOK)},
},
}
}
// TestResticHandler runs tests on the restic handler code, especially in append-only mode.
func TestResticHandler(t *testing.T) {
mux, data, fileID, _, cleanup := createTestHandler(t, &Server{
NoAuth: true,
Debug: true,
PanicOnError: true,
})
defer cleanup()
var tests = []struct {
seq []TestRequest
}{
{createIdempotentDeleteSeq(t, "/config", data)},
{createIdempotentDeleteSeq(t, "/data/"+fileID, data)},
}
// create the repo
checkRequest(t, mux.ServeHTTP,
newRequest(t, "POST", "/?create=true", nil),
[]wantFunc{wantCode(http.StatusOK)})
for _, test := range tests {
t.Run("", func(t *testing.T) {
for i, seq := range test.seq {
t.Logf("request %v: %v %v", i, seq.req.Method, seq.req.URL.Path)
checkRequest(t, mux.ServeHTTP, seq.req, seq.want)
}
})
}
}
// TestResticErrorHandler runs tests on the restic handler error handling.
func TestResticErrorHandler(t *testing.T) {
mux, _, _, tempdir, cleanup := createTestHandler(t, &Server{
AppendOnly: true,
NoAuth: true,
Debug: true,
})
defer cleanup()
var tests = []struct {
seq []TestRequest
}{
// Test inaccessible file
{
[]TestRequest{{
req: newRequest(t, "GET", "/config", nil),
want: []wantFunc{wantCode(http.StatusInternalServerError)},
}},
},
{
[]TestRequest{{
req: newRequest(t, "GET", "/parent4/config", nil),
want: []wantFunc{wantCode(http.StatusNotFound)},
}},
},
}
// create the repo
checkRequest(t, mux.ServeHTTP,
newRequest(t, "POST", "/?create=true", nil),
[]wantFunc{wantCode(http.StatusOK)})
// create inaccessible config
checkRequest(t, mux.ServeHTTP,
newRequest(t, "POST", "/config", strings.NewReader("example")),
[]wantFunc{wantCode(http.StatusOK)})
err := os.Chmod(path.Join(tempdir, "config"), 0o000)
if err != nil {
t.Fatal(err)
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
for i, seq := range test.seq {
t.Logf("request %v: %v %v", i, seq.req.Method, seq.req.URL.Path)
checkRequest(t, mux.ServeHTTP, seq.req, seq.want)
}
})
}
}
func TestEmptyList(t *testing.T) {
mux, _, _, _, cleanup := createTestHandler(t, &Server{
AppendOnly: true,
NoAuth: true,
Debug: true,
})
defer cleanup()
// create the repo
checkRequest(t, mux.ServeHTTP,
newRequest(t, "POST", "/?create=true", nil),
[]wantFunc{wantCode(http.StatusOK)})
for i := 1; i <= 2; i++ {
req := newRequest(t, "GET", "/data/", nil)
if i == 2 {
req.Header.Set("Accept", "application/vnd.x.restic.rest.v2")
}
checkRequest(t, mux.ServeHTTP, req,
[]wantFunc{wantCode(http.StatusOK), wantBody("[]")})
}
}
func TestListWithUnexpectedFiles(t *testing.T) {
mux, _, _, tempdir, cleanup := createTestHandler(t, &Server{
AppendOnly: true,
NoAuth: true,
Debug: true,
})
defer cleanup()
// create the repo
checkRequest(t, mux.ServeHTTP,
newRequest(t, "POST", "/?create=true", nil),
[]wantFunc{wantCode(http.StatusOK)})
err := os.WriteFile(path.Join(tempdir, "data", "temp"), []byte{}, 0o666)
if err != nil {
t.Fatalf("creating unexpected file failed: %v", err)
}
for i := 1; i <= 2; i++ {
req := newRequest(t, "GET", "/data/", nil)
if i == 2 {
req.Header.Set("Accept", "application/vnd.x.restic.rest.v2")
}
checkRequest(t, mux.ServeHTTP, req,
[]wantFunc{wantCode(http.StatusOK)})
}
}
func TestSplitURLPath(t *testing.T) { func TestSplitURLPath(t *testing.T) {
var tests = []struct { var tests = []struct {
// Params // Params
@@ -348,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)
@@ -359,31 +520,13 @@ 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) {
// setup the server with a local backend in a temporary directory // the race condition doesn't happen for append-only repositories
tempdir, err := ioutil.TempDir("", "rest-server-test-") mux, _, _, _, cleanup := createTestHandler(t, &Server{
if err != nil {
t.Fatal(err)
}
// make sure the tempdir is properly removed
defer func() {
err := os.RemoveAll(tempdir)
if err != nil {
t.Fatal(err)
}
}()
// configure path, the race condition doesn't happen for append-only repositories
mux, err := NewHandler(&Server{
AppendOnly: false,
Path: tempdir,
NoAuth: true, NoAuth: true,
Debug: true, Debug: true,
PanicOnError: true, PanicOnError: true,
}) })
if err != nil { defer cleanup()
t.Fatalf("error from NewHandler: %v", err)
}
// create the repo // create the repo
checkRequest(t, mux.ServeHTTP, checkRequest(t, mux.ServeHTTP,

View File

@@ -26,6 +26,8 @@ THE SOFTWARE.
import ( import (
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"crypto/subtle"
"encoding/base64" "encoding/base64"
"encoding/csv" "encoding/csv"
"log" "log"
@@ -42,15 +44,26 @@ import (
// CheckInterval represents how often we check for changes in htpasswd file. // CheckInterval represents how often we check for changes in htpasswd file.
const CheckInterval = 30 * time.Second const CheckInterval = 30 * time.Second
// PasswordCacheDuration represents how long authentication credentials are
// cached in memory after they were successfully verified. This allows avoiding
// repeatedly verifying the same authentication credentials.
const PasswordCacheDuration = time.Minute
// Lookup passwords in a htpasswd file. The entries must have been created with -s for SHA encryption. // Lookup passwords in a htpasswd file. The entries must have been created with -s for SHA encryption.
type cacheEntry struct {
expiry time.Time
verifier []byte
}
// HtpasswdFile is a map for usernames to passwords. // HtpasswdFile is a map for usernames to passwords.
type HtpasswdFile struct { type HtpasswdFile struct {
mutex sync.Mutex mutex sync.Mutex
path string path string
stat os.FileInfo stat os.FileInfo
throttle chan struct{} throttle chan struct{}
Users map[string]string users map[string]string
cache map[string]cacheEntry
} }
// NewHtpasswdFromFile reads the users and passwords from a htpasswd file and returns them. If an error is encountered, // NewHtpasswdFromFile reads the users and passwords from a htpasswd file and returns them. If an error is encountered,
@@ -68,6 +81,7 @@ func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) {
path: path, path: path,
stat: stat, stat: stat,
throttle: make(chan struct{}), throttle: make(chan struct{}),
cache: make(map[string]cacheEntry),
} }
if err := h.Reload(); err != nil { if err := h.Reload(); err != nil {
@@ -76,6 +90,7 @@ func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) {
// Start a goroutine that limits reload checks to once per CheckInterval // Start a goroutine that limits reload checks to once per CheckInterval
go h.throttleTimer() go h.throttleTimer()
go h.expiryTimer()
go func() { go func() {
for range c { for range c {
@@ -100,7 +115,24 @@ func (h *HtpasswdFile) throttleTimer() {
} }
} }
var validUsernameRegexp = regexp.MustCompile(`^[\p{L}\d@.-]+$`) func (h *HtpasswdFile) expiryTimer() {
for {
time.Sleep(5 * time.Second)
now := time.Now()
h.mutex.Lock()
var zeros [sha256.Size]byte
// try to wipe expired cache entries
for user, entry := range h.cache {
if entry.expiry.After(now) {
copy(entry.verifier, zeros[:])
delete(h.cache, user)
}
}
h.mutex.Unlock()
}
}
var validUsernameRegexp = regexp.MustCompile(`^[\p{L}\d@._-]+$`)
// Reload reloads the htpasswd file. If the reload fails, the Users map is not changed and the error is returned. // Reload reloads the htpasswd file. If the reload fails, the Users map is not changed and the error is returned.
func (h *HtpasswdFile) Reload() error { func (h *HtpasswdFile) Reload() error {
@@ -122,7 +154,7 @@ func (h *HtpasswdFile) Reload() error {
users := make(map[string]string) users := make(map[string]string)
for _, record := range records { for _, record := range records {
if !validUsernameRegexp.MatchString(record[0]) { if !validUsernameRegexp.MatchString(record[0]) {
log.Printf("Ignoring invalid username %q in htpasswd, consists of characters other than letters", record[0]) log.Printf("Ignoring invalid username %q in htpasswd, consists of characters other than letters, numbers, '_', '-', '.' and '@'", record[0])
continue continue
} }
users[record[0]] = record[1] users[record[0]] = record[1]
@@ -130,7 +162,14 @@ func (h *HtpasswdFile) Reload() error {
// Replace the Users map // Replace the Users map
h.mutex.Lock() h.mutex.Lock()
h.Users = users var zeros [sha256.Size]byte
// try to wipe the old cache entries
for _, entry := range h.cache {
copy(entry.verifier, zeros[:])
}
h.cache = make(map[string]cacheEntry)
h.users = users
h.mutex.Unlock() h.mutex.Unlock()
_ = r.Close() _ = r.Close()
@@ -172,35 +211,74 @@ func (h *HtpasswdFile) ReloadCheck() error {
return nil return nil
} }
var shaRe = regexp.MustCompile(`^{SHA}`)
var bcrRe = regexp.MustCompile(`^\$2b\$|^\$2a\$|^\$2y\$`)
// Validate returns true if password matches the stored password for user. If no password for user is stored, or the // Validate returns true if password matches the stored password for user. If no password for user is stored, or the
// password is wrong, false is returned. // password is wrong, false is returned.
func (h *HtpasswdFile) Validate(user string, password string) bool { func (h *HtpasswdFile) Validate(user string, password string) bool {
_ = h.ReloadCheck() _ = h.ReloadCheck()
hash := sha256.New()
// hash.Write can never fail
_, _ = hash.Write([]byte(user))
_, _ = hash.Write([]byte(":"))
_, _ = hash.Write([]byte(password))
h.mutex.Lock() h.mutex.Lock()
realPassword, exists := h.Users[user] // avoid race conditions with cache replacements
cache := h.cache
hashedPassword, exists := h.users[user]
entry, cacheExists := h.cache[user]
h.mutex.Unlock() h.mutex.Unlock()
if !exists { if !exists {
return false return false
} }
var shaRe = regexp.MustCompile(`^{SHA}`) if cacheExists && subtle.ConstantTimeCompare(entry.verifier, hash.Sum(nil)) == 1 {
var bcrRe = regexp.MustCompile(`^\$2b\$|^\$2a\$|^\$2y\$`) h.mutex.Lock()
// repurpose mutex to prevent concurrent cache updates
switch { // extend cache entry
case shaRe.MatchString(realPassword): cache[user] = cacheEntry{
d := sha1.New() verifier: entry.verifier,
_, _ = d.Write([]byte(password)) expiry: time.Now().Add(PasswordCacheDuration),
if realPassword[5:] == base64.StdEncoding.EncodeToString(d.Sum(nil)) { }
h.mutex.Unlock()
return true return true
} }
case bcrRe.MatchString(realPassword):
err := bcrypt.CompareHashAndPassword([]byte(realPassword), []byte(password)) isValid := isMatchingHashAndPassword(hashedPassword, password)
if !isValid {
log.Printf("Invalid htpasswd entry for %s.", user)
return false
}
h.mutex.Lock()
// repurpose mutex to prevent concurrent cache updates
cache[user] = cacheEntry{
verifier: hash.Sum(nil),
expiry: time.Now().Add(PasswordCacheDuration),
}
h.mutex.Unlock()
return true
}
func isMatchingHashAndPassword(hashedPassword string, password string) bool {
switch {
case shaRe.MatchString(hashedPassword):
d := sha1.New()
_, _ = d.Write([]byte(password))
if subtle.ConstantTimeCompare([]byte(hashedPassword[5:]), []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) == 1 {
return true
}
case bcrRe.MatchString(hashedPassword):
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
if err == nil { if err == nil {
return true return true
} }
} }
log.Printf("Invalid htpasswd entry for %s.", user)
return false return false
} }

45
htpasswd_test.go Normal file
View File

@@ -0,0 +1,45 @@
package restserver
import (
"os"
"testing"
)
func TestValidate(t *testing.T) {
user := "restic"
pwd := "$2y$05$z/OEmNQamd6m6LSegUErh.r/Owk9Xwmc5lxDheIuHY2Z7XiS6FtJm"
rawPwd := "test"
wrongPwd := "wrong"
tmpfile, err := os.CreateTemp("", "rest-validate-")
if err != nil {
t.Fatal(err)
}
if _, err = tmpfile.Write([]byte(user + ":" + pwd + "\n")); err != nil {
t.Fatal(err)
}
if err = tmpfile.Close(); err != nil {
t.Fatal(err)
}
htpass, err := NewHtpasswdFromFile(tmpfile.Name())
if err != nil {
t.Fatal(err)
}
for i := 0; i < 10; i++ {
isValid := htpass.Validate(user, rawPwd)
if !isValid {
t.Fatal("correct password not accepted")
}
isValid = htpass.Validate(user, wrongPwd)
if isValid {
t.Fatal("wrong password accepted")
}
}
if err = os.Remove(tmpfile.Name()); err != nil {
t.Fatal(err)
}
}

29
mux.go
View File

@@ -2,6 +2,7 @@ package restserver
import ( import (
"fmt" "fmt"
"io"
"log" "log"
"net/http" "net/http"
"os" "os"
@@ -21,10 +22,17 @@ func (s *Server) debugHandler(next http.Handler) http.Handler {
} }
func (s *Server) logHandler(next http.Handler) http.Handler { func (s *Server) logHandler(next http.Handler) http.Handler {
accessLog, err := os.OpenFile(s.Log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) var accessLog io.Writer
if s.Log == "-" {
accessLog = os.Stdout
} else {
var err error
accessLog, err = os.OpenFile(s.Log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil { if err != nil {
log.Fatalf("error: %v", err) log.Fatalf("error: %v", err)
} }
}
return handlers.CombinedLoggingHandler(accessLog, next) return handlers.CombinedLoggingHandler(accessLog, next)
} }
@@ -33,11 +41,18 @@ func (s *Server) checkAuth(r *http.Request) (username string, ok bool) {
if s.NoAuth { if s.NoAuth {
return username, true return username, true
} }
if s.ProxyAuthUsername != "" {
username = r.Header.Get(s.ProxyAuthUsername)
if username == "" {
return "", false
}
} else {
var password string var password string
username, password, ok = r.BasicAuth() username, password, ok = r.BasicAuth()
if !ok || !s.htpasswdFile.Validate(username, password) { if !ok || !s.htpasswdFile.Validate(username, password) {
return "", false return "", false
} }
}
return username, true return username, true
} }
@@ -58,12 +73,16 @@ 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
server.htpasswdFile, err = NewHtpasswdFromFile(filepath.Join(server.Path, ".htpasswd")) if server.HtpasswdPath == "" {
if err != nil { server.HtpasswdPath = filepath.Join(server.Path, ".htpasswd")
return nil, fmt.Errorf("cannot load .htpasswd (use --no-auth to disable): %v", err)
} }
server.htpasswdFile, err = NewHtpasswdFromFile(server.HtpasswdPath)
if err != nil {
return nil, fmt.Errorf("cannot load %s (use --no-auth to disable): %v", server.HtpasswdPath, err)
}
log.Printf("Loaded htpasswd file %s", server.HtpasswdPath)
} }
const GiB = 1024 * 1024 * 1024 const GiB = 1024 * 1024 * 1024

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"
@@ -16,6 +15,7 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync"
"syscall" "syscall"
"time" "time"
@@ -28,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
@@ -38,6 +36,14 @@ type Options struct {
BlobMetricFunc BlobMetricFunc BlobMetricFunc BlobMetricFunc
QuotaManager *quota.Manager QuotaManager *quota.Manager
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
@@ -48,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.
@@ -55,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,
@@ -249,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()")
} }
@@ -257,10 +274,7 @@ func (h *Handler) checkConfig(w http.ResponseWriter, r *http.Request) {
st, err := os.Stat(cfg) st, err := os.Stat(cfg)
if err != nil { if err != nil {
if h.opt.Debug { h.fileAccessError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusNotFound)
return return
} }
@@ -268,18 +282,15 @@ 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 {
if h.opt.Debug { h.fileAccessError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusNotFound)
return return
} }
@@ -293,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)
@@ -318,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()")
} }
@@ -331,13 +342,10 @@ func (h *Handler) deleteConfig(w http.ResponseWriter, r *http.Request) {
cfg := h.getSubPath("config") cfg := h.getSubPath("config")
if err := os.Remove(cfg); err != nil { if err := os.Remove(cfg); err != nil {
if h.opt.Debug { // ignore not exist errors to make deleting idempotent, which is
log.Print(err) // necessary to properly handle request retries
} if !errors.Is(err, os.ErrNotExist) {
if os.IsNotExist(err) { h.fileAccessError(w, err)
httpDefaultError(w, http.StatusNotFound)
} else {
h.internalServerError(w, err)
} }
return return
} }
@@ -376,26 +384,24 @@ 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 {
if h.opt.Debug { h.fileAccessError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusNotFound)
return return
} }
var names []string names := []string{}
for _, i := range items { for _, i := range items {
if isHashed(objectType) { if isHashed(objectType) {
subpath := filepath.Join(path, i.Name()) if !i.IsDir() {
var subitems []os.FileInfo // ignore files in intermediate directories
subitems, err = ioutil.ReadDir(subpath) continue
if err != nil {
if h.opt.Debug {
log.Print(err)
} }
httpDefaultError(w, http.StatusNotFound) subpath := filepath.Join(path, i.Name())
var subitems []os.DirEntry
subitems, err = os.ReadDir(subpath)
if err != nil {
h.fileAccessError(w, err)
return return
} }
for _, f := range subitems { for _, f := range subitems {
@@ -437,33 +443,41 @@ 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 {
if h.opt.Debug { h.fileAccessError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusNotFound)
return return
} }
var blobs []Blob blobs := []Blob{}
for _, i := range items { for _, i := range items {
if isHashed(objectType) { if isHashed(objectType) {
subpath := filepath.Join(path, i.Name()) if !i.IsDir() {
var subitems []os.FileInfo // ignore files in intermediate directories
subitems, err = ioutil.ReadDir(subpath) continue
if err != nil {
if h.opt.Debug {
log.Print(err)
} }
httpDefaultError(w, http.StatusNotFound) subpath := filepath.Join(path, i.Name())
var subitems []os.DirEntry
subitems, err = os.ReadDir(subpath)
if err != nil {
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()})
} }
} }
@@ -493,10 +507,7 @@ func (h *Handler) checkBlob(w http.ResponseWriter, r *http.Request) {
st, err := os.Stat(path) st, err := os.Stat(path)
if err != nil { if err != nil {
if h.opt.Debug { h.fileAccessError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusNotFound)
return return
} }
@@ -519,10 +530,7 @@ func (h *Handler) getBlob(w http.ResponseWriter, r *http.Request) {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
if h.opt.Debug { h.fileAccessError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusNotFound)
return return
} }
@@ -562,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 {
@@ -631,7 +639,8 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
return return
} }
if err := tf.Sync(); err != nil { syncNotSup, err := syncFile(tf)
if err != nil {
_ = tf.Close() _ = tf.Close()
_ = os.Remove(tf.Name()) _ = os.Remove(tf.Name())
h.incrementRepoSpaceUsage(-written) h.incrementRepoSpaceUsage(-written)
@@ -653,16 +662,22 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
return return
} }
if syncNotSup {
h.opt.FsyncWarning.Do(func() {
log.Print("WARNING: fsync is not supported by the data storage. This can lead to data loss, if the system crashes or the storage is unexpectedly disconnected.")
})
} else {
if err := syncDir(filepath.Dir(path)); err != nil { if err := syncDir(filepath.Dir(path)); err != nil {
// Don't call os.Remove(path) as this is prone to race conditions with parallel upload retries // Don't call os.Remove(path) as this is prone to race conditions with parallel upload retries
h.internalServerError(w, err) h.internalServerError(w, err)
return return
} }
}
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)
@@ -675,6 +690,16 @@ func tempFile(fn string, perm os.FileMode) (f *os.File, err error) {
return return
} }
func syncFile(f *os.File) (bool, error) {
err := f.Sync()
// Ignore error if filesystem does not support fsync.
syncNotSup := err != nil && (errors.Is(err, syscall.ENOTSUP) || isMacENOTTY(err))
if syncNotSup {
err = nil
}
return syncNotSup, err
}
func syncDir(dirname string) error { func syncDir(dirname string) error {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
// syncing a directory is not possible on windows // syncing a directory is not possible on windows
@@ -686,6 +711,10 @@ func syncDir(dirname string) error {
return err return err
} }
err = dir.Sync() err = dir.Sync()
// Ignore error if filesystem does not support fsync.
if errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOENT) || errors.Is(err, syscall.EINVAL) {
err = nil
}
if err != nil { if err != nil {
_ = dir.Close() _ = dir.Close()
return err return err
@@ -721,13 +750,10 @@ func (h *Handler) deleteBlob(w http.ResponseWriter, r *http.Request) {
} }
if err := os.Remove(path); err != nil { if err := os.Remove(path); err != nil {
if h.opt.Debug { // ignore not exist errors to make deleting idempotent, which is
log.Print(err) // necessary to properly handle request retries
} if !errors.Is(err, os.ErrNotExist) {
if os.IsNotExist(err) { h.fileAccessError(w, err)
httpDefaultError(w, http.StatusNotFound)
} else {
h.internalServerError(w, err)
} }
return return
} }
@@ -749,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
} }
@@ -763,14 +789,14 @@ 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
} }
} }
} }
// internalServerError is called to repot an internal server error. // internalServerError is called to report an internal server error.
// The error message will be reported in the server logs. If PanicOnError // The error message will be reported in the server logs. If PanicOnError
// is set, this will panic instead, which makes debugging easier. // is set, this will panic instead, which makes debugging easier.
func (h *Handler) internalServerError(w http.ResponseWriter, err error) { func (h *Handler) internalServerError(w http.ResponseWriter, err error) {
@@ -780,3 +806,18 @@ func (h *Handler) internalServerError(w http.ResponseWriter, err error) {
} }
httpDefaultError(w, http.StatusInternalServerError) httpDefaultError(w, http.StatusInternalServerError)
} }
// internalServerError is called to report an error that occurred while
// accessing a file. If the does not exist, the corresponding http status code
// will be returned to the client. All other errors are passed on to
// internalServerError
func (h *Handler) fileAccessError(w http.ResponseWriter, err error) {
if h.opt.Debug {
log.Print(err)
}
if errors.Is(err, os.ErrNotExist) {
httpDefaultError(w, http.StatusNotFound)
} else {
h.internalServerError(w, err)
}
}

19
repo/repo_unix.go Normal file
View File

@@ -0,0 +1,19 @@
//go:build !windows
// +build !windows
package repo
import (
"errors"
"runtime"
"syscall"
)
// The ExFAT driver on some versions of macOS can return ENOTTY,
// "inappropriate ioctl for device", for fsync.
//
// https://github.com/restic/restic/issues/4016
// https://github.com/realm/realm-core/issues/5789
func isMacENOTTY(err error) bool {
return runtime.GOOS == "darwin" && errors.Is(err, syscall.ENOTTY)
}

4
repo/repo_windows.go Normal file
View File

@@ -0,0 +1,4 @@
package repo
// Windows is not macOS.
func isMacENOTTY(err error) bool { return false }