1
0
mirror of https://github.com/drakkan/sftpgo.git synced 2025-11-23 22:04:50 +02:00

Add support for allowed/denied IP/Mask

Login can be restricted to specific ranges of IP address or to a specific IP
address.

Please apply the appropriate SQL upgrade script to add the filter field to your
database.

The filter database field will allow to add other filters without requiring a
new database migration
This commit is contained in:
Nicola Murino
2019-12-30 18:37:50 +01:00
parent ad5436e3f6
commit 1b1c740b29
22 changed files with 623 additions and 95 deletions

View File

@@ -70,9 +70,9 @@ class SFTPGoApiRequests:
else:
print(r.text)
def buildUserObject(self, user_id=0, username="", password="", public_keys="", home_dir="", uid=0,
def buildUserObject(self, user_id=0, username="", password="", public_keys=[], home_dir="", uid=0,
gid=0, max_sessions=0, quota_size=0, quota_files=0, permissions={}, upload_bandwidth=0,
download_bandwidth=0, status=1, expiration_date=0):
download_bandwidth=0, status=1, expiration_date=0, allowed_ip=[], denied_ip=[]):
user = {"id":user_id, "username":username, "uid":uid, "gid":gid,
"max_sessions":max_sessions, "quota_size":quota_size, "quota_files":quota_files,
"upload_bandwidth":upload_bandwidth, "download_bandwidth":download_bandwidth,
@@ -88,6 +88,8 @@ class SFTPGoApiRequests:
user.update({"home_dir":home_dir})
if permissions:
user.update({"permissions":permissions})
if allowed_ip or denied_ip:
user.update({"filters":self.buildFilters(allowed_ip, denied_ip)})
return user
def buildPermissions(self, root_perms, subdirs_perms):
@@ -107,6 +109,20 @@ class SFTPGoApiRequests:
permissions.update({directory:values})
return permissions
def buildFilters(self, allowed_ip, denied_ip):
filters = {}
if allowed_ip:
if len(allowed_ip) == 1 and not allowed_ip[0]:
filters.update({"allowed_ip":[]})
else:
filters.update({"allowed_ip":allowed_ip})
if denied_ip:
if len(denied_ip) == 1 and not denied_ip[0]:
filters.update({"denied_ip":[]})
else:
filters.update({"denied_ip":denied_ip})
return filters
def getUsers(self, limit=100, offset=0, order="ASC", username=""):
r = requests.get(self.userPath, params={"limit":limit, "offset":offset, "order":order,
"username":username}, auth=self.auth, verify=self.verify)
@@ -118,19 +134,20 @@ class SFTPGoApiRequests:
def addUser(self, username="", password="", public_keys="", home_dir="", uid=0, gid=0, max_sessions=0,
quota_size=0, quota_files=0, perms=[], upload_bandwidth=0, download_bandwidth=0, status=1,
expiration_date=0, subdirs_permissions=[]):
expiration_date=0, subdirs_permissions=[], allowed_ip=[], denied_ip=[]):
u = self.buildUserObject(0, username, password, public_keys, home_dir, uid, gid, max_sessions,
quota_size, quota_files, self.buildPermissions(perms, subdirs_permissions), upload_bandwidth, download_bandwidth,
status, expiration_date)
status, expiration_date, allowed_ip, denied_ip)
r = requests.post(self.userPath, json=u, auth=self.auth, verify=self.verify)
self.printResponse(r)
def updateUser(self, user_id, username="", password="", public_keys="", home_dir="", uid=0, gid=0,
max_sessions=0, quota_size=0, quota_files=0, perms=[], upload_bandwidth=0,
download_bandwidth=0, status=1, expiration_date=0, subdirs_permissions=[]):
download_bandwidth=0, status=1, expiration_date=0, subdirs_permissions=[],
allowed_ip=[], denied_ip=[]):
u = self.buildUserObject(user_id, username, password, public_keys, home_dir, uid, gid, max_sessions,
quota_size, quota_files, self.buildPermissions(perms, subdirs_permissions), upload_bandwidth, download_bandwidth,
status, expiration_date)
status, expiration_date, allowed_ip, denied_ip)
r = requests.put(urlparse.urljoin(self.userPath, "user/" + str(user_id)), json=u, auth=self.auth, verify=self.verify)
self.printResponse(r)
@@ -251,7 +268,7 @@ class ConvertUsers:
user_info = spwd.getspnam(username)
password = user_info.sp_pwdp
if not password or password == '!!':
print('cannot import user "{}" without password'.format(username))
print('cannot import user "{}" without a password'.format(username))
continue
if user_info.sp_inact > 0:
last_pwd_change_diff = days_from_epoch_time - user_info.sp_lstchg
@@ -283,11 +300,27 @@ class ConvertUsers:
self.addUser(self.SFTPGoRestAPI.buildUserObject(0, username, password, [], home_dir, uid, gid, 0, 0,
0, permissions, 0, 0, 1, 0))
def convertPureFTPDIP(self, fields):
result = []
if not fields:
return result
for v in fields.split(","):
ip_mask = v.strip()
if not ip_mask:
continue
if ip_mask.count(".") < 3 and ip_mask.count(":") < 3:
print("cannot import pure-ftpd IP: {}".format(ip_mask))
continue
if "/" not in ip_mask:
ip_mask += "/32"
result.append(ip_mask)
return result
def convertFromPureFTPD(self):
with open(self.input_file, 'r') as f:
for line in f:
fields = line.split(':')
if len(fields) > 13:
if len(fields) > 16:
username = fields[0]
password = fields[1]
uid = int(fields[2])
@@ -308,6 +341,8 @@ class ConvertUsers:
quota_size = 0
if fields[12]:
quota_size = int(fields[12])
allowed_ip = self.convertPureFTPDIP(fields[15])
denied_ip = self.convertPureFTPDIP(fields[16])
if not self.isUserValid(username, uid, gid):
continue
if self.force_uid >= 0:
@@ -317,7 +352,8 @@ class ConvertUsers:
permissions = self.SFTPGoRestAPI.buildPermissions(['*'], [])
self.addUser(self.SFTPGoRestAPI.buildUserObject(0, username, password, [], home_dir, uid, gid,
max_sessions, quota_size, quota_files, permissions,
upload_bandwidth, download_bandwidth, 1, 0))
upload_bandwidth, download_bandwidth, 1, 0, allowed_ip,
denied_ip))
def validDate(s):
@@ -361,6 +397,10 @@ def addCommonUserArguments(parser):
help='User\'s status. 1 enabled, 0 disabled. Default: %(default)s')
parser.add_argument('-E', '--expiration-date', type=validDate, default="",
help='Expiration date as YYYY-MM-DD, empty string means no expiration. Default: %(default)s')
parser.add_argument('-Y', '--allowed-ip', type=str, nargs='+', default=[],
help='Allowed IP/Mask in CIDR notation. For example "192.168.2.0/24" or "2001:db8::/32". Default: %(default)s')
parser.add_argument('-N', '--denied-ip', type=str, nargs='+', default=[],
help='Denied IP/Mask in CIDR notation. For example "192.168.2.0/24" or "2001:db8::/32". Default: %(default)s')
if __name__ == '__main__':
@@ -458,12 +498,13 @@ if __name__ == '__main__':
if args.command == 'add-user':
api.addUser(args.username, args.password, args.public_keys, args.home_dir, args.uid, args.gid, args.max_sessions,
args.quota_size, args.quota_files, args.permissions, args.upload_bandwidth, args.download_bandwidth,
args.status, getDatetimeAsMillisSinceEpoch(args.expiration_date), args.subdirs_permissions)
args.status, getDatetimeAsMillisSinceEpoch(args.expiration_date), args.subdirs_permissions, args.allowed_ip,
args.denied_ip)
elif args.command == 'update-user':
api.updateUser(args.id, args.username, args.password, args.public_keys, args.home_dir, args.uid, args.gid,
args.max_sessions, args.quota_size, args.quota_files, args.permissions, args.upload_bandwidth,
args.download_bandwidth, args.status, getDatetimeAsMillisSinceEpoch(args.expiration_date),
args.subdirs_permissions)
args.subdirs_permissions, args.allowed_ip, args.denied_ip)
elif args.command == 'delete-user':
api.deleteUser(args.id)
elif args.command == 'get-users':