openziti / ziti

The parent project for OpenZiti. Here you will find the executables for a fully zero trust, application embedded, programmable network @OpenZiti
https://openziti.io
Apache License 2.0
2.24k stars 130 forks source link

How to get UDP request source address #1506

Open cctomato opened 7 months ago

cctomato commented 7 months ago

I have a usage scenario for device discovery where the requester and device communicate using the UDP protocol.

image

As shown in the figure, the requester(192.168.2.x) sends a udp request to the device(192.168.3.20), and the device needs to reply to the message through the udp request. However, the device does not know which requester sent the request.

It can only obtain the ip address of the Linux server(192.168.3.10)

So the device cannot return data to the requester.

I want to know how to obtain the source IP address of the requester (192.168.2. x). Or is it necessary to establish a service configuration from the device to the requester for communication.

Please help me! Thanks!

scareything commented 7 months ago

Hello and thanks for the insightful question!

Eventually it will help to know exactly which ziti tunnel client you are using (ziti-edge-tunnel? ziti-router with tunneler enabled?) and your current service configurations, but I can start by explaining the features that make it possible to project source IP.

Are you aware of the sourceIp field in the intercept configuration? This is certainly the first step in controlling the source IP that is used when the tunneler client on the Linux server connects to the device. Here's an example intercept configuration that sets sourceIp:

ziti edge create config myservice.intercept intercept.v1 '{ "addresses": [ "192.168.3.20" ], "protocols": [ "udp" ], "portRanges": [ { "low": 5000, "high": 6000 } ], "sourceIp": "$tunneler_id.name" }'

You can put any IP address[:port] in sourceIp, but usually you'll want to use the following variables so you don't need a separate service and intercept configuration for each client:

variable description
tunneler_id.name the name of the openziti identity that the intercepting tunneler is using. this obviously must be a dotted-decimal IP address for the feature to work.
src_ip / src_port the actual source IP and/or port of the UDP or TCP client that got intercepted. this variable is really only useful if the intercepting tunneler is acting as a gateway for other hosts on a LAN. if connections are being intercepted from processes that are running on the same host as the intercepting tunneler, which I assume is your situation based on your diagram, then $src_ip will end up being the IP address that is assigned to the tun interface if ziti-edge-tunnel is the client ("100.64.0.1" by default) or the destination IP of the intercepted connection if you're using ziti-router or ziti-tunnel to intercept the connections.
dst_ip / dst_port the destination ip and/or port of the intercepted connection

You'll also need to whitelist any IPs that you want to project from the ziti tunnel client on the server. For this, the host.v1 configuration type supports an allowedSourceAddresses field. You can use a list of IPs and/or CIDRs for this:

ziti edge create config myservice.host host.v1 '{"address": "127.0.0.1", "protocol":"udp", "forwardPort":true, "allowedPortRanges":[{"low":5000,"high":6000}], "allowedSourceAddresses":["192.168.2.0/24"]}'

There are some caveats:

  1. For the time being you cannot use ziti-edge-tunnel on the server-side if you are using source IPs and want the return path to use that source IP. This is due to very technical reasons that are summarized in https://github.com/openziti/ziti-tunnel-sdk-c/issues/443. So you must use either an edge router (with tunneler features enabled), or ziti-edge-tunnel with zfw integration on the server-side.
  2. The IPs that you use as source IPs must be local addresses on the host that runs the server-side tunneler. This is a requirement of the underlying system call (bind) that is used to set the source address of the connection. The edge router automatically adds any addresses in the allowedSourceAddresses field to the lo interface. ziti-edge-tunnel expect that you will manage the local addresses manually.
cctomato commented 7 months ago

@scareything Thank you for your answer!

The requesters is a PC with Ziti's Windows client application(desktop-edge-win) installed. And the Ubuntu Linux Server installed with ziti-edge-tunnel.

create identity

# for Requester 192.168.2.2
ziti edge create identity user requester001 -o 001.jwt
# for Requester 192.168.2.3
ziti edge create identity user requester002 -o 002.jwt
# for Requester 192.168.2.4
ziti edge create identity user requester003 -o 003.jwt
# for Linux Server 192.168.3.10
ziti edge create identity user ubuntu001 -o ubuntu001.jwt
# add addtribute
ziti edge update identity requester001 -a requesters
ziti edge update identity requester002 -a requesters
ziti edge update identity requester003 -a requesters
ziti edge update identity ubuntu -a devices

intercept config

ziti edge create config device-intercept-config intercept.v1 '{"protocols": ["udp"], "addresses": ["192.168.3.20"], "portRanges": [{"low": 5000, "high": 5000}]}'

hostv1 config

ziti edge create config device-host-config host.v1 '{"forwardAddress": "true", "allowedAddresses": ["192.168.3.20"] ,"forwardPort":"true", "allowedPortRanges":[{"low":5000,"high":5000}],"forwardProtocol": "true","allowedProtocols":["udp"]}'

service config

ziti edge create service device-connect  -c device-intercept-config,device-host-config

bind polices

ziti edge create service-policy device-bind Bind --identity-roles "#devices" --service-roles '@device-connect' --semantic 'AnyOf'

dial polices

ziti edge create service-policy device-dial Dial --identity-roles "#requesters" --service-roles '@device-connect' --semantic 'AnyOf'

After completing the above settings, All requester can send a udp request to 192.168.3.20:5000 through its 3000 port, and the device's 5000 port can also receive this udp request. Now this device needs to send data to the requester 3000 port. The program on the device only knows that the source IP of the udp request is 192.168.3.10. Therefore, the device will send a udp request to 192.168.3.10:3000, the Linux Severs's 3000 port can get the udp request.

It is obvious that the requester's 3000 port will not receive this udp request.

Because there are multiple requesters, I don't know how to forward the udp requests received on the Linux server to the original requester's 3000 port.

scareything commented 7 months ago

Ok, so your service configurations will need to use the source address fields that I described in my previous message for the source IP to be used at the Linux server. You'll also need to name your identities as literal IP addresses for the "$tunneler_id.name" variable to work. Finally, you must use an edge router to do the tunneling on the Linux server.

If your requesting applications are setting their source port 3000 then the device will be able to return traffic through that port.