Edge

Ringleader Edge is a small VM in your cloud account that enforces hostname egress rules and adds injected credentials for your workstations.

Your cloud’s firewall matches addresses, not names. An Edge reads the host name off each outbound connection, so a workstation’s egress policy can name github.com instead of an address range. It is also where credential injection adds a key to a request.

An Edge resource declares one edge instance: a VM that Ringleader builds in your cloud account, in one cloud and region. A namespace administrator declares it once. From then on, every workstation in that namespace and region that needs it sends its outbound connections through it.

A cloud workstation needs an Edge in two cases:

  • Its egress policy names a host. A policy that names only cidr destinations does not need one, because your cloud’s firewall holds addresses directly.
  • An Integration injects a credential into it, or inspects a host for it.
apiVersion: core.ringleader.dev/v1
kind: Edge

Declare an Edge

On Google Cloud, the provider and the region are all an Edge needs:

apiVersion: core.ringleader.dev/v1
kind: Edge
metadata:
  name: gcp-us-east4
  namespace: platform
spec:
  provider: gcp
  region: us-east4

On AWS and Azure, also name the subnet your cloud onboarding reserved for the edge instance. Without it, no VM is built. The onboarding publishes it as gateway_subnet_id (Terraform), GatewaySubnetId (CloudFormation) or gatewaySubnetId (Azure’s ARM template):

apiVersion: core.ringleader.dev/v1
kind: Edge
metadata:
  name: aws-us-east-1
  namespace: platform
spec:
  provider: aws
  region: us-east-1
  subnet: subnet-0abc1234def567890

On AWS and Azure, the workstations the Edge serves also go in a subnet of their own: the one the onboarding publishes as governed_subnet_id (Terraform), GovernedSubnetId (CloudFormation) or governedSubnetId (ARM). Name it in the workstation’s providerConfig.aws.subnetId or providerConfig.azure.subnetId, or in a CloudIdentity’s spec.subnet. The edge instance cannot route a subnet that already has a route table of its own, and the onboarding’s general workstations subnet has one.

Apply the Edge with rl apply -f edge.yaml. The VM is built when the first workstation in the Edge’s region that needs it reports in. Until then rl edge get shows Ready False with reason NoMachine, and the message says it is waiting for a workstation. Once the VM is serving, Ready reads True with reason Serving.

If that is all you need, you are done. The rest of this page covers what changes for the workstations it serves, the fields you can set, what it reports, and how to delete it.

Who can write one

Namespace administrators may create, read, update and delete Edges. An ordinary namespace member cannot write one, because an edge instance is shared by every workstation in its region, and it is a VM your organization pays for. In your own personal namespace, you are the namespace administrator.

Which workstations it serves

A workstation is served by the Edge in its own namespace whose provider and region match the workstation’s. No field on a workstation or an Integration names an Edge.

  • A namespace may hold one Edge per provider and region. A second is refused when you apply it, and the refusal names the Edge that already serves that region. The regions an Edge lists in additionalRegions count as its own.
  • All the workstations an Edge serves in its own region must be in one subnet. If two are not, the Edge builds nothing, and its message names both. Move the workstations into one subnet, or give the others a namespace and an Edge of their own.
  • Once an Edge exists for a region, every workstation there with an egress policy goes through it, including one whose policy lists only cidr destinations. See What a workstation can reach.

While no edge instance serves a workstation

On a cloud, a workstation whose egress policy names a host needs an edge instance. What it gets before one serves it depends on whether an Edge is declared, and on the policy’s enforcement:

bestEffortstrict
An Edge is declared for its provider and region, and is not serving it yetHeld closed.Held closed.
No Edge is declaredUnrestricted, reporting NoEdge.Refused when you apply it. A workstation that was admitted and then lost its Edge is held closed.

A bestEffort workstation with no Edge is unrestricted in full. Ringleader builds a rule set whole or not at all, so one host in the policy leaves the workstation able to reach any address on any port, including its cidr destinations.

A workstation that is held closed reaches only what it needs to stay managed by Ringleader, and the endpoints of the Integrations attached to it. It keeps running, you can still edit it, and you can still reach it over SSH.

A bestEffort workstation whose Edge was deleted keeps the rule set it had while it was routed, which permits the whole public internet. Its EgressEnforced message says so.

