R
Railway•10mo ago
alexbalandi

Listen on udp workaround?

I understand that railway doesn't support it out of the box, but what could be the workaround? I have tinkered with this repo quite a bit : https://github.com/mmmaxwwwell/space-engineers-dedicated-docker-linux The only thing stopping it from actually working on railway rn (aside from 5 gb limit on volume, which would need to be 10 gb for it to run on my personal hobby plan and not as one of team projects, lol) is well, the fact, that server needs to support udp 😦 I think someone mentioned using proxy for that?
GitHub
GitHub - mmmaxwwwell/space-engineers-dedicated-docker-linux: Space ...
Space Engineers Dedicated Server running in Docker for Linux - GitHub - mmmaxwwwell/space-engineers-dedicated-docker-linux: Space Engineers Dedicated Server running in Docker for Linux
22 Replies
Percy
Percy•10mo ago
Project ID: N/A
alexbalandi
alexbalandi•10mo ago
N/A @emma you said in the other branch "eh, i guess you could if you ran your own service outside of railway to convert back to udp", I would really appreciate if you could give me a few pointers 👀
emma
emma•10mo ago
LOL i said possible, not advisable. this is not a smart idea
alexbalandi
alexbalandi•10mo ago
How awful would that be in terms of liliderp
emma
emma•10mo ago
not…. good
alexbalandi
alexbalandi•10mo ago
any ideas for workaround that would work better? ;_;
emma
emma•10mo ago
don’t use railway?
alexbalandi
alexbalandi•10mo ago
(except "nag on railway team untill they implement it")
emma
emma•10mo ago
you also probably want persistent storage for a game server, no?
alexbalandi
alexbalandi•10mo ago
well, attached volume works, so that can be done on railway it is like 7.5 Gb not much in terms of costs, I think? but out of hobby plan, which is eugh For now, I just want to essentially do this as POC. I mean, railway end goal, as I understand it, is to support this kind of thing too And I do like the platform So I'd like to be able to host whatever comes to my mind (and not against ToS) here NinoFace
emma
emma•10mo ago
i mean it’s definitely feasible, but it’s not what railway seems to be designed with in mind
alexbalandi
alexbalandi•10mo ago
GitHub
GitHub - mullvad/udp-over-tcp: Proxy UDP traffic over a TCP stream
Proxy UDP traffic over a TCP stream. Contribute to mullvad/udp-over-tcp development by creating an account on GitHub.
alexbalandi
alexbalandi•10mo ago
Looking at this I could potentially just plug it as daemon run service into my container, I think No need for external services. So one [Udp2Tcp] instance can handle traffic from a single peer only. this is not pretty though 🤔 If railway allowed multiple listen ports, the easiest (although super clunky) solution would be just to attach a separate instance to each of them, which is good enough for like server for 4 friends. With the way it is now, hmmm. I think still doable, but too much work I guess. I'll still test if it works for single connection some time later.
emma
emma•10mo ago
if you wanted to bypass railway’s http forwarding you could run a vpn on an external device that forwards to the server running on railway you’d have to do some fun network finagling to get the server to see the correct remote ips or you could just run tailscale (or something similar) on each client and have a separate vpn connection per client
BrianJM
BrianJM•10mo ago
I am working on a template that creates a VPC with tailscale (a "subnet router"), resolves Railway IP Addresses from any device running tailscale, and routes traffic through each subnet router. This does not allow Railway services without tailscale to connect to other Railway private environments/services, but that could be done easily by adding tailscale to the service (or adding routes). The intent is to allow remote administration of all Railway services, and provide a VPC to serve truly private services (that will never be public). Each environment runs a single service/node with tailscale, and the same service runs dnsmasq. A background script calls the Railway Api to get the IPv6 addresses and create an alternate hosts file that dnsmasq reads (with custom domain like *.prod.alt). The tailscale DNS servers are added to tailscale so any device running tailscale can connect to the Railway environment (that the subnet router is in). I intend to publish it as some point, but the code is not clean at the moment. Tailscale runs in userspace networking, so UDP doesn't work as expected. I tried to mesh together DNS servers (in each environment) that would query each other (and obtain the correct DNS records from the Railway DNS) but I did get stuck on UDP traffic. I tried a custom build of socat (with socks5), tcp2udp, and udp2tcp, and a variety of each. I had varying levels of success, but ultimately felt I ran into issues related to conversion of UDP/TCP traffic and forking; that is, I could send and receive, but the packets were malformed and terminated prematurely. Are you trying to route UDP over TCP or TCP over UDP?
alexbalandi
alexbalandi•10mo ago
This is a good question, because I'm not quite sure about the internals of space engineers, but I assume I need both. I need first to listen to incoming udp traffic and then send udp traffic from my server
BrianJM
BrianJM•10mo ago
Here's what I mean. The WP test instance is not running tailscale.
No description
BrianJM
BrianJM•10mo ago
Listening over UDP via public network a problem. You'll need to listen over TCP and covert to UDP, or use tailscale (and still need to convert). If you're using tailscale, you may need a custom build of socat (with socks5 support) and udp2tcp/tcp2udp. Due to userspace networking, you have to route UDP traffic over SOCKS5 (socat) and you may need to use tcp2udp/udp2tcp to convert between TCP and UDP (on both ends). It's complicated and not really an area I want to become an expert in, which is why I gave up; for my use cases, there are workarounds. From my dockerfile:
FROM alpine:latest as builder
WORKDIR /app
COPY . ./

# More stuff ...

# **** udp2tcp / tcp2udp
FROM rust:alpine as rbuilder
ENV SOURCE_FILE=v0.3.0.tar.gz
WORKDIR /app/tmp/udp
RUN apk update && \
apk --no-cache add \
libc-dev \
tree
RUN wget https://github.com/mullvad/udp-over-tcp/archive/refs/tags/${SOURCE_FILE} \
&& tar xzf ${SOURCE_FILE} --strip-components=1
RUN rustup target add x86_64-unknown-linux-gnu
RUN sh build-static-bins.sh
RUN tree target/x86_64-unknown-linux-gnu/release/
RUN rm -rf /var/cache/apk/*

# More stuff ...

**** Tailscale
FROM builder as pbuilder
ENV TSFILE=tailscale_1.48.1_amd64.tgz
WORKDIR /app
RUN apk update

# socat
RUN apk --no-cache add \
libcrypto1.1
RUN wget "https://git.rlab.io/forks/socat/uploads/9ac6cf806c236df94f0624fb66cf7424/socat-1.7.4.3-r1.apk" && \
apk --no-cache --allow-untrusted add socat-1.7.4.3-r1.apk && \
rm socat-1.7.4.3-r1.apk

# tailscale
RUN apk --no-cache add \
ca-certificates \
iptables \
ip6tables
WORKDIR /app/tmp/ts
RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && \
tar xzf ${TSFILE} --strip-components=1
RUN rm -rf /var/cache/apk/*

# More stuff...

# **** Copy binary to production image
FROM pbuilder as ebuilder
WORKDIR /app
COPY --from=rbuilder /app/tmp/udp/target/x86_64-unknown-linux-gnu/release/tcp2udp /usr/local/bin/tcp2udp
COPY --from=rbuilder /app/tmp/udp/target/x86_64-unknown-linux-gnu/release/udp2tcp /usr/local/bin/udp2tcp
COPY --from=pbuilder /app/tmp/ts/tailscaled /app/tailscaled
COPY --from=pbuilder /app/tmp/ts/tailscale /app/tailscale

RUN chmod +x /app/start.sh

ENV ENABLE_ALPINE_PRIVATE_NETWORKING=true

# Run on container startup.
ENTRYPOINT ["/app/start.sh"]
FROM alpine:latest as builder
WORKDIR /app
COPY . ./

# More stuff ...

# **** udp2tcp / tcp2udp
FROM rust:alpine as rbuilder
ENV SOURCE_FILE=v0.3.0.tar.gz
WORKDIR /app/tmp/udp
RUN apk update && \
apk --no-cache add \
libc-dev \
tree
RUN wget https://github.com/mullvad/udp-over-tcp/archive/refs/tags/${SOURCE_FILE} \
&& tar xzf ${SOURCE_FILE} --strip-components=1
RUN rustup target add x86_64-unknown-linux-gnu
RUN sh build-static-bins.sh
RUN tree target/x86_64-unknown-linux-gnu/release/
RUN rm -rf /var/cache/apk/*

# More stuff ...

**** Tailscale
FROM builder as pbuilder
ENV TSFILE=tailscale_1.48.1_amd64.tgz
WORKDIR /app
RUN apk update

# socat
RUN apk --no-cache add \
libcrypto1.1
RUN wget "https://git.rlab.io/forks/socat/uploads/9ac6cf806c236df94f0624fb66cf7424/socat-1.7.4.3-r1.apk" && \
apk --no-cache --allow-untrusted add socat-1.7.4.3-r1.apk && \
rm socat-1.7.4.3-r1.apk

# tailscale
RUN apk --no-cache add \
ca-certificates \
iptables \
ip6tables
WORKDIR /app/tmp/ts
RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && \
tar xzf ${TSFILE} --strip-components=1
RUN rm -rf /var/cache/apk/*

# More stuff...

# **** Copy binary to production image
FROM pbuilder as ebuilder
WORKDIR /app
COPY --from=rbuilder /app/tmp/udp/target/x86_64-unknown-linux-gnu/release/tcp2udp /usr/local/bin/tcp2udp
COPY --from=rbuilder /app/tmp/udp/target/x86_64-unknown-linux-gnu/release/udp2tcp /usr/local/bin/udp2tcp
COPY --from=pbuilder /app/tmp/ts/tailscaled /app/tailscaled
COPY --from=pbuilder /app/tmp/ts/tailscale /app/tailscale

RUN chmod +x /app/start.sh

ENV ENABLE_ALPINE_PRIVATE_NETWORKING=true

# Run on container startup.
ENTRYPOINT ["/app/start.sh"]
And start.sh:
#!/bin/sh

# Wait for interface
while ! /sbin/ip -6 route show via fd12::10 | grep -F "::/64 dev railnet0 metric 1024 onlink" > /dev/null; do
sleep 0.1
done

SUBNET=$(/sbin/ip -6 route show via fd12::10 | awk '/fd12:\w{1,4}:\w{1,4}::\/64/ {print $1}')

/app/tailscaled \
--state=/var/lib/tailscale/tailscaled.state --socket=/var/run/tailscale/tailscaled.sock --tun=userspace-networking --socks5-server=localhost:1055 &
until /app/tailscale up --authkey=${TAILSCALE_AUTHKEY} --hostname=${RAILWAY_PROJECT_NAME}.${RAILWAY_ENVIRONMENT_NAME} --advertise-tags=tag:rw-subnet --advertise-routes=$SUBNET --snat-subnet-routes=true --accept-routes=true --accept-dns=true
do
sleep 0.1
done

tailscale_ip=$(/app/tailscale ip -4)
tailscale_ip6=$(/app/tailscale ip -6)
echo "Tailscale is up at IPv4 ${tailscale_ip}"
echo "Tailscale is up at IPv6 ${tailscale_ip6}"
echo "Tailscale status:" & \
/app/tailscale status
echo "Tailscale netcheck:" & \
/app/tailscale netcheck


sleep infinity
#!/bin/sh

# Wait for interface
while ! /sbin/ip -6 route show via fd12::10 | grep -F "::/64 dev railnet0 metric 1024 onlink" > /dev/null; do
sleep 0.1
done

SUBNET=$(/sbin/ip -6 route show via fd12::10 | awk '/fd12:\w{1,4}:\w{1,4}::\/64/ {print $1}')

/app/tailscaled \
--state=/var/lib/tailscale/tailscaled.state --socket=/var/run/tailscale/tailscaled.sock --tun=userspace-networking --socks5-server=localhost:1055 &
until /app/tailscale up --authkey=${TAILSCALE_AUTHKEY} --hostname=${RAILWAY_PROJECT_NAME}.${RAILWAY_ENVIRONMENT_NAME} --advertise-tags=tag:rw-subnet --advertise-routes=$SUBNET --snat-subnet-routes=true --accept-routes=true --accept-dns=true
do
sleep 0.1
done

tailscale_ip=$(/app/tailscale ip -4)
tailscale_ip6=$(/app/tailscale ip -6)
echo "Tailscale is up at IPv4 ${tailscale_ip}"
echo "Tailscale is up at IPv6 ${tailscale_ip6}"
echo "Tailscale status:" & \
/app/tailscale status
echo "Tailscale netcheck:" & \
/app/tailscale netcheck


sleep infinity
alexbalandi
alexbalandi•10mo ago
Yikes 😅 Thank you for sharing, I think i'll try this thing first and if it doesn't work, I'll probably drop the idea too
BrianJM
BrianJM•10mo ago
This is from my desktop which is connected to Tailscale. Two tailscale devices (self + subnet router) that can resolve IPv6 and connect to other services within the railway private network (WP does not have tailscale installed). Ping is from East coast to West coast (USA) using Tailscale DERP servers. Ping is typically around ~80-85ms, which about double the ping to the production URL (because that's on an edge network and located on the East coast); the ping is roughly the same as a speed test. All that to say, the ping is exactly that it should be using tailscale and I have not found issues with throughput.
No description
No description
No description
Dayblox
Dayblox•9mo ago
Can you guys share how one would run a tailscale node as a subnet router since private networking is done via domains and not ip?
BrianJM
BrianJM•9mo ago
Most of what is necessary is posted above but its not a full working solution. Did you try that?