What is the formula for directing all traffice to Google Workspace through a single egress IP?

Hi again, I am working towards how to control all outbound traffic to a public SaaS service like Google Workspace? I would like to be able to control access to certain public services from known IP addresses.

I am trying to duplicate the features of the sample quick start example on Netfoundry, shown in the screenshots below. Can you correct me if I understand this or not?

  • The sample.tools.netfoundry.io is a Service, itโ€™s destination address can only be accessed via the Fabric.
  • A computer with the agent installed and enrolled is an Endpoint.
  • The INTERCEPT HOST NAME / IP is MY service hostname on the fabric like my.super.private
  • The HOST CONFIGURATION is the destination, like google.com, including the protocol and port.
  • The outbound traffic is through the selected Edge Router.
  • The App/WAN is what associates the Service with an Endpoints. The attributes fields accept individual objects or tags/groups of these objects. If I put the tag of #all, then this service will work for any Endpoint or Service.
  • To create more such Services I need to make an associated App/WAN to connect the Service to Endpoint(s).

As a result of this arrangement. All traffic between my endpoints and the host my.super.private will exit fabric from the IP address of the Edge Router. Is this right?

Also, how does the Endpoint resolve my Service hostname in this case?

Edit: I was able to the get ZAC working in docker, and I see the console is quite different than the NetFoundry one. So there are some taxonomy differences, perhaps?

Yes there are some differences in terminology between CloudZiti and OpenZiti. If you have a newer ZAC there is a screen that is similar to this by clicking the plus in the upper left hand corner and making a new service. That new screen looks like this:

As to the screen shot, let me sum that up. This first screen is composing two OpenZiti primitive. It is making a "service" named "Sample_Service", a "config" that is of type "intercept.v1", and a "config" of type "host.v1" and then associating the two configs to the service. It will also create a sevice policy allowing the router to 'bind' the service.

The bottom picture is then making a final service policy that allows users to 'dial' the service.

So, in ZAC, using the singular screens (not the new one) you would:

  • create a service

  • add an intercept.v1 config to that service (I named mine Sample_Service_intercept.v1)

  • add a host.v1 config to that service

    • name the config: Sample_Service_host.v1
    • use "TCP", "splash.tools.netfoundry.ziti", port 443 like this and 'attach to service'
      image
  • Click 'save' to save the service

  • Go to "Policies -> Service Policies"

  • Add a new "bind" service policy. Pick the service you just made, pick the edge router you want to host the service (or the identity) and save the bind policy

  • Add a new "dial" service policy, pick the identities/attributes you want to be allowed to dial the service and save the policy

At that point you should have recreated that CloudZiti page

