Source code for containers.manager

# Checkpot - Honeypot Checker
# Copyright (C) 2018  Vlad Florea
#
# 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, version 3 of the License.
#
# DISCLAIMER: All prerequisites (containers, additional programs, etc.)
# and libraries that might be needed to run this program are property
# of their original authors and carry their own separate licenses that
# you should read to inform yourself about their terms.
#
# 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.
#
# As this software is PROVIDED WITH ABSOLUTELY NO WARRANTY OF ANY KIND,
# YOU USE THIS SOFTWARE AT YOUR OWN RISK!
#
# By using this tool YOU TAKE FULL LEGAL RESPONSIBILITY FOR ANY
# POSSIBLE OUTCOME.
#
# We strongly recommend that you read all the information in the README.md
# file (found in the root folder of this project) and even the
# documentation (which you can find locally in the /docs/ folder or
# at http://checkpot.readthedocs.io/) to make sure you fully
# understand how this tool works and consult all laws that apply
# to your use case.
#
# We strongly suggest that you keep this notice intact for all files.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
# The local copy of the license should be located in the root folder of the app
# in the file gpl-3.0.txt.
#
# You can contact the author and team at any time via the Checkpot official
# GitHub page: https://github.com/honeynet/checkpot or via the Honeynet
# official Slack channel: https://gsoc-slack.honeynet.org/ or
#                         https://honeynetpublic.slack.com/
#
# You can contact us at any time and with any question, we are here to help!
#
# If you consider any information in this copyright notice might be incorrect,
# outdated, arises any questions, raises any problems, etc. please contact us
# and we will make it our top priority to fix it. We have the deepest respect
# for the work of all authors and for all of our users.

import os
import docker
import docker.errors
import ast
from termcolor import colored


[docs]class Manager: """Facilitates working with honeypot containers"""
[docs] def __init__(self, verbose=True, logfile=None, custom_client=None, build_info=True): """ :param verbose: generate logs related to container operations :param logfile: write logs to a file :param custom_client: specify a custom Docker Client :param build_info: show log during image build phase """ if custom_client: assert isinstance(custom_client, docker.DockerClient) self._client = custom_client else: self._client = docker.APIClient() self._verbose = verbose self._logfile = logfile self._build_info = build_info self._tag = "MANAGER"
def _log(self, *args): """ Creates a new line in the log with given description :param args: log description """ if self._verbose: if self._logfile: with open(self._logfile, 'a') as f: print(*args, file=f) else: print(colored(self._tag, color="magenta"), " : ", *args)
[docs] def build_honeypot(self, name): """ Builds the required image (if it doesn't exist) and then creates a container from it :param name: container name """ try: self._client.inspect_image(name) except docker.errors.ImageNotFound: self._log("Image not found, building image for ", name, " ...") container_path = os.path.join(os.path.dirname(__file__), name) if not os.path.exists(os.path.join(container_path, 'Dockerfile')): raise BuildError("Dockerfile for container ", name, "not found") output = self._client.build(path=os.path.join(os.path.dirname(__file__), name), tag=name) for line in output: # log is supplied as dict containing the required string # TODO parsed = ast.literal_eval(line.decode('utf-8').replace('\r\n', '')) if self._build_info: self._log(line.decode('utf-8').replace('\r', '').replace('\n', '')) if name != "honeypy": self._client.create_container(image=name, detach=True, name=name)
[docs] def start_honeypot(self, name): """ Starts the chosen container :param name: container name """ # check if the container exists try: self._client.inspect_container(name) except docker.errors.NotFound: self._log("Container ", name, " not found, creating new container from image ...") try: self.build_honeypot(name) except BuildError as e: self._log("Build failed:", e) return else: self._log("Container ", name, " found") self._log("Starting container", name) if name == "honeypy": docker.from_env().containers.run(name, cap_add='NET_ADMIN', detach=True, name=name) else: self._client.start(name)
[docs] def get_honeypot_ip(self, name): """ Gets the IP address of a running container :param name: container name :return: container ip address """ # TODO graceful exit when container does not exist container_details = self._client.inspect_container(name) target_ip = container_details['NetworkSettings']['IPAddress'] return target_ip
[docs] def stop_honeypot(self, name): """ Stops the chosen container :param name: container name """ # TODO graceful exit when container does not exist self._log("Stopping container ", name) self._client.stop(name)
[docs] def stop_all_honeypots(self): """Stops all active containers""" available_honeypots = self.get_available_honeypots() for hp in available_honeypots: try: self.stop_honeypot(hp) except docker.errors.NotFound: print("Container", hp, "not found") continue
[docs] def build_all_honeypots(self): """Builds all available containers""" available_honeypots = self.get_available_honeypots() for hp in available_honeypots: self.build_honeypot(hp)
[docs] @staticmethod def get_available_honeypots(): """Returns a list with the names of all available honeypots""" containers_folder = os.path.dirname(os.path.abspath(__file__)) # folders contained in this module represent the available honeypots # folders starting with underscore are considered hidden return [f.name for f in os.scandir(containers_folder) if f.is_dir() and f.name[0] != '_']
[docs] def clean_honeypot(self, name): """ Removes container and underlying image for chosen container :param name: container name """ self._log("Cleaning container ", name) self._client.stop(name) self._client.remove_container(name, force=True) self._client.remove_image(name, force=True)
[docs] def clean_all_honeypots(self): """ Removes all containers and underlying images """ available_honeypots = self.get_available_honeypots() for hp in available_honeypots: try: self.clean_honeypot(hp) except docker.errors.NotFound: print("Container", hp, "not found") continue
[docs]class BuildError(Exception): """Raised when build fails"""
[docs] def __init__(self, *report): """ :param report: description of the error """ self.value = " ".join(str(r) for r in report)
def __str__(self): return repr(self.value) def __repr__(self): return 'BuildError exception ' + self.value