proxy

package module
v0.0.0-...-46726be Latest Latest
Warning

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

Go to latest
Published: Feb 11, 2024 License: MIT Imports: 34 Imported by: 5

README

mt-multiserver-proxy

mt-multiserver-proxy is a reverse proxy designed for linking multiple Minetest servers together. It is the successor to multiserver.

mt

This project was made possible by anon55555's mt module.

Supported Minetest versions

Each commit only supports a single Minetest minor version.

This is because each minor version breaks compatibility with its predecessors. Patch versions should be safe. Example: A proxy commit for 5.5.0 should also work with 5.5.1 but is highly likely to break when used with 5.6.0.

All internal servers should have strict version checking enabled. Otherwise version mismatches may remain undetected.

Compatibility breaks because upstream Minetest doesn't take the protocol version into account when sending packets and instead expects the receiver to ignore any new fields it doesn't recognize. This causes a trailing data error on the proxy that prevents the packet from being parsed and processed.

Proxy updates

Only the currently supported Minetest version will get proxy updates, i.e. features and bug fixes won't be backported to earlier Minetest versions. If you need this you can manually merge the commits yourself.

Commit hashes for Minetest releases

The latest main usually supports the latest Minetest release unless it isn't supported yet. The following list contains the commit hashes for all versions that were ever supported:

The chat command plugin commit hashes are mainly specified for old proxy versions that didn't support automatic plugin building and version management yet. Using the mt-build-plugin tool should be sufficient, though there may be API changes preventing the plugin from compiling against an old proxy version in which case the commit hashes are needed too. Conclusively it's important to downgrade the plugin to that version if you want it to work with an old proxy version without automatic plugin building and version management or if it doesn't compile against old proxy versions anymore.

Minetest development builds

Development builds aren't supported at all because it would be a monumental maintenance effort. If you have to use one, try the proxy version for its release first and continue with the proxy version for the last release preceeding the development build. If this doesn't work you'll have to edit the code of the proxy yourself.

Development builds may pass the version check performed by the proxy and experience major breakage. This is because the protocol version isn't bumped when a new development phase is started after a release.

Installation

It is recommended to explicitly set the GOBIN environment variable to a directory that is only used for the proxy binaries, databases and configuration files.

Go 1.21 or higher is required. Run

export GOBIN=~/.local/share/mt-multiserver-proxy
mkdir -p ${GOBIN}
go install github.com/HimbeerserverDE/mt-multiserver-proxy/cmd/...@latest

to download and compile the project. A mt-multiserver-proxy executable will be created in your ${GOBIN} directory. The same command is also used to upgrade to the latest version. You will need to recompile all plugins after upgrading.

In addition to the main mt-multiserver-proxy binary the following additional utilities are installed:

  • mt-auth-convert: Helper program to convert between authentication database formats.
  • mt-build-plugin: Utility for building plugins against the correct proxy version.

You can replace the ... in the installation command with any of the binary names to limit installation and updating to a single executable. This is not recommended, however, as it can cause version mismatches between them.

Development builds

Build the following binaries from the proxy repository directory:

go build -race ./cmd/mt-auth-convert
go build -race ./cmd/mt-build-plugin
go build -race ./cmd/mt-multiserver-proxy

Do not move the binaries! Doing so breaks automatic plugin builds.

Docker

The proxy can be run in Docker. See doc/docker.md for instructions and details.

Usage

Starting

Run ${GOBIN}/mt-multiserver-proxy. The configuration file and other required files are created automatically in the directory the executable is in, so make sure to install the executable to the desired location. Symlinks to the executable will be followed, only the real path matters.

Stopping

mt-multiserver-proxy reacts to SIGINT, SIGTERM and SIGHUP. It stops listening for new connections, kicks all clients, disconnects from all servers and exits. If some clients aren't responding, mt-multiserver-proxy waits until they have timed out.

Configuration

The configuration file name and format including a minimal example are described in doc/config.md.

All internal servers need to allow empty passwords and must not be reachable from the internet!

Authentication database migration

It is possible to import existing Minetest authentication databases. See doc/auth_backends.md for details.

Chat commands

The default chat commands can be installed as a plugin.

Plugins

