Port to Docker

This commit is contained in:
Michael Zhang 2022-11-28 20:40:12 -06:00
parent 4225cc4dde
commit fe32273dcc
No known key found for this signature in database
GPG Key ID: BDA47A31A3C8EE6B
81 changed files with 1496 additions and 45 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

3
.gitignore vendored
View File

@ -8,3 +8,6 @@
__pycache__
*.pyc
ubuntu-xenial-16.04-cloudimg-console.log
ctf-data
.direnv

7
db/init.d/init.sql Executable file
View File

@ -0,0 +1,7 @@
CREATE DATABASE `app`;
CREATE USER `app`@'%' IDENTIFIED BY 'hellosu';
GRANT ALL PRIVILEGES ON app.* TO app@'%';
CREATE DATABASE `minio`;
CREATE USER minio@'%' IDENTIFIED BY 'hellosu';
GRANT ALL PRIVILEGES ON minio.* TO minio@'%';

37
docker-compose.yml Normal file
View File

@ -0,0 +1,37 @@
version: "3"
services:
nginx:
build: nginx
app:
build: server
depends_on: [db]
ports: [3000:5000]
volumes:
- ./server:/app
environment:
- SECRET_KEY=ad88fec19a7641e5de308e45dd4fa1c5
- DATABASE_URL=mysql://app:hellosu@db:3306/app
- WAIT_HOSTS=db:3306
- WAIT_HOSTS_TIMEOUT=300
- WAIT_SLEEP_INTERVAL=10
- WAIT_HOST_CONNECT_TIMEOUT=30
db:
image: mariadb
expose: [3306]
volumes:
- ./db/init.d:/docker-entrypoint-initdb.d
- ./ctf-data/mariadb:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=45694fd9e39afc4a3597bc2797620e15
files:
image: minio/minio
volumes:
- ./ctf-data/minio:/data
redis:
image: redis

40
flake.lock Normal file
View File

@ -0,0 +1,40 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1653936696,
"narHash": "sha256-M6bJShji9AIDZ7Kh7CPwPBPb/T7RiVev2PAcOi4fxDQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ce6aa13369b667ac2542593170993504932eb836",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

15
flake.nix Normal file
View File

@ -0,0 +1,15 @@
{
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
pythonPackages = pkgs.python310Packages;
in {
devShell = pkgs.mkShell {
buildInputs = (with pkgs; [ libmysqlclient ])
++ (with pythonPackages; [ poetry ]);
SECRET_KEY = "ad88fec19a7641e5de308e45dd4fa1c5";
};
});
}

1
nginx/Dockerfile Normal file
View File

@ -0,0 +1 @@
FROM nginx

0
server/.dockerignore Normal file
View File

20
server/Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM python:3
ENV FLASK_DEBUG=1
RUN apt-get update -y && apt-get install -y --no-install-recommends \
libmariadb-dev \
;
RUN pip install poetry
ENV WAIT_VERSION 2.7.2
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/$WAIT_VERSION/wait /wait
RUN chmod +x /wait
RUN mkdir -p /app
WORKDIR /app
COPY poetry.lock .
COPY pyproject.toml .
RUN poetry install
CMD ["sh", "-c", "/wait && poetry run flask db upgrade && poetry run flask run --host 0.0.0.0"]

0
server/app.py Executable file → Normal file
View File

0
server/cloud-provision.sh Executable file → Normal file
View File

5
server/easyctf/__init__.py Executable file → Normal file
View File

@ -8,7 +8,7 @@ from flask_login import current_user
def create_app(config=None):
app = Flask(__name__, static_folder="assets", static_path="/assets")
app = Flask(__name__, static_folder="assets", static_url_path="/assets")
hostname = socket.gethostname()
if not config:
@ -16,10 +16,11 @@ def create_app(config=None):
config = Config()
app.config.from_object(config)
from easyctf.objects import cache, db, login_manager, sentry
from easyctf.objects import cache, db, login_manager, sentry, migrate
import easyctf.models
cache.init_app(app)
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)
if app.config.get("ENVIRONMENT") != "development":
sentry.init_app(app, logging=True, level=logging.WARNING)

0
server/easyctf/assets/css/bootstrap.min.css vendored Executable file → Normal file
View File

0
server/easyctf/assets/css/font-awesome.css vendored Executable file → Normal file
View File

0
server/easyctf/assets/css/font-awesome.min.css vendored Executable file → Normal file
View File

0
server/easyctf/assets/css/main.css Executable file → Normal file
View File

0
server/easyctf/assets/fonts/FontAwesome.otf Executable file → Normal file
View File

0
server/easyctf/assets/fonts/OFL.txt Executable file → Normal file
View File

0
server/easyctf/assets/fonts/SourceSansPro-Black.ttf Executable file → Normal file
View File

View File

0
server/easyctf/assets/fonts/SourceSansPro-Bold.ttf Executable file → Normal file
View File

View File

View File

View File

0
server/easyctf/assets/fonts/SourceSansPro-Italic.ttf Executable file → Normal file
View File

0
server/easyctf/assets/fonts/SourceSansPro-Light.ttf Executable file → Normal file
View File

View File

0
server/easyctf/assets/fonts/SourceSansPro-Regular.ttf Executable file → Normal file
View File

0
server/easyctf/assets/fonts/SourceSansPro-SemiBold.ttf Executable file → Normal file
View File

View File

0
server/easyctf/assets/fonts/fontawesome-webfont.eot Executable file → Normal file
View File

0
server/easyctf/assets/fonts/fontawesome-webfont.svg Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 434 KiB

0
server/easyctf/assets/fonts/fontawesome-webfont.ttf Executable file → Normal file
View File

0
server/easyctf/assets/fonts/fontawesome-webfont.woff Executable file → Normal file
View File

0
server/easyctf/assets/fonts/fontawesome-webfont.woff2 Executable file → Normal file
View File

0
server/easyctf/assets/js/bootstrap.min.js vendored Executable file → Normal file
View File

0
server/easyctf/assets/js/bootstrap3-typeahead.min.js vendored Executable file → Normal file
View File

0
server/easyctf/assets/js/jquery-2.1.4.min.js vendored Executable file → Normal file
View File

0
server/easyctf/assets/js/livestamp.min.js vendored Executable file → Normal file
View File

0
server/easyctf/assets/js/moment.min.js vendored Executable file → Normal file
View File

0
server/easyctf/assets/js/smooth-scroll.min.js vendored Executable file → Normal file
View File

2
server/easyctf/config.py Executable file → Normal file
View File

@ -4,7 +4,7 @@ import sys
import logging
import pathlib
from werkzeug.contrib.cache import RedisCache
from cachelib import RedisCache
class CTFCache(RedisCache):

0
server/easyctf/constants.py Executable file → Normal file
View File

0
server/easyctf/decorators.py Executable file → Normal file
View File

0
server/easyctf/forms/__init__.py Executable file → Normal file
View File

3
server/easyctf/forms/admin.py Executable file → Normal file
View File

@ -7,8 +7,7 @@ from sqlalchemy import and_
from wtforms import ValidationError
from wtforms.fields import (BooleanField, FloatField, HiddenField,
IntegerField, StringField, SubmitField,
TextAreaField)
from wtforms.fields.html5 import DateTimeLocalField
TextAreaField, DateTimeLocalField)
from wtforms.validators import InputRequired, NumberRange, Optional
from easyctf.models import Problem

0
server/easyctf/forms/chals.py Executable file → Normal file
View File

0
server/easyctf/forms/game.py Executable file → Normal file
View File

0
server/easyctf/forms/teams.py Executable file → Normal file
View File

2
server/easyctf/forms/users.py Executable file → Normal file
View File

@ -6,7 +6,7 @@ from wtforms.fields import (BooleanField, FileField, IntegerField,
PasswordField, RadioField, StringField,
SubmitField)
from wtforms.validators import Email, EqualTo, InputRequired, Length, Optional
from wtforms.widgets.html5 import NumberInput
from wtforms.widgets import NumberInput
from easyctf.forms.validators import UsernameLengthValidator
from easyctf.models import User

10
server/easyctf/models.py Executable file → Normal file
View File

@ -620,15 +620,15 @@ class Team(db.Model):
def size(self):
return len(self.members)
@hybrid_property
# @hybrid_property
@cache.memoize(timeout=120)
def observer(self):
return User.query.filter(and_(User.tid == self.tid, User.level != USER_REGULAR)).count()
@observer.expression
@cache.memoize(timeout=120)
def observer(self):
return db.session.query(User).filter(User.tid == self.tid and User.level != USER_REGULAR).count()
# @observer.expression
# @cache.memoize(timeout=120)
# def observer(self):
# return db.session.query(User).filter(User.tid == self.tid and User.level != USER_REGULAR).count()
@hybrid_property
def prop_points(self):

2
server/easyctf/objects.py Executable file → Normal file
View File

@ -1,6 +1,7 @@
from random import SystemRandom
from flask_caching import Cache
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from raven.contrib.flask import Sentry
@ -10,3 +11,4 @@ cache = Cache()
login_manager = LoginManager()
db = SQLAlchemy()
sentry = Sentry()
migrate = Migrate()

