Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added filter for minecraft server #2852

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

MinePro120
Copy link

@MinePro120 MinePro120 commented Oct 15, 2020

Before submitting your PR, please review the following checklist:

  • CHOOSE CORRECT BRANCH: if filing a bugfix/enhancement
    against certain release version, choose 0.9, 0.10 or 0.11 branch,
    for dev-edition use master branch
  • CONSIDER adding a unit test if your PR resolves an issue
  • LIST ISSUES this PR resolves
  • MAKE SURE this PR doesn't break existing tests
  • KEEP PR small so it could be easily reviewed.
  • AVOID making unnecessary stylistic changes in unrelated code
  • ACCOMPANY each new failregex for filter X with sample log lines
    within fail2ban/tests/files/logs/X file

@MinePro120
Copy link
Author

MinePro120 commented Oct 15, 2020

The timestamps of the logs are in this format : "[21:20:18]" (no date or year), so I'm not sure what value would "time" in failJSON have.

@sebres
Copy link
Contributor

sebres commented Oct 15, 2020

The time simulating by test suite for NOW is 1124013600 - Sun, 14 Aug 12:00:00 CEST 2005 (in the time zone CET).
But because [21:20:18] lies in the future, fail2ban would assume failure from previous day, so it results to 2005-08-13T21:20:18.

@coveralls
Copy link

coveralls commented Oct 15, 2020

Coverage Status

Coverage remained the same at 98.104% when pulling 778b094 on MinePro120:master into 960e30c on fail2ban:master.


[Definition]

failregex = \[\/<HOST>\:
Copy link
Contributor

Choose a reason for hiding this comment

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

This is most vulnerable regex ever, I seen in the filter.
It would match [/192.0.2.1:12345] successfully logged in as well as [/192.0.2.2:12345] access denied.
Please correct this (provide more precise, anchored regex).

Copy link
Author

Choose a reason for hiding this comment

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

There is no "access denied" really. There is a whitelist feature provided by the server, but it's rarely used (but I might as well add it in the future). The purpose of this filter is to block a DOS attack in which players with random names join the server if the server is in offline mode, where an online-based layer of authentication is not used (an attack I've seen). So the key here are the findtime and maxretry options that can rule out spamming bots. I should probably clarify this in a comment in the filter.

Copy link
Contributor

Choose a reason for hiding this comment

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

logged in and lost connection: Disconnected surely don't represent any attack vector

Copy link

Choose a reason for hiding this comment

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

Here's what a bot attack looks like on my own server:

