
OPShield
Protects your server from OP/admin abuse with console-only OP (via password) and optional admin command restriction.
🛡️ OPShield
Advanced security & command protection plugin for Paper 1.21+
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
/opand/deop— no password, no privilege changes - 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
/opto 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:opor 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_levelis reached - Set
auto_punish_level: 99to 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
ProcessBuilderwith 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"inconfig.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_sizeprevents OOM if disk writes fail for extended periods - Dual output format —
audit.format: plain(default human-readable) oraudit.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_mbandaudit.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
| Component | Version |
|---|---|
| Java | 21+ |
| Paper | 1.21+ |
| Folia | ❌ Not supported |
🚀 Installation
- Download the plugin
.jar - Drop it into your server's
plugins/folder - Start the server — OPShield will generate a random password and print it once in the console
- Save the password somewhere safe (it is only shown once)
- Grant permissions — add
opshield.adminto 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) - Open
plugins/OPShield/config.ymlto customise behaviour - Run
/opshield reloadin-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.
⌨️ Commands
| 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 levels, flagged IPs, etc.) |
🔐 Permissions
⚠️ 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 |
Example LuckPerms setup
/lp group admin permission set opshield.admin true
🔧 Configuration
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
⚙️ 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
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
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: trueinconfig.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 reachauto_punish_level- Run
/opshield reloadafter any config change - Run
/opshield statusto see current shadow-ban levels and punish state
Audit log is empty or not updating
- Check
audit.console_output: trueto confirm logging is active - Check write permissions on the
plugins/OPShield/folder - If
audit.max_queue_sizeis reached, aSEVEREwarning 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 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.
- Password-gated
/opand/deop— no password, no privilege changes - 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
- Console warning if a legacy SHA-256 hash is detected on startup
- OP whitelist — restrict
/opto 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:opor 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_levelis reached - Set
auto_punish_level: 99to keep decoy behaviour without escalation
⚠️ 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
ProcessBuilderwith 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"inconfig.yml
📊 Audit & Logging
- Every privilege change, password failure, command block, and punishment is logged
- Async queue — log writes never touch the main thread
- UTF-8 safe — uses NIO
Files.write()with explicit charset (fixed in 1.6.0) - Retry on failure — failed writes are re-queued instead of silently discarded (fixed in 1.6.0)
- Configurable rotation:
audit.max_file_size_mbandaudit.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
| Component | Version |
|---|---|
| Java | 21+ |
| Paper | 1.21+ |
| Folia | ❌ Not supported |
🚀 Installation
- Download the plugin
.jar - Drop it into your server's
plugins/folder - Start the server — OPShield will generate a random password and print it once in the console
- Save the password somewhere safe (it is only shown once)
- Open
plugins/OPShield/config.ymlto customise behaviour - Run
/opshield reloadin-game or restart to apply changes ✅
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.
⌨️ Commands
| 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 |
🔐 Permissions
| Permission | Default | Description |
|---|---|---|
opshield.admin | op | Grants all child permissions |
opshield.reload | op | Reload OPShield configuration |
opshield.unlock | op | Unlock a tracked player or IP |
opshield.op | op | Use password-protected /op |
opshield.deop | op | Use password-protected /deop |
opshield.bypass | false | Bypass non-OP restricted command blocking |
🔧 Configuration
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
⚙️ Example Config
# Password security
security:
lockout:
enabled: true
max-attempts: 3
duration-minutes: 3
track_ip: true
count_decay_hours: 168
password:
pbkdf2_iterations: 120000 # range: 10000 – 1000000
# 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: 3
# Audit log
audit:
console_output: true
max_file_size_mb: 5
log_retention: 3
📐 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
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: 3
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: 2
🔥 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
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.
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: trueinconfig.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 instead- Run
/opshield reloadafter any config change
Audit log is empty or not updating
- Check
audit.console_output: trueto confirm logging is active - Check write permissions on the
plugins/OPShield/folder - In 1.6.0, failed writes are retried and logged to console as
SEVERE— check console output
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.
📄 Credits
Author: Duong2012G
License: Apache 2.0
Website: https://modrinth.com/user/Duong2012G
Built for secure, professional Minecraft servers.
