mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
parent
59140a6d51
commit
101c2962ab
7 changed files with 79 additions and 20 deletions
|
@ -719,6 +719,11 @@ func (u *User) CanManageMFA() bool {
|
|||
return len(mfa.GetAvailableTOTPConfigs()) > 0
|
||||
}
|
||||
|
||||
// CanChangePassword returns true if this user is allowed to change its password
|
||||
func (u *User) CanChangePassword() bool {
|
||||
return !util.IsStringInSlice(sdk.WebClientPasswordChangeDisabled, u.Filters.WebClient)
|
||||
}
|
||||
|
||||
// CanManagePublicKeys returns true if this user is allowed to manage public keys
|
||||
// from the web client. Used in web client UI
|
||||
func (u *User) CanManagePublicKeys() bool {
|
||||
|
|
|
@ -304,19 +304,6 @@ then SFTPGo will try to create `id_rsa`, `id_ecdsa` and `id_ed25519`, if they ar
|
|||
|
||||
The configuration can be read from JSON, TOML, YAML, HCL, envfile and Java properties config files. If your `config-file` flag is set to `sftpgo` (default value), you need to create a configuration file called `sftpgo.json` or `sftpgo.yaml` and so on inside `config-dir`.
|
||||
|
||||
## Binding to privileged ports
|
||||
|
||||
On Linux, if you want to use Internet domain privileged ports (port numbers less than 1024) instead of running the SFTPGo service as root user you can set the `cap_net_bind_service` capability on the `sftpgo` binary. To set the capability you can use the following command:
|
||||
|
||||
```shell
|
||||
$ sudo setcap cap_net_bind_service=+ep /usr/bin/sftpgo
|
||||
# Check that the capability is added:
|
||||
$ getcap /usr/bin/sftpgo
|
||||
/usr/bin/sftpgo cap_net_bind_service=ep
|
||||
```
|
||||
|
||||
Now you can use privileged ports such as 21, 22, 443 etc.. without running the SFTPGo service as root user. You have to set the `cap_net_bind_service` capability each time you update the `sftpgo` binary.
|
||||
|
||||
## Environment variables
|
||||
|
||||
You can also override all the available configuration options using environment variables. SFTPGo will check for environment variables with a name matching the key uppercased and prefixed with the `SFTPGO_`. You need to use `__` to traverse a struct.
|
||||
|
@ -335,6 +322,26 @@ You can select `sha256-simd` setting the environment variable `SFTPGO_MINIO_SHA2
|
|||
|
||||
`sha256-simd` is particularly useful if you have an Intel CPU with SHA extensions or an ARM CPU with Cryptography Extensions.
|
||||
|
||||
## Binding to privileged ports
|
||||
|
||||
On Linux, if you want to use Internet domain privileged ports (port numbers less than 1024) instead of running the SFTPGo service as root user you can set the `cap_net_bind_service` capability on the `sftpgo` binary. To set the capability you can use the following command:
|
||||
|
||||
```shell
|
||||
$ sudo setcap cap_net_bind_service=+ep /usr/bin/sftpgo
|
||||
# Check that the capability is added
|
||||
$ getcap /usr/bin/sftpgo
|
||||
/usr/bin/sftpgo cap_net_bind_service=ep
|
||||
```
|
||||
|
||||
Now you can use privileged ports such as 21, 22, 443 etc.. without running the SFTPGo service as root user. You have to set the `cap_net_bind_service` capability each time you update the `sftpgo` binary.
|
||||
|
||||
An alternative method is to use `iptables`, for example you run the SFTP service on port `2022` and redirect traffic from port `22` to port `2022`:
|
||||
|
||||
```shell
|
||||
sudo iptables -t nat -A PREROUTING -d <ip> -p tcp --dport 22 -m addrtype --dst-type LOCAL -j DNAT --to-destination <ip>:2022
|
||||
sudo iptables -t nat -A OUTPUT -d <ip> -p tcp --dport 22 -m addrtype --dst-type LOCAL -j DNAT --to-destination <ip>:2022
|
||||
```
|
||||
|
||||
## Telemetry Server
|
||||
|
||||
The telemetry server exposes the following endpoints:
|
||||
|
|
|
@ -5854,6 +5854,27 @@ func TestWebAPIChangeUserPwdMock(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
|
||||
// remove the change password permission
|
||||
user.Filters.WebClient = []string{sdk.WebClientPasswordChangeDisabled}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, user.Filters.WebClient, 1)
|
||||
assert.Contains(t, user.Filters.WebClient, sdk.WebClientPasswordChangeDisabled)
|
||||
|
||||
token, err = getJWTAPIUserTokenFromTestServer(defaultUsername, altAdminPassword)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
|
||||
pwd["current_password"] = altAdminPassword
|
||||
pwd["new_password"] = defaultPassword
|
||||
asJSON, err = json.Marshal(pwd)
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodPut, userPwdPath, bytes.NewBuffer(asJSON))
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
|
@ -7568,6 +7589,24 @@ func TestWebClientChangePwd(t *testing.T) {
|
|||
_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword+"1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// remove the change password permission
|
||||
user.Filters.WebClient = []string{sdk.WebClientPasswordChangeDisabled}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, user.Filters.WebClient, 1)
|
||||
assert.Contains(t, user.Filters.WebClient, sdk.WebClientPasswordChangeDisabled)
|
||||
|
||||
webToken, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword+"1")
|
||||
assert.NoError(t, err)
|
||||
form.Set("current_password", defaultPassword+"1")
|
||||
form.Set("new_password1", defaultPassword)
|
||||
form.Set("new_password2", defaultPassword)
|
||||
req, _ = http.NewRequest(http.MethodPost, webChangeClientPwdPath, bytes.NewBuffer([]byte(form.Encode())))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
|
|
|
@ -3025,11 +3025,13 @@ components:
|
|||
- publickey-change-disabled
|
||||
- write-disabled
|
||||
- mfa-disabled
|
||||
- password-change-disabled
|
||||
description: |
|
||||
Options:
|
||||
* `publickey-change-disabled` - changing SSH public keys is not allowed
|
||||
* `write-disabled` - upload, rename, delete are not allowed even if the user has permissions for these actions
|
||||
* `mfa-disabled` - the user cannot enable multi-factor authentication. This option cannot be set if the user has MFA already enabled
|
||||
* `mfa-disabled` - enabling multi-factor authentication is not allowed. This option cannot be set if the user has MFA already enabled
|
||||
* `password-change-disabled` - changing password is not allowed
|
||||
APIKeyScope:
|
||||
type: integer
|
||||
enum:
|
||||
|
|
|
@ -988,7 +988,8 @@ func (s *httpdServer) initializeRouter() {
|
|||
router.Use(jwtAuthenticatorAPIUser)
|
||||
|
||||
router.With(forbidAPIKeyAuthentication).Get(userLogoutPath, s.logout)
|
||||
router.With(forbidAPIKeyAuthentication).Put(userPwdPath, changeUserPassword)
|
||||
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
|
||||
Put(userPwdPath, changeUserPassword)
|
||||
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
|
||||
Get(userPublicKeysPath, getUserPublicKeys)
|
||||
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
|
||||
|
@ -1089,7 +1090,8 @@ func (s *httpdServer) initializeRouter() {
|
|||
Delete(webClientDirsPath, deleteUserDir)
|
||||
router.With(s.refreshCookie).Get(webClientDownloadZipPath, handleWebClientDownloadZip)
|
||||
router.With(s.refreshCookie).Get(webClientCredentialsPath, handleClientGetCredentials)
|
||||
router.Post(webChangeClientPwdPath, handleWebClientChangePwdPost)
|
||||
router.With(checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
|
||||
Post(webChangeClientPwdPath, handleWebClientChangePwdPost)
|
||||
router.Post(webChangeClientAPIKeyAccessPath, handleWebClientManageAPIKeyPost)
|
||||
router.With(checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
|
||||
Post(webChangeClientKeysPath, handleWebClientManageKeysPost)
|
||||
|
|
10
sdk/user.go
10
sdk/user.go
|
@ -9,14 +9,16 @@ import (
|
|||
|
||||
// Web Client/user REST API restrictions
|
||||
const (
|
||||
WebClientPubKeyChangeDisabled = "publickey-change-disabled"
|
||||
WebClientWriteDisabled = "write-disabled"
|
||||
WebClientMFADisabled = "mfa-disabled"
|
||||
WebClientPubKeyChangeDisabled = "publickey-change-disabled"
|
||||
WebClientWriteDisabled = "write-disabled"
|
||||
WebClientMFADisabled = "mfa-disabled"
|
||||
WebClientPasswordChangeDisabled = "password-change-disabled"
|
||||
)
|
||||
|
||||
var (
|
||||
// WebClientOptions defines the available options for the web client interface/user REST API
|
||||
WebClientOptions = []string{WebClientPubKeyChangeDisabled, WebClientWriteDisabled, WebClientMFADisabled}
|
||||
WebClientOptions = []string{WebClientPubKeyChangeDisabled, WebClientWriteDisabled, WebClientMFADisabled,
|
||||
WebClientPasswordChangeDisabled}
|
||||
// UserTypes defines the supported user type hints for auth plugins
|
||||
UserTypes = []string{string(UserTypeLDAP), string(UserTypeOS)}
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
{{define "page_body"}}
|
||||
|
||||
{{if .LoggedUser.CanChangePassword}}
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Change password</h6>
|
||||
|
@ -41,6 +42,7 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .LoggedUser.CanManagePublicKeys}}
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
|
|
Loading…
Reference in a new issue