COWMODSКаталог модов

OPShield

Protects your server from OP/admin abuse with console-only OP (via password) and optional admin command restriction.

Автор: Duong2012G4471обновлён 4 июн. 2026 г.
Управление

Описание

🛡️ OPShield

Advanced security & command protection plugin for Paper 1.21+

Version Paper Java License

Protect your server from abuse — secure OP access, track every action, stop attackers instantly.


📖 Table of Contents

  • ✨ Features
  • 🔐 Security System
  • 📊 Audit & Logging
  • 📦 Requirements
  • 🚀 Installation
  • ⌨️ Commands & Permissions
  • 🔧 Configuration
  • 🔍 Troubleshooting
  • ❓ FAQ
  • 📄 Credits

✨ Features

🔒 OP Protection

  • Password-gated /op and /deop — no password, no privilege changes
  • Async PBKDF2 verification — password hashing runs off the main thread; zero TPS impact even under brute-force attack
  • Authenticated session tokens — after one successful login, a configurable session window lets admins skip re-entering the password (security.session_timeout_minutes, default 30 min)
  • Session cleared on disconnect — sessions are never carried across reconnections
  • Global auth concurrency cap — server-wide PBKDF2 operations capped at 4 concurrent verifications to prevent CPU saturation under mass attack
  • PBKDF2-HMAC-SHA256 hashed storage — plaintext never kept in config
  • Configurable PBKDF2 iteration count (security.password.pbkdf2_iterations)
  • Automatic migration from legacy plaintext and SHA-256 hashes
  • Auto-upgrade legacy hashes — on next successful login, SHA-256 is silently replaced with PBKDF2 (security.password.auto_upgrade_legacy_hash)
  • Console warning if a legacy SHA-256 hash is detected on startup
  • OP whitelist — restrict /op to a predefined set of player names

🚫 Sensitive Command Protection

  • Block dangerous commands for non-OP players (blocked_commands)
  • Optionally block commands even for OP players (blocked_op_commands)
  • Block entire command namespaces via prefix list (blocked_command_prefixes)
  • Alias and namespace resolution — bypass attempts via minecraft:op or plugin aliases are caught
  • Per-player bypass permission (opshield.bypass) for trusted staff

🧠 Brute-force Detection & Lockout

  • Configurable failed-attempt limit before lockout (security.lockout.max-attempts)
  • Exponential backoff — each offence doubles the lockout duration
  • Optional IP-mirrored lockout (security.lockout.track_ip)
  • Lockout count decay after a cooling-off period (security.lockout.count_decay_hours)
  • Persistent tracking — lockout state survives server restarts
  • Manual unlock via /opshield unlock <player|ip>

🕵️ Shadow Ban System

  • Sensitive blocked commands send a fake success message instead of an error
  • Each trigger increments the player's hidden shadow-ban level
  • Level persists across restarts
  • Auto-escalates to real punishment when shadow_ban.auto_punish_level is reached
  • Set auto_punish_level: 99 to keep decoy behaviour without escalation
  • Fake messages come from language files — fully customisable per locale

⚠️ Auto Punishment System

  • Punishment modes: kick, ban, ban-ip, firewall, custom
  • Persistent rolling-window threshold — survives restarts and crashes
  • Firewall mode runs an OS script asynchronously via ProcessBuilder with configurable timeout
  • Custom mode supports {player} and {ip} placeholders
  • IP-limit auto-punishment for accounts detected sharing the same IP

🌍 Multi-Language Support

  • Bundled language files: English (en), Vietnamese (vn), Russian (ru)
  • Automatic fallback to English for any missing key
  • Switch language via language: "en" in config.yml

📊 Audit & Logging

  • Every privilege change, password failure, command block, and punishment is logged
  • Async queue — log writes never touch the main thread
  • Queue capacity limit — audit.max_queue_size prevents OOM if disk writes fail for extended periods
  • Dual output format — audit.format: plain (default human-readable) or audit.format: json (machine-readable, one JSON object per line)
  • UTF-8 safe — uses NIO Files.write() with explicit charset
  • Retry on failure — failed writes are re-queued instead of silently discarded
  • Configurable rotation: audit.max_file_size_mb and audit.log_retention (up to N backup files)
  • Optional console mirror: audit.console_output: true
  • Log files: plugins/OPShield/audit.log, audit.log.1 … audit.log.N

📦 Requirements

ComponentVersion
Java21+
Paper1.21+
Folia❌ Not supported

🚀 Installation

  1. Download the plugin .jar
  2. Drop it into your server's plugins/ folder
  3. Start the server — OPShield will generate a random password and print it once in the console
  4. Save the password somewhere safe (it is only shown once)
  5. Grant permissions — add opshield.admin to your admin group in your permission plugin (e.g. LuckPerms). OPShield no longer grants permissions based on OP status alone (changed in v1.8.0)
  6. Open plugins/OPShield/config.yml to customise behaviour
  7. Run /opshield reload in-game or restart to apply changes ✅

Upgrading from 1.7.0? data.yml is automatically migrated on first boot. You only need to update your permission plugin setup — see the CRITICAL note in the changelog.

Tip: If you already have an op_password plaintext value from an older version, OPShield will automatically migrate it to op_password_hash and remove the plaintext entry.


⌨️ Commands

CommandDescription
/op <player> [password]Grant OP with password verification
/deop <player> [password]Remove OP with password verification
/opshield reloadReload configuration
/opshield unlock <player|ip>Clear all tracking state for a player or IP
/opshield statusShow runtime statistics (active sessions, auth queue, flagged IPs, etc.)

🔐 Permissions

⚠️ Changed in v1.8.0: All permissions now default to false. You must grant them explicitly via a permission plugin.

