From 50a3c0d9116580f95b37d12829657af75f8d9d93 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Fri, 17 May 2024 20:48:18 +0200 Subject: [PATCH] defender: allow to impose a delay between login attempts Signed-off-by: Nicola Murino --- README.md | 4 +- go.mod | 46 +++++++------- go.sum | 100 ++++++++++++++----------------- internal/common/common.go | 7 +++ internal/common/common_test.go | 13 ++++ internal/common/defender.go | 24 ++++++++ internal/common/defender_test.go | 27 +++++++++ internal/config/config.go | 6 ++ internal/ftpd/server.go | 7 ++- internal/httpd/api_shares.go | 1 + internal/httpd/api_utils.go | 2 + internal/httpd/middleware.go | 2 + internal/httpd/oidc.go | 1 + internal/httpd/server.go | 2 + internal/sftpd/server.go | 4 ++ internal/webdavd/server.go | 4 ++ sftpgo.json | 6 +- 17 files changed, 174 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index b46ea863..499c2955 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ You can use SFTPGo for free, respecting the obligations of the Open Source licen Use [discussions](https://github.com/drakkan/sftpgo/discussions) to ask questions and get support from the community. -If you report an invalid issue and/or ask for step-by-step support, your issue will be closed as invalid without further explanation. Invalid bug reports left open may confuse other users. Thanks for understanding. +If you report an invalid issue and/or ask for step-by-step support, your issue will be closed as invalid without further explanation and/or the "support request" label will be added. Invalid bug reports may confuse other users. Thanks for understanding. ## Documentation @@ -61,7 +61,7 @@ You can read more about supported features and documentation at [sftpgo.github.i ## Release Cadence -SFTPGo releases are feature-driven, we don't have a fixed time based schedule. As a rough estimate, you can expect 1 or 2 new releases per year. +SFTPGo releases are feature-driven, we don't have a fixed time based schedule. As a rough estimate, you can expect 1 or 2 new major releases per year and several bug fix releases. ## Acknowledgements diff --git a/go.mod b/go.mod index 667e32ed..4bd7f6c5 100644 --- a/go.mod +++ b/go.mod @@ -9,15 +9,15 @@ require ( 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.26.1 - github.com/aws/aws-sdk-go-v2/config v1.27.13 - github.com/aws/aws-sdk-go-v2/credentials v1.17.13 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.18 - github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.5 - github.com/aws/aws-sdk-go-v2/service/s3 v1.54.0 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.7 - github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 + github.com/aws/aws-sdk-go-v2 v1.27.0 + github.com/aws/aws-sdk-go-v2/config v1.27.15 + github.com/aws/aws-sdk-go-v2/credentials v1.17.15 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.20 + github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.7 + github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.9 + github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/cockroachdb/cockroach-go/v2 v2.3.8 github.com/coreos/go-oidc/v3 v3.10.0 @@ -42,7 +42,7 @@ require ( github.com/lithammer/shortuuid/v3 v3.0.7 github.com/mattn/go-sqlite3 v1.14.22 github.com/mhale/smtpd v0.8.3 - github.com/minio/sio v0.3.1 + github.com/minio/sio v0.4.0 github.com/otiai10/copy v1.14.0 github.com/pires/go-proxyproto v0.7.0 github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317 @@ -73,13 +73,13 @@ require ( golang.org/x/sys v0.20.0 golang.org/x/term v0.20.0 golang.org/x/time v0.5.0 - google.golang.org/api v0.180.0 + google.golang.org/api v0.181.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( cloud.google.com/go v0.113.0 // indirect - cloud.google.com/go/auth v0.4.1 // indirect + cloud.google.com/go/auth v0.4.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/iam v1.1.8 // indirect @@ -87,16 +87,16 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect github.com/ajg/form v1.5.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 // indirect github.com/aws/smithy-go v1.20.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1 // indirect @@ -173,9 +173,9 @@ require ( golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/genproto v0.0.0-20240513163218-0867130af1f8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect + google.golang.org/genproto v0.0.0-20240515191416-fc5f0ca64291 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240515191416-fc5f0ca64291 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect google.golang.org/grpc v1.64.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 7e7a0ad5..68235f14 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.113.0 h1:g3C70mn3lWfckKBiCVsAshabrDg01pQ0pnX1MNtnMkA= cloud.google.com/go v0.113.0/go.mod h1:glEqlogERKYeePz6ZdkcLJ28Q2I6aERgDDErBg9GzO8= -cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg= -cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro= +cloud.google.com/go/auth v0.4.2 h1:sb0eyLkhRtpq5jA+a8KWw0W70YcdVca7KJ8TM0AFYDg= +cloud.google.com/go/auth v0.4.2/go.mod h1:Kqvlz1cf1sNA0D+sYJnkPQOP+JMHkuHeIgVmCRtZOLc= cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= -cloud.google.com/go/compute v1.26.0 h1:uHf0NN2nvxl1Gh4QO83yRCOdMK4zivtMS5gv0dEX0hg= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0= @@ -40,50 +39,46 @@ github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHc github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw= github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/4wKeNvD/IQla3U3SUa61U7ul+xM4= github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k= -github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= -github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo= +github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg= -github.com/aws/aws-sdk-go-v2/config v1.27.13 h1:WbKW8hOzrWoOA/+35S5okqO/2Ap8hkkFUzoW8Hzq24A= -github.com/aws/aws-sdk-go-v2/config v1.27.13/go.mod h1:XLiyiTMnguytjRER7u5RIkhIqS8Nyz41SwAWb4xEjxs= -github.com/aws/aws-sdk-go-v2/credentials v1.17.13 h1:XDCJDzk/u5cN7Aple7D/MiAhx1Rjo/0nueJ0La8mRuE= -github.com/aws/aws-sdk-go-v2/credentials v1.17.13/go.mod h1:FMNcjQrmuBYvOTZDtOLCIu0esmxjF7RuA/89iSXWzQI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.17 h1:9b1Os1s11mF5qTIKLgSsyPG810di2+ySSLIIt9bwe9I= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.17/go.mod h1:9Wp7tDOMhv0+sb/FTRAkbHNQ7abYDnoJRzm5AAtCnTc= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.18 h1:fUHit8Pe+2dWEHtxpOVDTOSQR257iH24HjT17DAz6qs= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.18/go.mod h1:IX1n1o870YYxzqN56w26s7FrO5Zaw/hdatxhJDiEf2U= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= +github.com/aws/aws-sdk-go-v2/config v1.27.15 h1:uNnGLZ+DutuNEkuPh6fwqK7LpEiPmzb7MIMA1mNWEUc= +github.com/aws/aws-sdk-go-v2/config v1.27.15/go.mod h1:7j7Kxx9/7kTmL7z4LlhwQe63MYEE5vkVV6nWg4ZAI8M= +github.com/aws/aws-sdk-go-v2/credentials v1.17.15 h1:YDexlvDRCA8ems2T5IP1xkMtOZ1uLJOCJdTr0igs5zo= +github.com/aws/aws-sdk-go-v2/credentials v1.17.15/go.mod h1:vxHggqW6hFNaeNC0WyXS3VdyjcV0a4KMUY4dKJ96buU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.20 h1:NCM9wYaJCmlIWZSO/JwUEveKf0NCvsSgo9V9BwOAolo= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.20/go.mod h1:dmxIx3qriuepxqZgFeFMitFuftWPB94+MZv/6Btpth4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 h1:81KE7vaZzrl7yHBYHVEzYB8sypz11NMOZ40YlWvPxsU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5/go.mod h1:LIt2rg7Mcgn09Ygbdh/RdIm0rQ+3BNkbP1gyVMFtRK0= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 h1:/FUtT3xsoHO3cfh+I/kCbcMCN98QZRsiFet/V8QkWSs= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7/go.mod h1:MaCAgWpGooQoCWZnMur97rGn5dp350w2+CeiV5406wE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 h1:ZMeFZ5yk+Ek+jNr1+uwCd2tG89t6oTS5yVWpa6yy2es= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7/go.mod h1:mxV05U+4JiHqIpGqqYXOHLPKUC6bDXC44bsUhNjOEwY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 h1:f9RyWNtS8oH7cZlbn+/JNPpjUk5+5fLd5lM9M0i49Ys= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5/go.mod h1:h5CoMZV2VF297/VLhRhO1WF+XYWOzXo+4HsObA4HjBQ= -github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.5 h1:p2PxN+OO28p2bCCXE79sJfFBaSohwxa24bQdjuyPZCs= -github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.5/go.mod h1:Q01yJLephuOzv6IYzcknrpVAriOqB66+qtGnpqgw9UE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.53.2 h1:rq2hglTQM3yHZvOPVMtNvLS5x6hijx7JvRDgKiTNDGQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.53.2/go.mod h1:qmdkIIAC+GCLASF7R2whgNrJADz0QZPX+Seiw/i4S3o= -github.com/aws/aws-sdk-go-v2/service/s3 v1.54.0 h1:Ls94RY3P6HtB88JkzXo1lHrXzonHPpNR//OSAV63mSE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.54.0/go.mod h1:qmdkIIAC+GCLASF7R2whgNrJADz0QZPX+Seiw/i4S3o= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.7 h1:4cziOtpDwtgcb+wTYRzz8C+GoH1XySy0p7j4oBbqPQE= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.7/go.mod h1:3Ba++UwWd154xtP4FRX5pUK3Gt4up5sDHCve6kVfE+g= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 h1:o5cTaeunSpfXiLTIBx5xo2enQmiChtu1IBbzXnfU9Hs= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.6/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 h1:Qe0r0lVURDDeBQJ4yP+BOrJkvkiCo/3FH/t+wY11dmw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1qYpP5uU7cvNql8ns= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 h1:UXqEWQI0n+q0QixzU0yUUQBZXRd5037qdInTIHFTl98= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9/go.mod h1:xP6Gq6fzGZT8w/ZN+XvGMZ2RU1LeEs7b2yUP5DN8NY4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 h1:uO5XR6QGBcmPyo2gxofYJLFkcVQ4izOoGDNenlZhTEk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7/go.mod h1:feeeAYfAcwTReM6vbwjEyDmiGho+YgBhaFULuXDW8kc= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.7 h1:Qro9bPGqmXbFouJEEs/5eqYXd4mI0MnpNzyn99A2fug= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.7/go.mod h1:XvmRgpZk17Rf5Yqmmlp68mFNQQh0hCaqI/ygv875xFA= +github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2 h1:gYSJhNiOF6J9xaYxu2NFNstoiNELwt0T9w29FxSfN+Y= +github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2/go.mod h1:739CllldowZiPPsDFcJHNF4FXrVxaSGVnZ9Ez9Iz9hc= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.9 h1:qH8ovDAQkGLgYh/QEEKx/uSDL1vCIuVB2VIZJsVr4dA= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.9/go.mod h1:5mMk0DgUgaHlcqtN65fNyZI0ZDX3i9Cw+nwq75HKB3U= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 h1:Kv1hwNG6jHC/sxMTe5saMjH6t6ZLkgfvVxyEjfWL1ks= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.8/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 h1:nWBZ1xHCF+A7vv9sDzJOq4NWIdzFYm0kH7Pr4OjHYsQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 h1:Qp6Boy0cGDloOE3zI6XhNLNZgjNS8YmiFQFHe71SaW0= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.9/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -242,7 +237,6 @@ github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= @@ -298,8 +292,8 @@ github.com/mhale/smtpd v0.8.3 h1:8j8YNXajksoSLZja3HdwvYVZPuJSqAxFsib3adzRRt8= github.com/mhale/smtpd v0.8.3/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4= github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= -github.com/minio/sio v0.3.1 h1:d59r5RTHb1OsQaSl1EaTWurzMMDRLA5fgNmjzD4eVu4= -github.com/minio/sio v0.3.1/go.mod h1:S0ovgVgc+sTlQyhiXA1ppBLv7REM7TYi5yyq2qL/Y6o= +github.com/minio/sio v0.4.0 h1:u4SWVEm5lXSqU42ZWawV0D9I5AZ5YMmo2RXpEQ/kRhc= +github.com/minio/sio v0.4.0/go.mod h1:oBSjJeGbBdRMZZwna07sX9EFzZy+ywu5aofRiV1g79I= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -517,26 +511,24 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4= -google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE= +google.golang.org/api v0.181.0 h1:rPdjwnWgiPPOJx3IcSAQ2III5aX5tCer6wMpa/xmZi4= +google.golang.org/api v0.181.0/go.mod h1:MnQ+M0CFsfUwA5beZ+g/vCBCPXvtmZwRz2qzZk8ih1k= 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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240513163218-0867130af1f8 h1:XpH03M6PDRKTo1oGfZBXu2SzwcbfxUokgobVinuUZoU= -google.golang.org/genproto v0.0.0-20240513163218-0867130af1f8/go.mod h1:OLh2Ylz+WlYAJaSBRpJIJLP8iQP+8da+fpxbwNEAV/o= -google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No= -google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= +google.golang.org/genproto v0.0.0-20240515191416-fc5f0ca64291 h1:CTZGpOdDJr2Jq+LcJ/mpjG8mClGy/uJdBBVYbS9g5lY= +google.golang.org/genproto v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:ch5ZrEj5+9MCxUeR3Gp3mCJ4u0eVpusYAmSr/mvpMSk= +google.golang.org/genproto/googleapis/api v0.0.0-20240515191416-fc5f0ca64291 h1:4HZJ3Xv1cmrJ+0aFo304Zn79ur1HMxptAE7aCPNLSqc= +google.golang.org/genproto/googleapis/api v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/internal/common/common.go b/internal/common/common.go index e35316bc..76286493 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -327,6 +327,13 @@ func Reload() error { return nil } +// DelayLogin applies the configured login delay +func DelayLogin(err error) { + if Config.defender != nil { + Config.defender.DelayLogin(err) + } +} + // IsBanned returns true if the specified IP address is banned func IsBanned(ip, protocol string) bool { if plugin.Handler.IsIPBanned(ip, protocol) { diff --git a/internal/common/common_test.go b/internal/common/common_test.go index c7d4ba51..bc13e3c4 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -419,6 +419,9 @@ func TestDefenderIntegration(t *testing.T) { ObservationTime: 15, EntriesSoftLimit: 100, EntriesHardLimit: 150, + LoginDelay: LoginDelay{ + PasswordFailed: 200, + }, } err = Initialize(Config, 0) // ScoreInvalid cannot be greater than threshold @@ -477,6 +480,16 @@ func TestDefenderIntegration(t *testing.T) { assert.Nil(t, banTime) assert.False(t, DeleteDefenderHost(ip)) + startTime := time.Now() + DelayLogin(nil) + elapsed := time.Since(startTime) + assert.Less(t, elapsed, time.Millisecond*50) + + startTime = time.Now() + DelayLogin(ErrInternalFailure) + elapsed = time.Since(startTime) + assert.Greater(t, elapsed, time.Millisecond*150) + Config = configCopy } diff --git a/internal/common/defender.go b/internal/common/defender.go index d3f38221..18ea0ea7 100644 --- a/internal/common/defender.go +++ b/internal/common/defender.go @@ -53,6 +53,7 @@ type Defender interface { GetBanTime(ip string) (*time.Time, error) GetScore(ip string) (int, error) DeleteHost(ip string) bool + DelayLogin(err error) } // DefenderConfig defines the "defender" configuration @@ -90,6 +91,16 @@ type DefenderConfig struct { // to return when you request for the entire host list from the defender EntriesSoftLimit int `json:"entries_soft_limit" mapstructure:"entries_soft_limit"` EntriesHardLimit int `json:"entries_hard_limit" mapstructure:"entries_hard_limit"` + // Configuration to impose a delay between login attempts + LoginDelay LoginDelay `json:"login_delay" mapstructure:"login_delay"` +} + +// LoginDelay defines the delays to impose between login attempts. +type LoginDelay struct { + // The number of milliseconds to pause prior to allowing a successful login + Success int `json:"success" mapstructure:"success"` + // The number of milliseconds to pause prior to reporting a failed login + PasswordFailed int `json:"password_failed" mapstructure:"password_failed"` } type baseDefender struct { @@ -163,6 +174,19 @@ func (d *baseDefender) logBan(ip, protocol string) { Send() } +// DelayLogin applies the configured login delay. +func (d *baseDefender) DelayLogin(err error) { + if err == nil { + if d.config.LoginDelay.Success > 0 { + time.Sleep(time.Duration(d.config.LoginDelay.Success) * time.Millisecond) + } + return + } + if d.config.LoginDelay.PasswordFailed > 0 { + time.Sleep(time.Duration(d.config.LoginDelay.PasswordFailed) * time.Millisecond) + } +} + type hostEvent struct { dateTime time.Time score int diff --git a/internal/common/defender_test.go b/internal/common/defender_test.go index 9465a770..1a1bb7c5 100644 --- a/internal/common/defender_test.go +++ b/internal/common/defender_test.go @@ -435,6 +435,33 @@ func TestDefenderCleanup(t *testing.T) { assert.Equal(t, 0, score) } +func TestDefenderDelay(t *testing.T) { + d := memoryDefender{ + baseDefender: baseDefender{ + config: &DefenderConfig{ + ObservationTime: 1, + EntriesSoftLimit: 2, + EntriesHardLimit: 3, + LoginDelay: LoginDelay{ + Success: 50, + PasswordFailed: 200, + }, + }, + }, + banned: make(map[string]time.Time), + hosts: make(map[string]hostScore), + } + startTime := time.Now() + d.DelayLogin(nil) + elapsed := time.Since(startTime) + assert.Less(t, elapsed, time.Millisecond*100) + + startTime = time.Now() + d.DelayLogin(ErrInternalFailure) + elapsed = time.Since(startTime) + assert.Greater(t, elapsed, time.Millisecond*150) +} + func TestDefenderConfig(t *testing.T) { c := DefenderConfig{} err := c.validate() diff --git a/internal/config/config.go b/internal/config/config.go index 97a0da7b..e5c322de 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -226,6 +226,10 @@ func Init() { ObservationTime: 30, EntriesSoftLimit: 100, EntriesHardLimit: 150, + LoginDelay: common.LoginDelay{ + Success: 0, + PasswordFailed: 1000, + }, }, RateLimitersConfig: []common.RateLimiterConfig{defaultRateLimiter}, Umask: "", @@ -1995,6 +1999,8 @@ func setViperDefaults() { viper.SetDefault("common.defender.observation_time", globalConf.Common.DefenderConfig.ObservationTime) viper.SetDefault("common.defender.entries_soft_limit", globalConf.Common.DefenderConfig.EntriesSoftLimit) viper.SetDefault("common.defender.entries_hard_limit", globalConf.Common.DefenderConfig.EntriesHardLimit) + viper.SetDefault("common.defender.login_delay.success", globalConf.Common.DefenderConfig.LoginDelay.Success) + viper.SetDefault("common.defender.login_delay.password_failed", globalConf.Common.DefenderConfig.LoginDelay.PasswordFailed) viper.SetDefault("common.umask", globalConf.Common.Umask) viper.SetDefault("common.server_version", globalConf.Common.ServerVersion) viper.SetDefault("common.metadata.read", globalConf.Common.Metadata.Read) diff --git a/internal/ftpd/server.go b/internal/ftpd/server.go index ecd1edb3..8baf631c 100644 --- a/internal/ftpd/server.go +++ b/internal/ftpd/server.go @@ -420,9 +420,9 @@ func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err err metric.AddLoginAttempt(loginMethod) if err == nil { plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolFTP, user.Username, ip, "", nil) + common.DelayLogin(nil) } else if err != common.ErrInternalFailure { - logger.ConnectionFailedLog(user.Username, ip, loginMethod, - common.ProtocolFTP, err.Error()) + logger.ConnectionFailedLog(user.Username, ip, loginMethod, common.ProtocolFTP, err.Error()) event := common.HostEventLoginFailed logEv := notifier.LogEventTypeLoginFailed if errors.Is(err, util.ErrNotFound) { @@ -431,6 +431,9 @@ func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err err } common.AddDefenderEvent(ip, common.ProtocolFTP, event) plugin.Handler.NotifyLogEvent(logEv, common.ProtocolFTP, user.Username, ip, "", err) + if loginMethod != dataprovider.LoginMethodTLSCertificate { + common.DelayLogin(err) + } } metric.AddLoginResult(loginMethod, err) dataprovider.ExecutePostLoginHook(user, loginMethod, ip, common.ProtocolFTP, err) diff --git a/internal/httpd/api_shares.go b/internal/httpd/api_shares.go index c45f6f1f..73f85299 100644 --- a/internal/httpd/api_shares.go +++ b/internal/httpd/api_shares.go @@ -512,6 +512,7 @@ func (s *httpdServer) checkPublicShare(w http.ResponseWriter, r *http.Request, v return share, nil, dataprovider.ErrInvalidCredentials } } + common.DelayLogin(nil) } user, err := getUserForShare(share) if err != nil { diff --git a/internal/httpd/api_utils.go b/internal/httpd/api_utils.go index 78d7219f..09c0f258 100644 --- a/internal/httpd/api_utils.go +++ b/internal/httpd/api_utils.go @@ -686,6 +686,7 @@ func handleDefenderEventLoginFailed(ipAddr string, err error) error { err = dataprovider.ErrInvalidCredentials } common.AddDefenderEvent(ipAddr, common.ProtocolHTTP, event) + common.DelayLogin(err) return err } @@ -700,6 +701,7 @@ func updateLoginMetrics(user *dataprovider.User, loginMethod, ip string, err err } if err == nil { plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, protocol, user.Username, ip, "", nil) + common.DelayLogin(nil) } else if err != common.ErrInternalFailure && err != common.ErrNoCredentials { logger.ConnectionFailedLog(user.Username, ip, loginMethod, protocol, err.Error()) err = handleDefenderEventLoginFailed(ip, err) diff --git a/internal/httpd/middleware.go b/internal/httpd/middleware.go index 8c98f02b..02eb438b 100644 --- a/internal/httpd/middleware.go +++ b/internal/httpd/middleware.go @@ -440,6 +440,7 @@ func checkAPIKeyAuth(tokenAuth *jwtauth.JWTAuth, scope dataprovider.APIKeyScope) "", http.StatusUnauthorized) return } + common.DelayLogin(nil) } else { if k.User != "" { apiUser = k.User @@ -512,6 +513,7 @@ func authenticateAdminWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTA } r.Header.Set("Authorization", fmt.Sprintf("Bearer %v", resp["access_token"])) dataprovider.UpdateAdminLastLogin(&admin) + common.DelayLogin(nil) return nil } diff --git a/internal/httpd/oidc.go b/internal/httpd/oidc.go index b686caab..22ba3c7c 100644 --- a/internal/httpd/oidc.go +++ b/internal/httpd/oidc.go @@ -428,6 +428,7 @@ func (t *oidcToken) getUser(r *http.Request) error { t.TokenRole = admin.Role t.HideUserPageSections = admin.Filters.Preferences.HideUserPageSections dataprovider.UpdateAdminLastLogin(admin) + common.DelayLogin(nil) return nil } params.Event = common.IDPLoginUser diff --git a/internal/httpd/server.go b/internal/httpd/server.go index 0584344f..175c844d 100644 --- a/internal/httpd/server.go +++ b/internal/httpd/server.go @@ -821,6 +821,7 @@ func (s *httpdServer) loginAdmin( return } dataprovider.UpdateAdminLastLogin(admin) + common.DelayLogin(nil) redirectURL := webUsersPath if errorFunc == nil { redirectURL = webAdminMFAPath @@ -1000,6 +1001,7 @@ func (s *httpdServer) generateAndSendToken(w http.ResponseWriter, r *http.Reques } dataprovider.UpdateAdminLastLogin(&admin) + common.DelayLogin(nil) render.JSON(w, r, resp) } diff --git a/internal/sftpd/server.go b/internal/sftpd/server.go index 5b4455a3..b72c8188 100644 --- a/internal/sftpd/server.go +++ b/internal/sftpd/server.go @@ -1216,6 +1216,7 @@ func updateLoginMetrics(user *dataprovider.User, ip, method string, err error) { metric.AddLoginAttempt(method) if err == nil { plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolSSH, user.Username, ip, "", err) + common.DelayLogin(nil) } else { logger.ConnectionFailedLog(user.Username, ip, method, common.ProtocolSSH, err.Error()) if method != dataprovider.SSHLoginMethodPublicKey { @@ -1230,6 +1231,9 @@ func updateLoginMetrics(user *dataprovider.User, ip, method string, err error) { } common.AddDefenderEvent(ip, common.ProtocolSSH, event) plugin.Handler.NotifyLogEvent(logEv, common.ProtocolSSH, user.Username, ip, "", err) + if method != dataprovider.SSHLoginMethodPublicKey { + common.DelayLogin(err) + } } } metric.AddLoginResult(method, err) diff --git a/internal/webdavd/server.go b/internal/webdavd/server.go index 88ba6a08..aad07040 100644 --- a/internal/webdavd/server.go +++ b/internal/webdavd/server.go @@ -426,6 +426,7 @@ func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err err metric.AddLoginAttempt(loginMethod) if err == nil { plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolWebDAV, user.Username, ip, "", nil) + common.DelayLogin(nil) } else if err != common.ErrInternalFailure && err != common.ErrNoCredentials { logger.ConnectionFailedLog(user.Username, ip, loginMethod, common.ProtocolWebDAV, err.Error()) event := common.HostEventLoginFailed @@ -436,6 +437,9 @@ func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err err } common.AddDefenderEvent(ip, common.ProtocolWebDAV, event) plugin.Handler.NotifyLogEvent(logEv, common.ProtocolWebDAV, user.Username, ip, "", err) + if loginMethod != dataprovider.LoginMethodTLSCertificate { + common.DelayLogin(err) + } } metric.AddLoginResult(loginMethod, err) dataprovider.ExecutePostLoginHook(user, loginMethod, ip, common.ProtocolWebDAV, err) diff --git a/sftpgo.json b/sftpgo.json index 55d17315..2a725166 100644 --- a/sftpgo.json +++ b/sftpgo.json @@ -39,7 +39,11 @@ "score_no_auth": 0, "observation_time": 30, "entries_soft_limit": 100, - "entries_hard_limit": 150 + "entries_hard_limit": 150, + "login_delay": { + "success": 0, + "password_failed": 1000 + } }, "rate_limiters": [ {