check that the jwt token is used by the same IP for which it

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-03-24 22:03:17 +01:00
parent 4bbb195711
commit d955ddcef9
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
13 changed files with 154 additions and 118 deletions

View file

@ -382,7 +382,7 @@ jobs:
gzip output/man/man1/*
cp sftpgo output/
- uses: uraimo/run-on-arch-action@v2.1.1
- uses: uraimo/run-on-arch-action@v2
if: ${{ matrix.arch != 'amd64' }}
name: Build for ${{ matrix.arch }}
id: build

View file

@ -326,7 +326,7 @@ jobs:
env:
SFTPGO_VERSION: ${{ steps.get_version.outputs.SFTPGO_VERSION }}
- uses: uraimo/run-on-arch-action@v2.1.1
- uses: uraimo/run-on-arch-action@v2
if: ${{ matrix.arch != 'amd64' }}
name: Build for ${{ matrix.arch }}
id: build

View file

@ -11,8 +11,6 @@ Several storage backends are supported: local filesystem, encrypted local filesy
## Features
<details>
- 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.
- Configurable [custom commands and/or HTTP hooks](./docs/custom-actions.md) on file upload, pre-upload, download, pre-download, delete, pre-delete, rename, mkdir, rmdir on SSH commands and on user add, update and delete.
@ -42,6 +40,7 @@ Several storage backends are supported: local filesystem, encrypted local filesy
- Per-user and per-directory shell like patterns filters: files can be allowed, denied or hidden based on shell like patterns.
- Automatically terminating idle connections.
- Automatic blocklist management using the built-in [defender](./docs/defender.md).
- Geo-IP filtering using a [plugin](https://github.com/sftpgo/sftpgo-plugin-geoipfilter).
- Atomic uploads are configurable.
- Per-user files/folders ownership mapping: you can map all the users to the system account that runs SFTPGo (all platforms are supported) or you can run SFTPGo as root user and map each user or group of users to a different system account (\*NIX only).
- Support for Git repositories over SSH.
@ -60,8 +59,6 @@ Several storage backends are supported: local filesystem, encrypted local filesy
- 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.
</details>
## Platforms
SFTPGo is developed and tested on Linux. After each commit, the code is automatically built and tested on Linux, macOS and Windows using a [GitHub Action](./.github/workflows/development.yml). The test cases are regularly manually executed and passed on FreeBSD. Other *BSD variants should work too.

36
go.mod
View file

@ -8,12 +8,12 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0
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.16.0
github.com/aws/aws-sdk-go-v2/config v1.15.1
github.com/aws/aws-sdk-go-v2/credentials v1.11.0
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.1
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.1
github.com/aws/aws-sdk-go-v2/service/sts v1.16.1
github.com/aws/aws-sdk-go-v2 v1.16.1
github.com/aws/aws-sdk-go-v2/config v1.15.2
github.com/aws/aws-sdk-go-v2/credentials v1.11.1
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.2
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.2
github.com/aws/aws-sdk-go-v2/service/sts v1.16.2
github.com/cockroachdb/cockroach-go/v2 v2.2.8
github.com/coreos/go-oidc/v3 v3.1.0
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
@ -75,17 +75,17 @@ require (
cloud.google.com/go/compute v1.5.0 // indirect
cloud.google.com/go/iam v0.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.0 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.1 // indirect
github.com/aws/smithy-go v1.11.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.2 // indirect
github.com/aws/smithy-go v1.11.2 // 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
@ -147,7 +147,7 @@ require (
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220323144105-ec3c684e5b14 // indirect
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb // indirect
google.golang.org/grpc v1.45.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect

72
go.sum
View file

@ -137,51 +137,51 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.16.0 h1:cBAYjiiexRAg9v2z9vb6IdxAa7ef4KCtjW7w7e3GxGo=
github.com/aws/aws-sdk-go-v2 v1.16.0/go.mod h1:lJYcuZZEHWNIb6ugJjbQY1fykdoobWbOS7kJYb4APoI=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.0 h1:J/tiyHbl07LL4/1i0rFrW5pbLMvo7M6JrekBUNpLeT4=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.0/go.mod h1:ohZjRmiToJ4NybwWTGOCbzlUQU8dxSHxYKzuX7k5l6Y=
github.com/aws/aws-sdk-go-v2 v1.16.1 h1:udzee98w8H6ikRgtFdVN9JzzYEbi/quFfSvduZETJIU=
github.com/aws/aws-sdk-go-v2 v1.16.1/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 h1:SdK4Ppk5IzLs64ZMvr6MrSficMtjY2oS0WOORXTlxwU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM=
github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY=
github.com/aws/aws-sdk-go-v2/config v1.15.1 h1:hTIZFepYESYyowQUBo47lu69WSxsYqGUILY9Nu8+7pY=
github.com/aws/aws-sdk-go-v2/config v1.15.1/go.mod h1:MZHGbuW2WnqIOQQBKu2ZkhTjuutZSTnn56TDq4QyydE=
github.com/aws/aws-sdk-go-v2/config v1.15.2 h1:4oGcm1yqqtTc2Z8YpwehwjSiBA3TR0iZbFCgNlXcVFQ=
github.com/aws/aws-sdk-go-v2/config v1.15.2/go.mod h1:S1p1xf7DGVp0srNq0BakyxfirOldPQeDVlx7+fllyok=
github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY=
github.com/aws/aws-sdk-go-v2/credentials v1.11.0 h1:gc4Uhs80s60nmLon5Z4JXWinX2BkAGT0YROoUT8h8U4=
github.com/aws/aws-sdk-go-v2/credentials v1.11.0/go.mod h1:EdV1ZFgtZ4XM5RDHWcRWK8H+xW5duNVBqWj2oLu7tRo=
github.com/aws/aws-sdk-go-v2/credentials v1.11.1 h1:uR323+M7ca3v2GKXbFSwWbNA3kLjjFzaalL6W4rpB9s=
github.com/aws/aws-sdk-go-v2/credentials v1.11.1/go.mod h1:pYrHWfKUoWTmbr+xTf6ZoWeyyvLAQ5BPT3aL+nKlTpE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0/go.mod h1:CpNzHK9VEFUCknu50kkB8z58AH2B5DvPP7ea1LHve/Y=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1 h1:F9Je1nq5YXfMOv6451NHvMf6U0iTWeMnsG0MMIQoUmk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1/go.mod h1:Yph0XsTbQ5GGZ2+mO1a03P/SO9fdX3t1nejIp2tq79g=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.1 h1:2lGuOytsLs4N2z1UmZ9s7BuhHMcZxNkm612YsLHK/8g=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.1/go.mod h1:hkzjqerOQhhBGAL/DdmKLsP8hGZvtyvukCLvrU5twz4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.7 h1:KUErSJgdqmqAPBWAp6Zx9CjL0YXfytXJeXcsWnuCM1c=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.7/go.mod h1:oB9nZcxH1cGq7NPGurVJwxrO2vmJ9mmEBayCwcAlmT8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.1 h1:feVfa9eJonhJiss7g51ikjNB2DrUzbNZNvPL8pw/54k=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.1/go.mod h1:K4vz7lRYCyLYpYAMCLObODahFgARdD3YVa0MvQte9Co=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.2 h1:+AULPOLHEDjH2TcNKpixl4gt26hFOdlUuuisZUBFczA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.2/go.mod h1:jmsqNRVo2XlUTNXG/NF7hM7o2gd2jhfg8vdJ135d4XA=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.2 h1:PTFTblDWY/HdQ6ix5+to1uLARgLLuYbzKGLQnIdE5Us=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.2/go.mod h1:j4OwU2Gb7yaQaidJRpdlIRYX93jBCWhVYIgMlPjf89o=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.8 h1:CDaO90VZVBAL1sK87S5oSPIrp7yZqORv1hPIi2UsTMk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.8/go.mod h1:LnTQMTqbKsbtt+UI5+wPsB7jedW+2ZgozoPG8k6cMxg=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.2 h1:XXR3cdOcKRCTZf6ctcqpMf+go1BdzTm6+T9Ul5zxcMI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.2/go.mod h1:1x4ZP3Z8odssdhuLI+/1Tqw6Pt/VAaP4Tr8EUxHvPXE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8 h1:adr3PfiggFtqgFofAMUFCtdvwzpf3QxPES4ezK4M3iI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8/go.mod h1:wLbQYt36AJqaRZUQiCNXzbtkNigyPfKHrotHuIDiCy8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.0 h1:uhb7moM7VjqIEpWzTpCvceLDSwrWpaleXm39OnVjuLE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.0/go.mod h1:pA2St3Pu2Ldy6fBPY45Azoh1WBG4oS7eIKOd4XN7Meg=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.1 h1:iq77O8kBduROlzJ6mhN8zqxXxctDZ1PnWY0kyCfYMGc=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.1/go.mod h1:1R7cjoiEG9cgMCBpIOuCyZWT0Dn87vNUeAaC8Reiaow=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.9 h1:8umg6LSQ/b0+ZTq+Ro8K7VLGVwd7kiYQtIACpf2N/Yo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.9/go.mod h1:kASRBzoVW4I8KUmGCjsowAqVor9QU9DuTUABVducrTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1 h1:T4pFel53bkHjL2mMo+4DKE6r6AuoZnM0fg7k1/ratr4=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:GeUru+8VzrTXV/83XyMJ80KpH8xO89VPoUileyNQ+tc=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.2 h1:VoMBHtQZygRs8mcQNDrfmn09vFH2ccjf79nGJ0xuUfo=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.2/go.mod h1:2Fzbfwkx7z4yue1Lz6KDSKG84UpOcUKFl3VAtSF/gcg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1 h1:B/SPX7J+Y0Yrcjv60Nhbh1gC2uBN47SfN8JYre6Mp4M=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1/go.mod h1:2Hhr9Eh1gJzDatwACX/ozAZ/ljq5vzvPRu5cdu25tzc=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.1 h1:sjASzLVAG9okVe5HgLur36itaOY4UC90VZNXAtcn0+s=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.1/go.mod h1:ELGakx1J1qEJqWkwiN0jh3CMDTN6v17kOgN8kZgo6LQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.2 h1:RrN7V0r8+lUUKZM4OAoCOIZqjPLZPOl6wuwMd2QIryI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.2/go.mod h1:7hwSi01X5Yj9H0qLQljrn8OSdLwwSym1aQCfGn1tDQQ=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.2 h1:yxr9h06slG9fdVmO3CpBVuFVD73AeUHLmBxhCr3T3+E=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.2/go.mod h1:rvV/Jr4T8H3kMMw/9fFQw9kxqb70YKihA0oWuUFd3K8=
github.com/aws/aws-sdk-go-v2/service/kms v1.5.0/go.mod h1:w7JuP9Oq1IKMFQPkNe3V6s9rOssXzOVEMNEqK1L1bao=
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.1 h1:FSoVdUKHxAzZKXuemm+7vVj3eOUY5u01SSwwkfhWpqA=
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.1/go.mod h1:ZS+sYP2DfetYS7n42MLAoz8x50T/w5su+rZmhaqTubg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.2 h1:Op/A+5+D1K0bmwH3BStYbp/7iod9Rdfm9898A0qYxLc=
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.2/go.mod h1:Ao1W746VIMdV1WhEkjeVa5JzlaE1JkxJ46facHX9kzs=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.6.0/go.mod h1:B+7C5UKdVq1ylkI/A6O8wcurFtaux0R1njePNPtKwoA=
github.com/aws/aws-sdk-go-v2/service/ssm v1.10.0/go.mod h1:4dXS5YNqI3SNbetQ7X7vfsMlX6ZnboJA2dulBwJx7+g=
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.1 h1:DyHctRsJIAWIvom1Itb4T84D2jwpIu+KIi3d0SFaswg=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.1/go.mod h1:CvFTucADIx7U/M44vjLs/ZttpQHdpxwK+62+dUGhDeY=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.2 h1:8fVz1c9B/63w7O0kxbrCTT69iV4DgXnFumarPCZ3Cns=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.2/go.mod h1:GdCj3+FzI3D5tauOzz8n3YjN70XvgZz82PVVtJXmDds=
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2Fh6yq8wm0HSDAimPhzCoM=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.1 h1:xsOtPAvHqhvQvBza5ohaUcfq1LceH2lZKMUGZJKiZiM=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.1/go.mod h1:Aq2/Qggh2oemSfyHH+EO4UBbgWG6zFCXLHYI4ILTY7w=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.2 h1:qgK5htfKByTiPxS/diZ/mTCfDwGAVuyjRdqu6VoCh80=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.2/go.mod h1:RoMljzynmRe3jyOsRgqIMTzyhpAv6XNxu549M1X4Mdo=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.11.1 h1:IQ+lPZVkSM3FRtyaDox41R8YS6iwPMYIreejOgPW49g=
github.com/aws/smithy-go v1.11.1/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
github.com/aws/smithy-go v1.11.2 h1:eG/N+CcUMAvsdffgMvjMKwfyDzIkjM6pfxMJ8Mzc6mE=
github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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=
@ -1131,8 +1131,8 @@ google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220323144105-ec3c684e5b14 h1:17TOyVD+9MLIDtDJW9PdtMuVT7gNLEkN+G/xFYjZmr8=
google.golang.org/genproto v0.0.0-20220323144105-ec3c684e5b14/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=

View file

@ -159,11 +159,11 @@ func (c *jwtTokenClaims) hasPerm(perm string) bool {
return util.IsStringInSlice(perm, c.Permissions)
}
func (c *jwtTokenClaims) createToken(tokenAuth *jwtauth.JWTAuth, audience tokenAudience) (jwt.Token, string, error) {
func (c *jwtTokenClaims) createToken(tokenAuth *jwtauth.JWTAuth, audience tokenAudience, ip string) (jwt.Token, string, error) {
claims := c.asMap()
now := time.Now().UTC()
claims[jwt.JwtIDKey] = xid.New().String()
claims[jwt.JwtIDKey] = fmt.Sprintf("%s%s", xid.New().String(), ip)
claims[jwt.NotBeforeKey] = now.Add(-30 * time.Second)
claims[jwt.ExpirationKey] = now.Add(tokenDuration)
claims[jwt.AudienceKey] = audience
@ -171,8 +171,8 @@ func (c *jwtTokenClaims) createToken(tokenAuth *jwtauth.JWTAuth, audience tokenA
return tokenAuth.Encode(claims)
}
func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth, audience tokenAudience) (map[string]interface{}, error) {
token, tokenString, err := c.createToken(tokenAuth, audience)
func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth, audience tokenAudience, ip string) (map[string]interface{}, error) {
token, tokenString, err := c.createToken(tokenAuth, audience, ip)
if err != nil {
return nil, err
}
@ -184,8 +184,10 @@ func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth, audienc
return response, nil
}
func (c *jwtTokenClaims) createAndSetCookie(w http.ResponseWriter, r *http.Request, tokenAuth *jwtauth.JWTAuth, audience tokenAudience) error {
resp, err := c.createTokenResponse(tokenAuth, audience)
func (c *jwtTokenClaims) createAndSetCookie(w http.ResponseWriter, r *http.Request, tokenAuth *jwtauth.JWTAuth,
audience tokenAudience, ip string,
) error {
resp, err := c.createTokenResponse(tokenAuth, audience, ip)
if err != nil {
return err
}

View file

@ -8713,6 +8713,38 @@ func TestWebClientMaxConnections(t *testing.T) {
common.Config.MaxTotalConnections = oldValue
}
func TestTokenInvalidIPAddress(t *testing.T) {
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
assert.NoError(t, err)
webToken, err := getJWTWebClientTokenFromTestServerWithAddr(defaultUsername, defaultPassword, "1.1.1.1")
assert.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
assert.NoError(t, err)
req.RemoteAddr = "1.1.1.2"
req.RequestURI = webClientFilesPath
setJWTCookieForReq(req, webToken)
rr := executeRequest(req)
checkResponseCode(t, http.StatusFound, rr)
apiToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodGet, userDirsPath+"/?path=%2F", nil)
assert.NoError(t, err)
req.RemoteAddr = "2.2.2.2"
setBearerForReq(req, apiToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusUnauthorized, rr)
assert.Contains(t, rr.Body.String(), "Your token is not valid")
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
}
func TestDefender(t *testing.T) {
oldConfig := config.GetCommonConfig()
@ -17500,7 +17532,7 @@ func getJWTAPIUserTokenFromTestServer(username, password string) (string, error)
req.SetBasicAuth(username, password)
rr := executeRequest(req)
if rr.Code != http.StatusOK {
return "", fmt.Errorf("unexpected status code %v", rr.Code)
return "", fmt.Errorf("unexpected status code %v", rr.Code)
}
responseHolder := make(map[string]interface{})
err := render.DecodeJSON(rr.Body, &responseHolder)

View file

@ -629,7 +629,7 @@ func TestUpdateWebAdminInvalidClaims(t *testing.T) {
Permissions: admin.Permissions,
Signature: admin.GetSignature(),
}
token, err := c.createTokenResponse(server.tokenAuth, tokenAudienceWebAdmin)
token, err := c.createTokenResponse(server.tokenAuth, tokenAudienceWebAdmin, "")
assert.NoError(t, err)
form := make(url.Values)
@ -746,7 +746,7 @@ func TestCreateTokenError(t *testing.T) {
}
req, _ := http.NewRequest(http.MethodGet, tokenPath, nil)
server.generateAndSendToken(rr, req, admin)
server.generateAndSendToken(rr, req, admin, "")
assert.Equal(t, http.StatusInternalServerError, rr.Code)
rr = httptest.NewRecorder()
@ -778,7 +778,7 @@ func TestCreateTokenError(t *testing.T) {
assert.Equal(t, http.StatusOK, rr.Code, rr.Body.String())
req, _ = http.NewRequest(http.MethodPost, webAdminSetupPath, nil)
rr = httptest.NewRecorder()
server.loginAdmin(rr, req, &admin, false, nil)
server.loginAdmin(rr, req, &admin, false, nil, "")
// req with no POST body
req, _ = http.NewRequest(http.MethodGet, webAdminLoginPath+"?a=a%C3%AO%GG", nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@ -1991,7 +1991,7 @@ func TestWebUserInvalidClaims(t *testing.T) {
Permissions: nil,
Signature: user.GetSignature(),
}
token, err := c.createTokenResponse(server.tokenAuth, tokenAudienceWebClient)
token, err := c.createTokenResponse(server.tokenAuth, tokenAudienceWebClient, "")
assert.NoError(t, err)
req, _ := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
@ -2066,7 +2066,7 @@ func TestInvalidClaims(t *testing.T) {
Permissions: nil,
Signature: user.GetSignature(),
}
token, err := c.createTokenResponse(server.tokenAuth, tokenAudienceWebClient)
token, err := c.createTokenResponse(server.tokenAuth, tokenAudienceWebClient, "")
assert.NoError(t, err)
form := make(url.Values)
form.Set(csrfFormToken, createCSRFToken())
@ -2086,7 +2086,7 @@ func TestInvalidClaims(t *testing.T) {
Permissions: nil,
Signature: admin.GetSignature(),
}
token, err = c.createTokenResponse(server.tokenAuth, tokenAudienceWebAdmin)
token, err = c.createTokenResponse(server.tokenAuth, tokenAudienceWebAdmin, "")
assert.NoError(t, err)
form = make(url.Values)
form.Set(csrfFormToken, createCSRFToken())
@ -2134,7 +2134,7 @@ func TestSigningKey(t *testing.T) {
Permissions: nil,
Signature: user.GetSignature(),
}
token, err := c.createTokenResponse(server1.tokenAuth, tokenAudienceWebClient)
token, err := c.createTokenResponse(server1.tokenAuth, tokenAudienceWebClient, "")
assert.NoError(t, err)
accessToken := token["access_token"].(string)
assert.NotEmpty(t, accessToken)

View file

@ -42,33 +42,29 @@ func validateJWTToken(w http.ResponseWriter, r *http.Request, audience tokenAudi
isAPIToken := (audience == tokenAudienceAPI || audience == tokenAudienceAPIUser)
if err != nil || token == nil {
logger.Debug(logSender, "", "error getting jwt token: %v", err)
doRedirect := func(message string, err error) {
if isAPIToken {
sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
sendAPIResponse(w, r, err, message, http.StatusUnauthorized)
} else {
http.Redirect(w, r, redirectPath, http.StatusFound)
}
}
if err != nil || token == nil {
logger.Debug(logSender, "", "error getting jwt token: %v", err)
doRedirect(http.StatusText(http.StatusUnauthorized), err)
return errInvalidToken
}
err = jwt.Validate(token)
if err != nil {
logger.Debug(logSender, "", "error validating jwt token: %v", err)
if isAPIToken {
sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
} else {
http.Redirect(w, r, redirectPath, http.StatusFound)
}
doRedirect(http.StatusText(http.StatusUnauthorized), err)
return errInvalidToken
}
if isTokenInvalidated(r) {
logger.Debug(logSender, "", "the token has been invalidated")
if isAPIToken {
sendAPIResponse(w, r, nil, "Your token is no longer valid", http.StatusUnauthorized)
} else {
http.Redirect(w, r, redirectPath, http.StatusFound)
}
doRedirect("Your token is no longer valid", nil)
return errInvalidToken
}
// a user with a partial token will be always redirected to the appropriate two factor auth page
@ -77,11 +73,13 @@ func validateJWTToken(w http.ResponseWriter, r *http.Request, audience tokenAudi
}
if !util.IsStringInSlice(audience, token.Audience()) {
logger.Debug(logSender, "", "the token is not valid for audience %#v", audience)
if isAPIToken {
sendAPIResponse(w, r, nil, "Your token audience is not valid", http.StatusUnauthorized)
} else {
http.Redirect(w, r, redirectPath, http.StatusFound)
}
doRedirect("Your token audience is not valid", nil)
return errInvalidToken
}
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
if ipAddr != "" && !strings.Contains(token.JwtID(), ipAddr) {
logger.Debug(logSender, "", "the token with id %#v is not valid for the ip address %#v", token.JwtID(), ipAddr)
doRedirect("Your token is not valid", nil)
return errInvalidToken
}
return nil
@ -382,7 +380,8 @@ func authenticateAdminWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTA
if !admin.Filters.AllowAPIKeyAuth {
return fmt.Errorf("API key authentication disabled for admin %#v", admin.Username)
}
if err := admin.CanLogin(util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
if err := admin.CanLogin(ipAddr); err != nil {
return err
}
c := jwtTokenClaims{
@ -392,7 +391,7 @@ func authenticateAdminWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTA
APIKeyID: keyID,
}
resp, err := c.createTokenResponse(tokenAuth, tokenAudienceAPI)
resp, err := c.createTokenResponse(tokenAuth, tokenAudienceAPI, ipAddr)
if err != nil {
return err
}
@ -446,7 +445,7 @@ func authenticateUserWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTAu
APIKeyID: keyID,
}
resp, err := c.createTokenResponse(tokenAuth, tokenAudienceAPIUser)
resp, err := c.createTokenResponse(tokenAuth, tokenAudienceAPIUser, ipAddr)
if err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, common.ErrInternalFailure)
return err

View file

@ -496,7 +496,7 @@ func (s *httpdServer) oidcTokenAuthenticator(audience tokenAudience) func(next h
Username: token.Username,
Permissions: token.Permissions,
}
_, tokenString, err := jwtTokenClaims.createToken(s.tokenAuth, audience)
_, tokenString, err := jwtTokenClaims.createToken(s.tokenAuth, audience, util.GetIPFromRemoteAddress(r.RemoteAddr))
if err != nil {
setFlashMessage(w, r, "Unable to create cookie")
if audience == tokenAudienceWebAdmin {

View file

@ -666,7 +666,7 @@ func TestSkipOIDCAuth(t *testing.T) {
jwtTokenClaims := jwtTokenClaims{
Username: "user",
}
_, tokenString, err := jwtTokenClaims.createToken(server.tokenAuth, tokenAudienceWebClient)
_, tokenString, err := jwtTokenClaims.createToken(server.tokenAuth, tokenAudienceWebClient, "")
assert.NoError(t, err)
rr := httptest.NewRecorder()
r, err := http.NewRequest(http.MethodGet, webClientLogoutPath, nil)

View file

@ -413,6 +413,7 @@ func (s *httpdServer) handleWebAdminTwoFactorRecoveryPost(w http.ResponseWriter,
s.renderTwoFactorRecoveryPage(w, "Two factory authentication is not enabled")
return
}
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
for idx, code := range admin.Filters.RecoveryCodes {
if err := code.Secret.Decrypt(); err != nil {
s.renderInternalServerErrorPage(w, r, fmt.Errorf("unable to decrypt recovery code: %w", err))
@ -424,13 +425,13 @@ func (s *httpdServer) handleWebAdminTwoFactorRecoveryPost(w http.ResponseWriter,
return
}
admin.Filters.RecoveryCodes[idx].Used = true
err = dataprovider.UpdateAdmin(&admin, dataprovider.ActionExecutorSelf, util.GetIPFromRemoteAddress(r.RemoteAddr))
err = dataprovider.UpdateAdmin(&admin, dataprovider.ActionExecutorSelf, ipAddr)
if err != nil {
logger.Warn(logSender, "", "unable to set the recovery code %#v as used: %v", recoveryCode, err)
s.renderInternalServerErrorPage(w, r, errors.New("unable to set the recovery code as used"))
return
}
s.loginAdmin(w, r, &admin, true, s.renderTwoFactorRecoveryPage)
s.loginAdmin(w, r, &admin, true, s.renderTwoFactorRecoveryPage, ipAddr)
return
}
}
@ -478,7 +479,7 @@ func (s *httpdServer) handleWebAdminTwoFactorPost(w http.ResponseWriter, r *http
s.renderTwoFactorPage(w, "Invalid authentication code")
return
}
s.loginAdmin(w, r, &admin, true, s.renderTwoFactorPage)
s.loginAdmin(w, r, &admin, true, s.renderTwoFactorPage, util.GetIPFromRemoteAddress(r.RemoteAddr))
}
func (s *httpdServer) handleWebAdminLoginPost(w http.ResponseWriter, r *http.Request) {
@ -497,12 +498,13 @@ func (s *httpdServer) handleWebAdminLoginPost(w http.ResponseWriter, r *http.Req
s.renderAdminLoginPage(w, err.Error())
return
}
admin, err := dataprovider.CheckAdminAndPass(username, password, util.GetIPFromRemoteAddress(r.RemoteAddr))
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
admin, err := dataprovider.CheckAdminAndPass(username, password, ipAddr)
if err != nil {
s.renderAdminLoginPage(w, err.Error())
return
}
s.loginAdmin(w, r, &admin, false, s.renderAdminLoginPage)
s.loginAdmin(w, r, &admin, false, s.renderAdminLoginPage, ipAddr)
}
func (s *httpdServer) renderAdminLoginPage(w http.ResponseWriter, error string) {
@ -585,7 +587,7 @@ func (s *httpdServer) handleWebAdminPasswordResetPost(w http.ResponseWriter, r *
return
}
s.loginAdmin(w, r, admin, false, s.renderResetPwdPage)
s.loginAdmin(w, r, admin, false, s.renderResetPwdPage, util.GetIPFromRemoteAddress(r.RemoteAddr))
}
func (s *httpdServer) handleWebAdminSetupPost(w http.ResponseWriter, r *http.Request) {
@ -629,12 +631,13 @@ func (s *httpdServer) handleWebAdminSetupPost(w http.ResponseWriter, r *http.Req
Status: 1,
Permissions: []string{dataprovider.PermAdminAny},
}
err = dataprovider.AddAdmin(&admin, username, util.GetIPFromRemoteAddress(r.RemoteAddr))
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
err = dataprovider.AddAdmin(&admin, username, ipAddr)
if err != nil {
s.renderAdminSetupPage(w, r, username, err.Error())
return
}
s.loginAdmin(w, r, &admin, false, nil)
s.loginAdmin(w, r, &admin, false, nil, ipAddr)
}
func (s *httpdServer) loginUser(
@ -655,7 +658,7 @@ func (s *httpdServer) loginUser(
audience = tokenAudienceWebClientPartial
}
err := c.createAndSetCookie(w, r, s.tokenAuth, audience)
err := c.createAndSetCookie(w, r, s.tokenAuth, audience, ipAddr)
if err != nil {
logger.Warn(logSender, connectionID, "unable to set user login cookie %v", err)
updateLoginMetrics(user, dataprovider.LoginMethodPassword, ipAddr, common.ErrInternalFailure)
@ -676,7 +679,7 @@ func (s *httpdServer) loginUser(
func (s *httpdServer) loginAdmin(
w http.ResponseWriter, r *http.Request, admin *dataprovider.Admin,
isSecondFactorAuth bool, errorFunc func(w http.ResponseWriter, error string),
isSecondFactorAuth bool, errorFunc func(w http.ResponseWriter, error string), ip string,
) {
c := jwtTokenClaims{
Username: admin.Username,
@ -689,7 +692,7 @@ func (s *httpdServer) loginAdmin(
audience = tokenAudienceWebAdminPartial
}
err := c.createAndSetCookie(w, r, s.tokenAuth, audience)
err := c.createAndSetCookie(w, r, s.tokenAuth, audience, ip)
if err != nil {
logger.Warn(logSender, "", "unable to set admin login cookie %v", err)
if errorFunc == nil {
@ -803,7 +806,7 @@ func (s *httpdServer) generateAndSendUserToken(w http.ResponseWriter, r *http.Re
RequiredTwoFactorProtocols: user.Filters.TwoFactorAuthProtocols,
}
resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPIUser)
resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPIUser, ipAddr)
if err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, common.ErrInternalFailure)
@ -823,7 +826,8 @@ func (s *httpdServer) getToken(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, nil, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
admin, err := dataprovider.CheckAdminAndPass(username, password, util.GetIPFromRemoteAddress(r.RemoteAddr))
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
admin, err := dataprovider.CheckAdminAndPass(username, password, ipAddr)
if err != nil {
w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
@ -854,17 +858,17 @@ func (s *httpdServer) getToken(w http.ResponseWriter, r *http.Request) {
}
}
s.generateAndSendToken(w, r, admin)
s.generateAndSendToken(w, r, admin, ipAddr)
}
func (s *httpdServer) generateAndSendToken(w http.ResponseWriter, r *http.Request, admin dataprovider.Admin) {
func (s *httpdServer) generateAndSendToken(w http.ResponseWriter, r *http.Request, admin dataprovider.Admin, ip string) {
c := jwtTokenClaims{
Username: admin.Username,
Permissions: admin.Permissions,
Signature: admin.GetSignature(),
}
resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPI)
resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPI, ip)
if err != nil {
sendAPIResponse(w, r, err, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -914,7 +918,7 @@ func (s *httpdServer) refreshClientToken(w http.ResponseWriter, r *http.Request,
tokenClaims.Permissions = user.Filters.WebClient
logger.Debug(logSender, "", "cookie refreshed for user %#v", user.Username)
tokenClaims.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebClient) //nolint:errcheck
tokenClaims.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebClient, util.GetIPFromRemoteAddress(r.RemoteAddr)) //nolint:errcheck
}
func (s *httpdServer) refreshAdminToken(w http.ResponseWriter, r *http.Request, tokenClaims jwtTokenClaims) {
@ -930,13 +934,14 @@ func (s *httpdServer) refreshAdminToken(w http.ResponseWriter, r *http.Request,
logger.Debug(logSender, "", "signature mismatch for admin %#v, unable to refresh cookie", admin.Username)
return
}
if !admin.CanLoginFromIP(util.GetIPFromRemoteAddress(r.RemoteAddr)) {
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
if !admin.CanLoginFromIP(ipAddr) {
logger.Debug(logSender, "", "admin %#v cannot login from %v, unable to refresh cookie", admin.Username, r.RemoteAddr)
return
}
tokenClaims.Permissions = admin.Permissions
logger.Debug(logSender, "", "cookie refreshed for admin %#v", admin.Username)
tokenClaims.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebAdmin) //nolint:errcheck
tokenClaims.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebAdmin, ipAddr) //nolint:errcheck
}
func (s *httpdServer) updateContextFromCookie(r *http.Request) *http.Request {

View file

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"net"
@ -509,7 +510,7 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
go func(in <-chan *ssh.Request, counter int64) {
for req := range in {
ok := false
connID := fmt.Sprintf("%v_%v", connectionID, counter)
connID := fmt.Sprintf("%s_%d", connectionID, counter)
switch req.Type {
case "subsystem":
@ -879,7 +880,7 @@ func (c *Configuration) validatePublicKeyCredentials(conn ssh.ConnMetadata, pubK
return nil, err
}
if !c.certChecker.IsUserAuthority(cert.SignatureKey) {
err = fmt.Errorf("ssh: certificate signed by unrecognized authority")
err = errors.New("ssh: certificate signed by unrecognized authority")
user.Username = conn.User()
updateLoginMetrics(&user, ipAddr, method, err)
return nil, err