Cannot create Intercept address wildcards, validation error

Good day everyone!

Using 0.28, I cannot create intercept.v1 address wildcard, receiving error:

ziti edge create config microsoft.com.dial intercept.v1 '{"protocols":["tcp"],"addresses":[".microsoft.com"], "portRanges":[{"low":443, "high":443}]}'
error: error creating configs instance in Ziti Edge Controller at https://controller01:443/edge/management/v1. Status code: 400 Bad Request, Server returned: {
"error": {
"cause": {
"field": "addresses.0",
"reason": "addresses.0 is invalid: addresses.0: Must validate one and only one schema (oneOf)",
"value": "
.microsoft.com"
},
"code": "COULD_NOT_VALIDATE",
"message": "The supplied request contains an invalid document or no valid accept content were available, see cause",
"requestId": "LHAOG5J.P"
},
"meta": {
"apiEnrollmentVersion": "0.0.1",
"apiVersion": "0.0.1"
}
}

My prior wildcard intercepts continue to function as expected. I just can't create new wildcard intercepts.

You were so close! just add a * to your wildcard… :slight_smile:

This would work:

ziti edge create config microsoft.com.dial intercept.v1 '{"protocols":["tcp"],"addresses":["*.microsoft.com"], "portRanges":[{"low":443, "high":443}]}'
New config microsoft.com.dial created with id: 110L02OenVluywfv87onGO

Thanks, @TheLumberjack.

I just learned block quotes eat asterisks. I copied and pasted your command, but received an outcome different from you:

ziti edge create config microsoft.com.dial intercept.v1 '{"protocols":["tcp"],"addresses":["*.microsoft.com"], "portRanges":[{"low":443, "high":443}]}'
error: error creating configs instance in Ziti Edge Controller at https://controller01:443/edge/management/v1. Status code: 400 Bad Request, Server returned: {
"error": {
"cause": {
"field": "addresses.0",
"reason": "addresses.0 is invalid: addresses.0: Must validate one and only one schema (oneOf)",
"value": "*.microsoft.com"
},
"code": "COULD_NOT_VALIDATE",
"message": "The supplied request contains an invalid document or no valid accept content were available, see cause",
"requestId": "ihkSE37-J"
},
"meta": {
"apiEnrollmentVersion": "0.0.1",
"apiVersion": "0.0.1"
}
}

Existing configurations continue to work. I cannot create new configurations with 0.28.

This is very strange. I’m also not having a problem creating the config from your post (after changing quotes to ascii single and double quotes):

% ziti edge create config microsoft.com.dial intercept.v1 '{"protocols":["tcp"],"addresses":["*.microsoft.com"], "portRanges":[{"low":443, "high":443}]}'
New config microsoft.com.dial created with id: 3vqeJuEDmJeEHKSQAeCjbF

% ziti edge show config microsoft.com.dial
{
    "addresses": [
        "*.microsoft.com"
    ],
    "portRanges": [
        {
            "high": 443,
            "low": 443
        }
    ],
    "protocols": [
        "tcp"
    ]
}

Can you check your command for non-ascii characters? If that doesn’t turn up anything, what does your intercept.v1 config type look like? Mine looks like this:

% ziti edge show config-type intercept.v1
{
    "$id": "http://edge.openziti.org/schemas/intercept.v1.config.json",
    "additionalProperties": false,
    "definitions": {
        "cidr": {
            "oneOf": [
                {
                    "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(3[0-2]|[1-2][0-9]|[0-9]))$"
                },
                {
                    "pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\\/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))$"
                }
            ],
            "type": "string"
        },
        "dialAddress": {
            "oneOf": [
                {
                    "$ref": "#/definitions/ipAddress"
                },
                {
                    "$ref": "#/definitions/hostname"
                }
            ]
        },
        "hostname": {
            "format": "hostname",
            "not": {
                "$ref": "#/definitions/ipAddressFormat"
            },
            "type": "string"
        },
        "inhabitedSet": {
            "minItems": 1,
            "type": "array",
            "uniqueItems": true
        },
        "ipAddress": {
            "$ref": "#/definitions/ipAddressFormat",
            "type": "string"
        },
        "ipAddressFormat": {
            "oneOf": [
                {
                    "format": "ipv4"
                },
                {
                    "format": "ipv6"
                }
            ]
        },
        "listenAddress": {
            "oneOf": [
                {
                    "$ref": "#/definitions/ipAddress"
                },
                {
                    "$ref": "#/definitions/hostname"
                },
                {
                    "$ref": "#/definitions/wildcardDomain"
                },
                {
                    "$ref": "#/definitions/cidr"
                }
            ]
        },
        "portNumber": {
            "maximum": 65535,
            "minimum": 0,
            "type": "integer"
        },
        "portRange": {
            "additionalProperties": false,
            "properties": {
                "high": {
                    "$ref": "#/definitions/portNumber"
                },
                "low": {
                    "$ref": "#/definitions/portNumber"
                }
            },
            "required": [
                "low",
                "high"
            ],
            "type": "object"
        },
        "protocolName": {
            "enum": [
                "tcp",
                "udp"
            ],
            "type": "string"
        },
        "timeoutSeconds": {
            "maximum": 2147483647,
            "minimum": 0,
            "type": "integer"
        },
        "wildcardDomain": {
            "pattern": "^\\*\\.(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$",
            "type": "string"
        }
    },
    "properties": {
        "addresses": {
            "allOf": [
                {
                    "$ref": "#/definitions/inhabitedSet"
                },
                {
                    "items": {
                        "$ref": "#/definitions/listenAddress"
                    }
                }
            ]
        },
        "dialOptions": {
            "additionalProperties": false,
            "properties": {
                "connectTimeoutSeconds": {
                    "$ref": "#/definitions/timeoutSeconds",
                    "description": "defaults to 5 seconds if no dialOptions are defined. defaults to 15 if dialOptions are defined but connectTimeoutSeconds is not specified."
                },
                "identity": {
                    "description": "Dial a terminator with the specified identity. '$dst_protocol', '$dst_ip', '$dst_port are resolved to the corresponding value of the destination address.",
                    "type": "string"
                }
            },
            "type": "object"
        },
        "portRanges": {
            "allOf": [
                {
                    "$ref": "#/definitions/inhabitedSet"
                },
                {
                    "items": {
                        "$ref": "#/definitions/portRange"
                    }
                }
            ]
        },
        "protocols": {
            "allOf": [
                {
                    "$ref": "#/definitions/inhabitedSet"
                },
                {
                    "items": {
                        "$ref": "#/definitions/protocolName"
                    }
                }
            ]
        },
        "sourceIp": {
            "description": "The source IP (and optional :port) to spoof when the connection is egressed from the hosting tunneler. '$tunneler_id.name' resolves to the name of the client tunneler's identity. '$tunneler_id.tag[tagName]' resolves to the value of the 'tagName' tag on the client tunneler's identity. '$src_ip' and '$src_port' resolve to the source IP / port of the originating client. '$dst_port' resolves to the port that the client is trying to connect.",
            "type": "string"
        }
    },
    "required": [
        "protocols",
        "addresses",
        "portRanges"
    ],
    "type": "object"
}

Ignore my previous request. After reading my own post I noticed that my intercept.v1 config type schema specified the “hostname” string format. Then I realized that I wasn’t actually running against a .28 controller. Sorry for the confusion.

One of the changes in 0.28.0 was to use “idn-hostname” instead of “hostname” when validating addresses in the configurations. The intent was to permit underscores in the hostname values and that was accomplished, but as you discovered “idn-hostname” does not permit asterisk. Well at least not unless an underscore is also in the hostname.

It turns out the spec is more tricky than one might think. So we’re going to update the config type with a schema specifically for wildcard domains. I want to do more testing with it but the updated config type will look very much like this.

In the meantime if you’re held up by this you can grab the new json schema and update the intercept.v1 config type in your controller with something like this:

% curl -sO https://raw.githubusercontent.com/openziti/edge/dfae06aa3197b3d40947221a107c049a0ec04d2a/tunnel/entities/intercept.v1.json

% ziti edge update config-type intercept.v1 -f intercept.v1.json

After doing this you should be able to create config types with wildcard addresses, etc.

Hey @tomc, just an fyi that the PR for the schema change was merged and will be in the 0.28.1 release. You can get the updated schema from the original PR at https://raw.githubusercontent.com/openziti/edge/31c4d50d2669c293472fdb2ad5afda2c15c168fb/tunnel/entities/intercept.v1.json if you’re interested.