Protects your server from OP/admin abuse with console-only OP (via password) and optional admin command restriction.
Advanced security & command protection plugin for Paper 1.21+
Protect your server from abuse — secure OP access, track every action, stop attackers instantly.
/op and /deop — no password, no privilege changessecurity.session_timeout_minutes, default 30 min)security.password.pbkdf2_iterations)security.password.auto_upgrade_legacy_hash)/op to a predefined set of player namesblocked_commands)blocked_op_commands)blocked_command_prefixes)minecraft:op or plugin aliases are caughtopshield.bypass) for trusted staffsecurity.lockout.max-attempts)security.lockout.track_ip)security.lockout.count_decay_hours)/opshield unlock <player|ip>shadow_ban.auto_punish_level is reachedauto_punish_level: 99 to keep decoy behaviour without escalationkick, ban, ban-ip, firewall, customProcessBuilder with configurable timeout{player} and {ip} placeholdersen), Vietnamese (vn), Russian (ru)language: "en" in config.ymlaudit.max_queue_size prevents OOM if disk writes fail for extended periodsaudit.format: plain (default human-readable) or audit.format: json (machine-readable, one JSON object per line)Files.write() with explicit charsetaudit.max_file_size_mb and audit.log_retention (up to N backup files)audit.console_output: trueplugins/OPShield/audit.log, audit.log.1 … audit.log.N| Component | Version |
|---|---|
| Java | 21+ |
| Paper | 1.21+ |
| Folia | ❌ Not supported |
.jarplugins/ folderopshield.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)plugins/OPShield/config.yml to customise behaviour/opshield reload in-game or restart to apply changes ✅Upgrading from 1.7.0?
data.ymlis 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_passwordplaintext value from an older version,
OPShield will automatically migrate it toop_password_hashand remove the plaintext entry.
| Command | Description |
|---|---|
/op <player> [password] |
Grant OP with password verification |
/deop <player> [password] |
Remove OP with password verification |
/opshield reload |
Reload configuration |
/opshield unlock <player|ip> |
Clear all tracking state for a player or IP |
/opshield status |
Show runtime statistics (active sessions, auth queue, flagged IPs, etc.) |
⚠️ Changed in v1.8.0: All permissions now default to
false. You must grant them explicitly via a permission plugin.
| Permission | Default | Description |
|---|---|---|
opshield.* |
false | Wildcard — grants all permissions |
opshield.admin |
false | Grants all child permissions |
opshield.reload |
false | Reload OPShield configuration |
opshield.unlock |
false | Unlock a tracked player or IP |
opshield.status |
false | View runtime statistics |
opshield.op |
false | Use password-protected /op |
opshield.deop |
false | Use password-protected /deop |
opshield.bypass |
false | Bypass non-OP restricted command blocking |
/lp group admin permission set opshield.admin true
Files generated under plugins/OPShield/:
config.yml — main configurationdata.yml — persistent runtime state (lockouts, shadow-ban levels, IP windows)languages/en.yml — English messageslanguages/vn.yml — Vietnamese messageslanguages/ru.yml — Russian messages# 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
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 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.
OPShield grants a session after each successful authentication. Check:
security.session_timeout_minutes in config.yml — if set to 0, sessions are disabled/opshield status to see how many active sessions existThe 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.
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.ymlauto_punishment.sensitive_commandsshadow_ban.enabled — if true, the player may be getting fake success messages first; level must reach auto_punish_level/opshield reload after any config change/opshield status to see current shadow-ban levels and punish stateaudit.console_output: true to confirm logging is activeplugins/OPShield/ folderaudit.max_queue_size is reached, a SEVERE warning appears in console — check disk spaceRun in-game or console:
/opshield reload
/op?No — it intercepts and wraps it. The original /op behaviour is preserved but gated behind a password.
Yes — passwords are hashed using PBKDF2-HMAC-SHA256 with a random salt and 120,000 iterations (configurable). The plaintext is never written to disk.
Paper 1.21+ only. Spigot may work but is not tested. Folia is explicitly not supported (folia-supported: false).
Yes — set auto_punishment.enabled: false. Shadow-ban fake messages will still work independently.
Lockout state is persisted to data.yml and restored on startup. Players cannot bypass lockouts by crashing or restarting the server.
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.
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.lockoutDecayHours, autoPunishmentWindowSeconds, ipLimitTimeWindowSeconds, and sessionTimeoutMs are now volatile so async tasks always see up-to-date values after /opshield reload.AuditLogger fields — consoleOutput, maxBytes, maxBackups, maxQueueSize, and format are now volatile.startsWith() checks with InetAddress predicates, adding previously missing link-local range detection.INFO instead of WARNING.See the Changelog for details.
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.
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.
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.
Author: Duong2012G
License: Apache 2.0
Website: https://modrinth.com/user/Duong2012G
Built for secure, professional Minecraft servers.