A strict policy that names a host is accepted when the workstation names its provider, and the namespace holds an Edge for that provider that is not being deleted. The check is on the workstation’s merged policy: its own egress block plus the WorkstationConfig layers it inherits. It does not check the region, or whether the VM is ready. Read EgressEnforced on the workstation for what is actually held.

A workstation with an injected credential and no egress policy is not held closed. It runs on its usual network, without the credential, until an edge instance serves it.

What a workstation can reach

Behind an edge instance, a workstation reaches the public internet on TCP through the edge instance, which applies the workstation’s policy to each connection. Three things change for it:

  • A cidr destination in private address space is unreachable. On GCP and Azure that is any private range, such as 10.0.0.0/8. On AWS it is any range inside the workstation’s VPC. The workstation reports each one with the reason DestinationUnreachable.
  • A permitted host whose name resolves to a private, loopback, link-local or 100.64.0.0/10 address is refused, so a service inside your network cannot be reached by name.
  • UDP traffic other than DNS is dropped, whether or not the policy permits where it was going.

Reaching a workstation behind an Edge

Routing a workstation’s traffic through an edge instance also routes the replies to connections opened to the workstation, so its own public address stops answering. The edge instance gives it a new way in instead. Each workstation it serves that has a public address of its own gets a TCP port on the edge instance’s public address, forwarded to the workstation’s port 22.

  • The workstation publishes that endpoint as status.managementAddress. rl shell, rl code and the rest of Ringleader use it with no change on your side. The workstation’s own SSH server still checks who is connecting.
  • Ports come from the range 10000 to 32767. Ringleader writes the firewall rule that admits them, open to any address unless the CloudAccount lists sshSourceRanges. When Ringleader cannot write that rule, it uses ports from 30000 to 32767, which your cloud onboarding’s own rule admits.
  • On Azure, the workstation sees each such connection as coming from the edge instance’s address, not from the caller’s.
  • The first time an edge instance routes a workstation that was reachable before, it waits until the workstation’s port is in place. The workstation reports SteeringHeld meanwhile. On GCP it waits for each workstation for as long as it takes. On AWS and Azure it routes a whole subnet at once and waits at most 30 minutes. After that, a workstation still without a port reports InboundUnreachable.

To keep the management ports off the internet, set inboundManagement: false, or on GCP and Azure publicAddress: false. The edge instance then forwards no ports, and on GCP and Azure it takes no public address. The workstations it serves that have a public address of their own report EgressEnforced True with reason InboundUnreachable. Reach them over your own private network instead.

How the DNS works

A workstation behind an edge instance keeps using your cloud’s own DNS resolver, which it reaches directly. The edge instance also answers DNS on TCP and UDP port 53, by asking your cloud’s resolver in turn. It answers a query sent to its own address, and a query sent over TCP to any public resolver. A query sent over UDP to a public resolver is dropped, because the workstation’s firewall rules allow only TCP to public addresses.

Any name resolves, including a name the workstation’s policy does not list. That does not weaken the policy, because the edge instance decides on the connection, not on the lookup. When the workstation opens a connection, the edge instance reads the name off it, resolves that name itself, and connects to the address it resolved. Pointing a permitted name at some other address does not reach that other address.

What filtering by name cannot stop

Because any name resolves, DNS is a slow way out of a workstation whose policy otherwise permits nothing. Your cloud’s resolver contacts the nameservers for the names a workstation looks up. So data encoded into those names can leave the workstation, and whoever runs those nameservers can read it. This is true of any filtering by name, not only of Ringleader’s. If it matters for your workloads, control it in your cloud’s DNS configuration. An egress policy cannot.

Time

Workstations behind an edge instance keep their clock from the cloud, not from a public time server:

  • On GCP, the metadata server at 169.254.169.254 answers NTP.
  • On AWS, the Amazon Time Sync Service answers at 169.254.169.123.
  • On Azure, the supported images read the host’s clock directly and need no network time source.

NTP to a public server on UDP port 123 is dropped.

What a denied connection looks like

The edge instance accepts the TCP connection before it decides anything, because it reads the name or the address off the open connection. So a connect that completes does not prove that a destination is permitted.

