-
Notifications
You must be signed in to change notification settings - Fork 2
OpenZiti Inventory Autodiscovery: Overview
The community.openziti.connect_autodiscovery.py
dynamic inventory plugin relies
on a specific set of OpenZiti constructs, objects, and conventions to function
correctly. There is some initial setup overhead involved when using this
integration, and we outline here what is needed end-to-end that makes it all work.
The plugin's function is to provide the community.openziti.*
connection plugins
with the necessary variables used to automatically configure the SSH connection
for targets discovered from OpenZiti's Edge API. In this setup, a ziti tunneling
software (e.g. the ziti-edge-tunnel
), proxies connections to the SSH server,
which is configured to listen on localhost only.
- The OpenZiti identity name for the ansible target MUST match the
inventory_hostname
derived from other inventory sources. - An OpenZiti
config-type
namedansible-target-discovery.v1
must be defined, and aconfig
that implements the schema must be assigned to theservice
that is hosted by the ziti tunneler. - An OpenZiti
host.v1
config
that fronts traffic to the SSH server. You may change the config to match your setup, but this integration relies on binding the name of the identity as known to the edge (that is, theinventory_hostname
as described above). The option"bindUsingEdgeIdentity": true
can only be substituted to the equivalent"identity": "$tunneler_id.name"
for the purposes of this integration. - Identities, assigned to Ansible controller users (generally, sysadmins),
must have
Dial
permissions to the Openziti service. This is accomplished through aservice-policy
in OpenZiti. - The
comunity.openziti.connect_autodiscovery
plugin should be last in the list ofenabled_plugins
in the[inventory]
section of theansible.cfg
- The magic string
openziti_connection_autodiscovery
must be in the list ofinventory
sources defined in the[defaults]
section of theansible.cfg
- The plugin option
ziti_identities
must be satisfied with at least 1 ziti identity on the Ansible controller, which is used to discover the list of inventory targets. See:ansible-doc -t inventory community.openziti.connection_autodiscovery
for details.
- New OpenZiti
config-type
object namedansible-target-discovery.v1
:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"connection_plugin": {
"type": "string",
"enum": ["paramiko", "libssh"]
}
},
"required": [
"connection_plugin"
]
}
- Concrete
config
object from above:
Sample name: impl-ansible-target-discovery.v1
{
"connection_plugin": "paramiko"
}
- Define
host.v1
config
object:
ansible-ssh-host.v1
{
"address": "localhost",
"port": 22,
"protocol": "tcp",
"listenOptions": {
"bindUsingEdgeIdentity": true
}
}
[defaults]
inventory = ./hosts.yaml, openziti_connection_autodiscovery
...
[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml, community.openziti.connection_autodiscovery
...
[openziti_connection]
ziti_identities = ./identities/site-0-ansible-admin-identity.json, ./identities/site-N-ansible-admin-identity.json
...
[openziti]
ziti_log_level = 0
This section is meant to give an account of the inner workings of this integration. It involves certain "advanced" topics in OpenZiti and Ansible. We describe the high level functionality of the integration, not the topics themselves.
The connect_autodiscovery.py
is loaded when the magic string openziti_connection_autodiscovery
is received by the BaseInventoryPlugin.verify_file()
function implemented in our plugin class.
The plugin reads Ansible options to determine which ziti identities to read for the purposes of target discovery. Each identity is then used by the OpenAPI generated python module to communicate with the OpenZiti Edge API. The plugin uses a scoped session to identity services an identity has permission to Dial
, which also have a config
assigned to them of type ansible-target-discovery.v1
. For these services, a dictionary is created for each identity
that Binds
the service. This dictionary yields information to each of the community.openziti.*
connection plugins as follows:
Example (only relevant fields):
{
"ansible-target-1": {
"ansible_connection": "community.openziti.paramiko",
"ziti_connection_dial_service": {
"ziti_connection_identity_file": "./identities/site-0-ansible-admin-identity.json",
"ziti_connection_service": "ansible-ssh",
"ziti_connection_service_terminator": "ansible-target-1"
}
}
}
When the community.openziti.*
connection plugins load, and the ziti_connection_dial_service
varaible is defined, the plugin will instantiate a ZitiContext
using the ziti-sdk-py
library. This socket is then used for the connection to the target by leveraging OpenZiti addressable terminators
. In order to accomplish this with minimal configuration, the connection plugins set the remote_addr
option to something that is always resolvable, like 0.0.0.1
, so that hostname resolution need not be congruent. Alternatively, connections to inventory_hostname
s which happen to be OpenZiti intercept addresses
are still intercepted normally.
Generally, the connection plugin classes utilize monkeypatched versions of the original connection plugins, which extend their functionality to hold some needed state, and operate over the Ziti socket for transport. This is a deliberate choice, so that all normal configurations for these plugins continue to work without modification or duplication.