pax_global_header 0000666 0000000 0000000 00000000064 13622517253 0014520 g ustar 00root root 0000000 0000000 52 comment=7b373b47313d11ce09b232e2ff430dbbdcaddd6b
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/ 0000775 0000000 0000000 00000000000 13622517253 0023576 5 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/ 0000775 0000000 0000000 00000000000 13622517253 0025430 5 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/ 0000775 0000000 0000000 00000000000 13622517253 0027106 5 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/README.md 0000664 0000000 0000000 00000001532 13622517253 0030366 0 ustar 00root root 0000000 0000000 Proftpd with sftp and virtual users
http://www.proftpd.org/docs/
# Features
* sftp only is enabled
* partially uploadloaded are not visible thanks to [`HiddenStores`](http://proftpd.org/docs/directives/linked/config_ref_HiddenStores.html) ( in fact they are, but name starts with `.` )
* 5 failed login attempts will cause the host to be temporary banned
# TODO
* only password login is enabled. enabling [`SFTPAuthorizedUserKeys`](http://www.proftpd.org/docs/contrib/mod_sftp.html#SFTPAuthorizedUserKeys) seems to break password only login
* log rotation
* make sure SFTPLog is useful (seems very verbose and does not contain more than stdout)
* make it easier to manage users ( using `mod_auth_web` against an ERP5 endpoint or accepting a list of user/password as instance parameter )
* allow configuring webhooks when new file is uploaded
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/buildout.hash.cfg 0000664 0000000 0000000 00000001745 13622517253 0032347 0 ustar 00root root 0000000 0000000 # THIS IS NOT A BUILDOUT FILE, despite purposedly using a compatible syntax.
# The only allowed lines here are (regexes):
# - "^#" comments, copied verbatim
# - "^[" section beginings, copied verbatim
# - lines containing an "=" sign which must fit in the following categorie.
# - "^\s*filename\s*=\s*path\s*$" where "path" is relative to this file
# Copied verbatim.
# - "^\s*hashtype\s*=.*" where "hashtype" is one of the values supported
# by the re-generation script.
# Re-generated.
# - other lines are copied verbatim
# Substitution (${...:...}), extension ([buildout] extends = ...) and
# section inheritance (< = ...) are NOT supported (but you should really
# not need these here).
[instance-profile]
filename = instance.cfg.in
md5sum = efb4238229681447aa7fe73898dffad4
[instance-default]
filename = instance-default.cfg.in
md5sum = 2a2c066d7d40dd8545f3008f434ee842
[proftpd-config-file]
filename = proftpd-config-file.cfg.in
md5sum = a7c0f4607c378b640379cc258a8aadfa
instance-default.cfg.in 0000664 0000000 0000000 00000007520 13622517253 0033347 0 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd [buildout]
parts =
promises
publish-connection-parameter
extends = {{ template_monitor }}
[instance-parameter]
# TODO: this is not needed
recipe = slapos.cookbook:slapconfiguration
computer = ${slap-connection:computer-id}
partition = ${slap-connection:partition-id}
url = ${slap-connection:server-url}
key = ${slap-connection:key-file}
cert = ${slap-connection:cert-file}
[slap-configuration]
recipe = slapos.cookbook:slapconfiguration.serialised
computer = ${slap-connection:computer-id}
partition = ${slap-connection:partition-id}
url = ${slap-connection:server-url}
key = ${slap-connection:key-file}
cert = ${slap-connection:cert-file}
[directory]
recipe = slapos.cookbook:mkdirectory
home = ${buildout:directory}
etc = ${:home}/etc
var = ${:home}/var
log = ${:var}/log
srv = ${:home}/srv
service = ${:etc}/service
proftpd-dir = ${:srv}/proftpd/
ssh-authorized-keys-dir = ${:etc}/authorized_keys/
[config-file]
recipe = slapos.recipe.template:jinja2
template = {{ software_parts_directory }}/${:_buildout_section_name_}/${:_buildout_section_name_}.cfg.in
rendered = ${directory:etc}/${:_buildout_section_name_}.cfg
mode = 0644
extensions = jinja2.ext.do
[proftpd-userinfo]
recipe = slapos.cookbook:userinfo
[proftpd-password]
recipe = slapos.cookbook:generate.password
username = proftpd
bytes = 12
[proftpd]
ipv6 = ${instance-parameter:ipv6-random}
ipv4 = ${instance-parameter:ipv4-random}
host = ${:ipv6}
sftp-port = {{ slapparameter_dict.get('port', 8022) }}
url = sftp://[${:host}]:${:sftp-port}
data-dir = ${directory:proftpd-dir}
user=${proftpd-userinfo:pw-name}
group=${proftpd-userinfo:gr-name}
scoreboard-file=${directory:var}/proftpd.scoreboard
pid-file=${directory:var}/proftpd.pid
sftp-log=${directory:log}/proftpd-sftp.log
xfer-log=${directory:log}/proftpd-xfer.log
ban-log=${directory:log}/proftpd-ban.log
ssh-host-rsa-key=${ssh-host-rsa-key:output}
ssh-host-dsa-key=${ssh-host-dsa-key:output}
ssh-host-ecdsa-key=${ssh-host-ecdsa-key:output}
ssh-authorized-keys-dir = ${directory:ssh-authorized-keys-dir}
ban-table=${directory:srv}/proftpd-ban-table
control-socket=${directory:var}/proftpd.sock
auth-user-file=${auth-user-file:output}
recipe = slapos.cookbook:wrapper
command-line =
{{ proftpd_bin }} --nodaemon --config ${proftpd-config-file:rendered}
wrapper-path = ${directory:service}/proftpd
[proftpd-listen-promise]
<= monitor-promise-base
module = check_port_listening
name = ${:_buildout_section_name_}.py
config-hostname = ${proftpd:ipv6}
config-port = ${proftpd:sftp-port}
[ftpasswd]
# command line to add a user, invoke with:
# ftpasswd --name=bob
# to prompt for password, or --stdin to read password from stdin
recipe = slapos.cookbook:wrapper
wrapper-path =${buildout:bin-directory}/${:_buildout_section_name_}
command-line =
{{ perl_bin }} {{ ftpasswd_bin }} --passwd --home=${proftpd:data-dir} --shell=/bin/false --uid=${proftpd-userinfo:pw-uid} --gid=${proftpd-userinfo:gr-gid} --file ${auth-user-file:output}
[auth-user-file]
recipe = plone.recipe.command
output = ${directory:etc}/ftpd.passwd
command =
echo ${proftpd-password:passwd} | ${ftpasswd:wrapper-path} --name=${proftpd-password:username} --stdin
update-command = ${:command}
[ssh-keygen-base]
recipe = plone.recipe.command
output = ${directory:etc}/${:_buildout_section_name_}
command = {{ ssh_keygen_bin }} -f ${:output} -N '' ${:extra-args}
[ssh-host-rsa-key]
<=ssh-keygen-base
extra-args=-t rsa
[ssh-host-dsa-key]
<=ssh-keygen-base
extra-args=-t dsa
[ssh-host-ecdsa-key]
<=ssh-keygen-base
extra-args=-t ecdsa -b 521
[proftpd-config-file]
<= config-file
context =
section proftpd proftpd
key slapparameter_dict slap-configuration:configuration
[promises]
recipe =
instance-promises =
${proftpd-listen-promise:name}
[publish-connection-parameter]
recipe = slapos.cookbook:publish
url = ${proftpd:url}
username = ${proftpd-password:username}
password = ${proftpd-password:passwd}
instance-input-schema.json 0000664 0000000 0000000 00000000426 13622517253 0034123 0 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Parameters to instantiate PoFTPd",
"additionalProperties": false,
"properties": {
"port": {
"description": "Port number to listen to - default to 8022",
"type": "number"
}
}
}
instance-output-schema.json 0000664 0000000 0000000 00000001052 13622517253 0034320 0 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Values returned by ProFTPd instantiation",
"additionalProperties": false,
"properties": {
"url": {
"description": "URL of the SFTP service",
"pattern": "^sftp://",
"type": "string"
},
"username": {
"description": "Default username",
"type": "string",
"optional": true
},
"password": {
"description": "Password for default username",
"type": "string",
"optional": true
}
},
"type": "object"
}
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/instance.cfg.in 0000664 0000000 0000000 00000002470 13622517253 0032003 0 ustar 00root root 0000000 0000000 [buildout]
parts = switch-softwaretype
eggs-directory = {{ buildout['eggs-directory'] }}
develop-eggs-directory = {{ buildout['develop-eggs-directory'] }}
[slap-configuration]
recipe = slapos.cookbook:slapconfiguration.serialised
computer = ${slap-connection:computer-id}
partition = ${slap-connection:partition-id}
url = ${slap-connection:server-url}
key = ${slap-connection:key-file}
cert = ${slap-connection:cert-file}
[instance-template]
recipe = slapos.recipe.template:jinja2
filename = ${:_buildout_section_name_}.cfg
rendered = ${buildout:parts-directory}/${:_buildout_section_name_}/${:filename}
mode = 0644
extensions = jinja2.ext.do
context =
key slapparameter_dict slap-configuration:configuration
raw software_parts_directory {{ buildout['parts-directory'] }}
raw proftpd_bin {{ proftpd_bin }}
raw ftpasswd_bin {{ ftpasswd_bin }}
raw ftpdctl_bin {{ ftpdctl_bin }}
raw ssh_keygen_bin {{ ssh_keygen_bin }}
raw perl_bin {{ perl_bin }}
raw template_monitor {{ template_monitor }}
[instance-default]
<= instance-template
template = {{ instance_default }}
[switch-softwaretype]
recipe = slapos.cookbook:switch-softwaretype
# XXX Jerome: what is this override for ?
override = {{ dumps(override_switch_softwaretype |default) }}
default = instance-default:rendered
# BBB
RootSoftwareInstance = ${:default}
proftpd-config-file.cfg.in 0000664 0000000 0000000 00000003132 13622517253 0033752 0 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd ServerName "ProFTPD SlapOS"
ServerType standalone
MaxInstances 30
DefaultAddress {{ proftpd['ipv6'] }} {{ proftpd['ipv4']}}
SocketBindTight on
DefaultServer off
Port {{ proftpd['sftp-port'] }}
User {{ proftpd['user'] }}
Group {{ proftpd['group'] }}
ScoreboardFile {{ proftpd['scoreboard-file'] }}
PidFile {{ proftpd['pid-file'] }}
Umask 022
AllowOverwrite on
# SFTP
SFTPEngine on
SFTPHostKey {{ proftpd['ssh-host-rsa-key'] }}
SFTPHostKey {{ proftpd['ssh-host-dsa-key'] }}
SFTPHostKey {{ proftpd['ssh-host-ecdsa-key'] }}
#SFTPAuthorizedUserKeys file:{{ proftpd['ssh-authorized-keys-dir'] }}%u
# Logging
TransferLog {{ proftpd['xfer-log'] }}
SFTPLog {{ proftpd['sftp-log'] }}
BanLog {{ proftpd['ban-log'] }}
# Virtual users
RequireValidShell off
AuthUserFile {{ proftpd['auth-user-file'] }}
# Prevent partially uploaded files to be visible
HiddenStores on
DeleteAbortedStores on
AllowStoreRestart off
# Limitations of running as non-root user
WtmpLog off
# ( we cannot use DefaultRoot ~ )
# Prevent user for escaping their home
DenyAll
AllowAll
# Ban failed logins
MaxLoginAttempts 1
BanEngine on
BanTable {{ proftpd['ban-table'] }}
# 5 failed login attemps in 5 minutes -> ban for 20 minutes
BanOnEvent MaxLoginAttempts 5/00:05:00 00:20:00 "Too many Failed Login Attempts"
BanControlsACLs all allow user {{ proftpd['user'] }}
# This depends on a control socket
ControlsSocket {{ proftpd['control-socket'] }}
ControlsSocketOwner {{ proftpd['user'] }} {{ proftpd['group'] }}
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/software.cfg 0000664 0000000 0000000 00000002220 13622517253 0031415 0 ustar 00root root 0000000 0000000 [buildout]
extends =
../../stack/slapos.cfg
../../stack/monitor/buildout.cfg
../../component/openssh/buildout.cfg
../../component/proftpd/buildout.cfg
buildout.hash.cfg
versions = versions
parts =
slapos-cookbook
proftpd-config-file
instance-profile
[download-file-base]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:filename}
download-only = true
mode = 0644
[proftpd-config-file]
<= download-file-base
[instance-default]
<= download-file-base
[instance-profile]
recipe = slapos.recipe.template:jinja2
template = ${:_profile_base_location_}/${:filename}
rendered = ${buildout:directory}/instance.cfg
mode = 0644
extensions = jinja2.ext.do
context =
section buildout buildout
key instance_default instance-default:target
key proftpd_bin proftpd-output:proftpd
key ftpasswd_bin proftpd-output:ftpasswd
key ftpdctl_bin proftpd-output:ftpdctl
key ssh_keygen_bin openssh-output:keygen
key perl_bin proftpd-output:perl
raw template_monitor ${monitor2-template:rendered}
[versions]
collective.recipe.environment = 1.1.0
collective.recipe.grp = 1.1.0
slapos.recipe.template = 4.4
plone.recipe.command = 1.1
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/software.cfg.json 0000664 0000000 0000000 00000000615 13622517253 0032373 0 ustar 00root root 0000000 0000000 {
"name": "ProFTPd",
"description": "ProFTPd as a SFTP server with virtual users",
"serialisation": "json-in-xml",
"software-type": {
"default": {
"title": "Default",
"description": "ProFTPd, with a default user and users added with command line",
"request": "instance-input-schema.json",
"response": "instance-output-schema.json",
"index": 0
}
}
}
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/test/ 0000775 0000000 0000000 00000000000 13622517253 0030065 5 ustar 00root root 0000000 0000000 slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/test/README.md 0000664 0000000 0000000 00000000043 13622517253 0031341 0 ustar 00root root 0000000 0000000 Tests for ProFTPd software release
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/test/setup.py 0000664 0000000 0000000 00000003720 13622517253 0031601 0 ustar 00root root 0000000 0000000 ##############################################################################
#
# Copyright (c) 2018 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from setuptools import setup, find_packages
version = '0.0.1.dev0'
name = 'slapos.test.proftpd'
with open("README.md") as f:
long_description = f.read()
setup(
name=name,
version=version,
description="Test for SlapOS' ProFTPd",
long_description=long_description,
long_description_content_type='text/markdown',
maintainer="Nexedi",
maintainer_email="info@nexedi.com",
url="https://lab.nexedi.com/nexedi/slapos",
packages=find_packages(),
install_requires=[
'slapos.core',
'slapos.libnetworkcache',
'erp5.util',
'pysftp',
'supervisor',
'psutil',
],
zip_safe=True,
test_suite='test',
)
slapos-7b373b47313d11ce09b232e2ff430dbbdcaddd6b-software-proftpd/software/proftpd/test/test.py 0000664 0000000 0000000 00000021110 13622517253 0031411 0 ustar 00root root 0000000 0000000 ##############################################################################
#
# Copyright (c) 2018 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import os
import shutil
import urlparse
import tempfile
import StringIO
import subprocess
import pysftp
import psutil
from paramiko.ssh_exception import SSHException
from paramiko.ssh_exception import AuthenticationException
from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass
from slapos.testing.utils import findFreeTCPPort
setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass(
os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', 'software.cfg')))
class ProFTPdTestCase(SlapOSInstanceTestCase):
def _getConnection(self, username=None, password=None, hostname=None):
"""Returns a pysftp connection connected to the SFTP
username and password can be specified and default to the ones from
instance connection parameters.
another hostname can also be passed.
"""
# this tells paramiko not to verify host key
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
parameter_dict = self.computer_partition.getConnectionParameterDict()
sftp_url = urlparse.urlparse(parameter_dict['url'])
return pysftp.Connection(
hostname or sftp_url.hostname,
port=sftp_url.port,
cnopts=cnopts,
username=username or parameter_dict['username'],
password=password or parameter_dict['password'])
class TestSFTPListen(ProFTPdTestCase):
def test_listen_on_ipv4(self):
self.assertTrue(self._getConnection(hostname=self._ipv4_address))
def test_does_not_listen_on_all_ip(self):
with self.assertRaises(SSHException):
self._getConnection(hostname='0.0.0.0')
class TestSFTPOperations(ProFTPdTestCase):
"""Tests upload / download features we expect in SFTP server.
"""
def setUp(self):
self.upload_dir = os.path.join(
self.computer_partition_root_path, 'srv', 'proftpd')
def tearDown(self):
for name in os.listdir(self.upload_dir):
path = os.path.join(self.upload_dir, name)
if os.path.isfile(path) or os.path.islink(path):
os.remove(path)
else:
shutil.rmtree(path)
def test_simple_sftp_session(self):
with self._getConnection() as sftp:
# put a file
with tempfile.NamedTemporaryFile() as f:
f.write("Hello FTP !")
f.flush()
sftp.put(f.name, remotepath='testfile')
# it's visible in listdir()
self.assertEqual(['testfile'], sftp.listdir())
# and also in the server filesystem
self.assertEqual(['testfile'], os.listdir(self.upload_dir))
# download the file again, it should have same content
tempdir = tempfile.mkdtemp()
self.addCleanup(lambda: shutil.rmtree(tempdir))
local_file = os.path.join(tempdir, 'testfile')
sftp.get('testfile', local_file)
with open(local_file) as f:
self.assertEqual(f.read(), "Hello FTP !")
def test_uploaded_file_not_visible_until_fully_uploaded(self):
test_self = self
class PartialFile(StringIO.StringIO):
def read(self, *args):
# file is not visible yet
test_self.assertNotIn('destination', os.listdir(test_self.upload_dir))
# it's just a hidden file
test_self.assertEqual(
['.in.destination.'], os.listdir(test_self.upload_dir))
return StringIO.StringIO.read(self, *args)
with self._getConnection() as sftp:
sftp.sftp_client.putfo(PartialFile("content"), "destination")
# now file is visible
self.assertEqual(['destination'], os.listdir(self.upload_dir))
def test_partial_upload_are_deleted(self):
test_self = self
with self._getConnection() as sftp:
class ErrorFile(StringIO.StringIO):
def read(self, *args):
# at this point, file is already created on server
test_self.assertEqual(
['.in.destination.'], os.listdir(test_self.upload_dir))
# simulate a connection closed
sftp.sftp_client.close()
return "something that will not be sent to server"
with self.assertRaises(IOError):
sftp.sftp_client.putfo(ErrorFile(), "destination")
# no half uploaded file is kept
self.assertEqual([], os.listdir(self.upload_dir))
def test_user_cannot_escape_home(self):
with self._getConnection() as sftp:
with self.assertRaisesRegexp(IOError, 'Permission denied'):
sftp.listdir('..')
with self.assertRaisesRegexp(IOError, 'Permission denied'):
sftp.listdir('/')
with self.assertRaisesRegexp(IOError, 'Permission denied'):
sftp.listdir('/tmp/')
class TestUserManagement(ProFTPdTestCase):
def test_user_can_be_added_from_script(self):
with self.assertRaisesRegexp(AuthenticationException,
'Authentication failed'):
self._getConnection(username='bob', password='secret')
subprocess.check_call(
'echo secret | %s/bin/ftpasswd --name=bob --stdin' %
self.computer_partition_root_path,
shell=True)
self.assertTrue(self._getConnection(username='bob', password='secret'))
class TestBan(ProFTPdTestCase):
def test_client_are_banned_after_5_wrong_passwords(self):
# Simulate failed 5 login attempts
for i in range(5):
with self.assertRaisesRegexp(AuthenticationException,
'Authentication failed'):
self._getConnection(password='wrong')
# after that, even with a valid password we cannot connect
with self.assertRaisesRegexp(SSHException, 'Connection reset by peer'):
self._getConnection()
# ban event is logged
with open(os.path.join(self.computer_partition_root_path,
'var',
'log',
'proftpd-ban.log')) as ban_log_file:
self.assertRegexpMatches(
ban_log_file.readlines()[-1],
'login from host .* denied due to host ban')
class TestInstanceParameterPort(ProFTPdTestCase):
@classmethod
def getInstanceParameterDict(cls):
cls.free_port = findFreeTCPPort(cls._ipv4_address)
return {'port': cls.free_port}
def test_instance_parameter_port(self):
parameter_dict = self.computer_partition.getConnectionParameterDict()
sftp_url = urlparse.urlparse(parameter_dict['url'])
self.assertEqual(self.free_port, sftp_url.port)
self.assertTrue(self._getConnection())
class TestFilesAndSocketsInInstanceDir(ProFTPdTestCase):
"""Make sure the instance only have files and socket in software dir.
"""
def setUp(self):
"""sets `self.proftpdProcess` to `psutil.Process` for the running proftpd in the instance.
"""
with self.slap.instance_supervisor_rpc as supervisor:
all_process_info = supervisor.getAllProcessInfo()
# there is only one process in this instance
process_info, = [p for p in all_process_info if p['name'] != 'watchdog']
process = psutil.Process(process_info['pid'])
self.assertEqual('proftpd', process.name()) # sanity check
self.proftpdProcess = process
def test_only_write_file_in_instance_dir(self):
self.assertEqual(
[],
[
f for f in self.proftpdProcess.open_files() if f.mode != 'r'
if not f.path.startswith(self.computer_partition_root_path)
])
def test_only_unix_socket_in_instance_dir(self):
self.assertEqual(
[],
[
s for s in self.proftpdProcess.connections('unix')
if not s.laddr.startswith(self.computer_partition_root_path)
])