Once the edge instance decides to deny a connection, what you see depends on the port you connected to:

  • A connection to port 80 gets a real 403 response whose body reads egress policy refused this request. It names no destination and no rule.
  • A connection to every other TCP port, including 443, is closed with a TCP reset. Your client reports a connection reset by the peer.

On a host an Integration injects into or inspects, a request can name a different host in its Host header from the one the connection was opened to. It gets 421 Misdirected Request, and the connection is closed.

Only TCP and DNS go through the edge instance. HTTP/3, which runs over UDP, is the case you are most likely to meet. A client that falls back to TCP gets the behavior above. One that does not fall back times out without being told anything.

A connection that closes quietly is not a verdict either, because that is also what a connection dropped for any other reason looks like. To know what is enforced, read EgressEnforced on the workstation.

Spec

FieldNotes
providerRequired. gcp, aws or azure for an edge instance in your cloud account, which is what most of this page describes. The local providers are below.
regionRequired on a cloud. The region the VM runs in. Refused for a local provider.
additionalRegionsOther regions whose workstations this Edge serves. The VM still runs only in region. See Serving other regions.
zonesGCP only. The zone the VM goes in. Ringleader uses the first entry, which must be in region. Empty places the VM in the first zone, by name, that the region’s workstations run in. AWS and Azure ignore it and place the VM by subnet.
machineTypeThe VM’s size. See Machine type.
subnetWhere the VM goes, on AWS and Azure. No VM is built without it there. Refused on GCP. See The subnet on AWS and Azure.
publicAddressWhether the VM takes a public address, on GCP and Azure. Refused on AWS. See Public address and management ports.
inboundManagementfalse turns off the management ports for the workstations it serves. See Public address and management ports.
cloudCheckIntervalHow often a settled edge instance re-reads its cloud objects. Default 10m, from 1m to 1h. See Checking the cloud.
maintenanceWhen updates that interrupt the edge instance may run. See Maintenance windows.

Machine type

With no machineType, the VM takes the provider’s default:

ProviderDefaultArm (arm64) sizes
GCPe2-standard-2the t2a, c4a, n4a and a4x families
AWSm7i.largeGraviton types, with a g after the generation digit (m7g, c7g, t4g, m6gd), and a1
AzureStandard_D2as_v5sizes with a p after the vCPU count, such as Standard_D2ps_v5

The VM’s architecture follows the size you name, and the VM runs Debian 13 either way. The size is fixed when the VM is built, so changing machineType on a running Edge has no effect. To change it, delete the Edge and declare it again.

On Azure, vCPU quota is granted per VM family, and the default size’s family often has none. The build then fails with Azure’s own quota message on the Edge. Name a machineType in a family your subscription already runs, or raise the quota.

Serving other regions

additionalRegions is never filled in for you. Traffic from another region to the VM is billed per gigabyte by your cloud, where it is possible at all, so an administrator lists each region to accept that cost.

  • GCP carries it within one VPC network, so the workstations in other regions must be in the same network.
  • Azure carries it only over a global virtual network peering that you create, within one subscription. The Edge checks for the peering and records what it reaches in status.reaches.
  • AWS refuses additionalRegions. Declare one Edge per region.

The VM is built only once a workstation in region itself needs it. With workstations only in the additional regions, the Edge’s message says to put one in region, or to declare the Edge where the workstations are.

The subnet on AWS and Azure

AWS and Azure route traffic through the edge instance by subnet: a route replaces the default route for everything in a subnet. The VM needs a subnet of its own, because in the subnet it routes it would send its own traffic to itself. Name the subnet your cloud onboarding reserved for it, not the subnet your workstations are in. A build in the workstations’ subnet is refused, and so is one in a different VPC or virtual network from the workstations.

The VM cannot move once it is built. To put it in another subnet, delete the Edge and declare it again.

GCP routes by network tag instead, so the VM needs no subnet of its own, and subnet is refused there.

Public address and management ports

The management ports described in Reaching a workstation behind an Edge are on by default. Two fields change that:

GCPAzureAWS
Neither field setTakes a public address when it first needs a management port, and keeps it until the Edge is deleted or rebuilt.The same.Always has a public address, because its subnet is public.
publicAddress: trueAlways takes a public address, which also carries the VM’s own outbound traffic instead of Cloud NAT.No effect, and the Edge’s message says it can be removed.Refused.
publicAddress: false or inboundManagement: falseNo public address and no management ports.The same.inboundManagement: false turns the ports off. publicAddress is refused.

inboundManagement: true has no effect, and the Edge’s message says it can be removed. inboundManagement: true together with publicAddress: false is refused.

Setting either field to false on a running Edge turns the ports off, and the VM keeps any address it already took until it is rebuilt. To drop the address, delete the Edge and declare it again.

If your cloud refuses the address, for example because GCP’s constraints/compute.vmExternalIpAccess organization policy is enforced, the edge instance is still built and still filters traffic. It records the refusal in status.externalAddressRefused and tries again after 10 minutes. On GCP, a workstation with a public address of its own stays held closed meanwhile, reporting SteeringHeld. Set inboundManagement: false to route it with no management port.

Checking the cloud

Once an edge instance has settled, it re-reads its VM, firewall rules and routes every cloudCheckInterval. A change made outside Ringleader, such as a deleted route, is noticed within one interval. Each check spends calls against your cloud account’s API rate limits. A change Ringleader has to make is made immediately, whatever the interval.

Maintenance windows

maintenance limits when two updates that interrupt the edge instance may run: a new edge build, which restarts its filtering and drops open connections, and an update to Ringleader’s agent on the VM. Policy changes and repairs run whenever they are needed.

spec:
  maintenance:
    timezone: Europe/Warsaw
    windows:
      - days: [Sat, Sun]
        start: "02:00"
        duration: 4h
    maxDeferral: 7d
FieldNotes
timezoneAn IANA time zone name. Default UTC. An offset such as +02:00 is refused.
windows[]Required, at least one. Leave out the whole maintenance block instead of giving an empty list.
windows[].daysDay names, short (Sat) or full. Empty means every day.
windows[].start"HH:MM", 24-hour.
windows[].durationFrom 1h to 7d.
maxDeferralThe longest an update may wait for a window, at most 14d. Without it, the limit your deployment sets applies, 14 days unless your operator changed it.

An update that is waiting shows in status.maintenance, with the next window.

Edges for local workstations

provider also accepts vz, qemu and lima. An Edge for one of these runs on each member’s own machine instead of in a cloud. It takes no region, refuses every cloud field, and a namespace holds one per provider.

New Edge declarations for hcs and wsl2 are refused. An hcs workstation is its own Edge. Declare its egress policy in the workstation’s spec.egress. No Edge is needed. An Edge for wsl2 serves nothing. It enforces, injects, inspects and records nothing for a wsl2 workstation.

A hcs, vz or qemu workstation needs no Edge to enforce a policy or inject a credential. With no Edge for its provider, the network process Ringleader runs for each hcs workstation on Windows, each vz workstation on your Mac, and each qemu workstation on Linux enforces its policy and adds its credentials. An Edge for vz or qemu is optional. It puts the namespace’s workstations on that provider that have a policy or an injected credential behind an edge VM instead. A workstation created before its provider gave each workstation its own address cannot be served either way, and neither can a qemu workstation created before Ringleader could set up the network cards its own Edge needs. Delete such a workstation and create it again.

  • For vz on a Mac and qemu on Linux, with an Edge declared, the Ringleader daemon on each machine runs a small edge VM for the workstations there that need it. The VM has 2 vCPUs and 1 GiB of memory. It enforces egress policies by name and by address, and injects credentials for rules that allow it. rl status lists the edge VMs on your machine and what each is doing.
  • The daemon restarts an edge VM that stops working, rebuilds it if a restart does not help, and deletes it once its last workstation is gone. Moving a vz or qemu workstation between its own network process and an edge VM does not restart it.
  • For lima, the Edge builds nothing and does not make an egress policy enforceable.

A local Edge reports Ready False: LocalVMPlacement for vz and qemu, and DaemonPlacement for lima. Whether a workstation is served shows on the workstation itself, on EgressEnforced for vz and qemu and on EgressCredential for lima. Credential injection says what each local provider can inject.

Subnets have one owner

On AWS and Azure, a route applies to every machine in a subnet. So an edge instance routes a whole subnet, and only one edge instance can route a given subnet.

  • A namespace administrator can claim a subnet for their namespace, on any of the three clouds, by naming it in a CloudIdentity’s spec.subnet. The namespace must belong to an organization. A claim on a subnet another namespace already claimed is refused, and the refusal names that namespace. A workstation in another namespace that would be placed in a claimed subnet is refused when it is applied. A subnet nobody claimed is open to every namespace.
  • Before an edge instance first routes a subnet, it lists every machine in it. A machine it does not serve, such as a VM Ringleader did not create, holds that routing back. The workstation’s entry in the Edge’s status.boxes names the machine, and the workstations in the subnet report SteeringHeld. Move the machine out of the subnet, or, if it is a workstation in this namespace, give it an egress policy.
  • When another edge instance already routes the subnet, the workstation reports SubnetSteeredElsewhere. Its entry in status.boxes names the other Edge when that Edge is in your organization.
  • A machine created later in a subnet that is already routed is routed too, because a route applies to the whole subnet. The Edge lists it in status.neighbours, and a workstation in that position reports the SteeredWithoutServing condition.
  • The Edge keeps routing the subnet after its last workstation there is gone. A workstation whose policy you remove, or any other machine left in the subnet, reaches nothing and reports SteeredWithoutServing until you delete the Edge.

Status

You never write status. These are the fields you are most likely to read:

FieldMeaning
messageWhat is stopping the Edge from converging, or what it is doing.
machine, zone, addressThe VM’s name, zone and address inside your network.
externalAddressThe VM’s public address, when it has one.
builtArchThe architecture the VM was built for.
boxes[]The workstations it serves, each with its management port and anything holding it back.
steeredSubnetsThe subnets it routes, on AWS and Azure.
neighbours[]Machines in those subnets that it does not serve.
health, proxyWhether the VM is filtering traffic, and which edge build it runs.
recreateThe times Ringleader replaced the VM. See below.
maintenanceAn update waiting for a window.
cloudCallsRequests to your cloud and errors, hour by hour over the last day.

proxy.desired is the build this deployment ships, and proxy.version is what the running VM reports. An empty version means unknown, not broken: an edge instance whose agent has not reported yet shows it.

If the VM stops working in a way that a new VM can fix, such as when it stops forwarding traffic or its agent goes silent, Ringleader replaces it. It does this up to three times in 24 hours, then stops and says so in the Edge’s message.

rl edge describe <name> prints the message and the single-value fields. It then prints a Cloud calls section, and a Credential injection section listing what the edge instance is injecting for each Integration. rl edge get <name> -o yaml shows every field, including boxes, health and neighbours.

The Ready condition

Ready says whether the edge instance is working, and its reason names which part is missing. Two reasons are True:

ReasonMeaning
ServingThe VM is up, has confirmed the policy it was sent, and nothing wrong has been observed.
ServingNoBoxesThe same, and it serves no workstation at the moment. The VM keeps running, and billing, at the same address for the next workstation that needs it. Delete the Edge to stop paying for it.

The rest are False:

ReasonMeaning
LocalVMPlacementA vz or qemu Edge. Read each workstation’s EgressEnforced condition instead. hcs needs no Edge VM because its workstation’s network process is its Edge.
DaemonPlacementA lima Edge, or a wsl2 Edge stored before new wsl2 declarations were refused. Neither builds a VM.
NoMachineNo VM has been built. The message says why: waiting for a workstation, a missing subnet, or a build refused by the cloud.
NoAddressA VM exists and has no address in the network yet, so no traffic can be routed to it.
AgentSilentThe VM’s agent has stopped reporting while other edge instances go on reporting. Until it reports again, the rest of the Edge’s status shows what the VM said before it went silent.
NotForwardingThe VM reports it is not forwarding traffic, so every connection behind it is dropped.
NotConfirmedThe VM has confirmed no policy at all: its filter never loaded one, or refused the one it was given.
ServingStaleThe VM has confirmed a different policy from the one it was sent, so it is enforcing an older one.

The CloudCalls condition

CloudCalls reports whether your cloud is answering the edge instance’s requests. It is True with CallsAnswered, or with NoCloudCalls for an Edge that has made none. It is False with CallsFailing, and the message names the failing request. A failing request does not by itself make Ready False.

When the control plane is missing something

A cloud edge instance also needs two things from whoever runs your control plane: an edge build for the VM’s architecture, and the settings the VM connects back with. Where one is missing, the Edge is admitted and then waits with a message naming what is missing, and no VM is created. When the missing piece is a build for the VM’s architecture, a machineType of the other architecture fixes it. Anything else is for whoever runs your control plane.

Which cloud identity builds the machine

An edge instance is built with one of the CloudIdentity objects in its own namespace, and the Edge has no field naming one. Ringleader picks it this way:

  1. Among the identities for the Edge’s provider that carry no selector, it picks the one with the highest priority. Equal priorities are broken by name, in ascending order.
  2. If the namespace has no identity without a selector for that provider, it applies the same ordering to every identity the namespace holds for the provider, whatever their selectors.

A workstation resolves its identity differently: its own labels are matched against each identity’s selector. So an Edge and a workstation in one namespace can use different identities.

Once an Edge has recorded an identity, it keeps it. Declaring another identity later does not move a running edge instance onto it, because moving it would rebuild the VM and change the address every workstation it serves points at. To move one, delete the identity it recorded.

status.cloudIdentity names the identity in use. status.cloudIdentityReason says which rule chose it: LabelFree for the first rule, SelectorFallback for the second, and Kept for an Edge holding the identity it started with.

Deleting an Edge

Deleting an Edge changes what the workstations it serves can reach, so the delete is refused while a workstation in the namespace depends on it. Each of these workstations depends on it:

  • One the Edge serves whose policy names a host.
  • One the Edge serves only to add a credential.
  • One with a strict policy naming a host, when no other Edge for its provider would serve it.

A vz or qemu workstation depends on the Edge only when its own network process cannot serve it. Otherwise it moves to that process once the Edge is gone. A workstation cannot be served that way when it was created before its provider gave each workstation its own address, or, on qemu, before Ringleader could set up the network cards its own Edge needs. One on a machine whose rl is too old to say is counted too. The refusal names these causes. Recreate a workstation of the first kind, or update rl on the machine of the second, and the delete goes through without rl edge force-delete.

The refusal lists those workstations, grouped by what each would lose:

  • A cloud workstation with a bestEffort policy keeps the rule set it was given while it was routed, which permits the whole public internet. Its policy still reads the same.
  • A bestEffort workstation on vz or qemu whose own network process cannot serve it runs on the open network.
  • A workstation with a strict policy is held closed until another edge instance serves it.
  • A workstation that only had an injected credential stops getting the credential. It was never restricted.

To let the delete through, change those workstations first so they no longer need the Edge. On a cloud, replace their host destinations with cidr ones, which your cloud’s firewall holds without an Edge, or remove their policies. Detach the Integrations that inject credentials into them. Adding a cidr beside a host does not help, because the host still needs an edge instance.

To delete it anyway, an administrator runs rl edge force-delete, typing the Edge’s name again after --acknowledge-unrestricted-workstations. This overrides the refusal and nothing else: the VM is deleted the same way a plain delete deletes it, and the command prints what each group of workstations lost.

rl edge force-delete <name> --acknowledge-unrestricted-workstations <name>

An Edge that stays in Terminating

Deleting an Edge deletes its VM first, and the Edge is not removed until the VM is confirmed gone. While it waits, its provider and region stay taken, so you cannot declare a replacement.

If the VM cannot be deleted, and you will delete it yourself in your cloud console, an administrator can remove the Edge without it:

rl edge release-finalizer <name> --acknowledge-orphaned-machine <machine-name>

<machine-name> is the VM’s name, from status.machine. The VM is left running and billing, and Ringleader no longer tracks it.

Existing resources

Manifests of kind EgressGateway are still accepted. They address the same Edge resource, so changing them to kind: Edge does not recreate the VM or discard its status. rl edge get lists them.

See also

  • One Edge, two teams: a worked example in which one Edge adds a token for one team and gives each team its own allowlist.
  • WorkstationConfig: the egress policy an Edge enforces the hostname half of.
  • Credential injection: adding a key to a workstation’s requests at the Edge.
  • Workstation: the EgressEnforced condition and every reason it reports.
  • CloudAccount: sshSourceRanges, which limits who can reach the management ports.
  • Cloud onboarding: the subnets each onboarding reserves for an Edge and the workstations it serves.
  • Access control: the namespace administrator role.