This proxy supports loading Go plugins. Consult doc/plugins.md for details on how to develop or install them.

Docker

The proxy can be run in Docker. See doc/docker.md for details.

Documentation

Overview

Package proxy is a minetest reverse proxy for multiple servers. It also provides an API for plugins.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrAuthBackendExists   = errors.New("auth backend already set")
	ErrInvalidSRPHeader    = errors.New("encoded password is not SRP")
	ErrLastSrvNotSupported = errors.New("auth backend does not support server information")
)
View Source
var (
	ErrNoServerConn = errors.New("no server connection")
	ErrNoSuchServer = errors.New("inexistent server")
	ErrNewMediaPool = errors.New("media pool unknown to client")
)
View Source
var ChatCmdTimeout = 10 * time.Second

ChatCmdTimeout is the time needed until a user is warned about a chat command that's taking long to execute.

View Source
var ErrNoBuildInfo = errors.New("unable to retrieve proxy version: no build info")

Functions

func AddServer

func AddServer(name string, s Server) bool

AddServer dynamically configures a new Server at runtime. Servers added in this way are ephemeral and will be lost when the proxy shuts down. The server must be part of a media pool with at least one other member. At least one of the other members always needs to be reachable. WARNING: Reloading the config will not overwrite servers added using this function. The server definition from the configuration file will silently be ignored.

func Banned

func Banned(addr *net.UDPAddr) bool

Banned reports whether a network address is banned.

func BuildPlugin

func BuildPlugin() error

func ChatCmdExists

func ChatCmdExists(name string) bool

ChatCmdExists reports if a ChatCmd exists.

func ChatCmds

func ChatCmds() map[string]ChatCmd

ChatCmds returns a map of all ChatCmds indexed by their names.

func Clts

func Clts() map[*ClientConn]struct{}

Clts returns all ClientConns currently connected to the proxy.

func Colorize

func Colorize(text, color string) string

Colorize returns the minetest-colorized version of the input.

func FallbackServers

func FallbackServers(server string) []string

FallbackServers returns a slice of server names that a server can fall back to.

func LoadConfig

func LoadConfig() error

LoadConfig attempts to parse the configuration file. It leaves the config unchanged if there is an error and returns the error.

func Path

func Path(path ...string) string

Path prepends the directory the executable is in to the given path. It follows symlinks to the executable.

func Players

func Players() map[string]struct{}

Players returns the names of all players that are currently connected to the proxy.

func RegisterChatCmd

func RegisterChatCmd(cmd ChatCmd) bool

RegisterChatCmd adds a new ChatCmd. It returns true on success and false if a command with the same name already exists.

func RegisterInteractionHandler

func RegisterInteractionHandler(handler InteractionHandler)

RegisterInteractionHandler adds a new InteractionHandler.

func ReplaceAllStringSubmatchFunc

func ReplaceAllStringSubmatchFunc(re *regexp.Regexp, str string, repl func([]string) string) string

func RmServer

func RmServer(name string) bool

RmServer deletes a Server from the Config at runtime. Only servers added using AddServer can be deleted at runtime. Returns true on success or if the server doesn't exist.

func Run

func Run()

Run initializes the proxy and starts the main listener loop. It blocks forever.

func Unban

func Unban(id string) error

Unban removes a player from the ban list. It accepts both network addresses and player names.

func Uptime

func Uptime() time.Duration

Uptime returns the time the proxy has been running for

func Version

func Version() (string, error)

Version returns the version string of the running instance.

Types

type AuthBackend

type AuthBackend interface {
	Exists(name string) bool
	Passwd(name string) (salt, verifier []byte, err error)
	SetPasswd(name string, salt, verifier []byte) error
	LastSrv(name string) (string, error)
	SetLastSrv(name, srv string) error
	Timestamp(name string) (time.Time, error)
	Import(in []User) error
	Export() ([]User, error)

	Ban(addr, name string) error
	Unban(id string) error
	Banned(addr *net.UDPAddr) bool
	ImportBans(in []Ban) error
	ExportBans() ([]Ban, error)
}

type AuthFiles

type AuthFiles struct{}

func (AuthFiles) Ban

func (a AuthFiles) Ban(addr, name string) error