[03:32:54] [Server thread/INFO]: com.mojang.authlib.GameProfile@508346c3[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:50306) lost connection: Disconnected
[03:36:28] [Server thread/INFO]: com.mojang.authlib.GameProfile@4392677b[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:54278) lost connection: Disconnected
[03:39:37] [Server thread/INFO]: com.mojang.authlib.GameProfile@59032ddf[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:59320) lost connection: Disconnected
[03:40:53] [Server thread/INFO]: com.mojang.authlib.GameProfile@3c65e0e1[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:55888) lost connection: Disconnected
[03:46:07] [Server thread/INFO]: com.mojang.authlib.GameProfile@672c7a59[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:46164) lost connection: Disconnected
[03:56:35] [Server thread/INFO]: com.mojang.authlib.GameProfile@3feaeec1[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:47884) lost connection: Disconnected
[04:00:12] [Server thread/INFO]: com.mojang.authlib.GameProfile@124d287d[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:47432) lost connection: Disconnected
[04:02:28] [Server thread/INFO]: com.mojang.authlib.GameProfile@4c7d9ef5[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:38110) lost connection: Disconnected
[04:06:09] [Server thread/INFO]: com.mojang.authlib.GameProfile@72fd24a0[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:47528) lost connection: Disconnected
[04:09:48] [Server thread/INFO]: com.mojang.authlib.GameProfile@62e80648[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:59492) lost connection: Disconnected
[04:13:36] [Server thread/INFO]: com.mojang.authlib.GameProfile@5981520a[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:35350) lost connection: Disconnected
[04:17:27] [Server thread/INFO]: com.mojang.authlib.GameProfile@351d996e[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:49122) lost connection: Disconnected
[04:21:19] [Server thread/INFO]: com.mojang.authlib.GameProfile@62be7d0d[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:50928) lost connection: Disconnected
[04:25:02] [Server thread/INFO]: com.mojang.authlib.GameProfile@e1ada86[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:59308) lost connection: Disconnected
[04:28:42] [Server thread/INFO]: com.mojang.authlib.GameProfile@46d2bd38[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:44334) lost connection: Disconnected
[04:32:41] [Server thread/INFO]: com.mojang.authlib.GameProfile@4cf80fb3[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:58452) lost connection: Disconnected
[04:36:37] [Server thread/INFO]: com.mojang.authlib.GameProfile@34b3d11a[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:58510) lost connection: Disconnected
[04:40:34] [Server thread/INFO]: com.mojang.authlib.GameProfile@29a1059[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:37508) lost connection: Disconnected
[04:44:27] [Server thread/INFO]: com.mojang.authlib.GameProfile@240239f7[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:56702) lost connection: Disconnected
[04:48:26] [Server thread/INFO]: com.mojang.authlib.GameProfile@4d26720b[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:55874) lost connection: Disconnected
[04:52:23] [Server thread/INFO]: com.mojang.authlib.GameProfile@6e4a5cf3[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:34900) lost connection: Disconnected
[04:56:20] [Server thread/INFO]: com.mojang.authlib.GameProfile@1457bf54[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:32780) lost connection: Disconnected
[05:00:11] [Server thread/INFO]: com.mojang.authlib.GameProfile@213952cc[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:37350) lost connection: Disconnected
[05:04:07] [Server thread/INFO]: com.mojang.authlib.GameProfile@342c9e32[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:40264) lost connection: Disconnected
[05:08:00] [Server thread/INFO]: com.mojang.authlib.GameProfile@4c6b588a[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:42654) lost connection: Disconnected
[05:11:57] [Server thread/INFO]: com.mojang.authlib.GameProfile@40ec9934[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:42936) lost connection: Disconnected
[05:16:17] [Server thread/INFO]: com.mojang.authlib.GameProfile@19967be1[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:54126) lost connection: Disconnected
[05:20:11] [Server thread/INFO]: com.mojang.authlib.GameProfile@151407c8[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:44118) lost connection: Disconnected
[05:24:03] [Server thread/INFO]: com.mojang.authlib.GameProfile@1b7635c7[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:60018) lost connection: Disconnected
[05:28:02] [Server thread/INFO]: com.mojang.authlib.GameProfile@5fcdd47a[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:60892) lost connection: Disconnected
[05:31:57] [Server thread/INFO]: com.mojang.authlib.GameProfile@6c78fdce[id=<null>,name=cuute,properties={},legacy=false] (/20.4.48.76:60474) lost connection: Disconnected

It's a whitelisted server using online mode (meaning player identities are verified by Mojang). As a result the attackers are only generating a disconnected message without ever generating the messages:

[14:36:24] [Server thread/INFO]: <player name>[/<IP>:<port>] logged in with entity id 73 at (-611.530629608662, 65.0, -266.9221458299592)
[14:36:24] [Server thread/INFO]: <player name> joined the game

Regular disconnect messages come in a couple of forms:

[14:43:01] [Server thread/INFO]: <player name> lost connection: Disconnected
[14:43:01] [Server thread/INFO]: <player name> left the game
[19:54:35] [Server thread/INFO]: <player name>lost connection: Timed out
[19:54:35] [Server thread/INFO]: <player name> left the game

This last disconnect, noted here below, came seconds before the legitimate player decided to log in, suggesting this is some kind of scan by the legitimate client to gather server status information on the list of servers configured by the player. This information contains server connection quality as well as the list of players currently online.

The top line of this one makes it difficult to just take the contents of this specific line to make a filter, given the fact that it's an exact match for the log result when a bot scans your server.

