diff --git a/.travis.yml b/.travis.yml index f733e25..47809cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,13 @@ language: python python: - "2.7" sudo: false + +services: + - docker + install: pip install -r requirements.txt + script: + - docker build . - flake8 --ignore F401 securitybot/ - PYTHONPATH=$(pwd) py.test -v tests/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a4096de --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:2.7 + +COPY . /securitybot + +ENV PYTHONPATH $PYTHONPATH:/securitybot + +RUN apt-get update +RUN apt-get install -y mysql-client +RUN pip install -r /securitybot/requirements.txt +RUN useradd -N -s '/bin/false' -e '' securitybot + +USER securitybot + +WORKDIR /securitybot + +ENTRYPOINT ["/securitybot/docker_entrypoint.sh"] diff --git a/README.md b/README.md index 440aa64..da2b00c 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,46 @@ If the following were all generated successfully, Securitybot should be up and r To test it, message the bot user it's assigned to and say `hi`. To test the process of dealing with an alert, message `test` to test the bot. +#### Environment variable +To prevent having to modify the scripts, you can set the following environment variables to configure the bot: + +|Variable|Setting| +|--------|-------| +|SLACK_API_TOKEN|Slack API Token| +|REPORTING_CHANNEL|Slack Channel ID| +|DUO_INTEGRATION_KEY|Duo Integration Key| +|DUO_SECRET_KEY|Duo Secret Key| +|DUO_ENDPOINT|Duo Endpoint| +|DB_HOST|MySQL Hostname| +|DB_USER|MySQL Username| +|DB_PASS|MySQL Password| +|DB_NAME|MySQL Database name| + +#### Docker + +Dockerfile is included to generate a Docker Image to run the bot. Entrypoint script will wait for database startup and initialize database if it does not already exist. Entrypoint takes one of two arguments: + +* bot: Starts the main bot +* frontend: starts the frontend and API server + +Run configuration will be based on the environment variables above. + +Example: + +Bot: +``` +docker build --tag securitybot +docker run -e DB_NAME=securitybot -e DB_USER=root -e DB_HOST=127.0.0.1 -e DB_PASS=password -e SLACK_API_TOKEN= -e REPORTING_CHANNEL=security-notifications -e DUO_INTEGRATION_KEY= -e DUO_SECRET_KEY= -e DUO_ENDPOINT= securitybot bot +``` + +Frontend: +``` +docker build --tag securitybot +docker run -p 8888:8888 -e DB_NAME=securitybot -e DB_USER=root -e DB_HOST=127.0.0.1 -e DB_PASS=password securitybot frontend +``` + +A docker-compose file is provided for ease of use. docker-compose up will start database, bot and frontend. Frontend and API server will be available on port 8888. Database content will be stored in persistent volume `mysql-securitybot`. Slack-related, Duo related and database password environment variables must be set for docker-compose stack to run properly. + ## Architecture Securitybot was designed to be as modular as possible. This means that it's possible to easily swap out chat systems, 2FA providers, and alerting data sources. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..bbd4216 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +version: '3' +services: + db: + image: mysql + environment: + - MYSQL_ROOT_PASSWORD=${DB_PASS} + volumes: + - mysql-securitybot:/var/lib/mysql + ports: + - "3306" + bot: + build: . + image: securitybot + command: bot + environment: + - SLACK_API_TOKEN=${SLACK_API_TOKEN} + - REPORTING_CHANNEL=${REPORTING_CHANNEL} + - DUO_INTEGRATION_KEY=${DUO_INTEGRATION_KEY} + - DUO_SECRET_KEY=${DUO_SECRET_KEY} + - DUO_ENDPOINT=${DUO_ENDPOINT} + - DB_HOST=db + - DB_USER=root + - DB_PASS=${DB_PASS} + - DB_NAME=securitybot + frontend: + build: . + image: securitybot + command: frontend + ports: + - "8888:8888" + environment: + - DB_HOST=db + - DB_USER=root + - DB_PASS=${DB_PASS} + - DB_NAME=securitybot +volumes: + mysql-securitybot: diff --git a/docker_entrypoint.sh b/docker_entrypoint.sh new file mode 100755 index 0000000..b622d6b --- /dev/null +++ b/docker_entrypoint.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + + +end=$((SECONDS + 120)) + +# Wait max of 2 minutes for database to come up +until mysql --user $DB_USER --password=$DB_PASS --host=$DB_HOST -e"quit" || [ $SECONDS -ge $end ]; do echo "Waiting for DB"; sleep 2; done; + +# Check if database is initialized +if ! mysql --user $DB_USER --password=$DB_PASS --host=$DB_HOST -e"use $DB_NAME"; then + echo "Creating database" + mysql --user $DB_USER --password=$DB_PASS --host=$DB_HOST -e"create database $DB_NAME" + echo "Populating database" + python /securitybot/util/db_up.py +fi + + +if [ "$1" = 'bot' ]; then + echo "Starting bot" + shift + exec python /securitybot/main.py "$@" +elif [ "$1" = 'frontend' ]; then + echo "Starting frontend" + shift + exec python /securitybot/frontend/securitybot_frontend.py "$@" +fi + +exec "$@" diff --git a/main.py b/main.py index 9d81a72..b67cf2e 100644 --- a/main.py +++ b/main.py @@ -6,14 +6,16 @@ from securitybot.tasker.sql_tasker import SQLTasker from securitybot.auth.duo import DuoAuth from securitybot.sql import init_sql +from os import getenv + import duo_client CONFIG = {} -SLACK_KEY = 'slack_api_token' -DUO_INTEGRATION = 'duo_integration_key' -DUO_SECRET = 'duo_secret_key' -DUO_ENDPOINT = 'duo_endpoint' -REPORTING_CHANNEL = 'some_slack_channel_id' +SLACK_KEY = getenv('SLACK_API_TOKEN', 'slack_api_token') +DUO_INTEGRATION = getenv('DUO_INTEGRATION_KEY', 'duo_integration_key') +DUO_SECRET = getenv('DUO_SECRET_KEY', 'duo_secret_key') +DUO_ENDPOINT = getenv('DUO_ENDPOINT', 'duo_endpoint') +REPORTING_CHANNEL = getenv('REPORTING_CHANNEL', 'some_slack_channel_id') ICON_URL = 'https://dl.dropboxusercontent.com/s/t01pwfrqzbz3gzu/securitybot.png' def init(): diff --git a/securitybot/sql.py b/securitybot/sql.py index 43ebd7d..f5434ec 100644 --- a/securitybot/sql.py +++ b/securitybot/sql.py @@ -4,8 +4,15 @@ import MySQLdb import logging +from os import getenv from typing import Any, Sequence +DB_HOST = getenv('DB_HOST', 'localhost') +DB_USER = getenv('DB_USER', 'root') +DB_PASS = getenv('DB_PASS', '') +DB_NAME = getenv('DB_NAME', 'securitybot') + + class SQLEngine(object): # Whether the singleton has been instantiated _host = None # type: str @@ -90,4 +97,4 @@ class SQLEngineException(Exception): def init_sql(): # type: () -> None '''Initializes SQL.''' - SQLEngine('localhost', 'root', '', 'securitybot') + SQLEngine(DB_HOST, DB_USER, DB_PASS, DB_NAME) diff --git a/util/db_up.py b/util/db_up.py index fd1fdd1..901105d 100644 --- a/util/db_up.py +++ b/util/db_up.py @@ -1,16 +1,18 @@ #!/usr/bin/env python import MySQLdb import sys +from os import getenv # DB CONFIG GOES HERE -host = 'localhost' -user = 'root' -passwd= '' +host = getenv('DB_HOST', 'localhost') +user = getenv('DB_USER', 'root') +passwd=getenv('DB_PASS', '') +dbname=getenv('DB_NAME', 'securitybot') db = MySQLdb.connect(host=host, user=user, passwd=passwd, - db='securitybot') + db=dbname) cur = db.cursor()