Skip to content

Enforce whitelist and add command - #3467

Open
yel0h wants to merge 5 commits into
PixelGuys:masterfrom
yel0h:whitelist-command
Open

Enforce whitelist and add command#3467
yel0h wants to merge 5 commits into
PixelGuys:masterfrom
yel0h:whitelist-command

Conversation

@yel0h

@yel0h yel0h commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Enforces the player whitelist during the connection handshake and adds a /whitelist command to manage it.

After a connecting player's key is resolved, the server checks players.isAllowedToJoin when world.settings.whitelistEnabled is set, rejecting the connection with error.NotWhitelisted.

Command syntax: /whitelist <add/block> <keyType>:<base64Key> or /whitelist <add/block> @<playerIndex>

Also removes error logging for error.NotWhitelisted since it's expected to reject and already logs the reason right after rejection.

Closes #2566

@Wunka Wunka moved this to High Priority in PRs to review Aug 4, 2026

@Wunka Wunka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for continuing with this!

Comment thread src/network/protocols.zig Outdated
const keys = zon.getChild("keys");
try conn.user.?.identifyFromKeysAndName(name, keys);

if (main.server.world.?.settings.whitelistEnabled and !main.server.players.isAllowedToJoin(conn.user.?.newKeyString.?)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this. In my mind this can perfectly work in sync with a ban system (blocked = banned)
I would suggest to go in the direction I went with permissions. Instead of returning in isAllowedToJoin a bool return an enum: {allowed, neutral, blocked} neutral would then be the case where you need to look if whitelist is enabled

Comment thread src/server/command/whitelist.zig Outdated
.alreadyAllowed => source.sendMessage("#ff0000{s}§#ff0000 is already on the whitelist", .{key}),
},
.block => switch (players.block(key)) {
.blocked => source.sendMessage("#00ff00Blocked {s}§#00ff00 from connecting", .{key}),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we go by the principle that blocked = ban then I would suggest to also kick the player here

Comment thread src/server/command/whitelist.zig Outdated
}
}

const KeyString = struct {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general we put these kinds of helper in command.zig I can already imagine there being a command to for example update the key where this would help

@yel0h

yel0h commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

True, it does make more sense to treat an explicit block as a ban

@yel0h
yel0h force-pushed the whitelist-command branch from 3a68e6c to 49bd0fd Compare August 7, 2026 19:24
Comment thread src/network/protocols.zig
switch (main.server.players.isAllowedToJoin(conn.user.?.newKeyString.?)) {
.allowed => {},
.blocked => {
std.log.info("Rejected connection from '{s}': blocked", .{name});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name alone is rather unhelpful, it would make sense to also print the public key string

Comment thread src/server/command.zig

pub fn parse(_: NeverFailingAllocator, name: []const u8, arg: []const u8, errorMessage: *ListManaged(u8)) error{ParseError}!KeyString {
const colonIndex = std.mem.indexOfScalar(u8, arg, ':') orelse {
errorMessage.print("Expected a public key of the form \"<keyType>:<base64>\" for <{s}>, found \"{s}\"", .{name, arg});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding your own validation logic, please use authentication.PublicKey.initFromBase64

Comment thread src/network/protocols.zig
const keys = zon.getChild("keys");
try conn.user.?.identifyFromKeysAndName(name, keys);

switch (main.server.players.isAllowedToJoin(conn.user.?.newKeyString.?)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have this switch and the check for whitelistEnabled inside the isAllowedToJoin function, also in my opinion the same generic message for both paths would be enough.

Comment thread src/server/server.zig
return null;
}

pub fn getUserByKey(key: []const u8) ?*User {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a one-time use case, there is no need to pollute the namespace with it, everything used inside here is already public, so please inline it to the implementation site.

Comment thread src/network.zig
if (@errorReturnTrace()) |trace| {
std.log.info("{f}", .{main.fmt.FormatErrorTrace{.stackTrace = trace.*}});
switch (err) {
error.NotWhitelisted => {},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this code here should worry about the error sets of arbitrary network protocols. I assume you want to get rid of the error popup? I think the right solution here would be to change the error to a warning, there are many other cases where this code is run (e.g. version differences) that probably shouldn't have the error popup either.

pub const description = "Manages the connection whitelist";
pub const usage =
\\/whitelist <add/block> <keyType>:<base64Key>
\\/whitelist <add/block> @<playerIndex>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to also have whitelist enable/disable commands?
To implement this in a thread-safe manner you can just make the bool atomic.

@IntegratedQuantum IntegratedQuantum moved this from High Priority to In review in PRs to review Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

Player whitelist

3 participants