Merge branch 'devops/userns-rootless-container' into 'dev'

Convert container to run as non-root

See merge request crafty-controller/crafty-commander!172
This commit is contained in:
Iain Powrie 2022-03-02 19:47:41 +00:00
commit 8da2bcae17
10 changed files with 118 additions and 47 deletions

View File

@ -10,6 +10,10 @@ docker-compose.yml
.gitlab-ci.yml
# root
.editorconfig
.pylintrc
.venv
.vscode
crafty_commander.exe
DBCHANGES.md
docker-compose.yml.example

View File

@ -7,18 +7,43 @@ LABEL maintainer="Dockerfile created by Zedifus <https://gitlab.com/zedifus>"
# Security Patch for CVE-2021-44228
ENV LOG4J_FORMAT_MSG_NO_LOOKUPS=true
# Install Packages And Dependencies
COPY requirements.txt /commander/requirements.txt
RUN apt update \
&& apt install -y gcc python3 python3-pip libmariadb-dev openjdk-8-jre-headless openjdk-11-jre-headless openjdk-16-jre-headless openjdk-17-jre-headless default-jre \
&& pip3 install --no-cache-dir -r /commander/requirements.txt
# Create non-root user & required dirs
RUN useradd -M crafty \
&& mkdir /commander \
&& chown -R crafty:root /commander
# Copy Source & copy default config from image
COPY ./ /commander
# Install required system packages
RUN apt-get update \
&& apt-get -y --no-install-recommends install \
gcc \
python3 \
python3-dev \
python3-pip \
python3-venv \
libmariadb-dev \
default-jre \
openjdk-8-jre-headless \
openjdk-11-jre-headless \
openjdk-16-jre-headless \
openjdk-17-jre-headless \
&& apt-get autoremove \
&& apt-get clean
# Switch to service user for installing crafty deps
USER crafty
WORKDIR /commander
COPY --chown=crafty:root requirements.txt ./
RUN python3 -m venv ./.venv \
&& . .venv/bin/activate \
&& pip3 install --no-cache-dir --upgrade setuptools==50.3.2 pip==22.0.3 \
&& pip3 install --no-cache-dir -r requirements.txt \
&& deactivate
# Copy Source w/ perms & prepare default config from example
COPY --chown=crafty:root ./ ./
RUN mv ./app/config ./app/config_original \
&& mv ./app/config_original/default.json.example ./app/config_original/default.json \
&& chmod +x ./docker_launcher.sh
&& mv ./app/config_original/default.json.example ./app/config_original/default.json \
&& chmod +x ./docker_launcher.sh
# Expose Web Interface port & Server port range
EXPOSE 8000

View File