Ban adds a ban entry for a network address and an associated name.

func (AuthFiles) Banned

func (a AuthFiles) Banned(addr *net.UDPAddr) bool

Banned reports whether a network address is banned. Error cases count as banned.

func (AuthFiles) Exists

func (a AuthFiles) Exists(name string) bool

Exists reports whether a user is registered. Error cases count as inexistent.

func (AuthFiles) Export

func (a AuthFiles) Export() ([]User, error)

Export returns data that can be processed by Import or an error.

func (AuthFiles) ExportBans

func (a AuthFiles) ExportBans() ([]Ban, error)

ExportBans returns data that can be processed by ImportBans or an error,

func (AuthFiles) Import

func (a AuthFiles) Import(in []User) error

Import deletes all users and adds the passed users.

func (AuthFiles) ImportBans

func (a AuthFiles) ImportBans(in []Ban) error

ImportBans deletes all ban entries and adds the passed entries.

func (AuthFiles) LastSrv

func (a AuthFiles) LastSrv(name string) (string, error)

LastSrv returns the last server a user was on.

func (AuthFiles) Passwd

func (a AuthFiles) Passwd(name string) (salt, verifier []byte, err error)

Passwd returns the SRP salt and verifier of a user or an error.

func (AuthFiles) SetLastSrv

func (a AuthFiles) SetLastSrv(name, srv string) error

SetLastSrv sets the last server a user was on.

func (AuthFiles) SetPasswd

func (a AuthFiles) SetPasswd(name string, salt, verifier []byte) error

SetPasswd creates a password entry if necessary and sets the password of a user.

func (AuthFiles) Timestamp

func (a AuthFiles) Timestamp(name string) (time.Time, error)

Timestamp returns the last time an authentication entry was accessed or an error.

func (AuthFiles) Unban

func (a AuthFiles) Unban(id string) error

Unban deletes a ban entry. It accepts both network addresses and player names.

type AuthMTPostgreSQL

type AuthMTPostgreSQL struct {
	// contains filtered or unexported fields
}

A handle to a PostgreSQL authentication database. The upstream Minetest schema is used.

Table info:

                           Table "public.auth"
Column   |  Type   | Collation | Nullable |              Default

------------+---------+-----------+----------+------------------------------------

id         | integer |           | not null | nextval('auth_id_seq'::regclass)
name       | text    |           |          |
password   | text    |           |          |
last_login | integer |           | not null | 0

Indexes:

"auth_pkey" PRIMARY KEY, btree (id)
"auth_name_key" UNIQUE CONSTRAINT, btree (name)

Sequence info:

                 Sequence "public.auth_id_seq"
Type   | Start | Minimum |  Maximum   | Increment | Cycles? | Cache

---------+-------+---------+------------+-----------+---------+-------

integer |     1 |       1 | 2147483647 |         1 | no      |     1

Owned by: public.auth.id

func NewAuthMTPostgreSQL

func NewAuthMTPostgreSQL(conn string) (*AuthMTPostgreSQL, error)

NewAuthMTPostgreSQL opens the PostgreSQL authentication database at the specified connection string.

func (*AuthMTPostgreSQL) Ban

func (a *AuthMTPostgreSQL) Ban(addr, name string) error

Ban adds a ban entry for a network address and an associated name.

func (*AuthMTPostgreSQL) Banned

func (a *AuthMTPostgreSQL) Banned(addr *net.UDPAddr) bool

Banned reports whether a network address is banned. Error cases count as banned.

func (*AuthMTPostgreSQL) Close

func (a *AuthMTPostgreSQL) Close() error

Close closes the underlying PostgreSQL database handle.

func (*AuthMTPostgreSQL) Exists

func (a *AuthMTPostgreSQL) Exists(name string) bool

Exists reports whether a user is registered. Error cases count as inexistent.

func (*AuthMTPostgreSQL) Export

func (a *AuthMTPostgreSQL) Export() ([]User, error)

Export returns data that can be processed by Import or an error.

func (*AuthMTPostgreSQL) ExportBans

func (a *AuthMTPostgreSQL) ExportBans() ([]Ban, error)

ExportBans returns data that can be processed by ImportBans or an error.