[11:21:19] [Server thread/INFO]: com.mojang.authlib.GameProfile@27ce9407[id=<null>,name=<player name>,properties={},legacy=false] (/<ip>:<port>) lost connection: Disconnected
[11:21:29] [User Authenticator #2/INFO]: UUID of player <player name> is <player uuid>
[11:21:29] [Server thread/INFO]: <player name>[/<ip>:<port>] logged in with entity id 294 at (-45.820534318481705, 68.0, 60.00821373182576)
[11:21:29] [Server thread/INFO]: <player name> joined the game

The main purpose of the attack seems to be people scanning for player online information to find griefing targets, or to find servers with sensitive player names (such as youtubers) so they can attack those servers.

Choose a reason for hiding this comment

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

What's the difference between a bot's lost connection: Disconnected message in the logs and a legitimate player's? If they're the same, or have any crossover, fail2ban can't protect against that afaik.

Copy link

Choose a reason for hiding this comment

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

@trainzkid whilst it is possible for a legitimate user to generate the messages containing the com.mojang.authlib.GameProfile bits, they usually have a much simpler disconnect message. So it's really the frequency of the authlib line that stands out.

If you look at my attack log, at the 4 AM mark, you see that an abnormal connection disconnect happens like 16 times within that hour alone. If that were a normal player trying to put some extra load on my server by trying to load and unload the world around them that often I'd also have no issue with fail2ban showing them the door for a couple of hours/days.

As for when normal players create that message? I've seen it happen once, maybe twice a week. Could be a network issue when that happens though, and if that's the case you don't really wanna block them for having a bad ISP.

With all that said, I agree that Mojang could be providing better log messages for this kind of issue.

Choose a reason for hiding this comment

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

I see what you mean, however, I think the main intent of fail2ban is to "ban" on bad authentication type errors, and the suggested filter above is just way too vague/broad that it's just as likely to affect legitimate players as it is bots.

That being said, the beauty of open source software and the freedom of config files means anyone who disagrees with my statement is more than welcome to copy the above filter and go to town on their local Minecrafters with extreme prejudice.

I also agree that Mojang could definitely improve their error messages a bit better so it's more obvious when a legitimate bot/baddie attempts to access your server. Unfortunately, I'm not confident that Mojang will make any sort of improvements in the near future. All we can do is adapt with what we're given.

On another note, adding a filter for the whitelist error doesn't sound like a bad idea to me. I think that probably has more candidacy than the subtle "disconnected" error, personally.

@@ -0,0 +1,2 @@
# failJSON: { "time": "2005-08-13T21:30:40", "match": true, "host": "123.45.67.89" }
[21:30:40] [Server thread/INFO]: Player[/123.45.67.89:12345] logged in with entity id 551 at (825.302579326729, 65.0, -886.9107800126116)
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... now I confused still more - does Player logged in not mean a successful login.
I don't really see it is an authentication failure or even something critical or evil (in sense of fail2ban).

Copy link
Contributor

Choose a reason for hiding this comment

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

So you decided to remove it from the test, but the issue remain - the message will be matched with the filter, so still don't think such regex is good idea.

Copy link

Choose a reason for hiding this comment

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

For Geyser (de-DE) these Message should also be handled:
[epollEventLoopGroup-3-2/INFO]: [Geyser-Spigot] /123.127.235.6:1051 versuchte zu verbinden

config/jail.conf Outdated Show resolved Hide resolved
@codecov-io
Copy link

codecov-io commented Oct 15, 2020

Codecov Report

Merging #2852 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2852   +/-   ##
=======================================
  Coverage   95.91%   95.91%           
=======================================
  Files          79       79           
  Lines       15978    15978           
  Branches     2615     2615           
=======================================
  Hits        15326    15326           
  Misses        344      344           
  Partials      308      308           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 960e30c...43a9ce9. Read the comment docs.

@codecov-commenter
Copy link

Codecov Report

Merging #2852 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2852   +/-   ##
=======================================
  Coverage   95.91%   95.91%           
=======================================
  Files          79       79           
  Lines       15978    15978           
  Branches     2615     2615           
=======================================
  Hits        15326    15326           
  Misses        344      344           
  Partials      308      308           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 960e30c...9d16444. Read the comment docs.

1 similar comment
@codecov-io
Copy link

codecov-io commented Oct 15, 2020

Codecov Report

Merging #2852 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2852   +/-   ##
=======================================
  Coverage   95.91%   95.91%           
=======================================
  Files          79       79           
  Lines       15978    15978           
  Branches     2615     2615           
=======================================
  Hits        15326    15326           
  Misses        344      344           
  Partials      308      308           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 960e30c...9d16444. Read the comment docs.

-Added support for blocking non-whitelisted players
-Added support for blocking "cracked" players
-Changed jail.conf recommendations
@sebres sebres force-pushed the master branch 5 times, most recently from fe8a9d2 to 9d77fb2 Compare November 3, 2020 14:38
@Ludo-code
Copy link

Any news about this pull request?

@sebres
Copy link
Contributor

sebres commented Jan 3, 2023

I don't think it is justified to merge this PR, because it still looks "vulnerable" at the moment.
Moreover, take a closer look to testcase - it looks like a single attempt will be counted 3 times (as it'd be 3 attempts), what is not correct in my opinion.

If the log-file containing the message like this:

[21:30:40] [Server thread/INFO]: Disconnecting com.mojang.authlib.GameProfile@12345678[id=7de34b7e-069d-48a5-92c5-f583ae7c7a8b,name=Player,properties={textures=[com.mojang.authlib.properties.Property@12345678]},legacy=false] (/123.45.67.89:12345): You are not white-listed on this server!

I would use something like this instead:

[minecraft]
port = ...
logpath = ...
filter = 
failregex = ^[^:]*: Disconnecting \S+ \(/<ADDR>:\d+\): You are not white-listed on this server!
enabled = true

@Omig12
Copy link

Omig12 commented May 19, 2023

This is still a non-working solution, but it builds on @sebres suggestion:

failregex = \[(?P<time>\d+:\d+:\d+)\].*\(\/(?P<host>\d+.\d+.\d+.\d+):\d+\) lost connection.*$

The host and time capture attempt to comply with some fail2ban specifications, but Minecraft Servers output the timestamp in a format that seems incompatible with the fail2ban time regex. Which I think is what @MinePro120 and @sebres were discussing earlier. I'm looking for a way to modify how the Minecraft server reports the date and time in the logs. Still, all I've found so far are guides for tweaking log4j; from my understanding, that logger was deprecated for vulnerabilities.

@sebres
Copy link
Contributor

sebres commented May 27, 2023

This is still a non-working solution

Why then do you provide such non-working "solution" here?

I'm looking for a way to modify how the Minecraft server reports the date and time in the logs.

You don't need... One can simply specify datepattern for the jail or filter.
It looks like you'd need:

datepattern = ^\[%%H:%%M:%%S\]\s+

By the way - usage of catch-all's is ugly and .*$ is not an anchor at all, so either remove it completely (if it is justified unanchored) or rewrite catch-all more precisely.
And don't use captured groups of (?P<host>...), use <ADDR> (or <HOST>) instead.

@Omig12
Copy link

Omig12 commented Jul 13, 2023

Hey, thanks for the reply.

Why then do you provide such non-working "solution" here?

Because this is a collaborative platform, and instead of being snarky, I'll contribute my limited knowledge so that others may improve upon it.

You don't need... One can simply specify datepattern for the jail or filter.

The date patterns the logs provide resolve from seconds up to the hour, so there is no day, month, or year information. Thanks for that link to the documentation; I see that by specifying datepattern as NONE we could bypass that limitation.

I don't know what the fial2ban best practices are. Would you recommend keeping the datepattern matching the time from seconds up to the hour or setting it to NONE?

Regarding the use of catch-alls not sure how to avoid using it since the log entries are quite heterogeneous. It might not be the best solution, but if it works, it provides something for others with the skills to improve upon it. I was testing it against the regex 101 Python option, and .*$ seemed to work; I might not be understanding your comment correctly.

And don't use captured groups of (?P...), use (or ) instead.

Just followed what the DOCs I had available said.

image

https://www.fail2ban.org/wiki/index.php/MANUAL_0_8#:~:text=for%20%22permanent%22%20ban.-,Filters,-The%20directory%20filter

Hope this helps anyone trying to improve on this solution, it would be really cool to have something like this working (even if it's just remedially) to get rid of the robot-spam on the server.

@trainzkid
Copy link

This isn't quite the same as the use case mentioned in the aforementioned PR, but I've found using a systemd service for logging means fail2ban can utilize systemd-journal's timestamps, and a regex that matches the whitelist errors works. That's not exactly the bot problem this discussion has been about, but it's better than nothing.

Here's my minecraft-whitelist.conf:

[INCLUDES]
before = common.conf

[Definition]
failregex = Disconnecting .* \(/<ADDR>:.*\): You are not white-listed on this server!
journalmatch = _SYSTEMD_UNIT=$SERVICE.service + _COMM=$START.sh

where $SERVICE is the name of the systemd service and $START.sh is the script or binary that starts the server (usually ServerStart.sh or something similar).

I'm also aware there aren't any anchors, I've had some trouble with them in the past for whatever reason.

@sebres
Copy link
Contributor

sebres commented Apr 3, 2024

I'm also aware there aren't any anchors, I've had some trouble with them in the past for whatever reason.

Well it depends on log-format (and log-target, e. g. systemd journal or log-file), here is fully-anchored example for log-line from this PR:

failregex = ^[^:]*: Disconnecting [^\(]*\(/<ADDR>:\d+\): You are not white-listed on this server!

([^:]* and [^\(]* are not catch-alls and mean everything before colon and open round parenthesis, respectively)

Alternatively it can be only anchored from the end, with few conditions:

  • there should be no catch-alls between <ADDR> and determining message (exactly the case here);
  • logging of free message content (some foreign user input) at the end of the log is explicitly excluded (hardly possible to estimate without to review the source code or asking developers).
failregex = \(/<ADDR>:\d+\): You are not white-listed on this server!\s*$

@trainzkid
Copy link

I think the issues I ran into were related to extraneous timestamps at the beginning of a log entry, despite using systemd-journal. Basically, the timestamp mentioned earlier in this thread is still present in the journal entry, so when I tried to anchor at the beginning, I'm guessing my regex was having trouble with that initial timestamp when it had already successfully parsed the journal's timestamp. I don't know if fail2ban tries to parse timestamps again after successfully getting the timestamp from the journal or if I need to add something to the regex to make it ignore that initial section in square brackets with the timestamp. I can't tell if that's what your regex does, it doesn't look like it does.

The other issue I may have been facing was when I heard anchoring, I thought it meant on both sides (both beginning and end). Is it preferred to only anchor from one or the other and not both?

I think in this specific case for minecraft-whitelist, anchoring from the end would work just fine, I don't think there is any added input in between.

@sebres
Copy link
Contributor

sebres commented Apr 3, 2024

I thought it meant on both sides (both beginning and end).

It is true, either if one needs to be precise as possible, or if there are some catch-alls similar REs used inside, before the captured data in left anchored RE, or after captured data in right anchored RE (quasi to restrict corner cases for injection attempts via foreign data).

Is it preferred to only anchor from one or the other and not both?

It depends, but generally spoken - no, it is not preferred.
If there is no catch-alls and matched message part is obvious a failure and such match allows unambiguously distinguish failed messages from the rest, one can surely use the anchor from one side only (mostly begin-anchor).
This has also another pros, like it wouldn't matter for which reason the failure happens, it is still a failure and it'd be considered as a failed attempt regardless what devs of service would devise later (or which info the log may get latter additionally).

For instance an RE ^\s*authentication failed for user "[^"]+": would match all that:

authentication failed for user "usr1": user unknown
authentication failed for user "usr2": password mismatch
authentication failed for user "usr3": auth-service unavailable
authentication failed for user "usr4": unsupported method
authentication failed for user "usr5": whatever reason devs invent tomorrow or more data gets included later

And if something of that shall not cause a failure (e. g. 3rd message), it can be pretty simple done with negative lookahead: ^\s*authentication failed for user "[^"]+": (?!auth-service unavailable)

It is hardly possible to give an unique answer for general usecase.

I think in this specific case for minecraft-whitelist, anchoring from the end would work just fine, I don't think there is any added input in between.

"I don't think" is not equal "I know", what is mostly expected for a stock filter.
You can basically do what you want on your hosts (since it is your responsibility).
But in case of some OSS-project, we must ensure it'd not cause false positives (or be affected by some inject-attempts).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet