diff --git a/Dockerfile b/Dockerfile index d3b758f..d3061dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN CGO_ENABLED=0 go build ./cmd/mc-router +RUN CGO_ENABLED=0 go build -buildvcs=false ./cmd/mc-router FROM scratch ENTRYPOINT ["/mc-router"] diff --git a/README.md b/README.md index f7850d7..ad0fb27 100644 --- a/README.md +++ b/README.md @@ -7,89 +7,69 @@ Routes Minecraft client connections to backend servers based upon the requested server address. -# Usage +## Usage ```text -api-binding host:port - The host:port bound for servicing API requests (env API_BINDING) + The host:port bound for servicing API requests (env API_BINDING) -auto-scale-up - Increase Kubernetes StatefulSet Replicas (only) from 0 to 1 on respective backend servers when accessed (env AUTO_SCALE_UP) + Increase Kubernetes StatefulSet Replicas (only) from 0 to 1 on respective backend servers when accessed (env AUTO_SCALE_UP) -connection-rate-limit int - Max number of connections to allow per second (env CONNECTION_RATE_LIMIT) (default 1) + Max number of connections to allow per second (env CONNECTION_RATE_LIMIT) (default 1) -cpu-profile string - Enables CPU profiling and writes to given path (env CPU_PROFILE) + Enables CPU profiling and writes to given path (env CPU_PROFILE) -debug - Enable debug logs (env DEBUG) - -in-kube-cluster - Use in-cluster Kubernetes config (env IN_KUBE_CLUSTER) - -kube-config string - The path to a Kubernetes configuration file (env KUBE_CONFIG) + Enable debug logs (env DEBUG) + -default string + host:port of a default Minecraft server to use when mapping not found (env DEFAULT) + -docker-refresh-interval int + Refresh interval in seconds for the Docker Swarm integration (env DOCKER_REFRESH_INTERVAL) (default 15) + -docker-timeout int + Timeout configuration in seconds for the Docker Swarm integration (env DOCKER_TIMEOUT) -in-docker-swarm - Use in-swarm Docker config (env IN_DOCKER_SWARM) - -docker-timeout - Timeout configuration in seconds for the Docker Swarm integration (env DOCKER_TIMEOUT) (default 0) - -docker-refresh-interval - Refresh interval in seconds for the Docker Swarm integration (env DOCKER_REFRESH_INTERVAL) (default 15) - -mapping string - Comma-separated mappings of externalHostname=host:port (env MAPPING) + Use in-swarm Docker config (env IN_DOCKER_SWARM) + -in-kube-cluster + Use in-cluster Kubernetes config (env IN_KUBE_CLUSTER) + -kube-config string + The path to a Kubernetes configuration file (env KUBE_CONFIG) + -mapping value + Comma-separated or repeated mappings of externalHostname=host:port (env MAPPING) -metrics-backend string - Backend to use for metrics exposure/publishing: discard,expvar,influxdb (env METRICS_BACKEND) (default "discard") + Backend to use for metrics exposure/publishing: discard,expvar,influxdb (env METRICS_BACKEND) (default "discard") -metrics-backend-config-influxdb-addr string - (env METRICS_BACKEND_CONFIG_INFLUXDB_ADDR) + (env METRICS_BACKEND_CONFIG_INFLUXDB_ADDR) -metrics-backend-config-influxdb-database string - (env METRICS_BACKEND_CONFIG_INFLUXDB_DATABASE) + (env METRICS_BACKEND_CONFIG_INFLUXDB_DATABASE) -metrics-backend-config-influxdb-interval duration - (env METRICS_BACKEND_CONFIG_INFLUXDB_INTERVAL) (default 1m0s) + (env METRICS_BACKEND_CONFIG_INFLUXDB_INTERVAL) (default 1m0s) -metrics-backend-config-influxdb-password string - (env METRICS_BACKEND_CONFIG_INFLUXDB_PASSWORD) + (env METRICS_BACKEND_CONFIG_INFLUXDB_PASSWORD) -metrics-backend-config-influxdb-retention-policy string - (env METRICS_BACKEND_CONFIG_INFLUXDB_RETENTION_POLICY) + (env METRICS_BACKEND_CONFIG_INFLUXDB_RETENTION_POLICY) -metrics-backend-config-influxdb-tags value - any extra tags to be included with all reported metrics (env METRICS_BACKEND_CONFIG_INFLUXDB_TAGS) + any extra tags to be included with all reported metrics (env METRICS_BACKEND_CONFIG_INFLUXDB_TAGS) -metrics-backend-config-influxdb-username string - (env METRICS_BACKEND_CONFIG_INFLUXDB_USERNAME) + (env METRICS_BACKEND_CONFIG_INFLUXDB_USERNAME) + -ngrok-token string + If set, an ngrok tunnel will be established. It is HIGHLY recommended to pass as an environment variable. (env NGROK_TOKEN) -port port - The port bound to listen for Minecraft client connections (env PORT) (default 25565) + The port bound to listen for Minecraft client connections (env PORT) (default 25565) -routes-config string - The path to the routes config file (env ROUTES_CONFIG) + Name or full path to routes config file (env ROUTES_CONFIG) + -simplify-srv + Simplify fully qualified SRV records for mapping (env SIMPLIFY_SRV) + -use-proxy-protocol + Send PROXY protocol to backend servers (env USE_PROXY_PROTOCOL) -version - Output version and exit (env VERSION) + Output version and exit (env VERSION) ``` -# REST API -* `GET /routes` (with `Accept: application/json`) - - Retrieves the currently configured routes - -* `POST /routes` (with `Content-Type: application/json`) - - Registers a route given a JSON body structured like: - ```json - { - "serverAddress": "CLIENT REQUESTED SERVER ADDRESS", - "backend": "HOST:PORT" - } - ``` - -* `POST /defaultRoute` (with `Content-Type: application/json`) - - Registers a default route to the given backend. JSON body is structured as: - ```json - { - "backend": "HOST:PORT" - } - ``` - -* `DELETE /routes/{serverAddress}` - - Deletes an existing route for the given `serverAddress` - -# Docker Multi-Architecture Image +## Docker Multi-Architecture Image The [multi-architecture image published at Docker Hub](https://hub.docker.com/repository/docker/itzg/mc-router) supports amd64, arm64, and arm32v6 (i.e. RaspberryPi). -# Docker Compose Usage +## Docker Compose Usage The following diagram shows how [the example docker-compose.yml](docs/docker-compose.yml) configures two Minecraft server services named `vanilla` and `forge`, which also become the internal @@ -109,7 +89,7 @@ To test out this example, I added these two entries to my "hosts" file: 127.0.0.1 forge.example.com ``` -# Routing Configuration +## Routing Configuration The routing configuration allows routing via a config file rather than a command. You need to set `-routes-config` or `ROUTES_CONFIG` env variable. @@ -125,9 +105,9 @@ The following shows a JSON file for routes config, where `default-server` can al } ``` -# Kubernetes Usage +## Kubernetes Usage -## Using Kubernetes Service auto-discovery +### Using Kubernetes Service auto-discovery When running `mc-router` as a Kubernetes Pod and you pass the `--in-kube-cluster` command-line argument, then it will automatically watch for any services annotated with - `mc-router.itzg.me/externalServerName` : The value of the annotation will be registered as the external hostname Minecraft clients would used to connect to the routed service. The service's clusterIP and target port are used as the routed backend. You can use more hostnames by splitting them with comma. @@ -165,7 +145,7 @@ metadata: mc-router will pick the service port named either `minecraft` or `mc-router`. If neither port names exist, it will use port value 25565. -## Example Kubernetes deployment +### Example Kubernetes deployment [This example deployment](docs/k8s-example-auto.yaml) * Declares an `mc-router` service that exposes a node port 25565 @@ -180,12 +160,12 @@ kubectl apply -f https://raw.githubusercontent.com/itzg/mc-router/master/docs/k8 ![](docs/example-deployment-auto.drawio.png) -#### Notes +##### Notes * This deployment assumes two persistent volume claims: `mc-stable` and `mc-snapshot` * I extended the allowed node port range by adding `--service-node-port-range=25000-32767` to `/etc/kubernetes/manifests/kube-apiserver.yaml` -#### Auto Scale Up +##### Auto Scale Up The `-auto-scale-up` flag argument makes the router "wake up" any stopped backend servers, by changing `replicas: 0` to `replicas: 1`. @@ -208,9 +188,9 @@ rules: verbs: ["watch","list","get","update"] ``` -# Docker Swarm Usage +## Docker Swarm Usage -## Using Docker Swarm Service auto-discovery +### Using Docker Swarm Service auto-discovery When running `mc-router` in a Docker Swarm environment you can pass the `--in-docker-swarm` command-line argument and it will poll the Docker API periodically to find all the running @@ -227,22 +207,105 @@ label on the service. These are the labels scanned: - `mc-router.network`: Specify the network you are using for the router if multiple are present in the service. You can either use the network ID, it's full name or an alias. -## Example Docker Swarm deployment +### Example Docker Swarm deployment Refer to [this example docker-compose.yml](docs/swarm.docker-compose.yml) to see how to configure two different Minecraft servers and a `mc-router` instance. Notice how you don't have to expose the Minecraft instances ports, but all the containers are required to be in the same network. -# Development +## REST API -## Building locally with Docker +* `GET /routes` (with `Accept: application/json`) + + Retrieves the currently configured routes + +* `POST /routes` (with `Content-Type: application/json`) + + Registers a route given a JSON body structured like: + ```json + { + "serverAddress": "CLIENT REQUESTED SERVER ADDRESS", + "backend": "HOST:PORT" + } + ``` + +* `POST /defaultRoute` (with `Content-Type: application/json`) + + Registers a default route to the given backend. JSON body is structured as: + ```json + { + "backend": "HOST:PORT" + } + ``` + +* `DELETE /routes/{serverAddress}` + + Deletes an existing route for the given `serverAddress` + +## ngrok + +mc-router has built-in support to run as an [ngrok agent](https://ngrok.com/docs/secure-tunnels/ngrok-agent/). To enable this support, pass [an ngrok authtoken](https://ngrok.com/docs/secure-tunnels/ngrok-agent/tunnel-authtokens/#per-agent-authtokens) to the command-line argument or environment variable, [shown above](#usage). + +### Ngrok Quick Start + +Create/access an ngrok account and [allocate an agent authtoken from the dashboard](https://dashboard.ngrok.com/tunnels/authtokens). + +In a new directory, create a file called `.env` with the allocated token + +```dotenv +NGROK_TOKEN=... +``` + +In the same directory, create the following compose file: + +```yaml +version: "3.8" + +services: + mc: + image: itzg/minecraft-server + environment: + EULA: true + volumes: + - mc-data:/data + # No port mapping since mc-router connects over compose network + router: + image: itzg/mc-router + environment: + DEFAULT: mc:25565 + NGROK_TOKEN: ${NGROK_TOKEN} + # No port mapping needed since it routes through ngrok tunnel + +volumes: + mc-data: {} +``` + +Start the compose project: + +```shell +docker compose up -d +``` + +Grab the mc-router logs using: + +```shell +docker compose logs router +``` + +From those logs, locate the `ngrokUrl` parameter from the "Listening" info log message, such as `tcp://8.tcp.ngrok.io:99999`. + +In the Minecraft client, the server address will be the part after the "tcp://" prefix, such as `8.tcp.ngrok.io:99999`. + +## Development + +### Building locally with Docker ```bash docker build -t mc-router . ``` -## Build locally without Docker +### Build locally without Docker After [installing Go](https://go.dev/doc/install) and doing a `go mod download` to install all required prerequisites, just like the [Dockerfile](Dockerfile) does, you can: @@ -251,7 +314,7 @@ make test # go test -v ./... go build ./cmd/mc-router/ ``` -## Skaffold +### Skaffold For "in-cluster development" it's convenient to use https://skaffold.dev. Any changes to Go source code will trigger a go build, new container image pushed to registry with a new tag, and refresh in Kubernetes @@ -265,7 +328,7 @@ then add the _Artifact Registry Reader_ Role to the _Compute Engine default serv then use e.g. `gcloud auth configure-docker europe-docker.pkg.dev` or equivalent one time (to create a `~/.docker/config.json`), and then use e.g. `--default-repo=europe-docker.pkg.dev/YOUR-PROJECT/YOUR-ARTIFACT-REGISTRY` option for `skaffold dev`. -## Performing snapshot release with Docker +### Performing snapshot release with Docker ```bash docker run -it --rm \ @@ -275,6 +338,6 @@ docker run -it --rm \ release --snapshot --rm-dist ``` -# Related Projects +## Related Projects * https://github.com/haveachin/infrared diff --git a/cmd/mc-router/main.go b/cmd/mc-router/main.go index 4340c6c..fbda6ef 100644 --- a/cmd/mc-router/main.go +++ b/cmd/mc-router/main.go @@ -31,6 +31,7 @@ type MetricsBackendConfig struct { type Config struct { Port int `default:"25565" usage:"The [port] bound to listen for Minecraft client connections"` + Default string `usage:"host:port of a default Minecraft server to use when mapping not found"` Mapping []string `usage:"Comma-separated or repeated mappings of externalHostname=host:port"` ApiBinding string `usage:"The [host:port] bound for servicing API requests"` Version bool `usage:"Output version and exit"` @@ -46,7 +47,8 @@ type Config struct { MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb"` UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"` MetricsBackendConfig MetricsBackendConfig - RoutesConfig string `usage:"Name or full path to routes config file"` + RoutesConfig string `usage:"Name or full path to routes config file"` + NgrokToken string `usage:"If set, an ngrok tunnel will be established. It is HIGHLY recommended to pass as an environment variable."` SimplifySRV bool `default:"false" usage:"Simplify fully qualified SRV records for mapping"` } @@ -109,11 +111,17 @@ func main() { } server.Routes.RegisterAll(parseMappings(config.Mapping)) + if config.Default != "" { + server.Routes.SetDefaultRoute(config.Default) + } if config.ConnectionRateLimit < 1 { config.ConnectionRateLimit = 1 } connector := server.NewConnector(metricsBuilder.BuildConnectorMetrics(), config.UseProxyProtocol) + if config.NgrokToken != "" { + connector.UseNgrok(config.NgrokToken) + } err = connector.StartAcceptingConnections(ctx, net.JoinHostPort("", strconv.Itoa(config.Port)), config.ConnectionRateLimit, diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/docs/ngrok/docker-compose.yml b/docs/ngrok/docker-compose.yml new file mode 100644 index 0000000..c29f608 --- /dev/null +++ b/docs/ngrok/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3.8" + +services: + mc: + image: itzg/minecraft-server + environment: + EULA: true + volumes: + - mc-data:/data + # No port mapping since mc-router connects over compose network + router: + image: itzg/mc-router + environment: + DEFAULT: mc:25565 + NGROK_TOKEN: ${NGROK_TOKEN} + # No port mapping needed since it routes through ngrok tunnel + +volumes: + mc-data: {} \ No newline at end of file diff --git a/go.mod b/go.mod index 6ef8e85..e9a46bd 100644 --- a/go.mod +++ b/go.mod @@ -12,12 +12,25 @@ require ( github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.3 + golang.ngrok.com/ngrok v1.4.0 golang.org/x/text v0.11.0 k8s.io/api v0.18.5 k8s.io/apimachinery v0.18.5 k8s.io/client-go v0.18.5 ) +require ( + github.com/go-stack/stack v1.8.1 // indirect + github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible // indirect + github.com/inconshreveable/log15/v3 v3.0.0-testing.5 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.ngrok.com/muxado/v2 v2.0.0 // indirect +) + require ( github.com/Microsoft/go-winio v0.5.2 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect @@ -30,7 +43,7 @@ require ( github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.6 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/googleapis/gnostic v0.2.0 // indirect @@ -38,25 +51,22 @@ require ( github.com/iancoleman/strcase v0.2.0 // indirect github.com/imdario/mergo v0.3.7 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kr/text v0.2.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.6 // indirect - google.golang.org/protobuf v1.27.1 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 5d218bf..9743100 100644 --- a/go.sum +++ b/go.sum @@ -19,7 +19,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -50,6 +49,8 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -71,8 +72,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -94,16 +95,23 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible h1:zaX5fYT98jX5j4UhO/WbfY8T1HkgVrydiDMC9PWqGCo= +github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= +github.com/inconshreveable/log15/v3 v3.0.0-testing.5 h1:h4e0f3kjgg+RJBlKOabrohjHe47D3bbAB9BgMrc3DYA= +github.com/inconshreveable/log15/v3 v3.0.0-testing.5/go.mod h1:3GQg1SVrLoWGfRv/kAZMsdyU5cp8eFc1P3cw+Wwku94= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/itzg/go-flagsfiller v1.12.0 h1:LSwSUGxzZqueprm0D8FBCAG0JMgwAkkh2UjtwreNgAg= github.com/itzg/go-flagsfiller v1.12.0/go.mod h1:47WeO9fl+QyS48AdRHfarhF3rKBh0enbe90tTko03gg= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -115,11 +123,16 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -133,8 +146,6 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -170,13 +181,19 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.ngrok.com/muxado/v2 v2.0.0 h1:bu9eIDhRdYNtIXNnqat/HyMeHYOAbUH55ebD7gTvW6c= +golang.ngrok.com/muxado/v2 v2.0.0/go.mod h1:wzxJYX4xiAtmwumzL+QsukVwFRXmPNv86vB8RPpOxyM= +golang.ngrok.com/ngrok v1.4.0 h1:QhUJ2jZr1xyf80zFLJuUsdc8exf3fVebQgbvOyVSbbk= +golang.ngrok.com/ngrok v1.4.0/go.mod h1:8a8GVoqR305t0O51ld211Xq2UeKgm32o8px24ddvXZI= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -196,8 +213,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -222,10 +239,11 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -251,7 +269,6 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -265,12 +282,11 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= diff --git a/server/connector.go b/server/connector.go index d09fcc6..a8437fa 100644 --- a/server/connector.go +++ b/server/connector.go @@ -3,6 +3,8 @@ package server import ( "bytes" "context" + "golang.ngrok.com/ngrok" + "golang.ngrok.com/ngrok/config" "io" "net" "strconv" @@ -46,16 +48,32 @@ type Connector struct { activeConnections int32 connectionsCond *sync.Cond + ngrokToken string } func (c *Connector) StartAcceptingConnections(ctx context.Context, listenAddress string, connRateLimit int) error { - ln, err := net.Listen("tcp", listenAddress) - if err != nil { - logrus.WithError(err).Fatal("Unable to start listening") - return err + var ln net.Listener + var err error + if c.ngrokToken != "" { + ngrokTun, err := ngrok.Listen(ctx, + config.TCPEndpoint(), + ngrok.WithAuthtoken(c.ngrokToken), + ) + if err != nil { + logrus.WithError(err).Fatal("Unable to start ngrok tunnel") + return err + } + ln = ngrokTun + logrus.WithField("ngrokUrl", ngrokTun.URL()).Info("Listening for Minecraft client connections via ngrok tunnel") + } else { + ln, err = net.Listen("tcp", listenAddress) + if err != nil { + logrus.WithError(err).Fatal("Unable to start listening") + return err + } + logrus.WithField("listenAddress", listenAddress).Info("Listening for Minecraft client connections") } - logrus.WithField("listenAddress", listenAddress).Info("Listening for Minecraft client connections") go c.acceptConnections(ctx, ln, connRateLimit) @@ -322,3 +340,7 @@ func (c *Connector) pumpFrames(incoming io.Reader, outgoing io.Writer, errors ch errors <- io.EOF } } + +func (c *Connector) UseNgrok(token string) { + c.ngrokToken = token +}