Make host service being client too

Hi, I'm back again! :joy::joy:

In a docker compose project i've the following configuration (copying only the rilevant part):

services:
  plcsiemens:
    profiles:
      - host-siemens
    build:
      context: ./PLCsiemens
      dockerfile: Dockerfile
    container_name: ${PLCSIEMENS_CONTAINER_NAME:-plcsiemens}
    environment:
      PLCSIEMENS_PORT: ${PLCSIEMENS_PORT:-102}
      PLCOMRON_ADDRESS: ${PLCOMRON_ADDRESS:-192.168.3.1}
      PLCOMRON_PORT: ${PLCOMRON_PORT:-9600}
      PROCESSING_FAILURE_RATE: ${PROCESSING_FAILURE_RATE:-0.15}
      QUALITY_ASSURANCE_FAILURE_RATE: ${QUALITY_ASSURANCE_FAILURE_RATE:-0.18}
      DISCARDING_OR_SENDING_FAILURE_RATE: ${DISCARDING_OR_SENDING_FAILURE_RATE:-0.11}
      DEFECT_RATE: ${DEFECT_RATE:-0.24}
      MEMORY_AREA_SIZE: ${MEMORY_AREA_SIZE:-8}
      DATA_BLOCK_NUMBER: ${DATA_BLOCK_NUMBER:-5}
      ZITI_PLCSIEMENS_TUNNELER_ADDRESS: ${ZITI_PLCSIEMENS_TUNNELER_ADDRESS:-172.19.0.3}
      SLEEP_TIME: ${SLEEP_TIME:-0.95}
    networks:
      testnet:
        ipv4_address: ${PLCSIEMENS_ADDRESS:-172.19.1.1}
    command: ["python3", "PLCsiemens.py"]

  hmisiemens:
    profiles:
      - client-siemens
    build:
      context: ./HMIsiemens
      dockerfile: Dockerfile
    stdin_open: true
    tty: true
    depends_on:
      ziti-hmisiemens-router:
        condition: service_healthy
    environment:
      PLCSIEMENS_ADDRESS: ${PLCSIEMENS_ADDRESS:-10.11.12.13}
      PLCSIEMENS_PORT: ${PLCSIEMENS_PORT:-102}
      PLCSIEMENS_RACK: ${PLCSIEMENS_RACK:-0}
      PLCSIEMENS_SLOT: ${PLCSIEMENS_SLOT:-1}
    container_name: ${HMISIEMENS_CONTAINER_NAME:-hmisiemens}
    network_mode: service:ziti-hmisiemens-router
    command: ["python3", "HMIsiemens.py"]

  ziti-plcsiemens-tunneler:
      profiles:
          - host-siemens
      image: openziti/ziti-host
      container_name: ziti-plcsiemens-tunneler
      networks:
          testnet:
              ipv4_address: ${ZITI_PLCSIEMENS_TUNNELER_ADDRESS:-172.19.0.3}
      volumes:
          - ziti-plcsiemens-tunneler:/ziti-edge-tunnel
      environment:
          - ZITI_ENROLL_TOKEN

ziti-hmisiemens-router:
        profiles:
            - client-siemens
        image: openziti/ziti-router:1.1.9
        container_name: ziti-hmisiemens-router
        expose:
            - 3022
        networks:
            testnet:
                ipv4_address: ${HMI_SIEMENS_ADDRESS:-172.19.1.2}
        environment:
            ZITI_CTRL_ADVERTISED_ADDRESS: ziti-controller
            ZITI_ENROLL_TOKEN:
            ZITI_ROUTER_MODE: tproxy
        volumes:
            - ziti-hmisiemens-router:/ziti-router
        dns:
            - 127.0.0.1
            - 1.1.1.1
        user: root
        cap_add:
            - NET_ADMIN
        healthcheck:
            test:
                - CMD
                - ziti
                - agent
                - stats
            interval: 3s
            timeout: 3s
            retries: 5
            start_period: 30s

I need PLCsiemens container to be client (so with his own intercept.v1 tunneler) of a new service called PLComron (with his own host.v1 tunneler). The problem is: in docker networking, containers cannot have multiple network interfaces in the same network. How can I reach the goal?

