mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 23:00:55 +03:00
ftpd: allow to require TLS on a per-user basis
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
@@ -926,6 +926,61 @@ func TestLoginNonExistentUser(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestFTPSecurity(t *testing.T) {
|
||||
u := getTestUser()
|
||||
u.Filters.FTPSecurity = 1
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client, err := getFTPClient(user, true, nil)
|
||||
if assert.NoError(t, err) {
|
||||
err = checkBasicFTP(client)
|
||||
assert.NoError(t, err)
|
||||
err := client.Quit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
_, err = getFTPClient(user, false, nil)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "TLS is required")
|
||||
}
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGroupFTPSecurity(t *testing.T) {
|
||||
g := getTestGroup()
|
||||
g.UserSettings.Filters.FTPSecurity = 1
|
||||
group, _, err := httpdtest.AddGroup(g, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
u := getTestUser()
|
||||
u.Groups = []sdk.GroupMapping{
|
||||
{
|
||||
Name: group.Name,
|
||||
Type: sdk.GroupTypePrimary,
|
||||
},
|
||||
}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client, err := getFTPClient(user, true, nil)
|
||||
if assert.NoError(t, err) {
|
||||
err = checkBasicFTP(client)
|
||||
assert.NoError(t, err)
|
||||
err := client.Quit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
_, err = getFTPClient(user, false, nil)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "TLS is required")
|
||||
}
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveGroup(group, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestLoginExternalAuth(t *testing.T) {
|
||||
if runtime.GOOS == osWindows {
|
||||
t.Skip("this test is not available on Windows")
|
||||
@@ -1055,6 +1110,19 @@ func TestPreLoginHook(t *testing.T) {
|
||||
err := client.Quit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
user.Status = 0
|
||||
user.Filters.FTPSecurity = 1
|
||||
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, false), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
client, err = getFTPClient(u, true, nil)
|
||||
if !assert.Error(t, err) {
|
||||
err := client.Quit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
_, err = getFTPClient(user, false, nil)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "TLS is required")
|
||||
}
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -325,6 +325,10 @@ func (cc mockFTPClientContext) HasTLSForTransfers() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (cc mockFTPClientContext) SetTLSRequirement(requirement ftpserver.TLSRequirement) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cc mockFTPClientContext) GetLastCommand() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -221,6 +221,23 @@ func (s *Server) AuthUser(cc ftpserver.ClientContext, username, password string)
|
||||
return connection, nil
|
||||
}
|
||||
|
||||
// PreAuthUser implements the MainDriverExtensionUserVerifier interface
|
||||
func (s *Server) PreAuthUser(cc ftpserver.ClientContext, username string) error {
|
||||
if s.binding.TLSMode == 0 && s.tlsConfig != nil {
|
||||
user, err := dataprovider.GetFTPPreAuthUser(username, util.GetIPFromRemoteAddress(cc.RemoteAddr().String()))
|
||||
if err == nil {
|
||||
if user.Filters.FTPSecurity == 1 {
|
||||
return cc.SetTLSRequirement(ftpserver.MandatoryEncryption)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if _, ok := err.(*util.RecordNotFoundError); !ok {
|
||||
return common.ErrInternalFailure
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WrapPassiveListener implements the MainDriverExtensionPassiveWrapper interface
|
||||
func (s *Server) WrapPassiveListener(listener net.Listener) (net.Listener, error) {
|
||||
if s.binding.HasProxy() {
|
||||
|
||||
Reference in New Issue
Block a user