Skip to main content

Provision a network and service

This guide walks through automating the full provisioning flow end-to-end using two APIs: the NetFoundry API for infrastructure, and the OpenZiti controller API for Ziti resources. When you're done you'll have a network, a customer-hosted edge router, two identities, and a service that routes traffic through the overlay.

You'll need curl and jq. Set NETFOUNDRY_API_TOKEN before you start—see Authentication if you don't have a token yet.

NF_API=https://gateway.production.netfoundry.io

Find your network group

Your organization belongs to one or more network groups. You need the network group ID to create a network.

  1. List your network groups:

    curl -s ${NF_API}/core/v3/network-groups \
    --header "Authorization: Bearer ${NETFOUNDRY_API_TOKEN}" \
    | jq '._embedded.networkGroupList[] | {name, id}'

    The response lists your groups by name:

    {
    "name": "My Company",
    "id": "4d15ef35-cfa0-4963-a667-5c86d16ce77e"
    }
  2. Set the ID of the group you want to use:

    NF_NETWORK_GROUP=4d15ef35-cfa0-4963-a667-5c86d16ce77e

Create a network

Provision the network. Specify the network group, a cloud provider, a region, and a size. Use small for cost-conscious testing.

  1. Create the network:

    NF_CREATE=$(curl -s -X POST ${NF_API}/core/v3/networks \
    --header "Authorization: Bearer ${NETFOUNDRY_API_TOKEN}" \
    --header "Content-Type: application/json" \
    --data '{
    "networkGroupId": "'"${NF_NETWORK_GROUP}"'",
    "name": "example-network",
    "size": "small",
    "provider": "AWS",
    "region": "us-east-1"
    }')
    NF_NETWORK=$(echo "${NF_CREATE}" | jq -r .id)
    NF_EXECUTION=$(echo "${NF_CREATE}" | jq -r '._links.execution.href')
  2. Poll the execution link until it returns SUCCESS. The network reaches PROVISIONED status before it's fully ready to use, so use the execution link—not the network status—as your readiness signal. Provisioning takes a few minutes:

    curl -s "${NF_EXECUTION}" \
    --header "Authorization: Bearer ${NETFOUNDRY_API_TOKEN}" | jq -r .status
note

To see available cloud providers and region codes, query GET /core/v3/regions. The locationCode value is what you pass as region:

curl -s "${NF_API}/core/v3/regions?provider=AWS" \
--header "Authorization: Bearer ${NETFOUNDRY_API_TOKEN}" \
| jq '._embedded.regionList[] | .locationCode'

To list available sizes, query GET /core/v3/host-sizes.

Get the controller address

Fetch the controller's domain name using the network-controllers embed—you'll use it for all subsequent controller API calls. Any _links entry in a NetFoundry API response can be embedded this way by passing its name as the embed query parameter.

NF_CONTROLLER=$(curl -s "${NF_API}/core/v3/networks/${NF_NETWORK}?embed=network-controllers" \
--header "Authorization: Bearer ${NETFOUNDRY_API_TOKEN}" \
| jq -r '._embedded["network-controllers"][0].domainName')

Authenticate to the controller

Exchange your NetFoundry Bearer token for a session token to authenticate against the controller API.

SESSION=$(curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/authenticate?method=ext-jwt \
--header "Authorization: Bearer ${NETFOUNDRY_API_TOKEN}" \
--header "Content-Type: application/json" \
| jq -r .data.token)

All controller API calls from this point use --header "zt-session: ${SESSION}" instead of a Bearer token.

Create an edge router

Create the edge router on the controller. The enrollmentJwt in the response is a one-time token used to enroll the router software—fetch and save it before it expires.

  1. Create the edge router entry:

    NF_ROUTER=$(curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/edge-routers \
    --header "zt-session: ${SESSION}" \
    --header "Content-Type: application/json" \
    --data '{
    "name": "example-router",
    "roleAttributes": ["defaultRouters"],
    "enrollment": {"ott": true}
    }' | jq -r .data.id)
  2. Fetch the enrollment JWT and save it to a file:

    curl -s https://${NF_CONTROLLER}/edge/management/v1/edge-routers/${NF_ROUTER} \
    --header "zt-session: ${SESSION}" \
    | jq -r .data.enrollmentJwt > router.jwt

Create an edge router policy

note

New networks include a default edge router policy that allows all identities to use all edge routers (known as an all/all policy). If you're working with a default network, you can skip this step.

An edge router policy governs which identities can route through which edge routers. This policy allows all identities to use routers with the defaultRouters attribute.

curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/edge-router-policies \
--header "zt-session: ${SESSION}" \
--header "Content-Type: application/json" \
--data '{
"name": "example-er-policy",
"edgeRouterRoles": ["#defaultRouters"],
"identityRoles": ["#all"],
"semantic": "AnyOf"
}' | jq -r .data.id

Create identities

You need two identities: one for the client that dials the service, and one for the host that provides egress.

Client identity

The client identity represents the device that dials into the service.

  1. Create the identity:

    NF_CLIENT=$(curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/identities \
    --header "zt-session: ${SESSION}" \
    --header "Content-Type: application/json" \
    --data '{
    "name": "client1",
    "type": "Default",
    "isAdmin": false,
    "roleAttributes": ["clients"],
    "enrollment": {"ott": true}
    }' | jq -r .data.id)
  2. Fetch the enrollment JWT and save it to a file:

    curl -s https://${NF_CONTROLLER}/edge/management/v1/identities/${NF_CLIENT} \
    --header "zt-session: ${SESSION}" \
    | jq -r '.data.enrollment.ott.jwt' > client1.jwt

Host identity

The host identity runs on the machine that egresses to your server.

  1. Create the identity:

    NF_HOST=$(curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/identities \
    --header "zt-session: ${SESSION}" \
    --header "Content-Type: application/json" \
    --data '{
    "name": "exit1",
    "type": "Default",
    "isAdmin": false,
    "roleAttributes": ["exits"],
    "enrollment": {"ott": true}
    }' | jq -r .data.id)
  2. Fetch the enrollment JWT and save it to a file:

    curl -s https://${NF_CONTROLLER}/edge/management/v1/identities/${NF_HOST} \
    --header "zt-session: ${SESSION}" \
    | jq -r '.data.enrollment.ott.jwt' > exit1.jwt

Create the service

Services are defined by a pair of configs: an intercept config (how clients connect in) and a host config (where traffic egresses). Create the configs first, then the service that references them.

  1. Look up the built-in config type IDs. These are unique per controller:

    NF_INTERCEPT_TYPE=$(curl -s https://${NF_CONTROLLER}/edge/management/v1/config-types \
    --header "zt-session: ${SESSION}" \
    | jq -r '.data[] | select(.name == "intercept.v1") | .id')

    NF_HOST_TYPE=$(curl -s https://${NF_CONTROLLER}/edge/management/v1/config-types \
    --header "zt-session: ${SESSION}" \
    | jq -r '.data[] | select(.name == "host.v1") | .id')
  2. Create the intercept config. Clients connect to the hostname and port defined here—the tunneler intercepts traffic to this address and routes it through the overlay:

    NF_INTERCEPT_CONFIG=$(curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/configs \
    --header "zt-session: ${SESSION}" \
    --header "Content-Type: application/json" \
    --data '{
    "name": "echo-intercept",
    "configTypeId": "'"${NF_INTERCEPT_TYPE}"'",
    "data": {
    "protocols": ["tcp"],
    "addresses": ["echo.ziti"],
    "portRanges": [{"low": 80, "high": 80}]
    }
    }' | jq -r .data.id)
  3. Create the host config. This defines where the host identity egresses traffic. This example routes to eth0.me:80—an external IP-checking service useful for verifying the overlay is working:

    NF_HOST_CONFIG=$(curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/configs \
    --header "zt-session: ${SESSION}" \
    --header "Content-Type: application/json" \
    --data '{
    "name": "echo-host",
    "configTypeId": "'"${NF_HOST_TYPE}"'",
    "data": {
    "protocol": "tcp",
    "address": "eth0.me",
    "port": 80
    }
    }' | jq -r .data.id)
  4. Create the service, referencing both configs:

    NF_SERVICE=$(curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/services \
    --header "zt-session: ${SESSION}" \
    --header "Content-Type: application/json" \
    --data '{
    "name": "echo-service",
    "encryptionRequired": true,
    "roleAttributes": ["publicServices"],
    "configs": ["'"${NF_INTERCEPT_CONFIG}"'", "'"${NF_HOST_CONFIG}"'"]
    }' | jq -r .data.id)

Create service policies

Service policies authorize identities to dial or bind a service. You need two: one for clients (Dial) and one for the host (Bind).

curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/service-policies \
--header "zt-session: ${SESSION}" \
--header "Content-Type: application/json" \
--data '{
"name": "echo-dial",
"type": "Dial",
"semantic": "AnyOf",
"serviceRoles": ["#publicServices"],
"identityRoles": ["#clients"]
}' | jq -r .data.id

curl -s -X POST https://${NF_CONTROLLER}/edge/management/v1/service-policies \
--header "zt-session: ${SESSION}" \
--header "Content-Type: application/json" \
--data '{
"name": "echo-bind",
"type": "Bind",
"semantic": "AnyOf",
"serviceRoles": ["#publicServices"],
"identityRoles": ["#exits"]
}' | jq -r .data.id

Enroll

Each enrollment JWT is a one-time token. Run these commands on the target machine before the JWT expires.

Enroll the edge router

Run this on the machine where the router will run. The router.jwt file was saved in the edge router step above.

ziti router enroll --jwt router.jwt

Enroll the identities

Run these on the respective machines—client1.jwt on the client machine, exit1.jwt on the host machine.

ziti edge enroll --jwt client1.jwt
ziti edge enroll --jwt exit1.jwt

The resulting .json files are the identity files. Load them into a Ziti-compatible tunneler to connect. See the NetFoundry downloads page for tunneler options, or the Linux tunneler guide for CLI-based setup.