@ -8,7 +8,7 @@ a web interface for the server administrators to interact with their servers. Cr
is compatible with Docker, Linux, Windows 7, Windows 8 and Windows 10.
## Documentation
Temporary documentation available on [GitLab](https://gitlab.com/crafty-controller/crafty-commander/wikis/home)
Documentation available on [wiki.craftycontrol.com](https://craftycontrol.com)
## Meta
Project Homepage - https://craftycontrol.com
@ -17,15 +17,33 @@ Discord Server - https://discord.gg/9VJPhCE
Git Repository - https://gitlab.com/crafty-controller/crafty-web
## Basic Docker Usage
<br>
**To get started with docker**, all you need to do is pull the image from this git repository's registry.
This is done by using `docker-compose` or `docker run`(You don't need to clone the Repository and build, like in 3.x ).
## Basic Docker Usage 🐳
If you have a config folder already from previous local installation or docker setup, the image should mount this volume, if none is present then it will populate its own config folder for you.
With `Crafty Controller 4.0` we have focused on building our DevOps Principles, implementing build automation, and securing our containers, with the hopes of making our Container user's lives abit easier.
### Using the registry image:
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself.
### - Two big changes you will notice is:
- We now provide pre-built images for you guys.
- Containers now run as non-root, using practices used by OpenSwift & Kubernetes (root group perms).
----
### - To get started with docker 🛫
All you need to do is pull the image from this git repository's registry.
This is done by using `'docker-compose'` or `'docker run'` (You don't need to clone the Repository and build, like in 3.x ).
If you have a config folder already from previous local installation or _docker setup_*, the image should mount this volume, if no config present then it will populate its own config folder for you. <br> <br>
As the Dockerfile uses the permission structure of `crafty:root` **internally** there is no need to worry about matching the `UID` or `GID` on the host system :)
> ***Make sure the ownership permissions on `servers/ backups/ logs/ configs/ imports/` in the `docker/` are not `root:root`, please just chown the dir recursively to your host user.**
> **Please make sure if you are using a `compose` file, that the above volume mount directories are present, otherwise, docker will just make them and they'll be `root:root` which is not what we want.💀**
<br>
### - Using the registry image 🌎
The provided image supports both `arm64` and `amd64` out the box, if you have issues though you can build it yourself with the `compose` file in `docker/`.
The image is located at: `registry.gitlab.com/crafty-controller/crafty-commander:latest`
| Branch | Status |
@ -56,7 +74,14 @@ $ cat ~/my_password.txt | docker login registry.gitlab.com -u <username> --passw
```
Then use one of the following methods:
#### docker-compose.yml
### **docker-compose.yml:**
```sh
# We need to make them because of permissions remember!
$ mkdir docker/backups docker/logs docker/servers docker/config docker/import
# Make your compose file
$ vim docker-compose.yml
```
```yml
version: '3'
@ -64,40 +89,52 @@ services:
crafty:
container_name: crafty_commander
image: registry.gitlab.com/crafty-controller/crafty-commander:latest
environment:
- TZ=Etc/UTC
ports:
- "8000:8000" # HTTP
- "8443:8443" # HTTPS
- "8123:8123" # DYNMAP
- "19132:19132/udp" # BEDROCK
- "24000-25600:24000-25600" # MC SERV PORT RANGE
- "25500-25600:25500-25600" # MC SERV PORT RANGE
volumes:
- ./docker/backups:/commander/backups
- ./docker/logs:/commander/logs
- ./docker/servers:/commander/servers
- ./docker/config:/commander/app/config
- ./docker/import:/commander/import
```
#### docker run
```sh
$ docker-compose up -d && docker-compose logs -f
```
<br>
### **docker run:**
```sh
# We need to make them because of permissions remember!
$ mkdir docker/backups docker/logs docker/servers docker/config docker/import
$ docker run \
--name crafty_commander \
-p 8000:8000 \
-p 8443:8443 \
-p 8123:8123 \
-p 19132:19132/udp \
-p 24000-25600:24000-25600 \
-p 25500-25600:25500-25600 \
-e TZ=Etc/UTC \
-v "/$(pwd)/docker/backups:/commander/backups" \
-v "/$(pwd)/docker/logs:/commander/logs" \
-v "/$(pwd)/docker/servers:/commander/servers" \
-v "/$(pwd)/docker/config:/commander/app/config" \
-v "/$(pwd)/docker/import:/commander/import" \
registry.gitlab.com/crafty-controller/crafty-commander:latest
```
### Building from the cloned repository:
### **Building from the cloned repository:**
If you are building from `docker-compose` you can find the compose file in `./docker/docker-compose.yml` just `cd` to the docker directory and `docker-compose up -d`
If you'd rather not use `docker-compose` you can use the following `docker run`in the directory where the *Dockerfile* is:
If you'd rather not use `docker-compose` you can use the following `docker run` in the directory where the *Dockerfile* is:
```sh
# REMEMBER, Build your image first!
$ docker build . -t crafty
@ -108,11 +145,13 @@ $ docker run \
-p 8443:8443 \
-p 8123:8123 \
-p 19132:19132/udp \
-p 24000-25600:24000-25600 \
-p 25500-25600:25500-25600 \
-e TZ=Etc/UTC \
-v "/$(pwd)/docker/backups:/commander/backups" \
-v "/$(pwd)/docker/logs:/commander/logs" \
-v "/$(pwd)/docker/servers:/commander/servers" \
-v "/$(pwd)/docker/config:/commander/app/config" \
-v "/$(pwd)/docker/import:/commander/import" \
crafty
```
A fresh build will take several minutes depending on your system, but will be rapid there after.
A fresh build will take several minutes depending on your system, but will be rapid thereafter.

View File

@ -451,7 +451,7 @@ class Controller:
server_log_file, server_stop, port, server_type='minecraft-bedrock')
if os.name != "nt":
if helper.check_file_exists(full_jar_path):
os.chmod(full_jar_path, 2775)
os.chmod(full_jar_path, 0o2775)
return new_id
def import_bedrock_zip_server(self, server_name: str, zip_path: str, server_exe: str, port: int):
@ -500,7 +500,7 @@ class Controller:
server_log_file, server_stop, port, server_type='minecraft-bedrock')
if os.name != "nt":
if helper.check_file_exists(full_jar_path):
os.chmod(full_jar_path, 2775)
os.chmod(full_jar_path, 0o2775)
return new_id

View File

@ -4,15 +4,17 @@ services:
crafty:
container_name: crafty_commander
image: registry.gitlab.com/crafty-controller/crafty-commander:latest
environment:
- TZ=Etc/UTC
ports:
- "8000:8000" # HTTP
- "8443:8443" # HTTPS
- "8123:8123" # DYNMAP
- "19132:19132/udp" # BEDROCK
- "24000-25600:24000-25600" # MC SERV PORT RANGE
- "25500-25600:25500-25600" # MC SERV PORT RANGE
volumes:
- ./docker/backups:/commander/backups
- ./docker/logs:/commander/logs
- ./docker/servers:/commander/servers
- ./docker/config:/commander/app/config
- ./import:/commander/import
- ./docker/import:/commander/import

0
docker/config/.gitkeep Normal file
View File

View File

@ -4,12 +4,14 @@ services:
crafty:
container_name: crafty_commander
build: ..
environment:
- TZ=Etc/UTC
ports:
- "8000:8000" # HTTP
- "8443:8443" # HTTPS
- "8123:8123" # DYNMAP
- "19132:19132/udp" # BEDROCK
- "24000-25600:24000-25600" # MC SERV PORT RANGE
- "25500-25600:25500-25600" # MC SERV PORT RANGE
volumes:
- ./backups:/commander/backups
- ./logs:/commander/logs

0
docker/import/.gitkeep Normal file
View File

View File

@ -1,9 +1,11 @@
#!/bin/sh
# Check if config exists from existing installation (venv or previous docker launch)
if [ ! "$(ls -A ./app/config)" ]; then
if [ ! "$(ls -A --ignore=.gitkeep ./app/config)" ]; then
mkdir ./app/config/
cp -r ./app/config_original/* ./app/config/
fi
# Activate our prepared venv and launch crafty with provided args
. .venv/bin/activate
exec python3 main.py $@

27
main.py
View File

@ -7,14 +7,11 @@ import logging.config
import signal
from app.classes.shared.console import console
from app.classes.shared.helpers import helper
if helper.check_file_exists('/.dockerenv'):
console.cyan("Docker environment detected!")
else:
if helper.checkRoot():
console.critical("Root detected. Root/Admin access denied. Run Crafty again with non-elevated permissions.")
time.sleep(5)
console.critical("Crafty shutting down. Root/Admin access denied.")
sys.exit(0)
if helper.checkRoot():
console.critical("Root detected. Root/Admin access denied. Run Crafty again with non-elevated permissions.")
time.sleep(5)
console.critical("Crafty shutting down. Root/Admin access denied.")
sys.exit(0)
# pylint: disable=wrong-import-position
from app.classes.shared.main_models import installer, database
from app.classes.shared.tasks import TasksManager
@ -152,10 +149,10 @@ if __name__ == '__main__':
controller.set_project_root(project_root)
controller.clear_unexecuted_commands()
def sigterm_handler():
def sigterm_handler(*sig):
print() # for newline
logger.info("Recieved SIGTERM, stopping Crafty")
console.info("Recieved SIGTERM, stopping Crafty")
logger.info(f"Recieved SIGINT [{sig[0]}], stopping Crafty...")
console.info(f"Recieved SIGINT [{sig[0]}], stopping Crafty...")
tasks_manager._main_graceful_exit()
Crafty.universal_exit()
@ -166,8 +163,8 @@ if __name__ == '__main__':
Crafty.cmdloop()
except KeyboardInterrupt:
print() # for newline
logger.info("Recieved SIGINT, stopping Crafty")
console.info("Recieved SIGINT, stopping Crafty")
logger.info("Recieved SIGINT, stopping Crafty...")
console.info("Recieved SIGINT, stopping Crafty...")
tasks_manager._main_graceful_exit()
Crafty.universal_exit()
else:
@ -178,8 +175,8 @@ if __name__ == '__main__':
break
time.sleep(1)
except KeyboardInterrupt:
logger.info("Recieved SIGINT, stopping Crafty")
console.info("Recieved SIGINT, stopping Crafty")
logger.info("Recieved SIGINT, stopping Crafty...")
console.info("Recieved SIGINT, stopping Crafty...")
break
tasks_manager._main_graceful_exit()
Crafty.universal_exit()