Ok. The short story… The step I kept forgetting about was the enrollment. That still needs to happen, even with an automatic identity. The difference is that the certificate itself will allow the enrollment/creation of the identity.
So here are the steps I performed…
Please note 1: I used an environment which was provisioned by the quickstart. That means I have a couple of environment variables in my environment. You will need to replace those accordingly.
Please note 2: The ziti-edge-tunnel
and ziti
CLI supports 3rd party enrollment but the Ziti Desktop Edge for Mac/Windows don’t support that natively/easily yet. I don’t expect that’s an issue for you but, FYI. Also note that it seems that ziti-edge-tunnel
has a bug for this enrollment as well that needs to be sorted. I had to use ziti
to enroll the identities due to that apparent bug.
Automatic CA creation steps, leveraging ziti CLI
CA Name
establish a variable name of the CA we’ll be using. This variable will be used below, often
ca_name="sep08ca"
Create the x509 CA
This process will use the ziti
CLI to create a certificate that is a CA. The product of these commands will also, by necessity, create a key. If you already have a key, you cannot currently provide it to the ziti
CLI, unlike other pki
commands. You’ll have to use openssl for now if you’re trying to do that.
ziti pki create ca \
--pki-root="${ZITI_PKI_OS_SPECIFIC}" \
--ca-file="${ca_name}" \
--pki-country "US" \
--pki-locality "locality" \
--pki-organization "org" \
--pki-organizational-unit "the_ou" \
--pki-province "ST"
Add the CA to Your Overlay
Now you’ll need to send the certificate to the controller and register it as a third party cert. This command also demonstrates how to set the identity-format for the identities that will be automatically created, as well as setting the CA valid for auth
, and autoca
. Here’s another env var you’ll need to replace: ZITI_PKI_OS_SPECIFIC
ziti edge create ca "${ca_name}" \
"${ZITI_PKI_OS_SPECIFIC}/${ca_name}/certs/${ca_name}.cert" \
--auth \
--autoca \
--identity-name-format '[caName]-[caId]-[commonName]-[requestedName]-[identityId]'
Verify the CA
The CA needs to be verified by providing a certificate back to the controller that was signed by the CA you’re trying to use. The certificate must have the expected verification token in the certificate’s “CN” field.
For this process first get the verification token using the ziti
CLI and jq
and set it into the verification_token
variable:
verification_token=$(ziti edge list cas 'name contains "'"${ca_name}"'"' -j \
| jq -r .data[].verificationToken)
Now you can use the ziti
CLI to create a certificate that can verify the CA, proving you have the key for the CA:
ziti pki create client \
--pki-root="${ZITI_PKI_OS_SPECIFIC}" \
--ca-name="${ca_name}" \
--client-name="${verification_token}" \
--client-file="${verification_token}-file"
Now we’ll actually verify the certificate. These steps will just located the verification cert and the ca cert/key for use in the next step:
verification_cert=$(find $ZITI_PKI_OS_SPECIFIC \
-name "*${verification_token}*.cert")
cacert=$(find $ZITI_PKI_OS_SPECIFIC \
-name "*${ca_name}*.cert")
cakey=$(find $ZITI_PKI_OS_SPECIFIC \
-name "*${ca_name}*.key")
Let’s list the CA now and see the flags returned. Notice that the CA is not verified yet: flags: [AE]
ziti edge list cas 'name contains "'"${ca_name}"'"'
Now we can verify the CA
ziti edge verify ca "${ca_name}" \
--cert "${verification_cert}" \
--cacert "${cacert}" \
--cakey "${cakey}"
And after being verified, we can list the CA and see it verified: flags: [VAE]
ziti edge list cas 'name contains "'"${ca_name}"'"'
At this point the CA is ready to be used.
The work to illustrate two clients
Now we’ll want to make two new sets of certificates which are signed by the CA made above, using ziti
CLI again. If you have a private key, you could supply it here with --key-file
.
client1_name="sep08_one"
client2_name="sep08_two"
ziti pki create client \
--pki-root=${ZITI_PKI_OS_SPECIFIC} \
--ca-name=${ca_name} \
--client-name="${client1_name}" \
--client-file="${client1_name}-file"
ziti pki create client \
--pki-root=${ZITI_PKI_OS_SPECIFIC} \
--ca-name=${ca_name} \
--client-name="${client2_name}" \
--client-file="${client2_name}-file"
Once created, we can set a couple of variables that represent the location of the client key and cert created above.
client1_cert="$(find $ZITI_PKI_OS_SPECIFIC \
-name "*${client1_name}-file.cert")"
client1_key="$(find $ZITI_PKI_OS_SPECIFIC \
-name "*${client1_name}-file.key")"
client2_cert="$(find $ZITI_PKI_OS_SPECIFIC \
-name "*${client2_name}-file.cert")"
client2_key="$(find $ZITI_PKI_OS_SPECIFIC \
-name "*${client2_name}-file.key")"
Now, we’ll need to fetch the jwt that an automatic, enrollment, 3rd party CA supports. Right now there’s no “really easy” way to fetch this CA so when you deploy a client, you’ll need to include this jwt as part of the provisioning step. The jwt is always available to download and only needs to be downloaded one time, however when you enroll an identity with ziti
it’ll consume the .jwt. Just notice that, because below you’ll see me download the jwt two times.
First, we’ll need the session cookie returned when logging into the controller. Again, I’ve used a quickstart provisioned overlay, which means my $ZITI_HOME is set to $HOME/.ziti/quickstart/$(hostname)
.
Assign the use the output of ziti login to find your ziti-cli.json file and assign it to ziti_cli_json
:
ziti_cli_json="$ZITI_HOME/ziti-cli.json"
Use jq
to get and set the zt_session
from that file
zt_session=$(jq -r .edgeIdentities.default.token "${ziti_cli_json}")
Now get the id of the CA using ziti
CLI and assign it to ca_id
ca_id=$(ziti edge list cas 'name = "'"$ca_name"'"' -j | jq -r .data[].id)
And we’re almost done… Now we can now list identities before enrollment… and then after…
ziti edge list identities 'name contains "sep08"'
curl -sk -H "zt-session: ${zt_session}" "https://${ZITI_EDGE_CTRL_ADVERTISED}/edge/management/v1/cas/${ca_id}/jwt" > "${ca_name}.jwt"
echo "fetched jwt: ${ca_name}.jwt"
ziti edge enroll \
--cert ${client1_cert} \
--key ${client1_key} \
--jwt ${ca_name}.jwt \
--idname "${client1_name}.requestedName" \
--out $HOME/${client1_name}.enrolled.json
curl -sk -H "zt-session: ${zt_session}" "https://${ZITI_EDGE_CTRL_ADVERTISED}/edge/management/v1/cas/${ca_id}/jwt" > "${ca_name}.jwt"
echo "fetched jwt: ${ca_name}.jwt"
ziti edge enroll \
--cert ${client2_cert} \
--key ${client2_key} \
--jwt ${ca_name}.jwt \
--idname "${client2_name}.requestedName" \
--out $HOME/${client2_name}.enrolled.json
ziti edge list identities 'name contains "sep08"'
Using the identity file
./ziti-edge-tunnel run-host -i $HOME/${client1_name}.enrolled.json
[ 0.099] INFO tunnel-cbs:ziti_tunnel_ctrl.c:801 on_ziti_event() ztx[sep08ca-vj6x99YBDI-sep08_one-sep08_one.requestedName-F-o7RnepDI] router ip-172-31-42-64-edge-router@tls://ec2-18-188-201-183.us-east-2.compute.amazonaws.com:8442 connected
./ziti-edge-tunnel run-host -i $HOME/${client2_name}.enrolled.json
[ 0.098] INFO tunnel-cbs:ziti_tunnel_ctrl.c:801 on_ziti_event() ztx[sep08ca-vj6x99YBDI-sep08_two-sep08_two.requestedName-Pi77RneBD] router ip-172-31-42-64-edge-router@tls://ec2-18-188-201-183.us-east-2.compute.amazonaws.com:8442 connected
Ok - and “that’s it”… I know it looks like a lot but really it’s not all that complicated.