PermissionDefaultDescription
opshield.*falseWildcard — grants all permissions
opshield.adminfalseGrants all child permissions
opshield.reloadfalseReload OPShield configuration
opshield.unlockfalseUnlock a tracked player or IP
opshield.statusfalseView runtime statistics
opshield.opfalseUse password-protected /op
opshield.deopfalseUse password-protected /deop
opshield.bypassfalseBypass non-OP restricted command blocking

Example LuckPerms setup

/lp group admin permission set opshield.admin true

🔧 Configuration

Files generated under plugins/OPShield/:

  • config.yml — main configuration
  • data.yml — persistent runtime state (lockouts, shadow-ban levels, IP windows)
  • languages/en.yml — English messages
  • languages/vn.yml — Vietnamese messages
  • languages/ru.yml — Russian messages

⚙️ Key Config Options

# Enable verbose console logging for troubleshooting (disable in production)
debug: false

# Password security
security:
  lockout:
    enabled: true
    max-attempts: 3
    duration-minutes: 3
    track_ip: true
    count_decay_hours: 168
  # Session timeout after successful auth (0 = disable, always require password)
  session_timeout_minutes: 30
  password:
    pbkdf2_iterations: 120000       # range: 10000 – 1000000
    auto_upgrade_legacy_hash: true  # silently upgrade SHA-256 → PBKDF2 on login

# Auto-punishment
auto_punishment:
  enabled: true
  threshold: 5
  window_seconds: 300
  command: "kick"                   # kick | ban | ban-ip | firewall | custom
  firewall_timeout_seconds: 10

# Shadow ban
shadow_ban:
  enabled: true
  auto_punish_level: 5

# Audit log
audit:
  console_output: true
  max_file_size_mb: 5
  log_retention: 3
  max_queue_size: 10000             # 0 = unlimited (not recommended)
  format: "plain"                   # plain | json

📐 Recommended settings by server size

Small server (≤ 20 players)

security.lockout.max-attempts: 3
security.lockout.duration-minutes: 5
ip_limit.max_accounts: 2
auto_punishment.enabled: false
shadow_ban.auto_punish_level: 10

Medium server (20–100 players)

security.lockout.max-attempts: 3
security.lockout.duration-minutes: 3
ip_limit.max_accounts: 3
auto_punishment.enabled: true
auto_punishment.command: kick
auto_punishment.threshold: 5
shadow_ban.auto_punish_level: 5

Large server (100+ players)

security.lockout.max-attempts: 2
security.lockout.duration-minutes: 10
ip_limit.max_accounts: 2
auto_punishment.enabled: true
auto_punishment.command: ban-ip
auto_punishment.threshold: 3
shadow_ban.auto_punish_level: 3

🔥 Firewall mode setup

Firewall mode executes an OS script asynchronously. To enable it:

auto_punishment:
  command: "firewall"
  allow_unsafe_firewall_exec: true
  firewall_timeout_seconds: 10
  # Linux:
  firewall_script: "iptables -A INPUT -s {ip} -j DROP"
  # Windows:
  # firewall_script: "netsh advfirewall firewall add rule name=OPShield dir=in action=block remoteip={ip}"

If allow_unsafe_firewall_exec is false, firewall mode falls back to a safe kick.


🔍 Troubleshooting

Admin needs to enter password every single /op command

OPShield grants a session after each successful authentication. Check:

  • security.session_timeout_minutes in config.yml — if set to 0, sessions are disabled
  • The session is cleared when you disconnect; if you keep reconnecting you will need to re-authenticate
  • Run /opshield status to see how many active sessions exist
Cannot use /op — "Incorrect password"

The password is required. Run:

/op <yourname> <password>

If you forgot the password, clear op_password_hash in config.yml and restart — a new password will be generated and printed in the console.

Admin cannot use /opshield after upgrading from 1.7.0

In v1.8.0, permissions now default to false instead of op. You need to explicitly grant the permission:

/lp group admin permission set opshield.admin true
Player is locked out and cannot try again

An admin can manually clear the lockout:

/opshield unlock <playername>
/opshield unlock <ip-address>
Auto-punishment is not triggering

Check the following:

  • auto_punishment.enabled: true in config.yml
  • The command the player used is listed in auto_punishment.sensitive_commands
  • shadow_ban.enabled — if true, the player may be getting fake success messages first; level must reach auto_punish_level
  • Run /opshield reload after any config change
  • Run /opshield status to see current shadow-ban levels and punish state
Audit log is empty or not updating
  • Check audit.console_output: true to confirm logging is active
  • Check write permissions on the plugins/OPShield/ folder
  • If audit.max_queue_size is reached, a SEVERE warning appears in console — check disk space
Config changes are not taking effect

Run in-game or console:

/opshield reload

❓ FAQ

Does OPShield replace /op?

No — it intercepts and wraps it. The original /op behaviour is preserved but gated behind a password.


Is the password stored securely?

Yes — passwords are hashed using PBKDF2-HMAC-SHA256 with a random salt and 120,000 iterations (configurable). The plaintext is never written to disk.


Does it support Spigot or Folia?

Paper 1.21+ only. Spigot may work but is not tested. Folia is explicitly not supported (folia-supported: false).


Can I disable auto-punishment entirely?

Yes — set auto_punishment.enabled: false. Shadow-ban fake messages will still work independently.


What happens if the server restarts during a lockout?

Lockout state is persisted to data.yml and restored on startup. Players cannot bypass lockouts by crashing or restarting the server.


Can I have multiple language files?

Yes — all three bundled files (en, vn, ru) are always present. Switch via language: in config.yml. Missing keys automatically fall back to the bundled English defaults.


What changed in v2.0.0?