func (*AuthMTPostgreSQL) Import

func (a *AuthMTPostgreSQL) Import(in []User) error

Import adds the passed users.

func (*AuthMTPostgreSQL) ImportBans

func (a *AuthMTPostgreSQL) ImportBans(in []Ban) error

ImportBans adds the passed entries.

func (*AuthMTPostgreSQL) LastSrv

func (a *AuthMTPostgreSQL) LastSrv(_ string) (string, error)

LastSrv always returns an error since the Minetest database schema cannot store this information.

func (*AuthMTPostgreSQL) Passwd

func (a *AuthMTPostgreSQL) Passwd(name string) (salt, verifier []byte, err error)

Passwd returns the SRP salt and verifier of a user or an error.

func (*AuthMTPostgreSQL) SetLastSrv

func (a *AuthMTPostgreSQL) SetLastSrv(_, _ string) error

SetLastSrv is a no-op since the Minetest database schema cannot store this information.

func (*AuthMTPostgreSQL) SetPasswd

func (a *AuthMTPostgreSQL) SetPasswd(name string, salt, verifier []byte) error

SetPasswd creates a password entry if necessary and sets the password of a user.

func (*AuthMTPostgreSQL) Timestamp

func (a *AuthMTPostgreSQL) Timestamp(name string) (time.Time, error)

Timestamp returns the last time an authentication entry was accessed or an error.

func (*AuthMTPostgreSQL) Unban

func (a *AuthMTPostgreSQL) Unban(id string) error

Unban deletes a ban entry. It accepts bot network addresses and player names.

type AuthMTSQLite3

type AuthMTSQLite3 struct {
	// contains filtered or unexported fields
}

A handle to a SQLite3 authentication database. The upstream Minetest schema is used.

Table info:

0|id|INTEGER|0||1 1|name|VARCHAR(32)|0||0 2|password|VARCHAR(512)|0||0 3|last_login|INTEGER|0||0

func NewAuthMTSQLite3

func NewAuthMTSQLite3() (*AuthMTSQLite3, error)

NewAuthMTSQLite3 opens the SQLite3 authentication database at auth.sqlite.

func (*AuthMTSQLite3) Ban

func (a *AuthMTSQLite3) Ban(addr, name string) error

Ban adds a ban entry for a network address and an associated name.

func (*AuthMTSQLite3) Banned

func (a *AuthMTSQLite3) Banned(addr *net.UDPAddr) bool

Banned reports whether a network address is banned. Error cases count as banned.

func (*AuthMTSQLite3) Close

func (a *AuthMTSQLite3) Close() error

Close closes the underlying SQLite3 database handle.

func (*AuthMTSQLite3) Exists

func (a *AuthMTSQLite3) Exists(name string) bool

Exists reports whether a user is registered. Error cases count as inexistent.

func (*AuthMTSQLite3) Export

func (a *AuthMTSQLite3) Export() ([]User, error)

Export returns data that can be processed by Import or an error.

func (*AuthMTSQLite3) ExportBans

func (a *AuthMTSQLite3) ExportBans() ([]Ban, error)

ExportBans returns data that can be processed by ImportBans or an error.

func (*AuthMTSQLite3) Import

func (a *AuthMTSQLite3) Import(in []User) error

Import adds the passed users.

func (*AuthMTSQLite3) ImportBans

func (a *AuthMTSQLite3) ImportBans(in []Ban) error

ImportBans adds the passed entries.

func (*AuthMTSQLite3) LastSrv

func (a *AuthMTSQLite3) LastSrv(_ string) (string, error)

LastSrv always returns an error since the Minetest database schema cannot store this information.

func (*AuthMTSQLite3) Passwd

func (a *AuthMTSQLite3) Passwd(name string) (salt, verifier []byte, err error)

Passwd returns the SRP salt and verifier of a user or an error.

func (*AuthMTSQLite3) SetLastSrv

func (a *AuthMTSQLite3) SetLastSrv(_, _ string) error

SetLastSrv is a no-op since the Minetest database schema cannot store this information.

func (*AuthMTSQLite3) SetPasswd

func (a *AuthMTSQLite3) SetPasswd(name string, salt, verifier []byte) error

