infrared

package module
v1.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 20, 2022 License: Apache-2.0 Imports: 28 Imported by: 0

README

Discord Docker Pulls

build GitHub

Infrared - a Minecraft Proxy

An ultra lightweight Minecraft reverse proxy and idle placeholder: Ever wanted to have only one exposed port on your server for multiple Minecraft servers? Then Infrared is the tool you need! Infrared works as a reverse proxy using a subdomain to connect clients to a specific Minecraft server. It works similar to Nginx for those of you who are familiar.

Features

  • Reverse Proxy
  • Display Placeholder Server
  • Autostart Server when pinged
  • Logger Callback URLs
  • HAProxy Protocol Support
  • TCPShield/RealIP Protocol Support
  • Prometheus Support
  • REST API

Deploy

$ docker build --no-cache -t haveachin/infrared:latest https://github.com/haveachin/infrared.git &&
  docker image prune -f --filter label=stage=intermediate &&
  docker run -d --name infrared --restart=unless-stopped -it -v /usr/local/infrared/configs/:/configs -p 25565:25565/tcp --expose 25565 haveachin/infrared:latest

Update

$ docker build --no-cache -t haveachin/infrared:latest https://github.com/haveachin/infrared.git &&
  docker image prune -f --filter label=stage=intermediate &&
  docker stop infrared &&
  docker rm infrared &&
  docker run -d --name infrared --restart=unless-stopped -it -v /usr/local/infrared/configs/:/configs -p 25565:25565/tcp --expose 25565 haveachin/infrared:latest

Environment Variables

Info: Command-line flags override environment variables.

INFRARED_CONFIG_PATH is the path to all your server configs [default: "./configs/"] INFRARED_RECEIVE_PROXY_PROTOCOL if Infrared should be able to receive proxy protocol [default: "false"]

INFRARED_API_ENABLED if the api should be enabled [default: "false"]
INFRARED_API_BIND change the http bind option [default: "127.0.0.1:8080"]

INFRARED_PROMETHEUS_ENABLED enables the Prometheus stats exporter [default: "false"]
INFRARED_PROMETHEUS_BIND specifies what the Prometheus HTTP server should bind to [default: ":9100"]

Command-Line Flags

-config-path specifies the path to all your server configs [default: "./configs/"]

-receive-proxy-protocol if Infrared should be able to receive proxy protocol [default: false]

-enable-prometheus enables the Prometheus stats exporter [default: false]

-prometheus-bind specifies what the Prometheus HTTP server should bind to [default: :9100]

Example Usage

./infrared -config-path="." -receive-proxy-protocol=true -enable-prometheus -prometheus-bind="localhost:9123"

Proxy Config

Field Name Type Required Default Description
domainName String true localhost Should be fully qualified domain name.
Note: Every string is accepted. So localhost is also valid.
listenTo String true :25565 The address (usually just the port; so short term :port) that the proxy should listen to for incoming connections.
Accepts basically every address format you throw at it. Valid examples: :25565, localhost:25565, 0.0.0.0:25565, 127.0.0.1:25565, example.de:25565
proxyTo String true The address that the proxy should send incoming connections to. Accepts Same formats as the listenTo field.
proxyBind String false The local IP that is being used to dail to the server on proxyTo. (Same as Nginx proxy-bind)
disconnectMessage String false Sorry {{username}}, but the server is offline. The message a client sees when he gets disconnected from Infrared due to the server on proxyTo won't respond. Currently available placeholders:
- username the username of player that tries to connect
- now the current server time
- remoteAddress the address of the client that tries to connect
- localAddress the local address of the server
- domain the domain of the proxy (same as domainName)
- proxyTo the address that the proxy proxies to (same as proxyTo)
- listenTo the address that Infrared listens on (same as listenTo)
timeout Integer true 1000 The time in milliseconds for the proxy to wait for a ping response before the host (the address you proxyTo) will be declared as offline. This "online check" will be resend for every new connection.
spoofForcedHost String false If Infrared should modify the handshake packet to spoof BungeeCords forced_hosts option.
proxyProtocol Boolean false false If Infrared should use HAProxy's Proxy Protocol for IP forwarding.
Warning: You should only ever set this to true if you now that the server you proxyTo is compatible.
realIp Boolean false false If Infrared should use TCPShield/RealIP Protocol for IP forwarding.
Warning: You should only ever set this to true if you now that the server you proxyTo is compatible.
docker Object false See Docker Optional Docker configuration to automatically start a container and stop it again if unused.
Note: Infrared will not take direct connections into account. Be sure to route all traffic that connects to the container through Infrared.
onlineStatus Object false This is the response that Infrared will give when a client asks for the server status and the server is online.
offlineStatus Object false See Response Status This is the response that Infrared will give when a client asks for the server status and the server is offline.
callbackServer Object false See Callback Server Optional callback server configuration to send events as a POST request to a specified URL.
Docker
Field Name Type Required Default Description
dnsServer String false 127.0.0.11 The address of the DNS that resolves the container names.
containerName String true The name of the container that should be automatically started/stopped.
portainer Object false Optional Portainer configuration for authorization management.
Portainer

More info on Portainer.

Field Name Type Required Default Description
address String true URL of the Portainer instance.
endpointId String true The ID typically an integer of the docker endpoint in the portainer instance.
username String true Username for the Portainer user.
password String true Password for the Portainer user.
Response Status
Field Name Type Required Default Description
versionName String false Infrared 1.18 The version name of the Minecraft Server.
protocolNumber Integer true 757 The protocol version number.
maxPlayers Integer false 20 The maximum number of players that can join the server.
Note: Infrared will not limit more players from joining. This number is just for display.
playersOnline Integer false 0 The number of online players.
Note: Infrared will not that this number is also just for display.
playerSamples Array false An array of player samples. See [Player Sample](#Player Sample).
iconPath String false The path to the server icon.
motd String false The motto of the day, short MOTD.
Player Sample
Field Name Type Required Default Description
Name String true Username of the player.
uuid String false UUID of the player.
Callback Server
Field Name Type Required Default Description
url String true URL of the callback server URL.
events Array true A string array of event names. Currently available event names are:
- Error will send error logs
- PlayerJoin will send player joins
- PlayerLeave will send player leaves
- ContainerStart will send container starts
- ContainerStop will send container stops
Examples
Minimal Config
min.example.com
{
  "domainName": "mc.example.com",
  "proxyTo": ":8080"
}
Full Config
full.example.com
{
  "domainName": "mc.example.com",
  "listenTo": ":25565",
  "proxyTo": ":8080",
  "proxyBind": "0.0.0.0",
  "proxyProtocol": false,
  "realIp": false,
  "timeout": 1000,
  "disconnectMessage": "Username: {{username}}\nNow: {{now}}\nRemoteAddress: {{remoteAddress}}\nLocalAddress: {{localAddress}}\nDomain: {{domain}}\nProxyTo: {{proxyTo}}\nListenTo: {{listenTo}}",
  "docker": {
    "dnsServer": "127.0.0.11",
    "containerName": "mc",
    "timeout": 30000,
    "portainer": {
      "address": "localhost:9000",
      "endpointId": "1",
      "username": "admin",
      "password": "foobar"
    }
  },
  "onlineStatus": {
    "versionName": "1.18",
    "protocolNumber": 757,
    "maxPlayers": 20,
    "playersOnline": 2,
    "playerSamples": [
      {
        "name": "Steve",
        "uuid": "8667ba71-b85a-4004-af54-457a9734eed7"
      },
      {
        "name": "Alex",
        "uuid": "ec561538-f3fd-461d-aff5-086b22154bce"
      }
    ],
    "motd": "Join us!"
  },
  "offlineStatus": {
    "versionName": "1.18",
    "protocolNumber": 757,
    "maxPlayers": 20,
    "playersOnline": 0,
    "motd": "Server is currently offline"
  },
  "callbackServer": {
    "url": "https://mc.example.com/callback",
    "events": [
      "Error",
      "PlayerJoin",
      "PlayerLeave",
      "ContainerStart",
      "ContainerStop"
    ]
  }
}

Rest API

The API should not be accessible from the internet!

Enabling API

To enable the API the environment variable INFRARED_API_ENABLED must be set to "true". To change the http bind, set the env variable INFRARED_API_BIND to something like "0.0.0.0:3000" the default value is "127.0.0.1:8080"

API Methods
Create new config

POST /proxies
Body must contain:

{
"domainName": "mc.example.com",
"proxyTo": ":8080"
}

But all values (like in a normal config file) can be set.

The API then will create a file with the name of the domain (if the file exists it will be overwritten) and write the body to it. The proxy can now be visited.


POST /proxies/{fileName}
Body must contain:

{
"domainName": "mc.example.com",
"proxyTo": ":8080"
}

But all values (like in a normal config file) can be set.

The server will create a file with the given filename (if the file exists it will be overwritten) and store the config in it.

Remove config

DELETE /proxies/{fileName}
Replace :file with the name of the proxy configuration file.

If the file was found it will be unloaded and deleted. Open connections do not close, but no new player can connect anymore.

Prometheus exporter

The built-in prometheus exporter can be used to view metrics about infrareds operation.
When the command line flag -enable-prometheus is enabled it will bind to :9100 by default, if you would like to use another port or use an application like node_exporter that also uses port 9100 on the same machine you can change the port with the -prometheus-bind command line flag, example: -prometheus-bind=":9070".
It is recommended to firewall the prometheus exporter with an application like ufw or iptables to make it only accessible by your own Prometheus instance.

Prometheus configuration:

Example prometheus.yml configuration:

scrape_configs:
  - job_name: infrared
    static_configs:
    - targets: ['infrared-exporter-hostname:port']
Metrics:
  • infrared_connected: show the amount of connected players per instance and proxy:
    • Example response: infrared_connected{host="proxy.example.com",instance="vps1.example.com:9070",job="infrared"} 10
    • host: listenTo domain as specified in the infrared configuration.
    • instance: what infrared instance the amount of players are connected to.
    • job: what job was specified in the prometheus configuration.
  • infrared_proxies: show the amount of active infrared proxies:
    • Example response: infrared_proxies{instance="vps1.example.com:9070",job="infrared"} 5
    • instance: what infrared instance has that amount of active proxies.
    • job: what job was specified in the prometheus configuration.

Coding Guidelines

Commit Messages

When contributing to this project please follow the Conventional Commits specification for writing commit messages, so that changelogs and release versions can be generated automatically.

Example commit message

fix: prevent racing of requests

Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.

Remove timeouts which were used to mitigate the racing issue but are
obsolete now.

Reviewed-by: Z
Refs: #123

Some tooling that can help you author those commit messages are the following plugins:

Similar Projects

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ReadFilePaths

func ReadFilePaths(path string, recursive bool) ([]string, error)

func WatchProxyConfigFolder

func WatchProxyConfigFolder(path string, out chan *ProxyConfig) error

Types

type CallbackServerConfig

type CallbackServerConfig struct {
	URL    string   `json:"url"`
	Events []string `json:"events"`
}

type Conn

type Conn interface {
	net.Conn
	PacketWriter
	PacketReader
	PacketPeeker

	Reader() *bufio.Reader
}

Conn is a minecraft Connection

type Dialer

type Dialer struct {
	net.Dialer
}

func (Dialer) Dial

func (d Dialer) Dial(addr string) (Conn, error)

Dial create a Minecraft connection

type DockerConfig

type DockerConfig struct {
	DNSServer     string `json:"dnsServer"`
	ContainerName string `json:"containerName"`
	Timeout       int    `json:"timeout"`
	Portainer     struct {
		Address    string `json:"address"`
		EndpointID string `json:"endpointId"`
		Username   string `json:"username"`
		Password   string `json:"password"`
	} `json:"portainer"`
}

func (DockerConfig) IsDocker

func (docker DockerConfig) IsDocker() bool

func (DockerConfig) IsPortainer

func (docker DockerConfig) IsPortainer() bool

type Gateway

type Gateway struct {
	ReceiveProxyProtocol bool

	Proxies sync.Map
	// contains filtered or unexported fields
}

func (*Gateway) Close

func (gateway *Gateway) Close()

Close closes all listeners

func (*Gateway) CloseProxy

func (gateway *Gateway) CloseProxy(proxyUID string)

func (*Gateway) EnablePrometheus added in v1.1.0

func (gateway *Gateway) EnablePrometheus(bind string) error

func (*Gateway) KeepProcessActive

func (gateway *Gateway) KeepProcessActive()

func (*Gateway) ListenAndServe

func (gateway *Gateway) ListenAndServe(proxies []*Proxy) error

func (*Gateway) RegisterProxy

func (gateway *Gateway) RegisterProxy(proxy *Proxy) error

type Listener

type Listener struct {
	net.Listener
}

func Listen

func Listen(addr string) (Listener, error)

func (Listener) Accept

func (l Listener) Accept() (Conn, error)

type PacketPeeker

type PacketPeeker interface {
	PeekPacket() (protocol.Packet, error)
}

type PacketReader

type PacketReader interface {
	ReadPacket() (protocol.Packet, error)
}

type PacketWriter

type PacketWriter interface {
	WritePacket(p protocol.Packet) error
}

type PlayerSample

type PlayerSample struct {
	Name string `json:"name"`
	UUID string `json:"uuid"`
}

type Proxy

type Proxy struct {
	Config *ProxyConfig
	// contains filtered or unexported fields
}

func (*Proxy) CallbackLogger

func (proxy *Proxy) CallbackLogger() callback.Logger

func (*Proxy) Dialer

func (proxy *Proxy) Dialer() (*Dialer, error)

func (*Proxy) DisconnectMessage

func (proxy *Proxy) DisconnectMessage() string

func (*Proxy) DockerTimeout

func (proxy *Proxy) DockerTimeout() time.Duration

func (*Proxy) DomainName

func (proxy *Proxy) DomainName() string

func (*Proxy) IsOnlineStatusConfigured

func (proxy *Proxy) IsOnlineStatusConfigured() bool

func (*Proxy) ListenTo

func (proxy *Proxy) ListenTo() string

func (*Proxy) OfflineStatusPacket

func (proxy *Proxy) OfflineStatusPacket() (protocol.Packet, error)

func (*Proxy) OnlineStatusPacket

func (proxy *Proxy) OnlineStatusPacket() (protocol.Packet, error)

func (*Proxy) Process

func (proxy *Proxy) Process() process.Process

func (*Proxy) ProxyProtocol

func (proxy *Proxy) ProxyProtocol() bool

func (*Proxy) ProxyTo

func (proxy *Proxy) ProxyTo() string

func (*Proxy) RealIP

func (proxy *Proxy) RealIP() bool

func (*Proxy) SpoofForcedHost added in v1.3.0

func (proxy *Proxy) SpoofForcedHost() string

func (*Proxy) Timeout

func (proxy *Proxy) Timeout() time.Duration

func (*Proxy) UID

func (proxy *Proxy) UID() string

type ProxyConfig

type ProxyConfig struct {
	sync.RWMutex

	DomainName        string               `json:"domainName"`
	ListenTo          string               `json:"listenTo"`
	ProxyTo           string               `json:"proxyTo"`
	ProxyBind         string               `json:"proxyBind"`
	SpoofForcedHost   string               `json:"spoofForcedHost"`
	ProxyProtocol     bool                 `json:"proxyProtocol"`
	RealIP            bool                 `json:"realIp"`
	Timeout           int                  `json:"timeout"`
	DisconnectMessage string               `json:"disconnectMessage"`
	Docker            DockerConfig         `json:"docker"`
	OnlineStatus      StatusConfig         `json:"onlineStatus"`
	OfflineStatus     StatusConfig         `json:"offlineStatus"`
	CallbackServer    CallbackServerConfig `json:"callbackServer"`
	// contains filtered or unexported fields
}

ProxyConfig is a data representation of a Proxy configuration

func DefaultProxyConfig

func DefaultProxyConfig() ProxyConfig

func LoadProxyConfigsFromPath

func LoadProxyConfigsFromPath(path string, recursive bool) ([]*ProxyConfig, error)

func NewProxyConfigFromPath

func NewProxyConfigFromPath(path string) (*ProxyConfig, error)

NewProxyConfigFromPath loads a ProxyConfig from a file path and then starts watching it for changes. On change the ProxyConfig will automatically LoadFromPath itself

func (*ProxyConfig) Dialer

func (cfg *ProxyConfig) Dialer() (*Dialer, error)

func (*ProxyConfig) LoadFromPath

func (cfg *ProxyConfig) LoadFromPath(path string) error

LoadFromPath loads the ProxyConfig from a file

type StatusConfig

type StatusConfig struct {
	VersionName    string         `json:"versionName"`
	ProtocolNumber int            `json:"protocolNumber"`
	MaxPlayers     int            `json:"maxPlayers"`
	PlayersOnline  int            `json:"playersOnline"`
	PlayerSamples  []PlayerSample `json:"playerSamples"`
	IconPath       string         `json:"iconPath"`
	MOTD           string         `json:"motd"`
	// contains filtered or unexported fields
}

func (StatusConfig) StatusResponsePacket

func (cfg StatusConfig) StatusResponsePacket() (protocol.Packet, error)

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL