Networking

Preview — pending review

This reference is generated from the platform API contract and is still under review. Operations, fields, and descriptions can change before publication.

Create an IPsec site connection from existing objects

POST
/scopes/{scopeId}/network/vpn/site-connections

Creates one IPsec site connection by REFERENCING four objects that already exist - a VPN service, an IKE policy, an IPsec policy, a local endpoint group and a peer endpoint group. Nothing is created implicitly and nothing is cloned: the same policy or endpoint group may back any number of connections. This is the one endpoint both the manual flow and the step-by-step wizard use; there is no wizard-only shortcut. Both endpoint groups are required and peerCidrs does not exist here. neutron-VPNaaS's validator makes the two models mutually exclusive: because this API always creates subnet-less VPN services, a connection MUST carry a local endpoint group (type subnet) and a peer endpoint group (type cidr), and must not carry flat peer CIDRs. Validation: name non-empty and unique in the scope (409 CONFLICT); vpnServiceId, ikePolicyId, ipsecPolicyId, localEndpointGroupId and peerEndpointGroupId must each reference an existing, READY object in the scope (unknown -> 404 NOT_FOUND; not yet provisioned -> 409 CONFLICT, since neutron needs the provider id); localEndpointGroupId must be of type subnet and peerEndpointGroupId of type cidr (wrong way round -> 422 VALIDATION_FAILED naming the field, matching neutron's WrongEndpointGroupType); the local subnet must be attached to the service's router (-> 422); peerAddress must be a valid IPv4 address or a resolvable FQDN (-> 422); presharedKey at least 8 characters; initiator and dpdAction must be enumerated values; dpdTimeout must be strictly greater than dpdInterval (neutron's own _check_dpd, -> 422); mtu must be at least 68 and at most the deployment's configured tenant network MTU - a larger tunnel MTU than the underlay can carry makes large packets disappear silently rather than fail loudly (-> 422 with the configured ceiling in the message). Async & state: Asynchronous; returns 202 with the connection in provisioningState PENDING_CREATE. neutron drives status PENDING_CREATE -> ACTIVE on a successful negotiation, or DOWN/ERROR; a provider rejection lands on the record as provisioningState ERROR with statusReason, never as a silently dropped change. resource.updated SSE frames carry each transition. Errors: 401; 403; 404 when a referenced object does not exist; 409 duplicate name or a referenced object not yet provisioned; 422 on any field-level or cross-object validation failure; 500 on backend failure. Idempotency/Concurrency: Idempotency-Key is required; a replay returns the original 202. presharedKey is write-only: accepted once, stored app-encrypted, never echoed in this response or any later read.

Authorization

bearerToken
headerAuthorizationBearer <token>

An API token (pc_live_…) or a service-account key (sak_live_…), created in Identity & Access Management and sent as Authorization: Bearer <token>. Requests are rate limited per credential and per tenant: every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a refused request gets 429 RATE_LIMITED with Retry-After.

Path Parameters

scopeId*string

Opaque ownership+authorization scope resolving to tenant + subscription + project.

Header Parameters

Idempotency-Key*string

Required: this operation creates a new resource asynchronously, so a retry without the key would create a second one. Same key+body within 24h replays the original result; the same key with a different body is 409 CONFLICT. Keys are unique per tenant (the same key in two tenants is two different requests). An API token or service-account (bearer) caller MUST send it -- a request without it is refused with 400 IDEMPOTENCY_KEY_REQUIRED (ErrIdempotencyKeyRequired); reuse the same key on every retry of one logical request. A console session gets one from the edge.

Formatuuid

Request Body

application/json
  1. body

Payload for creating an IPsec site connection from objects that already exist. Both endpoint groups are required and there is no peerCidrs field: because this API always creates subnet-less VPN services, neutron's validator requires a local endpoint group (type subnet) and a peer endpoint group (type cidr) and forbids flat peer CIDRs. This is the same endpoint the step-by-step wizard calls; there is no wizard-only shortcut.

name*string

Connection display name; must be unique within the scope.

Match^[^\p{C}\p{Z}\x{10000}-\x{10FFFF}](?:[^\p{C}\p{Zl}\p{Zp}\x{10000}-\x{10FFFF}]*[^\p{C}\p{Z}\x{10000}-\x{10FFFF}])?$
Length1 <= length <= 255
description?string

Free-text description.

Lengthlength <= 255
Default""
vpnServiceId*string

An existing, provisioned VPN service in this scope.

Length1 <= length
ikePolicyId*string

An existing, provisioned IKE policy in this scope; it may already be in use by other connections.

Length1 <= length
ipsecPolicyId*string

An existing, provisioned IPsec policy in this scope; it may already be in use by other connections.

Length1 <= length
localEndpointGroupId*string

An existing endpoint group of type subnet, all of whose subnets are attached to the service's router. Put every local subnet that should reach this peer in this one group - one connection covering several subnets works, a second connection to the same peer gateway does not.

Length1 <= length
peerEndpointGroupId*string

An existing endpoint group of type cidr naming the remote network or networks.

Length1 <= length
peerAddress*string

Public address of the remote VPN gateway - an IPv4 address or a resolvable FQDN.

Length1 <= length
initiator?string

Negotiation initiator mode.

Default"bi-directional"
Value in"bi-directional""response-only"
dpdAction?string

What to do when dead peer detection fires.

Default"hold"
Value in"hold""clear""disabled""restart""restart-by-peer"
dpdInterval?integer

Dead-peer-detection probe interval in seconds.

Range1 <= value
Default30
dpdTimeout?integer

Dead-peer-detection timeout in seconds; must be strictly greater than dpdInterval.

Range2 <= value
Default120
mtu?|

Tunnel MTU. Omit to inherit the deployment's configured tenant network MTU, which is the right answer on an underlay that cannot carry 1500; a larger value is rejected. Note this is the MTU of the tunnel, NOT the largest packet that fits inside it: ESP adds its own header, IV, padding and ICV, so the usable payload is smaller. Measured on this deployment with mtu 1400, the largest inner IP packet that crossed the tunnel was 1374 bytes (ping -M do -s 1346); 1375 and above failed. A 1372-byte payload - the largest that fits a plain 1400-MTU tenant network - therefore does NOT fit the tunnel. It fails loudly with a local DF / EMSGSIZE error rather than silently, so path-MTU discovery works, but a workload sized to the tenant MTU must be told to lower its own MTU or clamp MSS.

Range68 <= value
adminStateUp?boolean

Whether the connection starts administratively up.

Defaulttrue
presharedKey*string

Shared secret configured identically on both endpoints; at least 8 characters. Write-only - stored app-encrypted and never returned by any read.

Formatpassword
Length8 <= length

Response Body

Creation accepted; returns the new site connection in provisioningState PENDING_CREATE. The pre-shared key is never included.

application/json
  1. response

An IPsec site connection, mirroring neutron-VPNaaS ipsec_site_connections. It REFERENCES the VPN service, the IKE and IPsec policies and the two endpoint groups - it never copies or clones them, which is what lets one policy back many connections. The pre-shared key is write-only and appears in no read.

origin?string

Where the resource was made -- portal for a create on the console, api for an object made straight against the OpenStack API (CLI, Terraform, the Kubernetes cloud provider) that discovery adopted. Informational; nothing behaves differently. Absent from a backend that predates it, which means portal.

Value in"portal""api""system""imported"
id*string

Platform identifier of the site connection.

name*string

Connection display name, unique within the scope.

Match^[^\p{C}\p{Z}\x{10000}-\x{10FFFF}](?:[^\p{C}\p{Zl}\p{Zp}\x{10000}-\x{10FFFF}]*[^\p{C}\p{Z}\x{10000}-\x{10FFFF}])?$
Length1 <= length <= 255
description*string

Free-text description; empty string when unset.

vpnServiceId*string

The VPN service this connection terminates on. Immutable after creation (neutron marks it allow_put false).

ikePolicyId*string

The IKE (Phase 1) policy this connection negotiates with. Immutable after creation.

