-
Notifications
You must be signed in to change notification settings - Fork 10
/
livectf.py
executable file
·127 lines (89 loc) · 3.94 KB
/
livectf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
import argparse
from pathlib import Path
import subprocess
from typing import Optional
BASE_DIR = Path(__file__).parent
CHALLENGES_DIR = BASE_DIR / 'challenges'
IMAGE_PREFIX = 'livectf_'
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(required=True, dest='command')
build_parser = subparsers.add_parser('build', help='Build a challenge image')
build_parser.add_argument('challenge', help='Challenge to build')
create_parser = subparsers.add_parser('create', help='Create a new challenge')
create_parser.add_argument('challenge', help='Challenge to create')
start_parser = subparsers.add_parser('start', help='Start a challenge')
start_parser.add_argument('challenge', help='Challenge to start')
start_parser.add_argument('port', default=8000, type=int, help='Challenge listen port')
start_parser.add_argument('team', type=int, default=None)
stop_parser = subparsers.add_parser('stop', help='Stop a challenge')
stop_parser.add_argument('challenge', help='Challenge to stop')
stop_parser.add_argument('team', type=int, default=None)
args = parser.parse_args()
def build_base_image():
base_image_dir = BASE_DIR / 'base-image'
print("Building base image...")
subprocess.run(['make', '-C', base_image_dir], check=True)
print("Base image up to date")
def build_challenge(name: str):
build_base_image()
chal_dir = CHALLENGES_DIR / name / 'challenge'
chal_image = f'{IMAGE_PREFIX}{name}:latest'
# Build challenge image
print(f'Building challenge image for "{name}"...')
subprocess.check_call(['docker', 'build', '-t', chal_image, chal_dir])
# Copy handout file from challenge image
print(f'Copying handout from "{chal_image}"...')
container_id = subprocess.check_output(
['docker', 'create', chal_image],
).decode().strip()
try:
subprocess.check_call(['docker', 'cp', f'{container_id}:/handout.tar.gz', chal_dir])
finally:
subprocess.check_call(['docker', 'rm', '-f', container_id])
print(f'Successfully built image and handout for "{name}"')
def create_challenge(name: str):
chal_dir = CHALLENGES_DIR / name
if chal_dir.exists():
raise Exception('Challenge "{name}" already exists!')
subprocess.check_call(['cp', '-r', 'template', chal_dir])
print(f'Successfully initialized "{name}"')
def _check_if_challenge_needs_priv(chal_img):
proc = subprocess.run(['docker', 'history', chal_img], check=True, stdout=subprocess.PIPE)
return b'nsjail' in proc.stdout
def start_challenge(name: str, port: int, team: Optional[int]):
chal_image = f'{IMAGE_PREFIX}{name}:latest'
# Start challenge container
print(f'Starting container for "{name}"...')
# Special case non-privileged challenges
run_privileged = _check_if_challenge_needs_priv(chal_image)
run_args = [
'--init',
'-d',
'-p', f'{port}:31337',
'--name', f'{IMAGE_PREFIX}{name}' + f'_{team}' if team else '',
chal_image
]
if run_privileged:
run_args.insert(0, '--privileged')
subprocess.check_call(['docker', 'run'] + run_args)
print(f'Successfully started "{name}"')
def stop_challenge(name: str, team: Optional[int]):
# Stop and remove challenge container
container_name = f'{IMAGE_PREFIX}{name}' + f'_{team}' if team else ''
print(f'Stopping container for "{name}"...')
subprocess.check_call(['docker', 'stop', container_name])
print(f'Removing container for "{name}"...')
subprocess.check_call(['docker', 'rm', container_name])
print(f'Successfully stopped "{name}"')
# So we play nicely with tab complete
if args.challenge.endswith('/'):
args.challenge = args.challenge[:-1]
if args.command == 'build':
build_challenge(args.challenge)
elif args.command == 'create':
create_challenge(args.challenge)
elif args.command == 'start':
start_challenge(args.challenge, args.port, args.team)
elif args.command == 'stop':
stop_challenge(args.challenge, args.team)