Skip to main content
An allowed address pair is an entry — an IP address or CIDR range with an optional MAC address — attached to a Virtual Machine’s network interface port. By default, each port enforces a strict MAC-to-IP binding, so only traffic addressed to the port’s own IP is accepted. Adding an entry to the allowed address pairs list lifts that restriction for the specified address, making the machine accessible on both its original IP and the additional one. A common use case is distributing a virtual IP address across multiple VMs using Keepalived — each VM’s port must allow the shared VIP address.
An API token is required, along with a project ID and region ID.
Set these environment variables before running the examples:
export GCORE_API_KEY="{YOUR_API_KEY}"
export GCORE_CLOUD_PROJECT_ID="{YOUR_PROJECT_ID}"
export GCORE_CLOUD_REGION_ID="{YOUR_REGION_ID}"
export INSTANCE_ID="{YOUR_INSTANCE_ID}"
export PORT_ID="{YOUR_PORT_ID}"
To find the values for each variable:
  • INSTANCE_ID: UUID of the Virtual Machine whose port to configure.
  • PORT_ID: the port ID of the VM’s private network interface. Only private (non-external) ports support allowed address pairs.

Find the port ID

Each VM network interface has a port_id. Allowed address pairs are applied per port, and only ports on private (non-external) networks support this feature.
import os
import requests

session = requests.Session()
session.headers.update({"Authorization": f"APIKey {os.environ['GCORE_API_KEY']}"})

resp = session.get(
    f"https://api.gcore.com/cloud/v1/instances"
    f"/{os.environ['GCORE_CLOUD_PROJECT_ID']}/{os.environ['GCORE_CLOUD_REGION_ID']}"
    f"/{os.environ['INSTANCE_ID']}/interfaces"
)
resp.raise_for_status()
for iface in resp.json()["results"]:
    print(f"port_id={iface['port_id']}  ip={iface['ip_assignments'][0]['ip_address']}")
Save the port_id of the private interface (the one with a private IP range — 10.x.x.x or 192.168.x.x) as PORT_ID. The external interface does not support allowed address pairs. The same endpoint also returns allowed_address_pairs per interface, so the current state is visible after applying changes.

Set allowed address pairs

The PUT request replaces the entire list of allowed address pairs for the port. To add one pair without losing existing ones, include all current pairs in the request body alongside the new entry.
ParameterRequiredDescription
ip_addressYesIP address or CIDR range to allow (e.g. 192.168.1.10 or 10.0.0.0/24)
mac_addressNoMAC address to associate; defaults to the port’s own MAC when omitted
Up to 10 pairs are allowed per port.
import os
import time
import requests

session = requests.Session()
session.headers.update({"Authorization": f"APIKey {os.environ['GCORE_API_KEY']}"})
base = "https://api.gcore.com/cloud"

resp = session.put(
    f"{base}/v2/ports"
    f"/{os.environ['GCORE_CLOUD_PROJECT_ID']}/{os.environ['GCORE_CLOUD_REGION_ID']}"
    f"/{os.environ['PORT_ID']}/allow_address_pairs",
    json={
        "allowed_address_pairs": [
            {"ip_address": "192.168.123.20", "mac_address": "00:16:3e:f2:87:16"},
            {"ip_address": "192.168.0.0/24"},
        ]
    },
)
resp.raise_for_status()
task_id = resp.json()["tasks"][0]

while True:
    state = session.get(f"{base}/v1/tasks/{task_id}").json()["state"]
    if state == "FINISHED":
        break
    time.sleep(5)
print("Pairs applied.")
When mac_address is omitted, the API assigns the port’s default MAC address to that entry automatically.

Remove allowed address pairs

To remove all allowed address pairs from a port, send the same PUT request with an empty allowed_address_pairs array. The operation is asynchronous — poll the returned task until state is FINISHED.
import os
import time
import requests

session = requests.Session()
session.headers.update({"Authorization": f"APIKey {os.environ['GCORE_API_KEY']}"})
base = "https://api.gcore.com/cloud"

resp = session.put(
    f"{base}/v2/ports"
    f"/{os.environ['GCORE_CLOUD_PROJECT_ID']}/{os.environ['GCORE_CLOUD_REGION_ID']}"
    f"/{os.environ['PORT_ID']}/allow_address_pairs",
    json={"allowed_address_pairs": []},
)
resp.raise_for_status()
task_id = resp.json()["tasks"][0]

while True:
    state = session.get(f"{base}/v1/tasks/{task_id}").json()["state"]
    if state == "FINISHED":
        break
    time.sleep(5)
print("Pairs removed.")
To remove individual pairs rather than all of them, omit only the entries to remove and include the rest in the request body.