SetPasswd creates a password entry if necessary and sets the password of a user.

func (*AuthMTSQLite3) Timestamp

func (a *AuthMTSQLite3) Timestamp(name string) (time.Time, error)

Timestamp returns the last time an authentication entry was accessed or an error.

func (*AuthMTSQLite3) Unban

func (a *AuthMTSQLite3) Unban(id string) error

Unban deletes a ban entry. It accepts bot network addresses and player names.

type Ban

type Ban struct {
	Addr string
	Name string
}

type ChatCmd

type ChatCmd struct {
	Name    string
	Perm    string
	Help    string
	Usage   string
	Handler func(*ClientConn, ...string) string
}

A ChatCmd holds information on how to handle a chat command.

type ClientConn

type ClientConn struct {
	mt.Peer
	// contains filtered or unexported fields
}

A ClientConn is a connection to a minetest client.

func Find

func Find(name string) *ClientConn

Find returns the ClientConn that has the specified player name. If no ClientConn is found, nil is returned.

func (*ClientConn) Ban

func (cc *ClientConn) Ban() error

Ban disconnects the ClientConn and prevents the underlying network address from connecting again.

func (*ClientConn) DoChatMsg

func (cc *ClientConn) DoChatMsg(msg string)

DoChatMsg handles a chat message string as if it was sent by the ClientConn.

func (*ClientConn) HasPerms

func (cc *ClientConn) HasPerms(want ...string) bool

HasPerms returns true if the ClientConn has all of the specified permissions. Otherwise it returns false. This method matches wildcards, but they may only be used at the end of a raw permission. Asterisks in other places will be treated as regular characters.

func (*ClientConn) Hop

func (cc *ClientConn) Hop(serverName string) (err error)

Hop connects the ClientConn to the specified upstream server or the first working fallback server, saving the player's last server unless `ForceDefaultSrv` is enabled. If all attempts fail the client stays connected to the current server with the potential for inconsistent state. At the moment the ClientConn is NOT fixed if an error occurs so the player may have to reconnect.

func (*ClientConn) HopGroup

func (cc *ClientConn) HopGroup(groupName string) error

HopGroup connects the ClientConn to the specified server group or the first working fallback server, saving the player's last server unless `ForceDefaultSrv` is enabled. See the documentation on `Server.Groups` in `doc/config.md` for details on how a specific game server is selected from the group name. If all attempts fail the client stays connected to the current server with the potential for inconsistent state. At the moment the ClientConn is NOT fixed if an error occurs so the player may have to reconnect.

func (*ClientConn) HopRaw

func (cc *ClientConn) HopRaw(serverName string) error

HopRaw connects the ClientConn to the specified upstream server. At the moment the ClientConn is NOT fixed if an error occurs so the player may have to reconnect.

This method ignores fallback servers and doesn't save the player's last server. You may use the `Hop` wrapper for these purposes.

func (*ClientConn) Init

func (cc *ClientConn) Init() <-chan struct{}

Init returns a channel that is closed when the ClientConn enters the csActive state.

func (*ClientConn) Kick

func (cc *ClientConn) Kick(reason string)

Kick sends mt.ToCltKick with the specified custom reason and closes the ClientConn.

func (*ClientConn) Log

func (cc *ClientConn) Log(dir string, v ...interface{})

Log logs an interaction with the ClientConn. dir indicates the direction of the interaction.

func (*ClientConn) Name

func (cc *ClientConn) Name() string

Name returns the player name of the ClientConn.

func (*ClientConn) Perms

func (cc *ClientConn) Perms() []string

Perms returns the raw permissions of the ClientConn.

func (*ClientConn) SendChatMsg

func (cc *ClientConn) SendChatMsg(msg ...string)

SendChatMsg sends a chat message to the ClientConn.

func (*ClientConn) ServerName

func (cc *ClientConn) ServerName() string

ServerName returns the name of the current upstream server of the ClientConn. It is empty if there is no upstream connection.

type Config

type Config struct {
	NoPlugins        bool
	NoAutoPlugins    bool
	CmdPrefix        string
	RequirePasswd    bool
	SendInterval     float32
	UserLimit        int
	AuthBackend      string
	AuthPostgresConn string
	NoTelnet         bool
	TelnetAddr       string
	BindAddr         string
	DefaultSrv       string
	Servers          map[string]Server
	ForceDefaultSrv  bool
	KickOnNewPool    bool
	FallbackServers  []string
	CSMRF            struct {
		NoCSMs          bool
		ChatMsgs        bool
		ItemDefs        bool
		NodeDefs        bool
		NoLimitMapRange bool
		PlayerList      bool
	}
	MapRange   uint32
	DropCSMRF  bool
	Groups     map[string][]string
	UserGroups map[string]string
	List       struct {
		Enable   bool
		Addr     string
		Interval int

		Name     string
		Desc     string
		URL      string
		Creative bool
		Dmg      bool
		PvP      bool
		Game     string
		FarNames bool
		Mods     []string
	}
}

A Config contains information from the configuration file that affects the way the proxy works.

func Conf

func Conf() Config

Conf returns a copy of the Config used by the proxy. Any modifications will not affect the original Config.

func (Config) DefaultServer

func (cnf Config) DefaultServer() Server

DefaultServer returns information about the default server. If no servers exist the returned struct will be uninitialized. This is a faster shortcut for Config.Servers[Config.DefaultServerName()]. You should thus only use this method or the DefaultServerInfo method.

func (Config) DefaultServerInfo

func (cnf Config) DefaultServerInfo() (string, Server)

DefaultServerInfo returns both the name of the default server and information about it. The return values are uninitialized if no servers exist.

func (Config) DefaultServerName

func (cnf Config) DefaultServerName() string

DefaultServerName returns the name of the default server. If no servers exist it returns an empty string.

func (Config) Pools

func (cnf Config) Pools() map[string]map[string]Server

Pools returns all media pools and their member servers.

func (Config) RandomGroupServer

func (cnf Config) RandomGroupServer(search string) (string, bool)

RandomGroupServer returns the name of a random member of a server group or the input string if it is a valid, existent server name. It also returns a boolean indicating success. The returned string is blank if there is a failure, i.e. if the input string is neither a server nor a group.

func (Config) ServerGroups

func (cnf Config) ServerGroups() map[string]map[string]Server

Groups returns all server groups and their member servers.

type Interaction

type Interaction uint8
const (
	Dig Interaction = iota
	StopDigging
	Dug
	Place
	Use
	Activate
	AnyInteraction = 255
)

type InteractionHandler

type InteractionHandler struct {
	Type    Interaction
	Handler func(*ClientConn, *mt.ToSrvInteract) bool
}

A InteractionHandler holds information on how to handle a Minetest Interaction.

type LogWriter

type LogWriter struct {
	// contains filtered or unexported fields
}

func (*LogWriter) Write

func (lw *LogWriter) Write(p []byte) (n int, err error)

Write writes the input data to os.Stderr and the log file. It returns the number of bytes written and an error.

type Server

type Server struct {
	Addr      string
	MediaPool string
	Groups    []string
	Fallbacks []string
	// contains filtered or unexported fields
}

type ServerConn

type ServerConn struct {
	mt.Peer
	// contains filtered or unexported fields
}

A ServerConn is a connection to a minetest server.

func (*ServerConn) Init

func (sc *ServerConn) Init() <-chan struct{}

Init returns a channel that is closed when the ServerConn enters the csActive state.

func (*ServerConn) Log

func (sc *ServerConn) Log(dir string, v ...interface{})

Log logs an interaction with the ServerConn. dir indicates the direction of the interaction.

type User

type User struct {
	Name      string
	Salt      []byte
	Verifier  []byte
	Timestamp time.Time
}

Directories

Path Synopsis
cmd
mt-auth-convert
mt-auth-convert converts between authentication backends.
mt-auth-convert converts between authentication backends.
mt-build-plugin
mt-build-plugin builds a plugin using the proxy version the tool was built for.
mt-build-plugin builds a plugin using the proxy version the tool was built for.
mt-multiserver-proxy
mt-multiserver-proxy starts the reverse proxy.
mt-multiserver-proxy starts the reverse proxy.

Jump to

Keyboard shortcuts

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