FreeBSD Jail 容器中的 Minecraft 服务器

今天——应我儿子的要求——我们来讲讲如何在 FreeBSD Jail 容器中运行 Minecraft 服务器。

这有点像 Linux 上的 Docker/Podman,但安全性更高。

准备工作

首先我们将在 /jail 路径下创建环境,并获取所需的 FreeBSD 基本系统 版本。

今天我们将使用 FreeBSD 14.2-RELEASE 版本。

host # mkdir -p /jail/minecraft /jail/BASE

host # VER=$( freebsd-version | awk -F '-' '{print $1 "-" $2}' )

host # fetch -o /jail/BASE/${VER}-base.txz https://download.freebsd.org/releases/amd64/14.2-RELEASE/base.txz

接下来我们将创建专用的 Minecraft FreeBSD Jail,并为其填充独立的 基本系统 内容。

我们还会复制 /var/run/dmesg.boot,因为 Minecraft 服务器需要使用它。

host # tar -C /jail/minecraft --unlink -xvf /jail/BASE/14.2-RELEASE-base.txz

host # cp /var/run/dmesg.boot /jail/minecraft/var/run/

创建

现在我们将设置基础 FreeBSD Jail 配置。

这些配置将作为所有其他 Jail 的默认值,除非我们重新定义它们。

host # cat /etc/jail.conf
exec.start      = "/bin/sh /etc/rc";
exec.stop       = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
exec.clean;
mount.devfs;

现在配置我们的 Minecraft Jail。

我们将使用局域网接口 em0 和 IP 地址 10.0.0.210

host # cat /etc/jail.conf.d/minecraft.conf
  minecraft {
  # 全局
    exec.start = "/bin/sh /etc/rc";
    exec.stop  = "/bin/sh /etc/rc.shutdown";
    exec.consolelog = "/var/log/jail_console_${name}.log";
    exec.clean;
    mount.devfs;
    host.hostname = ${name};
    path = /jail/${name};

  # 自定义
    ip4.addr = 10.0.0.210;
    interface = em0;
    allow.raw_sockets;
    allow.sysvipc;
    devfs_ruleset=210;
    allow.mount;
    enforce_statfs=1;
    allow.mount.devfs;
    allow.mount.procfs;
    allow.mount.fdescfs;
  }

下面你还会看到宿主机上的规则集 /etc/devfs.rules

host # grep -A 4 minecraft /etc/devfs.rules
[minecraft=210]
add include $devfsrules_jail
add path 'fd*' unhide

现在我们可以启动我们的 Jail 了。

host # service jail onestart minecraft
Starting jails: minecraft.

host # jls
   JID  IP Address      Hostname                      Path
     1  10.0.0.210      minecraft                     /jail/minecraft

你也可以使用我提供的工具 jmore(8)

host # jmore
JAIL       JID  TYPE  VER     DIR              IFACE   IP(s)
----       ---  ----  ---     ---              -----   -----
classic    -    std   13.2-R  /jail/classic    em0     10.0.0.199
ctld-two   -    vnet  13.2-R  /jail/ctld-two   ${if}b  -
ctld       -    vnet  13.2-R  /jail/ctld       ${if}b  -
fbsdjail   -    std   13.1-R  /jail/fbsdjail   wlan0   10.0.0.43
iscsi      -    vnet  13.2-R  /jail/iscsi      ${if}b  -
minecraft  1    std   14.2-R  /jail/minecraft  em0     10.0.0.210
minio      -    std   14.0-R  /jail/minio      em0     10.0.0.133
nfsd       -    vnet  14.1-R  /jail/nfsd       ${if}b  -
other      -    std   14.1-R  /jail/other      em0     10.0.0.199
sambajail  -    vnet  14.1-R  /jail/sambajail  ${if}b  -
unfs3      -    vnet  14.1-R  /jail/unfs3      ${if}b  -

为了让 Minecraft Jail 在启动时自动运行,需要在 host 系统的 /etc/rc.conf 文件中添加如下内容:

host # grep jail /etc/rc.conf
jail_enable=YES
jail_devfs_enable=YES
jail_list="minecraft"

配置 FreeBSD Jail

现在我们进入 Minecraft Jail。

使用 jmore minecraft c 相当于执行了知名的命令:

env PS1='minecraft # ' jexec minecraft /bin/sh

示例:

host # jmore minecraft c
minecraft #

接下来进行一些基本配置,例如设置 DNS 或将 pkg(8) FreeBSD 包管理器切换到 latest 分支:

minecraft # echo nameserver 1.1.1.1 > /etc/resolv.conf
minecraft # mkdir -p /usr/local/etc/pkg/repos
minecraft # sed -e 's|quarterly|latest|g' /etc/pkg/FreeBSD.conf > /usr/local/etc/pkg/repos/FreeBSD.conf
minecraft # pkg search -o minecraft
games/minecraft-client         Client for the block building game

现在安装其他所需的包,因为 Minecraft 服务器需要通过 FreeBSD Ports 构建:

minecraft # pkg install gitup bsddialog ccache portconfig openjdk21 tmux jless

由于需要通过 FreeBSD Ports 构建 Minecraft 服务器,并且许可需要手动接受(或忽略)(译注:并不需要),因此接下来使用 gitup 工具获取 FreeBSD Ports 树。

make config 阶段选择选项 DAEMON

minecraft # gitup ports
(...)
#
# Please review the following file(s) for important changes.
#       /usr/ports/UPDATING
#       /usr/ports/mail/dspam/files/UPDATING
#
# Done.

minecraft # cd /usr/ports/games/minecraft-server

minecraft # make config

 +------------|minecraft-server-1.21.4|--------------+
 | 'F1' for Ports Collection help.                   |  
 | +---------- RUN [select at least one] ----------+ |
 | | new (*) DAEMON     Run as a service           | |
 | | new ( ) STANDALONE Run the .jar file directly | |
 | +-----------------------------------------------+ |
 |                [  OK  ]     [Cancel]              |
 +---------------------------------------------------+

构建

接下来我们将构建 Minecraft 服务器。

minecraft # echo DISABLE_LICENSES=yes >> /etc/make.conf

minecraft # env BATCH=yes make build install clean
(...)
When you first run minecraft-server, it will populate the file
/usr/local/etc/minecraft-server/eula.txt

It is required to read the EULA, and then set eula=true

- Configuration files can be found in /usr/local/etc/minecraft-server/
- Log and debug output files can be found in /var/log/minecraft-server/
- World files can be found in /var/db/minecraft-server/

Without daemon option:
- To run the server, run /usr/local/bin/minecraft-server
- To edit java's parameters, edit /usr/local/etc/minecraft-server/java-args.txt
- To run with a specific version of Java, set environment variable JAVA_VERSION,
  for example:
    export JAVA_VERSION=22
    /usr/local/bin/minecraft-server
  or:
    JAVA_VERSION=22 /usr/local/bin/minecraft-server

With daemon option:
- The service has been installed with the name 'minecraft'
- To adjust maximum memory usage (-Xmx), use minecraft_memx= in /etc/rc.conf
- To adjust initial memory usage (-Xms), use minecraft_mems= in /etc/rc.conf
- To add other java parameters, use minecraft_args= in /etc/rc.conf
- To run with a specific version of Java, use minecraft_java_version= in /etc/rc.conf
- To see the interactive console, type service minecraft console

===>  Cleaning for minecraft-server-1.21.4

Minecraft 服务器配置

按照 pkg-message 中的建议,我们将在 /etc/fstab 文件中添加额外的虚拟文件系统。

同时,我们还要确保这些文件系统在 Jail 启动时通过 /etc/rc.local 文件被挂载。

minecraft # cat << FSTAB >> /etc/fstab
        fdesc   /dev/fd         fdescfs         rw      0       0
        proc    /proc           procfs          rw      0       0
FSTAB

minecraft # echo 'mount -a' >> /etc/rc.local

minecraft # mount -a

minecraft # mount
zroot/jail on / (zfs, local, noatime, nfsv4acls)
devfs on /dev (devfs)
fdescfs on /dev/fd (fdescfs)
procfs on /proc (procfs, local)
devfs on /dev (devfs)

我们不会修改 Minecraft Jail 的主 /etc/rc.conf 配置文件来添加所需的 Minecraft 服务器选项。

同时,我们将“接受”EULA,并在 /usr/local/etc/minecraft-server/server.properties 文件中创建基本的 Minecraft 服务器配置。

你也可以在 /usr/local/etc/minecraft-server/java-args.txt 文件中配置额外的 Java 参数。如果默认值对你的情况太小,请自行增大。

minecraft # cat << RC >> /etc/rc.conf
minecraft_enable=YES
minecraft_mems=1024M
minecraft_memx=1024M
RC

minecraft # echo eula=true > /usr/local/etc/minecraft-server/eula.txt

minecraft # cat << MINECRAFT > /usr/local/etc/minecraft-server/server.properties
enable-jmx-monitoring=false
rcon.port=25575
level-seed=
gamemode=survival
enable-command-block=false
enable-query=false
generator-settings={}
enforce-secure-profile=true
level-name=world
motd=FreeBSD Minecraft Server
query.port=25565
pvp=true
generate-structures=true
max-chained-neighbor-updates=1000000
difficulty=easy
network-compression-threshold=256
max-tick-time=60000
require-resource-pack=false
use-native-transport=true
max-players=20
online-mode=false
enable-status=true
allow-flight=false
initial-disabled-packs=
broadcast-rcon-to-ops=true
view-distance=10
server-ip=
resource-pack-prompt=
allow-nether=true
server-port=25565
enable-rcon=false
sync-chunk-writes=true
resource-pack-id=
op-permission-level=4
prevent-proxy-connections=false
hide-online-players=false
resource-pack=
entity-broadcast-range-percentage=100
simulation-distance=10
rcon.password=
player-idle-timeout=0
force-gamemode=false
rate-limit=0
hardcore=false
white-list=false
broadcast-console-to-ops=true
spawn-npcs=true
spawn-animals=true
log-ips=true
function-permission-level=2
initial-enabled-packs=vanilla
level-type=minecraft\:normal
text-filtering-config=
spawn-monsters=true
enforce-whitelist=false
spawn-protection=16
resource-pack-sha1=
max-world-size=29999984
MINECRAFT

启动

现在是时候启动已安装并配置好的 Minecraft 服务器了。

minecraft # service minecraft start
Starting minecraft.

minecraft # service minecraft status
minecraft is running.

minecraft # sockstat -l4
USER     COMMAND    PID   FD  PROTO  LOCAL ADDRESS         FOREIGN ADDRESS      
mcserver java       33227 103 tcp4   10.0.0.210:25565      *:*
root     syslogd     7809 5   udp4   10.0.0.210:514        *:*

看起来服务器运行正常——但如果不正常,可以使用以下命令进行调试。

minecraft # su mcserver -c '/usr/local/bin/java -Xmx1024M -Xms1024M -jar /usr/local/minecraft-server/server.jar nogui'

连接 Minecraft 客户端

首先 – 确保你的客户端版本与 Minecraft 服务器版本一致 – 我这里是 1.21.4

我的情况是,Minecraft 客户端是在某台随机的 Windows 电脑上启动的,如下所示。

点击 多人游戏 按钮。

然后点击 添加服务器 按钮 – 我们将添加基于 FreeBSD 的 Minecraft 服务器。

输入你喜欢的 Minecraft 服务器名称和 IP 地址。

稍等片刻,我们的基于 FreeBSD 的 Minecraft 服务器将出现在列表中。

现在点击 加入服务器 按钮加入服务器。

我们会看到 正在连接服务器… 的提示。

……片刻之后,我们就成功加入了 Minecraft 服务器。

用户

由于我不是 Minecraft 专家,我花了一些时间才找到在该服务器上定义“管理员”的方法。

幸运的是,我有一个可行的解决方案 🙂:

当我在这里写关于我自己的 jmore(8) 工具时 New jmore(8) FreeBSD Jails List/Manage Tool,我最初把它叫作 jless(8),但有人提醒我这个名字已经被一个处理 JSON 的工具占用了。我们稍后会用到它 🙂。

当有人连接到我们的服务器时,他的 名字uuid 会被添加到 /usr/local/etc/minecraft-server/usercache.json 文件中,如下所示。

host # cat /jail/minecraft/usr/local/etc/minecraft-server/usercache.json | tr ',' '\n'
[{"name":"antuan"
"uuid":"0d61326c-dfd1-3fa8-ba9d-249d402fb700"
"expiresOn":"2025-05-05 14:04:15 +0000"}
{"name":"antek"
"uuid":"4b520bac-4b31-3c41-8f9b-2781763e5c88"
"expiresOn":"2025-05-05 09:16:08 +0000"}]

host # jless /jail/minecraft/usr/local/etc/minecraft-server/usercache.json | cat
[
  {
    "name": "antuan",
    "uuid": "0d61326c-dfd1-3fa8-ba9d-249d402fb700",
    "expiresOn": "2025-05-05 14:04:15 +0000"
  },
  {
    "name": "antek",
    "uuid": "4b520bac-4b31-3c41-8f9b-2781763e5c88",
    "expiresOn": "2025-05-05 09:16:08 +0000"
  }
]

bat(1) 命令显示颜色后看起来效果更好。

现在 – 要让这些用户成为“管理员”,我们需要将他们添加到 /usr/local/etc/minecraft-server/ops.json 文件中,并且重启 Minecraft 服务器才能生效。

host # jless /jail/minecraft/usr/local/etc/minecraft-server/ops.json | cat
[
  {
    "uuid": "4b520bac-4b31-3c41-8f9b-2781763e5c88",
    "name": "antek",
    "level": 4,
    "bypassesPlayerLimit": false

  },
  {
    "uuid": "0d61326c-dfd1-3fa8-ba9d-249d402fb700",
    "name": "antuan",
    "level": 4,
    "bypassesPlayerLimit": false
  }
]

权限等级 4 是记录中最高的权限等级。

现在我儿子拥有了他需要使用的所有命令,例如 /gamemode/time 🙂。

总结

欢迎随意分享你对个人 Minecraft 服务器其他所需配置的想法。

更新 1 – FreeBSD Jail 与 Linux Podman

我没想到第一句话会成为评论的焦点,因此在这里补充一些细节。我们来讨论一下 FreeBSD Jails 和 Linux Podman 容器在安全性上的差异。

隔离性: 对于无 root Podman 来说,如果启用了 SELinux/AppArmor,它的隔离性似乎与 Jail 在同一水平。但如果没有 SELinux/AppArmor,Jail 提供了更好的隔离性。当你在 Podman 中启用 SELinux/AppArmor 并再添加 MAC 框架(如 mac_sebsd / mac_jail / mac_bsdextended / mac_portacl)时,Jail 的隔离性更高。

内核系统调用暴露面: 即使是无 root Podman,除非通过 seccomp(SELinux)限制,否则仍有“完全”的系统调用访问权限。Jail 对系统调用的使用有限制,不需要额外工具就能实现;在 FreeBSD 上结合 MAC 框架还可以进一步缩小系统调用的可用范围。

防火墙: 你无法在无 root Podman 容器内运行防火墙。而在 VNET Jail 中,你可以运行完整的网络栈和任何防火墙(如 PF 或 IPFW),独立于宿主机运行,这意味着安全性更高。

总结: FreeBSD Jail 通常在默认情况下比 Podman 容器更安全,如果花时间添加额外的安全层,其安全性会更高。

市场存在时间也是一个重要因素。

Jail 自 1999/2000 年引入以来已经投入生产环境,已有 25 年历史,久经考验。Docker 从 2014 年开始流行,时间短约 10 年,但我们要比较的是 Jail 与 Podman。Podman 的无 root 支持首次出现在 2019 年晚期(1.6 版本),因此在市场上的时间不足 6 年。

这意味着 Jail 是所有方案中最经受考验的。

最后更新于

这有帮助吗?