Thread-safety overhaul — five race conditions and visibility bugs fixed:

  • ArrayDeque race condition — replaced with ConcurrentLinkedDeque to prevent ConcurrentModificationException and data corruption when the async cleanup task and main-thread event handler accessed the same deque simultaneously.
  • Non-volatile config fields — lockoutDecayHours, autoPunishmentWindowSeconds, ipLimitTimeWindowSeconds, and sessionTimeoutMs are now volatile so async tasks always see up-to-date values after /opshield reload.
  • Non-volatile AuditLogger fields — consoleOutput, maxBytes, maxBackups, maxQueueSize, and format are now volatile.
  • IP range check — replaced 16 manual startsWith() checks with InetAddress predicates, adding previously missing link-local range detection.
  • Audit log level — audit console output now uses INFO instead of WARNING.

See the Changelog for details.

What changed in v1.9.1?

Six bugs fixed in a patch release: double timestamps in audit log, missing session-granted/active messages, shadow-ban decoy messages losing their colours, exponential backoff count being reset too early (making count_decay_hours ineffective), and the /opshield reload permission check order. See the Changelog for details.

What changed in v1.9.0?

The biggest changes are async PBKDF2 authentication (no more TPS impact under brute-force) and session tokens (no need to re-type the password every command within the session window). See the Changelog for the full list.

What changed in v1.8.0?

The most important change is permission defaults — see the Changelog for the full list. The short version: grant opshield.admin to your admin group in LuckPerms.


📄 Credits

Author: Duong2012G
License: Apache 2.0
Website: https://modrinth.com/user/Duong2012G

Built for secure, professional Minecraft servers.

Последние версии

Все версии →
release

OPShield 2.0.0

2.0.0
4 июн. 2026 г.137Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [2.0.0] - 2026-06-03 — Thread-Safety & Security Overhaul This release resolves all known thread-safety bugs identified through a full code audit. No configuration keys were added, removed, or renamed. Existing `config.yml` and `data.yml` files are **fully compatible** — no migration required. ---
release

OPShield 1.9.1

1.9.1
23 мая 2026 г.41Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [1.9.1] - 2026-05-23 — Bug Fix Release This is a patch release that resolves six bugs identified through code review. No configuration keys were added, removed, or renamed. Existing `data.yml` and `config.yml` files are fully compatible — no migration needed. --- ### 🐛 Bug Fixes #### FIX [Critical] — Double timestamp in every audit log entry - **Root cause:** `OPShield.audit()` manually prepended a formatted timestamp to the message string before passing it to `AuditLogger.log()`. However, `AuditLogger.buildLine()` also wraps every line with its own timestamp, resulting in output like: ``` [2026-05-23 10:00:00] 2026-05-23 10:00:00 | Configuration reloaded. ``` This affected every audit entry since v1.6.0. - **Fix:** Removed the manual timestamp construction from `OPShield.audit()`. Timestamp formatting is now owned exclusively by `AuditLogger.buildLine()`. Audit lines now read correctly: ``` [2026-05-23 10:00:00] Configuration reloaded. ``` - **Impact:** Existing rotated log files (`audit.log.1` etc.) retain the old double-timestamp format. New entries written after the upgrade are clean. No action required on existing files. #### FIX [High] — `auth_session_granted` and `auth_session_active` messages were never sent - **Root cause:** The language key `auth_session_granted` was introduced in v1.9.0 but the corresponding `send()` call was absent from both `completePrivilegeCommand()` (session grant path) and `handlePrivilegeCommand()` (session reuse path). Players had no way to know a session existed or how much time remained. - **Fix:** - `completePrivilegeCommand()` now calls `sendReplaced(player, "auth_session_granted", "minutes", …)` immediately after the session is stored in `authenticatedSessions`. - `handlePrivilegeCommand()` now calls `sendReplaced(player, "auth_session_active", "minutes", …)` (with the remaining minutes) on the session-reuse fast-path. - **New language key:** `auth_session_active` added to `en.yml`, `vn.yml`, and `ru.yml`. #### FIX [Medium] — Shadow-ban decoy messages lost all colour formatting - **Root cause:** The fake-success message pipeline was: ```java PlainTextComponentSerializer.plainText().serialize(colorize(fakeMsg)) ``` `colorize()` correctly parsed `&a`, `&c`, etc. into an Adventure `Component` with colour, but `PlainTextComponentSerializer.plainText().serialize()` then stripped every colour code before wrapping the result in a hard-coded `NamedTextColor.YELLOW`. Language-file formatting was silently discarded. - **Fix:** Removed the unnecessary serialise/re-wrap chain. The `Component` returned by `colorize(fakeMsg)` is now sent directly: ```java player.sendMessage(colorize(fakeMsg)); ``` Server owners can now apply any Adventure-compatible colour codes (`&a`, `&c`, `&l`, etc.) in their language files and have them rendered correctly. - **Cleanup:** Removed now-unused imports `NamedTextColor`, `PlainTextComponentSerializer`, and `Component` from `OPShield.java`. #### FIX [Medium] — `LockoutManager.cleanupExpired()` discarded backoff `count` earlier than `count_decay_hours` intended - **Root cause:** `cleanupExpired()` removed any record whose `expiryMs <= now` AND `attempts == 0`. When a lockout expired naturally, `isLockedOut()` set `expiryMs = 0` and `attempts = 0` (intentionally preserving `count` for exponential backoff). On the very next cleanup tick (≤ 60 s later), the cleanup predicate matched and deleted the entire record, including `count`. The `security.lockout.count_decay_hours` config key had no practical effect: the backoff counter was always reset within one cleanup cycle after each lockout expired. - **Fix:** `cleanupExpired()` now accepts a `decayMs` parameter (derived from `lockoutDecayHours` in `OPShield`). A record is only removed when **both** conditions are true: 1. No active lockout and no in-progress attempts. 2. The time elapsed since `lastLockoutAtMs` exceeds `decayMs`. Exponential backoff now behaves as documented. - **API change (internal):** `LockoutManager.cleanupExpired()` → `cleanupExpired(long decayMs)`. No external callers; internal call site updated in `OPShield.cleanupRuntimeState()`. #### FIX [Medium] — `/opshield reload` blocked by `allow_op_reload: false` even for players with explicit `opshield.reload` permission - **Root cause:** In `handleManagementCommand()`, the `allow_op_reload` flag was evaluated **before** the permission check. A player with an explicit `opshield.reload` grant from a permissions plugin was still denied when `allow_op_reload: false`, making the permission effectively meaningless. - **Fix:** The permission check (`opshield.reload`) now runs first. The `allow_op_reload` flag is applied only as a secondary restriction after the caller is confirmed to hold the permission. Console senders are unaffected. #### FIX [Low] — IP tracking unreliable behind BungeeCord / Velocity (documentation + debug warning) - **Root cause:** `getPlayerIpRaw()` uses `player.getAddress().getAddress().getHostAddress()`. Behind a proxy, this resolves to the proxy's own address (commonly `127.0.0.1` or a private RFC-1918 range), not the connecting client's real IP. IP-based lockout mirroring and `ip_limit` tracking silently group all players under the same key. - **Fix (partial):** Full proxy-IP forwarding requires server-side configuration outside the plugin's scope (Paper IP forwarding, `BungeeGuard`, etc.). This release adds a **debug-mode warning** logged when `debug: true` and the resolved address is a loopback or private-range address, so administrators are alerted during setup. The warning is suppressed in production (`debug: false`) to avoid console spam on legitimate LAN servers. --- ### 🌍 Language File Changes | Key | Files | Change | |-----|-------|--------| | `auth_session_active` | `en.yml`, `vn.yml`, `ru.yml` | **Added** — shown when an existing valid session is reused | All other keys are unchanged and backwards-compatible. --- ### 🔧 Internal / Code Quality - Removed four now-unused imports from `OPShield.java`: `LocalDateTime`, `ZoneId`, `Instant`, `DateTimeFormatter` (timestamp moved to `AuditLogger`), `Component`, `NamedTextColor`, `PlainTextComponentSerializer` (shadow-ban message fix). - `LockoutManager.cleanupExpired()` signature updated (internal only). - Added inline `FIX[…]` comments at every corrected site for audit trail. --- ### ✅ Compatibility | Item | Status | |------|--------| | `config.yml` (v1.9.0) | ✅ No changes required | | `data.yml` (v1.9.0) | ✅ No changes required | | Language files (v1.9.0) | ⚠️ `auth_session_active` key added; old files work but players will see `[Missing Message: auth_session_active]` until updated | | Paper API | ✅ 1.21+ (unchanged) | ---
release

