Intercept destinations vs policies attributes

Hi Team,

There is something really confusing for me: I have created a service to access database server on multiple hosts.

  • host.v1 config: TCP / localhost / port 4900 / bind using edge identity
  • intercept.v1: TCP / port 4900 / addresses srv1.mydomain, srv2.mydomain, ... (or *.mydomain) / dial option: $dst_hostname

After controller and router reboot (unfortunately), I'm able to access one or another server.

Now, I want to restrict access to certain servers for certain users. So:

  • I create an attribute ServerGroup1
  • I assign it to srv1.mydomain
  • I assign it also to the bind policy

Therefore, I should be able to access only srv1.mydomain but actually, I still can access the other servers. So it looks like policies are not applied when multiple servers can be reached for the same service. Am I right?

Hi @Eric,

You have to reboot the controller and router??? That's definitely unexpected. I'd be interested to hear more about your setup and what happens if you have to do this.

Hmm. If I understand what you have done, I think you have to create dial policies for the users you want to constrain to different servers. Unless it's a featue I am not aware of, I'm pretty sure we don't have the notion currently of applying a policy based on the binding identity like this.

If this were me, I would create multiple dial policies. You can keep your bind policy but you'll need individual dial policies letting "user 1" dial "server 1" and "user2" dial "server 2" and so on. Oh no... I misunderstood - Paul's reply below is better. If you can dial the service, you'll have access to all hosts... Same situation

Do you have a use case where you need 100's or 1000's of these sorts of dial policies? I'd be interested in hearing it as it might be a place where OpenZiti could add a feature to support such a use case. We have not had this particular request yet, so I'd be very interested to hear what it might be?

You'll want separate services. A service is the is the base level construct for policy access. The dialing side should not care about the hosting side and vice-versa, with some exceptions:

  1. If appropriate for the service, hosting identity can set an instance id when they bind. This is for services like ssh, where you might have a general 'ssh' service, but you want to be able to select which host you ssh to. This would only be appropriate if every identity that had access to the ssh service should have access to every machine hosting ssh. If this is not the case, you would do a service per machine
  2. We've added a facility to let the hosting side know which identity is dialing it. You can do with that what you want, including additional access control, but that is outside the scope of ziti policies at that point.

So, with those caveats out of the way, if you want to restrict access to specific hosts, they should have their own service. You can make the same hosted applications available via multiple services if that makes things easier. So an 'all-hosts' service would cover all hosts and the 'some-hosts' service would be on the more limted set. They would have the same host.v1 config, but those don't conflict, because they're outgoing, i.e. you can have two names for the same thing, because it resolves to one thing. On the intercept side, you can't have overlaps because then you have one name going to two destinations, which doesn't work.

Hope that makes sense,
Paul

Thank you all for your replies. To recap our architecture, I had started a thread on the subject: Help with designing/configuring desired overlay.

After upgrading to version 1.6.13, I spent a lot of time last week trying to understand the difficulties we're facing and how to resolve them. As a reminder, the main goal is to create groups. Within each group, multiple clients can access multiple servers. Each group must be isolated from the others.

I thought I had found a solution:

  • All servers in the infrastructure have the Pix attribute.
  • All clients in the infrastructure have the Terms attribute.

For each group:

  • I generate a random domain name, for example: dyvSeKuqen.
  • I create a service named 2609-dyvSeKuqen (2609 is the TCP port of the service).
  • In the intercept.v1 configuration, I specify *.dyvSeKuqen as the destination and $dst_hostname in dial options -> identity.
  • In the host.v1 configuration, I specify localhost as the destination address and enable the Bind using edge identity option.
  • I assign the servers and clients in this group an attribute named dyvSeKuqen.
  • I created a server policy named dyvSeKuqen-Bind specifying the attributes Pix and dyvSeKuqen with semantics set to allOf.
  • I created a client policy named dyvSeKuqen-Dial specifying the attributes Terms and dyvSeKuqen with semantics set to allOf.