It's hard to know if it'll capture ALL traffic to be totally honest, without being more in touch with what you're doing. Often times there are numerous domains that need to be intercepted. I think you'd have to actually inspect the traffic using something like wireshark/tcpdump to be certain. But, yes that's the general idea... If the traffic was intercepted, it'd be tunneled to the far side (whatever the 'binding' identity is) and exit from that node towards whereever you configured the host.v1 config to point to. (in the above, all the traffic would exit towards splash.tools.netfoundry.ziti.

Also, how does the Endpoint resolve my Service hostname in this case?

I'm not sure I entirely understand the question but I'm assuming you mean the configured "sample.tools.netfoundry.io" that we used in the intercept.v1 config? At the end of the day that comes down to our "private DNS/intercept" technology. Every OS is slightly different, but at the end of the day there's something on the OS that is able to intercept DNS requests and return the private IP address provided by tunnelers. I don't know if that's the right level of detail, or answers your question or not but hopefully it does. If not, let us know and we'll try to clarify.

1 Like

Wowโ€ฆ that was all the โ€˜right level of detailโ€™ :slight_smile: !
Thanks for taking the time to explain that. I can appreciate now how much abstraction/simplification the CloudZiti product does. But understanding the underlying primatives and what to call them is going to go a long way in understanding this.

I definitely need to do a deep dive with an uninterrupted week.

1 Like

Good, I'm glad! :slight_smile:

It's definitely hard learning the new concepts. I think I'm going to dedicate this week's Ziti TV to an explainer of OpenZiti. What each of these concepts are, what they mean, what they are used for. I think that'll be an insightful Ziti TV. If you are able to tune in it'll be Friday at 11 am ET (8 am PT). You could ask questions directly via chat if you like.

I'll go over all the concepts again, it's good to have a fresh video like that with updated screens etc.

Look for it! :slight_smile: Catch it live or catch the replay!!! :smiley:

Hereโ€™s the YouTube link

Ok, I setup a server and a couple of clients, a web-service (you can curl -v hostname:8080). I added a service using the simple method with the big plus button on the dashboard. Now I can even see a service in my ziti desktop edge on my mac. I can resolve helloworld.ziti on my mac, and the IP address matches what is on the server when I do ip a. However, it wonโ€™t respond.

I feel like maybe I am missing a step. I deleted all the configurations and service again and cleaned it up and started it over. Same thing. What am I missing?

If you have access to the ziti command line, ziti edge policy-advisor can help track down any configuration issues.

There is also a log for Ziti Desktop Edge in the appโ€™s menu (either from the green Z icon in your Macโ€™s menu bar or from right click on the Ziti icon on your dock) under Logging/Packet Tunnel... that you can check for ERROR messages.

1 Like

Edit: I have not created any user identities, only device identities. Is that possibly part of the issue?

Very helpful... I can tail the logs now!

Here is the error I am getting. It is failed to connect, reason=service ... has no terminators. I am not sure the format the terminator should be. I have only one choice for Router. For Binding I am assuming it is TCP. I don't know what the host and port is supposed to be here. I tried localhost:8080 and the name of the identity.

image

^[[A(72208)[2023-07-25T00:33:33.512Z]    INFO tunnel-cbs:ziti_dns.c:500 format_resp() found record[100.64.0.3] for query[1:helloworld.ziti]
(72208)[2023-07-25T00:33:33.595Z]   ERROR ziti-sdk:connect.c:965 connect_reply_cb() conn[0.7/Connecting] failed to connect, reason=service ... has no terminators
(72208)[2023-07-25T00:33:33.595Z]   ERROR tunnel-cbs:ziti_tunnel_cbs.c:103 on_ziti_connect() ziti dial failed: connection is closed

You shouldnโ€™t need to configure that section unless you are hosting a service directly on the router. From your earlier screenshot, it looks like you have the service setup correctly to dial and bind, and an available edge router is shown that looks correct. The next thing Iโ€™d look at is your Edge Router Policies, and make sure there is a policy that allows each of the identities to access a common edge router (note: you can set a policy using #all for routers and #all for identities that will allow all identities to access all of your edge routers).

That ziti edge policy-advisor command should call this out if not configured in a way that allows your client identity a path to your server identity.

I can't figure this part out. This is what I see.

image

I can't figure this one out either. It's the 'pick the edge router' part... but I only see the tags or a name/value pair that doesn't autofill... so I am not sure the key/value.

image

For bind policy, use the IDENTITY ROLE ATTRIBUTES field. It has a type-ahead for finding the edge router (or identity) you want to use to bind the service (start with @ for a specific identity or # for an attribute).

The Dial policy is similar regarding IDENTITY ROLE ATTRIBUTES but specifies the identities that can dial the service.

The SERVICE ROLE ATTRIBUTES will likely be the same for both the dial and bind policy (e.g., @my-service-name for a specific service).

This interface is different now. There is no 'Attach to service' button (unless I am doing something wrong), it is now just save. But after creating those two configs I went back to the service and attached, them, is that right?

Yes, Iโ€™d expect that to do the same thing

1 Like

Okโ€ฆ man this is a ridiculously long thread. So sorryโ€ฆ you have been so patient!

I am just trying to get a dark host with a tunnel and the most basic nginx container to work over the fabric. I have a linux host and a mac host both setup as identities. I have added the service

The helloworld.jptech.corp is a debian12 host (internal lab name) I get a ziti IP and the tunneler shows enrolled

$ ip a
...
    inet 100.64.0.1/32 scope global tun0

and I can curl the localhost

$ curl -v localhost:8080
*   Trying 127.0.0.1:8080...
...
< HTTP/1.1 200 OK

If I view this identity, it shows โ€˜API Sessionโ€™ in GREEN. โ€˜Edgerouter Connectionโ€™ is GRAY.

If I go to the settings of the identity > SERVICES > click a service to test connection > click the HELLO service, the output is:

HELLO CONNECTION ISSUES

(BIND)
EVERYTHING IS CONFIGURED PROPERLY

The service I created is called hello, and here is the API call:

{
  "name":"hello",
  "configs":[
    "6FM8MdQOmRxshhWuySD2kJ", # hello_host.v1 I think
    "4ePDxMc9cuis4EI5XbiQXe"], # hello_intercept.v1 I think
  "tags":{
  },
  "terminatorStrategy":"smartrouting",
  "encryptionRequired":true,
  "roleAttributes":[ 
  ] # no attributes assigned
}

It has the following configs attached to it.

2 SELECTED CONFIGURATIONS
hello_host.v1
localhost {"bindUsingEdgeIdentity":false,"identity":"","precedence":"default"} 8080 tcp
hello_intercept.v1
hello.ziti {"identity":""} {"high":443,"low":443} tcp

If I look at the bind policy, this is the json:

{
  "name":"hello_bind",
  "type":"Bind",
  "serviceRoles":[
    "@BpEIZsvN9LjP5Nl0ASPUh"], # this is @hello
  "identityRoles":[
    "@XSZKba8iG"], # this is @helloworld.jptech.corp
  "postureCheckRoles":[
  ],
  "semantic":"AnyOf",
  "tags":{
  }
}

Here is the dial policy json

{
  "name":"hello_dial",
  "type":"Dial",
  "serviceRoles":[
  ],
  "identityRoles":[
    "#admin",
    "#dev",
    "#http",
    "#jptech", # these are pretty much all the tags, I can't find the #all tag reference to use instead.
    "@hlpiktLNe"], # this is @atreyu, my MacBook I am testing with
  "postureCheckRoles":[
  ],
  "semantic":"AnyOf",
  "tags":{
  }
}

From my MacBook, identity atreyu, I canโ€™t dig or curl the service.

$ dig hello.ziti @100.64.0.2 +short

I donโ€™t know if it matters, but I also have nebula running on this mac, but not the linux host. Here is my ifconfig portions.

$ ifconfig
...
utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1300
	inet 10.xxx.xxx.101 --> 20.xxx.xxx.0 netmask 0xff000000
utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380
...
utun4: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 4000
	options=6463<RXCSUM,TXCSUM,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
	inet 100.64.0.1 --> 100.64.0.1 netmask 0xffc00000

Here are screenshots for all of the relevant screens on my ZAC. https://photos.app.goo.gl/thgzt4CJJLfYksp86

What is your edge router policy (or policies)?

What are the results of ziti edge policy-advisor for the service and for your client identities?

Here are the two policies. I added screenshots to my album.

{
  "name":"all-endpoints-public-routers",
  "edgeRouterRoles":[
    "#public"],
  "identityRoles":[
    "#all"],
  "semantic":"AllOf",
  "tags":{
  }
}

{
โ€œnameโ€:โ€œedge-router-AeoaxkLiG6-systemโ€,
โ€œedgeRouterRolesโ€:[
โ€œ@AeoaxkLiG6โ€], # @ziti-edge-router
โ€œidentityRolesโ€:[
โ€œ@AeoaxkLiG6โ€], # @ziti-edge-router
โ€œsemanticโ€:โ€œAnyOfโ€,
โ€œtagsโ€:{
}
}

I havenโ€™t learned yet how to use ziti edge policy-advisor, so maybe this helps.

$ ziti edge policy-advisor identities

Policy General Guidelines
  In order for an identity to dial or bind a service, the following must be true:
    - The identity must have access to the service via a service policy of the correct type (dial or bind)
    - The identity must have acces to at least one on-line edge router via an edge router policy
    - The service must have access to at least one on-line edge router via a service edge router policy
    - There must be at least one on-line edge router that both the identity and service have access to.

Policy Advisor Output Guide:
  STATUS = The status of the identity -> service reachability. Will be OKAY or ERROR.
  ID = identity name
  ID ROUTERS = number of routers accessible to the identity via edge router policies.
    - See edge router polices for an identity: ziti edge controller list identity edge-router-policies <identity>
  SVC = service name
  SVC ROUTERS = number of routers accessible to the service via service edge router policies.
    - See service edge router policies for a service with: ziti edge controller list service service-edge-router-policies <service>
  ONLINE COMMON ROUTERS = number of routers the identity and service have in common which are online.
  COMMON ROUTERS = number of routers (online or offline) the identity and service have in common.
  DIAL_OK = indicates if the identity has permission to dial the service.
    - See service polices for a service  : ziti edge controller list service service-policies <service>
    - See service polices for an identity: ziti edge controller list identity service-policies <identity>
  BIND_OK = indicates if the identity has permission to bind the service.
  ERROR_LIST = if the status is ERROR, error details will be listed on the following lines

Output format: STATUS: ID (ID ROUTERS) -> SVC (SVC ROUTERS) Common Routers: (ONLINE COMMON ROUTERS/COMMON ROUTERS) Dial: DIAL_OK Bind: BIND_OK. ERROR_LIST
-------------------------------------------------------------------------------
error: no identity 'default' found in cli config /home/ziti/.config/ziti/ziti-cli.json

and

$ ziti edge policy-advisor services

Policy General Guidelines
  In order for an identity to dial or bind a service, the following must be true:
    - The identity must have access to the service via a service policy of the correct type (dial or bind)
    - The identity must have acces to at least one on-line edge router via an edge router policy
    - The service must have access to at least one on-line edge router via a service edge router policy
    - There must be at least one on-line edge router that both the identity and service have access to.

Policy Advisor Output Guide:
  STATUS = The status of the identity -> service reachability. Will be OKAY or ERROR.
  ID = identity name
  ID ROUTERS = number of routers accessible to the identity via edge router policies.
    - See edge router polices for an identity: ziti edge controller list identity edge-router-policies <identity>
  SVC = service name
  SVC ROUTERS = number of routers accessible to the service via service edge router policies.
    - See service edge router policies for a service with: ziti edge controller list service service-edge-router-policies <service>
  ONLINE COMMON ROUTERS = number of routers the identity and service have in common which are online.
  COMMON ROUTERS = number of routers (online or offline) the identity and service have in common.
  DIAL_OK = indicates if the identity has permission to dial the service.
    - See service polices for a service  : ziti edge controller list service service-policies <service>
    - See service polices for an identity: ziti edge controller list identity service-policies <identity>
  BIND_OK = indicates if the identity has permission to bind the service.
  ERROR_LIST = if the status is ERROR, error details will be listed on the following lines

Output format: STATUS: ID (ID ROUTERS) -> SVC (SVC ROUTERS) Common Routers: (ONLINE COMMON ROUTERS/COMMON ROUTERS) Dial: DIAL_OK Bind: BIND_OK. ERROR_LIST
-------------------------------------------------------------------------------
error: no identity 'default' found in cli config /home/ziti/.config/ziti/ziti-cli.json

It looks like you werenโ€™t logged in when you ran the policy-advisor. You can log in by running ziti edge login before running other commands

Your edge router policy shows that all identities can access all edge routers that have the #public attribute. Iโ€™d like to double-check that you have an edge router with #public. You can check by running ziti edge list edge-routers (attributes are listed in the last column)

Here is what I have in my edge-router.

ziti@c219d306ea99:/persistent$ ziti edge login
Using controller url: https://ziti.jptech.ninja:8441/edge/management/v1 from identity 'default' in config file: /home/ziti/.config/ziti/ziti-cli.json
Using username: admin from identity 'default' in config file: /home/ziti/.config/ziti/ziti-cli.json
Enter password:
Token: bf6...c1a
Saving identity 'default' to /home/ziti/.config/ziti/ziti-cli.json
ziti@c219d306ea99:/persistent$ ziti edge list edge-routers
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ ID        โ”‚ NAME             โ”‚ ONLINE โ”‚ ALLOW TRANSIT โ”‚ COST โ”‚ ATTRIBUTES โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ pE4Bv1tDS โ”‚ ziti-edge-router โ”‚ true   โ”‚ true          โ”‚    0 โ”‚ public     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
results: 1-1 of 1

I am running via the docker-compose simplified version, the only change being the location of the volumes.

# diff --color=always docker-compose*
25c25
<       - ziti-fs:/persistent
---
>       - ./ziti-fs:/persistent
41c41
<       - ziti-fs:/persistent
---
>       - ./ziti-fs:/persistent
69c69
<       - ziti-fs:/persistent
---
>       - ./ziti-fs:/persistent
88c88
<       - ziti-fs:/persistent
---
>       - ./ziti-fs:/persistent
95,96c95,96
< volumes:
<   ziti-fs:
---
>     # volumes:
>     #   ziti-fs: