Improve the flexibility and ease-of-use for docker-compose deployment (#560)

* docker-compose improvements
    * Use gevent gunicorn workers
    * Makes logs easier to access
* Customization of the logs location
* Improve secret key generation & only generate secret keys if one isn't defined (Closes #123)
* Install requirements required by plugins
selenium-screenshot-testing
Victor "Nate" Graf 2018-02-11 00:52:21 -08:00 committed by Kevin Chung
parent 0aefdcc162
commit 54d12460d5
6 changed files with 65 additions and 25 deletions

View File

@ -2,13 +2,26 @@ import os
''' GENERATE SECRET KEY ''' ''' GENERATE SECRET KEY '''
with open('.ctfd_secret_key', 'a+b') as secret: if not os.environ.get('SECRET_KEY'):
secret.seek(0) # Seek to beginning of file since a+ mode leaves you at the end and w+ deletes the file # Attempt to read the secret from the secret file
# This will fail if the secret has not been written
try:
with open('.ctfd_secret_key', 'rb') as secret:
key = secret.read() key = secret.read()
except (OSError, IOError):
key = None
if not key: if not key:
key = os.urandom(64) key = os.urandom(64)
# Attempt to write the secret file
# This will fail if the filesystem is read-only
try:
with open('.ctfd_secret_key', 'wb') as secret:
secret.write(key) secret.write(key)
secret.flush() secret.flush()
except (OSError, IOError):
pass
''' SERVER SETTINGS ''' ''' SERVER SETTINGS '''
@ -72,6 +85,13 @@ class Config(object):
''' '''
MAILFROM_ADDR = "noreply@ctfd.io" MAILFROM_ADDR = "noreply@ctfd.io"
'''
LOG_FOLDER is the location where logs are written
These are the logs for CTFd key submissions, registrations, and logins
The default location is the CTFd/logs folder
'''
LOG_FOLDER = os.environ.get('LOG_FOLDER') or os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')
''' '''
UPLOAD_FOLDER is the location where files are uploaded. UPLOAD_FOLDER is the location where files are uploaded.
The default destination is the CTFd/uploads folder. If you need Amazon S3 files The default destination is the CTFd/uploads folder. If you need Amazon S3 files

View File

@ -95,28 +95,23 @@ def init_logs(app):
logger_logins.setLevel(logging.INFO) logger_logins.setLevel(logging.INFO)
logger_regs.setLevel(logging.INFO) logger_regs.setLevel(logging.INFO)
try: log_dir = app.config['LOG_FOLDER']
parent = os.path.dirname(__file__)
except:
parent = os.path.dirname(os.path.realpath(sys.argv[0]))
log_dir = os.path.join(parent, 'logs')
if not os.path.exists(log_dir): if not os.path.exists(log_dir):
os.makedirs(log_dir) os.makedirs(log_dir)
logs = [ logs = {
os.path.join(parent, 'logs', 'keys.log'), 'keys': os.path.join(log_dir, 'keys.log'),
os.path.join(parent, 'logs', 'logins.log'), 'logins': os.path.join(log_dir, 'logins.log'),
os.path.join(parent, 'logs', 'registers.log') 'registers': os.path.join(log_dir, 'registers.log')
] }
for log in logs: for log in logs.values():
if not os.path.exists(log): if not os.path.exists(log):
open(log, 'a').close() open(log, 'a').close()
key_log = logging.handlers.RotatingFileHandler(os.path.join(parent, 'logs', 'keys.log'), maxBytes=10000) key_log = logging.handlers.RotatingFileHandler(logs['keys'], maxBytes=10000)
login_log = logging.handlers.RotatingFileHandler(os.path.join(parent, 'logs', 'logins.log'), maxBytes=10000) login_log = logging.handlers.RotatingFileHandler(logs['logins'], maxBytes=10000)
register_log = logging.handlers.RotatingFileHandler(os.path.join(parent, 'logs', 'registers.log'), maxBytes=10000) register_log = logging.handlers.RotatingFileHandler(logs['registers'], maxBytes=10000)
logger_keys.addHandler(key_log) logger_keys.addHandler(key_log)
logger_logins.addHandler(login_log) logger_logins.addHandler(login_log)

View File

@ -8,6 +8,11 @@ WORKDIR /opt/CTFd
VOLUME ["/opt/CTFd"] VOLUME ["/opt/CTFd"]
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
RUN for d in CTFd/plugins/*; do \
if [ -f "$d/requirements.txt" ]; then \
pip install -r $d/requirements.txt; \
fi; \
done;
RUN chmod +x /opt/CTFd/docker-entrypoint.sh RUN chmod +x /opt/CTFd/docker-entrypoint.sh

View File

@ -7,20 +7,34 @@ services:
ports: ports:
- "8000:8000" - "8000:8000"
environment: environment:
- UPLOAD_FOLDER=/var/uploads
- LOG_FOLDER=/var/log/CTFd
- DATABASE_URL=mysql+pymysql://root:ctfd@db/ctfd - DATABASE_URL=mysql+pymysql://root:ctfd@db/ctfd
volumes: volumes:
- .data/CTFd/logs:/opt/CTFd/CTFd/logs - .data/CTFd/logs:/var/log/CTFd
- .data/CTFd/uploads:/opt/CTFd/CTFd/uploads - .data/CTFd/uploads:/var/uploads
- .:/opt/CTFd:ro
depends_on: depends_on:
- db - db
networks:
default:
internal:
db: db:
image: mariadb:10.2 image: mariadb:10.2
# This command is required to set important mariadb defaults restart: always
command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --wait_timeout=28800]
environment: environment:
- MYSQL_ROOT_PASSWORD=ctfd - MYSQL_ROOT_PASSWORD=ctfd
- MYSQL_USER=ctfd - MYSQL_USER=ctfd
- MYSQL_PASSWORD=ctfd - MYSQL_PASSWORD=ctfd
volumes: volumes:
- .data/mysql:/var/lib/mysql - .data/mysql:/var/lib/mysql
networks:
internal:
# This command is required to set important mariadb defaults
command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --wait_timeout=28800]
networks:
default:
internal:
internal: true

View File

@ -15,4 +15,9 @@ if [ -n "$DATABASE_URL" ]
fi fi
echo "Starting CTFd" echo "Starting CTFd"
gunicorn --bind 0.0.0.0:8000 -w 1 'CTFd:create_app()' --access-logfile '/opt/CTFd/CTFd/logs/access.log' --error-logfile '/opt/CTFd/CTFd/logs/error.log' gunicorn 'CTFd:create_app()' \
--bind '0.0.0.0:8000' \
--workers 1 \
--worker-class 'gevent' \
--access-logfile "${LOG_FOLDER:-/opt/CTFd/CTFd/logs}/access.log" \
--error-logfile "${LOG_FOLDER:-/opt/CTFd/CTFd/logs}/error.log"

View File

@ -18,3 +18,4 @@ mistune==0.8.3
netaddr==0.7.19 netaddr==0.7.19
redis==2.10.6 redis==2.10.6
datafreeze==0.1.0 datafreeze==0.1.0
gevent==1.2.2