mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 14:50:55 +03:00
Compare commits
243 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d9cda9d34 | ||
|
|
14d79e052c | ||
|
|
b81f819b3e | ||
|
|
5c1c7e4fa3 | ||
|
|
ebec3042e9 | ||
|
|
50cae4ee7d | ||
|
|
a4009c8894 | ||
|
|
c50d2c15e8 | ||
|
|
cd953e6794 | ||
|
|
f5d64a1a8a | ||
|
|
9a9d16292a | ||
|
|
1c579d73f8 | ||
|
|
904ad2f691 | ||
|
|
bc6bdb2f05 | ||
|
|
d9ac1a5631 | ||
|
|
f37b57884f | ||
|
|
d6e31ce8e2 | ||
|
|
cf1cc25a48 | ||
|
|
9906caefd5 | ||
|
|
bef0e10d1e | ||
|
|
e8df1b6e4c | ||
|
|
991739d47a | ||
|
|
1508fc9253 | ||
|
|
520e22b63d | ||
|
|
d6b584e064 | ||
|
|
cc381443be | ||
|
|
89a251d640 | ||
|
|
dbbae3129d | ||
|
|
c457538280 | ||
|
|
7f65aa1fa4 | ||
|
|
abac3cfc8d | ||
|
|
a805a930e8 | ||
|
|
de72495092 | ||
|
|
7c845f07d5 | ||
|
|
b9ace46180 | ||
|
|
e446e3392d | ||
|
|
a503feaab6 | ||
|
|
cba894987c | ||
|
|
1d120bdd26 | ||
|
|
7245710b31 | ||
|
|
3a3df5670d | ||
|
|
97bbf37af4 | ||
|
|
d120957736 | ||
|
|
324d695d93 | ||
|
|
9d60972743 | ||
|
|
f938af5a61 | ||
|
|
9ccdc3a597 | ||
|
|
3499edd5c2 | ||
|
|
9470cd6e69 | ||
|
|
1f7433e798 | ||
|
|
4ba3d026b4 | ||
|
|
98c639579f | ||
|
|
74e5999c63 | ||
|
|
48939b2b4f | ||
|
|
8339fee69d | ||
|
|
a2fc7d3cc5 | ||
|
|
ae7954eee2 | ||
|
|
8f934f7c82 | ||
|
|
b2781e0bfc | ||
|
|
e11473cf52 | ||
|
|
f8f8962ccb | ||
|
|
2238043efd | ||
|
|
d9426cef20 | ||
|
|
052d586364 | ||
|
|
11ba41e903 | ||
|
|
255985b7b0 | ||
|
|
2b77709a04 | ||
|
|
5b4a1bda2e | ||
|
|
3f94f6d0e7 | ||
|
|
d28a53a6cf | ||
|
|
963cec124e | ||
|
|
bbaca578cd | ||
|
|
da30389989 | ||
|
|
52ec36dbd6 | ||
|
|
b524d178dd | ||
|
|
e0d9b8bddf | ||
|
|
19da923369 | ||
|
|
824a70b22d | ||
|
|
cea70d5d6b | ||
|
|
adad8e658b | ||
|
|
e10487ad57 | ||
|
|
4eded56d5f | ||
|
|
43d011f125 | ||
|
|
a292044501 | ||
|
|
05c54614b2 | ||
|
|
32020e236f | ||
|
|
b9cf6e5083 | ||
|
|
ee5b7290a0 | ||
|
|
fd6a44c562 | ||
|
|
8d12872608 | ||
|
|
712f2053a4 | ||
|
|
54462c26f2 | ||
|
|
d0a171558d | ||
|
|
1ade850557 | ||
|
|
466f2e88b3 | ||
|
|
3cb53b2c33 | ||
|
|
6279216c2e | ||
|
|
5219c1fdd1 | ||
|
|
4294659785 | ||
|
|
f03f1b0156 | ||
|
|
184b99d500 | ||
|
|
74f05e5305 | ||
|
|
aefa7f77c2 | ||
|
|
084d4109b8 | ||
|
|
b60d3f680e | ||
|
|
ee90bfb506 | ||
|
|
e17068a76f | ||
|
|
354fc9b3d6 | ||
|
|
e29f6857db | ||
|
|
40344ec0ff | ||
|
|
783dff369b | ||
|
|
72e0325d05 | ||
|
|
2710207779 | ||
|
|
b719d03ebe | ||
|
|
84396343da | ||
|
|
14242b59a2 | ||
|
|
dad346cee8 | ||
|
|
04282f94a4 | ||
|
|
0423e8f157 | ||
|
|
bdcee06665 | ||
|
|
ae90ed2ba0 | ||
|
|
4ba3ae876d | ||
|
|
662164c7ff | ||
|
|
fad6af11e5 | ||
|
|
dba088daed | ||
|
|
a23fdea9e3 | ||
|
|
561976bcd0 | ||
|
|
874776bd12 | ||
|
|
ec67b67e9e | ||
|
|
71f691b208 | ||
|
|
e0cbb966f0 | ||
|
|
df9d47900a | ||
|
|
b8496c4d6e | ||
|
|
b0cfaf189c | ||
|
|
195cb9f081 | ||
|
|
9a10740218 | ||
|
|
7bcd79a70a | ||
|
|
beb8822df4 | ||
|
|
8805d85377 | ||
|
|
fcf9a8c673 | ||
|
|
2c1319985d | ||
|
|
a3fff56da5 | ||
|
|
14961a573f | ||
|
|
78cd5d8eba | ||
|
|
2df2803a37 | ||
|
|
7738faa040 | ||
|
|
157d1db0b1 | ||
|
|
7e85356325 | ||
|
|
a3d0cf5ddf | ||
|
|
04ab8e72f6 | ||
|
|
e0c3a13ac5 | ||
|
|
1b1745b7f7 | ||
|
|
2412a0a369 | ||
|
|
1e14d006b1 | ||
|
|
27c4ffd663 | ||
|
|
c0fe08b597 | ||
|
|
5550a5d2c0 | ||
|
|
2066ad7c83 | ||
|
|
61199172d0 | ||
|
|
3ce4d04b27 | ||
|
|
707729ee61 | ||
|
|
7b5bebc588 | ||
|
|
53f17b5715 | ||
|
|
496c8bc785 | ||
|
|
396d67bb2c | ||
|
|
bbebd9b163 | ||
|
|
c8d94f0a27 | ||
|
|
8be8343fee | ||
|
|
f3995901e3 | ||
|
|
f2618e7de6 | ||
|
|
6afbd77fd5 | ||
|
|
93e5cb36df | ||
|
|
09dea57850 | ||
|
|
8cad436421 | ||
|
|
f0dedbfabf | ||
|
|
51f0ded222 | ||
|
|
6b555cf0d8 | ||
|
|
0190d0b849 | ||
|
|
9977c64459 | ||
|
|
20706e45b0 | ||
|
|
53864fd8c1 | ||
|
|
7fa0959af4 | ||
|
|
2611dd2c98 | ||
|
|
6cebc037a0 | ||
|
|
15ad31da54 | ||
|
|
fe9904a54d | ||
|
|
831851c0c3 | ||
|
|
ea4c4dd57f | ||
|
|
e5a8220b8a | ||
|
|
ed949604d3 | ||
|
|
0841c7d7bd | ||
|
|
e17975ed7d | ||
|
|
f4eb9e7cd6 | ||
|
|
1085f9e5ec | ||
|
|
37eceffed9 | ||
|
|
6270b2c2d3 | ||
|
|
ad5bd18dd0 | ||
|
|
0296e0cafa | ||
|
|
147ad3b230 | ||
|
|
2da3eabc12 | ||
|
|
ac91170d65 | ||
|
|
f13b901f2d | ||
|
|
c23c73ed34 | ||
|
|
ad5d657a1a | ||
|
|
e2bebc99d1 | ||
|
|
926dcbbc63 | ||
|
|
a7f9581d99 | ||
|
|
75d911f29e | ||
|
|
91e4a54385 | ||
|
|
221a4878aa | ||
|
|
2ea43647ed | ||
|
|
04bdd3a5e4 | ||
|
|
1f9cf194fe | ||
|
|
e87118d2a8 | ||
|
|
fe888729f9 | ||
|
|
d7cd2ac803 | ||
|
|
ba9fe38b8b | ||
|
|
7b00fe3d5a | ||
|
|
fc1ba36ae5 | ||
|
|
2290137868 | ||
|
|
6ebe7691db | ||
|
|
29d1993a3b | ||
|
|
81c693de4e | ||
|
|
2017cb60e9 | ||
|
|
ec4cc33364 | ||
|
|
a22282f275 | ||
|
|
67de4c9c07 | ||
|
|
6591769a07 | ||
|
|
5a222807b7 | ||
|
|
a9207857cf | ||
|
|
37ffa3b55a | ||
|
|
048591553a | ||
|
|
33bfd61a0c | ||
|
|
965d059400 | ||
|
|
676286182a | ||
|
|
3b2002d9ef | ||
|
|
9d7e30807d | ||
|
|
91fae5c4d4 | ||
|
|
e3e85867b1 | ||
|
|
5618b95372 | ||
|
|
bf45d04600 | ||
|
|
80244bd83b | ||
|
|
9a9e7d1a7f |
30
.cirrus.yml
Normal file
30
.cirrus.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
freebsd_task:
|
||||
name: FreeBSD
|
||||
|
||||
matrix:
|
||||
- name: FreeBSD 13.2
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-2
|
||||
|
||||
pkginstall_script:
|
||||
- pkg update -f
|
||||
- pkg install -y go
|
||||
- pkg install -y git
|
||||
|
||||
setup_script:
|
||||
- pw groupadd sftpgo
|
||||
- pw useradd sftpgo -g sftpgo -w none -m
|
||||
- mkdir /home/sftpgo/sftpgo
|
||||
- cp -R . /home/sftpgo/sftpgo
|
||||
- chown -R sftpgo:sftpgo /home/sftpgo/sftpgo
|
||||
|
||||
compile_script:
|
||||
- su sftpgo -c 'cd ~/sftpgo && go build -trimpath -tags nopgxregisterdefaulttypes -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo'
|
||||
- su sftpgo -c 'cd ~/sftpgo/tests/eventsearcher && go build -trimpath -ldflags "-s -w" -o eventsearcher'
|
||||
- su sftpgo -c 'cd ~/sftpgo/tests/ipfilter && go build -trimpath -ldflags "-s -w" -o ipfilter'
|
||||
|
||||
check_script:
|
||||
- su sftpgo -c 'cd ~/sftpgo && ./sftpgo initprovider && ./sftpgo resetprovider --force'
|
||||
|
||||
test_script:
|
||||
- su sftpgo -c 'cd ~/sftpgo && go test -v -tags nopgxregisterdefaulttypes -p 1 -timeout 20m ./... -coverprofile=coverage.txt -covermode=atomic'
|
||||
106
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
106
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
name: Open Source Bug Report
|
||||
description: "Submit a report and help us improve SFTPGo"
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### 👍 Thank you for contributing to our project!
|
||||
Before asking for help please check the [support policy](https://github.com/drakkan/sftpgo#support-policy).
|
||||
If you are a commercial user or a project sponsor please contact us using the dedicated [email address](mailto:support@sftpgo.com).
|
||||
- type: checkboxes
|
||||
id: before-posting
|
||||
attributes:
|
||||
label: "⚠️ This issue respects the following points: ⚠️"
|
||||
description: All conditions are **required**.
|
||||
options:
|
||||
- label: This is a **bug**, not a question or a configuration issue.
|
||||
required: true
|
||||
- label: This issue is **not** already reported on Github _(I've searched it)_.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Bug description
|
||||
description: |
|
||||
Provide a description of the bug you're experiencing.
|
||||
Don't just expect someone will guess what your specific problem is and provide full details.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: |
|
||||
Describe the steps to reproduce the bug.
|
||||
The better your description is the fastest you'll get an _(accurate)_ answer.
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: Describe what you expected to happen instead.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: SFTPGo version
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: data-provider
|
||||
attributes:
|
||||
label: Data provider
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: install-method
|
||||
attributes:
|
||||
label: Installation method
|
||||
description: |
|
||||
Select installation method you've used.
|
||||
_Describe the method in the "Additional info" section if you chose "Other"._
|
||||
options:
|
||||
- "Community Docker image"
|
||||
- "Community Deb package"
|
||||
- "Community RPM package"
|
||||
- "Other"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: "Describe your customizations to the configuration: both config file changes and overrides via environment variables"
|
||||
value: config
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: dropdown
|
||||
id: usecase
|
||||
attributes:
|
||||
label: What are you using SFTPGo for?
|
||||
description: We'd like to understand your SFTPGo usecase more
|
||||
multiple: true
|
||||
options:
|
||||
- "Private user, home usecase (home backup/VPS)"
|
||||
- "Professional user, 1 person business"
|
||||
- "Small business (3-person firm with file exchange?)"
|
||||
- "Medium business"
|
||||
- "Enterprise"
|
||||
- type: textarea
|
||||
id: additional-info
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Any additional information related to the issue.
|
||||
9
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
9
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Commercial Support
|
||||
url: https://sftpgo.com/
|
||||
about: >
|
||||
If you need Professional support, so your reports are prioritized and resolved more quickly.
|
||||
- name: GitHub Community Discussions
|
||||
url: https://github.com/drakkan/sftpgo/discussions
|
||||
about: Please ask and answer questions here.
|
||||
40
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
40
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: 🚀 Feature request
|
||||
description: Suggest an idea for SFTPGo
|
||||
labels: ["suggestion"]
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: A clear and concise description of what the problem is.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: usecase
|
||||
attributes:
|
||||
label: What are you using SFTPGo for?
|
||||
description: We'd like to understand your SFTPGo usecase more
|
||||
multiple: true
|
||||
options:
|
||||
- "Private user, home usecase (home backup/VPS)"
|
||||
- "Professional user, 1 person business"
|
||||
- "Small business (3-person firm with file exchange?)"
|
||||
- "Medium business"
|
||||
- "Enterprise"
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
||||
36
.github/workflows/codeql.yml
vendored
Normal file
36
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '30 1 * * 6'
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: go
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
95
.github/workflows/development.yml
vendored
95
.github/workflows/development.yml
vendored
@@ -2,7 +2,7 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [2.4.x]
|
||||
branches: [2.5.x]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -11,11 +11,11 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go: [1.19]
|
||||
go: ['1.20']
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
upload-coverage: [true]
|
||||
include:
|
||||
- go: 1.19
|
||||
- go: '1.20'
|
||||
os: windows-latest
|
||||
upload-coverage: false
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
@@ -69,11 +69,11 @@ jobs:
|
||||
$Env:GOOS='windows'
|
||||
$Env:GOARCH='arm64'
|
||||
go-winres simply --arch arm64 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
|
||||
mkdir x86
|
||||
$Env:GOARCH='386'
|
||||
go-winres simply --arch 386 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
|
||||
Remove-Item Env:\CGO_ENABLED
|
||||
Remove-Item Env:\GOOS
|
||||
Remove-Item Env:\GOARCH
|
||||
@@ -222,54 +222,26 @@ jobs:
|
||||
name: sftpgo-${{ matrix.os }}-go-${{ matrix.go }}
|
||||
path: output
|
||||
|
||||
test-bundle:
|
||||
name: Build in bundle mode
|
||||
test-build-flags:
|
||||
name: Test build flags
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes,nogcs,nos3,noportable,nobolt,nomysql,nopgsql,nosqlite,nometrics,noazblob,unixcrypt -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
./sftpgo -v
|
||||
cp -r openapi static templates internal/bundle/
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes,bundle -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
./sftpgo -v
|
||||
|
||||
test-goarch-386:
|
||||
name: Run test cases on 32-bit arch
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd tests/eventsearcher
|
||||
go build -trimpath -ldflags "-s -w" -o eventsearcher
|
||||
cd -
|
||||
cd tests/ipfilter
|
||||
go build -trimpath -ldflags "-s -w" -o ipfilter
|
||||
cd -
|
||||
env:
|
||||
GOARCH: 386
|
||||
|
||||
- name: Run test cases
|
||||
run: go test -v -tags nopgxregisterdefaulttypes -p 1 -timeout 15m ./... -covermode=atomic
|
||||
env:
|
||||
SFTPGO_DATA_PROVIDER__DRIVER: memory
|
||||
SFTPGO_DATA_PROVIDER__NAME: ''
|
||||
GOARCH: 386
|
||||
|
||||
test-postgresql-mysql-crdb:
|
||||
name: Test with PgSQL/MySQL/Cockroach
|
||||
runs-on: ubuntu-latest
|
||||
@@ -296,7 +268,7 @@ jobs:
|
||||
MYSQL_USER: sftpgo
|
||||
MYSQL_PASSWORD: sftpgo
|
||||
options: >-
|
||||
--health-cmd "mysqladmin status -h 127.0.0.1 -P 3306 -u root -p$MYSQL_ROOT_PASSWORD"
|
||||
--health-cmd "mariadb-admin status -h 127.0.0.1 -P 3306 -u root -p$MYSQL_ROOT_PASSWORD"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 6
|
||||
@@ -322,9 +294,9 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@@ -336,19 +308,6 @@ jobs:
|
||||
go build -trimpath -ldflags "-s -w" -o ipfilter
|
||||
cd -
|
||||
|
||||
- name: Run tests using PostgreSQL provider
|
||||
run: |
|
||||
./sftpgo initprovider
|
||||
./sftpgo resetprovider --force
|
||||
go test -v -tags nopgxregisterdefaulttypes -p 1 -timeout 15m ./... -covermode=atomic
|
||||
env:
|
||||
SFTPGO_DATA_PROVIDER__DRIVER: postgresql
|
||||
SFTPGO_DATA_PROVIDER__NAME: sftpgo
|
||||
SFTPGO_DATA_PROVIDER__HOST: localhost
|
||||
SFTPGO_DATA_PROVIDER__PORT: 5432
|
||||
SFTPGO_DATA_PROVIDER__USERNAME: postgres
|
||||
SFTPGO_DATA_PROVIDER__PASSWORD: postgres
|
||||
|
||||
- name: Run tests using MySQL provider
|
||||
run: |
|
||||
./sftpgo initprovider
|
||||
@@ -362,6 +321,19 @@ jobs:
|
||||
SFTPGO_DATA_PROVIDER__USERNAME: sftpgo
|
||||
SFTPGO_DATA_PROVIDER__PASSWORD: sftpgo
|
||||
|
||||
- name: Run tests using PostgreSQL provider
|
||||
run: |
|
||||
./sftpgo initprovider
|
||||
./sftpgo resetprovider --force
|
||||
go test -v -tags nopgxregisterdefaulttypes -p 1 -timeout 15m ./... -covermode=atomic
|
||||
env:
|
||||
SFTPGO_DATA_PROVIDER__DRIVER: postgresql
|
||||
SFTPGO_DATA_PROVIDER__NAME: sftpgo
|
||||
SFTPGO_DATA_PROVIDER__HOST: localhost
|
||||
SFTPGO_DATA_PROVIDER__PORT: 5432
|
||||
SFTPGO_DATA_PROVIDER__USERNAME: postgres
|
||||
SFTPGO_DATA_PROVIDER__PASSWORD: postgres
|
||||
|
||||
- name: Run tests using MariaDB provider
|
||||
run: |
|
||||
./sftpgo initprovider
|
||||
@@ -392,6 +364,7 @@ jobs:
|
||||
SFTPGO_DATA_PROVIDER__PORT: 26257
|
||||
SFTPGO_DATA_PROVIDER__USERNAME: root
|
||||
SFTPGO_DATA_PROVIDER__PASSWORD:
|
||||
SFTPGO_DATA_PROVIDER__TARGET_SESSION_ATTRS: any
|
||||
SFTPGO_DATA_PROVIDER__SQL_TABLES_PREFIX: prefix_
|
||||
|
||||
build-linux-packages:
|
||||
@@ -436,7 +409,7 @@ jobs:
|
||||
echo 'apt-get install -q -y curl gcc' >> build.sh
|
||||
if [ ${{ matrix.go }} == 'latest' ]
|
||||
then
|
||||
echo 'GO_VERSION=$(curl -L https://go.dev/VERSION?m=text)' >> build.sh
|
||||
echo 'GO_VERSION=$(curl -L https://go.dev/VERSION?m=text | head -n 1)' >> build.sh
|
||||
else
|
||||
echo 'GO_VERSION=${{ matrix.go }}' >> build.sh
|
||||
fi
|
||||
@@ -479,7 +452,7 @@ jobs:
|
||||
apt-get install -q -y curl gcc
|
||||
if [ ${{ matrix.go }} == 'latest' ]
|
||||
then
|
||||
GO_VERSION=$(curl -L https://go.dev/VERSION?m=text)
|
||||
GO_VERSION=$(curl -L https://go.dev/VERSION?m=text | head -n 1)
|
||||
else
|
||||
GO_VERSION=${{ matrix.go }}
|
||||
fi
|
||||
@@ -542,11 +515,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.20'
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
version: v1.54.2
|
||||
|
||||
27
.github/workflows/docker.yml
vendored
27
.github/workflows/docker.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
# - cron: '0 4 * * *' # everyday at 4:00 AM UTC
|
||||
push:
|
||||
branches:
|
||||
- 2.4.x
|
||||
- 2.5.x
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
@@ -25,9 +25,6 @@ jobs:
|
||||
- true
|
||||
- false
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
docker_pkg: distroless
|
||||
optional_deps: false
|
||||
- os: ubuntu-latest
|
||||
docker_pkg: debian-plugins
|
||||
optional_deps: true
|
||||
@@ -42,6 +39,7 @@ jobs:
|
||||
DOCKERFILE=Dockerfile
|
||||
MINOR=""
|
||||
MAJOR=""
|
||||
FEATURES="nopgxregisterdefaulttypes"
|
||||
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
VERSION=nightly
|
||||
elif [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
@@ -63,13 +61,12 @@ jobs:
|
||||
VERSION="${VERSION}-alpine"
|
||||
VERSION_SLIM="${VERSION}-slim"
|
||||
DOCKERFILE=Dockerfile.alpine
|
||||
elif [[ $DOCKER_PKG == distroless ]]; then
|
||||
VERSION="${VERSION}-distroless"
|
||||
VERSION_SLIM="${VERSION}-slim"
|
||||
DOCKERFILE=Dockerfile.distroless
|
||||
elif [[ $DOCKER_PKG == debian-plugins ]]; then
|
||||
VERSION="${VERSION}-plugins"
|
||||
VERSION_SLIM="${VERSION}-slim"
|
||||
FEATURES="${FEATURES},unixcrypt"
|
||||
elif [[ $DOCKER_PKG == debian ]]; then
|
||||
FEATURES="${FEATURES},unixcrypt"
|
||||
fi
|
||||
DOCKER_IMAGES=("drakkan/sftpgo" "ghcr.io/drakkan/sftpgo")
|
||||
TAGS="${DOCKER_IMAGES[0]}:${VERSION}"
|
||||
@@ -88,13 +85,6 @@ jobs:
|
||||
fi
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:latest"
|
||||
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:slim"
|
||||
elif [[ $DOCKER_PKG == distroless ]]; then
|
||||
if [[ -n $MAJOR && -n $MINOR ]]; then
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:${MINOR}-distroless,${DOCKER_IMAGE}:${MAJOR}-distroless"
|
||||
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:${MINOR}-distroless-slim,${DOCKER_IMAGE}:${MAJOR}-distroless-slim"
|
||||
fi
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:distroless"
|
||||
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:distroless-slim"
|
||||
elif [[ $DOCKER_PKG == debian-plugins ]]; then
|
||||
if [[ -n $MAJOR && -n $MINOR ]]; then
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:${MINOR}-plugins,${DOCKER_IMAGE}:${MAJOR}-plugins"
|
||||
@@ -128,6 +118,7 @@ jobs:
|
||||
echo "plugins=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "dockerfile=${DOCKERFILE}" >> $GITHUB_OUTPUT
|
||||
echo "features=${FEATURES}" >> $GITHUB_OUTPUT
|
||||
echo "created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "sha=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
@@ -157,19 +148,19 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
builder: ${{ steps.builder.outputs.name }}
|
||||
file: ./${{ steps.info.outputs.dockerfile }}
|
||||
platforms: linux/amd64,linux/arm64,linux/ppc64le
|
||||
platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v7
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.info.outputs.tags }}
|
||||
build-args: |
|
||||
COMMIT_SHA=${{ steps.info.outputs.sha }}
|
||||
INSTALL_OPTIONAL_PACKAGES=${{ steps.info.outputs.full }}
|
||||
DOWNLOAD_PLUGINS=${{ steps.info.outputs.plugins }}
|
||||
FEATURES=nopgxregisterdefaulttypes
|
||||
FEATURES=${{ steps.info.outputs.features }}
|
||||
labels: |
|
||||
org.opencontainers.image.title=SFTPGo
|
||||
org.opencontainers.image.description=Fully featured and highly configurable SFTP server with optional HTTP, FTP/S and WebDAV support
|
||||
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
tags: 'v*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.19.3
|
||||
GO_VERSION: 1.20.11
|
||||
|
||||
jobs:
|
||||
prepare-sources-with-deps:
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
@@ -92,11 +92,11 @@ jobs:
|
||||
$Env:GOOS='windows'
|
||||
$Env:GOARCH='arm64'
|
||||
go-winres simply --arch arm64 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
|
||||
mkdir x86
|
||||
$Env:GOARCH='386'
|
||||
go-winres simply --arch 386 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
|
||||
go build -trimpath -tags nopgxregisterdefaulttypes,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
|
||||
Remove-Item Env:\CGO_ENABLED
|
||||
Remove-Item Env:\GOOS
|
||||
Remove-Item Env:\GOARCH
|
||||
|
||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
support@sftpgo.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -1,7 +1,9 @@
|
||||
FROM golang:1.19-bullseye as builder
|
||||
FROM golang:1.20-bullseye as builder
|
||||
|
||||
ENV GOFLAGS="-mod=readonly"
|
||||
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get install --no-install-recommends -y openssh-server && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /workspace
|
||||
WORKDIR /workspace
|
||||
|
||||
@@ -28,14 +30,12 @@ ARG DOWNLOAD_PLUGINS=false
|
||||
|
||||
RUN if [ "${DOWNLOAD_PLUGINS}" = "true" ]; then apt-get update && apt-get install --no-install-recommends -y curl && ./docker/scripts/download-plugins.sh; fi
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y openssh-server && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
# Set to "true" to install jq and the optional git and rsync dependencies
|
||||
ARG INSTALL_OPTIONAL_PACKAGES=false
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y ca-certificates media-types && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get install --no-install-recommends -y ca-certificates media-types && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN if [ "${INSTALL_OPTIONAL_PACKAGES}" = "true" ]; then apt-get update && apt-get install --no-install-recommends -y jq git rsync && rm -rf /var/lib/apt/lists/*; fi
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
FROM golang:1.19-alpine3.16 AS builder
|
||||
FROM golang:1.20-alpine3.18 AS builder
|
||||
|
||||
ENV GOFLAGS="-mod=readonly"
|
||||
|
||||
RUN apk add --update --no-cache bash ca-certificates curl git gcc g++
|
||||
RUN apk -U upgrade --no-cache && apk add --update --no-cache bash ca-certificates curl git gcc g++
|
||||
|
||||
RUN mkdir -p /workspace
|
||||
WORKDIR /workspace
|
||||
@@ -27,12 +27,12 @@ RUN set -xe && \
|
||||
|
||||
RUN apk add --update --no-cache openssh-client-common
|
||||
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.18
|
||||
|
||||
# Set to "true" to install jq and the optional git and rsync dependencies
|
||||
ARG INSTALL_OPTIONAL_PACKAGES=false
|
||||
|
||||
RUN apk add --update --no-cache ca-certificates tzdata mailcap
|
||||
RUN apk -U upgrade --no-cache && apk add --update --no-cache ca-certificates tzdata mailcap
|
||||
|
||||
RUN if [ "${INSTALL_OPTIONAL_PACKAGES}" = "true" ]; then apk add --update --no-cache jq git rsync; fi
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
FROM golang:1.19-bullseye as builder
|
||||
|
||||
ENV CGO_ENABLED=0 GOFLAGS="-mod=readonly"
|
||||
|
||||
RUN mkdir -p /workspace
|
||||
WORKDIR /workspace
|
||||
|
||||
ARG GOPROXY
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
ARG COMMIT_SHA
|
||||
|
||||
# This ARG allows to disable some optional features and it might be useful if you build the image yourself.
|
||||
# For this variant we disable SQLite support since it requires CGO and so a C runtime which is not installed
|
||||
# in distroless/static-* images
|
||||
ARG FEATURES=nosqlite
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN set -xe && \
|
||||
export COMMIT_SHA=${COMMIT_SHA:-$(git describe --always --abbrev=8 --dirty)} && \
|
||||
go build $(if [ -n "${FEATURES}" ]; then echo "-tags ${FEATURES}"; fi) -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=${COMMIT_SHA} -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -v -o sftpgo
|
||||
|
||||
# Modify the default configuration file
|
||||
RUN sed -i 's|"users_base_dir": "",|"users_base_dir": "/srv/sftpgo/data",|' sftpgo.json && \
|
||||
sed -i 's|"backups"|"/srv/sftpgo/backups"|' sftpgo.json && \
|
||||
sed -i 's|"sqlite"|"bolt"|' sftpgo.json
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y media-types openssh-server && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir /etc/sftpgo /var/lib/sftpgo /srv/sftpgo
|
||||
|
||||
FROM gcr.io/distroless/static-debian11
|
||||
|
||||
COPY --from=builder --chown=1000:1000 /etc/sftpgo /etc/sftpgo
|
||||
COPY --from=builder --chown=1000:1000 /srv/sftpgo /srv/sftpgo
|
||||
COPY --from=builder --chown=1000:1000 /var/lib/sftpgo /var/lib/sftpgo
|
||||
COPY --from=builder --chown=1000:1000 /workspace/sftpgo.json /etc/sftpgo/sftpgo.json
|
||||
COPY --from=builder --chown=1000:1000 /etc/ssh/moduli /etc/sftpgo/moduli
|
||||
COPY --from=builder /workspace/templates /usr/share/sftpgo/templates
|
||||
COPY --from=builder /workspace/static /usr/share/sftpgo/static
|
||||
COPY --from=builder /workspace/openapi /usr/share/sftpgo/openapi
|
||||
COPY --from=builder /workspace/sftpgo /usr/local/bin/
|
||||
COPY --from=builder /etc/mime.types /etc/mime.types
|
||||
|
||||
# Log to the stdout so the logs will be available using docker logs
|
||||
ENV SFTPGO_LOG_FILE_PATH=""
|
||||
# These env vars are required to avoid the following error when calling user.Current():
|
||||
# unable to get the current user: user: Current requires cgo or $USER set in environment
|
||||
ENV USER=sftpgo
|
||||
ENV HOME=/var/lib/sftpgo
|
||||
|
||||
WORKDIR /var/lib/sftpgo
|
||||
USER 1000:1000
|
||||
|
||||
CMD ["sftpgo", "serve"]
|
||||
51
README.md
51
README.md
@@ -22,9 +22,9 @@ I'd like to make SFTPGo into a sustainable long term project and would not like
|
||||
If you use SFTPGo, it is in your best interest to ensure that the project you rely on stays healthy and well maintained.
|
||||
This can only happen with your donations and [sponsorships](https://github.com/sponsors/drakkan) :heart:
|
||||
|
||||
If you just take and don't return anything back, the project will die in the long run and you will be forced to pay for a similar proprietary solution.
|
||||
You can also purchase, using many payment methods, support plans from the [SFTPGo website](https://sftpgo.com/#pricing).
|
||||
|
||||
More [info](https://github.com/drakkan/sftpgo/issues/452).
|
||||
With sponsorships/donations or support plans we establish a channel for reciprocal access, ensuring better outcomes for both you and the project.
|
||||
|
||||
### Thank you to our sponsors
|
||||
|
||||
@@ -32,6 +32,10 @@ More [info](https://github.com/drakkan/sftpgo/issues/452).
|
||||
|
||||
[<img src="./img/Aledade_logo.png" alt="Aledade logo" width="202" height="70">](https://www.aledade.com/)
|
||||
|
||||
#### Silver sponsors
|
||||
|
||||
[<img src="./img/Dendi_logo.png" alt="Dendi logo" width="212" height="66">](https://dendisoftware.com/)
|
||||
|
||||
#### Bronze sponsors
|
||||
|
||||
[<img src="https://www.7digital.com/wp-content/themes/sevendigital/images/top_logo.png" alt="7digital logo">](https://www.7digital.com/)
|
||||
@@ -40,19 +44,19 @@ More [info](https://github.com/drakkan/sftpgo/issues/452).
|
||||
|
||||
SFTPGo is an Open Source project and you can of course use it for free but please don't ask for free support as well.
|
||||
|
||||
We will check the reported issues to see if you are experiencing a bug and if so we'll will fix it, but will only provide support to project [sponsors/donors](#sponsors).
|
||||
We will check the reported issues to see if you are experiencing a bug and if so, it may or may not be fixed, we only provide support to project [sponsors/donors](#sponsors).
|
||||
|
||||
If you report an invalid issue or ask for step-by-step support, your issue will remain open with no answer or will be closed as invalid without further explanation. Thanks for understanding.
|
||||
|
||||
## Features
|
||||
|
||||
- Support for serving local filesystem, encrypted local filesystem, S3 Compatible Object Storage, Google Cloud Storage, Azure Blob Storage or other SFTP accounts over SFTP/SCP/FTP/WebDAV.
|
||||
- Virtual folders are supported: a virtual folder can use any of the supported storage backends. So you can have, for example, an S3 user that exposes a GCS bucket (or part of it) on a specified path and an encrypted local filesystem on another one. Virtual folders can be private or shared among multiple users, for shared virtual folders you can define different quota limits for each user.
|
||||
- Virtual folders are supported: a virtual folder can use any of the supported storage backends. So you can have, for example, a user with the S3 backend mapping a GCS bucket (or part of it) on a specified path and an encrypted local filesystem on another one. Virtual folders can be private or shared among multiple users, for shared virtual folders you can define different quota limits for each user.
|
||||
- Configurable [custom commands and/or HTTP hooks](./docs/custom-actions.md) on upload, pre-upload, download, pre-download, delete, pre-delete, rename, mkdir, rmdir on SSH commands and on user add, update and delete.
|
||||
- Virtual accounts stored within a "data provider".
|
||||
- SQLite, MySQL, PostgreSQL, CockroachDB, Bolt (key/value store in pure Go) and in-memory data providers are supported.
|
||||
- Chroot isolation for local accounts. Cloud-based accounts can be restricted to a certain base path.
|
||||
- Per-user and per-directory virtual permissions, for each exposed path you can allow or deny: directory listing, upload, overwrite, download, delete, rename, create directories, create symlinks, change owner/group/file mode and modification time.
|
||||
- Per-user and per-directory virtual permissions, for each path you can allow or deny: directory listing, upload, overwrite, download, delete, rename, create directories, create symlinks, change owner/group/file mode and modification time.
|
||||
- [REST API](./docs/rest-api.md) for users and folders management, data retention, backup, restore and real time reports of the active connections with possibility of forcibly closing a connection.
|
||||
- The [Event Manager](./docs/eventmanager.md) allows to define custom workflows based on server events or schedules.
|
||||
- [Web based administration interface](./docs/web-admin.md) to easily manage users, folders and connections.
|
||||
@@ -62,8 +66,10 @@ If you report an invalid issue or ask for step-by-step support, your issue will
|
||||
- Keyboard interactive authentication. You can easily setup a customizable multi-factor authentication.
|
||||
- Partial authentication. You can configure multi-step authentication requiring, for example, the user password after successful public key authentication.
|
||||
- Per-user authentication methods.
|
||||
- [Two-factor authentication](./docs/howto/two-factor-authentication.md) based on time-based one time passwords (RFC 6238) which works with Authy, Google Authenticator and other compatible apps.
|
||||
- [Two-factor authentication](./docs/howto/two-factor-authentication.md) based on time-based one time passwords (RFC 6238) which works with Authy, Google Authenticator, Microsoft Authenticator and other compatible apps.
|
||||
- LDAP/Active Directory authentication using a [plugin](https://github.com/sftpgo/sftpgo-plugin-auth).
|
||||
- Simplified user administrations using [groups](./docs/groups.md).
|
||||
- [Roles](./docs/roles.md) allow to create limited administrators who can only create and manage users with their role.
|
||||
- Custom authentication via [external programs/HTTP API](./docs/external-auth.md).
|
||||
- Web Client and Web Admin user interfaces support [OpenID Connect](https://openid.net/connect/) authentication and so they can be integrated with identity providers such as [Keycloak](https://www.keycloak.org/). You can find more details [here](./docs/oidc.md).
|
||||
- [Data At Rest Encryption](./docs/dare.md).
|
||||
@@ -84,10 +90,10 @@ If you report an invalid issue or ask for step-by-step support, your issue will
|
||||
- SCP and rsync are supported.
|
||||
- FTP/S is supported. You can configure the FTP service to require TLS for both control and data connections.
|
||||
- [WebDAV](./docs/webdav.md) is supported.
|
||||
- ACME protocol is supported. SFTPGo can obtain and automatically renew TLS certificates for HTTPS, WebDAV and FTPS from `Let's Encrypt` or other ACME compliant certificate authorities, using the the `HTTP-01` or `TLS-ALPN-01` [challenge types](https://letsencrypt.org/docs/challenge-types/).
|
||||
- ACME protocol is supported. SFTPGo can obtain and automatically renew TLS certificates for HTTPS, WebDAV and FTPS from `Let's Encrypt` or other ACME compliant certificate authorities, using the `HTTP-01` or `TLS-ALPN-01` [challenge types](https://letsencrypt.org/docs/challenge-types/).
|
||||
- Two-Way TLS authentication, aka TLS with client certificate authentication, is supported for REST API/Web Admin, FTPS and WebDAV over HTTPS.
|
||||
- Per-user protocols restrictions. You can configure the allowed protocols (SSH/HTTP/FTP/WebDAV) for each user.
|
||||
- [Prometheus metrics](./docs/metrics.md) are exposed.
|
||||
- [Prometheus metrics](./docs/metrics.md) are supported.
|
||||
- Support for HAProxy PROXY protocol: you can proxy and/or load balance the SFTP/SCP/FTP service without losing the information about the client's address.
|
||||
- Easy [migration](./examples/convertusers) from Linux system user accounts.
|
||||
- [Portable mode](./docs/portable-mode.md): a convenient way to share a single directory on demand.
|
||||
@@ -96,10 +102,11 @@ If you report an invalid issue or ask for step-by-step support, your issue will
|
||||
- Configuration format is at your choice: JSON, TOML, YAML, HCL, envfile are supported.
|
||||
- Log files are accurate and they are saved in the easily parsable JSON format ([more information](./docs/logs.md)).
|
||||
- SFTPGo supports a [plugin system](./docs/plugins.md) and therefore can be extended using external plugins.
|
||||
- Infrastructure as Code (IaC) support using the [Terraform provider](https://registry.terraform.io/providers/drakkan/sftpgo/latest).
|
||||
|
||||
## Platforms
|
||||
|
||||
SFTPGo is developed and tested on Linux. After each commit, the code is automatically built and tested on Linux, macOS and Windows using [GitHub Actions](./.github/workflows/development.yml). The test cases are regularly manually executed and passed on FreeBSD. Other *BSD variants should work too.
|
||||
SFTPGo is developed and tested on Linux. After each commit, the code is automatically built and tested on Linux, macOS, Windows and FreeBSD. Other *BSD variants should work too.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -134,7 +141,7 @@ APT and YUM repositories are [available](./docs/repo.md).
|
||||
SFTPGo is also available on some marketplaces:
|
||||
|
||||
- [AWS Marketplace](https://aws.amazon.com/marketplace/seller-profile?id=6e849ab8-70a6-47de-9a43-13c3fa849335)
|
||||
- [Azure Marketplace](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/eliamarzia1667381463185.sftpgo_linux)
|
||||
- Azure Marketplace: [SFTPGo for Linux](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/eliamarzia1667381463185.sftpgo_linux), [SFTPGo for Windows](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/eliamarzia1667381463185.sftpgo_windows)
|
||||
- [Elest.io](https://elest.io/open-source/sftpgo)
|
||||
|
||||
Purchasing from there will help keep SFTPGo a long-term sustainable project.
|
||||
@@ -217,12 +224,12 @@ To start using SFTPGo you need to create an admin user, you can do it in several
|
||||
SFTPGo supports upgrading from the previous release branch to the current one.
|
||||
Some examples for supported upgrade paths are:
|
||||
|
||||
- from 1.2.x to 2.0.x
|
||||
- from 2.0.x to 2.1.x and so on.
|
||||
- from 2.1.x to 2.2.x
|
||||
- from 2.2.x to 2.3.x and so on.
|
||||
|
||||
For supported upgrade paths, the data and schema are migrated automatically, alternately you can use the `initprovider` command.
|
||||
For supported upgrade paths, the data and schema are migrated automatically when SFTPGo starts, alternatively you can use the `initprovider` command before starting SFTPGo.
|
||||
|
||||
So if, for example, you want to upgrade from a version before 1.2.x to 2.0.x, you must first install version 1.2.x, update the data provider and finally install the version 2.0.x. It is recommended to always install the latest available minor version, ie do not install 1.2.0 if 1.2.2 is available.
|
||||
So if, for example, you want to upgrade from 2.0.x to 2.2.x, you must first install version 2.1.x, update the data provider (automatically, by starting SFTPGo or manually using the `initprovider` command) and finally install the version 2.2.x. It is recommended to always install the latest available minor version, ie do not install 2.1.0 if 2.1.2 is available.
|
||||
|
||||
Loading data from a provider independent JSON dump is supported from the previous release branch to the current one too. After upgrading SFTPGo it is advisable to regenerate the JSON dump from the new version.
|
||||
|
||||
@@ -232,13 +239,13 @@ If for some reason you want to downgrade SFTPGo, you may need to downgrade your
|
||||
|
||||
As for upgrading, SFTPGo supports downgrading from the previous release branch to the current one.
|
||||
|
||||
So, if you plan to downgrade from 2.0.x to 1.2.x, before uninstalling 2.0.x version, you can prepare your data provider executing the following command from the configuration directory:
|
||||
So, if you plan to downgrade from 2.3.x to 2.2.x, before uninstalling 2.3.x version, you can prepare your data provider executing the following command from the configuration directory:
|
||||
|
||||
```shell
|
||||
sftpgo revertprovider --to-version 4
|
||||
sftpgo revertprovider
|
||||
```
|
||||
|
||||
Take a look at the CLI usage to see the supported parameter for the `--to-version` argument and to learn how to specify a different configuration file:
|
||||
Take a look at the CLI usage to learn how to specify a configuration file:
|
||||
|
||||
```shell
|
||||
sftpgo revertprovider --help
|
||||
@@ -248,11 +255,11 @@ The `revertprovider` command is not supported for the memory provider.
|
||||
|
||||
Please note that we only support the current release branch and the current main branch, if you find a bug it is better to report it rather than downgrading to an older unsupported version.
|
||||
|
||||
## Users, groups and folders management
|
||||
## Users, groups, folders and other resource management
|
||||
|
||||
After starting SFTPGo you can manage users, groups, folders and other resources using:
|
||||
|
||||
- the [web based administration interface](./docs/web-admin.md)
|
||||
- the [WebAdmin UI](./docs/web-admin.md)
|
||||
- the [REST API](./docs/rest-api.md)
|
||||
|
||||
To support embedded data providers like `bolt` and `SQLite`, which do not support concurrent connections, we can't have a CLI that directly write users and other resources to the data provider, we always have to use the REST API.
|
||||
@@ -294,7 +301,7 @@ More information about custom actions can be found [here](./docs/custom-actions.
|
||||
|
||||
## Virtual folders
|
||||
|
||||
Directories outside the user home directory or based on a different storage provider can be exposed as virtual folders, more information [here](./docs/virtual-folders.md).
|
||||
Directories outside the user home directory or based on a different storage provider can be mapped as virtual folders, more information [here](./docs/virtual-folders.md).
|
||||
|
||||
## Other hooks
|
||||
|
||||
@@ -305,7 +312,7 @@ You can use your own hook to [check passwords](./docs/check-password-hook.md).
|
||||
|
||||
### S3/GCP/Azure
|
||||
|
||||
Each user can be mapped with a [S3 Compatible Object Storage](./docs/s3.md) /[Google Cloud Storage](./docs/google-cloud-storage.md)/[Azure Blob Storage](./docs/azure-blob-storage.md) bucket or a bucket virtual folder that is exposed over SFTP/SCP/FTP/WebDAV.
|
||||
Each user can be mapped with a [S3 Compatible Object Storage](./docs/s3.md) /[Google Cloud Storage](./docs/google-cloud-storage.md)/[Azure Blob Storage](./docs/azure-blob-storage.md) bucket or a bucket virtual folder.
|
||||
|
||||
### SFTP backend
|
||||
|
||||
@@ -323,7 +330,7 @@ HTTP/S backend allows you to write your own custom storage backend by implementi
|
||||
|
||||
Adding new storage backends is quite easy:
|
||||
|
||||
- implement the [Fs interface](./vfs/vfs.go#L28 "interface for filesystem backends").
|
||||
- implement the [Fs interface](./internal/vfs/vfs.go#L86 "interface for filesystem backends").
|
||||
- update the user method `GetFilesystem` to return the new backend
|
||||
- update the web interface and the REST API CLI
|
||||
- add the flags for the new storage backed to the `portable` mode
|
||||
|
||||
@@ -11,20 +11,22 @@
|
||||
功能齐全、高度可配置化、支持自定义 HTTP/S,FTP/S 和 WebDAV 的 SFTP 服务。
|
||||
一些存储后端支持:本地文件系统、加密本地文件系统、S3(兼容)对象存储,Google Cloud 存储,Azure Blob 存储,SFTP。
|
||||
|
||||
:warning: 我無法自己維護中文翻譯,這個文檔可能已經過時了
|
||||
|
||||
## 赞助商
|
||||
|
||||
如果你觉得 SFTPGo 有用,请考虑支持这个开源项目。
|
||||
如果您发现 SFTPGo 有用,请考虑支持这个开源项目。
|
||||
|
||||
维护和发展 SFTPGo 对我来说是很多工作——很容易相当于一份全职工作。
|
||||
维护和发展 SFTPGo 对我来说是一项繁重的工作——很容易相当于一份全职工作。
|
||||
|
||||
我想让 SFTPGo 成为一个可持续的长期项目,并且不想引入双重许可选项并将某些功能仅限于专有版本。
|
||||
|
||||
如果您使用 SFTPGo,确保您所依赖的项目保持健康和维护良好符合您的最大利益。
|
||||
这只能通过您的捐款和[赞助](https://github.com/sponsors/drakkan) 发生:heart:
|
||||
如果您使用 SFTPGo,确保您所依赖的项目保持健康和良好维护符合您的最大利益。
|
||||
这只能通过您的捐赠和[赞助](https://github.com/sponsors/drakkan) 实现:心:
|
||||
|
||||
如果您只是拿走任何东西而不返回任何东西,从长远来看,该项目将失败,您将被迫为类似的专有解决方案付费。
|
||||
您还可以从 [SFTPGo 网站](https://sftpgo.com/#pricing) 购买支持计划。
|
||||
|
||||
[更多信息](https://github.com/drakkan/sftpgo/issues/452)。
|
||||
通过赞助/捐赠或支持计划,我们建立了一个互惠渠道,确保您和项目取得更好的成果。
|
||||
|
||||
### 感谢我们的赞助商
|
||||
|
||||
@@ -32,6 +34,10 @@
|
||||
|
||||
[<img src="./img/Aledade_logo.png" alt="Aledade logo" width="202" height="70">](https://www.aledade.com/)
|
||||
|
||||
#### 銀牌贊助商
|
||||
|
||||
[<img src="./img/Dendi_logo.png" alt="Dendi logo" width="212" height="66">](https://dendisoftware.com/)
|
||||
|
||||
#### 铜牌赞助商
|
||||
|
||||
[<img src="https://www.7digital.com/wp-content/themes/sevendigital/images/top_logo.png" alt="7digital logo">](https://www.7digital.com/)
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Only the current release of the software is actively supported. If you need
|
||||
help backporting fixes into an older release, feel free to ask.
|
||||
Only the current release of the software is actively supported.
|
||||
[Contact us](mailto:support@sftpgo.com) if you need early security patches and enterprise-grade security.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Email your vulnerability information to SFTPGo's maintainer:
|
||||
|
||||
Nicola Murino <nicola.murino@gmail.com>
|
||||
To report (possible) security issues in SFTPGo, please either send a mail to the [SFTPGo Team](mailto:support@sftpgo.com) or use Github's [private reporting feature](https://github.com/drakkan/sftpgo/security/advisories/new).
|
||||
|
||||
@@ -4,18 +4,16 @@ SFTPGo provides an official Docker image, it is available on both [Docker Hub](h
|
||||
|
||||
## Supported tags and respective Dockerfile links
|
||||
|
||||
- [v2.4.2, v2.4, v2, latest](https://github.com/drakkan/sftpgo/blob/v2.4.2/Dockerfile)
|
||||
- [v2.4.2-plugins, v2.4-plugins, v2-plugins, plugins](https://github.com/drakkan/sftpgo/blob/v2.4.2/Dockerfile)
|
||||
- [v2.4.2-alpine, v2.4-alpine, v2-alpine, alpine](https://github.com/drakkan/sftpgo/blob/v2.4.2/Dockerfile.alpine)
|
||||
- [v2.4.2-slim, v2.4-slim, v2-slim, slim](https://github.com/drakkan/sftpgo/blob/v2.4.2/Dockerfile)
|
||||
- [v2.4.2-alpine-slim, v2.4-alpine-slim, v2-alpine-slim, alpine-slim](https://github.com/drakkan/sftpgo/blob/v2.4.2/Dockerfile.alpine)
|
||||
- [v2.4.2-distroless-slim, v2.4-distroless-slim, v2-distroless-slim, distroless-slim](https://github.com/drakkan/sftpgo/blob/v2.4.2/Dockerfile.distroless)
|
||||
- [v2.5.5, v2.5, v2, latest](https://github.com/drakkan/sftpgo/blob/v2.5.5/Dockerfile)
|
||||
- [v2.5.5-plugins, v2.5-plugins, v2-plugins, plugins](https://github.com/drakkan/sftpgo/blob/v2.5.5/Dockerfile)
|
||||
- [v2.5.5-alpine, v2.5-alpine, v2-alpine, alpine](https://github.com/drakkan/sftpgo/blob/v2.5.5/Dockerfile.alpine)
|
||||
- [v2.5.5-slim, v2.5-slim, v2-slim, slim](https://github.com/drakkan/sftpgo/blob/v2.5.5/Dockerfile)
|
||||
- [v2.5.5-alpine-slim, v2.5-alpine-slim, v2-alpine-slim, alpine-slim](https://github.com/drakkan/sftpgo/blob/v2.5.5/Dockerfile.alpine)
|
||||
- [edge](../Dockerfile)
|
||||
- [edge-plugins](../Dockerfile)
|
||||
- [edge-alpine](../Dockerfile.alpine)
|
||||
- [edge-slim](../Dockerfile)
|
||||
- [edge-alpine-slim](../Dockerfile.alpine)
|
||||
- [edge-distroless-slim](../Dockerfile.distroless)
|
||||
|
||||
## How to use the SFTPGo image
|
||||
|
||||
@@ -58,7 +56,7 @@ The FTP service is now available on port 2121 and SFTP on port 2022.
|
||||
|
||||
You can change the passive ports range (`50000-50100` by default) by setting the environment variables `SFTPGO_FTPD__PASSIVE_PORT_RANGE__START` and `SFTPGO_FTPD__PASSIVE_PORT_RANGE__END`.
|
||||
|
||||
It is recommended that you provide a certificate and key file to expose FTP over TLS. You should prefer SFTP to FTP even if you configure TLS, please don't blindly enable the old FTP protocol.
|
||||
It is recommended that you provide a certificate and key file to enable FTP over TLS. You should prefer SFTP to FTP even if you configure TLS, please don't blindly enable the old FTP protocol.
|
||||
|
||||
### Enable WebDAV service
|
||||
|
||||
@@ -75,7 +73,7 @@ docker run --name some-sftpgo \
|
||||
|
||||
The WebDAV service is now available on port 10080 and SFTP on port 2022.
|
||||
|
||||
It is recommended that you provide a certificate and key file to expose WebDAV over https.
|
||||
It is recommended that you provide a certificate and key file to enable WebDAV over https.
|
||||
|
||||
### Container shell access and viewing SFTPGo logs
|
||||
|
||||
@@ -91,14 +89,30 @@ The logs are available through Docker's container log:
|
||||
docker logs some-sftpgo
|
||||
```
|
||||
|
||||
**Note:** [distroless](../Dockerfile.distroless) image contains only a statically linked sftpgo binary and its minimal runtime dependencies. Shell is not available on this image.
|
||||
### Container graceful shutdown
|
||||
|
||||
```shell
|
||||
docker run --name some-sftpgo \
|
||||
-p 2022:2022 \
|
||||
-e SFTPGO_GRACE_TIME=32 \
|
||||
-d "drakkan/sftpgo:tag"
|
||||
```
|
||||
|
||||
Setting the `SFTPGO_GRACE_TIME` environment variable to a non zero value when creating or running a container will enable a graceful shutdown period in seconds that will allow existing connections to hopefully complete before being forcibly closed when the time has passed.
|
||||
|
||||
While the SFTPGo container is in graceful shutdown mode waiting for the last connection(s) to finish, no new connections will be allowed.
|
||||
|
||||
If no connections are active or `SFTPGO_GRACE_TIME=0` (default value if unset) the container will shutdown immediately.
|
||||
|
||||
:warning: The default docker grace time is 10 seconds, so if your SFTPGO_GRACE_TIME is larger than the docker grace time, then any `docker stop some-sftpgo` command will terminate your container once the docker grace time has passed. To ensure that the full SFTPGO_GRACE_TIME can be used, you can send a SIGINT or SIGTERM signal. Those signals can be sent using one of these commands: `docker kill --signal=SIGINT some-sftpgo` or `docker kill --signal=SIGTERM some-sftpgo`.
|
||||
Alternatively you can increase the default docker grace time to a value larger than your SFTPGO_GRACE_TIME. The default docker grace time can either be specified at creation/run time using `--stop-timeout <value>` or you can simply add `--time <value>` to the docker stop command like in this 60 seconds example `docker stop --time 60 some-sftpgo`.
|
||||
|
||||
### Where to Store Data
|
||||
|
||||
Important note: There are several ways to store data used by applications that run in Docker containers. We encourage users of the SFTPGo images to familiarize themselves with the options available, including:
|
||||
|
||||
- Let Docker manage the storage for SFTPGo data by [writing them to disk on the host system using its own internal volume management](https://docs.docker.com/engine/tutorials/dockervolumes/#adding-a-data-volume). This is the default and is easy and fairly transparent to the user. The downside is that the files may be hard to locate for tools and applications that run directly on the host system, i.e. outside containers.
|
||||
- Create a data directory on the host system (outside the container) and [mount this to a directory visible from inside the container]((https://docs.docker.com/engine/tutorials/dockervolumes/#mount-a-host-directory-as-a-data-volume)). This places the SFTPGo files in a known location on the host system, and makes it easy for tools and applications on the host system to access the files. The downside is that the user needs to make sure that the directory exists, and that e.g. directory permissions and other security mechanisms on the host system are set up correctly. The SFTPGo image runs using `1000` as UID/GID by default.
|
||||
- Create a data directory on the host system (outside the container) and [mount this to a directory visible from inside the container](https://docs.docker.com/engine/tutorials/dockervolumes/#mount-a-host-directory-as-a-data-volume). This places the SFTPGo files in a known location on the host system, and makes it easy for tools and applications on the host system to access the files. The downside is that the user needs to make sure that the directory exists, and that e.g. directory permissions and other security mechanisms on the host system are set up correctly. The SFTPGo image runs using `1000` as UID/GID by default.
|
||||
|
||||
The Docker documentation is a good starting point for understanding the different storage options and variations, and there are multiple blogs and forum postings that discuss and give advice in this area. We will simply show the basic procedure here for the latter option above:
|
||||
|
||||
@@ -172,11 +186,9 @@ RUN chown -R 1100:1100 /etc/sftpgo && chown 1100:1100 /var/lib/sftpgo /srv/sftpg
|
||||
USER 1100:1100
|
||||
```
|
||||
|
||||
**Note:** the above Dockerfile will not work if you use the [distroless](../Dockerfile.distroless) image as base since the `chown` command is not available there.
|
||||
|
||||
## Image Variants
|
||||
|
||||
The `sftpgo` images comes in many flavors, each designed for a specific use case. The `edge`, `edge-slim`, `edge-alpine`, `edge-alpine-slim` and `edge-distroless-slim` tags are updated after each new commit.
|
||||
The `sftpgo` images comes in many flavors, each designed for a specific use case. The `edge`, `edge-slim`, `edge-alpine`, and `edge-alpine-slim` tags are updated after each new commit.
|
||||
|
||||
### `sftpgo:<version>`
|
||||
|
||||
@@ -188,15 +200,6 @@ This image is based on the popular [Alpine Linux project](https://alpinelinux.or
|
||||
|
||||
This variant is highly recommended when final image size being as small as possible is desired. The main caveat to note is that it does use [musl libc](https://musl.libc.org/) instead of [glibc and friends](https://www.etalabs.net/compare_libcs.html), so certain software might run into issues depending on the depth of their libc requirements. However, most software doesn't have an issue with this, so this variant is usually a very safe choice. See [this Hacker News comment thread](https://news.ycombinator.com/item?id=10782897) for more discussion of the issues that might arise and some pro/con comparisons of using Alpine-based images.
|
||||
|
||||
### `sftpgo:<version>-distroless`
|
||||
|
||||
This image is based on the popular [Distroless project](https://github.com/GoogleContainerTools/distroless). We use the latest Debian based distroless image as base.
|
||||
|
||||
Distroless variant contains only a statically linked sftpgo binary and its minimal runtime dependencies and so it doesn't allow shell access (no shell is installed).
|
||||
SQLite support is disabled since it requires CGO and so a C runtime which is not installed.
|
||||
The default data provider is `bolt`, all the supported data providers except `sqlite` work.
|
||||
We only provide the slim variant and so the optional `git` dependency is not available.
|
||||
|
||||
### `sftpgo:<suite>-slim`
|
||||
|
||||
These tags provide a slimmer image that does not include `jq` and the optional `git` and `rsync` dependencies.
|
||||
@@ -207,9 +210,6 @@ These tags provide the standard image with the addition of all "official" plugin
|
||||
|
||||
## Helm Chart
|
||||
|
||||
Some helm charts are available:
|
||||
An helm chart is [available](https://artifacthub.io/packages/helm/sagikazarmark/sftpgo). You can find the source code [here](https://github.com/sagikazarmark/helm-charts/tree/master/charts/sftpgo).
|
||||
|
||||
- [sagikazarmark/sftpgo](https://artifacthub.io/packages/helm/sagikazarmark/sftpgo)
|
||||
- [truecharts/sftpgo](https://artifacthub.io/packages/helm/truecharts/sftpgo)
|
||||
|
||||
These charts are not maintained by the SFTPGo project and any issues with the charts should be raised to the upstream repo.
|
||||
This chart is not maintained by the SFTPGo project and any issues with it should be raised to the upstream repo.
|
||||
|
||||
@@ -17,7 +17,7 @@ esac
|
||||
|
||||
echo "download plugins for arch ${SUFFIX}"
|
||||
|
||||
for PLUGIN in geoipfilter kms pubsub eventstore eventsearch metadata
|
||||
for PLUGIN in geoipfilter kms pubsub eventstore eventsearch metadata auth
|
||||
do
|
||||
echo "download plugin from https://github.com/sftpgo/sftpgo-plugin-${PLUGIN}/releases/latest/download/sftpgo-plugin-${PLUGIN}-linux-${SUFFIX}"
|
||||
curl -L "https://github.com/sftpgo/sftpgo-plugin-${PLUGIN}/releases/latest/download/sftpgo-plugin-${PLUGIN}-linux-${SUFFIX}" --output "/usr/local/bin/sftpgo-plugin-${PLUGIN}"
|
||||
|
||||
@@ -18,5 +18,5 @@ SFTPGo supports checking passwords stored with argon2id, bcrypt, pbkdf2, md5cryp
|
||||
|
||||
If you want to use your existing accounts, you have these options:
|
||||
|
||||
- you can import your users inside SFTPGo. Take a look at [convert users](.../examples/convertusers) script, it can convert and import users from Linux system users and Pure-FTPd/ProFTPD virtual users
|
||||
- you can import your users inside SFTPGo. Take a look at [convert users](../examples/convertusers) script, it can convert and import users from Linux system users and Pure-FTPd/ProFTPD virtual users
|
||||
- you can use an external authentication program
|
||||
|
||||
@@ -14,6 +14,7 @@ The following build tags are available:
|
||||
- `noportable`, disable portable mode, default enabled
|
||||
- `nometrics`, disable Prometheus metrics, default enabled
|
||||
- `bundle`, embed static files and templates. Before building with this tag enabled you have to copy `openapi`, `static` and `templates` dirs to `internal/bundle` directory. Default disabled
|
||||
- `unixcrypt`, enable linking to `libcrypt`, default disabled, requires `CGO`
|
||||
|
||||
If no build tag is specified the build will include the default features.
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ Global environment variables are cleared, for security reasons, when the script
|
||||
|
||||
The program must write, on its standard output, the expected JSON serialized response described above.
|
||||
|
||||
Any output of the program on its standard error will be recorded in the SFTPGo logs with sender `check_password_hook` and level `warn`.
|
||||
|
||||
If the hook is an HTTP URL then it will be invoked as HTTP POST. The request body will contain a JSON serialized struct with the following fields:
|
||||
|
||||
- `username`
|
||||
|
||||
@@ -21,15 +21,14 @@ The following `actions` are supported:
|
||||
- `mkdir`
|
||||
- `rmdir`
|
||||
- `ssh_cmd`
|
||||
- `copy`
|
||||
|
||||
The `upload` condition includes both uploads to new files and overwrite of existing ones. If an upload is aborted for quota limits SFTPGo tries to remove the partial file, so if the notification reports a zero size file and a quota exceeded error the file has been deleted. The `ssh_cmd` condition will be triggered after a command is successfully executed via SSH. `scp` will trigger the `download` and `upload` conditions and not `ssh_cmd`. The `first-download` and `first-upload` action are executed only if no error occour and they don't exclude the `download` and `upload` notifications, so you will get both the `first-upload` and `upload` notification after the first successful upload and the same for the first successful download.
|
||||
For cloud backends directories are virtual, they are created implicitly when you upload a file and are implicitly removed when the last file within a directory is removed. The `mkdir` and `rmdir` notifications are sent only when a directory is explicitly created or removed.
|
||||
|
||||
The notification will indicate if an error is detected and so, for example, a partial file is uploaded.
|
||||
|
||||
The `pre-delete` action, if defined, will be called just before files deletion. If the external command completes with a zero exit status or the HTTP notification response code is `200` then SFTPGo will assume that the file was already deleted/moved and so it will not try to remove the file and it will not execute the hook defined for the `delete` action.
|
||||
|
||||
The `pre-download` and `pre-upload` actions, will be called before downloads and uploads. If the external command completes with a zero exit status or the HTTP notification response code is `200` then SFTPGo allows the operation, otherwise the client will get a permission denied error.
|
||||
The `pre-delete`, `pre-download` and `pre-upload` actions, will be called before deleting, downloading and uploading files. If the external command completes with a zero exit status or the HTTP notification response code is `200`, SFTPGo will allow the operation, otherwise the client will get a permission denied error.
|
||||
|
||||
If the `hook` defines a path to an external program, then this program can read the following environment variables:
|
||||
|
||||
@@ -40,7 +39,8 @@ If the `hook` defines a path to an external program, then this program can read
|
||||
- `SFTPGO_ACTION_VIRTUAL_PATH`, virtual path, seen by SFTPGo users
|
||||
- `SFTPGO_ACTION_VIRTUAL_TARGET`, virtual target path, seen by SFTPGo users
|
||||
- `SFTPGO_ACTION_SSH_CMD`, non-empty for `ssh_cmd` `SFTPGO_ACTION`
|
||||
- `SFTPGO_ACTION_FILE_SIZE`, non-zero for `pre-upload`,`upload`, `download` and `delete` actions if the file size is greater than `0`
|
||||
- `SFTPGO_ACTION_FILE_SIZE`, non-zero for `pre-upload`, `upload`, `download`, `delete`, and `copy` actions if the file size is greater than `0`
|
||||
- `SFTPGO_ACTION_ELAPSED`, elapsed time as milliseconds
|
||||
- `SFTPGO_ACTION_FS_PROVIDER`, `0` for local filesystem, `1` for S3 backend, `2` for Google Cloud Storage (GCS) backend, `3` for Azure Blob Storage backend, `4` for local encrypted backend, `5` for SFTP backend
|
||||
- `SFTPGO_ACTION_BUCKET`, non-empty for S3, GCS and Azure backends
|
||||
- `SFTPGO_ACTION_ENDPOINT`, non-empty for S3, SFTP and Azure backend if configured
|
||||
@@ -49,6 +49,7 @@ If the `hook` defines a path to an external program, then this program can read
|
||||
- `SFTPGO_ACTION_IP`, the action was executed from this IP address
|
||||
- `SFTPGO_ACTION_SESSION_ID`, string. Unique protocol session identifier. For stateless protocols such as HTTP the session id will change for each request
|
||||
- `SFTPGO_ACTION_OPEN_FLAGS`, integer. File open flags, can be non-zero for `pre-upload` action. If `SFTPGO_ACTION_FILE_SIZE` is greater than zero and `SFTPGO_ACTION_OPEN_FLAGS&512 == 0` the target file will not be truncated
|
||||
- `SFTPGO_ACTION_ROLE`, string. Role of the user who executed the action
|
||||
- `SFTPGO_ACTION_TIMESTAMP`, int64. Event timestamp as nanoseconds since epoch
|
||||
|
||||
Global environment variables are cleared, for security reasons, when the script is called. You can set additional environment variables in the "command" configuration section.
|
||||
@@ -63,7 +64,8 @@ If the `hook` defines an HTTP URL then this URL will be invoked as HTTP POST. Th
|
||||
- `virtual_path`, string, virtual path, seen by SFTPGo users
|
||||
- `virtual_target_path`, string, virtual target path, seen by SFTPGo users
|
||||
- `ssh_cmd`, string, included for `ssh_cmd` action
|
||||
- `file_size`, int64, included for `pre-upload`, `upload`, `download`, `delete` actions if the file size is greater than `0`
|
||||
- `file_size`, int64, included for `pre-upload`, `upload`, `download`, `delete` and `copy` actions if the file size is greater than `0`
|
||||
- `elapsed`, int64, elapsed size as milliseconds
|
||||
- `fs_provider`, integer, `0` for local filesystem, `1` for S3 backend, `2` for Google Cloud Storage (GCS) backend, `3` for Azure Blob Storage backend, `4` for local encrypted backend, `5` for SFTP backend, `6` for HTTPFs backend
|
||||
- `bucket`, string, included for S3, GCS and Azure backends
|
||||
- `endpoint`, string, included for S3, SFTP and Azure backend if configured
|
||||
@@ -72,6 +74,7 @@ If the `hook` defines an HTTP URL then this URL will be invoked as HTTP POST. Th
|
||||
- `ip`, string. The action was executed from this IP address
|
||||
- `session_id`, string. Unique protocol session identifier. For stateless protocols such as HTTP the session id will change for each request
|
||||
- `open_flags`, integer. File open flags, can be non-zero for `pre-upload` action. If `file_size` is greater than zero and `file_size&512 == 0` the target file will not be truncated
|
||||
- `role`, string. Included if the user who executed the action has a role
|
||||
- `timestamp`, int64. Event timestamp as nanoseconds since epoch
|
||||
|
||||
The HTTP hook will use the global configuration for HTTP clients and will respect the retry configurations.
|
||||
@@ -102,15 +105,16 @@ If the `hook` defines a path to an external program, then this program can read
|
||||
- `SFTPGO_PROVIDER_ACTION`, supported values are `add`, `update`, `delete`
|
||||
- `SFTPGO_PROVIDER_OBJECT_TYPE`, affected object type
|
||||
- `SFTPGO_PROVIDER_OBJECT_NAME`, unique identifier for the affected object, for example username or key id
|
||||
- `SFTPGO_PROVIDER_USERNAME`, the username that executed the action. There are two special usernames: `__self__` identifies a user/admin that updates itself and `__system__` identifies an action that does not have an explicit executor associated with it, for example users/admins can be added/updated by loading them from initial data
|
||||
- `SFTPGO_PROVIDER_USERNAME`, the admin username that executed the action. There are two special usernames: `__self__` identifies a user/admin that updates itself and `__system__` identifies an action that does not have an explicit executor associated with it, for example users/admins can be added/updated by loading them from initial data
|
||||
- `SFTPGO_PROVIDER_IP`, the action was executed from this IP address
|
||||
- `SFTPGO_PROVIDER_ROLE`, the action was executed by an admin with this role
|
||||
- `SFTPGO_PROVIDER_TIMESTAMP`, event timestamp as nanoseconds since epoch
|
||||
- `SFTPGO_PROVIDER_OBJECT`, object serialized as JSON with sensitive fields removed
|
||||
|
||||
Global environment variables are cleared, for security reasons, when the script is called. You can set additional environment variables in the "command" configuration section.
|
||||
The program must finish within 15 seconds.
|
||||
|
||||
If the `hook` defines an HTTP URL then this URL will be invoked as HTTP POST. The action, username, ip, object_type and object_name and timestamp are added to the query string, for example `<hook>?action=update&username=admin&ip=127.0.0.1&object_type=user&object_name=user1×tamp=1633860803249`, and the full object is sent serialized as JSON inside the POST body with sensitive fields removed.
|
||||
If the `hook` defines an HTTP URL then this URL will be invoked as HTTP POST. The action, username, ip, object_type and object_name and timestamp and role are added to the query string, for example `<hook>?action=update&username=admin&ip=127.0.0.1&object_type=user&object_name=user1×tamp=1633860803249`, and the full object is sent serialized as JSON inside the POST body with sensitive fields removed. The role is added only if not empty.
|
||||
|
||||
The HTTP hook will use the global configuration for HTTP clients and will respect the retry configurations.
|
||||
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
|
||||
The built-in `defender` allows you to configure an auto-blocking policy for SFTPGo and thus helps to prevent DoS (Denial of Service) and brute force password guessing.
|
||||
|
||||
If enabled it will protect SFTP, HTTP, FTP and WebDAV services and it will automatically block hosts (IP addresses) that continually fail to log in or attempt to connect.
|
||||
If enabled it will protect SFTP, HTTP (WebClient and user API), FTP and WebDAV services and it will automatically block hosts (IP addresses) that continually fail to log in or attempt to connect.
|
||||
|
||||
You can configure a score for the following events:
|
||||
|
||||
- `score_valid`, defines the score for valid login attempts, eg. user accounts that exist. Default `1`.
|
||||
- `score_invalid`, defines the score for invalid login attempts, eg. non-existent user accounts or client disconnected for inactivity without authentication attempts. Default `2`.
|
||||
- `score_invalid`, defines the score for invalid login attempts, eg. non-existent user accounts. Default `2`.
|
||||
- `score_no_auth`, defines the score for clients disconnected without any authentication attempt. Default `0`.
|
||||
- `score_limit_exceeded`, defines the score for hosts that exceeded the configured rate limits or the configured max connections per host. Default `3`.
|
||||
|
||||
You can set the score to `0` to not penalize some events.
|
||||
|
||||
And then you can configure:
|
||||
|
||||
- `observation_time`, defines the time window, in minutes, for tracking client errors.
|
||||
@@ -39,31 +42,4 @@ Using the REST API you can:
|
||||
- list hosts within the defender's lists
|
||||
- remove hosts from the defender's lists
|
||||
|
||||
The `defender` can also load a permanent block list and/or a safe list of ip addresses/networks from a file:
|
||||
|
||||
- `safelist_file`, defines the path to a file containing a list of ip addresses and/or networks to never ban.
|
||||
- `blocklist_file`, defines the path to a file containing a list of ip addresses and/or networks to always ban.
|
||||
|
||||
These list must be stored as JSON conforming to the following schema:
|
||||
|
||||
- `addresses`, list of strings. Each string must be a valid IPv4/IPv6 address.
|
||||
- `networks`, list of strings. Each string must be a valid IPv4/IPv6 CIDR address.
|
||||
|
||||
Here is a small example:
|
||||
|
||||
```json
|
||||
{
|
||||
"addresses":[
|
||||
"192.0.2.1",
|
||||
"2001:db8::68"
|
||||
],
|
||||
"networks":[
|
||||
"192.0.3.0/24",
|
||||
"2001:db8:1234::/48"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Small lists can also be set using the `safelist`/`blocklist` configuration parameters and or using environment variables. These lists will be merged with the ones specified via files, if any, so that you can set both.
|
||||
|
||||
These list will be always loaded in memory (even if you use the `provider` driver) for faster lookups. The REST API queries "live" data and not these lists.
|
||||
The `defender` can also check permanent block and safe lists of IP addresses/networks. You can define these lists using the WebAdmin UI or the REST API. In multi-nodes setups, the list entries propagation between nodes may take some minutes.
|
||||
|
||||
@@ -15,6 +15,8 @@ The program must write, on its standard output:
|
||||
- an empty string (or no response at all) if the user should not be created/updated
|
||||
- or the SFTPGo user, JSON serialized, if you want to create or update the given user
|
||||
|
||||
Any output of the program on its standard error will be recorded in the SFTPGo logs with sender `pre_login_hook` and level `warn`.
|
||||
|
||||
If the hook is an HTTP URL then it will be invoked as HTTP POST. The login method, the used protocol and the ip address of the user trying to login are added to the query string, for example `<http_url>?login_method=password&ip=1.2.3.4&protocol=SSH`.
|
||||
The request body will contain the user trying to login serialized as JSON. If no modification is needed the HTTP response code must be 204, otherwise the response code must be 200 and the response body a valid SFTPGo user serialized as JSON.
|
||||
|
||||
|
||||
@@ -13,11 +13,15 @@ The following actions are supported:
|
||||
- `Transfer quota reset`. The transfer quota values will be reset to `0`.
|
||||
- `Data retention check`. You can define per-folder retention policies.
|
||||
- `Metadata check`. A metadata check requires a metadata plugin such as [this one](https://github.com/sftpgo/sftpgo-plugin-metadata) and removes the metadata associated to missing items (for example objects deleted outside SFTPGo). A metadata check does nothing is no metadata plugin is installed or external metadata are not supported for a filesystem.
|
||||
- `Password expiration check`. You can send an email notification to users whose password is about to expire.
|
||||
- `User expiration check`. You can receive notifications with expired users.
|
||||
- `Identity Provider account check`. You can create/update accounts for users/admins logging in using an Identity Provider.
|
||||
- `Filesystem`. For these actions, the required permissions are automatically granted. This is the same as executing the actions from an SFTP client and the same restrictions applies. Supported actions:
|
||||
- `Rename`. You can rename one or more files or directories.
|
||||
- `Delete`. You can delete one or more files and directories.
|
||||
- `Create directories`. You can create one or more directories including sub-directories.
|
||||
- `Path exists`. Check if the specified path exists.
|
||||
- `Copy`. You can copy one or more files or directories.
|
||||
- `Compress paths`. You can compress (currently as zip) ore or more files and directories.
|
||||
|
||||
The following placeholders are supported:
|
||||
@@ -37,11 +41,15 @@ The following placeholders are supported:
|
||||
- `{{TargetName}}`. Target object name for renames.
|
||||
- `{{FsTargetPath}}`. Full filesystem target path for renames.
|
||||
- `{{FileSize}}`. File size.
|
||||
- `{{Elapsed}}`. Elapsed time as milliseconds for filesystem events.
|
||||
- `{{Protocol}}`. Used protocol, for example `SFTP`, `FTP`.
|
||||
- `{{IP}}`. Client IP address.
|
||||
- `{{Role}}`. User or admin role.
|
||||
- `{{Timestamp}}`. Event timestamp as nanoseconds since epoch.
|
||||
- `{{Email}}`. For filesystem events, this is the email associated with the user performing the action. For the provider events, this is the email associated with the affected user or admin. Blank in all other cases.
|
||||
- `{{ObjectData}}`. Provider object data serialized as JSON with sensitive fields removed.
|
||||
- `{{RetentionReports}}`. Data retention reports as zip compressed CSV files. Supported as email attachment, file path for multipart HTTP request and as single parameter for HTTP requests body. Data retention reports contain details on the number of files deleted and the total size deleted for each folder.
|
||||
- `{{IDPField<fieldname>}}`. Identity Provider custom fields containing a string.
|
||||
|
||||
Event rules are based on the premise that an event occours. To each rule you can associate one or more actions.
|
||||
The following trigger events are supported:
|
||||
@@ -51,6 +59,8 @@ The following trigger events are supported:
|
||||
- `Schedules`. The scheduler uses UTC time.
|
||||
- `IP Blocked`, this event can be generated if you enable the [defender](./defender.md).
|
||||
- `Certificate`, this event is generated when a certificate is renewed using the built-in ACME protocol. Both successful and failed renewals are notified.
|
||||
- `On demand`, this trigger is generated manually using the WebAdmin or the REST API.
|
||||
- `Identity Provider login`, this trigger is generated when a user/admin logs in using an external Identity Provider.
|
||||
|
||||
You can further restrict a rule by specifying additional conditions that must be met before the rule’s actions are taken. For example you can react to uploads only if they are performed by a particular user or using a specified protocol.
|
||||
|
||||
@@ -60,7 +70,7 @@ Actions are executed in a sequential order except for sync actions that are exec
|
||||
|
||||
- `Stop on failure`, the next action will not be executed if the current one fails.
|
||||
- `Failure action`, this action will be executed only if at least another one fails. :warning: Please note that a failure action isn't executed if the event fails, for example if a download fails the main action is executed. The failure action is executed only if one of the non-failure actions associated to a rule fails.
|
||||
- `Execute sync`, for upload events, you can execute the action synchronously. Executing an action synchronously means that SFTPGo will not return a result code to the client (which is waiting for it) until your action have completed its execution. If your acion takes a long time to complete this could cause a timeout on the client side, which wouldn't receive the server response in a timely manner and eventually drop the connection.
|
||||
- `Execute sync`, for upload events, you can execute the action(s) synchronously. Executing an action synchronously means that SFTPGo will not return a result code to the client (which is waiting for it) until your action have completed its execution. If your acion takes a long time to complete this could cause a timeout on the client side, which wouldn't receive the server response in a timely manner and eventually drop the connection. For pre-* events at least a sync action is required. If pre-delete,pre-upload, pre-download sync action(s) completes successfully, SFTPGo will allow the operation, otherwise the client will get a permission denied error.
|
||||
|
||||
If you are running multiple SFTPGo instances connected to the same data provider, you can choose whether to allow simultaneous execution for scheduled actions.
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ The program must write, on its standard output:
|
||||
- an empty string, or no response at all, if authentication succeeds and the existing SFTPGo user does not need to be updated. This means that the credentials already stored in SFTPGo must match those used for the current authentication.
|
||||
- a user with an empty username if the authentication fails
|
||||
|
||||
Any output of the program on its standard error will be recorded in the SFTPGo logs with sender `external_auth_hook` and level `warn`.
|
||||
|
||||
If the hook is an HTTP URL then it will be invoked as HTTP POST. The request body will contain a JSON serialized struct with the following fields:
|
||||
|
||||
- `username`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Configuring SFTPGo
|
||||
|
||||
<details><summary><font size=5> Command line option</font></summary>
|
||||
<details><summary><font size=5> Command line options</font></summary>
|
||||
|
||||
The SFTPGo executable can be used this way:
|
||||
|
||||
@@ -13,8 +13,10 @@ Available Commands:
|
||||
gen A collection of useful generators
|
||||
help Help about any command
|
||||
initprovider Initialize and/or updates the configured data provider
|
||||
ping Issues an health check to SFTPGo
|
||||
portable Serve a single directory/account
|
||||
resetprovider Reset the configured provider, any data will be lost
|
||||
resetpwd Reset the password for the specified administrator
|
||||
revertprovider Revert the configured data provider to a previous version
|
||||
serve Start the SFTPGo service
|
||||
smtptest Test the SMTP configuration
|
||||
@@ -24,7 +26,7 @@ Flags:
|
||||
-h, --help help for sftpgo
|
||||
-v, --version
|
||||
|
||||
Use "sftpgo [command] --help" for more information about a command
|
||||
Use "sftpgo [command] --help" for more information about a command.
|
||||
```
|
||||
|
||||
The `serve` command supports the following flags:
|
||||
@@ -56,60 +58,64 @@ The `gen` command allows to generate completion scripts for your shell and man p
|
||||
|
||||
The configuration file contains the following sections:
|
||||
|
||||
<details><summary><font size=4>Common</font></summary>
|
||||
|
||||
- **"common"**, configuration parameters shared among all the supported protocols
|
||||
- `idle_timeout`, integer. Time in minutes after which an idle client will be disconnected. 0 means disabled. Default: 15
|
||||
- `upload_mode` integer. 0 means standard: the files are uploaded directly to the requested path. 1 means atomic: files are uploaded to a temporary path and renamed to the requested path when the client ends the upload. Atomic mode avoids problems such as a web server that serves partial files when the files are being uploaded. In atomic mode, if there is an upload error, the temporary file is deleted and so the requested upload path will not contain a partial file. 2 means atomic with resume support: same as atomic but if there is an upload error, the temporary file is renamed to the requested path and not deleted. This way, a client can reconnect and resume the upload. Ignored for cloud-based storage backends (uploads are always atomic and resume is not supported for these backends) and for SFTP backend if buffering is enabled. Default: 0
|
||||
- `actions`, struct. It contains the command to execute and/or the HTTP URL to notify and the trigger conditions. See [Custom Actions](./custom-actions.md) for more details
|
||||
- `execute_on`, list of strings. Valid values are `pre-download`, `download`, `pre-upload`, `upload`, `pre-delete`, `delete`, `rename`, `mkdir`, `rmdir`, `ssh_cmd`. Leave empty to disable actions.
|
||||
- `execute_on`, list of strings. Valid values are `pre-download`, `download`, `first-download`, `pre-upload`, `upload`, `first-upload`, `pre-delete`, `delete`, `rename`, `mkdir`, `rmdir`, `ssh_cmd`, `copy`. Leave empty to disable actions.
|
||||
- `execute_sync`, list of strings. Actions, defined in the `execute_on` list above, to be performed synchronously. The `pre-*` actions are always executed synchronously while the other ones are asynchronous. Executing an action synchronously means that SFTPGo will not return a result code to the client (which is waiting for it) until your hook have completed its execution. Leave empty to execute only the defined `pre-*` hook synchronously
|
||||
- `hook`, string. Absolute path to the command to execute or HTTP URL to notify.
|
||||
- `setstat_mode`, integer. 0 means "normal mode": requests for changing permissions, owner/group and access/modification times are executed. 1 means "ignore mode": requests for changing permissions, owner/group and access/modification times are silently ignored. 2 means "ignore mode if not supported": requests for changing permissions and owner/group are silently ignored for cloud filesystems and executed for local/SFTP filesystem. Requests for changing modification times are always executed for local/SFTP filesystems and are executed for cloud based filesystems if the target is a file and there is a metadata plugin available. A metadata plugin can be found [here](https://github.com/sftpgo/sftpgo-plugin-metadata).
|
||||
- `rename_mode`, integer. By default (`0`), renaming of non-empty directories is not allowed for cloud storage providers (S3, GCS, Azure Blob). Set to `1` to enable recursive renames for these providers, they may be slow, there is no atomic rename API like for local filesystem, so SFTPGo will recursively list the directory contents and do a rename for each entry (partial renaming and incorrect disk quota updates are possible in error cases). Default `0`.
|
||||
- `temp_path`, string. Defines the path for temporary files such as those used for atomic uploads or file pipes. If you set this option you must make sure that the defined path exists, is accessible for writing by the user running SFTPGo, and is on the same filesystem as the users home directories otherwise the renaming for atomic uploads will become a copy and therefore may take a long time. The temporary files are not namespaced. The default is generally fine. Leave empty for the default.
|
||||
- `proxy_protocol`, integer. Support for [HAProxy PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt). If you are running SFTPGo behind a proxy server such as HAProxy, AWS ELB or NGINX, you can enable the proxy protocol. It provides a convenient way to safely transport connection information such as a client's address across multiple layers of NAT or TCP proxies to get the real client IP address instead of the proxy IP. Both protocol versions 1 and 2 are supported. If the proxy protocol is enabled in SFTPGo then you have to enable the protocol in your proxy configuration too. For example, for HAProxy, add `send-proxy` or `send-proxy-v2` to each server configuration line. The PROXY protocol is supported for SSH/SFTP and FTP/S. The following modes are supported:
|
||||
- 0, disabled
|
||||
- 1, enabled. If the upstream IP is not allowed to send a proxy header the header be ignored. Using this mode does not mean that we can accept connections with and without the proxy header. We always try to read the proxy header and we ignore it if the upstream IP is not allowed to send a proxy header
|
||||
- 2, required. If the upstream IP is not allowed to send a proxy header the connection will be rejected
|
||||
- `proxy_allowed`, List of IP addresses and IP ranges allowed to send the proxy header:
|
||||
- 1, enabled. If the upstream IP is not allowed to send a proxy header the header be ignored. Using this mode does not mean that we can accept connections with and without the proxy header. We always try to read the proxy header and we ignore it if the upstream IP is not allowed to send a proxy header. Set `proxy_skipped` if you want to allow some IP/networks to connect without sending a proxy header
|
||||
- 2, required. If the upstream IP is not allowed to send a proxy header the connection will be rejected (unless the upstream IP is listed in `proxy_skipped`)
|
||||
- `proxy_allowed`, list of IP addresses and IP ranges allowed to send the proxy header:
|
||||
- If `proxy_protocol` is set to 1 and we receive a proxy header from an IP that is not in the list then the connection will be accepted and the header will be ignored
|
||||
- If `proxy_protocol` is set to 2 and we receive a proxy header from an IP that is not in the list then the connection will be rejected
|
||||
- `proxy_skipped`, list of IP address and IP ranges for which not to read the proxy header
|
||||
- `startup_hook`, string. Absolute path to an external program or an HTTP URL to invoke as soon as SFTPGo starts. If you define an HTTP URL it will be invoked using a `GET` request. Please note that SFTPGo services may not yet be available when this hook is run. Leave empty do disable
|
||||
- `post_connect_hook`, string. Absolute path to the command to execute or HTTP URL to notify. See [Post-connect hook](./post-connect-hook.md) for more details. Leave empty to disable
|
||||
- `post_disconnect_hook`, string. Absolute path to the command to execute or HTTP URL to notify. See [Post-disconnect hook](./post-disconnect-hook.md) for more details. Leave empty to disable
|
||||
- `data_retention_hook`, string. Absolute path to the command to execute or HTTP URL to notify. See [Data retention hook](./data-retention-hook.md) for more details. Leave empty to disable
|
||||
- `max_total_connections`, integer. Maximum number of concurrent client connections. 0 means unlimited. Default: 0.
|
||||
- `max_per_host_connections`, integer. Maximum number of concurrent client connections from the same host (IP). If the defender is enabled, exceeding this limit will generate `score_limit_exceeded` events and thus hosts that repeatedly exceed the max allowed connections can be automatically blocked. 0 means unlimited. Default: 20.
|
||||
- `whitelist_file`, string. Path to a file containing a list of IP addresses and/or networks to allow. Only the listed IPs/networks can access the configured services, all other client connections will be dropped before they even try to authenticate. The whitelist must be a JSON file with the same structure documented for the [defenders's list](./defender.md). The whitelist can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. Default: "".
|
||||
- `max_total_connections`, integer. Maximum number of concurrent client connections. 0 means unlimited. Default: `0`.
|
||||
- `max_per_host_connections`, integer. Maximum number of concurrent client connections from the same host (IP). If the defender is enabled, exceeding this limit will generate `score_limit_exceeded` events and thus hosts that repeatedly exceed the max allowed connections can be automatically blocked. 0 means unlimited. Default: `20`.
|
||||
- `allowlist_status`, integer. Set to `1` to enable the allow list. The allow list can be populated using the WebAdmin or the REST API. If enabled, only the listed IPs/networks can access the configured services, all other client connections will be dropped before they even try to authenticate. Ensure to populate your allow list before enabling this setting. In multi-nodes setups, the list entries propagation between nodes may take some minutes. Default: `0`.
|
||||
- `allow_self_connections`, integer. Allow users on this instance to use other users/virtual folders on this instance as storage backend. Enable this setting if you know what you are doing. Set to `1` to enable. Default: `0`.
|
||||
- `defender`, struct containing the defender configuration. See [Defender](./defender.md) for more details.
|
||||
- `enabled`, boolean. Default `false`.
|
||||
- `driver`, string. Supported drivers are `memory` and `provider`. The `provider` driver will use the configured data provider to store defender events and it is supported for `MySQL`, `PostgreSQL` and `CockroachDB` data providers. Using the `provider` driver you can share the defender events among multiple SFTPGO instances. For a single instance the `memory` driver will be much faster. Default: `memory`.
|
||||
- `ban_time`, integer. Ban time in minutes.
|
||||
- `ban_time_increment`, integer. Ban time increment, as a percentage, if a banned host tries to connect again.
|
||||
- `threshold`, integer. Threshold value for banning a client.
|
||||
- `score_invalid`, integer. Score for invalid login attempts, eg. non-existent user accounts or client disconnected for inactivity without authentication attempts.
|
||||
- `score_valid`, integer. Score for valid login attempts, eg. user accounts that exist.
|
||||
- `score_limit_exceeded`, integer. Score for hosts that exceeded the configured rate limits or the maximum, per-host, allowed connections.
|
||||
- `observation_time`, integer. Defines the time window, in minutes, for tracking client errors. A host is banned if it has exceeded the defined threshold during the last observation time minutes.
|
||||
- `entries_soft_limit`, integer. Ignored for `provider` driver. Default: 100.
|
||||
- `entries_hard_limit`, integer. The number of banned IPs and host scores kept in memory will vary between the soft and hard limit for `memory` driver. If you use the `provider` driver, this setting will limit the number of entries to return when you ask for the entire host list from the defender. Default: 150.
|
||||
- `safelist_file`, string. Path to a file containing a list of ip addresses and/or networks to never ban.
|
||||
- `blocklist_file`, string. Path to a file containing a list of ip addresses and/or networks to always ban. The lists can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. An host that is already banned will not be automatically unbanned if you put it inside the safe list, you have to unban it using the REST API.
|
||||
- `safelist`, list of IP addresses and/or IP ranges and/or networks to never ban. Invalid entries will be silently ignored. For large lists prefer `safelist_file`. `safelist` and `safelist_file` will be merged so that you can set both.
|
||||
- `blocklist`, list of IP addresses and/or IP ranges and/or networks to always ban. Invalid entries will be silently ignored.. For large lists prefer `blocklist_file`. `blocklist` and `blocklist_file` will be merged so that you can set both.
|
||||
- `ban_time`, integer. Ban time in minutes. Default: `30`.
|
||||
- `ban_time_increment`, integer. Ban time increment, as a percentage, if a banned host tries to connect again. Default: `50`.
|
||||
- `threshold`, integer. Threshold value for banning a client. Default: `15`.
|
||||
- `score_invalid`, integer. Score for invalid login attempts, eg. non-existent user accounts. Default: `2`.
|
||||
- `score_valid`, integer. Score for valid login attempts, eg. user accounts that exist. Default: `1`.
|
||||
- `score_limit_exceeded`, integer. Score for hosts that exceeded the configured rate limits or the maximum, per-host, allowed connections. Default: `3`.
|
||||
- `score_no_auth`, defines the score for clients disconnected without any authentication attempt. Default: `0`.
|
||||
- `observation_time`, integer. Defines the time window, in minutes, for tracking client errors. A host is banned if it has exceeded the defined threshold during the last observation time minutes. Default: `30`.
|
||||
- `entries_soft_limit`, integer. Ignored for `provider` driver. Default: `100`.
|
||||
- `entries_hard_limit`, integer. The number of banned IPs and host scores kept in memory will vary between the soft and hard limit for `memory` driver. If you use the `provider` driver, this setting will limit the number of entries to return when you ask for the entire host list from the defender. Default: `150`.
|
||||
- `rate_limiters`, list of structs containing the rate limiters configuration. Take a look [here](./rate-limiting.md) for more details. Each struct has the following fields:
|
||||
- `average`, integer. Average defines the maximum rate allowed. 0 means disabled. Default: 0
|
||||
- `period`, integer. Period defines the period as milliseconds. The rate is actually defined by dividing average by period Default: 1000 (1 second).
|
||||
- `burst`, integer. Burst defines the maximum number of requests allowed to go through in the same arbitrarily small period of time. Default: 1
|
||||
- `type`, integer. 1 means a global rate limiter, independent from the source host. 2 means a per-ip rate limiter. Default: 2
|
||||
- `protocols`, list of strings. Available protocols are `SSH`, `FTP`, `DAV`, `HTTP`. By default all supported protocols are enabled
|
||||
- `allow_list`, list of IP addresses and IP ranges excluded from rate limiting. Default: empty
|
||||
- `generate_defender_events`, boolean. If `true`, the defender is enabled, and this is not a global rate limiter, a new defender event will be generated each time the configured limit is exceeded. Default `false`
|
||||
- `entries_soft_limit`, integer.
|
||||
- `entries_hard_limit`, integer. The number of per-ip rate limiters kept in memory will vary between the soft and hard limit
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>ACME</font></summary>
|
||||
|
||||
- **"acme"**, Automatic Certificate Management Environment (ACME) protocol configuration. To obtain the certificates the first time you have to configure the ACME protocol and execute the `sftpgo acme run` command. The SFTPGo service will take care of the automatic renewal of certificates for the configured domains.
|
||||
- `domains`, list of domains for which to obtain certificates. If a single certificate is to be valid for multiple domains specify the names separated by commas, for example: `example.com,www.example.com`. An empty list means that ACME protocol is disabled. Default: empty.
|
||||
- `domains`, list of domains for which to obtain certificates. If a single certificate is to be valid for multiple domains specify the names separated by commas or spaces, for example: `example.com,www.example.com` or `example.com www.example.com`. An empty list means that ACME protocol is disabled. Default: empty.
|
||||
- `email`, string. Email used for registration and recovery contact. Default: empty.
|
||||
- `key_type`, string. Key type to use for private keys. Supported values: `2048` (RSA 2048), `4096` (RSA 4096), `8192` (RSA 8192), `P256` (EC 256), `P384` (EC 384). Default: `4096`
|
||||
- `key_type`, string. Key type to use for private keys. Supported values: `2048` (RSA 2048), `3072` (RSA 3072), `4096` (RSA 4096), `8192` (RSA 8192), `P256` (EC 256), `P384` (EC 384). Default: `4096`
|
||||
- `certs_path`, string. Directory, absolute or relative to the configuration directory, to use for storing certificates and related data.
|
||||
- `ca_endpoint`, string. Default: `https://acme-v02.api.letsencrypt.org/directory`.
|
||||
- `renew_days`, integer. The number of days left on a certificate to renew it. Default: `30`.
|
||||
@@ -119,6 +125,10 @@ The configuration file contains the following sections:
|
||||
- `webroot`, string. Set the absolute path to the webroot folder to use for HTTP based challenges to write directly in a file in `.well-known/acme-challenge`. Setting a `webroot` disables the built-in server (the `port` setting is ignored) and expects the given directory to be publicly served, on port `80`, with access to `.well-known/acme-challenge`. If `webroot` is empty and `port` is `0` the `HTTP-01` challenge is disabled. Default: empty.
|
||||
- `tls_alpn01_challenge`, configuration for `TLS-ALPN-01` challenge type, the following fields are supported:
|
||||
- `port`, integer. This challenge is expected to run on port `443`. `0` means `TLS-ALPN-01` is disabled. Default: `0`.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>SFTP Server</font></summary>
|
||||
|
||||
- **"sftpd"**, the configuration for the SFTP server
|
||||
- `bindings`, list of structs. Each struct has the following fields:
|
||||
- `port`, integer. The port used for serving SFTP requests. 0 means disabled. Default: 2022
|
||||
@@ -128,19 +138,23 @@ The configuration file contains the following sections:
|
||||
- `banner`, string. Identification string used by the server. Leave empty to use the default banner. Default `SFTPGo_<version>`, for example `SSH-2.0-SFTPGo_0.9.5`
|
||||
- `host_keys`, list of strings. It contains the daemon's private host keys. Each host key can be defined as a path relative to the configuration directory or an absolute one. If empty, the daemon will search or try to generate `id_rsa`, `id_ecdsa` and `id_ed25519` keys inside the configuration directory. If you configure absolute paths to files named `id_rsa`, `id_ecdsa` and/or `id_ed25519` then SFTPGo will try to generate these keys using the default settings.
|
||||
- `host_certificates`, list of strings. Public host certificates. Each certificate can be defined as a path relative to the configuration directory or an absolute one. Certificate's public key must match a private host key otherwise it will be silently ignored. Default: empty.
|
||||
- `host_key_algorithms`, list of strings. Public key algorithms that the server will accept for host key authentication. The supported values are: `rsa-sha2-512-cert-v01@openssh.com`, `rsa-sha2-256-cert-v01@openssh.com`, `ssh-rsa-cert-v01@openssh.com`, `ssh-dss-cert-v01@openssh.com`, `ecdsa-sha2-nistp256-cert-v01@openssh.com`, `ecdsa-sha2-nistp384-cert-v01@openssh.com`, `ecdsa-sha2-nistp521-cert-v01@openssh.com`, `ssh-ed25519-cert-v01@openssh.com`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `rsa-sha2-512`, `rsa-sha2-256`, `ssh-rsa`, `ssh-dss`, `ssh-ed25519`. Default values: `rsa-sha2-512-cert-v01@openssh.com`, `rsa-sha2-256-cert-v01@openssh.com`, `ecdsa-sha2-nistp256-cert-v01@openssh.com`, `ecdsa-sha2-nistp384-cert-v01@openssh.com`, `ecdsa-sha2-nistp521-cert-v01@openssh.com`, `ssh-ed25519-cert-v01@openssh.com`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `rsa-sha2-512`, `rsa-sha2-256`, `ssh-ed25519`.
|
||||
- `moduli`, list of strings. Diffie-Hellman moduli files. Each moduli file can be defined as a path relative to the configuration directory or an absolute one. If set, `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1` KEX algorithms will be available, `diffie-hellman-group-exchange-sha256` will be enabled by default if you don't explicitly set KEXs. Default: empty.
|
||||
- `kex_algorithms`, list of strings. Available KEX (Key Exchange) algorithms in preference order. Leave empty to use default values. The supported values are: `curve25519-sha256`, `curve25519-sha256@libssh.org`, `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `diffie-hellman-group14-sha256`, `diffie-hellman-group16-sha512`, `diffie-hellman-group18-sha512`, `diffie-hellman-group14-sha1`, `diffie-hellman-group1-sha1`. Default values: `curve25519-sha256`, `curve25519-sha256@libssh.org`, `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `diffie-hellman-group14-sha256`. SHA512 based KEXs are disabled by default because they are slow. If you set one or more moduli files, `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1` will be available.
|
||||
- `host_key_algorithms`, list of strings. Public key algorithms that the server will accept for host key authentication. The supported values are: `rsa-sha2-512-cert-v01@openssh.com`, `rsa-sha2-256-cert-v01@openssh.com`, `ssh-rsa-cert-v01@openssh.com`, `ssh-dss-cert-v01@openssh.com`, `ecdsa-sha2-nistp256-cert-v01@openssh.com`, `ecdsa-sha2-nistp384-cert-v01@openssh.com`, `ecdsa-sha2-nistp521-cert-v01@openssh.com`, `ssh-ed25519-cert-v01@openssh.com`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `rsa-sha2-512`, `rsa-sha2-256`, `ssh-rsa`, `ssh-dss`, `ssh-ed25519`. Certificate algorithms are listed for backward compatibility purposes only, they are not used. Default values: `rsa-sha2-512`, `rsa-sha2-256`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519`.
|
||||
- `moduli`, list of strings. Diffie-Hellman moduli files. Each moduli file can be defined as a path relative to the configuration directory or an absolute one. If set and valid, `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1` KEX algorithms will be available, `diffie-hellman-group-exchange-sha256` will be enabled by default if you don't explicitly set KEXs. Invalid moduli file will be silently ignored. Default: empty.
|
||||
- `kex_algorithms`, list of strings. Available KEX (Key Exchange) algorithms in preference order. Leave empty to use default values. The supported values are: `curve25519-sha256`, `curve25519-sha256@libssh.org`, `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `diffie-hellman-group14-sha256`, `diffie-hellman-group16-sha512`, `diffie-hellman-group14-sha1`, `diffie-hellman-group1-sha1`. Default values: `curve25519-sha256`, `curve25519-sha256@libssh.org`, `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `diffie-hellman-group14-sha256`. SHA512 based KEXs are disabled by default because they are slow. If you set one or more moduli files, `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1` will be available.
|
||||
- `ciphers`, list of strings. Allowed ciphers in preference order. Leave empty to use default values. The supported values are: `aes128-gcm@openssh.com`, `aes256-gcm@openssh.com`, `chacha20-poly1305@openssh.com`, `aes128-ctr`, `aes192-ctr`, `aes256-ctr`, `aes128-cbc`, `aes192-cbc`, `aes256-cbc`, `3des-cbc`, `arcfour256`, `arcfour128`, `arcfour`. Default values: `aes128-gcm@openssh.com`, `aes256-gcm@openssh.com`, `chacha20-poly1305@openssh.com`, `aes128-ctr`, `aes192-ctr`, `aes256-ctr`. Please note that the ciphers disabled by default are insecure, you should expect that an active attacker can recover plaintext if you enable them.
|
||||
- `macs`, list of strings. Available MAC (message authentication code) algorithms in preference order. Leave empty to use default values. The supported values are: `hmac-sha2-256-etm@openssh.com`, `hmac-sha2-256`, `hmac-sha2-512-etm@openssh.com`, `hmac-sha2-512`, `hmac-sha1`, `hmac-sha1-96`. Default values: `hmac-sha2-256-etm@openssh.com`, `hmac-sha2-256`.
|
||||
- `trusted_user_ca_keys`, list of public keys paths of certificate authorities that are trusted to sign user certificates for authentication. The paths can be absolute or relative to the configuration directory.
|
||||
- `revoked_user_certs_file`, path to a file containing the revoked user certificates. The path can be absolute or relative to the configuration directory. It must contain a JSON list with the public key fingerprints of the revoked certificates. Example content: `["SHA256:bsBRHC/xgiqBJdSuvSTNpJNLTISP/G356jNMCRYC5Es","SHA256:119+8cL/HH+NLMawRsJx6CzPF1I3xC+jpM60bQHXGE8"]`. The revocation list can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. Default: "".
|
||||
- `login_banner_file`, path to the login banner file. The contents of the specified file, if any, are sent to the remote user before authentication is allowed. It can be a path relative to the config dir or an absolute one. Leave empty to disable login banner.
|
||||
- `enabled_ssh_commands`, list of enabled SSH commands. `*` enables all supported commands. More information can be found [here](./ssh-commands.md).
|
||||
- `keyboard_interactive_authentication`, boolean. This setting specifies whether keyboard interactive authentication is allowed. If no keyboard interactive hook or auth plugin is defined the default is to prompt for the user password and then the one time authentication code, if defined. Default: `false`.
|
||||
- `keyboard_interactive_authentication`, boolean. This setting specifies whether keyboard interactive authentication is allowed. If no keyboard interactive hook or auth plugin is defined the default is to prompt for the user password and then the one time authentication code, if defined. Default: `true`.
|
||||
- `keyboard_interactive_auth_hook`, string. Absolute path to an external program or an HTTP URL to invoke for keyboard interactive authentication. See [Keyboard Interactive Authentication](./keyboard-interactive.md) for more details.
|
||||
- `password_authentication`, boolean. Set to false to disable password authentication. This setting will disable multi-step authentication method using public key + password too. It is useful for public key only configurations if you need to manage old clients that will not attempt to authenticate with public keys if the password login method is advertised. Default: `true`.
|
||||
- `folder_prefix`, string. Virtual root folder prefix to include in all file operations (ex: `/files`). The virtual paths used for per-directory permissions, file patterns etc. must not include the folder prefix. The prefix is only applied to SFTP requests (in SFTP server mode), SCP and other SSH commands will be automatically disabled if you configure a prefix. The prefix is ignored while running as OpenSSH's SFTP subsystem. This setting can help some specific migrations from SFTP servers based on OpenSSH and it is not recommended for general usage. Default: blank.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>FTP Server</font></summary>
|
||||
|
||||
- **"ftpd"**, the configuration for the FTP server
|
||||
- `bindings`, list of structs. Each struct has the following fields:
|
||||
- `port`, integer. The port used for serving FTP requests. 0 means disabled. Default: 0.
|
||||
@@ -150,10 +164,11 @@ The configuration file contains the following sections:
|
||||
- `certificate_file`, string. Binding specific TLS certificate. This can be an absolute path or a path relative to the config dir.
|
||||
- `certificate_key_file`, string. Binding specific private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If not set the global ones will be used, if any.
|
||||
- `min_tls_version`, integer. Defines the minimum version of TLS to be enabled. `12` means TLS 1.2 (and therefore TLS 1.2 and TLS 1.3 will be enabled),`13` means TLS 1.3. Default: `12`.
|
||||
- `force_passive_ip`, ip address. External IP address to expose for passive connections. Leave empty to autodetect. If not empty, it must be a valid IPv4 address. Default: "".
|
||||
- `force_passive_ip`, ip address. External IP address for passive connections. Leave empty to autodetect. If not empty, it must be a valid IPv4 address. Default: "".
|
||||
- `passive_ip_overrides`, list of struct that allows to return a different passive ip based on the client IP address. Each struct has the following fields:
|
||||
- `networks`, list of strings. Each string must define a network in CIDR notation, for example 192.168.1.0/24.
|
||||
- `ip`, string. Passive IP to return if the client IP address belongs to the defined networks. Empty means autodetect.
|
||||
- `passive_host`, string. Hostname for passive connections. This hostname will be resolved each time a passive connection is requested and this can, depending on the DNS configuration, take a noticeable amount of time. Enable this setting only if you have a dynamic IP address. Default: "".
|
||||
- `client_auth_type`, integer. Set to `1` to require a client certificate and verify it. Set to `2` to request a client certificate during the TLS handshake and verify it if given, in this mode the client is allowed not to send a certificate. At least one certification authority must be defined in order to verify client certificates. If no certification authority is defined, this setting is ignored. Default: 0.
|
||||
- `tls_cipher_suites`, list of strings. List of supported cipher suites for TLS version 1.2. If empty, a default list of secure cipher suites is used, with a preference order based on hardware performance. Note that TLS 1.3 ciphersuites are not configurable. The supported ciphersuites names are defined [here](https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52). Any invalid name will be silently ignored. The order matters, the ciphers listed first will be the preferred ones. Default: empty.
|
||||
- `passive_connections_security`, integer. Defines the security checks for passive data connections. Set to `0` to require matching peer IP addresses of control and data connection. Set to `1` to disable any checks. Please note that if you run the FTP service behind a proxy you must enable the proxy protocol for control and data connections. Default: `0`.
|
||||
@@ -168,9 +183,13 @@ The configuration file contains the following sections:
|
||||
- `hash_support`, integer. Set to `1` to enable FTP commands that allow to calculate the hash value of files. These FTP commands will be enabled: `HASH`, `XCRC`, `MD5/XMD5`, `XSHA/XSHA1`, `XSHA256`, `XSHA512`. Please keep in mind that to calculate the hash we need to read the whole file, for remote backends this means downloading the file, for the encrypted backend this means decrypting the file. Default `0`.
|
||||
- `combine_support`, integer. Set to 1 to enable support for the non standard `COMB` FTP command. Combine is only supported for local filesystem, for cloud backends it has no advantage as it will download the partial files and will upload the combined one. Cloud backends natively support multipart uploads. Default `0`.
|
||||
- `certificate_file`, string. Certificate for FTPS. This can be an absolute path or a path relative to the config dir.
|
||||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. A certificate and the private key are required to enable explicit and implicit TLS. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. A certificate and the private key are required to enable explicit and implicit TLS. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. The certificates are also polled for changes every 8 hours.
|
||||
- `ca_certificates`, list of strings. Set of root certificate authorities to be used to verify client certificates.
|
||||
- `ca_revocation_lists`, list of strings. Set a revocation lists, one for each root CA, to be used to check if a client certificate has been revoked. The revocation lists can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>WebDAV Server</font></summary>
|
||||
|
||||
- **"webdavd"**, the configuration for the WebDAV server, more info [here](./webdav.md)
|
||||
- `bindings`, list of structs. Each struct has the following fields:
|
||||
- `port`, integer. The port used for serving WebDAV requests. 0 means disabled. Default: 0.
|
||||
@@ -189,7 +208,7 @@ The configuration file contains the following sections:
|
||||
- `certificate_file`, string. Certificate for WebDAV over HTTPS. This can be an absolute path or a path relative to the config dir.
|
||||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. A certificate and a private key are required to enable HTTPS connections. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
- `ca_certificates`, list of strings. Set of root certificate authorities to be used to verify client certificates.
|
||||
- `ca_revocation_lists`, list of strings. Set a revocation lists, one for each root CA, to be used to check if a client certificate has been revoked. The revocation lists can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
- `ca_revocation_lists`, list of strings. Set a revocation lists, one for each root CA, to be used to check if a client certificate has been revoked. The revocation lists can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. The certificates are also polled for changes every 8 hours.
|
||||
- `cors` struct containing CORS configuration. SFTPGo uses [Go CORS handler](https://github.com/rs/cors), please refer to upstream documentation for fields meaning and their default values.
|
||||
- `enabled`, boolean, set to true to enable CORS.
|
||||
- `allowed_origins`, list of strings.
|
||||
@@ -201,10 +220,20 @@ The configuration file contains the following sections:
|
||||
- `options_passthrough`, boolean.
|
||||
- `options_success_status`, integer.
|
||||
- `allow_private_network`, boolean.
|
||||
- `cache` struct containing cache configuration for the authenticated users.
|
||||
- `enabled`, boolean, set to true to enable user caching. Default: true.
|
||||
- `expiration_time`, integer. Expiration time, in minutes, for the cached users. 0 means unlimited. Default: 0.
|
||||
- `max_size`, integer. Maximum number of users to cache. 0 means unlimited. Default: 50.
|
||||
- `cache` struct containing cache configurations.
|
||||
- `users`, cache configuration for the authenticated users.
|
||||
- `expiration_time`, integer. Expiration time, in minutes, for the cached users. 0 means unlimited. Default: 0.
|
||||
- `max_size`, integer. Maximum number of users to cache. 0 means unlimited. Default: 50.
|
||||
- `mime_types`, cache configuration for mime types.
|
||||
- `enabled`, boolean, set to true to enable mime types caching. Default: `true`.
|
||||
- `max_size`, integer. Maximum number of mime types to cache. 0 means no cache. Default: 1000.
|
||||
- `custom_mappings`, additional mime types mapping. This is a platform independet way to add few additional mappings. You can set a limited number of mappings here, if you want to add a large list use the method provided by the OS of your choice. List of struct, each struct has the following fields:
|
||||
- `ext`, string, file extension including the dot, for example `.json`
|
||||
- `mime`, string, mime type, for example `application/json`
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>Data Provider</font></summary>
|
||||
|
||||
- **"data_provider"**, the configuration for the data provider
|
||||
- `driver`, string. Supported drivers are `sqlite`, `mysql`, `postgresql`, `cockroachdb`, `bolt`, `memory`
|
||||
- `name`, string. Database name. For driver `sqlite` this can be the database name relative to the config dir or the absolute path to the SQLite database. For driver `memory` this is the (optional) path relative to the config dir or the absolute path to the provider dump, obtained using the `dumpdata` REST API, to load. This dump will be loaded at startup and can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. The `memory` provider will not modify the provided file so quota usage and last login will not be persisted. If you plan to use a SQLite database over a `cifs` network share (this is not recommended in general) you must use the `nobrl` mount option otherwise you will get the `database is locked` error. Some users reported that the `bolt` provider works fine over `cifs` shares.
|
||||
@@ -215,7 +244,7 @@ The configuration file contains the following sections:
|
||||
- `sslmode`, integer. Used for drivers `mysql` and `postgresql`. 0 disable TLS connections, 1 require TLS, 2 set TLS mode to `verify-ca` for driver `postgresql` and `skip-verify` for driver `mysql`, 3 set TLS mode to `verify-full` for driver `postgresql` and `preferred` for driver `mysql`
|
||||
- `root_cert`, string. Path to the root certificate authority used to verify that the server certificate was signed by a trusted CA
|
||||
- `disable_sni`, boolean. Allows to opt out Server Name Indication (SNI) for TLS connections. Default: `false`
|
||||
- `target_session_attrs`, string. This is a `postgresql` and `cockroachdb` specific option. It determines whether the session must have certain properties to be acceptable. It's typically used in combination with multiple host names to select the first acceptable alternative among several hosts. Supported values: `any`, `read-write`, `read-only`, `primary`, `standby`, `prefer-standby`. If empty, `any` is assumed.
|
||||
- `target_session_attrs`, string. This is a `postgresql` and `cockroachdb` specific option. It determines whether the session must have certain properties to be acceptable. It's typically used in combination with multiple host names to select the first acceptable alternative among several hosts. Supported values: `any`, `read-write`, `read-only`, `primary`, `standby`, `prefer-standby`. If empty, `any` is assumed. If you explicitly set `any` the connections will be randomly distributed among the specified hosts
|
||||
- `client_cert`, string. Path to the client certificate for two-way TLS authentication
|
||||
- `client_key`,string. Path to the client key for two-way TLS authentication
|
||||
- `connection_string`, string. Provide a custom database connection string. If not empty, this connection string will be used instead of building one using the previous parameters. Leave empty for drivers `bolt` and `memory`
|
||||
@@ -251,18 +280,22 @@ The configuration file contains the following sections:
|
||||
- `admins`, struct. It defines the password validation rules for SFTPGo admins.
|
||||
- `min_entropy`, float. Defines the minimum password entropy. Take a looke [here](https://github.com/wagslane/go-password-validator#what-entropy-value-should-i-use) for more details. `0` means disabled, any password will be accepted. Default: `0`.
|
||||
- `users`, struct. It defines the password validation rules for SFTPGo protocol users.
|
||||
- `min_entropy`, float. Default: `0`.
|
||||
- `min_entropy`, float. This value is used as fallback if no more specific password strength is set at user/group level. Default: `0`.
|
||||
- `password_caching`, boolean. Verifying argon2id passwords has a high memory and computational cost, verifying bcrypt passwords has a high computational cost, by enabling, in memory, password caching you reduce these costs. Default: `true`
|
||||
- `update_mode`, integer. Defines how the database will be initialized/updated. 0 means automatically. 1 means manually using the initprovider sub-command.
|
||||
- `create_default_admin`, boolean. Before you can use SFTPGo you need to create an admin account. If you open the admin web UI, a setup screen will guide you in creating the first admin account. You can automatically create the first admin account by enabling this setting and setting the environment variables `SFTPGO_DEFAULT_ADMIN_USERNAME` and `SFTPGO_DEFAULT_ADMIN_PASSWORD`. You can also create the first admin by loading initial data. This setting has no effect if an admin account is already found within the data provider. Default `false`.
|
||||
- `naming_rules`, integer. Naming rules for usernames, folder and group names. `0` means no rules. `1` means you can use any UTF-8 character. The names are used in URIs for REST API and Web admin. If not set only unreserved URI characters are allowed: ALPHA / DIGIT / "-" / "." / "_" / "~". `2` means names are converted to lowercase before saving/matching and so case insensitive matching is possible. `3` means trimming trailing and leading white spaces before saving/matching. Rules can be combined, for example `3` means both converting to lowercase and allowing any UTF-8 character. Enabling these options for existing installations could be backward incompatible, some users could be unable to login, for example existing users with mixed cases in their usernames. You have to ensure that all existing users respect the defined rules. Default: `1`.
|
||||
- `naming_rules`, integer. Naming rules for usernames, folder, group, role and object names in general. `0` means no rules. `1` means you can use any UTF-8 character. The names are used in URIs for REST API and Web admin. If not set only unreserved URI characters are allowed: ALPHA / DIGIT / "-" / "." / "_" / "~". `2` means names are converted to lowercase before saving/matching and so case insensitive matching is possible. `4` means trimming trailing and leading white spaces before saving/matching, the WebAdmin needs this setting to work properly. Rules can be combined, for example `3` means both converting to lowercase and allowing any UTF-8 character. Enabling these options for existing installations could be backward incompatible, some users could be unable to login, for example existing users with mixed cases in their usernames. You have to ensure that all existing users respect the defined rules. Default: `5`.
|
||||
- `is_shared`, integer. If the data provider is shared across multiple SFTPGo instances, set this parameter to `1`. `MySQL`, `PostgreSQL` and `CockroachDB` can be shared, this setting is ignored for other data providers. For shared data providers, active transfers are persisted in the database and thus quota checks between ongoing transfers will work cross multiple instances. Password reset requests and OIDC tokens/states are also persisted in the database if the provider is shared. For shared data providers, scheduled event actions are only executed on a single SFTPGo instance by default, you can override this behavior on a per-action basis. The database table `shared_sessions` is used only to store temporary sessions. In performance critical installations, you might consider using a database-specific optimization, for example you might use an `UNLOGGED` table for PostgreSQL. This optimization in only required in very limited use cases. Default: `0`.
|
||||
- `node`, struct. Node-specific configurations to allow inter-node communications. If your provider is shared across multiple nodes, the nodes can exchange information to present a uniform view for node-specific data. The current implementation allows to obtain active connections from all nodes. Nodes connect to each other using the REST API.
|
||||
- `host`, string. IP address or hostname that other nodes can use to connect to this node via REST API. Empty means inter-node communications disabled. Default: empty.
|
||||
- `port`, integer. The port that other nodes can use to connect to this node via REST API. Default: `0`
|
||||
- `proto`, string. Supported values `http` or `https`. For `https` the configurations for http clients is used, so you can, for example, enable mutual TLS authentication. Default: `http`
|
||||
- `backups_path`, string. Path to the backup directory. This can be an absolute path or a path relative to the config dir. We don't allow backups in arbitrary paths for security reasons.
|
||||
- **"httpd"**, the configuration for the HTTP server used to serve REST API and to expose the built-in web interface
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>HTTP Server</font></summary>
|
||||
|
||||
- **"httpd"**, the configuration for the HTTP server used to serve REST API and the built-in web interfaces
|
||||
- `bindings`, list of structs. Each struct has the following fields:
|
||||
- `port`, integer. The port used for serving HTTP requests. Default: 8080.
|
||||
- `address`, string. Leave blank to listen on all available network interfaces. On *NIX you can specify an absolute path to listen on a Unix-domain socket Default: blank.
|
||||
@@ -301,7 +334,7 @@ The configuration file contains the following sections:
|
||||
- `allowed_hosts`, list of strings. Fully qualified domain names that are allowed. An empty list allows any and all host names. Default: empty.
|
||||
- `allowed_hosts_are_regex`, boolean. Determines if the provided allowed hosts contains valid regular expressions. Default: `false`.
|
||||
- `hosts_proxy_headers`, list of string. Defines a set of header keys that may hold a proxied hostname value for the request, for example `X-Forwarded-Host`. Default: empty.
|
||||
- `https_redirect`, boolean. Set to `true` to redirect HTTP requests to HTTPS. Default: `false`.
|
||||
- `https_redirect`, boolean. Set to `true` to redirect HTTP requests to HTTPS. If you redirect from port `80` and you get your TLS certificates using the built-in ACME protocol and the `HTTP-01` challenge type, you need to use the webroot method and set the ACME web root to a path writable by SFTPGo in order to renew your certificates. Default: `false`.
|
||||
- `https_host`, string. Defines the host name that is used to redirect HTTP requests to HTTPS. Default is blank, which indicates to use the same host. For example, if `https_redirect` is enabled and `https_host` is blank, a request for `http://127.0.0.1/web/client/login` will be redirected to `https://127.0.0.1/web/client/login`, if `https_host` is set to `www.example.com` the same request will be redirected to `https://www.example.com/web/client/login`.
|
||||
- `https_proxy_headers`, list of struct, each struct contains the fields `key` and `value`. Defines a a list of header keys with associated values that would indicate a valid https request. For example `key` could be `X-Forwarded-Proto` and `value` `https`. Default: empty.
|
||||
- `sts_seconds`, integer. Defines the max-age of the `Strict-Transport-Security` header. This header will be included for `https` responses or for HTTP request if the request includes a defined HTTPS proxy header. Default: `0`, which would NOT include the header.
|
||||
@@ -327,12 +360,12 @@ The configuration file contains the following sections:
|
||||
- `openapi_path`, string. Path to the directory that contains the OpenAPI schema and the default renderer. This can be an absolute path or a path relative to the config dir. If empty the OpenAPI schema and the renderer will not be served regardless of the `render_openapi` directive
|
||||
- `web_root`, string. Defines a base URL for the web admin and client interfaces. If empty web admin and client resources will be available at the root ("/") URI. If defined it must be an absolute URI or it will be ignored
|
||||
- `certificate_file`, string. Certificate for HTTPS. This can be an absolute path or a path relative to the config dir.
|
||||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If both the certificate and the private key are provided, you can enable HTTPS for the configured bindings. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If both the certificate and the private key are provided, you can enable HTTPS for the configured bindings. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. The certificates are also polled for changes every 8 hours.
|
||||
- `ca_certificates`, list of strings. Set of root certificate authorities to be used to verify client certificates.
|
||||
- `ca_revocation_lists`, list of strings. Set a revocation lists, one for each root CA, to be used to check if a client certificate has been revoked. The revocation lists can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
- `signing_passphrase`, string. Passphrase to use to derive the signing key for JWT and CSRF tokens. If empty a random signing key will be generated each time SFTPGo starts. If you set a signing passphrase you should consider rotating it periodically for added security.
|
||||
- `token_validation`, integer. Define how to validate JWT tokens, cookies and CSRF tokens. By default all the available security checks are enabled. Set to 1 to disable the requirement that a token must be used by the same IP for which it was issued. Default: `0`.
|
||||
- `max_upload_file_size`, integer. Defines the maximum request body size, in bytes, for Web Client/API HTTP upload requests. 0 means no limit. Default: 1048576000.
|
||||
- `max_upload_file_size`, integer. Defines the maximum request body size, in bytes, for Web Client/API HTTP upload requests. `0` means no limit. Default: `0`.
|
||||
- `cors` struct containing CORS configuration. SFTPGo uses [Go CORS handler](https://github.com/rs/cors), please refer to upstream documentation for fields meaning and their default values.
|
||||
- `enabled`, boolean, set to `true` to enable CORS.
|
||||
- `allowed_origins`, list of strings.
|
||||
@@ -348,6 +381,10 @@ The configuration file contains the following sections:
|
||||
- `installation_code`, string. If set, this installation code will be required when creating the first admin account. Please note that even if set using an environment variable this field is read at SFTPGo startup and not at runtime. This is not a license key or similar, the purpose here is to prevent anyone who can access to the initial setup screen from creating an admin user. Default: blank.
|
||||
- `installation_code_hint`, string. Description for the installation code input field. Default: `Installation code`.
|
||||
- `hide_support_link`, boolean. If set, the link to the [sponsors section](../README.md#sponsors) will not appear on the setup screen page. Default: `false`.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>Telemetry</font></summary>
|
||||
|
||||
- **"telemetry"**, the configuration for the telemetry server, more details [below](#telemetry-server)
|
||||
- `bind_port`, integer. The port used for serving HTTP requests. Set to 0 to disable HTTP server. Default: 0
|
||||
- `bind_address`, string. Leave blank to listen on all available network interfaces. On \*NIX you can specify an absolute path to listen on a Unix-domain socket. Default: `127.0.0.1`
|
||||
@@ -357,6 +394,10 @@ The configuration file contains the following sections:
|
||||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If both the certificate and the private key are provided, the server will expect HTTPS connections. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
|
||||
- `min_tls_version`, integer. Defines the minimum version of TLS to be enabled. `12` means TLS 1.2 (and therefore TLS 1.2 and TLS 1.3 will be enabled),`13` means TLS 1.3. Default: `12`.
|
||||
- `tls_cipher_suites`, list of strings. List of supported cipher suites for TLS version 1.2. If empty, a default list of secure cipher suites is used, with a preference order based on hardware performance. Note that TLS 1.3 ciphersuites are not configurable. The supported ciphersuites names are defined [here](https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52). Any invalid name will be silently ignored. The order matters, the ciphers listed first will be the preferred ones. Default: empty.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>HTTP clients</font></summary>
|
||||
|
||||
- **"http"**, the configuration for HTTP clients. HTTP clients are used for executing hooks. Some hooks use a retryable HTTP client, for these hooks you can configure the time between retries and the number of retries. Please check the hook specific documentation to understand which hooks use a retryable HTTP client.
|
||||
- `timeout`, float. Timeout specifies a time limit, in seconds, for requests. For requests with retries this is the timeout for a single request
|
||||
- `retry_wait_min`, integer. Defines the minimum waiting time between attempts in seconds.
|
||||
@@ -371,6 +412,10 @@ The configuration file contains the following sections:
|
||||
- `key`, string
|
||||
- `value`, string. The header is silently ignored if `key` or `value` are empty
|
||||
- `url`, string, optional. If not empty, the header will be added only if the request URL starts with the one specified here
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>Commands</font></summary>
|
||||
|
||||
- **command**, configuration for external commands such as program based hooks
|
||||
- `timeout`, integer. Timeout specifies a time limit, in seconds, to execute external commands. Valid range: `1-300`. Default: `30`
|
||||
- `env`, list of strings. Environment variables to pass to all the external commands. Global environment variables are cleared, for security reasons, you have to explicitly set any environment variable such as `PATH` etc. if you need them. Each entry is of the form `key=value`. Do not use environment variables prefixed with `SFTPGO_` to avoid conflicts with environment variables that SFTPGo hooks can set. Default: empty
|
||||
@@ -380,32 +425,56 @@ The configuration file contains the following sections:
|
||||
- `env`, list of strings. These values are added to the environment variables defined for all commands, if any. Default: empty
|
||||
- `args`, list of strings. Arguments to pass to the command identified by `path`. Default: empty
|
||||
- `hook`, string. If not empty this configuration only apply to the specified hook name. Supported hook names: `fs_actions`, `provider_actions`, `startup`, `post_connect`, `post_disconnect`, `data_retention`, `check_password`, `pre_login`, `post_login`, `external_auth`, `keyboard_interactive`. Default: empty
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>KMS</font></summary>
|
||||
|
||||
- **kms**, configuration for the Key Management Service, more details can be found [here](./kms.md)
|
||||
- `secrets`
|
||||
- `url`, string. Defines the URI to the KMS service. Default: blank.
|
||||
- `master_key`, string. Defines the master encryption key as string. If not empty, it takes precedence over `master_key_path`. Default: blank.
|
||||
- `master_key_path`, string. Defines the absolute path to a file containing the master encryption key. Default: blank.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>MFA</font></summary>
|
||||
|
||||
- **mfa**, multi-factor authentication settings
|
||||
- `totp`, list of struct that define settings for time-based one time passwords (RFC 6238). Each struct has the following fields:
|
||||
- `name`, string. Unique configuration name. This name should not be changed if there are users or admins using the configuration. The name is not exposed to the authentication apps. Default: `Default`.
|
||||
- `name`, string. Unique configuration name. This name should not be changed if there are users or admins using the configuration. The name is not visible to the authentication apps. Default: `Default`.
|
||||
- `issuer`, string. Name of the issuing Organization/Company. Default: `SFTPGo`.
|
||||
- `algo`, string. Algorithm to use for HMAC. The supported algorithms are: `sha1`, `sha256`, `sha512`. Currently Google Authenticator app on iPhone seems to only support `sha1`, please check the compatibility with your target apps/device before setting a different algorithm. You can also define multiple configurations, for example one that uses `sha256` or `sha512` and another one that uses `sha1` and instruct your users to use the appropriate configuration for their devices/apps. The algorithm should not be changed if there are users or admins using the configuration. Default: `sha1`.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>SMTP</font></summary>
|
||||
|
||||
- **smtp**, SMTP configuration enables SFTPGo email sending capabilities
|
||||
- `host`, string. Location of SMTP email server. Leave empty to disable email sending capabilities. Default: blank.
|
||||
- `port`, integer. Port of SMTP email server.
|
||||
- `from`, string. From address, for example `SFTPGo <sftpgo@example.com>`. Many SMTP servers reject emails without a `From` header so, if not set, SFTPGo will try to use the username as fallback, this may or may not be appropriate. Default: blank
|
||||
- `user`, string. SMTP username. Default: blank
|
||||
- `password`, string. SMTP password. Leaving both username and password empty the SMTP authentication will be disabled. Default: blank
|
||||
- `auth_type`, integer. 0 means `Plain`, 1 means `Login`, 2 means `CRAM-MD5`. Default: `0`.
|
||||
- `auth_type`, integer. 0 means `Plain`, 1 means `Login`, 2 means `CRAM-MD5`, 3 means `XOAUTH2`. Default: `0`.
|
||||
- `encryption`, integer. 0 means no encryption, 1 means `TLS`, 2 means `STARTTLS`. Default: `0`.
|
||||
- `domain`, string. Domain to use for `HELO` command, if empty `localhost` will be used. Default: blank.
|
||||
- `templates_path`, string. Path to the email templates. This can be an absolute path or a path relative to the config dir. Templates are searched within a subdirectory named "email" in the specified path. You can customize the email templates by simply specifying an alternate path and putting your custom templates there.
|
||||
- **plugins**, list of external plugins. Each plugin is configured using a struct with the following fields:
|
||||
- `debug`, integer. Set to `1` to enable SMTP debug. Default: `0`.
|
||||
- `oauth2`, struct containing OAuth2 related configurations:
|
||||
- `provider`, integer, 0 means `Google`, 1 means `Microsoft`. Default: `0`.
|
||||
- `tenant`, string. Azure Active Directory tenant for the Microsoft provider. Typical values are `common`, `organizations`, `consumers` or tenant identifier. If empty `common` is used. Default: blank.
|
||||
- `client_id`, string. Default: blank.
|
||||
- `client_secret`, string. Default: blank.
|
||||
- `refresh_token`, string. Default: blank.
|
||||
|
||||
</details>
|
||||
<details><summary><font size=4>Plugins</font></summary>
|
||||
|
||||
- **plugins**, list of external plugins. :warning: Please note that the plugin system is experimental, the configuration parameters and interfaces may change in a backward incompatible way in future. Each plugin is configured using a struct with the following fields:
|
||||
- `type`, string. Defines the plugin type. Supported types: `notifier`, `kms`, `auth`, `metadata`.
|
||||
- `notifier_options`, struct. Defines the options for notifier plugins.
|
||||
- `fs_events`, list of strings. Defines the filesystem events that will be notified to this plugin.
|
||||
- `provider_events`, list of strings. Defines the provider events that will be notified to this plugin.
|
||||
- `provider_objects`, list if strings. Defines the provider objects that will be notified to this plugin.
|
||||
- `log_events`, list of integers. Defines the log events that will be notified to this plugin. `1` means "Login failed", `2` means "Login with non-existent user", `3` means "No login tried", `4` means "Algorithm negotiation failed".
|
||||
- `retry_max_time`, integer. Defines the maximum number of seconds an event can be late. SFTPGo adds a timestamp to each event and add to an internal queue any events that a the plugin fails to handle (the plugin returns an error or it is not running). If a plugin fails to handle an event that is too late, based on this configuration, it will be discarded. SFTPGo will try to resend queued events every 30 seconds. 0 means no retry.
|
||||
- `retry_queue_max_size`, integer. Defines the maximum number of events that the internal queue can hold. Once the queue is full, the events that cannot be sent to the plugin will be discarded. 0 means no limit.
|
||||
- `kms_options`, struct. Defines the options for kms plugins.
|
||||
@@ -418,7 +487,7 @@ The configuration file contains the following sections:
|
||||
- `sha256sum`, string. SHA256 checksum for the plugin executable. If not empty it will be used to verify the integrity of the executable.
|
||||
- `auto_mtls`, boolean. If enabled the client and the server automatically negotiate mutual TLS for transport authentication. This ensures that only the original client will be allowed to connect to the server, and all other connections will be rejected. The client will also refuse to connect to any server that isn't the original instance started by the client.
|
||||
|
||||
:warning: Please note that the plugin system is experimental, the exposed configuration parameters and interfaces may change in a backward incompatible way in future.
|
||||
</details>
|
||||
|
||||
A full example showing the default config (in JSON format) can be found [here](../sftpgo.json).
|
||||
|
||||
@@ -516,7 +585,9 @@ Supported hash algorithms:
|
||||
- MD5 crypt APR1, prefix `$apr1$`
|
||||
- SHA256 crypt, prefix `$5$`
|
||||
- SHA512 crypt, prefix `$6$`
|
||||
- LDAP MD5, prefix `{MD5}`
|
||||
- MD5 digest, prefix `{MD5}`
|
||||
- SHA256 digest, prefix `{SHA256}`
|
||||
- SHA512 digest, prefix `{SHA512}`
|
||||
|
||||
If you set a password with one of these prefixes it will not be hashed.
|
||||
When users log in, if their passwords are stored with anything other than the preferred algorithm, SFTPGo will automatically upgrade the algorithm to the preferred one.
|
||||
@@ -525,7 +596,7 @@ When users log in, if their passwords are stored with anything other than the pr
|
||||
|
||||
## Telemetry Server
|
||||
|
||||
The telemetry server exposes the following endpoints:
|
||||
The telemetry server publishes the following endpoints:
|
||||
|
||||
- `/healthz`, health information (for health checks)
|
||||
- `/metrics`, Prometheus metrics
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
Using groups simplifies the administration of multiple accounts by letting you assign settings once to a group, instead of multiple times to each individual user.
|
||||
|
||||
SFTPGo supports two types of groups:
|
||||
SFTPGo supports the following types of groups:
|
||||
|
||||
- primary groups
|
||||
- secondary groups
|
||||
- membership groups
|
||||
|
||||
A user can be a member of a primary group and many secondary and membership groups. Depending on the group type, the settings are inherited differently.
|
||||
|
||||
@@ -15,7 +16,8 @@ The following settings are inherited from the primary group:
|
||||
|
||||
- home dir, if set for the group will replace the one defined for the user. The `%username%` placeholder is replaced with the username
|
||||
- filesystem config, if the provider set for the group is different from the "local provider" will replace the one defined for the user. The `%username%` placeholder is replaced with the username within the defined "prefix", for any vfs, and the "username" for the SFTP filesystem config
|
||||
- max sessions, quota size/files, upload/download bandwidth, upload/download/total data transfer, max upload size, external auth cache time, ftp_security, default share expiration: if they are set to `0` for the user they are replaced with the value set for the group, if different from `0`
|
||||
- max sessions, quota size/files, upload/download bandwidth, upload/download/total data transfer, max upload size, external auth cache time, ftp_security, default share expiration, password expiration, password strength: if they are set to `0` for the user they are replaced with the value set for the group, if different from `0`. The password strength defined at group level is only enforce when users change their password
|
||||
- expires_in, if defined and the user does not have an expiration date set, defines the expiration of the account in number of days from the creation date
|
||||
- TLS username, check password hook disabled, pre-login hook disabled, external auth hook disabled, filesystem checks disabled, allow API key authentication, anonymous user: if they are not set for the user they are replaced with the value set for the group
|
||||
- starting directory, if the user does not have a starting directory set, the value set for the group is used, if any. The `%username%` placeholder is replaced with the username
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ Let's see some common use cases.
|
||||
- [Daily backups](#daily-backups)
|
||||
- [Automatically create a folder structure](#automatically-create-a-folder-structure)
|
||||
- [Upload notifications](#upload-notifications)
|
||||
- [Recycle Bin](#recycle-bin)
|
||||
|
||||
## Preliminary Note
|
||||
|
||||
@@ -81,7 +82,7 @@ Done! Create a new user and check that the defined directories are automatically
|
||||
Let's see how you can receive an email notification after each upload and, optionally, the uploaded file as well.
|
||||
|
||||
From the WebAdmin expand the `Event Manager` section, select `Event actions` and add a new action.
|
||||
Create an action named `upload notification`, with the settings you can see in the following screen.
|
||||
Create an action named `upload notification` with the settings you can see in the following screen.
|
||||
|
||||

|
||||
|
||||
@@ -94,3 +95,36 @@ You can also filters events based on protocol, user and group name, filepath she
|
||||
|
||||
As actions, select `upload notification`.
|
||||
Done! Try uploading a new file and you will receive the configured email notification.
|
||||
|
||||
## Recycle Bin
|
||||
|
||||
Let's see how we can configure a Recycle Bin style function where files are not deleted strait away, but instead moved to a separate folder.
|
||||
|
||||
To easily apply the Recycle Bin to multiple users we will create a virtual folder and a group, this way all users who belong to the group will have a Recycle Bin.
|
||||
|
||||
Create a virtual folder named `recycle` with the settings you can see in the following screen.
|
||||
|
||||

|
||||
|
||||
Create a group named `recycle` with the settings you can see in the following screen.
|
||||
|
||||

|
||||
|
||||
Make your users members of the `recycle` group.
|
||||
|
||||
From the WebAdmin expand the `Event Manager` section, select `Event actions` and add a new action.
|
||||
Create an action named `move to recycle` with the settings you can see in the following screen.
|
||||
|
||||

|
||||
|
||||
Now select `Event rules` and create a rule named `Recycle rule`, select `Filesystem events` as trigger, `pre-delete` as filesystem event and exclude the `/recycle` path.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
As actions, select `move to recycle` and set `Execute sync`.
|
||||
|
||||
Done! Try deleting a file, it will be moved to the Recycle Bin.
|
||||
|
||||
You can also add a scheduled event rule to automatically delete files older than a configurable time from the `/recycle` folder.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
SFTPGo allows to securely share your files over SFTP and optionally FTP/S and WebDAV too.
|
||||
Several storage backends are supported and they are configurable per user, so you can serve a local directory for a user and an S3 bucket (or part of it) for another one.
|
||||
SFTPGo also supports virtual folders, a virtual folder can use any of the supported storage backends. So you can have, for example, an S3 user that exposes a GCS bucket (or part of it) on a specified path and an encrypted local filesystem on another one.
|
||||
SFTPGo also supports virtual folders, a virtual folder can use any of the supported storage backends. So you can have, for example, a user with the S3 backend mapping a GCS bucket (or part of it) on a specified path and an encrypted local filesystem on another one.
|
||||
Virtual folders can be private or shared among multiple users, for shared virtual folders you can define different quota limits for each user.
|
||||
|
||||
In this tutorial we explore the main features and concepts using the built-in web admin interface. Advanced users can also use the SFTPGo [REST API](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml)
|
||||
@@ -11,7 +11,7 @@ In this tutorial we explore the main features and concepts using the built-in we
|
||||
- [Initial configuration](#initial-configuration)
|
||||
- [Creating users](#creating-users)
|
||||
- [Creating users with a Cloud Storage backend](#creating-users-with-a-cloud-storage-backend)
|
||||
- [Creating users with a local encrypted backend (Data At Rest Encryption)](#creating-users-with-a-local-encrypted-backend-data-at-rest-Encryption)
|
||||
- [Creating users with a local encrypted backend (Data At Rest Encryption)](#creating-users-with-a-local-encrypted-backend-data-at-rest-encryption)
|
||||
- [Virtual permissions](#virtual-permissions)
|
||||
- [Virtual folders](#virtual-folders)
|
||||
- [Groups](#groups)
|
||||
@@ -26,9 +26,7 @@ In this tutorial we explore the main features and concepts using the built-in we
|
||||
|
||||
## Installation
|
||||
|
||||
You can easily install SFTPGo by downloading the appropriate package for your operating system and architecture. Please visit the [releases](https://github.com/drakkan/sftpgo/releases "releases") page.
|
||||
|
||||
An official Docker image is available. Documentation is [here](./../../docker/README.md).
|
||||
You can easily install SFTPGo in several ways, more details [here](../../README.md#installation).
|
||||
|
||||
In this guide, we assume that SFTPGo is already installed and running using the default configuration.
|
||||
|
||||
@@ -202,11 +200,11 @@ Suppose we created two virtual folders name `localfolder` and `minio` as you can
|
||||
|
||||
Now, click `Users`, on the left menu, select a user and click the `Edit` icon, to update the user and associate the virtual folders.
|
||||
|
||||
Virtual folders must be referenced using their unique name and you can expose them on a configurable virtual path. Take a look at the following screenshot.
|
||||
Virtual folders must be referenced using their unique name and you can map them on a configurable virtual path. Take a look at the following screenshot.
|
||||
|
||||

|
||||
|
||||
We exposed the folder named `localfolder` on the path `/vdirlocal` (this must be an absolute UNIX path on Windows too) and the folder named `minio` on the path `/vdirminio`. For `localfolder` the quota usage is included within the user quota, while for the `minio` folder we defined separate quota limits: at most 2 files and at most 100MB, whichever is reached first.
|
||||
We mapped the folder named `localfolder` on the path `/vdirlocal` (this must be an absolute UNIX path on Windows too) and the folder named `minio` on the path `/vdirminio`. For `localfolder` the quota usage is included within the user quota, while for the `minio` folder we defined separate quota limits: at most 2 files and at most 100MB, whichever is reached first.
|
||||
|
||||
The folder `minio` can be shared with other users and we can define different quota limits on a per-user basis. The folder `localfolder` is considered private since we have included its quota limits within those of the user, if we share them with other users we will break quota calculation.
|
||||
|
||||
@@ -621,7 +619,7 @@ Restart SFTPGo to apply the changes. The FTP service is now available on port `2
|
||||
|
||||
You can also configure the passive ports range (`50000-50100` by default), these ports must be reachable for passive FTP to work. If your FTP server is on the private network side of a NAT configuration you have to set `force_passive_ip` to your external IP address. You may also need to open the passive port range on your firewall.
|
||||
|
||||
It is recommended that you provide a certificate and key file to expose FTP over TLS. You should prefer SFTP to FTP even if you configure TLS, please don't blindly enable the old FTP protocol.
|
||||
It is recommended that you provide a certificate and key file to allow FTP over TLS. You should prefer SFTP to FTP even if you configure TLS, please don't blindly enable the old FTP protocol.
|
||||
|
||||
### Enable WebDAV service
|
||||
|
||||
@@ -656,4 +654,4 @@ Alternatively (recommended), you can use environment variables by creating the f
|
||||
SFTPGO_WEBDAVD__BINDINGS__0__PORT=10080
|
||||
```
|
||||
|
||||
Restart SFTPGo to apply the changes. The WebDAV service is now available on port `10080`. It is recommended that you provide a certificate and key file to expose WebDAV over https.
|
||||
Restart SFTPGo to apply the changes. The WebDAV service is now available on port `10080`. It is recommended that you provide a certificate and key file to allow WebDAV over https.
|
||||
|
||||
BIN
docs/howto/img/recycle-folder.png
Normal file
BIN
docs/howto/img/recycle-folder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/howto/img/recycle-group.png
Normal file
BIN
docs/howto/img/recycle-group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/howto/img/recycle-move-action.png
Normal file
BIN
docs/howto/img/recycle-move-action.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/howto/img/recycle-rule-path.png
Normal file
BIN
docs/howto/img/recycle-rule-path.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/howto/img/recycle-rule.png
Normal file
BIN
docs/howto/img/recycle-rule.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -19,12 +19,12 @@ In this tutorial we'll focus on `HTTP-01` challenge type and make the following
|
||||
|
||||
## Overview
|
||||
|
||||
- [Obtaining a certificate using the Lego CLI tool](#Obtaining-a-certificate-using-the-Lego-CLI-tool)
|
||||
- [Automatic certificate renewal using the Lego CLI tool](#Automatic-certificate-renewal-using-the-Lego-CLI-tool)
|
||||
- [Obtaining a certificate using the ACME protocol built into SFTPGo](#Obtaining-a-certificate-using-the-ACME-protocol-built-into-SFTPGo)
|
||||
- [Enable HTTPS for SFTPGo Web UI and REST API](#Enable-HTTPS-for-SFTPGo-Web-UI-and-REST-API)
|
||||
- [Enable HTTPS for WebDAV service](#Enable-HTTPS-for-WebDAV-service)
|
||||
- [Enable explicit FTP over TLS](#Enable-explicit-FTP-over-TLS)
|
||||
- [Obtaining a certificate using the Lego CLI tool](#obtaining-a-certificate-using-the-lego-cli-tool)
|
||||
- [Automatic certificate renewal using the Lego CLI tool](#automatic-certificate-renewal-using-the-lego-cli-tool)
|
||||
- [Obtaining a certificate using the ACME protocol built into SFTPGo](#obtaining-a-certificate-using-the-acme-protocol-built-into-sftpgo)
|
||||
- [Enable HTTPS for SFTPGo Web UI and REST API](#enable-https-for-sftpgo-web-ui-and-rest-api)
|
||||
- [Enable HTTPS for WebDAV service](#enable-https-for-webdav-service)
|
||||
- [Enable explicit FTP over TLS](#enable-explicit-ftp-over-tls)
|
||||
|
||||
## Obtaining a certificate using the Lego CLI tool
|
||||
|
||||
@@ -117,6 +117,8 @@ When the certificate is renewed you should see SFTPGo logs like the following to
|
||||
|
||||
## Obtaining a certificate using the ACME protocol built into SFTPGo
|
||||
|
||||
:warning: Starting from SFTPGo v2.5.0 you can request certificates from the Server Manager -> Configurations -> ACME section of the WebAdmin UI.
|
||||
|
||||
You can open the SFTPGo configuration file, search for the `acme` section and change it as follow.
|
||||
|
||||
```json
|
||||
@@ -149,6 +151,8 @@ SFTPGO_ACME__HTTP01_CHALLENGE__WEBROOT="/var/www/sftpgo.com"
|
||||
Make sure that the `sftpgo` user can write to the `/var/www/sftpgo.com` directory or pre-create the `/var/www/sftpgo.com/.well-known/acme-challenge` directory with the appropriate permissions.
|
||||
This directory must be publicly served by your web server.
|
||||
|
||||
:warning: in this example we assume you have an existing HTTP server. If not, you can leave the web root blank and SFTPGo will resolve the HTTP01 challenge by itself.
|
||||
|
||||
Register your account and obtain certificates by running the following command.
|
||||
|
||||
```bash
|
||||
|
||||
@@ -29,20 +29,11 @@ Two-factor authentication is enabled by default with the following settings.
|
||||
},
|
||||
```
|
||||
|
||||
The `issuer` and `algo` are exposed to the authenticators apps. For example, you could set your company/organization name as `issuer` and an `algo` appropriate for your target apps/devices. The supported algorithms are: `sha1`, `sha256`, `sha512`. Currently Google Authenticator app on iPhone seems to only support `sha1`, please check the compatibility with your target apps/device before setting a different algorithm.
|
||||
The `issuer` and `algo` are visible/used in the authenticators apps. For example, you could set your company/organization name as `issuer` and an `algo` appropriate for your target apps/devices. The supported algorithms are: `sha1`, `sha256`, `sha512`. Currently Google Authenticator app on iPhone seems to only support `sha1`, please check the compatibility with your target apps/device before setting a different algorithm.
|
||||
|
||||
You can also define multiple configurations, for example one that uses `sha256` or `sha512` and another one that uses `sha1` and instruct your users to use the appropriate configuration for their devices/apps. The algorithm should not be changed if there are users or admins using the configuration. The `name` is exposed to the users/admins when they select the 2FA configuration to use and it must be unique. A configuration name should not be changed if there are users or admins using it.
|
||||
You can also define multiple configurations, for example one that uses `sha256` or `sha512` and another one that uses `sha1` and instruct your users to use the appropriate configuration for their devices/apps. The algorithm should not be changed if there are users or admins using the configuration. The `name` is visible to the users/admins when they select the 2FA configuration to use and it must be unique. A configuration name should not be changed if there are users or admins using it.
|
||||
|
||||
SFTPGo can use 2FA for `HTTP`, `SSH` (SFTP, SCP) and `FTP` protocols. If you plan to use 2FA with `SSH` you have to enable the keyboard interactive authentication which is disabled by default.
|
||||
|
||||
```json
|
||||
"sftpd": {
|
||||
...
|
||||
"keyboard_interactive_authentication": true,
|
||||
...
|
||||
```
|
||||
|
||||
Or setting the environment variable `SFTPGO_SFTPD__KEYBOARD_INTERACTIVE_AUTHENTICATION=1`.
|
||||
SFTPGo can use 2FA for `HTTP`, `SSH` (SFTP, SCP) and `FTP` protocols.
|
||||
|
||||
## Enable 2FA for admins
|
||||
|
||||
|
||||
13
docs/logs.md
13
docs/logs.md
@@ -8,6 +8,7 @@ The logs can be divided into the following categories:
|
||||
- `sender` string. This is generally the package name that emits the log
|
||||
- `time` string. Date/time with millisecond precision
|
||||
- `level` string
|
||||
- `connection_id`, string, optional
|
||||
- `message` string
|
||||
- **"transfer logs"**, SFTP/SCP transfer logs:
|
||||
- `sender` string. `Upload` or `Download`
|
||||
@@ -20,10 +21,10 @@ The logs can be divided into the following categories:
|
||||
- `username`, string
|
||||
- `file_path` string
|
||||
- `connection_id` string. Unique connection identifier
|
||||
- `protocol` string. `SFTP`, `SCP`, `SSH`, `FTP`, `HTTP`, `DAV`, `DataRetention`
|
||||
- `protocol` string. `SFTP`, `SCP`, `SSH`, `FTP`, `HTTP`, `HTTPShare`, `DAV`, `DataRetention`, `EventAction`
|
||||
- `ftp_mode`, string. `active` or `passive`. Included only for `FTP` protocol
|
||||
- **"command logs"**, SFTP/SCP command logs:
|
||||
- `sender` string. `Rename`, `Rmdir`, `Mkdir`, `Symlink`, `Remove`, `Chmod`, `Chown`, `Chtimes`, `Truncate`, `SSHCommand`
|
||||
- `sender` string. `Rename`, `Rmdir`, `Mkdir`, `Symlink`, `Remove`, `Chmod`, `Chown`, `Chtimes`, `Truncate`, `Copy`, `SSHCommand`
|
||||
- `level` string
|
||||
- `local_addr` string. IP/port of the local address the connection arrived on. For example `127.0.0.1:1234`
|
||||
- `remote_addr` string. IP and, optionally, port of the remote client. For example `127.0.0.1:1234` or `127.0.0.1`
|
||||
@@ -36,16 +37,19 @@ The logs can be divided into the following categories:
|
||||
- `access_time` datetime as YYYY-MM-DDTHH:MM:SS. Valid for sender `Chtimes` otherwise empty
|
||||
- `modification_time` datetime as YYYY-MM-DDTHH:MM:SS. Valid for sender `Chtimes` otherwise empty
|
||||
- `size` int64. Valid for sender `Truncate` otherwise -1
|
||||
- `elapsed`, int64. Elapsed time, as milliseconds
|
||||
- `ssh_command`, string. Valid for sender `SSHCommand` otherwise empty
|
||||
- `connection_id` string. Unique connection identifier
|
||||
- `protocol` string. `SFTP`, `SCP` or `SSH`
|
||||
- `protocol` string. `SFTP`, `SCP`, `SSH`, `FTP`, `HTTP`, `DAV`, `DataRetention`, `EventAction`
|
||||
- **"http logs"**, REST API logs:
|
||||
- `sender` string. `httpd`
|
||||
- `level` string
|
||||
- `time` string. Date/time with millisecond precision
|
||||
- `local_addr` string. IP/port of the local address the connection arrived on. For example `127.0.0.1:1234`
|
||||
- `remote_addr` string. IP and, optionally, port of the remote client. For example `127.0.0.1:1234` or `127.0.0.1`
|
||||
- `proto` string, for example `HTTP/1.1`
|
||||
- `method` string. HTTP method (`GET`, `POST`, `PUT`, `DELETE` etc.)
|
||||
- `request_id` string. Omitted in telemetry logs
|
||||
- `user_agent` string
|
||||
- `uri` string. Full uri
|
||||
- `resp_status` integer. HTTP response status code
|
||||
@@ -55,8 +59,9 @@ The logs can be divided into the following categories:
|
||||
- **"connection failed logs"**, logs for failed attempts to initialize a connection. A connection can fail for an authentication error or other errors such as a client abort or a timeout if the login does not happen in two minutes
|
||||
- `sender` string. `connection_failed`
|
||||
- `level` string
|
||||
- `time` string. Date/time with millisecond precision
|
||||
- `username`, string. Can be empty if the connection is closed before an authentication attempt
|
||||
- `client_ip` string.
|
||||
- `protocol` string. Possible values are `SSH`, `FTP`, `DAV`
|
||||
- `login_type` string. Can be `publickey`, `password`, `keyboard-interactive`, `publickey+password`, `publickey+keyboard-interactive` or `no_auth_tryed`
|
||||
- `login_type` string. Can be `publickey`, `password`, `keyboard-interactive`, `publickey+password`, `publickey+keyboard-interactive` or `no_auth_tried`
|
||||
- `error` string. Optional error description
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Metrics
|
||||
|
||||
SFTPGo exposes [Prometheus](https://prometheus.io/) metrics at the `/metrics` HTTP endpoint of the telemetry server.
|
||||
SFTPGo supports [Prometheus](https://prometheus.io/) metrics at the `/metrics` HTTP endpoint of the telemetry server.
|
||||
Several counters and gauges are available, for example:
|
||||
|
||||
- Total uploads and downloads
|
||||
@@ -17,4 +17,4 @@ Several counters and gauges are available, for example:
|
||||
|
||||
Please check the `/metrics` page for more details.
|
||||
|
||||
We expose the `/metrics` endpoint in both HTTP server and the telemetry server, you should use the one from the telemetry server. The HTTP server `/metrics` endpoint is deprecated and it will be removed in future releases.
|
||||
The telemetry server is disabled by default in the released [sftpgo.json](https://raw.githubusercontent.com/drakkan/sftpgo/main/sftpgo.json). To enable look in [docs/full-configuration.md](https://raw.githubusercontent.com/drakkan/sftpgo/main/docs/full-configuration.md) for configuration details.
|
||||
|
||||
31
docs/oidc.md
31
docs/oidc.md
@@ -33,7 +33,7 @@ The resulting JSON configuration for the `sftpgo-client` that you can obtain fro
|
||||
}
|
||||
```
|
||||
|
||||
Add the following configuration parameters to the SFTPGo configuration file (or use env vars to set them):
|
||||
Add the following configuration parameters to the SFTPGo configuration file.
|
||||
|
||||
```json
|
||||
...
|
||||
@@ -55,9 +55,20 @@ Add the following configuration parameters to the SFTPGo configuration file (or
|
||||
...
|
||||
```
|
||||
|
||||
Alternatively (recommended), you can use environment variables by creating the file `oidc.env` in the `env.d` directory with the following content.
|
||||
|
||||
```shell
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_ID="sftpgo-client"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_SECRET="jRsmE0SWnuZjP7djBqNq0mrf8QN77j2c"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__CONFIG_URL="http://192.168.1.12:8086/auth/realms/sftpgo"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__REDIRECT_BASE_URL="http://192.168.1.50:8080"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__USERNAME_FIELD="preferred_username"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__ROLE_FIELD="sftpgo_role"
|
||||
```
|
||||
|
||||
SFTPGo will automatically add the `/.well-known/openid-configuration` suffix to the provided `config_url` and uses [OpenID Connect Discovery specifications](https://openid.net/specs/openid-connect-discovery-1_0.html) to obtain information needed to interact with it, including its OAuth 2.0 endpoint locations.
|
||||
|
||||
From SFTPGo login page click `Login with OpenID` button, you will be redirected to the Keycloak login page, after a successful authentication Keyclock will redirect back to SFTPGo Web Admin or SFTPGo Web Client.
|
||||
From SFTPGo login page click `Login with OpenID` button, you will be redirected to the Keycloak login page, after a successful authentication Keycloack will redirect back to SFTPGo Web Admin or SFTPGo Web Client.
|
||||
|
||||
Please note that the ID token returned from Keycloak must contain the `username_field` specified in the SFTPGo configuration and optionally the `role_field`. The mapped usernames must exist in SFTPGo.
|
||||
If you don't want to explicitly define SFTPGo roles in your identity provider, you can set `implicit_roles` to `true`. With this configuration, the SFTPGo role is assumed based on the login link used.
|
||||
@@ -110,6 +121,7 @@ And the following is an example ID token which allows the SFTPGo user `user1` to
|
||||
```
|
||||
|
||||
SFTPGo users (not admins) can be created/updated after successful OpenID authentication by defining a [pre-login hook](./dynamic-user-mod.md).
|
||||
Users and admins can also be created/updated after successful OpenID authentication using the [EventManager](./eventmanager.md).
|
||||
You can use `scopes` configuration to request additional information (claims) about authenticated users (See your provider's own documentation for more information).
|
||||
By default the scopes `"openid", "profile", "email"` are retrieved.
|
||||
The `custom_fields` configuration parameter can be used to define claim field names to pass to the pre-login hook,
|
||||
@@ -132,6 +144,19 @@ then you can add it to the `custom_fields` in the SFTPGo configuration like this
|
||||
...
|
||||
```
|
||||
|
||||
Alternatively (recommended), you can use environment variables by creating the file `oidc.env` in the `env.d` directory with the following content.
|
||||
|
||||
```shell
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_ID="sftpgo-client"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_SECRET="jRsmE0SWnuZjP7djBqNq0mrf8QN77j2c"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__CONFIG_URL="http://192.168.1.12:8086/auth/realms/sftpgo"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__REDIRECT_BASE_URL="http://192.168.1.50:8080"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__USERNAME_FIELD="preferred_username"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__SCOPES="openid,profile,email,sftpgo"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__ROLE_FIELD="sftpgo_role"
|
||||
SFTPGO_HTTPD__BINDINGS__0__OIDC__CUSTOM_FIELDS="sftpgo_home_dir"
|
||||
```
|
||||
|
||||
The pre-login hook will receive a JSON serialized user with the following field:
|
||||
|
||||
```json
|
||||
@@ -141,3 +166,5 @@ The pre-login hook will receive a JSON serialized user with the following field:
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
In EventManager actions you can use the placeholder `{{IDPFieldsftpgo_home_dir}}` for string-based custom fields.
|
||||
|
||||
@@ -18,7 +18,7 @@ The following plugin types are supported:
|
||||
|
||||
Full configuration details can be found [here](./full-configuration.md).
|
||||
|
||||
:warning: Please note that the plugin system is experimental, the exposed configuration parameters and interfaces may change in a backward incompatible way in future.
|
||||
:warning: Please note that the plugin system is experimental, the configuration parameters and interfaces may change in a backward incompatible way in future.
|
||||
|
||||
## Available plugins
|
||||
|
||||
|
||||
@@ -15,12 +15,6 @@ Usage:
|
||||
sftpgo portable [flags]
|
||||
|
||||
Flags:
|
||||
-C, --advertise-credentials If the SFTP/FTP service is
|
||||
advertised via multicast DNS, this
|
||||
flag allows to put username/password
|
||||
inside the advertised TXT record
|
||||
-S, --advertise-service Advertise configured services using
|
||||
multicast DNS
|
||||
--allowed-patterns stringArray Allowed file patterns case insensitive.
|
||||
The format is:
|
||||
/dir::pattern1,pattern2.
|
||||
@@ -90,6 +84,9 @@ Flags:
|
||||
--log-utc-time Use UTC time for logging
|
||||
-p, --password string Leave empty to use an auto generated
|
||||
value
|
||||
--password-file string Read the password from the specified
|
||||
file path. Leave empty to use an auto
|
||||
generated value
|
||||
-g, --permissions strings User's permissions. "*" means any
|
||||
permission (default [list,download])
|
||||
-k, --public-key strings
|
||||
@@ -148,15 +145,3 @@ Flags:
|
||||
--webdav-port int 0 means a random unprivileged port,
|
||||
< 0 disabled (default -1)
|
||||
```
|
||||
|
||||
In portable mode, SFTPGo can advertise the SFTP/FTP services and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it.
|
||||
|
||||
Here is an example of the advertised SFTP service including credentials as seen using `avahi-browse`:
|
||||
|
||||
```console
|
||||
= enp0s31f6 IPv4 SFTPGo portable 53705 SFTP File Transfer local
|
||||
hostname = [p1.local]
|
||||
address = [192.168.1.230]
|
||||
port = [53705]
|
||||
txt = ["password=EWOo6pJe" "user=user" "version=0.9.3-dev-b409523-dirty-2019-10-26T13:43:32Z"]
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Post-connect hook
|
||||
|
||||
This hook is executed as soon as a new connection is established. It notifies the connection's IP address and protocol. Based on the received response, the connection is accepted or rejected. Combining this hook with the [Post-login hook](./post-login-hook.md) you can implement your own (even for Protocol) blacklist/whitelist of IP addresses.
|
||||
This hook is executed as soon as a new connection is established. It notifies the connection's IP address and protocol. Based on the received response, the connection is accepted or rejected. Combining this hook with the [Post-login hook](./post-login-hook.md) you can implement your own (even for Protocol) blocklist/allowlist of IP addresses.
|
||||
|
||||
The `post_connect_hook` can be defined as the absolute path of your program or an HTTP URL.
|
||||
|
||||
@@ -17,7 +17,7 @@ The program must finish within 20 seconds.
|
||||
If the hook defines an HTTP URL then this URL will be invoked as HTTP GET with the following query parameters:
|
||||
|
||||
- `ip`
|
||||
- `protocol`, possible values are `SSH`, `FTP`, `DAV`, `HTTP`, `OIDC` (OpenID Connect)
|
||||
- `protocol`, possible values are `SSH`, `FTP`, `DAV`, `HTTP`, `HTTPShare`, `OIDC` (OpenID Connect)
|
||||
|
||||
The connection is accepted if the HTTP response code is `200` otherwise rejected.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ If the hook defines an external program it can reads the following environment v
|
||||
|
||||
- `SFTPGO_LOGIND_USER`, it contains the user serialized as JSON. The username is empty if the connection is closed for authentication timeout
|
||||
- `SFTPGO_LOGIND_IP`
|
||||
- `SFTPGO_LOGIND_METHOD`, possible values are `publickey`, `password`, `keyboard-interactive`, `publickey+password`, `publickey+keyboard-interactive`, `TLSCertificate`, `TLSCertificate+password` or `no_auth_tryed`, `IDP` (external identity provider)
|
||||
- `SFTPGO_LOGIND_METHOD`, possible values are `publickey`, `password`, `keyboard-interactive`, `publickey+password`, `publickey+keyboard-interactive`, `TLSCertificate`, `TLSCertificate+password` or `no_auth_tried`, `IDP` (external identity provider)
|
||||
- `SFTPGO_LOGIND_STATUS`, 1 means login OK, 0 login KO
|
||||
- `SFTPGO_LOGIND_PROTOCOL`, possible values are `SSH`, `FTP`, `DAV`, `HTTP`, `OIDC` (OpenID Connect)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
The built-in profiler lets you collect CPU profiles, traces, allocations and heap profiles that allow to identify and correct specific bottlenecks.
|
||||
You can enable the built-in profiler using `telemetry` configuration section inside the configuration file.
|
||||
|
||||
Profiling data are exposed via HTTP/HTTPS in the format expected by the [pprof](https://github.com/google/pprof/blob/main/doc/README.md) visualization tool. You can find the index page at the URL `/debug/pprof/`.
|
||||
Profiling data are available via HTTP/HTTPS in the format expected by the [pprof](https://github.com/google/pprof/blob/main/doc/README.md) visualization tool. You can find the index page at the URL `/debug/pprof/`.
|
||||
|
||||
The following profiles are available, you can obtain them via HTTP GET requests:
|
||||
|
||||
|
||||
@@ -22,19 +22,7 @@ You can also define two types of rate limiters:
|
||||
|
||||
If you configure a per-host rate limiter, SFTPGo will keep a rate limiter in memory for each host that connects to the service, you can limit the memory usage using the `entries_soft_limit` and `entries_hard_limit` configuration keys.
|
||||
|
||||
For each rate limiter you can exclude a list of IP addresses and IP ranges by defining an `allow_list`.
|
||||
The allow list supports IPv4/IPv6 address and CIDR networks, for example:
|
||||
|
||||
```json
|
||||
...
|
||||
"allow_list": [
|
||||
"192.0.2.1",
|
||||
"192.168.1.0/24",
|
||||
"2001:db8::68",
|
||||
"2001:db8:1234::/48"
|
||||
],
|
||||
...
|
||||
```
|
||||
You can exclude a list of IP addresses and IP ranges from rate limiters by adding them to rate limites allow list using the WebAdmin UI or the REST API. In multi-nodes setups, the list entries propagation between nodes may take some minutes.
|
||||
|
||||
You can defines how many rate limiters as you want, but keep in mind that if you defines multiple rate limiters each request will be checked against all the configured limiters and so it can potentially be delayed multiple times. Let's clarify with an example, here is a configuration that defines a global rate limiter and a per-host rate limiter for the FTP protocol:
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# REST API
|
||||
|
||||
SFTPGo exposes REST API to manage, backup, and restore users and folders, data retention, and to get real time reports of the active connections with the ability to forcibly close a connection.
|
||||
SFTPGo supports REST API to manage, backup, and restore users and folders, data retention, and to get real time reports of the active connections with the ability to forcibly close a connection.
|
||||
|
||||
If quota tracking is enabled in the configuration file, then the used size and number of files are updated each time a file is added/removed. If files are added/removed not using SFTP/SCP, or if you change `track_quota` from `2` to `1`, you can rescan the users home dir and update the used quota using the REST API.
|
||||
|
||||
REST API are protected using JSON Web Tokens (JWT) authentication and can be exposed over HTTPS. You can also configure client certificate authentication in addition to JWT.
|
||||
REST API are protected using JSON Web Tokens (JWT) authentication and can be served over HTTPS. You can also configure client certificate authentication in addition to JWT.
|
||||
|
||||
You can get a JWT token using the `/api/v2/token` endpoint, you need to authenticate using HTTP Basic authentication and the credentials of an active administrator. Here is a sample response:
|
||||
|
||||
@@ -99,7 +99,7 @@ You can find an example script that shows how to manage data retention [here](..
|
||||
|
||||
:warning: Deleting files is an irreversible action, please make sure you fully understand what you are doing before using this feature, you may have users with overlapping home directories or virtual folders shared between multiple users, it is relatively easy to inadvertently delete files you need.
|
||||
|
||||
The OpenAPI 3 schema for the exposed API can be found inside the source tree: [openapi.yaml](../openapi/openapi.yaml "OpenAPI 3 specs"). You can render the schema and try the API using the `/openapi` endpoint. SFTPGo uses by default [Swagger UI](https://github.com/swagger-api/swagger-ui), you can use another renderer just by copying it to the defined OpenAPI path.
|
||||
The OpenAPI 3 schema for the supported APIs can be found inside the source tree: [openapi.yaml](../openapi/openapi.yaml "OpenAPI 3 specs"). You can render the schema and try the API using the `/openapi` endpoint. SFTPGo uses by default [Swagger UI](https://github.com/swagger-api/swagger-ui), you can use another renderer just by copying it to the defined OpenAPI path.
|
||||
|
||||
You can also explore the schema on [Stoplight](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml).
|
||||
|
||||
|
||||
13
docs/roles.md
Normal file
13
docs/roles.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Roles
|
||||
|
||||
Roles can be assigned to users and administrators. Admins with a role are limited administrators, they can only view and manage users with their own role and they cannot have the following permissions:
|
||||
|
||||
- manage_admins
|
||||
- manage_system
|
||||
- manage_event_rules
|
||||
- manage_roles
|
||||
- view_events
|
||||
|
||||
Users created by role administrators automatically inherit their role.
|
||||
|
||||
Admins without a role are global administrators and can manage all users (with and without a role) and assign a specific role to users.
|
||||
@@ -7,6 +7,8 @@ AWS SDK has different options for credentials. We support:
|
||||
1. Providing [Access Keys](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys).
|
||||
2. Use IAM roles for Amazon EC2
|
||||
3. Use IAM roles for tasks if your application uses an ECS task definition
|
||||
4. Utilizing [IAM roles](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) for service accounts (IRSA) if you operate SFTPGo atop AWS EKS.
|
||||
5. Assuming specific IAM role by setting its ARN.
|
||||
|
||||
So, you need to provide access keys to activate option 1, or leave them blank to use the other ways to specify credentials.
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ SFTPGo supports the following built-in SSH commands:
|
||||
- `scp`, SFTPGo implements the SCP protocol so we can support it for cloud filesystems too and we can avoid the other system commands limitations. SCP between two remote hosts is supported using the `-3` scp option. Wildcard expansion is not supported.
|
||||
- `md5sum`, `sha1sum`, `sha256sum`, `sha384sum`, `sha512sum`. Useful to check message digests for uploaded files.
|
||||
- `cd`, `pwd`. Some SFTP clients do not support the SFTP SSH_FXP_REALPATH packet type, so they use `cd` and `pwd` SSH commands to get the initial directory. Currently `cd` does nothing and `pwd` always returns the `/` path. These commands will work with any storage backend but keep in mind that to calculate the hash we need to read the whole file, for remote backends this means downloading the file, for the encrypted backend this means decrypting the file.
|
||||
- `sftpgo-copy`. This is a built-in copy implementation. It allows server side copy for files and directories. The first argument is the source file/directory and the second one is the destination file/directory, for example `sftpgo-copy <src> <dst>`. The command will fail if the destination exists. Copy for directories spanning virtual folders is not supported. Only local filesystem is supported: recursive copy for Cloud Storage filesystems requires a new request for every file in any case, so a real server side copy is not possible.
|
||||
- `sftpgo-remove`. This is a built-in remove implementation. It allows to remove single files and to recursively remove directories. The first argument is the file/directory to remove, for example `sftpgo-remove <dst>`. Only local and encrypted filesystems are supported: recursive remove for Cloud Storage filesystems requires a new request for every file in any case, so a server side remove is not possible.
|
||||
- `sftpgo-copy`. This is a built-in copy implementation. It allows server side copy for files and directories. The first argument is the source file/directory and the second one is the destination file/directory, for example `sftpgo-copy <src> <dst>`. :warning: Copying directories that span virtual folders is supported but, for Cloud Storage filesystems, the remote copy API is not currently used.
|
||||
- `sftpgo-remove`. This is a built-in remove implementation. It allows to remove single files and to recursively remove directories. The first argument is the file/directory to remove, for example `sftpgo-remove <dst>`. Removing directories spanning virtual folders is not supported.
|
||||
|
||||
The following SSH commands are enabled by default:
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ SFTPGo will try to automatically create any missing parent directory for the con
|
||||
|
||||
For each virtual folder, the following properties can be configured:
|
||||
|
||||
- `folder_name`, is the ID for an existing folder. The folder structure contains the absolute filesystem path to expose as virtual folder
|
||||
- `folder_name`, is the ID for an existing folder. The folder structure contains the absolute filesystem path to map as virtual folder
|
||||
- `filesystem`, this way you can map a local path or a Cloud backend to mount as virtual folders
|
||||
- `virtual_path`, the SFTPGo absolute path to use to expose the mapped path
|
||||
- `virtual_path`, absolute path seen by SFTPGo users where the mapped path is accessible
|
||||
- `quota_size`, maximum size allowed as bytes. 0 means unlimited, -1 included in user quota
|
||||
- `quota_files`, maximum number of files allowed. 0 means unlimited, -1 included in user quota
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Web Admin
|
||||
|
||||
You can easily build your own interface using the exposed [REST API](./rest-api.md). Anyway, SFTPGo also provides a basic built-in web interface that allows you to manage users, virtual folders, admins and connections.
|
||||
You can easily build your own interface using the SFTPGo [REST API](./rest-api.md). Anyway, SFTPGo also provides a basic built-in web interface that allows you to manage users, virtual folders, admins and connections.
|
||||
With the default `httpd` configuration, the web admin is available at the following URL:
|
||||
|
||||
[http://127.0.0.1:8080/web/admin](http://127.0.0.1:8080/web/admin)
|
||||
|
||||
If no admin user is found within the data provider, typically after the initial installation, SFTPGo will ask you to create the first admin. You can also pre-create an admin user by loading initial data or by enabling the `create_default_admin` configuration key. Please take a look [here](./full-configuration.md) for more details.
|
||||
|
||||
The web interface can be exposed via HTTPS and may require mutual TLS authentication in addition to administrator credentials.
|
||||
The web interface can be configured over HTTPS and to require mutual TLS authentication in addition to administrator credentials.
|
||||
|
||||
@@ -32,4 +32,6 @@ SFTPGo has a minimal implementation for [Dead Properties](https://tools.ietf.org
|
||||
|
||||
To properly support dead properties we need a design decision, probably the best solution is to write a plugin and store them inside a supported data provider.
|
||||
|
||||
SFTPGo also supports setting the modification time using the `X-OC-Mtime` header. Nextcloud compatible clients set this header.
|
||||
|
||||
If you find any other quirks or problems please let us know opening a GitHub issue, thank you!
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module github.com/drakkan/sftpgo/authy/checkpwd
|
||||
|
||||
go 1.15
|
||||
go 1.20
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module github.com/drakkan/sftpgo/authy/extauth
|
||||
|
||||
go 1.15
|
||||
go 1.20
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module github.com/drakkan/sftpgo/authy/keyint
|
||||
|
||||
go 1.15
|
||||
go 1.20
|
||||
|
||||
@@ -47,3 +47,5 @@ python convertusers proftpd.passwd proftpd pro_users.json
|
||||
The generated json file can be used as input for the `loaddata` REST API.
|
||||
|
||||
Please note that when importing Linux/Unix users the input file is not required: `/etc/passwd` and `/etc/shadow` are automatically parsed. `/etc/shadow` read permission is typically granted to the `root` user only, so you need to execute `convertusers` as `root`.
|
||||
|
||||
:warning: SFTPGo does not currently support `yescrypt` hashed passwords.
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
module github.com/drakkan/ldapauth
|
||||
|
||||
go 1.15
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.2.4
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.4
|
||||
golang.org/x/crypto v0.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.2.4 h1:PFavAq2xTgzo/loE8qNXcQaofAaqIpI4WgaLdv+1l3E=
|
||||
github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -97,7 +97,7 @@ func main() {
|
||||
|
||||
// search the user trying to login and fetch some attributes, this search string is tested against 389ds using the default configuration
|
||||
log.Printf("username=%s\n", username)
|
||||
searchFilter := fmt.Sprintf("(uid=%s)", username)
|
||||
searchFilter := fmt.Sprintf("(uid=%s)", ldap.EscapeFilter(username))
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
"ou=people," + rootDN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
|
||||
@@ -153,6 +153,6 @@ func LoadConfig(configDir, configName string) error {
|
||||
logger.WarnToConsole("error parsing configuration file: %v. Default configuration will be used.", err)
|
||||
return err
|
||||
}
|
||||
logger.Debug(logSender, "", "config file used: '%#v', config loaded: %+v", viper.ConfigFileUsed(), getRedactedConf())
|
||||
logger.Debug(logSender, "", "config file used: '%q', config loaded: %+v", viper.ConfigFileUsed(), getRedactedConf())
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,26 +1,38 @@
|
||||
module github.com/drakkan/sftpgo/ldapauthserver
|
||||
|
||||
go 1.15
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-chi/chi v1.5.2
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/go-ldap/ldap/v3 v3.2.4
|
||||
github.com/magiconair/properties v1.8.4 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.4
|
||||
github.com/nathanaelle/password/v2 v2.0.1
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/spf13/afero v1.5.1 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.7.1
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
github.com/rs/zerolog v1.29.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/viper v1.15.0
|
||||
golang.org/x/crypto v0.7.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
||||
github.com/spf13/afero v1.9.4 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -3,235 +3,236 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
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.44.3/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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
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=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
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/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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
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-radix v0.0.0-20180808171621-7fddfc383310/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
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/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi v1.5.2 h1:YcLIBANL4OTaAOcTdp//sskGa0yGACQMCtbnr7YEn0Q=
|
||||
github.com/go-chi/chi v1.5.2/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-ldap/ldap/v3 v3.2.4 h1:PFavAq2xTgzo/loE8qNXcQaofAaqIpI4WgaLdv+1l3E=
|
||||
github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
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-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
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/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-20201218002935-b9804c9f04c2/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/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-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
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/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
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.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
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.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
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/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
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.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/nathanaelle/password/v2 v2.0.1 h1:ItoCTdsuIWzilYmllQPa3DR3YoCXcpfxScWLqr8Ii2s=
|
||||
github.com/nathanaelle/password/v2 v2.0.1/go.mod h1:eaoT+ICQEPNtikBRIAatN8ThWwMhVG+r1jTw60BvPJk=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
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/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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 v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
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/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
|
||||
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
|
||||
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/afero v1.9.4 h1:Sd43wM1IWz/s1aVXdOBkjJvuP8UdyqioeE4AmM0QsBs=
|
||||
github.com/spf13/afero v1.9.4/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
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.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
|
||||
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
|
||||
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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
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=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
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=
|
||||
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=
|
||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
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=
|
||||
@@ -241,16 +242,22 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
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/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/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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=
|
||||
@@ -259,20 +266,48 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
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-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-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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
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/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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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/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-20181107165924-66b7b1311ac8/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -280,49 +315,130 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/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-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/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-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-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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-20190328211700-ab21143f2384/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-20190828213141-aed303cbaa74/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-20191112195655-aa38f8e97acc/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-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-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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/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/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=
|
||||
@@ -332,28 +448,78 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98
|
||||
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-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-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
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=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
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.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/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=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/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=
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/drakkan/sftpgo/ldapauthserver/config"
|
||||
"github.com/drakkan/sftpgo/ldapauthserver/logger"
|
||||
"github.com/drakkan/sftpgo/ldapauthserver/utils"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
@@ -118,13 +118,13 @@ func loadCACerts(configDir string) error {
|
||||
caPath := getConfigPath(ca, configDir)
|
||||
certs, err := os.ReadFile(caPath)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, "", "error loading ca cert %#v: %v", caPath, err)
|
||||
logger.Warn(logSender, "", "error loading ca cert %q: %v", caPath, err)
|
||||
return err
|
||||
}
|
||||
if !rootCAs.AppendCertsFromPEM(certs) {
|
||||
logger.Warn(logSender, "", "unable to add ca cert %#v", caPath)
|
||||
logger.Warn(logSender, "", "unable to add ca cert %q", caPath)
|
||||
} else {
|
||||
logger.Debug(logSender, "", "ca cert %#v added to the trusted certificates", caPath)
|
||||
logger.Debug(logSender, "", "ca cert %q added to the trusted certificates", caPath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/drakkan/sftpgo/ldapauthserver/logger"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -78,14 +78,14 @@ func checkSFTPGoUserAuth(w http.ResponseWriter, r *http.Request) {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
ldapConfig.BaseDN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
strings.Replace(ldapConfig.SearchFilter, "%s", authReq.Username, 1),
|
||||
strings.Replace(ldapConfig.SearchFilter, "%s", ldap.EscapeFilter(authReq.Username), 1),
|
||||
ldapConfig.SearchBaseAttrs,
|
||||
nil,
|
||||
)
|
||||
|
||||
sr, err := l.Search(searchRequest)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "error searching LDAP user %#v: %v", authReq.Username, err)
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "error searching LDAP user %q: %v", authReq.Username, err)
|
||||
sendAPIResponse(w, r, err, "Error searching LDAP user", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func checkSFTPGoUserAuth(w http.ResponseWriter, r *http.Request) {
|
||||
if len(authReq.PublicKey) > 0 {
|
||||
userKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(authReq.PublicKey))
|
||||
if err != nil {
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "invalid public key for user %#v: %v", authReq.Username, err)
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "invalid public key for user %q: %v", authReq.Username, err)
|
||||
sendAPIResponse(w, r, err, "Invalid public key", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func checkSFTPGoUserAuth(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
if !authOk {
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "public key authentication failed for user: %#v", authReq.Username)
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "public key authentication failed for user: %q", authReq.Username)
|
||||
sendAPIResponse(w, r, nil, "public key authentication failed", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func checkSFTPGoUserAuth(w http.ResponseWriter, r *http.Request) {
|
||||
userdn := sr.Entries[0].DN
|
||||
err = l.Bind(userdn, authReq.Password)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "password authentication failed for user: %#v", authReq.Username)
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "password authentication failed for user: %q", authReq.Username)
|
||||
sendAPIResponse(w, r, nil, "password authentication failed", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
@@ -133,7 +133,7 @@ func checkSFTPGoUserAuth(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
user, err := getSFTPGoUser(sr.Entries[0], authReq.Username)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "get user from LDAP entry failed for username %#v: %v",
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "get user from LDAP entry failed for username %q: %v",
|
||||
authReq.Username, err)
|
||||
sendAPIResponse(w, r, err, "mapping LDAP user failed", http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
|
||||
264
go.mod
264
go.mod
@@ -1,175 +1,181 @@
|
||||
module github.com/drakkan/sftpgo/v2
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.28.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.3
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.3
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.42
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.24
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.4
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.8
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.17.5
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.2.19
|
||||
github.com/coreos/go-oidc/v3 v3.4.0
|
||||
github.com/drakkan/webdav v0.0.0-20221101181759-17ed21f9337b
|
||||
cloud.google.com/go/storage v1.34.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0
|
||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
|
||||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
|
||||
github.com/aws/aws-sdk-go-v2 v1.22.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.23.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.15.2
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.5
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.18.1
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.1
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.23.1
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.25.1
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.5
|
||||
github.com/coreos/go-oidc/v3 v3.7.0
|
||||
github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8
|
||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
|
||||
github.com/fclairamb/ftpserverlib v0.20.1-0.20221012093027-95be4ae0c9a6
|
||||
github.com/fclairamb/ftpserverlib v0.22.0
|
||||
github.com/fclairamb/go-log v0.4.1
|
||||
github.com/go-acme/lego/v4 v4.9.0
|
||||
github.com/go-chi/chi/v5 v5.0.8-0.20221018120124-e5529d9db4d3
|
||||
github.com/go-chi/jwtauth/v5 v5.1.0
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-acme/lego/v4 v4.14.2
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
github.com/go-chi/jwtauth/v5 v5.1.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/grandcat/zeroconf v1.0.0
|
||||
github.com/hashicorp/go-hclog v1.3.1
|
||||
github.com/hashicorp/go-plugin v1.4.6
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1
|
||||
github.com/jackc/pgx/v5 v5.1.1
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/hashicorp/go-hclog v1.5.0
|
||||
github.com/hashicorp/go-plugin v1.5.2
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5
|
||||
github.com/jackc/pgx/v5 v5.4.3
|
||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||
github.com/klauspost/compress v1.15.12
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8
|
||||
github.com/klauspost/compress v1.17.2
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.16
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/mattn/go-sqlite3 v1.14.18
|
||||
github.com/mhale/smtpd v0.8.0
|
||||
github.com/minio/sio v0.3.0
|
||||
github.com/otiai10/copy v1.9.0
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/pkg/sftp v1.13.6-0.20221020054726-e4133ab7e9bd
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/minio/sio v0.3.1
|
||||
github.com/otiai10/copy v1.14.0
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rs/cors v1.8.3-0.20220619195839-da52b0701de5
|
||||
github.com/rs/xid v1.4.0
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/sftpgo/sdk v0.1.2
|
||||
github.com/shirou/gopsutil/v3 v3.22.10
|
||||
github.com/spf13/afero v1.9.3
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/viper v1.14.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/studio-b12/gowebdav v0.0.0-20221109171924-60ec5ad56012
|
||||
github.com/subosito/gotenv v1.4.1
|
||||
github.com/rs/cors v1.10.1
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/rs/zerolog v1.31.0
|
||||
github.com/sftpgo/sdk v0.1.6
|
||||
github.com/shirou/gopsutil/v3 v3.23.10
|
||||
github.com/spf13/afero v1.10.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/studio-b12/gowebdav v0.9.0
|
||||
github.com/subosito/gotenv v1.6.0
|
||||
github.com/unrolled/secure v1.13.0
|
||||
github.com/wagslane/go-password-validator v0.3.0
|
||||
github.com/xhit/go-simple-mail/v2 v2.13.0
|
||||
github.com/wneessen/go-mail v0.4.1-0.20230823094700-0bd5390e370d
|
||||
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.uber.org/automaxprocs v1.5.1
|
||||
gocloud.dev v0.27.0
|
||||
golang.org/x/crypto v0.3.0
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/oauth2 v0.2.0
|
||||
golang.org/x/sys v0.2.0
|
||||
golang.org/x/time v0.2.0
|
||||
google.golang.org/api v0.103.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
go.etcd.io/bbolt v1.3.8
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gocloud.dev v0.34.0
|
||||
golang.org/x/crypto v0.15.0
|
||||
golang.org/x/net v0.18.0
|
||||
golang.org/x/oauth2 v0.14.0
|
||||
golang.org/x/sys v0.14.0
|
||||
golang.org/x/term v0.14.0
|
||||
golang.org/x/time v0.4.0
|
||||
google.golang.org/api v0.150.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.107.0 // indirect
|
||||
cloud.google.com/go/compute v1.12.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.1 // indirect
|
||||
cloud.google.com/go/iam v0.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.1 // indirect
|
||||
cloud.google.com/go v0.110.10 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.5 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect
|
||||
github.com/aws/smithy-go v1.13.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.6.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.17.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1 // indirect
|
||||
github.com/aws/smithy-go v1.16.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-test/deep v1.0.8 // indirect
|
||||
github.com/goccy/go-json v0.9.11 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.0 // indirect
|
||||
github.com/lib/pq v1.10.7 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/miekg/dns v1.1.56 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.15.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
|
||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20221117111000-a0321143587c
|
||||
github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
|
||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20231109074321-cb261f488895
|
||||
)
|
||||
|
||||
BIN
img/Dendi_logo.png
Normal file
BIN
img/Dendi_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package acme
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Package acme provides automatic access to certificates from Let's Encrypt and any other ACME-based CA
|
||||
// The code here is largely coiped from https://github.com/go-acme/lego/tree/master/cmd
|
||||
@@ -46,8 +46,8 @@ import (
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/common"
|
||||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/ftpd"
|
||||
"github.com/drakkan/sftpgo/v2/internal/httpd"
|
||||
"github.com/drakkan/sftpgo/v2/internal/logger"
|
||||
"github.com/drakkan/sftpgo/v2/internal/telemetry"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
@@ -60,12 +60,27 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
config *Configuration
|
||||
scheduler *cron.Cron
|
||||
logMode int
|
||||
config *Configuration
|
||||
initialConfig Configuration
|
||||
scheduler *cron.Cron
|
||||
logMode int
|
||||
supportedKeyTypes = []string{
|
||||
string(certcrypto.EC256),
|
||||
string(certcrypto.EC384),
|
||||
string(certcrypto.RSA2048),
|
||||
string(certcrypto.RSA3072),
|
||||
string(certcrypto.RSA4096),
|
||||
string(certcrypto.RSA8192),
|
||||
}
|
||||
fnReloadHTTPDCerts func() error
|
||||
)
|
||||
|
||||
// GetCertificates tries to obtain the certificates for the configured domains
|
||||
// SetReloadHTTPDCertsFn set the function to call to reload HTTPD certificates
|
||||
func SetReloadHTTPDCertsFn(fn func() error) {
|
||||
fnReloadHTTPDCerts = fn
|
||||
}
|
||||
|
||||
// GetCertificates tries to obtain the certificates using the global configuration
|
||||
func GetCertificates() error {
|
||||
if config == nil {
|
||||
return errors.New("acme is disabled")
|
||||
@@ -73,6 +88,83 @@ func GetCertificates() error {
|
||||
return config.getCertificates()
|
||||
}
|
||||
|
||||
// GetCertificatesForConfig tries to obtain the certificates using the provided
|
||||
// configuration override. This is a NOOP if we already have certificates
|
||||
func GetCertificatesForConfig(c *dataprovider.ACMEConfigs, configDir string) error {
|
||||
if c.Domain == "" {
|
||||
acmeLog(logger.LevelDebug, "no domain configured, nothing to do")
|
||||
return nil
|
||||
}
|
||||
config := mergeConfig(getConfiguration(), c)
|
||||
if err := config.Initialize(configDir); err != nil {
|
||||
return err
|
||||
}
|
||||
hasCerts, err := config.hasCertificates(c.Domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to check if we already have certificates for domain %q: %w", c.Domain, err)
|
||||
}
|
||||
if hasCerts {
|
||||
return nil
|
||||
}
|
||||
return config.getCertificates()
|
||||
}
|
||||
|
||||
// GetHTTP01WebRoot returns the web root for HTTP-01 challenge
|
||||
func GetHTTP01WebRoot() string {
|
||||
return initialConfig.HTTP01Challenge.WebRoot
|
||||
}
|
||||
|
||||
func mergeConfig(config Configuration, c *dataprovider.ACMEConfigs) Configuration {
|
||||
config.Domains = []string{c.Domain}
|
||||
config.Email = c.Email
|
||||
config.HTTP01Challenge.Port = c.HTTP01Challenge.Port
|
||||
config.TLSALPN01Challenge.Port = 0
|
||||
return config
|
||||
}
|
||||
|
||||
// getConfiguration returns the configuration set using config file and env vars
|
||||
func getConfiguration() Configuration {
|
||||
return initialConfig
|
||||
}
|
||||
|
||||
func loadProviderConf(c Configuration) (Configuration, error) {
|
||||
configs, err := dataprovider.GetConfigs()
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("unable to load config from provider: %w", err)
|
||||
}
|
||||
configs.SetNilsToEmpty()
|
||||
if configs.ACME.Domain == "" {
|
||||
return c, nil
|
||||
}
|
||||
return mergeConfig(c, configs.ACME), nil
|
||||
}
|
||||
|
||||
// Initialize validates and set the configuration
|
||||
func Initialize(c Configuration, configDir string, checkRenew bool) error {
|
||||
config = nil
|
||||
initialConfig = c
|
||||
c, err := loadProviderConf(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
util.CertsBasePath = ""
|
||||
setLogMode(checkRenew)
|
||||
|
||||
if err := c.Initialize(configDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(c.Domains) == 0 {
|
||||
return nil
|
||||
}
|
||||
util.CertsBasePath = c.CertsPath
|
||||
acmeLog(logger.LevelInfo, "configured domains: %+v, certs base path %q", c.Domains, c.CertsPath)
|
||||
config = &c
|
||||
if checkRenew {
|
||||
return startScheduler()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTP01Challenge defines the configuration for HTTP-01 challenge type
|
||||
type HTTP01Challenge struct {
|
||||
Port int `json:"port" mapstructure:"port"`
|
||||
@@ -141,70 +233,52 @@ type Configuration struct {
|
||||
tempDir string
|
||||
}
|
||||
|
||||
// Initialize validates and set the configuration
|
||||
func (c *Configuration) Initialize(configDir string, checkRenew bool) error {
|
||||
config = nil
|
||||
setLogMode(checkRenew)
|
||||
// Initialize validates and initialize the configuration
|
||||
func (c *Configuration) Initialize(configDir string) error {
|
||||
c.checkDomains()
|
||||
if len(c.Domains) == 0 {
|
||||
acmeLog(logger.LevelInfo, "no domains configured, acme disabled")
|
||||
return nil
|
||||
}
|
||||
if c.Email == "" || !util.IsEmailValid(c.Email) {
|
||||
return fmt.Errorf("invalid email address %#v", c.Email)
|
||||
return fmt.Errorf("invalid email address %q", c.Email)
|
||||
}
|
||||
if c.RenewDays < 1 {
|
||||
return fmt.Errorf("invalid number of days remaining before renewal: %d", c.RenewDays)
|
||||
}
|
||||
supportedKeyTypes := []string{
|
||||
string(certcrypto.EC256),
|
||||
string(certcrypto.EC384),
|
||||
string(certcrypto.RSA2048),
|
||||
string(certcrypto.RSA4096),
|
||||
string(certcrypto.RSA8192),
|
||||
}
|
||||
if !util.Contains(supportedKeyTypes, c.KeyType) {
|
||||
return fmt.Errorf("invalid key type %#v", c.KeyType)
|
||||
return fmt.Errorf("invalid key type %q", c.KeyType)
|
||||
}
|
||||
caURL, err := url.Parse(c.CAEndpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid CA endopoint: %w", err)
|
||||
}
|
||||
if !util.IsFileInputValid(c.CertsPath) {
|
||||
return fmt.Errorf("invalid certs path %#v", c.CertsPath)
|
||||
return fmt.Errorf("invalid certs path %q", c.CertsPath)
|
||||
}
|
||||
if !filepath.IsAbs(c.CertsPath) {
|
||||
c.CertsPath = filepath.Join(configDir, c.CertsPath)
|
||||
}
|
||||
err = os.MkdirAll(c.CertsPath, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create certs path %#v: %w", c.CertsPath, err)
|
||||
return fmt.Errorf("unable to create certs path %q: %w", c.CertsPath, err)
|
||||
}
|
||||
c.tempDir = filepath.Join(c.CertsPath, "temp")
|
||||
err = os.MkdirAll(c.CertsPath, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create certs temp path %#v: %w", c.tempDir, err)
|
||||
return fmt.Errorf("unable to create certs temp path %q: %w", c.tempDir, err)
|
||||
}
|
||||
serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(caURL.Host)
|
||||
accountPath := filepath.Join(c.CertsPath, serverPath)
|
||||
err = os.MkdirAll(accountPath, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create account path %#v: %w", accountPath, err)
|
||||
return fmt.Errorf("unable to create account path %q: %w", accountPath, err)
|
||||
}
|
||||
c.accountConfigPath = filepath.Join(accountPath, c.Email+".json")
|
||||
c.accountKeyPath = filepath.Join(accountPath, c.Email+".key")
|
||||
c.lockPath = filepath.Join(c.CertsPath, "lock")
|
||||
|
||||
if err = c.validateChallenges(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acmeLog(logger.LevelInfo, "configured domains: %+v", c.Domains)
|
||||
config = c
|
||||
if checkRenew {
|
||||
return startScheduler()
|
||||
}
|
||||
return nil
|
||||
return c.validateChallenges()
|
||||
}
|
||||
|
||||
func (c *Configuration) validateChallenges() error {
|
||||
@@ -214,10 +288,7 @@ func (c *Configuration) validateChallenges() error {
|
||||
if err := c.HTTP01Challenge.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.TLSALPN01Challenge.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return c.TLSALPN01Challenge.validate()
|
||||
}
|
||||
|
||||
func (c *Configuration) checkDomains() {
|
||||
@@ -238,10 +309,10 @@ func (c *Configuration) setLockTime() error {
|
||||
lockTime := fmt.Sprintf("%v", util.GetTimeAsMsSinceEpoch(time.Now()))
|
||||
err := os.WriteFile(c.lockPath, []byte(lockTime), 0600)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to save lock time to %#v: %v", c.lockPath, err)
|
||||
acmeLog(logger.LevelError, "unable to save lock time to %q: %v", c.lockPath, err)
|
||||
return fmt.Errorf("unable to save lock time: %w", err)
|
||||
}
|
||||
acmeLog(logger.LevelDebug, "lock time saved: %#v", lockTime)
|
||||
acmeLog(logger.LevelDebug, "lock time saved: %q", lockTime)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -249,10 +320,10 @@ func (c *Configuration) getLockTime() (time.Time, error) {
|
||||
content, err := os.ReadFile(c.lockPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
acmeLog(logger.LevelDebug, "lock file %#v not found", c.lockPath)
|
||||
acmeLog(logger.LevelDebug, "lock file %q not found", c.lockPath)
|
||||
return time.Time{}, nil
|
||||
}
|
||||
acmeLog(logger.LevelError, "unable to read lock file %#v: %v", c.lockPath, err)
|
||||
acmeLog(logger.LevelError, "unable to read lock file %q: %v", c.lockPath, err)
|
||||
return time.Time{}, err
|
||||
}
|
||||
msec, err := strconv.ParseInt(strings.TrimSpace(string(content)), 10, 64)
|
||||
@@ -270,7 +341,7 @@ func (c *Configuration) saveAccount(account *account) error {
|
||||
}
|
||||
err = os.WriteFile(c.accountConfigPath, jsonBytes, 0600)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to save account to file %#v: %v", c.accountConfigPath, err)
|
||||
acmeLog(logger.LevelError, "unable to save account to file %q: %v", c.accountConfigPath, err)
|
||||
return fmt.Errorf("unable to save account: %w", err)
|
||||
}
|
||||
return nil
|
||||
@@ -285,7 +356,7 @@ func (c *Configuration) getAccount(privateKey crypto.PrivateKey) (account, error
|
||||
var account account
|
||||
fileBytes, err := os.ReadFile(c.accountConfigPath)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to read account from file %#v: %v", c.accountConfigPath, err)
|
||||
acmeLog(logger.LevelError, "unable to read account from file %q: %v", c.accountConfigPath, err)
|
||||
return account, fmt.Errorf("unable to read account from file: %w", err)
|
||||
}
|
||||
err = json.Unmarshal(fileBytes, &account)
|
||||
@@ -314,7 +385,7 @@ func (c *Configuration) getAccount(privateKey crypto.PrivateKey) (account, error
|
||||
func (c *Configuration) loadPrivateKey() (crypto.PrivateKey, error) {
|
||||
keyBytes, err := os.ReadFile(c.accountKeyPath)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to read account key from file %#v: %v", c.accountKeyPath, err)
|
||||
acmeLog(logger.LevelError, "unable to read account key from file %q: %v", c.accountKeyPath, err)
|
||||
return nil, fmt.Errorf("unable to read account key: %w", err)
|
||||
}
|
||||
|
||||
@@ -327,10 +398,10 @@ func (c *Configuration) loadPrivateKey() (crypto.PrivateKey, error) {
|
||||
case "EC PRIVATE KEY":
|
||||
privateKey, err = x509.ParseECPrivateKey(keyBlock.Bytes)
|
||||
default:
|
||||
err = fmt.Errorf("unknown private key type %#v", keyBlock.Type)
|
||||
err = fmt.Errorf("unknown private key type %q", keyBlock.Type)
|
||||
}
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to parse private key from file %#v: %v", c.accountKeyPath, err)
|
||||
acmeLog(logger.LevelError, "unable to parse private key from file %q: %v", c.accountKeyPath, err)
|
||||
return privateKey, fmt.Errorf("unable to parse private key: %w", err)
|
||||
}
|
||||
return privateKey, nil
|
||||
@@ -344,7 +415,7 @@ func (c *Configuration) generatePrivateKey() (crypto.PrivateKey, error) {
|
||||
}
|
||||
certOut, err := os.Create(c.accountKeyPath)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to save private key to file %#v: %v", c.accountKeyPath, err)
|
||||
acmeLog(logger.LevelError, "unable to save private key to file %q: %v", c.accountKeyPath, err)
|
||||
return nil, fmt.Errorf("unable to save private key: %w", err)
|
||||
}
|
||||
defer certOut.Close()
|
||||
@@ -363,25 +434,25 @@ func (c *Configuration) generatePrivateKey() (crypto.PrivateKey, error) {
|
||||
func (c *Configuration) getPrivateKey() (crypto.PrivateKey, error) {
|
||||
_, err := os.Stat(c.accountKeyPath)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
acmeLog(logger.LevelDebug, "private key file %#v does not exist, generating new private key", c.accountKeyPath)
|
||||
acmeLog(logger.LevelDebug, "private key file %q does not exist, generating new private key", c.accountKeyPath)
|
||||
return c.generatePrivateKey()
|
||||
}
|
||||
acmeLog(logger.LevelDebug, "loading private key from file %#v, stat error: %v", c.accountKeyPath, err)
|
||||
acmeLog(logger.LevelDebug, "loading private key from file %q, stat error: %v", c.accountKeyPath, err)
|
||||
return c.loadPrivateKey()
|
||||
}
|
||||
|
||||
func (c *Configuration) loadCertificatesForDomain(domain string) ([]*x509.Certificate, error) {
|
||||
domain = sanitizedDomain(domain)
|
||||
acmeLog(logger.LevelDebug, "loading certificates for domain %#v", domain)
|
||||
domain = util.SanitizeDomain(domain)
|
||||
acmeLog(logger.LevelDebug, "loading certificates for domain %q", domain)
|
||||
content, err := os.ReadFile(filepath.Join(c.CertsPath, domain+".crt"))
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to load certificates for domain %#v: %v", domain, err)
|
||||
return nil, fmt.Errorf("unable to load certificates for domain %#v: %w", domain, err)
|
||||
acmeLog(logger.LevelError, "unable to load certificates for domain %q: %v", domain, err)
|
||||
return nil, fmt.Errorf("unable to load certificates for domain %q: %w", domain, err)
|
||||
}
|
||||
certs, err := certcrypto.ParsePEMBundle(content)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to parse certificates for domain %#v: %v", domain, err)
|
||||
return certs, fmt.Errorf("unable to parse certificates for domain %#v: %w", domain, err)
|
||||
acmeLog(logger.LevelError, "unable to parse certificates for domain %q: %v", domain, err)
|
||||
return certs, fmt.Errorf("unable to parse certificates for domain %q: %w", domain, err)
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
@@ -393,7 +464,7 @@ func (c *Configuration) needRenewal(x509Cert *x509.Certificate, domain string) b
|
||||
}
|
||||
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
|
||||
if notAfter > c.RenewDays {
|
||||
acmeLog(logger.LevelDebug, "the certificate for domain %#v expires in %d days, no renewal", domain, notAfter)
|
||||
acmeLog(logger.LevelDebug, "the certificate for domain %q expires in %d days, no renewal", domain, notAfter)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -428,10 +499,10 @@ func (c *Configuration) setupChalleges(client *lego.Client) error {
|
||||
client.Challenge.Remove(challenge.DNS01)
|
||||
if c.HTTP01Challenge.isEnabled() {
|
||||
if c.HTTP01Challenge.WebRoot != "" {
|
||||
acmeLog(logger.LevelDebug, "configuring HTTP-01 web root challenge, path %#v", c.HTTP01Challenge.WebRoot)
|
||||
acmeLog(logger.LevelDebug, "configuring HTTP-01 web root challenge, path %q", c.HTTP01Challenge.WebRoot)
|
||||
providerServer, err := webroot.NewHTTPProvider(c.HTTP01Challenge.WebRoot)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to create HTTP-01 web root challenge provider from path %#v: %v",
|
||||
acmeLog(logger.LevelError, "unable to create HTTP-01 web root challenge provider from path %q: %v",
|
||||
c.HTTP01Challenge.WebRoot, err)
|
||||
return fmt.Errorf("unable to create HTTP-01 web root challenge provider: %w", err)
|
||||
}
|
||||
@@ -488,15 +559,20 @@ func (c *Configuration) tryRecoverRegistration(privateKey crypto.PrivateKey) (*r
|
||||
return client.Registration.ResolveAccountByKey()
|
||||
}
|
||||
|
||||
func (c *Configuration) obtainAndSaveCertificate(client *lego.Client, domain string) error {
|
||||
var domains []string
|
||||
func (c *Configuration) getCrtPath(domain string) string {
|
||||
return filepath.Join(c.CertsPath, domain+".crt")
|
||||
}
|
||||
|
||||
for _, d := range strings.Split(domain, ",") {
|
||||
d = strings.TrimSpace(d)
|
||||
if d != "" {
|
||||
domains = append(domains, d)
|
||||
}
|
||||
}
|
||||
func (c *Configuration) getKeyPath(domain string) string {
|
||||
return filepath.Join(c.CertsPath, domain+".key")
|
||||
}
|
||||
|
||||
func (c *Configuration) getResourcePath(domain string) string {
|
||||
return filepath.Join(c.CertsPath, domain+".json")
|
||||
}
|
||||
|
||||
func (c *Configuration) obtainAndSaveCertificate(client *lego.Client, domain string) error {
|
||||
domains := getDomains(domain)
|
||||
acmeLog(logger.LevelInfo, "requesting certificates for domains %+v", domains)
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
@@ -510,15 +586,15 @@ func (c *Configuration) obtainAndSaveCertificate(client *lego.Client, domain str
|
||||
acmeLog(logger.LevelError, "unable to obtain certificates for domains %+v: %v", domains, err)
|
||||
return fmt.Errorf("unable to obtain certificates: %w", err)
|
||||
}
|
||||
domain = sanitizedDomain(domain)
|
||||
err = os.WriteFile(filepath.Join(c.CertsPath, domain+".crt"), cert.Certificate, 0600)
|
||||
domain = util.SanitizeDomain(domain)
|
||||
err = os.WriteFile(c.getCrtPath(domain), cert.Certificate, 0600)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to save certificate for domain %v: %v", domain, err)
|
||||
acmeLog(logger.LevelError, "unable to save certificate for domain %s: %v", domain, err)
|
||||
return fmt.Errorf("unable to save certificate: %w", err)
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(c.CertsPath, domain+".key"), cert.PrivateKey, 0600)
|
||||
err = os.WriteFile(c.getKeyPath(domain), cert.PrivateKey, 0600)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to save private key for domain %v: %v", domain, err)
|
||||
acmeLog(logger.LevelError, "unable to save private key for domain %s: %v", domain, err)
|
||||
return fmt.Errorf("unable to save private key: %w", err)
|
||||
}
|
||||
jsonBytes, err := json.MarshalIndent(cert, "", "\t")
|
||||
@@ -526,7 +602,7 @@ func (c *Configuration) obtainAndSaveCertificate(client *lego.Client, domain str
|
||||
acmeLog(logger.LevelError, "unable to marshal certificate resources for domain %v: %v", domain, err)
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(c.CertsPath, domain+".json"), jsonBytes, 0600)
|
||||
err = os.WriteFile(c.getResourcePath(domain), jsonBytes, 0600)
|
||||
if err != nil {
|
||||
acmeLog(logger.LevelError, "unable to save certificate resources for domain %v: %v", domain, err)
|
||||
return fmt.Errorf("unable to save certificate resources: %w", err)
|
||||
@@ -536,6 +612,25 @@ func (c *Configuration) obtainAndSaveCertificate(client *lego.Client, domain str
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasCertificates returns true if certificates for the specified domain has already been issued
|
||||
func (c *Configuration) hasCertificates(domain string) (bool, error) {
|
||||
domain = util.SanitizeDomain(domain)
|
||||
if _, err := os.Stat(c.getCrtPath(domain)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
if _, err := os.Stat(c.getKeyPath(domain)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// getCertificates tries to obtain the certificates for the configured domains
|
||||
func (c *Configuration) getCertificates() error {
|
||||
account, client, err := c.setup()
|
||||
if err != nil {
|
||||
@@ -632,8 +727,10 @@ func (c *Configuration) renewCertificates() error {
|
||||
// at least one certificate has been renewed, sends a reload to all services that may be using certificates
|
||||
err = ftpd.ReloadCertificateMgr()
|
||||
acmeLog(logger.LevelInfo, "ftpd certificate manager reloaded , error: %v", err)
|
||||
err = httpd.ReloadCertificateMgr()
|
||||
acmeLog(logger.LevelInfo, "httpd certificates manager reloaded , error: %v", err)
|
||||
if fnReloadHTTPDCerts != nil {
|
||||
err = fnReloadHTTPDCerts()
|
||||
acmeLog(logger.LevelInfo, "httpd certificates manager reloaded , error: %v", err)
|
||||
}
|
||||
err = webdavd.ReloadCertificateMgr()
|
||||
acmeLog(logger.LevelInfo, "webdav certificates manager reloaded , error: %v", err)
|
||||
err = telemetry.ReloadCertificateMgr()
|
||||
@@ -655,8 +752,21 @@ func isDomainValid(domain string) (string, bool) {
|
||||
return domain, isValid
|
||||
}
|
||||
|
||||
func sanitizedDomain(domain string) string {
|
||||
return strings.NewReplacer(":", "_", "*", "_", ",", "_").Replace(domain)
|
||||
func getDomains(domain string) []string {
|
||||
var domains []string
|
||||
|
||||
delimiter := ","
|
||||
if !strings.Contains(domain, ",") && strings.Contains(domain, " ") {
|
||||
delimiter = " "
|
||||
}
|
||||
|
||||
for _, d := range strings.Split(domain, delimiter) {
|
||||
d = strings.TrimSpace(d)
|
||||
if d != "" {
|
||||
domains = append(domains, d)
|
||||
}
|
||||
}
|
||||
return util.RemoveDuplicates(domains, false)
|
||||
}
|
||||
|
||||
func stopScheduler() {
|
||||
@@ -669,10 +779,8 @@ func stopScheduler() {
|
||||
func startScheduler() error {
|
||||
stopScheduler()
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
randSecs := rand.Intn(59)
|
||||
|
||||
scheduler = cron.New()
|
||||
scheduler = cron.New(cron.WithLocation(time.UTC), cron.WithLogger(cron.DiscardLogger))
|
||||
_, err := scheduler.AddFunc(fmt.Sprintf("@every 12h0m%ds", randSecs), renewCertificates)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to schedule certificates renewal: %w", err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build bundle
|
||||
// +build bundle
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/acme"
|
||||
"github.com/drakkan/sftpgo/v2/internal/config"
|
||||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/logger"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
)
|
||||
@@ -49,10 +50,29 @@ renewed by the SFTPGo service
|
||||
logger.ErrorToConsole("Unable to initialize ACME, config load error: %v", err)
|
||||
return
|
||||
}
|
||||
kmsConfig := config.GetKMSConfig()
|
||||
err = kmsConfig.Initialize()
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("unable to initialize KMS: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
mfaConfig := config.GetMFAConfig()
|
||||
err = mfaConfig.Initialize()
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to initialize MFA: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
providerConf := config.GetProviderConf()
|
||||
err = dataprovider.Initialize(providerConf, configDir, false)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("error initializing data provider: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
acmeConfig := config.GetACMEConfig()
|
||||
err = acmeConfig.Initialize(configDir, false)
|
||||
err = acme.Initialize(acmeConfig, configDir, false)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to initialize ACME configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = acme.GetCertificates(); err != nil {
|
||||
logger.ErrorToConsole("Cannot get certificates: %v", err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build awscontainer
|
||||
// +build awscontainer
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !awscontainer
|
||||
// +build !awscontainer
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
@@ -76,7 +76,7 @@ Please take a look at the usage below to customize the options.`,
|
||||
providerConf.Actions.Hook = ""
|
||||
providerConf.Actions.ExecuteFor = nil
|
||||
providerConf.Actions.ExecuteOn = nil
|
||||
logger.InfoToConsole("Initializing provider: %#v config file: %#v", providerConf.Driver, viper.ConfigFileUsed())
|
||||
logger.InfoToConsole("Initializing provider: %q config file: %q", providerConf.Driver, viper.ConfigFileUsed())
|
||||
err = dataprovider.InitializeDatabase(providerConf, configDir)
|
||||
if err == nil {
|
||||
logger.InfoToConsole("Data provider successfully initialized/updated")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
120
internal/cmd/ping.go
Normal file
120
internal/cmd/ping.go
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, version 3.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/config"
|
||||
"github.com/drakkan/sftpgo/v2/internal/httpclient"
|
||||
"github.com/drakkan/sftpgo/v2/internal/httpd"
|
||||
"github.com/drakkan/sftpgo/v2/internal/logger"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
)
|
||||
|
||||
func getHealthzURLFromBindings(bindings []httpd.Binding) string {
|
||||
for _, b := range bindings {
|
||||
if b.Port > 0 && b.IsValid() {
|
||||
var url string
|
||||
if b.EnableHTTPS {
|
||||
url = "https://"
|
||||
} else {
|
||||
url = "http://"
|
||||
}
|
||||
if b.Address == "" {
|
||||
url += "127.0.0.1"
|
||||
} else {
|
||||
url += b.Address
|
||||
}
|
||||
url += fmt.Sprintf(":%d", b.Port)
|
||||
url += "/healthz"
|
||||
return url
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var (
|
||||
pingCmd = &cobra.Command{
|
||||
Use: "ping",
|
||||
Short: "Issues an health check to SFTPGo",
|
||||
Long: `This command is only useful in environments where system commands like
|
||||
"curl", "wget" and similar are not available.
|
||||
Checks over UNIX domain sockets are not supported`,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
logger.DisableLogger()
|
||||
logger.EnableConsoleLogger(zerolog.DebugLevel)
|
||||
configDir = util.CleanDirInput(configDir)
|
||||
err := config.LoadConfig(configDir, configFile)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Unable to load configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
httpConfig := config.GetHTTPConfig()
|
||||
err = httpConfig.Initialize(configDir)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("error initializing http client: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
telemetryConfig := config.GetTelemetryConfig()
|
||||
var url string
|
||||
if telemetryConfig.BindPort > 0 {
|
||||
if telemetryConfig.CertificateFile != "" && telemetryConfig.CertificateKeyFile != "" {
|
||||
url += "https://"
|
||||
} else {
|
||||
url += "http://"
|
||||
}
|
||||
if telemetryConfig.BindAddress == "" {
|
||||
url += "127.0.0.1"
|
||||
} else {
|
||||
url += telemetryConfig.BindAddress
|
||||
}
|
||||
url += fmt.Sprintf(":%d", telemetryConfig.BindPort)
|
||||
url += "/healthz"
|
||||
}
|
||||
if url == "" {
|
||||
httpdConfig := config.GetHTTPDConfig()
|
||||
url = getHealthzURLFromBindings(httpdConfig.Bindings)
|
||||
}
|
||||
if url == "" {
|
||||
logger.ErrorToConsole("no suitable configuration found, please enable the telemetry server or REST API over HTTP/S")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logger.DebugToConsole("Health Check URL %q", url)
|
||||
resp, err := httpclient.RetryableGet(url)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to connect to SFTPGo: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logger.ErrorToConsole("Unexpected status code %d", resp.StatusCode)
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.InfoToConsole("OK")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
addConfigFlags(pingCmd)
|
||||
rootCmd.AddCommand(pingCmd)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !noportable
|
||||
// +build !noportable
|
||||
@@ -39,10 +39,9 @@ import (
|
||||
var (
|
||||
directoryToServe string
|
||||
portableSFTPDPort int
|
||||
portableAdvertiseService bool
|
||||
portableAdvertiseCredentials bool
|
||||
portableUsername string
|
||||
portablePassword string
|
||||
portablePasswordFile string
|
||||
portableStartDir string
|
||||
portableLogFile string
|
||||
portableLogLevel string
|
||||
@@ -148,7 +147,7 @@ Please take a look at the usage below to customize the serving parameters`,
|
||||
_, err := common.NewCertManager(keyPairs, filepath.Clean(defaultConfigDir),
|
||||
"FTP portable")
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to load FTPS key pair, cert file %#v key file %#v error: %v\n",
|
||||
fmt.Printf("Unable to load FTPS key pair, cert file %q key file %q error: %v\n",
|
||||
portableFTPSCert, portableFTPSKey, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -164,11 +163,20 @@ Please take a look at the usage below to customize the serving parameters`,
|
||||
_, err := common.NewCertManager(keyPairs, filepath.Clean(defaultConfigDir),
|
||||
"WebDAV portable")
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to load WebDAV key pair, cert file %#v key file %#v error: %v\n",
|
||||
fmt.Printf("Unable to load WebDAV key pair, cert file %q key file %q error: %v\n",
|
||||
portableWebDAVCert, portableWebDAVKey, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
pwd := portablePassword
|
||||
if portablePasswordFile != "" {
|
||||
content, err := os.ReadFile(portablePasswordFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to read password file %q: %v", portablePasswordFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
pwd = strings.TrimSpace(string(content))
|
||||
}
|
||||
service.SetGraceTime(graceTime)
|
||||
service := service.Service{
|
||||
ConfigDir: filepath.Clean(defaultConfigDir),
|
||||
@@ -185,7 +193,7 @@ Please take a look at the usage below to customize the serving parameters`,
|
||||
PortableUser: dataprovider.User{
|
||||
BaseUser: sdk.BaseUser{
|
||||
Username: portableUsername,
|
||||
Password: portablePassword,
|
||||
Password: pwd,
|
||||
PublicKeys: portablePublicKeys,
|
||||
Permissions: permissions,
|
||||
HomeDir: portableDir,
|
||||
@@ -259,7 +267,7 @@ Please take a look at the usage below to customize the serving parameters`,
|
||||
},
|
||||
}
|
||||
err := service.StartPortableMode(portableSFTPDPort, portableFTPDPort, portableWebDAVPort, portableSSHCommands,
|
||||
portableAdvertiseService, portableAdvertiseCredentials, portableFTPSCert, portableFTPSKey, portableWebDAVCert,
|
||||
portableFTPSCert, portableFTPSKey, portableWebDAVCert,
|
||||
portableWebDAVKey)
|
||||
if err == nil {
|
||||
service.Wait()
|
||||
@@ -297,6 +305,9 @@ including scp
|
||||
value`)
|
||||
portableCmd.Flags().StringVarP(&portablePassword, "password", "p", "", `Leave empty to use an auto generated
|
||||
value`)
|
||||
portableCmd.Flags().StringVar(&portablePasswordFile, "password-file", "", `Read the password from the specified
|
||||
file path. Leave empty to use an auto
|
||||
generated value`)
|
||||
portableCmd.Flags().StringVarP(&portableLogFile, logFilePathFlag, "l", "", "Leave empty to disable logging")
|
||||
portableCmd.Flags().StringVar(&portableLogLevel, logLevelFlag, defaultLogLevel, `Set the log level.
|
||||
Supported values:
|
||||
@@ -318,14 +329,6 @@ For example: "/somedir::*.jpg,a*b?.png"`)
|
||||
The format is:
|
||||
/dir::pattern1,pattern2.
|
||||
For example: "/somedir::*.jpg,a*b?.png"`)
|
||||
portableCmd.Flags().BoolVarP(&portableAdvertiseService, "advertise-service", "S", false,
|
||||
`Advertise configured services using
|
||||
multicast DNS`)
|
||||
portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false,
|
||||
`If the SFTP/FTP service is
|
||||
advertised via multicast DNS, this
|
||||
flag allows to put username/password
|
||||
inside the advertised TXT record`)
|
||||
portableCmd.Flags().StringVarP(&portableFsProvider, "fs-provider", "f", "osfs", `osfs => local filesystem (legacy value: 0)
|
||||
s3fs => AWS S3 compatible (legacy: 1)
|
||||
gcsfs => Google Cloud Storage (legacy: 2)
|
||||
@@ -477,7 +480,7 @@ func getFileContents(name string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
if fi.Size() > 1048576 {
|
||||
return "", fmt.Errorf("%#v is too big %v/1048576 bytes", name, fi.Size())
|
||||
return "", fmt.Errorf("%q is too big %v/1048576 bytes", name, fi.Size())
|
||||
}
|
||||
contents, err := os.ReadFile(name)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build noportable
|
||||
// +build noportable
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
@@ -45,7 +45,7 @@ Please take a look at the usage below to customize the options.`,
|
||||
configDir = util.CleanDirInput(configDir)
|
||||
err := config.LoadConfig(configDir, configFile)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Unable to initialize data provider, config load error: %v", err)
|
||||
logger.WarnToConsole("Unable to load configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
kmsConfig := config.GetKMSConfig()
|
||||
@@ -56,7 +56,7 @@ Please take a look at the usage below to customize the options.`,
|
||||
}
|
||||
providerConf := config.GetProviderConf()
|
||||
if !resetProviderForce {
|
||||
logger.WarnToConsole("You are about to delete all the SFTPGo data for provider %#v, config file: %#v",
|
||||
logger.WarnToConsole("You are about to delete all the SFTPGo data for provider %q, config file: %q",
|
||||
providerConf.Driver, viper.ConfigFileUsed())
|
||||
logger.WarnToConsole("Are you sure? (Y/n)")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
@@ -70,7 +70,7 @@ Please take a look at the usage below to customize the options.`,
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
logger.InfoToConsole("Resetting provider: %#v, config file: %#v", providerConf.Driver, viper.ConfigFileUsed())
|
||||
logger.InfoToConsole("Resetting provider: %q, config file: %q", providerConf.Driver, viper.ConfigFileUsed())
|
||||
err = dataprovider.ResetDatabase(providerConf, configDir)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Error resetting provider: %v", err)
|
||||
|
||||
116
internal/cmd/resetpwd.go
Normal file
116
internal/cmd/resetpwd.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, version 3.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/config"
|
||||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/logger"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
)
|
||||
|
||||
var (
|
||||
resetPwdAdmin string
|
||||
resetPwdCmd = &cobra.Command{
|
||||
Use: "resetpwd",
|
||||
Short: "Reset the password for the specified administrator",
|
||||
Long: `This command reads the data provider connection details from the specified
|
||||
configuration file and resets the password for the specified administrator.
|
||||
This command is not supported for the memory provider.
|
||||
For embedded providers like bolt and SQLite you should stop the running SFTPGo
|
||||
instance to avoid database corruption.
|
||||
|
||||
Please take a look at the usage below to customize the options.`,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
logger.DisableLogger()
|
||||
logger.EnableConsoleLogger(zerolog.DebugLevel)
|
||||
configDir = util.CleanDirInput(configDir)
|
||||
err := config.LoadConfig(configDir, configFile)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Unable to load configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
kmsConfig := config.GetKMSConfig()
|
||||
err = kmsConfig.Initialize()
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("unable to initialize KMS: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
mfaConfig := config.GetMFAConfig()
|
||||
err = mfaConfig.Initialize()
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to initialize MFA: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
providerConf := config.GetProviderConf()
|
||||
if providerConf.Driver == dataprovider.MemoryDataProviderName {
|
||||
logger.ErrorToConsole("memory provider is not supported")
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.InfoToConsole("Initializing provider: %q config file: %q", providerConf.Driver, viper.ConfigFileUsed())
|
||||
err = dataprovider.Initialize(providerConf, configDir, false)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to initialize data provider: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
admin, err := dataprovider.AdminExists(resetPwdAdmin)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to get admin %q: %v", resetPwdAdmin, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Enter Password: ")
|
||||
pwd, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to read the password: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("")
|
||||
fmt.Printf("Confirm Password: ")
|
||||
confirmPwd, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to read the password: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("")
|
||||
if !bytes.Equal(pwd, confirmPwd) {
|
||||
logger.ErrorToConsole("Passwords do not match")
|
||||
os.Exit(1)
|
||||
}
|
||||
admin.Password = string(pwd)
|
||||
if err := dataprovider.UpdateAdmin(&admin, dataprovider.ActionExecutorSystem, "", ""); err != nil {
|
||||
logger.ErrorToConsole("Unable to update password: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.InfoToConsole("Password updated for admin %q", resetPwdAdmin)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
addConfigFlags(resetPwdCmd)
|
||||
resetPwdCmd.Flags().StringVar(&resetPwdAdmin, "admin", "", `Administrator username whose password to reset`)
|
||||
resetPwdCmd.MarkFlagRequired("admin") //nolint:errcheck
|
||||
|
||||
rootCmd.AddCommand(resetPwdCmd)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
@@ -40,14 +40,14 @@ Please take a look at the usage below to customize the options.`,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
logger.DisableLogger()
|
||||
logger.EnableConsoleLogger(zerolog.DebugLevel)
|
||||
if revertProviderTargetVersion != 19 {
|
||||
logger.WarnToConsole("Unsupported target version, 19 is the only supported one")
|
||||
if revertProviderTargetVersion != 23 {
|
||||
logger.WarnToConsole("Unsupported target version, 23 is the only supported one")
|
||||
os.Exit(1)
|
||||
}
|
||||
configDir = util.CleanDirInput(configDir)
|
||||
err := config.LoadConfig(configDir, configFile)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Unable to initialize data provider, config load error: %v", err)
|
||||
logger.WarnToConsole("Unable to load configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
kmsConfig := config.GetKMSConfig()
|
||||
@@ -57,7 +57,7 @@ Please take a look at the usage below to customize the options.`,
|
||||
os.Exit(1)
|
||||
}
|
||||
providerConf := config.GetProviderConf()
|
||||
logger.InfoToConsole("Reverting provider: %#v config file: %#v target version %v", providerConf.Driver,
|
||||
logger.InfoToConsole("Reverting provider: %q config file: %q target version %d", providerConf.Driver,
|
||||
viper.ConfigFileUsed(), revertProviderTargetVersion)
|
||||
err = dataprovider.RevertDatabase(providerConf, configDir, revertProviderTargetVersion)
|
||||
if err != nil {
|
||||
@@ -71,7 +71,7 @@ Please take a look at the usage below to customize the options.`,
|
||||
|
||||
func init() {
|
||||
addConfigFlags(revertProviderCmd)
|
||||
revertProviderCmd.Flags().IntVar(&revertProviderTargetVersion, "to-version", 19, `19 means the version supported in v2.3.x`)
|
||||
revertProviderCmd.Flags().IntVar(&revertProviderTargetVersion, "to-version", 23, `23 means the version supported in v2.4.x`)
|
||||
|
||||
rootCmd.AddCommand(revertProviderCmd)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Package cmd provides Command Line Interface support
|
||||
package cmd
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/config"
|
||||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/logger"
|
||||
"github.com/drakkan/sftpgo/v2/internal/smtp"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
@@ -39,22 +40,29 @@ If the SMTP configuration is correct you should receive this email.`,
|
||||
configDir = util.CleanDirInput(configDir)
|
||||
err := config.LoadConfig(configDir, configFile)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Unable to initialize data provider, config load error: %v", err)
|
||||
logger.ErrorToConsole("Unable to load configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
providerConf := config.GetProviderConf()
|
||||
err = dataprovider.Initialize(providerConf, configDir, false)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("error initializing data provider: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
smtpConfig := config.GetSMTPConfig()
|
||||
err = smtpConfig.Initialize(configDir)
|
||||
smtpConfig.Debug = 1
|
||||
err = smtpConfig.Initialize(configDir, false)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("unable to initialize SMTP configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = smtp.SendEmail([]string{smtpTestRecipient}, "SFTPGo - Testing Email Settings", "It appears your SFTPGo email is setup correctly!",
|
||||
err = smtp.SendEmail([]string{smtpTestRecipient}, nil, "SFTPGo - Testing Email Settings", "It appears your SFTPGo email is setup correctly!",
|
||||
smtp.EmailContentTypeTextPlain)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Error sending email: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.InfoToConsole("No errors were reported while sending an email. Please check your inbox to make sure.")
|
||||
logger.InfoToConsole("No errors were reported while sending the test email. Please check your inbox to make sure.")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
@@ -78,22 +78,13 @@ Command-line flags should be specified in the Subsystem declaration.
|
||||
}
|
||||
username := osUser.Username
|
||||
homedir := osUser.HomeDir
|
||||
logger.Info(logSender, connectionID, "starting SFTPGo %v as subsystem, user %#v home dir %#v config dir %#v base home dir %#v",
|
||||
logger.Info(logSender, connectionID, "starting SFTPGo %v as subsystem, user %q home dir %q config dir %q base home dir %q",
|
||||
version.Get(), username, homedir, configDir, baseHomeDir)
|
||||
err = config.LoadConfig(configDir, configFile)
|
||||
if err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to load configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dataProviderConf := config.GetProviderConf()
|
||||
commonConfig := config.GetCommonConfig()
|
||||
// idle connection are managed externally
|
||||
commonConfig.IdleTimeout = 0
|
||||
config.SetCommonConfig(commonConfig)
|
||||
if err := common.Initialize(config.GetCommonConfig(), dataProviderConf.GetShared()); err != nil {
|
||||
logger.Error(logSender, connectionID, "%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
kmsConfig := config.GetKMSConfig()
|
||||
if err := kmsConfig.Initialize(); err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to initialize KMS: %v", err)
|
||||
@@ -105,18 +96,9 @@ Command-line flags should be specified in the Subsystem declaration.
|
||||
logger.Error(logSender, "", "unable to initialize MFA: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := plugin.Initialize(config.GetPluginsConfig(), logLevel); err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to initialize plugin system: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
smtpConfig := config.GetSMTPConfig()
|
||||
err = smtpConfig.Initialize(configDir)
|
||||
if err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to initialize SMTP configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dataProviderConf := config.GetProviderConf()
|
||||
if dataProviderConf.Driver == dataprovider.SQLiteDataProviderName || dataProviderConf.Driver == dataprovider.BoltDataProviderName {
|
||||
logger.Debug(logSender, connectionID, "data provider %#v not supported in subsystem mode, using %#v provider",
|
||||
logger.Debug(logSender, connectionID, "data provider %q not supported in subsystem mode, using %q provider",
|
||||
dataProviderConf.Driver, dataprovider.MemoryDataProviderName)
|
||||
dataProviderConf.Driver = dataprovider.MemoryDataProviderName
|
||||
dataProviderConf.Name = ""
|
||||
@@ -127,6 +109,24 @@ Command-line flags should be specified in the Subsystem declaration.
|
||||
logger.Error(logSender, connectionID, "unable to initialize the data provider: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := plugin.Initialize(config.GetPluginsConfig(), logLevel); err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to initialize plugin system: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
smtpConfig := config.GetSMTPConfig()
|
||||
err = smtpConfig.Initialize(configDir, false)
|
||||
if err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to initialize SMTP configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
commonConfig := config.GetCommonConfig()
|
||||
// idle connection are managed externally
|
||||
commonConfig.IdleTimeout = 0
|
||||
config.SetCommonConfig(commonConfig)
|
||||
if err := common.Initialize(config.GetCommonConfig(), dataProviderConf.GetShared()); err != nil {
|
||||
logger.Error(logSender, connectionID, "%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
httpConfig := config.GetHTTPConfig()
|
||||
if err := httpConfig.Initialize(configDir); err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to initialize http client: %v", err)
|
||||
@@ -137,14 +137,14 @@ Command-line flags should be specified in the Subsystem declaration.
|
||||
logger.Error(logSender, connectionID, "unable to initialize commands configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
user, err := dataprovider.UserExists(username)
|
||||
user, err := dataprovider.UserExists(username, "")
|
||||
if err == nil {
|
||||
if user.HomeDir != filepath.Clean(homedir) && !preserveHomeDir {
|
||||
// update the user
|
||||
user.HomeDir = filepath.Clean(homedir)
|
||||
err = dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSystem, "")
|
||||
err = dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSystem, "", "")
|
||||
if err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to update user %#v: %v", username, err)
|
||||
logger.Error(logSender, connectionID, "unable to update user %q: %v", username, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -155,19 +155,19 @@ Command-line flags should be specified in the Subsystem declaration.
|
||||
} else {
|
||||
user.HomeDir = filepath.Clean(homedir)
|
||||
}
|
||||
logger.Debug(logSender, connectionID, "home dir for new user %#v", user.HomeDir)
|
||||
logger.Debug(logSender, connectionID, "home dir for new user %q", user.HomeDir)
|
||||
user.Password = connectionID
|
||||
user.Permissions = make(map[string][]string)
|
||||
user.Permissions["/"] = []string{dataprovider.PermAny}
|
||||
err = dataprovider.AddUser(&user, dataprovider.ActionExecutorSystem, "")
|
||||
err = dataprovider.AddUser(&user, dataprovider.ActionExecutorSystem, "", "")
|
||||
if err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to add user %#v: %v", username, err)
|
||||
logger.Error(logSender, connectionID, "unable to add user %q: %v", username, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
err = user.LoadAndApplyGroupSettings()
|
||||
if err != nil {
|
||||
logger.Error(logSender, connectionID, "unable to apply group settings for user %#v: %v", username, err)
|
||||
logger.Error(logSender, connectionID, "unable to apply group settings for user %q: %v", username, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = sftpd.ServeSubSystemConnection(&user, connectionID, os.Stdin, os.Stdout)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
@@ -38,7 +38,7 @@ var (
|
||||
fmt.Printf("Error querying service status: %v\r\n", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("Service status: %#v\r\n", status.String())
|
||||
fmt.Printf("Service status: %q\r\n", status.String())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Package command provides command configuration for SFTPGo hooks
|
||||
package command
|
||||
@@ -96,23 +96,23 @@ func (c Config) Initialize() error {
|
||||
}
|
||||
for _, env := range c.Env {
|
||||
if len(strings.SplitN(env, "=", 2)) != 2 {
|
||||
return fmt.Errorf("invalid env var %#v", env)
|
||||
return fmt.Errorf("invalid env var %q", env)
|
||||
}
|
||||
}
|
||||
for idx, cmd := range c.Commands {
|
||||
if cmd.Path == "" {
|
||||
return fmt.Errorf("invalid path %#v", cmd.Path)
|
||||
return fmt.Errorf("invalid path %q", cmd.Path)
|
||||
}
|
||||
if cmd.Timeout == 0 {
|
||||
c.Commands[idx].Timeout = c.Timeout
|
||||
} else {
|
||||
if cmd.Timeout < minTimeout || cmd.Timeout > maxTimeout {
|
||||
return fmt.Errorf("invalid timeout %v for command %#v", cmd.Timeout, cmd.Path)
|
||||
return fmt.Errorf("invalid timeout %v for command %q", cmd.Timeout, cmd.Path)
|
||||
}
|
||||
}
|
||||
for _, env := range cmd.Env {
|
||||
if len(strings.SplitN(env, "=", 2)) != 2 {
|
||||
return fmt.Errorf("invalid env var %#v for command %#v", env, cmd.Path)
|
||||
return fmt.Errorf("invalid env var %q for command %q", env, cmd.Path)
|
||||
}
|
||||
}
|
||||
// don't validate args, we allow to pass empty arguments
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package command
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package common
|
||||
|
||||
@@ -41,8 +41,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errUnconfiguredAction = errors.New("no hook is configured for this action")
|
||||
errNoHook = errors.New("unable to execute action, no hook defined")
|
||||
errUnexpectedHTTResponse = errors.New("unexpected HTTP hook response code")
|
||||
hooksConcurrencyGuard = make(chan struct{}, 150)
|
||||
activeHooks atomic.Int32
|
||||
@@ -80,38 +78,57 @@ func InitializeActionHandler(handler ActionHandler) {
|
||||
actionHandler = handler
|
||||
}
|
||||
|
||||
func handleUnconfiguredPreAction(operation string) error {
|
||||
// for pre-delete we execute the internal handling on error, so we must return errUnconfiguredAction.
|
||||
// Other pre action will deny the operation on error so if we have no configuration we must return
|
||||
// a nil error
|
||||
if operation == operationPreDelete {
|
||||
return errUnconfiguredAction
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecutePreAction executes a pre-* action and returns the result
|
||||
func ExecutePreAction(conn *BaseConnection, operation, filePath, virtualPath string, fileSize int64, openFlags int) error {
|
||||
// ExecutePreAction executes a pre-* action and returns the result.
|
||||
// The returned status has the following meaning:
|
||||
// - 0 not executed
|
||||
// - 1 executed using an external hook
|
||||
// - 2 executed using the event manager
|
||||
func ExecutePreAction(conn *BaseConnection, operation, filePath, virtualPath string, fileSize int64, openFlags int) (int, error) {
|
||||
var event *notifier.FsEvent
|
||||
hasNotifiersPlugin := plugin.Handler.HasNotifiers()
|
||||
hasHook := util.Contains(Config.Actions.ExecuteOn, operation)
|
||||
if !hasHook && !hasNotifiersPlugin {
|
||||
return handleUnconfiguredPreAction(operation)
|
||||
hasRules := eventManager.hasFsRules()
|
||||
if !hasHook && !hasNotifiersPlugin && !hasRules {
|
||||
return 0, nil
|
||||
}
|
||||
event = newActionNotification(&conn.User, operation, filePath, virtualPath, "", "", "",
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, openFlags, nil)
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, openFlags, conn.getNotificationStatus(nil), 0)
|
||||
if hasNotifiersPlugin {
|
||||
plugin.Handler.NotifyFsEvent(event)
|
||||
}
|
||||
if hasRules {
|
||||
params := EventParams{
|
||||
Name: event.Username,
|
||||
Groups: conn.User.Groups,
|
||||
Event: event.Action,
|
||||
Status: event.Status,
|
||||
VirtualPath: event.VirtualPath,
|
||||
FsPath: event.Path,
|
||||
VirtualTargetPath: event.VirtualTargetPath,
|
||||
FsTargetPath: event.TargetPath,
|
||||
ObjectName: path.Base(event.VirtualPath),
|
||||
FileSize: event.FileSize,
|
||||
Protocol: event.Protocol,
|
||||
IP: event.IP,
|
||||
Role: event.Role,
|
||||
Timestamp: event.Timestamp,
|
||||
Email: conn.User.Email,
|
||||
Object: nil,
|
||||
}
|
||||
executedSync, err := eventManager.handleFsEvent(params)
|
||||
if executedSync {
|
||||
return 2, err
|
||||
}
|
||||
}
|
||||
if !hasHook {
|
||||
return handleUnconfiguredPreAction(operation)
|
||||
return 0, nil
|
||||
}
|
||||
return actionHandler.Handle(event)
|
||||
}
|
||||
|
||||
// ExecuteActionNotification executes the defined hook, if any, for the specified action
|
||||
func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtualPath, target, virtualTarget, sshCmd string,
|
||||
fileSize int64, err error,
|
||||
fileSize int64, err error, elapsed int64,
|
||||
) error {
|
||||
hasNotifiersPlugin := plugin.Handler.HasNotifiers()
|
||||
hasHook := util.Contains(Config.Actions.ExecuteOn, operation)
|
||||
@@ -120,11 +137,10 @@ func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtua
|
||||
return nil
|
||||
}
|
||||
notification := newActionNotification(&conn.User, operation, filePath, virtualPath, target, virtualTarget, sshCmd,
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, 0, err)
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, 0, conn.getNotificationStatus(err), elapsed)
|
||||
if hasNotifiersPlugin {
|
||||
plugin.Handler.NotifyFsEvent(notification)
|
||||
}
|
||||
var errRes error
|
||||
if hasRules {
|
||||
params := EventParams{
|
||||
Name: notification.Username,
|
||||
@@ -137,44 +153,47 @@ func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtua
|
||||
FsTargetPath: notification.TargetPath,
|
||||
ObjectName: path.Base(notification.VirtualPath),
|
||||
FileSize: notification.FileSize,
|
||||
Elapsed: notification.Elapsed,
|
||||
Protocol: notification.Protocol,
|
||||
IP: notification.IP,
|
||||
Role: notification.Role,
|
||||
Timestamp: notification.Timestamp,
|
||||
Email: conn.User.Email,
|
||||
Object: nil,
|
||||
}
|
||||
if err != nil {
|
||||
params.AddError(fmt.Errorf("%q failed: %w", params.Event, err))
|
||||
}
|
||||
errRes = eventManager.handleFsEvent(params)
|
||||
executedSync, err := eventManager.handleFsEvent(params)
|
||||
if executedSync {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if hasHook {
|
||||
if util.Contains(Config.Actions.ExecuteSync, operation) {
|
||||
if errHook := actionHandler.Handle(notification); errHook != nil {
|
||||
errRes = errHook
|
||||
}
|
||||
} else {
|
||||
go func() {
|
||||
startNewHook()
|
||||
defer hookEnded()
|
||||
|
||||
actionHandler.Handle(notification) //nolint:errcheck
|
||||
}()
|
||||
_, err := actionHandler.Handle(notification)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
startNewHook()
|
||||
defer hookEnded()
|
||||
|
||||
actionHandler.Handle(notification) //nolint:errcheck
|
||||
}()
|
||||
}
|
||||
return errRes
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionHandler handles a notification for a Protocol Action.
|
||||
type ActionHandler interface {
|
||||
Handle(notification *notifier.FsEvent) error
|
||||
Handle(notification *notifier.FsEvent) (int, error)
|
||||
}
|
||||
|
||||
func newActionNotification(
|
||||
user *dataprovider.User,
|
||||
operation, filePath, virtualPath, target, virtualTarget, sshCmd, protocol, ip, sessionID string,
|
||||
fileSize int64,
|
||||
openFlags int,
|
||||
err error,
|
||||
openFlags, status int, elapsed int64,
|
||||
) *notifier.FsEvent {
|
||||
var bucket, endpoint string
|
||||
|
||||
@@ -209,39 +228,43 @@ func newActionNotification(
|
||||
FsProvider: int(fsConfig.Provider),
|
||||
Bucket: bucket,
|
||||
Endpoint: endpoint,
|
||||
Status: getNotificationStatus(err),
|
||||
Status: status,
|
||||
Protocol: protocol,
|
||||
IP: ip,
|
||||
SessionID: sessionID,
|
||||
OpenFlags: openFlags,
|
||||
Role: user.Role,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Elapsed: elapsed,
|
||||
}
|
||||
}
|
||||
|
||||
type defaultActionHandler struct{}
|
||||
|
||||
func (h *defaultActionHandler) Handle(event *notifier.FsEvent) error {
|
||||
func (h *defaultActionHandler) Handle(event *notifier.FsEvent) (int, error) {
|
||||
if !util.Contains(Config.Actions.ExecuteOn, event.Action) {
|
||||
return errUnconfiguredAction
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if Config.Actions.Hook == "" {
|
||||
logger.Warn(event.Protocol, "", "Unable to send notification, no hook is defined")
|
||||
|
||||
return errNoHook
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(Config.Actions.Hook, "http") {
|
||||
return h.handleHTTP(event)
|
||||
err := h.handleHTTP(event)
|
||||
return 1, err
|
||||
}
|
||||
|
||||
return h.handleCommand(event)
|
||||
err := h.handleCommand(event)
|
||||
return 1, err
|
||||
}
|
||||
|
||||
func (h *defaultActionHandler) handleHTTP(event *notifier.FsEvent) error {
|
||||
u, err := url.Parse(Config.Actions.Hook)
|
||||
if err != nil {
|
||||
logger.Error(event.Protocol, "", "Invalid hook %#v for operation %#v: %v",
|
||||
logger.Error(event.Protocol, "", "Invalid hook %q for operation %q: %v",
|
||||
Config.Actions.Hook, event.Action, err)
|
||||
return err
|
||||
}
|
||||
@@ -270,7 +293,7 @@ func (h *defaultActionHandler) handleHTTP(event *notifier.FsEvent) error {
|
||||
|
||||
func (h *defaultActionHandler) handleCommand(event *notifier.FsEvent) error {
|
||||
if !filepath.IsAbs(Config.Actions.Hook) {
|
||||
err := fmt.Errorf("invalid notification command %#v", Config.Actions.Hook)
|
||||
err := fmt.Errorf("invalid notification command %q", Config.Actions.Hook)
|
||||
logger.Warn(event.Protocol, "", "unable to execute notification command: %v", err)
|
||||
|
||||
return err
|
||||
@@ -286,7 +309,7 @@ func (h *defaultActionHandler) handleCommand(event *notifier.FsEvent) error {
|
||||
startTime := time.Now()
|
||||
err := cmd.Run()
|
||||
|
||||
logger.Debug(event.Protocol, "", "executed command %#v, elapsed: %v, error: %v",
|
||||
logger.Debug(event.Protocol, "", "executed command %q, elapsed: %s, error: %v",
|
||||
Config.Actions.Hook, time.Since(startTime), err)
|
||||
|
||||
return err
|
||||
@@ -302,6 +325,7 @@ func notificationAsEnvVars(event *notifier.FsEvent) []string {
|
||||
fmt.Sprintf("SFTPGO_ACTION_VIRTUAL_TARGET=%s", event.VirtualTargetPath),
|
||||
fmt.Sprintf("SFTPGO_ACTION_SSH_CMD=%s", event.SSHCmd),
|
||||
fmt.Sprintf("SFTPGO_ACTION_FILE_SIZE=%d", event.FileSize),
|
||||
fmt.Sprintf("SFTPGO_ACTION_ELAPSED=%d", event.Elapsed),
|
||||
fmt.Sprintf("SFTPGO_ACTION_FS_PROVIDER=%d", event.FsProvider),
|
||||
fmt.Sprintf("SFTPGO_ACTION_BUCKET=%s", event.Bucket),
|
||||
fmt.Sprintf("SFTPGO_ACTION_ENDPOINT=%s", event.Endpoint),
|
||||
@@ -311,15 +335,6 @@ func notificationAsEnvVars(event *notifier.FsEvent) []string {
|
||||
fmt.Sprintf("SFTPGO_ACTION_SESSION_ID=%s", event.SessionID),
|
||||
fmt.Sprintf("SFTPGO_ACTION_OPEN_FLAGS=%d", event.OpenFlags),
|
||||
fmt.Sprintf("SFTPGO_ACTION_TIMESTAMP=%d", event.Timestamp),
|
||||
fmt.Sprintf("SFTPGO_ACTION_ROLE=%s", event.Role),
|
||||
}
|
||||
}
|
||||
|
||||
func getNotificationStatus(err error) int {
|
||||
status := 1
|
||||
if err == ErrQuotaExceeded {
|
||||
status = 3
|
||||
} else if err != nil {
|
||||
status = 2
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2022 Nicola Murino
|
||||
// Copyright (C) 2019-2023 Nicola Murino
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
@@ -10,7 +10,7 @@
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package common
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNewActionNotification(t *testing.T) {
|
||||
user := &dataprovider.User{
|
||||
user := dataprovider.User{
|
||||
BaseUser: sdk.BaseUser{
|
||||
Username: "username",
|
||||
},
|
||||
@@ -68,51 +68,57 @@ func TestNewActionNotification(t *testing.T) {
|
||||
Endpoint: "httpendpoint",
|
||||
},
|
||||
}
|
||||
c := NewBaseConnection("id", ProtocolSSH, "", "", user)
|
||||
sessionID := xid.New().String()
|
||||
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, errors.New("fake error"))
|
||||
a := newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(errors.New("fake error")), 0)
|
||||
assert.Equal(t, user.Username, a.Username)
|
||||
assert.Equal(t, 0, len(a.Bucket))
|
||||
assert.Equal(t, 0, len(a.Endpoint))
|
||||
assert.Equal(t, 2, a.Status)
|
||||
|
||||
user.FsConfig.Provider = sdk.S3FilesystemProvider
|
||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSSH, "", sessionID,
|
||||
123, 0, nil)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSSH, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0)
|
||||
assert.Equal(t, "s3bucket", a.Bucket)
|
||||
assert.Equal(t, "endpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
|
||||
user.FsConfig.Provider = sdk.GCSFilesystemProvider
|
||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, ErrQuotaExceeded)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(ErrQuotaExceeded), 0)
|
||||
assert.Equal(t, "gcsbucket", a.Bucket)
|
||||
assert.Equal(t, 0, len(a.Endpoint))
|
||||
assert.Equal(t, 3, a.Status)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(fmt.Errorf("wrapper quota error: %w", ErrQuotaExceeded)), 0)
|
||||
assert.Equal(t, "gcsbucket", a.Bucket)
|
||||
assert.Equal(t, 0, len(a.Endpoint))
|
||||
assert.Equal(t, 3, a.Status)
|
||||
|
||||
user.FsConfig.Provider = sdk.HTTPFilesystemProvider
|
||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSSH, "", sessionID,
|
||||
123, 0, nil)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSSH, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0)
|
||||
assert.Equal(t, "httpendpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
|
||||
user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
|
||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, nil)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0)
|
||||
assert.Equal(t, "azcontainer", a.Bucket)
|
||||
assert.Equal(t, "azendpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
|
||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, os.O_APPEND, nil)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, os.O_APPEND, c.getNotificationStatus(nil), 0)
|
||||
assert.Equal(t, "azcontainer", a.Bucket)
|
||||
assert.Equal(t, "azendpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
assert.Equal(t, os.O_APPEND, a.OpenFlags)
|
||||
|
||||
user.FsConfig.Provider = sdk.SFTPFilesystemProvider
|
||||
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, nil)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0)
|
||||
assert.Equal(t, "sftpendpoint", a.Endpoint)
|
||||
}
|
||||
|
||||
@@ -129,19 +135,22 @@ func TestActionHTTP(t *testing.T) {
|
||||
},
|
||||
}
|
||||
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "",
|
||||
xid.New().String(), 123, 0, nil)
|
||||
err := actionHandler.Handle(a)
|
||||
xid.New().String(), 123, 0, 1, 0)
|
||||
status, err := actionHandler.Handle(a)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
Config.Actions.Hook = "http://invalid:1234"
|
||||
err = actionHandler.Handle(a)
|
||||
status, err = actionHandler.Handle(a)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
Config.Actions.Hook = fmt.Sprintf("http://%v/404", httpAddr)
|
||||
err = actionHandler.Handle(a)
|
||||
status, err = actionHandler.Handle(a)
|
||||
if assert.Error(t, err) {
|
||||
assert.EqualError(t, err, errUnexpectedHTTResponse.Error())
|
||||
}
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
Config.Actions = actionsCopy
|
||||
}
|
||||
@@ -166,15 +175,16 @@ func TestActionCMD(t *testing.T) {
|
||||
}
|
||||
sessionID := shortuuid.New()
|
||||
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, nil)
|
||||
err = actionHandler.Handle(a)
|
||||
123, 0, 1, 0)
|
||||
status, err := actionHandler.Handle(a)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
c := NewBaseConnection("id", ProtocolSFTP, "", "", *user)
|
||||
err = ExecuteActionNotification(c, OperationSSHCmd, "path", "vpath", "target", "vtarget", "sha1sum", 0, nil)
|
||||
err = ExecuteActionNotification(c, OperationSSHCmd, "path", "vpath", "target", "vtarget", "sha1sum", 0, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = ExecuteActionNotification(c, operationDownload, "path", "vpath", "", "", "", 0, nil)
|
||||
err = ExecuteActionNotification(c, operationDownload, "path", "vpath", "", "", "", 0, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
Config.Actions = actionsCopy
|
||||
@@ -198,30 +208,33 @@ func TestWrongActions(t *testing.T) {
|
||||
}
|
||||
|
||||
a := newActionNotification(user, operationUpload, "", "", "", "", "", ProtocolSFTP, "", xid.New().String(),
|
||||
123, 0, nil)
|
||||
err := actionHandler.Handle(a)
|
||||
123, 0, 1, 0)
|
||||
status, err := actionHandler.Handle(a)
|
||||
assert.Error(t, err, "action with bad command must fail")
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
a.Action = operationDelete
|
||||
err = actionHandler.Handle(a)
|
||||
assert.EqualError(t, err, errUnconfiguredAction.Error())
|
||||
status, err = actionHandler.Handle(a)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, status)
|
||||
|
||||
Config.Actions.Hook = "http://foo\x7f.com/"
|
||||
a.Action = operationUpload
|
||||
err = actionHandler.Handle(a)
|
||||
status, err = actionHandler.Handle(a)
|
||||
assert.Error(t, err, "action with bad url must fail")
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
Config.Actions.Hook = ""
|
||||
err = actionHandler.Handle(a)
|
||||
if assert.Error(t, err) {
|
||||
assert.EqualError(t, err, errNoHook.Error())
|
||||
}
|
||||
status, err = actionHandler.Handle(a)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, status)
|
||||
|
||||
Config.Actions.Hook = "relative path"
|
||||
err = actionHandler.Handle(a)
|
||||
status, err = actionHandler.Handle(a)
|
||||
if assert.Error(t, err) {
|
||||
assert.EqualError(t, err, fmt.Sprintf("invalid notification command %#v", Config.Actions.Hook))
|
||||
assert.EqualError(t, err, fmt.Sprintf("invalid notification command %q", Config.Actions.Hook))
|
||||
}
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
Config.Actions = actionsCopy
|
||||
}
|
||||
@@ -236,7 +249,7 @@ func TestPreDeleteAction(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
Config.Actions = ProtocolActions{
|
||||
ExecuteOn: []string{operationPreDelete},
|
||||
Hook: hookCmd,
|
||||
Hook: "missing hook",
|
||||
}
|
||||
homeDir := filepath.Join(os.TempDir(), "test_user")
|
||||
err = os.MkdirAll(homeDir, os.ModePerm)
|
||||
@@ -249,7 +262,7 @@ func TestPreDeleteAction(t *testing.T) {
|
||||
}
|
||||
user.Permissions = make(map[string][]string)
|
||||
user.Permissions["/"] = []string{dataprovider.PermAny}
|
||||
fs := vfs.NewOsFs("id", homeDir, "")
|
||||
fs := vfs.NewOsFs("id", homeDir, "", nil)
|
||||
c := NewBaseConnection("id", ProtocolSFTP, "", "", user)
|
||||
|
||||
testfile := filepath.Join(user.HomeDir, "testfile")
|
||||
@@ -258,8 +271,12 @@ func TestPreDeleteAction(t *testing.T) {
|
||||
info, err := os.Stat(testfile)
|
||||
assert.NoError(t, err)
|
||||
err = c.RemoveFile(fs, testfile, "testfile", info)
|
||||
assert.NoError(t, err)
|
||||
assert.ErrorIs(t, err, c.GetPermissionDeniedError())
|
||||
assert.FileExists(t, testfile)
|
||||
Config.Actions.Hook = hookCmd
|
||||
err = c.RemoveFile(fs, testfile, "testfile", info)
|
||||
assert.NoError(t, err)
|
||||
assert.NoFileExists(t, testfile)
|
||||
|
||||
os.RemoveAll(homeDir)
|
||||
|
||||
@@ -283,12 +300,14 @@ func TestUnconfiguredHook(t *testing.T) {
|
||||
assert.True(t, plugin.Handler.HasNotifiers())
|
||||
|
||||
c := NewBaseConnection("id", ProtocolSFTP, "", "", dataprovider.User{})
|
||||
err = ExecutePreAction(c, OperationPreDownload, "", "", 0, 0)
|
||||
status, err := ExecutePreAction(c, OperationPreDownload, "", "", 0, 0)
|
||||
assert.NoError(t, err)
|
||||
err = ExecutePreAction(c, operationPreDelete, "", "", 0, 0)
|
||||
assert.ErrorIs(t, err, errUnconfiguredAction)
|
||||
assert.Equal(t, status, 0)
|
||||
status, err = ExecutePreAction(c, operationPreDelete, "", "", 0, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, status, 0)
|
||||
|
||||
err = ExecuteActionNotification(c, operationDownload, "", "", "", "", "", 0, nil)
|
||||
err = ExecuteActionNotification(c, operationDownload, "", "", "", "", "", 0, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = plugin.Initialize(nil, "debug")
|
||||
@@ -302,10 +321,10 @@ type actionHandlerStub struct {
|
||||
called bool
|
||||
}
|
||||
|
||||
func (h *actionHandlerStub) Handle(event *notifier.FsEvent) error {
|
||||
func (h *actionHandlerStub) Handle(_ *notifier.FsEvent) (int, error) {
|
||||
h.called = true
|
||||
|
||||
return nil
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func TestInitializeActionHandler(t *testing.T) {
|
||||
@@ -316,8 +335,8 @@ func TestInitializeActionHandler(t *testing.T) {
|
||||
InitializeActionHandler(&defaultActionHandler{})
|
||||
})
|
||||
|
||||
err := actionHandler.Handle(¬ifier.FsEvent{})
|
||||
|
||||
status, err := actionHandler.Handle(¬ifier.FsEvent{})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, handler.called)
|
||||
assert.Equal(t, 1, status)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user