0
server/easyctf/templates/admin/problems.html Executable file → Normal file
View File

0
server/easyctf/templates/admin/settings.html Executable file → Normal file
View File

0
server/easyctf/templates/base/index.html Executable file → Normal file
View File

0
server/easyctf/templates/chals/list.html Executable file → Normal file
View File

0
server/easyctf/templates/footer.html Executable file → Normal file
View File

0
server/easyctf/templates/layout.html Executable file → Normal file
View File

0
server/easyctf/templates/navbar.html Executable file → Normal file
View File

0
server/easyctf/templates/teams/create.html Executable file → Normal file
View File

0
server/easyctf/templates/teams/profile.html Executable file → Normal file
View File

0
server/easyctf/templates/teams/settings.html Executable file → Normal file
View File

0
server/easyctf/templates/templates.html Executable file → Normal file
View File

0
server/easyctf/templates/users/login.html Executable file → Normal file
View File

0
server/easyctf/templates/users/profile.html Executable file → Normal file
View File

0
server/easyctf/templates/users/register.html Executable file → Normal file
View File

0
server/easyctf/templates/users/settings.html Executable file → Normal file
View File

0
server/easyctf/utils.py Executable file → Normal file
View File

0
server/easyctf/views/__init__.py Executable file → Normal file
View File

0
server/easyctf/views/admin.py Executable file → Normal file
View File

0
server/easyctf/views/base.py Executable file → Normal file
View File

0
server/easyctf/views/chals.py Executable file → Normal file
View File

0
server/easyctf/views/teams.py Executable file → Normal file
View File

5
server/easyctf/views/users.py Executable file → Normal file
View File

@ -281,7 +281,10 @@ def register_user(name, email, username, password, level, admin=False, **kwargs)
setattr(new_user, key, value)
code = generate_string()
new_user.email_token = code
send_verification_email(username, email, url_for("users.verify", code=code, _external=True))
# TODO: Config for this
# send_verification_email(username, email, url_for("users.verify", code=code, _external=True))
db.session.add(new_user)
db.session.commit()
return new_user

0
server/forgot.mail Executable file → Normal file
View File

0
server/import_problems.sh Executable file → Normal file
View File

0
server/manage.py Executable file → Normal file
View File

1305
server/poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

50
server/pyproject.toml Normal file
View File

@ -0,0 +1,50 @@
[tool.poetry]
name = "easyctf-platform"
version = "0.1.0"
description = ""
authors = ["Michael Zhang <mail@mzhang.io>"]
license = "AGPL-3.0"
[tool.poetry.dependencies]
python = "^3.10"
bcrypt = "^4.0.1"
celery = "^5.2.7"
coverage = "^6.5.0"
cryptography = "^38.0.4"
Flask = "^2.2.2"
Flask-Breadcrumbs = "^0.5.1"
Flask-Caching = "^2.0.1"
Flask-Celery-Helper = "^1.1.0"
flask-csp = "^0.10"
Flask-Login = "^0.6.2"
Flask-Migrate = "^4.0.0"
Flask-Script = "^2.0.6"
flask-sqlalchemy = "^3.0.2"
Flask-WTF = "^1.0.1"
gitdb = "^4.0.10"
GitPython = "^3.1.29"
markdown2 = "^2.4.6"
mysqlclient = "^2.1.1"
onetimepass = "^1.0.1"
paramiko = "^2.12.0"
passlib = "^1.7.4"
pathlib = "^1.0.1"
Pillow = "^9.3.0"
pycryptodome = "^3.16.0"
PyQRCode = "^1.2.1"
pytest = "^7.2.0"
PyYAML = "^6.0"
rauth = "^0.7.3"
raven = {extras = ["flask"], version = "^6.10.0"}
redis = "^4.3.5"
requests = "^2.28.1"
SQLAlchemy = "^1.4.44"
WTForms-Components = "^0.10.5"
Werkzeug = "^2.2.2"
cachelib = "^0.9.0"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

0
server/registration.mail Executable file → Normal file
View File

View File

@ -1,33 +0,0 @@
bcrypt
celery
coverage
cryptography==2.1.4
flask
flask-breadcrumbs
flask-caching
flask-celery-helper
flask-csp
flask-login
flask-migrate
flask-script
flask-sqlalchemy
flask-wtf
gitdb
gitpython
markdown2
mysqlclient
onetimepass
paramiko
passlib
pathlib
pillow
pycryptodome
pyqrcode
pytest
pyyaml
rauth
raven[flask]
redis
requests
sqlalchemy==1.1.0b3
wtforms_components