Both features ran on every shard out of the box, each polling on its own 60s timer for files most shards never generate. Neither was ever asked for. Blocklist: the only disable path was an empty "file", and the default is non-empty, so Start() always reached Task.Run(PollLoop) plus a recurring SweepGuard timer. Adds "enabled" (default false). A shard that has a list on disk but no "enabled" key logs a Warning rather than silently dropping a gate it was relying on. File allowlist: moves out of blocklist.json into its own ip-allowlist.json with "enabled" (default false), "files" and "reloadInterval". It was never a sub-feature of the blocklist -- its two consumers are BlocklistFilter, where the generator already subtracts these files anyway, and BanExemptions, which suppresses behavioural ban contributions and works on a shard running no blocklist at all. That second consumer is the only mechanism for what it does, so a shared flag could not express it. "allowlistFiles" stays bound on BlocklistSettings, defaulting to null and deliberately not honoured, purely so an operator who set it is told where it went instead of losing the carve-out silently. The two still work together: the blocklist warns at startup when it is on and the file allowlist is not, since only the generator's subtraction is covering carve-outs then, and that does not cover ban contributions. Also fixes the promote-guard sweep, which was recurring and tokenless, so Stop() cancelled the poll but left the sweep running and a later Start() added another. It is now held and stopped, and only started when hits are reported -- nothing can mark the guard otherwise. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
83 lines
3.5 KiB
C#
83 lines
3.5 KiB
C#
/*************************************************************************
|
|
* ModernUO *
|
|
* Copyright 2019-2026 - ModernUO Development Team *
|
|
* Email: hi@modernuo.com *
|
|
* File: FileAllowlistConfiguration.cs *
|
|
* *
|
|
* This program is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation, either version 3 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
*************************************************************************/
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Text.Json.Serialization;
|
|
using Server.Json;
|
|
|
|
namespace Server.Network.Bans;
|
|
|
|
/// <summary>
|
|
/// Loads the <see cref="FileAllowlistSettings"/> from <c>Configuration/ip-allowlist.json</c>. Loaded once;
|
|
/// a missing file writes a template so operators have something to edit.
|
|
/// </summary>
|
|
public static class FileAllowlistConfiguration
|
|
{
|
|
private const string _path = "Configuration/ip-allowlist.json";
|
|
|
|
public static FileAllowlistSettings Settings { get; private set; }
|
|
|
|
public static void Load()
|
|
{
|
|
var path = Path.Join(Core.BaseDirectory, _path);
|
|
|
|
if (File.Exists(path))
|
|
{
|
|
Settings = JsonConfig.Deserialize<FileAllowlistSettings>(path);
|
|
}
|
|
else
|
|
{
|
|
Settings = new FileAllowlistSettings();
|
|
Save();
|
|
}
|
|
}
|
|
|
|
private static void Save()
|
|
{
|
|
JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bound configuration for <see cref="FileAllowlist"/>. Its own file rather than a corner of
|
|
/// <c>blocklist.json</c>: the blocklist is only one of two consumers, and the other
|
|
/// (<see cref="BanExemptions"/>) works on a shard that runs no blocklist at all.
|
|
/// </summary>
|
|
public record FileAllowlistSettings
|
|
{
|
|
/// <summary>
|
|
/// Whether the shard reads <see cref="Files"/> at all. Off by default: reading them costs a poll for
|
|
/// the whole uptime, which no shard should pay before an operator has written a carve-out.
|
|
/// </summary>
|
|
[JsonPropertyName("enabled")]
|
|
public bool Enabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// Addresses that must never be blocked and never escalated, in the blocklist's own format. The same
|
|
/// files <c>tools/Export-IpBlocklist.ps1</c> subtracts at generation time; the shard reads them so an
|
|
/// entry also suppresses ban contributions, which the generator alone cannot do.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The filename may contain wildcards, which is how the default picks up a carve-out an admin adds
|
|
/// without anyone editing this file.
|
|
/// </remarks>
|
|
[JsonPropertyName("files")]
|
|
public string[] Files { get; set; } = ["Configuration/ip-allowlist*.txt"];
|
|
|
|
/// <summary>How often the files are checked for changes. Reloads only happen when one actually changed.</summary>
|
|
[JsonPropertyName("reloadInterval")]
|
|
public TimeSpan ReloadInterval { get; set; } = TimeSpan.FromSeconds(60);
|
|
}
|