diff --git a/docs/manuals/spaces/howtos/cloud-spaces/private-network-agent.md b/docs/manuals/spaces/howtos/cloud-spaces/private-network-agent.md new file mode 100644 index 000000000..db12bd842 --- /dev/null +++ b/docs/manuals/spaces/howtos/cloud-spaces/private-network-agent.md @@ -0,0 +1,375 @@ +--- +title: Private Network Agent +sidebar_position: 5 +description: Connect an Upbound Cloud control plane to resources in your private network without exposing public endpoints +--- + + +:::important +Private Network Agent is in private preview. For more information, [contact us][contact-us]. +::: + + +The Private Network Agent lets an Upbound Cloud control plane manage Kubernetes +and Helm resources inside private networks. No public endpoints, inbound +firewall rules, or VPC peering required. + +## Architecture overview + +The Private Network Agent reverses the traditional connection model. Instead of +the control plane reaching into your private network, a lightweight agent inside +your network connects outbound to the control plane. The control plane sends +requests through this connection and receives responses through the same +channel. + +This approach: + +- Requires only outbound connectivity from your private network. +- Eliminates inbound firewall exceptions, VPC peering, and publicly exposed Kubernetes API servers. +- Keeps a minimal footprint in your environment. + +The system has two components: + +- **Proxy**: Runs inside your Upbound managed control plane. Upbound manages this automatically. +- **Private Network Agent**: Runs inside your private network. You deploy and manage this via a Helm chart. + +## Prerequisites + +Before you begin, make sure you have: + +- An Upbound organization and a managed control plane. +- `Admin` role on the control plane (required to create a `Proxy` resource). +- A destination Kubernetes cluster in your private network. +- Outbound connectivity from the private network to `connect.upbound.io:4222` (TCP/TLS). No inbound rules required. +- `kubectl` access to both the managed control plane and the destination cluster. +- The [Upbound CLI](/manuals/cli/overview/) (`up`) installed. +- Helm v3 installed. + +## Set up the destination cluster + +On your **destination cluster**, create a service account that +`provider-kubernetes` uses. + +Create the service account and bind it to a role, for example `cluster-admin`: + +```bash +kubectl create serviceaccount provider-kubernetes -n default + +kubectl create clusterrolebinding provider-kubernetes-binding \ + --clusterrole=cluster-admin \ + --serviceaccount=default:provider-kubernetes +``` + +Create a long-lived service account token: + +```bash +kubectl create -f - < +``` + +## Configure the control plane + +Switch your `kubectl` context to your **Upbound managed control plane**. + +### Install provider-kubernetes + +Install `provider-kubernetes` on the control plane: + +```yaml +kubectl apply -f - < + +up robot create proxyagent -a $UPBOUND_ORG +up robot token create proxyagent private-cluster -f ./token.json -a $UPBOUND_ORG + +ROBOT_TOKEN=$(cat token.json | jq -r .token) +``` + +Create a Kubernetes secret containing the token on the Upbound control plane: + +```bash +kubectl create -f - < +## Install the Private Network Agent + + + +Switch your `kubectl` context back to the **destination cluster**. + +Create the robot token secret in the destination cluster: + +```bash +kubectl create -f - < +## Proxies +### Proxies + + ## References ### Referenced Objects diff --git a/static/crds/space/v1.15/spaces.upbound.io_proxies.yaml b/static/crds/space/v1.15/spaces.upbound.io_proxies.yaml new file mode 100644 index 000000000..ee4939f9e --- /dev/null +++ b/static/crds/space/v1.15/spaces.upbound.io_proxies.yaml @@ -0,0 +1,266 @@ +# Copyright 2026 Upbound Inc. +# All rights reserved + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + name: proxies.spaces.upbound.io +spec: + group: spaces.upbound.io + names: + categories: + - spaces + kind: Proxy + listKind: ProxyList + plural: proxies + shortNames: + - px + singular: proxy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.phase + name: Phase + type: string + - jsonPath: .status.agentID + name: Agent ID + priority: 1 + type: string + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: Ready + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + Proxy represents a private-network agent connection point that enables + secure communication between Upbound and resources in private networks. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ProxySpec defines the desired state of a Proxy. + properties: + agentID: + description: |- + AgentID is an optional identifier that allows sharing a single + private-network agent across multiple proxies. If omitted, + a UUID is auto-generated and set in status.agentID. + type: string + description: + description: Description is an optional human-readable description + of this Proxy + type: string + podSelector: + description: |- + PodSelector specifies which pods should have HTTP_PROXY environment + variables injected. Uses standard Kubernetes label selector semantics. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + secretRef: + description: |- + SecretRef is a reference to a secret containing the robot token used + to authenticate the proxy. + properties: + key: + description: Key is the key within the secret that contains the + token. + type: string + name: + description: Name of the secret. + type: string + required: + - key + - name + type: object + required: + - podSelector + - secretRef + type: object + x-kubernetes-validations: + - message: podSelector must specify at least one of matchLabels or matchExpressions; + an empty selector would match all pods unintentionally. + rule: (has(self.podSelector.matchLabels) && size(self.podSelector.matchLabels) + > 0) || (has(self.podSelector.matchExpressions) && size(self.podSelector.matchExpressions) + > 0) + status: + description: ProxyStatus represents the observed state of a Proxy. + properties: + agentID: + description: |- + AgentID is the ID for this Proxy. If spec.agentID was + provided, this will match that value. Otherwise, it contains the + auto-generated UUID. + type: string + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time this condition transitioned from one + status to another. + format: date-time + type: string + message: + description: |- + A Message containing details about this condition's last transition from + one status to another, if any. + type: string + observedGeneration: + description: |- + ObservedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + type: integer + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: |- + Type of this condition. At most one of each condition type may apply to + a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lastAppliedPodSelector: + description: |- + LastAppliedPodSelector is the podSelector that was last applied. + Used to detect selector changes and evict only affected pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + phase: + description: Phase represents the current state of the Proxy. + enum: + - WaitingForProxy + - Connected + - Disconnected + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {}