Files
external-minecraft/README.md
2026-09-12 14:31:27 -06:00

146 lines
6.3 KiB
Markdown

# Hardened Fedora Minecraft Server (Ansible)
Ansible playbook that turns a fresh **Fedora 42** server into a hardened,
containerized **Minecraft** game server — Java *and* Bedrock clients —
running with **rootless Podman** via systemd Quadlet units.
Everything is managed declaratively from `harden_fedora_minecraft.yml`:
OS hardening, firewall, SELinux, SSH access control, the Podman runtime,
and the Minecraft/Geyser containers are all deployed by one command.
## Features
- 🔒 **[Hardening]** SELinux enforcing, firewalld default-DROP policy, password
policy (pwquality + aging), root account locked, automatic security updates
(`dnf-automatic`).
- 🔑 **[SSH]** Key-only authentication for a dedicated admin user, root login
disabled, `AllowUsers` restriction, and a `fail2ban` SSH jail.
- 🐳 **[Runtime]** Rootless Podman with user lingering, subuid/subgid ranges,
IPv4 forwarding, and the overlay storage driver.
- ⛏️ **[Minecraft]** Portable Java server built from
[`itzg/docker-minecraft-server`](https://github.com/itzg/docker-minecraft-server)
(PAPER by default, version/memory/difficulty configurable).
- 🪨 **[Geyser]** A second container, [`geysermc/geyser`](https://github.com/GeyserMC/geyser-docker),
in the same pod so **Bedrock** (phone/console) players can join the Java world.
- 🗄️ **[Data]** World data and backups live on the host in `/srv/minecraft`
with correct SELinux labels.
## Architecture
Both game containers run in a single Podman **pod** (shared network
namespace) as the non-root user `mcadmin`:
```
Internet
│
┌───────┴────────┐
│ firewall (DROP)│ only the ports below are allowed
└───────┬────────┘
│
pod: minecraft (rootless, user mcadmin)
├── minecraft-server JVM on 0.0.0.0:25565 (Java clients)
│ └── world data → /srv/minecraft/data
└── geyser Bedrock listener :19132/udp
└── config → /srv/minecraft/geyser
└── forwards Bedrock players to 127.0.0.1:25565
```
| Port | Protocol | Purpose |
|------|----------|---------|
| `22` | TCP | SSH (configurable via `ssh_port`) |
| `25565` | TCP | Minecraft Java server |
| `25565` | UDP | Optional Java server UDP (game query) |
| `19132` | UDP | Bedrock (Geyser), when `enable_geyser: true` |
## Repository layout
```
.
├── harden_fedora_minecraft.yml # entry point: hosts: external-apps → 7 roles
├── inventory.yml # target host definition
├── ansible.cfg # inventory + role path + pipelining
├── requirements.yml # Ansible collections (ansible.posix)
├── group_vars/all.yml # shared variables
├── host_vars/external-apps.yml # per-server settings (IP, SSH key, MC tuning)
└── roles/
├── packages/ # base + container tooling, full dnf update, dnf-automatic
├── firewall/ # firewalld: DROP target, open SSH + Minecraft + Geyser
├── selinux/ # enforcing mode + container_file_t context for /srv/minecraft
├── auth/ # admin user, SSH key, sudoers, password policy, lock root
├── ssh_harden/ # sshd drop-in + fail2ban jail
├── podman/ # socket, linger, subuid/subgid, ip_forward, storage
└── minecraft/ # data dirs, Quadlet pod/container units, image pulls
```
## Prerequisites
- Ansible control node (this repo) with Ansible Core ≥ 2.14 and `ansible.posix`
- A Fedora 42 target server reachable over SSH with an account that has sudo
- The `ansible.user` in the inventory must be able to `become` root on the target
## Quick start
```bash
# 1. Install the required collection
ansible-galaxy collection install -r requirements.yml
# 2. Configure the target server (REQUIRED before first run)
# - host_vars/external-apps.yml
# * ansible_host: real IP or DNS name
# * admin_user_ssh_key: your real SSH public key
# * game settings to taste (version, memory, whitelist…)
# 3. Run the playbook
ansible-playbook harden_fedora_minecraft.yml
```
> ⚠️ `admin_user_ssh_key` **must** be a real key before the first run —
> the playbook disables SSH password authentication and root login once
> the `ssh_harden` role executes.
## Configuration
All tunables live in `host_vars/external-apps.yml` (per server) and
`group_vars/all.yml` (cluster-wide). Highlights:
| Variable | Default | Meaning |
|----------|---------|---------|
| `admin_user` | `mcadmin` | Non-root user running the containers |
| `minecraft_version` | `LATEST` | Server version (`1.21.1`, …) |
| `minecraft_server_type` | `PAPER` | PAPER / VANILLA / FABRIC / FORGE |
| `minecraft_memory` | `2G` | JVM memory limit |
| `minecraft_whitelist` | `""` | Comma-separated allowed usernames (empty = off) |
| `enable_geyser` | `true` | Deploy the Bedrock bridge |
| `geyser_port` | `19132` | Bedrock UDP port |
| `geyser_auth_type` | `online` | `offline` only with a paired Floodgate key |
| `ssh_port` | `22` | SSH port used by firewall + sshd |
## Operations
After the playbook has run, manage the servers as `mcadmin`:
```bash
sudo machinectl shell mcadmin@ -- systemctl --user status minecraft-server geyser
sudo machinectl shell mcadmin@ -- systemctl --user restart minecraft-server
sudo machinectl shell mcadmin@ -- podman ps # container list
sudo machinectl shell mcadmin@ -- journalctl --user-unit minecraft-server -f
```
## References
- [Ansible Documentation](https://docs.ansible.com/)
- [Fedora Project](https://fedoraproject.org/)
- [Podman](https://podman.io/)
- [Podman Quadlet (systemd.unit) man page](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html)
- [Rootless Podman tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md)
- [itzg/docker-minecraft-server](https://github.com/itzg/docker-minecraft-server)
- [GeyserMC — Bedrock ↔ Java bridge](https://geysermc.dev/)
- [GeyserMC/Geyser](https://github.com/GeyserMC/Geyser)
- [Geyser docker image](https://github.com/GeyserMC/geyser-docker)
- [Firewalld](https://firewalld.org/)
- [fail2ban](https://github.com/fail2ban/fail2ban)
- [SELinux quick docs (Fedora)](https://docs.fedoraproject.org/en-US/quick-docs/selinux-getting-started/)
## License
[MIT](./LICENSE)