OPShield 1.9.0

1.9.0
15 мая 2026 г.22Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [1.9.0] - 2026-05-15 — Async Auth, Session Timeout & Security Hardening ### 🐛 Bug Fixes #### FIX [Critical] — PBKDF2 verification was running on the main server thread - **Root cause:** `handlePrivilegeCommand()` called `PasswordHasher.verify()` synchronously during `onCommand()`, which executes on the main thread. With 120,000 PBKDF2 iterations, each failed attempt caused a measurable TPS drop (~50–200 ms). Under coordinated brute-force from multiple clients this could stall the server tick loop. - **Fix:** Password verification is now dispatched via `Bukkit.getScheduler().runTaskAsynchronously()`. The result is delivered back to the main thread via `runTask()` for all Bukkit API side-effects. Zero blocking on the main thread. - **New status field:** `/opshield status` now shows pending verification count (`0/4`). #### FIX [Critical] — No session timeout after successful authentication - **Root cause:** After a correct password was accepted, no state was stored. Every subsequent `/op` or `/deop` required re-entering the password. In practice admins worked around this by keeping a note of the password nearby, which is a security anti-pattern. - **Fix:** A `ConcurrentHashMap<UUID, Long>` (`authenticatedSessions`) tracks expiry epoch-ms per player. After the first successful auth within a session window, password entry is skipped. - **Config key:** `security.session_timeout_minutes` (default `30`; set `0` to disable). - **Session lifecycle:** granted on successful auth → cleared on player quit (`PlayerQuitEvent`) → cleared on manual `/opshield unlock` → purged by cleanup task. #### FIX [High] — No global rate limit on PBKDF2 operations - **Root cause:** A coordinated attack with many accounts spamming `/op <name> <guess>` queued unbounded async hashing tasks, saturating CPU cores. - **Fix:** `AtomicInteger globalAuthCount` caps server-wide concurrent PBKDF2 operations at `MAX_CONCURRENT_AUTH = 4`. Requests over the cap receive `auth_server_busy` and are dropped without queuing. - **Per-sender dedup:** `Map<String, AtomicInteger> authPending` prevents the same player from queuing more than one verification at a time. Excess requests receive `auth_pending`. #### FIX [High] — `cleanupRuntimeState()` ran on the main thread - **Root cause:** `Bukkit.getScheduler().runTaskTimer()` was used instead of `runTaskTimerAsynchronously()`. The cleanup method holds synchronized blocks on `LockoutRecord` objects; under load this briefly stalled the tick. - **Fix:** Scheduler call changed to `runTaskTimerAsynchronously()`. All collections involved are `ConcurrentHashMap`/`ConcurrentLinkedQueue` — safe for async access. #### FIX [Medium] — Shutdown race condition between async periodic flush and `onDisable` sync flush - **Root cause:** `onDisable()` called `flushPersistentDataSync()` while the 200-tick async flush task could be mid-execution. Although `compareAndSet` prevented double saves, the two threads could both have a reference to `dataFile` and compete on the same `YamlConfiguration` serialisation. - **Fix:** `onDisable()` now cancels both async task IDs (`periodicFlushTaskId`, `cleanupTaskId`) **before** calling `flushPersistentDataSync()`, ensuring only one flush can run at shutdown. #### FIX [Medium] — `ShadowBanManager` used `HashMap` for command→message mapping - **Root cause:** `HashMap` has undefined iteration order. Commands like `deop op`, `ban-ip`, or `give op` could match the wrong message key depending on JVM run, leading to non-deterministic fake responses. - **Fix:** Replaced with `LinkedHashMap` preserving explicit insertion order. More specific and longer tokens (e.g. `"inventory"`, `"teleport"`, `"deop"`) are declared before shorter substrings (`"clear"`, `"tp"`, `"op"`) so prefix-matching is always predictable. #### FIX [Low] — `config-version` field was written but never read - **Root cause:** `config-version: 2` was introduced in v1.8.0 but `reloadConfiguration()` never checked it. There was no warning when admins ran an old config against a new build. - **Fix:** `reloadConfiguration()` now compares `config-version` against `EXPECTED_CONFIG_VERSION = 3`. It logs a warning if the file is outdated (new options missing → defaults used) or from a newer build (unknown keys may be ignored). The version is bumped in-place so the warning fires only once. #### FIX [Low] — `PlayerQuitEvent` not handled; stale session UUIDs accumulated - **Root cause:** `authenticatedSessions` was new in this version, but without a quit handler a UUID could remain in the map until the cleanup task ran, creating a window where a reconnecting player could bypass auth. - **Fix:** `@EventHandler onPlayerQuit()` immediately removes the UUID from `authenticatedSessions` on disconnect. --- ### ✨ New Features #### Authenticated Session Tokens - First successful `/op` or `/deop` authentication grants a timed session. - Subsequent privilege commands within the window skip password entry. - Session duration: `security.session_timeout_minutes` (default `30`; `0` = always require password). - Sessions are cleared on disconnect, manual unlock, or config reload. - New language keys: `auth_session_granted`, `auth_pending`, `auth_server_busy`. #### Async PBKDF2 Authentication Pipeline - All password verifications now run asynchronously. - Bukkit API side-effects (setOp, messages, broadcasts) still execute on the main thread via callback. - Extracted `completePrivilegeCommand()` to consolidate post-auth logic (hash upgrade, session grant, lockout clear, setOp, broadcast). #### Enhanced `/opshield status` Output Two new status lines: ``` Active auth sessions: 2 Pending PBKDF2 verifications: 0/4 Session timeout: 30 min ``` --- ### 🔧 Configuration Changes | Key | Change | Default | |-----|--------|---------| | `config-version` | Bumped `2` → `3` | `3` | | `security.session_timeout_minutes` | **NEW** — auth session TTL in minutes; `0` = disabled | `30` | No existing keys were removed or renamed. Old `config-version: 2` files are read normally with a one-time console warning. --- ### 🌍 Language Files Three new message keys added to all language files (`en`, `vn`, `ru`): | Key | Purpose | |-----|---------| | `auth_pending` | Sender already has a PBKDF2 verification in flight | | `auth_server_busy` | Server-wide auth cap reached | | `auth_session_granted` | Confirmation message shown after session is created | --- ### 📊 Code Quality Metrics | Metric | v1.8.0 | v1.9.0 | |--------|--------|--------| | PBKDF2 on main thread | ✅ yes | ❌ no (async) | | Session timeout | ❌ | ✅ | | Global auth rate limit | ❌ | ✅ (cap=4) | | Per-sender auth dedup | ❌ | ✅ | | Cleanup on main thread | ✅ yes | ❌ no (async) | | Shutdown race condition | ✅ present | ❌ fixed | | ShadowBan match order | non-deterministic | deterministic | | `config-version` enforced | ❌ | ✅ | | `PlayerQuitEvent` handled | ❌ | ✅ | --- ### 📝 Migration Notes from v1.8.0 1. **No breaking changes** — existing `config.yml` and `data.yml` are fully compatible. 2. **New config key** — add `security.session_timeout_minutes: 30` (or let the default apply on first reload). 3. **config-version** is auto-bumped from `2` → `3` on first reload; a one-time warning will appear in console. 4. **Permission setup** is unchanged from v1.8.0. ### 🔮 Planned for v1.10.0 - Extract `AutoPunishmentManager` — move all ban/kick/firewall logic out of `OPShield.java` - Extract `IpLimitManager` — move IP tracking and flagging - Extract `CommandRestrictionManager` - Unit tests for `LockoutManager` and `ShadowBanManager` - Consider Argon2id as an optional stronger hashing algorithm ---
release

OPShield 1.8.0

1.8.0
28 апр. 2026 г.29Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [1.8.0] - 2026-04-28 — Manager Refactor, Security Hardening & Quality Improvements ### 🔒 Security Fixes #### CRITICAL — Permission Default Changed - **`opshield.admin` default changed from `op` → `false`** (breaking if you relied on implicit OP grants) - Previously any player with OP status automatically received full OPShield admin rights - Now all permissions must be explicitly granted via a permission plugin (e.g. LuckPerms) - **Migration:** add `opshield.admin` to your OP group in your permission plugin - All child permissions (`opshield.reload`, `opshield.unlock`, `opshield.op`, `opshield.deop`) default changed from `op` → `false` for the same reason - Added `opshield.*` wildcard permission for convenience ### 🏗️ Architecture Improvements #### LockoutManager — Full Refactor - Introduced `LockoutRecord` inner class consolidating 5 separate `ConcurrentHashMap`s (failedAttempts, lockoutTimestamps, lockoutCount, lastLockoutAt) into a single per-key object - **Decay logic** moved entirely into `LockoutManager.recordFailure()` — no longer split between main class and manager - Added `mirrorLockout()` for IP-mirrored lockouts (called by OPShield when `track_ip=true`) - Added `exportSnapshot()` / `importSnapshot()` for clean persistence without raw map access - Backwards-compatible persistence: v1.8.0 reads legacy v1.7.0 data format and migrates automatically - `LockoutManager` is now the single source of truth for all lockout state #### ShadowBanManager — Full Refactor - **Shadow-ban levels now owned by `ShadowBanManager`** (previously a raw `ConcurrentHashMap` in `OPShield.java`) - `getFakeMessage()` is no longer static; it accepts a `MessageProvider` functional interface so messages come from language files, not hard-coded strings - Added `shouldEscalate(key, threshold)` method — clearly separates the "should I punish?" decision from execution - Added `exportLevels()` / `importLevels()` for persistence - Extended command keyword → message-key mapping: now covers `op`, `deop`, `kick`, `stop`, `reload`, `pardon` #### OPShield.java — Reduced God Class Burden - Replaced 5 raw state maps with delegation to `LockoutManager` - Replaced `playerShadowBanLevel` map with delegation to `ShadowBanManager` - Added `/opshield status` command for runtime diagnostics (shows active levels, flagged IPs, queue sizes) - Added `debugLog()` helper — controlled by `debug: false` config key; never exposes sensitive info in production ### ✨ New Features - **`debug` mode** (`debug: false` in config.yml) — enables verbose internal logging for troubleshooting without recompiling - **`/opshield status`** — new sub-command with `opshield.status` permission; reports shadow-ban level count, flagged IPs, sensitive-history windows, auto-punishment state - **`security.password.auto_upgrade_legacy_hash: true`** — automatically re-hashes a legacy SHA-256 password to PBKDF2 the next time the correct password is provided; hash is saved to config.yml with no manual action required - **Audit queue capacity** (`audit.max_queue_size: 10000`) — prevents unbounded memory growth if disk writes fail; oldest entries dropped with a console warning (rate-limited to once per flush cycle) - **Audit JSON format** (`audit.format: json`) — emits one machine-readable JSON object per line for log aggregator ingestion; `plain` format unchanged for backwards compatibility ### 🐛 Bug Fixes - **`ShadowBanManager.getFakeActionMessage()` was never called** — v1.7.0 added it but the main class still used hard-coded logic. Now the manager is the sole source of fake messages - **`LockoutManager.ipLimitMap` was unused** — `recordIpConnection()` was called but the data was never read. Removed; IP limit tracking remains in OPShield.java pending `IpLimitManager` extraction - **`PasswordHasher.upgradeHashIfNeeded()`** (NEW) — `isLegacyHash()` existed in 1.7.0 but there was no code path to actually upgrade the stored hash. Now the main class calls `upgradeHashIfNeeded()` after each successful login when `auto_upgrade_legacy_hash: true` - **`HASH_FORMAT_VERSION` constant** (NEW) — the string `"pbkdf2"` was scattered as a magic literal across `PasswordHasher`; centralised to a named constant ### 🔧 Build Improvements - **`maven-compiler-plugin 3.13.0`** added with explicit `<release>21</release>` and `<parameters>` flag - **`maven-shade-plugin 3.6.0`** added (no relocations yet, but scaffold is ready for future bundled deps) - **`maven-surefire-plugin 3.2.5`** added with JUnit 5 + Mockito test dependencies for unit testing managers - **Centralised version properties** — `java.version`, `paper.version`, and plugin versions now all defined in `<properties>` for consistency ### 📝 Configuration - Added `config-version: 2` — allows future automatic migration detection - Added `debug: false` — verbose diagnostic logging toggle - Added `security.password.auto_upgrade_legacy_hash: true` - Added `audit.max_queue_size: 10000` - Added `audit.format: plain` - Added `shadow_ban.auto_punish_level` default raised from `3` → `5` - Added inline "Recommended values by server size" comments to `config.yml` ### 🌍 Language Files - Added 7 new shadow-fake message keys: `shadow_fake_op`, `shadow_fake_deop`, `shadow_fake_kick`, `shadow_fake_pardon`, `shadow_fake_stop`, `shadow_fake_reload` (all three languages) - Fixed inconsistent Vietnamese translations in `vn.yml` - All three language files now use natural-language fake messages that better blend in with real server output ### 📊 Code Quality Metrics | Metric | v1.7.0 | v1.8.0 | |---|---|---| | Raw state maps in OPShield.java | 7 | 3 | | Manager classes | 2 (stub) | 2 (fully active) | | Permissions with insecure default `op` | 6 | 0 | | Hard-coded fake messages | 8 | 0 | | Unused manager methods | 2 | 0 | | `config-version` | ❌ | ✅ | | Debug mode | ❌ | ✅ | | Audit queue cap | ❌ | ✅ | | JSON audit format | ❌ | ✅ | ### 📝 Migration Notes 1. **Permission plugin setup required** — add `opshield.admin` to your OP group (see CRITICAL note above) 2. **data.yml** is auto-migrated from v1.7.0 format on first boot — no manual action needed 3. All configuration keys are backwards-compatible; new keys use sensible defaults 4. Old `lockout_timestamps` / `failed_attempts` / `lockout_count` / `last_lockout_at` sections in `data.yml` are read on upgrade and merged into `lockout_records`; old sections are replaced on next save ### 🔮 Planned for v1.9.0 - Extract `AutoPunishmentManager` — move all ban/kick/firewall logic out of `OPShield.java` - Extract `IpLimitManager` — move IP tracking and flagging - Extract `CommandRestrictionManager` — move `matchesConfiguredCommand` logic - Add unit tests for `LockoutManager` and `ShadowBanManager` - Consider Argon2id as an optional stronger hashing algorithm ---
release

