def setUp(self): self._config = Config() self._client = create_autospec(docker.APIClient) self._helper = DockerHelper(self._config, self._client) self._cid = "cont_id1" self._cid2 = "cont_id2" self._params = {"id": self._cid, "param1": "1"} self._params2 = {"id": self._cid2, "param1": "2"}
def __init__(self, rules, config=Config(), container_count=1, mem_limit=0, cpu_share=0, cpu_period=0, cpu_quota=0, mem_usage=0): self.judge = Judge(rules, "container", config) cid = '7de82a4e90f1bd4fd022bcce298e7277b8aec009e222892e44769d6c636b8205' params = { 'Image': 'sha256:47bcc53f74dc94b1920f0b34f6036096526296767650f223433fe65c35f149eb', 'HostConfig': { 'Isolation': '', 'IOMaximumBandwidth': 0, 'CpuPercent': 0, 'ReadonlyRootfs': False, 'CpuQuota': cpu_quota, 'IpcMode': '', 'RestartPolicy': { 'Name': 'no', 'MaximumRetryCount': 0 }, 'BlkioDeviceWriteIOps': None, 'PidMode': '', 'BlkioDeviceWriteBps': None, 'PortBindings': { '8080/tcp': [{ 'HostIp': '', 'HostPort': '8080' }] }, 'BlkioDeviceReadIOps': None, 'ContainerIDFile': '', 'ConsoleSize': [0, 0], 'IOMaximumIOps': 0, 'ShmSize': 67108864, 'GroupAdd': None, 'Cgroup': '', 'DiskQuota': 0, 'DnsSearch': [], 'BlkioWeight': 0, 'Dns': [], 'OomKillDisable': False, 'CpuPeriod': cpu_period, 'DnsOptions': [], 'NetworkMode': 'default', 'CpuCount': 0, 'KernelMemory': 0, 'Links': None, 'Memory': mem_limit, 'VolumesFrom': None, 'MemorySwap': -1, 'Ulimits': None, 'UsernsMode': '', 'Devices': [], 'CpusetMems': '', 'MemorySwappiness': -1, 'Privileged': False, 'CpuShares': cpu_share, 'CpusetCpus': '', 'VolumeDriver': '', 'SecurityOpt': None, 'OomScoreAdj': 0, 'PublishAllPorts': False, 'AutoRemove': False, 'MemoryReservation': 0, 'BlkioDeviceReadBps': None, 'PidsLimit': 0, 'Runtime': 'runc', 'UTSMode': '', 'Binds': ['/:/tmp'], 'CapDrop': None, 'ExtraHosts': None, 'BlkioWeightDevice': None, 'LogConfig': { 'Type': 'json-file', 'Config': {} }, 'CgroupParent': '', 'CapAdd': None }, 'MountLabel': '', 'AppArmorProfile': '', 'Driver': 'aufs', 'ResolvConfPath': '/opt/docker/containers/7de82a4e90f1bd4fd022bcce298e7277b8aec009e222892e44769d6c636b8205/resolv.conf', 'ExecIDs': None, 'Created': '2016-10-27T19:30:04.433919486Z', 'Path': 'sleep', 'Id': '7de82a4e90f1bd4fd022bcce298e7277b8aec009e222892e44769d6c636b8205', 'State': { 'StartedAt': '2016-10-27T19:30:04.770734821Z', 'Status': 'running', 'ExitCode': 0, 'OOMKilled': False, 'Dead': False, 'FinishedAt': '0001-01-01T00:00:00Z', 'Pid': 25800, 'Error': '', 'Paused': False, 'Running': True, 'Restarting': False }, 'Mounts': [{ 'Destination': '/tmp', 'Source': '/', 'Mode': '', 'RW': True, 'Propagation': 'rprivate' }], 'HostsPath': '/opt/docker/containers/7de82a4e90f1bd4fd022bcce298e7277b8aec009e222892e44769d6c636b8205/hosts', 'ProcessLabel': '', 'GraphDriver': { 'Data': None, 'Name': 'aufs' }, 'LogPath': '/opt/docker/containers/7de82a4e90f1bd4fd022bcce298e7277b8aec009e222892e44769d6c636b8205/7de82a4e90f1bd4fd022bcce298e7277b8aec009e222892e44769d6c636b8205-json.log', 'Name': '/testing_vro', 'Config': { 'OpenStdin': True, 'Image': 'busybox', 'ExposedPorts': { '8080/tcp': {} }, 'Cmd': ['sleep', '1000'], 'Hostname': '7de82a4e90f1', 'Volumes': None, 'Labels': {}, 'Tty': True, 'Env': None, 'Domainname': '', 'OnBuild': None, 'StdinOnce': True, 'User': '', 'AttachStdin': True, 'AttachStderr': True, 'AttachStdout': True, 'Entrypoint': None, 'WorkingDir': '' }, 'NetworkSettings': { 'GlobalIPv6PrefixLen': 0, 'SecondaryIPv6Addresses': None, 'GlobalIPv6Address': '', 'SandboxID': '2df12d715799cfdd03f2f421ea64b19b05ec60ff14ae86a29723e0c1b3199d47', 'Bridge': '', 'EndpointID': 'a5d310ae48dc6b7f70754ff94a0f242507472bbcc3dfd81a5a3da319e19695ef', 'SandboxKey': '/var/run/docker/netns/2df12d715799', 'SecondaryIPAddresses': None, 'Networks': { 'bridge': { 'GlobalIPv6PrefixLen': 0, 'GlobalIPv6Address': '', 'Links': None, 'IPPrefixLen': 16, 'EndpointID': 'a5d310ae48dc6b7f70754ff94a0f242507472bbcc3dfd81a5a3da319e19695ef', 'IPAddress': '172.17.0.2', 'Gateway': '172.17.0.1', 'Aliases': None, 'MacAddress': '02:42:ac:11:00:02', 'IPAMConfig': None, 'IPv6Gateway': '', 'NetworkID': 'd5ce26d62a33223deca40e3d99232557205af38093bbd23922cf6f1ed5c9cf56' } }, 'MacAddress': '02:42:ac:11:00:02', 'Ports': { '8080/tcp': [{ 'HostIp': '0.0.0.0', 'HostPort': '8080' }] }, 'IPv6Gateway': '', 'IPPrefixLen': 16, 'LinkLocalIPv6PrefixLen': 0, 'HairpinMode': False, 'IPAddress': '172.17.0.2', 'LinkLocalIPv6Address': '', 'Gateway': '172.17.0.1' }, 'Args': ['1000'], 'HostnamePath': '/opt/docker/containers/7de82a4e90f1bd4fd022bcce298e7277b8aec009e222892e44769d6c636b8205/hostname', 'RestartCount': 0 } metrics = { 'blkio_stats': { 'io_wait_time_recursive': [], 'io_serviced_recursive': [], 'io_merged_recursive': [], 'io_queue_recursive': [], 'io_service_time_recursive': [], 'sectors_recursive': [], 'io_time_recursive': [], 'io_service_bytes_recursive': [] }, 'memory_stats': { 'limit': 536870912, 'usage': mem_usage, 'stats': { 'total_active_file': 0, 'pgfault': 318, 'hierarchical_memory_limit': 536870912, 'total_pgfault': 318, 'mapped_file': 0, 'total_rss': 32768, 'pgmajfault': 0, 'total_active_anon': 32768, 'inactive_anon': 0, 'rss': 32768, 'inactive_file': 0, 'active_anon': 32768, 'total_pgpgin': 293, 'active_file': 0, 'unevictable': 0, 'pgpgout': 285, 'total_pgpgout': 285, 'cache': 0, 'total_dirty': 0, 'total_writeback': 0, 'total_mapped_file': 0, 'total_inactive_file': 0, 'writeback': 0, 'rss_huge': 0, 'dirty': 0, 'pgpgin': 293, 'total_inactive_anon': 0, 'total_rss_huge': 0, 'total_pgmajfault': 0, 'total_unevictable': 0, 'total_cache': 0 }, 'max_usage': 1581056, 'failcnt': 0 }, 'pids_stats': { 'current': 1 }, 'cpu_stats': { 'throttling_data': { 'throttled_time': 0, 'throttled_periods': 0, 'periods': 0 }, 'system_cpu_usage': 374267840000000, 'cpu_usage': { 'total_usage': 29623824, 'usage_in_usermode': 20000000, 'usage_in_kernelmode': 0, 'percpu_usage': [ 773217, 129812, 355729, 204383, 24629686, 493977, 520620, 2516400 ] } }, 'precpu_stats': { 'throttling_data': { 'throttled_time': 0, 'throttled_periods': 0, 'periods': 0 }, 'system_cpu_usage': 374259930000000, 'cpu_usage': { 'total_usage': 29623824, 'usage_in_usermode': 20000000, 'usage_in_kernelmode': 0, 'percpu_usage': [ 773217, 129812, 355729, 204383, 24629686, 493977, 520620, 2516400 ] } }, 'networks': { 'eth0': { 'rx_dropped': 0, 'tx_packets': 7, 'rx_bytes': 8719, 'tx_dropped': 0, 'rx_errors': 0, 'tx_bytes': 578, 'rx_packets': 80, 'tx_errors': 0 } }, 'read': '2016-10-27T19:30:13.751688232Z' } self.containers = [] for cnt in range(container_count): self.containers.append( Container(cid, DockerHelper(None, None).rename_keys_to_lower( copy(params)), metrics, cnt, check_source=CheckSource.Event))
from rx import Observable from rx.concurrency import NewThreadScheduler from urllib import parse from dockerenforcer.config import Config, ConfigEncoder, Mode from dockerenforcer.docker_helper import DockerHelper, Container, CheckSource from dockerenforcer.docker_image_helper import DockerImageHelper from dockerenforcer.killer import Killer, Judge, TriggerHandler from rules.rules import rules from request_rules.request_rules import request_rules from whitelist_rules.whitelist_rules import whitelist_rules config = Config() client: APIClient = APIClient(base_url=config.docker_socket, timeout=config.docker_req_timeout_sec) docker_helper = DockerHelper(config, client) docker_image_helper = DockerImageHelper(config, client) judge = Judge(rules, "container", config, run_whitelists=True, custom_whitelist_rules=whitelist_rules, docker_image_helper=docker_image_helper) requests_judge = Judge(request_rules, "request", config, run_whitelists=False) jurek = Killer(docker_helper, config.mode) trigger_handler = TriggerHandler() containers_regex = re.compile("^(/v.+?)?/containers/.+?$") def create_app(): def setup_logging():
from rx import Observable from rx.concurrency import ThreadPoolScheduler, NewThreadScheduler from dockerenforcer.config import Config, ConfigEncoder from dockerenforcer.docker_helper import DockerHelper from dockerenforcer.killer import Killer, Judge, TriggerHandler from rules.rules import rules from pygments import highlight from pygments.lexers.data import JsonLexer from pygments.lexers.python import Python3Lexer from pygments.formatters.html import HtmlFormatter version = "0.5.0" config = Config() docker_helper = DockerHelper(config) judge = Judge(rules, config.stop_on_first_violation) jurek = Killer(docker_helper, config.mode) trigger_handler = TriggerHandler() def create_app(): def setup_logging(): handler = StreamHandler(stream=sys.stdout) handler.setLevel(config.log_level) formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) flask_app.logger.addHandler(handler) flask_app.logger.setLevel(config.log_level) flask_app.logger.name = "docker_enforcer"
class DockerHelperTests(unittest.TestCase): def setUp(self): self._config = Config() self._client = create_autospec(docker.APIClient) self._helper = DockerHelper(self._config, self._client) self._cid = "cont_id1" self._cid2 = "cont_id2" self._params = {"id": self._cid, "param1": "1"} self._params2 = {"id": self._cid2, "param1": "2"} def test_kill_container(self): c = Container(self._cid, params=self._params, metrics={}, position=0, check_source=CheckSource.Periodic) self._helper.kill_container(c) self._client.stop.assert_called_once_with(self._cid) def test_get_params_no_cache(self): self._client.inspect_container.return_value = self._params params = self._helper.get_params(self._cid) self._client.inspect_container.assert_called_once_with(self._cid) self.assertDictEqual( params, self._helper.rename_keys_to_lower(copy(self._params))) def test_get_params_fill_cache(self): self._config.cache_params = True self._client.inspect_container.return_value = self._params params = self._helper.get_params(self._cid) self._client.inspect_container.assert_called_once_with(self._cid) self.assertDictEqual( params, self._helper.rename_keys_to_lower(copy(self._params))) self.assertDictEqual( self._helper._params_cache[self._cid], self._helper.rename_keys_to_lower(copy(self._params))) def test_get_params_from_cache_and_remove(self): self._config.cache_params = True self._helper._params_cache[self._cid] = self._params params = self._helper.get_params(self._cid) self._client.inspect_container.assert_not_called() self.assertDictEqual(params, self._params) self.assertDictEqual(self._helper._params_cache[self._cid], self._params) # now try to remove cached params self._helper.remove_from_cache(self._cid) self.assertFalse(self._cid in self._helper._params_cache) def test_purge_cache(self): self._config.cache_params = True self._helper._params_cache[self._cid] = self._params self._helper._params_cache[self._cid2] = self._params2 self._client.inspect_container.assert_not_called() self._helper.purge_cache([self._cid]) self.assertFalse(self._cid2 in self._helper._params_cache) def test_check_containers(self): self._config.disable_metrics = True self._client.containers.return_value = [{ 'Id': self._cid }, { 'Id': self._cid2 }] self._client.inspect_container.side_effect = [ self._params, self._params2 ] containers = list(self._helper.check_containers(CheckSource.Periodic)) self._client.containers.assert_called_once_with(quiet=True) self._client.inspect_container.side_effect = [ self._params, self._params2 ] self.assertEqual(len(containers), 2) self.assertEqual(containers[0].cid, self._cid) self.assertEqual(containers[0].check_source, CheckSource.Periodic) self.assertDictEqual( containers[0].params, self._helper.rename_keys_to_lower(copy(self._params))) self.assertEqual(containers[1].cid, self._cid2) self.assertEqual(containers[1].check_source, CheckSource.Periodic) self.assertDictEqual( containers[1].params, self._helper.rename_keys_to_lower(copy(self._params2))) def test_get_events(self): res = [{ u'from': u'image/with:tag', u'id': self._cid, u'status': u'start', u'time': 1423339459 }, { u'from': u'image/with:tag', u'id': self._cid2, u'status': u'start', u'time': 1423339459 }] self._client.events.return_value = res events = list(self._helper.get_events_observable()) self._client.events.assert_called_once_with(decode=True) self.assertEqual(len(events), 2) self.assertEqual(events[0]['id'], self._cid) self.assertEqual(events[1]['id'], self._cid2) def test_rename_keys_to_lower(self): params = { "Privileged": True, "CapAdd": ["SYS_ADMIN"], "HostConfig": { "MOUNTS": [{ "TESTLIST0KEY": "..." }, { "TESTLIST1KEY": "..." }] }, "TEST2KEY": "TEST2VALUE" } params = self._helper.rename_keys_to_lower(params) self.assertTrue("privileged" in params) self.assertFalse("Privileged" in params) self.assertTrue("capadd" in params) self.assertFalse("CapAdd" in params) self.assertTrue("hostconfig" in params) self.assertFalse("HostConfig" in params) self.assertTrue("mounts" in params['hostconfig']) self.assertFalse("MOUNTS" in params['hostconfig']) self.assertTrue("testlist0key" in params['hostconfig']["mounts"][0]) self.assertFalse("TESTLIST0KEY" in params['hostconfig']["mounts"][0]) self.assertTrue("testlist1key" in params['hostconfig']["mounts"][1]) self.assertFalse("TESTLIST1KEY" in params['hostconfig']["mounts"][1])