ipsecPolicyId*string

The IPsec (Phase 2) policy this connection negotiates with. Immutable after creation.

localEndpointGroupId*string

The endpoint group (type subnet) naming the local end of the tunnel.

peerEndpointGroupId*string

The endpoint group (type cidr) naming the remote end of the tunnel.

localId?|

Local IKE identity as neutron holds it. READ-ONLY and always neutron's default here - it cannot be set or changed through this API, because neutron-vpnaas writes the pre-shared key into ipsec.secrets keyed on the local/peer ADDRESS pair and never on these identities, so a custom identity authenticates against no key at all ("no shared key found").

peerAddress*string

Public address of the remote VPN gateway - an IPv4 address or a resolvable FQDN.

peerId*string

Remote IKE identity as neutron holds it. READ-ONLY and always equal to peerAddress here, for the same reason as localId.

initiator*string

Negotiation initiator mode.

Value in"bi-directional""response-only"
dpdAction*string

What to do when dead peer detection fires.

Value in"hold""clear""disabled""restart""restart-by-peer"
dpdInterval*integer

Dead-peer-detection probe interval in seconds.

Range1 <= value
dpdTimeout*integer

Dead-peer-detection timeout in seconds; neutron requires it to be strictly greater than dpdInterval.

Range2 <= value
mtu*integer

Maximum transmission unit for the tunnel. Capped at the deployment's configured tenant network MTU - a tunnel MTU above what the underlay carries makes large packets vanish silently instead of failing loudly. The usable payload INSIDE the tunnel is lower again by the IPsec overhead: measured on this deployment with mtu 1400, the largest inner IP packet that crossed was 1374 bytes (ping -M do -s 1346), so the 1372-byte payload a plain 1400-MTU tenant network allows does not fit. Oversized packets fail with a DF / EMSGSIZE error rather than disappearing.

Range68 <= value
adminStateUp*boolean

Administrative state; false tears the tunnel down without deleting anything.

status?|null

neutron's own status for the connection, verbatim. Null until the provider has reported one. ACTIVE means the security associations are established - it is not by itself evidence that traffic passes.

statusReason?|

The provider's own message from the last failed write or negotiation; null when the last write succeeded.

statusReasonCode?|

Machine-readable companion to statusReason: a small, OPEN vocabulary of codes the console can branch on without parsing the human text, deliberately a plain string rather than a closed enum so a code the client does not recognize yet degrades to the generic failure message. Today the only value is cancelled: the resource is in its failure state because its create operation was CANCELLED; a provider object the create had already built may be left in place, and the resource's DELETE removes it. Reported only while the resource is in that failure state; null otherwise.

provisioningState*string

Lifecycle state of this platform record.

Value in"PENDING_CREATE""PENDING_UPDATE""PENDING_DELETE""READY""ERROR"
providerId?|

neutron's UUID for this site connection; null until the worker has created it.

curl -X POST "https://example.com/scopes/string/network/vpn/site-connections" \  -H "Idempotency-Key: 497f6eca-6276-4993-bfeb-53cbbbba6f08" \  -H "Content-Type: application/json" \  -d '{    "name": "string",    "vpnServiceId": "string",    "ikePolicyId": "string",    "ipsecPolicyId": "string",    "localEndpointGroupId": "string",    "peerEndpointGroupId": "string",    "peerAddress": "string",    "presharedKey": "pa$$word"  }'
{  "origin": "portal",  "id": "string",  "name": "string",  "description": "string",  "vpnServiceId": "string",  "ikePolicyId": "string",  "ipsecPolicyId": "string",  "localEndpointGroupId": "string",  "peerEndpointGroupId": "string",  "localId": "string",  "peerAddress": "string",  "peerId": "string",  "initiator": "bi-directional",  "dpdAction": "hold",  "dpdInterval": 1,  "dpdTimeout": 2,  "mtu": 68,  "adminStateUp": true,  "status": "ACTIVE",  "statusReason": "string",  "statusReasonCode": "string",  "provisioningState": "PENDING_CREATE",  "providerId": "string"}