Thus, in the application, clients only know the dyvSeKuqen domain and therefore cannot access the servers of other groups. Even if they knew another domain name, the policy rules would prevent them from accessing it.

I was very pleased with myself... :smiling_face_with_sunglasses: but it's not working. :face_with_spiral_eyes: I think there are things I'm missing, but I can't see what because my approach seems perfectly logical. Unfortunately, we're at the stage where we have to deploy our application for the first time, and we're completely stuck due to various problems that we are unable to resolve using openZiti.

I checked the logs, and here's the error message:

ERROR ziti-sdk:connect.c:1070 connect_reply_cb() conn[1.6/uIyEQfaS/Connecting](2609-dyvSeKuqen) failed to connect, reason=service AqErEuQI8ZfkKZzPTaVvt has no terminators for instanceId 93f4c47e-9a49-41a8-9169-5ae30b54ee73.dyvsekuqen

I don't know what else to say except that I would be very grateful to understand what's wrong and how to implement a stable and functional solution. Your help is greatly appreciated!

I think the issue is that you're using addressable terminators, and that's not necessarily a good fit for you. Addressable terminators are when you have a service with multiple hosting endpoints, each with a unique id. The classic example is ssh. In that case you'd generally bind using the hostname, then you'd have to dial using a combination of the hostname and service: myserver@ssh.

I'm not sure I fully understand your use case, but based on what I think I understand, I would do the following:

  1. Create a service per-server. Don't bind by identity, just use a simple service.
  2. Create a bind policy per-server. Each server can bind to it's own service.
  3. Create a dial policy per group.
  4. Tag each service/server that should be accessible from the group with the group name.
  5. Tag each identity that should be a member of the group