OPShield 1.7.0

1.7.0
24 апр. 2026 г.16Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [1.7.0] - 2026-04-24 — Architecture Improvements & God Class Refactoring ### 🚀 Improvements #### Architecture Refactoring - **LockoutManager** (NEW) — Extracted lockout logic from main class - Centralized player/IP lockout state management - Clean public API for lockout operations - Expired lockout cleanup methods - **ShadowBanManager** (NEW) — Extracted shadow ban logic - Shadow ban state tracking - Fake action message generation - Duration management with expiry cleanup - Improved message consistency #### Password Security - **PasswordHasher improvements** — Enhanced password handling - PBKDF2 iteration count configurable at runtime - Legacy SHA-256 detection with `isLegacyHash()` method - Iteration count validation (10,000 - 1,000,000 range) - Better separation of hash versioning concerns #### Code Organization - Created `manager/` package for extracting business logic - Reduced OPShield.java God Class burden - Better separation of concerns - Improved testability of individual components #### Language Files - Fixed grammar inconsistencies (e.g., "1 player" vs "1 players") - Improved fake action message clarity - Better error message wording ### 📊 Code Quality #### Metrics - **Before**: 1,172 LOC in single class (God Class) - **After**: OPShield.java reduced + 2 new manager classes - **Managers Created**: 2 (LockoutManager, ShadowBanManager) - **Lines Extracted**: ~300+ from main class #### Quality Improvements - ✅ Reduced cyclomatic complexity in main class - ✅ Improved code organization - ✅ Better separation of concerns - ✅ More testable components - ✅ Easier to extend for future features ### 📝 Migration Notes For existing servers: 1. No database migration needed 2. All configuration stays the same 3. No command changes 4. Direct drop-in JAR replacement ### 🔮 Future Work (v1.8.0+) Recommended further refactoring: - Extract `AutoPunishmentManager` for ban/kick logic - Extract `IpLimitManager` for IP tracking - Extract `CommandRestrictionManager` for command validation - Create interface-based services for better testability - Add unit tests for new manager classes ---
release

