Network Security Groups (NSGs) are tenant-owned, rule-based filters that sit on top of the VPC / VRF model. They provide stateful or stateless L3 / L4 filtering for traffic into and out of tenant instances, complementing the routing isolation that the VPC itself provides.
This page describes the NSG object model, how rules are attached to traffic, how the operator enables and constrains the feature, and how to verify and troubleshoot rule enforcement.
Related pages
A tenant’s traffic on a NICo-managed host passes through three independent isolation layers in order:
deny_prefixes site ACL. The site-wide deny_prefixes list,
configured under the API server’s networking config, blocks tenant
traffic to a fixed set of prefixes (typically management plane and
infrastructure). This applies to every VRF on every DPU and cannot be
overridden by a tenant.NSGs are the right tool for filtering East-West traffic inside a VPC, for restricting which underlay-leaked prefixes a tenant is permitted to reach, and for enforcing site-wide baselines (operator overrides) that no tenant can disable. They are not a substitute for VPC routing isolation: an NSG cannot make two VPCs reachable that the routing profile keeps apart.
NSGs are almost entirely a tenant operation. The only operator-owned pieces are the site-level limits and overrides (TOML) and a break-glass path for incident response. See Network Isolation → Who configures what, and how for the role and interface model.
An NSG is a tenant-owned object with the following shape:
Each rule has:
Rules are evaluated in priority order. The first matching Permit or
Deny decides the packet; there is no implicit fall-through behaviour
between rules of the same NSG.
NSGs attach at exactly two scopes:
network_security_group_id on the VPC by updating it
(REST PATCH …/nico/vpc, or nicocli vpc update). Every instance that has
interfaces in this VPC inherits the NSG’s rules.network_security_group_id on the instance by
updating it (REST PATCH …/nico/instance, or nicocli instance update).
The instance NSG replaces the VPC NSG for that instance. Instance-scope
NSGs are not merged with VPC-scope NSGs; the instance-scope NSG wins
outright.An NSG can be referenced by multiple VPCs and multiple instances
concurrently. The NSG object itself does not list its references — the
attachment lives on the referencing side, as networkSecurityGroupId on
each VPC or instance. To find what an NSG is attached to, inspect the VPCs
and instances (their networkSecurityGroupPropagationDetails also report
rollout state — see How NSG Changes Propagate).
nicocli network-security-group delete (REST DELETE …/nico/network-security-group/{id})
succeeds only when the NSG is not referenced by any VPC or instance. The
expected workflow is:
nicocli vpc update with the field cleared).nicocli instance update).NSG propagation is tracked differently from the other network fabrics.
For VRF / segment / routing changes, convergence is reflected in the
instance’s configs_synced.ethernet field, which gates the instance’s
Ready state. NSGs do not work that way. Read this section before
relying on any readiness signal to confirm an NSG change has taken effect.
There are two attachment scopes, and they interact with the instance config version differently:
network_security_group_id is part of the instance’s
versioned configuration, so attaching or detaching an NSG directly on an
instance bumps that instance’s config_version.networkSecurityGroupInherited = true) without the NSG id appearing in
the instance’s own config. A VPC-scope attach or detach therefore does
not change the inheriting instances’ config_version.In neither case does NSG attachment feed configs_synced.ethernet or the
instance’s Ready state. Even at instance scope — where the config version
does bump — the actual application of the rules on the DPU is reported
through the NSG’s own propagation status, not through the config-sync
machinery.
Editing the rules of an NSG that is already attached bumps only the NSG’s
own version. It does not bump any instance’s config_version and
does not change any instance’s configs_synced. The only place the
rollout is visible is the NSG propagation status: each attached DPU
observes that the NSG version applied on its interfaces is older than the
NSG version now on the VPC / instance, and that mismatch is what the
propagation status reflects until the new rules are applied.
Propagation is exposed per attached object on the VPC and Instance
resources as networkSecurityGroupPropagationDetails, with:
status — Synchronizing, Synchronized, or Error.detailedStatus — None, Partial, Full, Unknown, or Error
(how many of the expected interfaces have applied the current NSG version).unpropagatedInstanceIds — instances that have not yet applied the
current version; the set to watch during a rollout.NICo computes this on read by comparing the NSG id and version on the VPC / instance against what each DPU reports observing on its interfaces. There is no separate “NSG synced” flag on the instance; the propagation detail is the authoritative signal.
NSG rules are resolved on the API server and pushed to the DPU as part of the per-interface configuration response, alongside the VRF, segment, and routing-profile data described in VPC Network Virtualization. The DPU agent materialises them into NVUE ACLs.
The DPU receives, per interface:
source tag (NSG_SOURCE_NONE, NSG_SOURCE_VPC, or
NSG_SOURCE_INSTANCE) indicating which scope produced the rules.stateful_egress flag.id and version, used by the propagation-status reporting.The agent renders IPv4 and IPv6 rules into separate NVUE policies and combines ingress and egress rules into the appropriate direction. Site-wide operator overrides (see below) are rendered into a separate policy that the DPU evaluates before any tenant policy.
The operator controls three site-wide knobs that affect NSG behaviour.
These live in the API server configuration file under
[network_security_group]:
max_network_security_group_sizeNICo expands rule entries before pushing them to the DPU (the cartesian product of source ports × destination ports × source prefixes × destination prefixes). This cap is the operator’s protection against a tenant accidentally requesting a vast rule set. The DPU agent also enforces its own ceiling, on the order of 10,000 expanded rules, as a final safeguard.
A tenant whose NSG would expand beyond this limit receives an error when creating or updating the NSG. Tune this value if tenants legitimately need larger rule sets; do not raise it unilaterally without coordinating with whatever ceiling is configured on the DPU side.
stateful_acls_enabledToggling this flag controls whether NICo will configure the DPU’s default
stateful-ACL options in the NVUE config it pushes. Stateful NSG behaviour
also requires the tenant to set stateful_egress = true on the NSG;
without the site-level switch, the DPU treats every rule as stateless
regardless of the per-NSG flag.
Leave stateful_acls_enabled = false until every DPU in the site is
running HBN 2.3 or later. Earlier HBN versions implement reflexive ACLs in
a way that lets a single rule permit traffic in both directions, which is
operationally unsafe.
policy_overridespolicy_overrides is a list of NetworkSecurityGroupRule entries (same
shape as tenant rules) that the operator wishes to enforce site-wide.
These rules are inserted into a separate policy on the DPU, evaluated:
deny_prefixes (the absolute site denylist).This ordering gives the operator a reliable place to:
Permit rule that
contradicts an operator override, because the override is evaluated
first and decides the packet.Each entry follows the same JSON / TOML structure as a tenant rule. A worked example:
Changing policy_overrides requires restarting the API server (it is a
static configuration field, not a runtime-mutable RPC). After restart, the
new override set propagates to every DPU as part of the next
configuration-poll cycle.
When a managed host is placed into a quarantine lifecycle state, NICo
substitutes a quarantine-specific override policy in place of
policy_overrides. The intent is to give an operator a way to constrain
traffic from a host that is under investigation without having to detach
it from its tenant VPCs first. This is internal behaviour and is not
operator-configurable; quarantine is driven by the machine lifecycle and
health-alert subsystem.
The NSG feature is in production use, but the following limitations are worth knowing before designing rule sets:
src_net / dst_net accept CIDR prefixes only. The model has
a structural extension point for VPC references (so that a tenant could
say “permit from any instance in VPC X”), but VPC-reference resolution
is not yet implemented. Use explicit CIDRs.ipv6 boolean and
applies only to one address family; if a tenant needs both, two rules
are required.stateful_egress requires the site flag. A tenant may set
stateful_egress = true on the NSG, but it has no effect until the
site-level stateful_acls_enabled = true.version and fails if it has been concurrently modified. This is
the standard NICo optimistic-concurrency pattern.The site operator’s NSG configuration is normally done once and rarely changed; the tenant flow is what runs day-to-day.
stateful_acls_enabled = false.max_network_security_group_size accordingly.deny_prefixes). Encode these as
policy_overrides.All tenant steps use the REST API or nicocli; none require TOML or
nico-admin-cli.
nicocli network-security-group create (REST POST …/nico/network-security-group)
with the desired rule set. The response includes the NSG id and version.nicocli vpc update) or instance scope
(nicocli instance update).Synchronized (detailedStatus: Full). Do not wait on
configs_synced.ethernet — NSG rollout is not reported there. See
How NSG Changes Propagate.Updates use nicocli network-security-group update with the current version
token. Deletion uses nicocli network-security-group delete after detaching
every reference.
For a given tenant configuration, confirm:
nicocli network-security-group get <id> (REST
GET …/nico/network-security-group/{id}) shows the rules,
stateful_egress, and current version.networkSecurityGroupPropagationDetails on each VPC and instance
(nicocli vpc get <id> / nicocli instance get <id>); each lists the
attached networkSecurityGroupId and, for instances, whether it is
inherited from the VPC (networkSecurityGroupInherited).networkSecurityGroupPropagationDetails.status reads Synchronized with
detailedStatus: Full, and unpropagatedInstanceIds is empty. While the
rollout is in flight the status is Synchronizing. Note this is a
separate signal from configs_synced.ethernet, which NSG changes do
not affect.policy_overrides. A discrepancy means the API server
was not restarted after the configuration change.