9 Commits
main ... v0.3.0

Author SHA1 Message Date
7138515904 ensure we're only logging rcon
(no query packets)
2024-03-08 21:42:39 +00:00
a01df2e905 only log client IP (remove port)
log game server address
2024-03-08 02:42:01 +00:00
d8015c921c add logrus dependency 2024-03-08 02:29:40 +00:00
a513c7ecc6 add new env var Q3RCON_HOST 2024-03-08 02:29:27 +00:00
6f49cc5b63 log rcon commands level INFO 2024-03-08 02:29:01 +00:00
88c90f1447 upd dockerfile 2024-01-29 11:53:36 +00:00
561b1ca654 Update README.md
expand readme
2024-01-28 03:34:28 +00:00
a3219e63c2 tests removed due to q3rcon changes 2024-01-27 23:12:26 +00:00
ceb87e14fd reword 2024-01-27 18:20:57 +00:00
9 changed files with 58 additions and 100 deletions

View File

@@ -13,7 +13,7 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
## [0.1.0] - 2024-01-27 ## [0.1.0] - 2024-01-27
- ignore any packets whose header does match a q3 rcon/query packet. - only forward packets if the header matches q3 rcon/query.
## [0.0.1] - 2024-01-27 ## [0.0.1] - 2024-01-27

View File

@@ -1,12 +1,14 @@
FROM golang:alpine FROM golang:1.21
WORKDIR /dist WORKDIR /usr/src/app
COPY . . # pre-copy/cache go.mod for pre-downloading dependencies and only redownloading them in subsequent builds if they change
COPY go.mod go.sum ./
# build binary and place into /usr/local/bin
RUN go mod download && go mod verify RUN go mod download && go mod verify
RUN go build -v -o /usr/local/bin/q3rcon-proxy ./cmd/q3rcon-proxy
# build binary and place into /usr/local/bin/
COPY . .
RUN go build -v -o /usr/local/bin/q3rcon-proxy ./cmd/q3rcon-proxy/
# Command to run when starting the container # Command to run when starting the container
ENTRYPOINT [ "q3rcon-proxy" ] ENTRYPOINT [ "q3rcon-proxy" ]

View File

@@ -12,13 +12,13 @@ for example:
export Q3RCON_PROXY="20000:28960;20001:28961;20002:28962" export Q3RCON_PROXY="20000:28960;20001:28961;20002:28962"
``` ```
This would run 3 proxy servers listening on ports `20000`, `20001` and `20002` that redirect rcon requests to game servers on ports `28960`, `28961` and `28962` respectively. This would configure q3rcon-proxy to run 3 proxy servers listening on ports `20000`, `20001` and `20002` that redirect rcon requests to game servers on ports `28960`, `28961` and `28962` respectively.
Then just run the binary which you can compile yourself, download from `Releases` or use the included Dockerfile.
### Why ### Why
Avoid sending plaintext rcon requests (that include the password) to public ports. Instead send them to whitelisted ports. Avoid sending plaintext rcon commands to the public game server port. In general I would advise anyone using rcon remotely to use a secured connection but perhaps you've passed rcon to a clan friend who doesn't know about secured connections. Now you can instruct them to use rcon only through a whitelisted port.
Gives you the option to disable remote rcon entirely and have the server accept requests only from localhost.
### Special Thanks ### Special Thanks

View File

@@ -15,22 +15,33 @@ func start(proxy string) {
return x[0], x[1] return x[0], x[1]
}() }()
c, err := udpproxy.New(fmt.Sprintf("0.0.0.0:%s", port), fmt.Sprintf("127.0.0.1:%s", target)) c, err := udpproxy.New(fmt.Sprintf("%s:%s", host, port), fmt.Sprintf("127.0.0.1:%s", target))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Printf("q3rcon-proxy initialized: [proxy] (0.0.0.0:%s) [target] (127.0.0.1:%s)", port, target) log.Printf("q3rcon-proxy initialized: [proxy] (%s:%s) [target] (127.0.0.1:%s)", host, port, target)
log.Fatal(c.ListenAndServe()) log.Fatal(c.ListenAndServe())
} }
func main() { var (
proxies := os.Getenv("Q3RCON_PROXY") proxies, host string
)
func init() {
proxies = os.Getenv("Q3RCON_PROXY")
if proxies == "" { if proxies == "" {
log.Fatal("env Q3RCON_PROXY required") log.Fatal("env Q3RCON_PROXY required")
} }
host = os.Getenv("Q3RCON_HOST")
if host == "" {
host = "0.0.0.0"
}
}
func main() {
for _, proxy := range strings.Split(proxies, ";") { for _, proxy := range strings.Split(proxies, ";") {
go start(proxy) go start(proxy)
} }

4
go.mod
View File

@@ -1,3 +1,7 @@
module github.com/onyx-and-iris/q3rcon-proxy module github.com/onyx-and-iris/q3rcon-proxy
go 1.18 go 1.18
require github.com/sirupsen/logrus v1.9.3
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect

15
go.sum
View File

@@ -0,0 +1,15 @@
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=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,9 +1,11 @@
package udpproxy package udpproxy
import ( import (
"log"
"net" "net"
"strings"
"time" "time"
log "github.com/sirupsen/logrus"
) )
type Session struct { type Session struct {
@@ -63,5 +65,11 @@ func (s *Session) proxyTo(buf []byte) error {
return err return err
} }
cmd := string(buf)
if cmd[:8] == "\xff\xff\xff\xffrcon" {
parts := strings.Split(cmd, " ")
log.Info("From [", s.caddr.IP, "] To [", s.serverConn.RemoteAddr().String(), "] Command: ", strings.Join(parts[2:], " "))
}
return nil return nil
} }

View File

@@ -1,10 +1,11 @@
package udpproxy package udpproxy
import ( import (
"log"
"net" "net"
"sync" "sync"
"time" "time"
log "github.com/sirupsen/logrus"
) )
type Client struct { type Client struct {

View File

@@ -1,83 +0,0 @@
package udpproxy
import (
"log"
"net"
"testing"
"time"
)
func TestSendAndReceive(t *testing.T) {
go runLilProxy()
go runUDPServer()
paddr, err := net.ResolveUDPAddr("udp", "localhost:9000")
if err != nil {
t.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, paddr)
if err != nil {
t.Fatal(err)
}
go func() {
for {
buf := make([]byte, 2048)
_, _, err = conn.ReadFromUDP(buf)
if err != nil {
log.Fatal(err)
}
log.Printf("response received: %s", string(buf))
}
}()
for {
time.Sleep(1 * time.Second)
_, err = conn.Write([]byte("hi\n"))
if err != nil {
log.Fatal(err)
}
}
}
func runLilProxy() {
port := ":9000"
target := "localhost:9001"
c, err := New(port, target)
if err != nil {
log.Fatal(err)
}
log.Fatal(c.ListenAndServe())
}
func runUDPServer() {
taddr, err := net.ResolveUDPAddr("udp", ":9001")
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", taddr)
if err != nil {
log.Fatal(err)
}
for {
buf := make([]byte, 2048)
_, caddr, err := conn.ReadFromUDP(buf)
if err != nil {
log.Fatal(err)
}
log.Printf("request received: %s", string(buf))
_, err = conn.WriteToUDP([]byte("bye\n"), caddr)
if err != nil {
log.Fatal(err)
}
}
}