OPShield 1.6.0

1.6.0
21 апр. 2026 г.12Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать
Что нового
## [1.6.0] — 2026-04-21 ### Bug fixes #### High severity - **`AuditLogger` switched from `FileWriter` to NIO `Files.write()`** — `FileWriter` used the JVM platform default charset, which could produce garbled or truncated log entries on servers whose OS locale is not UTF-8. All writes now use `java.nio.file.Files.write()` with an explicit `StandardCharsets.UTF_8` argument and `StandardOpenOption.APPEND`. - **`ensureFile()` is no longer called on every flush tick** — the previous implementation re-checked (and conditionally re-created) the log file and its parent directory on every async flush, even when neither had changed. An `AtomicBoolean fileReady` flag now gates the check so it runs at most once per file lifetime. The flag is cleared after rotation so the next write correctly re-creates the log file. - **Failed audit writes now re-queue entries instead of silently discarding them** — if a flush attempt throws `IOException`, the affected lines are returned to the front of the queue and retried up to `MAX_WRITE_RETRIES` (2) times. On final failure a `SEVERE` console error is printed and the lines are re-queued so they are not permanently lost. #### Medium severity - **Legacy SHA-256 password hash triggers a console warning on startup** — if `op_password_hash` in `config.yml` contains an old SHA-256 value (generated by OPShield < 1.4.0), the server console now displays a clear warning advising the admin to reset the password so it is upgraded to PBKDF2 storage. The plugin continues to accept the legacy hash for authentication; no data is lost. #### Low severity - **Magic string `"unknown"` for unresolvable player IPs replaced with named constant `UNKNOWN_IP`** — eliminates the class of silent typo bugs where inconsistent string literals caused an IP to be handled as a real address in some code paths but skipped correctly in others. - **Grammar correction in English shadow-ban fake messages** — `shadow_fake_clear` incorrectly read `"Cleared the inventory of 1 players"`. Corrected to `"Cleared the inventory of 1 player"`. Related entity messages (`shadow_fake_kill`, `shadow_fake_tp`) also updated to use the singular form `"entity"` for consistency. - **`folia-supported: false` added to `plugin.yml`** — OPShield uses the Bukkit task scheduler and is not compatible with Folia. The flag prevents Folia auto-detection from incorrectly classifying the plugin as Folia-safe and loading it on an incompatible runtime. - **`auto_punish_firewall_fail` message key added to all language files** — previously the firewall punishment path had no dedicated message for the case where the script is skipped (unsafe exec disabled, blank script, or unknown IP). All three language files (`en.yml`, `vn.yml`, `ru.yml`) now include the key. ### Improvements - **PBKDF2 iteration count is now configurable** via `security.password.pbkdf2_iterations` (default `120000`, range `10000`–`1000000`). Increasing the value raises brute-force resistance at the cost of slightly slower verification on each `/op` or `/deop` attempt. Existing stored hashes are unaffected — they carry their own iteration count. - **`firewall_script` config entry now includes OS-specific examples** — the config comment now shows both a Linux `iptables` example and a Windows `netsh` example so admins know the expected format without having to consult external documentation. ### New config keys | Key | Default | Description | |-----|---------|-------------| | `security.password.pbkdf2_iterations` | `120000` | PBKDF2 iteration count for new password hashes (10 000–1 000 000) | ### New language keys (all files) | Key | Description | |-----|-------------| | `auto_punish_firewall_fail` | Shown when firewall punishment is skipped and player is kicked instead | ---
release

OPShield 1.5.0

