0

I have a network interface with 5 public IPs. These IPs are working fine for incoming traffic. What I need to do is "force" outgoing traffic through specific IPs.

I also made a simple REST endpoint if you make a request it'll tell you your IP in the response body. E.g curl, or fetch, or any other HTTP request method:

curl https://enosqoiqvbk6q22.m.pipedream.net

In the real scenario I will see HTTP requests (as well as a websocket though that matters less) to the same URLs from different processes. These I want to isolate by process (either putting each process under a diff system user, or any other mechanism for doing this on the process level).

This is how the netplan config looks:

network:
  version: 2
  renderer: networkd
  ethernets:
   id0:
      match:
        macaddress: aa:aa:cb:4b:04:74
      addresses:
        - 123.123.12.102/29
        - 123.123.12.103/29
        - 123.123.12.104/29
        - 123.123.12.105/29
        - 123.123.12.106/29
      gateway4: 123.123.12.101
      nameservers:
        addresses: [8.8.8.8,4.2.2.2]

ip address returns the following:

# ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether aa:aa:cb:4b:04:74 brd ff:ff:ff:ff:ff:ff
    inet 123.123.12.102/29 brd 123.123.12.107 scope global eno1
       valid_lft forever preferred_lft forever
    inet 123.123.12.103/29 brd 123.123.12.107 scope global secondary eno1
       valid_lft forever preferred_lft forever
    inet 123.123.12.104/29 brd 123.123.12.107 scope global secondary eno1
       valid_lft forever preferred_lft forever
    inet 123.123.12.105/29 brd 123.123.12.107 scope global secondary eno1
       valid_lft forever preferred_lft forever
    inet 123.123.12.106/29 brd 123.123.12.107 scope global secondary eno1
       valid_lft forever preferred_lft forever
    inet6 fe80::7a2b:cbff:fe4b:474/64 scope link 
       valid_lft forever preferred_lft forever
3: eno2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether aa:aa:cb:4b:04:75 brd ff:ff:ff:ff:ff:ff

If I had physical network interfaces per IP, I thought I could use ip namespaces via ip netns bound to each interface, and ip netns exec ns_vlan02 ./someProcess, but doesn't seem to be an option with only one interface.

I'm completely open to making a user per IP, or doing this on the process level. I'm essentially looking for any viable route with what I have.

I saw mentions of iptables, but it seems that assumes processA or userA will always send requests to a different domain/ip than processB or userB. In my scenario requests are going to the same domain/ip (like that pipedream URL at the beginning).

Edit - discoveries so far:

Option 1 - per user iptables

https://serverfault.com/a/236724/621956

DNS was broken, but seems to work after specifying interface when setting up the rule!

-o en1

Option 2 - use containers (lxd/docker)

Might expose other control over how networking behaves for processes within, though the concern is that just introduces unnecessary complexity. Option 1 preferred if dns issue is resolved.

Other options?

How could I approach this? Thanks!

Tiago
  • 101
  • 2

1 Answers1

1

You may want to have a look at iptables-extensions(8):

iptables [-m name [module-options...]]  [-j target-name [target-options...]

...

owner
   This  module  attempts to match various characteristics of the packet
   creator, for locally generated packets. This match is only valid in the
   OUTPUT and POSTROUTING chains. Forwarded packets do not have any socket
   associated with them. Packets from kernel threads do have a socket, but
   usually no owner.

   [!] --uid-owner username

   [!] --uid-owner userid[-userid]
          Matches if the packet socket's file structure (if it has one) is
          owned by the given user. You may also specify a numerical UID,
          or an UID range.

   [!] --gid-owner groupname

   [!] --gid-owner groupid[-groupid]
          Matches if the packet socket's file structure is owned by the
          given group.  You may also specify a numerical GID, or a GID
          range.

   [!] --socket-exists
          Matches if the packet is associated with a socket.

Edit: Sorry I just saw that this was your option 1.

Mathias Weidner
  • 417
  • 3
  • 10