Skip to content
Iaroslav Iershov edited this page Jan 20, 2018 · 28 revisions

Table of Contents

  1. Description
  2. Examples of using
  3. Conditions
  4. Installation and Configuration
  5. Backend
  6. Memory
  7. Filesystem
  8. SQL
  9. Channels
  10. API HTTP Communication
  11. Full configuration examples
  12. Security
  13. Whitelist IPs
  14. Sign Request

Description

MEI is a Bukkit plugin provides ability to send commands for execution from different external sources. This allows to integrate MC server management into existing solutions. So you could execute Minecraft command from other application, such as web sites or standalone applications. MEI does not bind to specific language or platform. It uses simple HTTP protocol for communication, so you may use your favorite tool.

Examples of using

Lest assume you have a Minecraft server and forum for community. And you want to emit some command in terminal without direct server invocation. To achieve this you simple make a simple HTTP request and tell MEI what to do

POST http://minecraft.host:9090/api
{
    "command": "ban hacker"
}

This will execute following command into Minecraft server console

ban hacker

You may also supply command request with conditions

POST http://minecraft.host:9090/api
{
  "command": "tp Simon 0, 0, 0",
  "conditions": [
  {
    "condition": "user_online",
    "value": "Simon"
  }
 ]
}

With specified request MEI teleports player with name Simon to zero position immediately when Simon loggins to server.

It is possible to specify several command conditions

POST http://minecraft.host:9090/api
{
  "command": "tp Simon 0, 0, 0",
  "conditions": [
  {
    "condition": "user_online",
    "value": "Simon"
  },
  {
    "condition": "user_count",
    "value": "2"
  }
 ]
}

This will teleport Simon to zero position in case if he is online and there are 2 players online in total. Refer to Conditions section for more details.

There is no any limitation what commands to execute. If you are able to emit some command with server console you are also may entrust this job to MEI. Even if command is provided by external plugin.

Conditions

Allows to configure clauses when command should be executed. It allows to trigger events automatically and it is up to server administrator how to combine commands and conditions.

To apply condition to command conditions attribute should be added to reuest

POST http://minecraft.host:9090/api
{
  "command": "tp Simon 0, 0, 0",
  "conditions": [
  {
    // Condition key
    "condition": "user_online",
    // Condition value
    "value": "Simon"
  }
 ]
}

It is possible to supply several conditions with the same key, but with different values. If two or more conditions are fully equals, they will be invoked as one.

Condition key Value type Meaning
game_time positive integer Game time in ticks, equals in game /time command. Event will occur on specific game daytime
server_time positive integer Hosts time in UNIX timestamp. Occur if current time grater than specified value
user_count positive integer Matches on exact number of players online
user_online string Matches on mentioned player name

It is planned that list of available conditions will be expanded in future releases.

Installation and configuration

Copy latest version of MEI plugin to your server plugins directory. On next launch of your server, MEI will generate default config file at plugins/MEI/config.yml. To change MEI behaviour just update config file and restart your server.

Backend

Backend is a special storage where MEI keep commands data for future execution. It occurs when command is supplied with condition that doesn't meet.

To configure backend refer to backend section in configuration. There are 3 options available here.

Memory

Keeps commands in JVM memory. It does not require special configuration. But use it with caution - all stored tasks will be lost on server restart

# Memory backend configuration example
backend: "Memory"

Filesystem

Keeps command in specified folder on the server. Tasks will be restored even after server restart and usually suitable for small projects. This backend requires additional config section with directory option

# Filessytem based backend configuration example
backend: "Filesystem"
Filesystem:
  # Absolute path to directory where to store tasks. Make sure rights a correct
  directory: "/opt/tasks"

MEI will try to create specified directory, if case of absence.

Sql

Keeps commands details into SQL server. It is suitable for big projects with centralized SQL server. This backend requires separate config section

backend: "Sql"
Sql:
  # SQL connection url
  connectionString: "jdbc:mysql://host:3306/database?useSSL=false"
  # MySQL credentials
  username: "login"
  password: "password"
  # MySQL tables names
  tasks_table: "mei_tasks"
  conditions_table: "mei_conditions"

On next start MEI will create all required tables in accordance to provided configuration. If you have some empty option, just quote it with ""

username: "login"
password: ""

Channels

Channel define a method of communication with MEI. It is possible to enable several channels as well as disable communication at all. In case of channels disabling you still able to communicate with MEI internally via backend. For example, put a task files into Filesystem backend directory.   To configure channels update Channels option in plugin configuration file

# It is possible to provide several channels
Channels:
 - API

# Or disable communication at all
Channels []

For now only API channel option is available. MEI will bring up integrated web server to listen for HTTP requests. Additional options should be supplied to make it work

# IP address to listen to
# If you have several network interfaces specify which one to use for communication. 0.0.0.0 will listen all available interfaces
ipAddress: "0.0.0.0"
# Port where to bring up a channel facility
# Don not forget to open configured port
port: "9090"

# Channel security settings. Described detailed in Security section
checkSigh: false
# Token used to generate request sign.
# CAUTION: Redefine default value with strong random value
securityToken: "a78cde7a-5c15-46f8-9178-74fb5d8631c2"
# A list of comma separated IPs allowed to send requests.
# If empty all IPs are allowed
whitelistIps: []

# Api channel base path
# Defines endpoint for API channel
apiPath: "/api"

With specified configuration interface will be available at http://your-host:9090/api

API HTTP Communication

API channel uses JSON data, so Content-Type header with application/json value should be supplied by client for all requests to MEI.

POST http://host:9090/mei

Headers:
Content-Type: application/json

{
    "command": "kill Kenny"
}

MEI returns different HTTP codes based on command execution results

HTTP Code Meaning
200 Command is executed. If conditions are provided all conditions are meet
201 Command is not executed because of at least one condition does not meet. Task is saved for future execution
402 Command is not executed and will be removed. At least one condition is unknown or wrong
403 Request rejected because of security barrier. Client IP is not allowed or request signed wrong
500 Undefined error occured

Full configuration examples

All channels are disabled, /opt/mei directory as a Filesystem backend, postponed tasks polling interval 3- seconds

# Backend setup
backend: "Filesystem"
Filesystem:
  directory: "/opt/mei"

# Disabled all communication channels
Channels: []

# Postponed execution setup
interval: 30000

API channel enabled and available at http://host:10000/mei, only requests from 127.0.0.1 is allowed, Memory backend as a backend

# Backend
backend: "Memory"

# Channels setup
Channels:
 - "API"

# Network config 
ipAddress: "0.0.0.0"
port: "10000"
apiPath: "/mei"

# Security
checkSigh: false
securityToken: "a78cde7a-5c15-46f8-9178-74fb5d8631c2"
whitelistIps: ["127.0.0.1"]

# Other settings
interval: 60000

MySQL database backend, API channel with request sign check security

# Backend setup
backend: "Sql"
Sql:
  connectionString: "jdbc:mysql://sql-host:3306/database?useSSL=false"
  username: "login"
  password: "password"
  tasks_table: "mei_tasks"
  conditions_table: "mei_conditions"

# Channels setup
Channels:
 - "API"

# Security settings
checkSigh: true
securityToken: "a78cde7a-5c15-46f8-9178-74fb5d8631c2"
whitelistIps: []

Security

When channel is enabled it became exposes Minecraft server to entire network. This means that anyone may emit a command to MEI for execution. To prevent this situation MEI provides two methods of securing channels. Methods may be configured and enabled independently.

CAUTION: By default all security mechanisms are disabled. Do not forget to review configuration before go to production.

Whitelist IPs

Soft method of securing you MEI communication. With this approach only IPs specified in config may push a requests for execution. To specify whitelist IPs update mentioned option

# A list of comma separated IPs allowed to send requests
whitelistIps: ["127.0.0.1", "192.168.1.2"]

# If empty all IPs are allowed
whitelistIps: []

Sign Request

Recommended method of securing a connection with plugin. With this approach each request should be signed with special hash. To make this work update your configuration

# Force MEI to check sign
checkSigh: true
# Token used to generate request sign
# CAUTION: Redefine default value with strong random value
securityToken: "a78cde7a-5c15-46f8-9178-74fb5d8631c2"
CAUTION: Do not forget to change default securityToken with other security value.

With mentioned configuration you should supply two extra headers with your HTTP request

POST http://minecraft.host:9090/api

Headers:
Content-Type: application/json
Nonce: 1516453674
Sign: 5/wo3v6CxL1Duw0aJTAhWQcau1nc7yYMnO659E1/k+c=

{
    "command": "kill Kenny"
}

Nonce is an any positive integer with one limitation - it should be greater than previous sent Nonce. It is easy to use UNIX timestamp for this purposes, but actually it up to client to choose Nonce selecting logic.

If sent Nonce is leaser or equal to previous one request will be rejected.

Sign is a Base64 encoded HmacSHA256 hash calculated with securityToken

HmacSHA256(requestBody + Nonce) calculated with securityToken from configuration file

With request above it should be

HmacSHA256({"command": "kill Kenny"}1516453674) with a78cde7a-5c15-46f8-9178-74fb5d8631c2 key

Refer to details how to calculate Base64 HmacSHA256 with different languages.