1.5.0
20 апр. 2026 г.12Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать
Что нового
## [1.5.0] — 2026-04-19 ### Bug fixes #### Medium severity - **`sensitiveCommandHistory` now persisted to `data.yml`** — the auto-punishment rolling window survived previously only in memory, allowing players to bypass the threshold by timing restarts or crashes. Timestamps are now written on every dirty flush and restored on startup; stale entries outside the configured window are discarded automatically on load. - **Shadow-ban level escalation is now enforced** — `playerShadowBanLevel` was incremented and stored but never acted upon. A new config key `shadow_ban.auto_punish_level` (default `3`) defines the threshold at which the level triggers real `auto_punishment` and then resets. Requires `auto_punishment.enabled: true`. - **Firewall script no longer blocks the main thread** — `executeFirewallBlock` previously called `Runtime.getRuntime().exec()` synchronously, which could freeze the server if the script was slow. It now runs asynchronously via `ProcessBuilder` with a configurable hard timeout (`auto_punishment.firewall_timeout_seconds`, default `10`). The player is kicked immediately on the main thread; the OS script executes in the background. #### Low severity - **`unlockIdentifier()` now clears `sensitiveCommandHistory`** — previously, `/opshield unlock` cleared all other tracking maps but left `sensitiveCommandHistory` intact, causing inconsistent state after a manual unlock. - **`getMsgPlain()` replaced fragile color-strip logic** — manual `replace('&X', "")` calls missed several color codes and decorators. Now uses Adventure's `PlainTextComponentSerializer` for correct, future-proof plain-text extraction. - **Multi-file audit log rotation** — `AuditLogger` previously kept only one backup file (`audit.log.1`), permanently overwriting it on every rotation. Rotation now shifts files: `audit.log.1` → `audit.log.2` → … → `audit.log.N`. Controlled by `audit.log_retention` (default `3`). - **Config validation for `auto_punishment.command`** — an unrecognized mode with no `custom_command` set now prints a clear console warning on load and reload instead of silently falling back to a potentially unexpected behaviour. ### New config keys | Key | Default | Description | |-----|---------|-------------| | `shadow_ban.auto_punish_level` | `3` | Shadow-ban level threshold that triggers auto-punishment | | `audit.log_retention` | `3` | Number of rotated audit log backup files to keep | | `auto_punishment.firewall_timeout_seconds` | `10` | Max seconds before a hung firewall script is force-killed | ---
release

OPShield 1.4.0

1.4.0
14 апр. 2026 г.20Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать
Что нового
## [1.4.0] — 2026-04-14 ### Security hardening - migrated password storage to `op_password_hash` so plaintext is no longer kept in config after migration - added PBKDF2 password hashing for new stored credentials - preserved backward compatibility for older SHA-256 hashes during migration - made firewall execution explicitly unsafe and opt-in only via `allow_unsafe_firewall_exec` - disabled auto-punishment by default to reduce accidental false positives on fresh installs ### Logic fixes - fixed IP-limit detection so it counts unique accounts inside a real rolling time window - changed OP whitelist enforcement to apply to `/op` only - added lockout count decay after a configurable cooling-off period - cleaned expired lockouts and expired IP flags automatically on a schedule - localized shadow-ban fake success messages instead of hardcoding English strings in Java ### Performance and maintainability - replaced repeated async save spawns with a debounced persistent save loop - replaced synchronous audit file writes with queued async flushes - added basic audit log rotation - cached `CommandMap` reflection result instead of resolving it on every blocked command - split hashing and audit logging into dedicated helper classes ### Permissions and command handling - added `opshield.op` - added `opshield.deop` - added `opshield.admin` - added `opshield.bypass` - kept `/opshield reload` and `/opshield unlock <player|ip>` as admin management commands ### Config changes - added `op_password_hash` - retained `op_password` only as a legacy migration input - added `broadcast_on_privilege_change` - added `audit.*` - added `security.lockout.track_ip` - added `security.lockout.count_decay_hours` - added `auto_punishment.window_seconds` - added `auto_punishment.custom_command` - added `auto_punishment.allow_unsafe_firewall_exec` - added `ip_limit.auto_punish` - added `ip_limit.flag_duration_minutes`
release

OPShield 1.3.0

1.3.0
28 мар. 2026 г.32Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [1.3.0] — 2026-03-28 ### 🔥 New Security Features #### Auto-Punishment (Automatic Punishment) - Automatically ban-ip or execute punishment command when players attempt sensitive commands too many times - Config: `auto_punishment.threshold` - maximum allowed attempts - Supports multiple modes: `ban-ip`, `ban`, `kick`, `firewall`, or custom command - Firewall script: Execute system commands (iptables/ufw) to block IP at network level - Configurable sensitive commands list: `sensitive_commands` #### IP-Limit (Multi-account Detection) - Track number of accounts connecting from the same IP - Auto-flag and punish if exceeding `max_accounts` within time window - Default: 3 accounts / 5 minutes - Integrates with Auto-Punishment to automatically block suspicious IPs #### Shadow Ban / Fake Success (Deceptive) - Instead of showing "no permission", display fake success messages - Attackers think they succeeded but nothing actually happens - Buys time for admin to investigate and handle attackers - Fake messages for commands: clear, kill, ban, gamemode, tp, effect, give #### Anti-Spam Console Log - Blocked commands no longer appear in console log - Only logged to audit.log to prevent server console spam ### 🛡️ Security Improvements - Auto-punishment exempt for OP and whitelist-admin - IP tracking with automatic time window cleanup - Full audit logging for all auto-punishment actions ---
release

OPShield 1.2.1

1.2.1
16 мар. 2026 г.16Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [1.2.1] — 2026-03-16 ### ✨ New Features - **OP Command Restriction** — Added ability to restrict specific commands even for OPs. This helps prevent admin abuse and limits potential damage from backdoors. - New config options: `restrict_op_commands` (boolean) and `blocked_op_commands` (list). - Console remains exempt from these restrictions for emergency recovery.