Here's an example setup (generated):

  Scenario                                                                                                                                                                               
   
  ┌────────────┬────────────────┬────────────────────────────────┐                                                                                                                       
  │   Group    │     Users      │       Accessible Servers       │
  ├────────────┼────────────────┼────────────────────────────────┤
  │ developers │ alice, bob     │ web-1, web-2, api-1, api-2     │
  ├────────────┼────────────────┼────────────────────────────────┤
  │ dbadmins   │ charlie, diana │ db-1, db-2, monitoring         │
  ├────────────┼────────────────┼────────────────────────────────┤
  │ ops        │ eve, frank     │ web-1, api-1, db-1, monitoring │
  └────────────┴────────────────┴────────────────────────────────┘

  Note that some services are tagged with multiple groups (e.g. web-1 is accessible by both developers and ops).

  # =============================================
  # 1. Create server identities (one per server)
  # =============================================
  ziti edge create identity web-server-1        -o web-server-1.jwt
  ziti edge create identity web-server-2        -o web-server-2.jwt
  ziti edge create identity api-server-1        -o api-server-1.jwt
  ziti edge create identity api-server-2        -o api-server-2.jwt
  ziti edge create identity db-server-1         -o db-server-1.jwt
  ziti edge create identity db-server-2         -o db-server-2.jwt
  ziti edge create identity monitoring-server   -o monitoring-server.jwt

  # =============================================
  # 2. Create user identities, tagged with group
  # =============================================
  ziti edge create identity alice   -a "developers" -o alice.jwt
  ziti edge create identity bob     -a "developers" -o bob.jwt
  ziti edge create identity charlie -a "dbadmins"   -o charlie.jwt
  ziti edge create identity diana   -a "dbadmins"   -o diana.jwt
  ziti edge create identity eve     -a "ops"         -o eve.jwt
  ziti edge create identity frank   -a "ops"         -o frank.jwt

  # =============================================
  # 3. Create services, tagged with group(s)
  #    that should be able to dial them
  # =============================================
  ziti edge create service web-1-svc        -a "developers,ops"
  ziti edge create service web-2-svc        -a "developers"
  ziti edge create service api-1-svc        -a "developers,ops"
  ziti edge create service api-2-svc        -a "developers"
  ziti edge create service db-1-svc         -a "dbadmins,ops"
  ziti edge create service db-2-svc         -a "dbadmins"
  ziti edge create service monitoring-svc   -a "dbadmins,ops"

  # =============================================
  # 4. Bind policies — one per server
  #    Each server identity can bind its own service
  #    @ references a specific entity by name
  # =============================================
  ziti edge create service-policy web-1-bind        Bind --identity-roles "@web-server-1"      --service-roles "@web-1-svc"
  ziti edge create service-policy web-2-bind        Bind --identity-roles "@web-server-2"      --service-roles "@web-2-svc"
  ziti edge create service-policy api-1-bind        Bind --identity-roles "@api-server-1"      --service-roles "@api-1-svc"
  ziti edge create service-policy api-2-bind        Bind --identity-roles "@api-server-2"      --service-roles "@api-2-svc"
  ziti edge create service-policy db-1-bind         Bind --identity-roles "@db-server-1"       --service-roles "@db-1-svc"
  ziti edge create service-policy db-2-bind         Bind --identity-roles "@db-server-2"       --service-roles "@db-2-svc"
  ziti edge create service-policy monitoring-bind   Bind --identity-roles "@monitoring-server"  --service-roles "@monitoring-svc"

  # =============================================
  # 5. Dial policies — one per group
  #    # references a role attribute (tag)
  #    Identities tagged "developers" can dial
  #    services tagged "developers", etc.
  # =============================================
  ziti edge create service-policy developers-dial Dial --identity-roles "#developers" --service-roles "#developers"
  ziti edge create service-policy dbadmins-dial   Dial --identity-roles "#dbadmins"   --service-roles "#dbadmins"
  ziti edge create service-policy ops-dial        Dial --identity-roles "#ops"         --service-roles "#ops"

  # =============================================
  # 6. Edge router policies (catch-all for simplicity)
  #    Allows all identities to connect to all edge routers
  #    and all services to be hosted on all edge routers
  # =============================================
  ziti edge create edge-router-policy          all-endpoints --identity-roles "#all" --edge-router-roles "#all"
  ziti edge create service-edge-router-policy  all-services  --service-roles "#all"  --edge-router-roles "#all"

  How it works

  The @ vs # distinction is the key:
  - @web-server-1 — matches the specific identity named web-server-1
  - #developers — matches any entity with the role attribute developers

  Bind policies use @ on both sides: one specific server binds one specific service. Simple, explicit, no ambiguity.

  Dial policies use # on both sides: any identity tagged with the group can dial any service tagged with the group. This is where the power is — you only need 3 dial policies regardless
   of how many servers/users you have.

  Adding a new server to existing groups is just two commands:
  ziti edge create service    new-svc    -a "developers,ops"
  ziti edge create service-policy new-bind Bind --identity-roles "@new-server" --service-roles "@new-svc"
  # Done — developers and ops can already dial it, no policy changes needed

  Adding a user to an existing group is one command:
  ziti edge create identity greg -a "developers" -o greg.jwt
  # Done — greg can already dial all developer-tagged services

  Giving an existing user access to another group (e.g. diana also gets ops access):
  ziti edge update identity diana -a "dbadmins,ops"

I didn't include intercept/host configs. Hosting configs should be straightforward. Intercept configs can be by adding a hostname intercept per service.

Let me know if that makes sense, or if I'm missing something about your setup.
Paul

Paul,

That's funny because I tested multiple scenarios today and I finished by building exactly the settings you mention! With your last message, I know this is the right way.

This is good news, we will be able to move forward. The bad news is that we will have to manage many many services, configurations and policies. This will be done by our application so it's not time consuming but I don't know how it's going to load the controller and the router(s). We will see!

Thanks a lot to Clint and you for your availability.