My idea was to use a second subnet only for this purpose and make PLCsiemens to be part of both, but for the sidecar configuration I need network_mode: service:ziti-router-sidecar and that's not feasible to be on two networks. How can I configure a classic tunneler?

Thanks for the help! :pray:

I'm not quite understanding the problem. Looking at the compose, it would seem like the easy answer is to deploy a second router and assign it to the plcsiemens service? Did you reject that idea? I assume so?

In these situations a diagram would probably be helpful. I don't see where PLComron would be deployed?

A classic tunneler would work for the whole host OS - i don't think that's what you want? I think if we start with a diagram please, that'd be most helpful.

EDIT:
And welcome back :slight_smile:

1 Like

I relized that i was too much biref, sorry!!

That's the diagram:

The particular thing is that PLComron is not deployed, i've a real PLC connected by host's ethernet interface (communication from PLCsiemens container to the real PLC is not a problem).

The question is: how can I create and configure the ziti entities below of the diagram?

The first thing i wanted to do was another sidecar config (taking inspiration from the HMIsiemens-PLCsiemens one), but docker networking do not allow to do that.

Hi again :wave:

Yes, that will work. The main change is the PLC is becoming a Ziti client too, like the HMI, but for a different Ziti service.

You can use a router sidecar for the PLC to provide both functions:

  1. host.v1 for PLC SIEMENS with a target address like 127.0.0.1:102 (TCP)
  2. intercept.v1 for the PLC OMRON

This means the PLC SIEMENS node will need only the router sidecar for both Ziti services and functions as a server-side host for one and client-side dialer for the other.

I shared a live collaboration diagram link with you in a private message. Will you see if it makes sense and edit at will to reflect your aim?

1 Like

In makes absolutly sense!
While driving I was thinking about the problem and I relized probably I gave up too much easily! In fact, i thought to simply put the sidecar near to PLCsiemens and, because of the host.v1 working on IP addresses, all should work fine after some little changes.

Is it like the solution you suggested, isn’t it?

Thanks for all the amazing support :muscle:t3:

EDIT: sorry if i can’t edit the diagram but i’m from mobile right now!
Anyway it’s exactly what i need, so i need only to understand well what you suggested, because reading again it seems like I can use the sidecar router as both Host and Intercept! That’s even better and easier!!!!
Again: i gave up too much easily! :man_facepalming:t3::man_facepalming:t3:

1 Like

Great! Yes, I would change the host.v1 address like 127.0.0.1 on TCP port 102 for PLC SIEMENS because the packets will arrive from loopback shared with the new ziti-router sidecar.

1 Like

I’ll configure the router for both purposes simply through commands in the ziti-controller, or I need some extra configurations?

Each service must match a Dial service policy and Bind service policy. In my diagram, the identity of type "Router" with name "ziti-router2" must match a Dial service policy giving it permission to dial the the PLC OMRON service and a Bind service policy giving it permission to host the PLC SIEMENS service.

1 Like

Nothing more nothing less, clear!

Guys I just want you to know that’s not easy to find forums helpful and kind like this one. :pray:t3:
Thanks a lot for the help hoping we’ll listen another time but for a new project :joy::joy:

Tomorrow (in italy) we’ll see :man_shrugging:t3:

You're welcome and good luck. Let us know how it goes.

I'm in trouble while trying to configure the sidecar router for PLCsiemens. First step is to substitute the host tunneler with the sidecar and it doesn't work. I've tried the following:
PLCsiemens compose service:

services:
  plcsiemens:
    profiles:
      - host-siemens
    build:
      context: ./PLCsiemens
      dockerfile: Dockerfile
    container_name: ${PLCSIEMENS_CONTAINER_NAME:-plcsiemens}
    environment:
      PLCSIEMENS_PORT: ${PLCSIEMENS_PORT:-102}
      PLCOMRON_ADDRESS: ${PLCOMRON_ADDRESS:-192.168.3.1}
      PLCOMRON_PORT: ${PLCOMRON_PORT:-9600}
      PROCESSING_FAILURE_RATE: ${PROCESSING_FAILURE_RATE:-0.15}
      QUALITY_ASSURANCE_FAILURE_RATE: ${QUALITY_ASSURANCE_FAILURE_RATE:-0.18}
      DISCARDING_OR_SENDING_FAILURE_RATE: ${DISCARDING_OR_SENDING_FAILURE_RATE:-0.11}
      DEFECT_RATE: ${DEFECT_RATE:-0.24}
      MEMORY_AREA_SIZE: ${MEMORY_AREA_SIZE:-8}
      DATA_BLOCK_NUMBER: ${DATA_BLOCK_NUMBER:-5}
      ZITI_PLCSIEMENS_TUNNELER_ADDRESS: ${ZITI_PLCSIEMENS_TUNNELER_ADDRESS:-172.19.0.3}
      SLEEP_TIME: ${SLEEP_TIME:-0.95}
      network_mode: service:ziti-plcsiemens-sidecar
    command: ["python3", "PLCsiemens.py"]

  hmisiemens:
    profiles:
      - client-siemens
    build:
      context: ./HMIsiemens
      dockerfile: Dockerfile
    stdin_open: true
    tty: true
    depends_on:
      ziti-hmisiemens-router:
        condition: service_healthy
    environment:
      PLCSIEMENS_ADDRESS: ${PLCSIEMENS_ADDRESS:-10.11.12.13}
      PLCSIEMENS_PORT: ${PLCSIEMENS_PORT:-102}
      PLCSIEMENS_RACK: ${PLCSIEMENS_RACK:-0}
      PLCSIEMENS_SLOT: ${PLCSIEMENS_SLOT:-1}
    container_name: ${HMISIEMENS_CONTAINER_NAME:-hmisiemens}
    network_mode: service:ziti-hmisiemens-router
    command: ["python3", "HMIsiemens.py"]

Sidecar compose service:

ziti-plcsiemens-sidecar:
       profiles:
           - host-siemens
       image: openziti/ziti-router:1.1.9
       container_name: ziti-plcsiemens-sidecar
       expose:
           - 3022
       networks:
           testnet:
               ipv4_address: ${PLCSIEMENS_ADDRESS:-172.19.1.1}
       environment:
           ZITI_CTRL_ADVERTISED_ADDRESS: ziti-controller
           ZITI_ENROLL_TOKEN:
           ZITI_ROUTER_MODE: tproxy
       volumes:
           - ziti-plcsiemens-sidecar:/ziti-router
       dns:
           - 127.0.0.1
           - 1.1.1.1
       user: root
       cap_add:
           - NET_ADMIN
       healthcheck:
           test:
               - CMD
               - ziti
               - agent
               - stats
           interval: 3s
           timeout: 3s
           retries: 5
           start_period: 30s

ziti-hmisiemens-router:
       profiles:
           - client-siemens
       image: openziti/ziti-router:1.1.9
       container_name: ziti-hmisiemens-router
       expose:
           - 3022
       networks:
           testnet:
               ipv4_address: ${HMI_SIEMENS_ADDRESS:-172.19.1.2}
       environment:
           ZITI_CTRL_ADVERTISED_ADDRESS: ziti-controller
           ZITI_ENROLL_TOKEN:
           ZITI_ROUTER_MODE: tproxy
       volumes:
           - ziti-hmisiemens-router:/ziti-router
       dns:
           - 127.0.0.1
           - 1.1.1.1
       user: root
       cap_add:
           - NET_ADMIN
       healthcheck:
           test:
               - CMD
               - ziti
               - agent
               - stats
           interval: 3s
           timeout: 3s
           retries: 5
           start_period: 30s

Ziti controller commands:

ziti edge login https://ziti-controller:1280 --ca=/home/ziggy/quickstart/pki/root-ca/certs/root-ca.cert -u admin -p ziggy123

ziti edge create edge-router "hmi-siemens-router" --tunneler-enabled -o /tmp/hmi-siemens-router.erott.jwt

ziti edge create edge-router "plc-siemens-router" --tunneler-enabled -o /tmp/plc-siemens-router.erott.jwt

ziti edge list edge-routers

ziti edge update identity hmi-siemens-router --role-attributes hmi-siemens-attr

ziti edge update identity plc-siemens-router --role-attributes plc-siemens-attr

ziti edge list identities

ziti edge create config "hmi-siemens-config" intercept.v1 '{"protocols":["tcp"], "addresses":["10.11.12.13"], "portRanges":[{"low":102, "high":102}]}'

ziti edge create config "plc-siemens-config" host.v1 '{"protocol": "tcp", "address":"127.0.0.1", "port":102}'

ziti edge list configs

ziti edge create service "plc-siemens-service" --configs hmi-siemens-config,plc-siemens-config --role-attributes plc-siemens-service-attr

ziti edge list services

ziti edge create service-policy "plc-siemens-service-policy" Bind --service-roles '#plc-siemens-service-attr' --identity-roles '#plc-siemens-attr'

ziti edge create service-policy "hmi-siemens-service-policy" Dial --service-roles '#plc-siemens-service-attr' --identity-roles '#hmi-siemens-attr'

Starting all:

ZITI_ENROLL_TOKEN="$(docker compose exec --no-TTY ziti-ctrl cat /tmp/hmi-siemens-router.erott.jwt)" \
docker compose --profile=client-siemens up --detach

What am I missing?

You replaced the ziti-host container with a ziti-router container and now the PLCsiemens service can't be reached by the HMI client at 10.11.12.13:102, though it was working previously.

Does policy advisor indicate that "hmi-siemens-router" has permission to dial, and "plc-siemens-router" has permission to bind the service?

ziti edge policy-advisor services --quiet plc-siemens-service
+ ziti edge policy-advisor services --quiet plc-siemens-service
-------------------------------------------------------------
OKAY : hmi-siemens-router (2) -> plc-siemens-service (3) Common Routers: (1/2) Dial: Y Bind: N 

OKAY : plc-siemens-router (2) -> plc-siemens-service (3) Common Routers: (1/2) Dial: N Bind: Y

I think the output of policy advisor is good, and ziti-plcsiemens-sidecar shares correctly the network interface with the plcsiemens container. I really can't understand why hmisiemens cannot reach plcsiemens.

Anyway, I replaced the working tunneler beacuse i thought that a sidecar router could be able to do both host.v1 and intercept.v1 work. Was I wrong? Is ther an example like the one you shared for the sidecar config?

EDIT: Obviously I can go back to the working tunneler if necessary to. I tried also to make the tunneler being in front of the sidecar on plcsiemens, but something was wrong in the enrollment of the sidecar i think

I've updated this demonstration script to use a ziti router as a sidecar for both web server and web client: Use a Ziti Router as a Client TPROXY Sidecar and as a Server host · GitHub

I had to solve a problem along the way. Routers can do several jobs, and they require specific configuration for each. Using a router as a tunneler only means the router should be "private" or have a valid address other routers can reach it to form mesh links.

Either set ZITI_ROUTER_ADVERTISED_ADDRESS: ziti-plcsiemens-sidecar for the PLC server's sidecar and ZITI_ROUTER_ADVERTISED_ADDRESS: ziti-hmisiemens-router for the HMI client's sidecar so their router link listeners will be publicly reachable by all other routers or set ZITI_BOOTSTRAP_CONFIG_ARGS: --private on both sidecars to make them advertising public listeners.

ziti-plcsiemens-sidecar:
       profiles:
           - host-siemens
       image: openziti/ziti-router:1.1.9
       container_name: ziti-plcsiemens-sidecar
       expose:
           - 3022
       networks:
           testnet:
               ipv4_address: ${PLCSIEMENS_ADDRESS:-172.19.1.1}
       environment:
           ZITI_CTRL_ADVERTISED_ADDRESS: ziti-controller
           ZITI_ENROLL_TOKEN:
           ZITI_ROUTER_MODE: tproxy
           ZITI_BOOTSTRAP_CONFIG_ARGS: --private
       volumes:
           - ziti-plcsiemens-sidecar:/ziti-router
       dns:
           - 127.0.0.1
           - 1.1.1.1
       user: root
       cap_add:
           - NET_ADMIN
       healthcheck:
           test:
               - CMD
               - ziti
               - agent
               - stats
           interval: 3s
           timeout: 3s
           retries: 5
           start_period: 30s

ziti-hmisiemens-router:
       profiles:
           - client-siemens
       image: openziti/ziti-router:1.1.9
       container_name: ziti-hmisiemens-router
       expose:
           - 3022
       networks:
           testnet:
               ipv4_address: ${HMI_SIEMENS_ADDRESS:-172.19.1.2}
       environment:
           ZITI_CTRL_ADVERTISED_ADDRESS: ziti-controller
           ZITI_ENROLL_TOKEN:
           ZITI_ROUTER_MODE: tproxy
           ZITI_BOOTSTRAP_CONFIG_ARGS: --private
       volumes:
           - ziti-hmisiemens-router:/ziti-router
       dns:
           - 127.0.0.1
           - 1.1.1.1
       user: root
       cap_add:
           - NET_ADMIN
       healthcheck:
           test:
               - CMD
               - ziti
               - agent
               - stats
           interval: 3s
           timeout: 3s
           retries: 5
           start_period: 30s

Ok, now the HMI can connect to PLC with no problem and I've understood the reason too.

Only one question: when I run tcpdump -a tcp in plcsiemens container i read this:

19:28:45.825892 IP ziti-ctrl.safe-testnet.3022 > 24983529da1c.37112: Flags [P.], seq 1025662653:1025662798, ack 3249278954, win 1245, options [nop,nop,TS val 2526087631 ecr 89161832], length 145
19:28:45.826485 IP 24983529da1c.37128 > ziti-ctrl.safe-testnet.3022: Flags [P.], seq 1566725808:1566725905, ack 1191506477, win 249, options [nop,nop,TS val 89162535 ecr 2526086928], length 97
19:28:45.827210 IP 24983529da1c.37112 > ziti-ctrl.safe-testnet.3022: Flags [P.], seq 1:160, ack 145, win 340, options [nop,nop,TS val 89162536 ecr 2526087631], length 159
19:28:45.827277 IP ziti-ctrl.safe-testnet.3022 > 24983529da1c.37112: Flags [.], ack 160, win 1267, options [nop,nop,TS val 2526087632 ecr 89162536], length 0
19:28:45.828550 IP ziti-ctrl.safe-testnet.3022 > 24983529da1c.37128: Flags [P.], seq 1:86, ack 97, win 249, options [nop,nop,TS val 2526087633 ecr 89162535], length 85

It seems messages are exchanged only with ziti-ctrl, isn't it supposed to be also some messages with ziti-hmisiemens-router?

Both will communicate with ziti-ctrl (i.e., the ziti controller and router container) acting as a public relay for your private sidecar routers.

This means the private sidecar routers can be in different networks with no direct path between them, and they'll use only the public Ziti router as a relay.

1 Like

Ok, i'll investigate more in detail day by day this particular because i'm trying to set some iptables rules to allow only the ziti-ctrl <--> plcsiemenscommunication, but it's not enough and I don't know which rules i need.

iptables -t filter -P INPUT DROP
iptables -t filter -P FORWARD DROP
iptables -t filter -P OUTPUT DROP

iptables -t filter -A INPUT -p tcp -s $ZITI_CTRL_ADDRESS -m state --state NEW,ESTABLISHED -j ACCEPT

iptables -t filter -A OUTPUT -p tcp -d $ZITI_CTRL_ADDRESS -m state --state NEW,ESTABLISHED -j ACCEPT

Ideas?

ziti-plcsiemens-sidecar and ziti-hmisiemens-router containers connect only to ziti-ctrl:1280,3022/tcp

The PLC client and server containers only connect through the sidecars

Yes, that's why i only specify the above rules, but with them the hmi do not reach the plc

Sorry i forgot to say that those rules are in plcsiemens container, it should make sense since it shares the interface with the sidecar, shoudln't it?

You'll need the output rule for new and a generic established, related rule for input. I'm not certain it's necessary to lock down the input to ziti-ctrl for establish, and new input isn't required at all.

Do you see the tproxy rules inserted by the sidecar router? You're appending to output chain so your exceptions should be at lower precedence. Try adding a jump to log statement to catch packets that are reaching the default policy.