> ## Documentation Index
> Fetch the complete documentation index at: https://docs.emergence.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Networking

> Configure Gateway API for traffic routing, cert-manager for TLS certificates, and external-dns for automated DNS record management.

# Networking

CRAFT uses **Gateway API** for traffic routing, **cert-manager** for TLS certificate lifecycle, and **external-dns** for automatic DNS record management. This guide covers the networking stack across cloud and on-premises environments.

## Gateway API

EM-Runtime uses the [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io/) (v1) for all external traffic routing. Gateway API replaces traditional Ingress with a more expressive, role-oriented model.

### Ingress Flow

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {'lineColor': '#555555', 'fontFamily': 'sans-serif', 'edgeLabelBackground': '#ffffff'}}}%%
flowchart LR
    Internet["Internet"] --> LB["Load Balancer (HTTPS)"]
    LB --> GW["Gateway (TLS terminates)"]
    GW --> Route["HTTPRoute"]
    Route --> Svc["Service"]
    Svc --> Pod["Pod"]

    classDef entry fill:#56B4E9,stroke:#555555,color:#000
    classDef infra fill:#0072B2,stroke:#555555,color:#fff
    classDef app fill:#E69F00,stroke:#555555,color:#000
    class Internet entry
    class LB,GW,Route infra
    class Svc,Pod app
```

### Gateway Resource

The Gateway resource defines the load balancer and listeners. It must exist before the em-runtime chart deploys HTTPRoutes.

```yaml theme={null}
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: em-runtime-gateway
  namespace: gateway
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  gatewayClassName: nginx    # or gke-l7-global-external-managed
  listeners:
  - name: https
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      certificateRefs:
      - name: platform-tls
    allowedRoutes:
      namespaces:
        from: All
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: All
```

### HTTPRoute Rules

The em-runtime Helm chart creates an HTTPRoute with these default rules:

| Path Prefix       | Backend Service       | Port | URL Rewrite         |
| ----------------- | --------------------- | ---- | ------------------- |
| `/keycloak`       | keycloak              | 8080 | None (pass-through) |
| `/api/governance` | em-runtime-governance | 8000 | `/governance`       |
| `/api/assets`     | em-runtime-assets     | 8000 | `/assets`           |
| `/api/utils`      | em-runtime-utils      | 8000 | `/utils`            |

### Gateway Controllers by Platform

| Platform    | Controller                        | GatewayClassName                 |
| ----------- | --------------------------------- | -------------------------------- |
| **GKE**     | GKE Gateway Controller (built-in) | `gke-l7-global-external-managed` |
| **EKS**     | AWS Load Balancer Controller      | `aws-application-load-balancer`  |
| **AKS**     | Azure Application Gateway         | `azure-application-gateway`      |
| **On-Prem** | NGINX Gateway Fabric              | `nginx`                          |
| **On-Prem** | Envoy Gateway                     | `eg`                             |

### Configuration in Helm

```yaml theme={null}
# Chart values for Gateway reference
gatewayName: "em-runtime-gateway"
gatewayNamespace: "gateway"
hostname: "api.example.com"

httpRoute:
  enabled: true
  rules:
    - pathPrefix: "/keycloak"
      serviceName: keycloak
      servicePort: 8080
    - pathPrefix: "/api/governance"
      rewritePrefix: "/governance"
      serviceName: em-runtime-governance
      servicePort: 8000
```

## cert-manager

[cert-manager](https://cert-manager.io/) automates TLS certificate provisioning and renewal. It supports ACME (Let's Encrypt) and internal CA issuers.

### Certificate Flow

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {'lineColor': '#555555', 'fontFamily': 'sans-serif', 'edgeLabelBackground': '#ffffff'}}}%%
flowchart LR
    CM["cert-manager"] -->|DNS-01 challenge| DNS["DNS Provider"]
    CM -->|stores| Secret["K8s Secret"]
    GW["Gateway"] -->|reads| Secret
    LE["Let's Encrypt"] -->|verifies| DNS
    LE -->|issues cert| CM

    classDef process fill:#E69F00,stroke:#555555,color:#000
    classDef ext fill:#CC79A7,stroke:#555555,color:#000
    classDef data fill:#009E73,stroke:#555555,color:#000
    class CM,GW process
    class DNS,LE ext
    class Secret data
```

### Installation

Install cert-manager with Gateway API support:

```bash theme={null}
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set config.enableGatewayAPI=true \
  --set crds.enabled=true
```

### ClusterIssuer Configuration

<Tabs>
  <Tab title="Let's Encrypt (Production)">
    ```yaml theme={null}
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: letsencrypt-prod
    spec:
      acme:
        server: https://acme-v2.api.letsencrypt.org/directory
        email: admin@example.com
        privateKeySecretRef:
          name: letsencrypt-prod-key
        solvers:
        - dns01:
            cloudDNS:                    # GCP
              project: your-project-id
            # route53:                   # AWS
            #   region: us-east-1
            # azureDNS:                  # Azure
            #   subscriptionID: ...
    ```
  </Tab>

  <Tab title="Internal CA (On-Prem)">
    ```yaml theme={null}
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: internal-ca
    spec:
      ca:
        secretName: internal-ca-key-pair
    ---
    # Create the CA key pair first
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: internal-ca
      namespace: cert-manager
    spec:
      isCA: true
      commonName: "CRAFT Internal CA"
      secretName: internal-ca-key-pair
      issuerRef:
        name: selfsigned
        kind: ClusterIssuer
    ```
  </Tab>
</Tabs>

### DNS-01 Solver by Provider

| Provider           | Solver           | IAM Requirement                     |
| ------------------ | ---------------- | ----------------------------------- |
| **GCP Cloud DNS**  | `dns01.cloudDNS` | Workload Identity service account   |
| **AWS Route 53**   | `dns01.route53`  | IRSA role with Route 53 permissions |
| **Azure DNS**      | `dns01.azureDNS` | Workload Identity managed identity  |
| **On-Prem (BIND)** | `dns01.rfc2136`  | TSIG key for dynamic DNS updates    |

### Gateway Annotation

Annotate the Gateway to trigger automatic certificate provisioning:

```yaml theme={null}
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
```

cert-manager watches for Gateway resources with this annotation and provisions TLS certificates for each HTTPS listener.

## external-dns

[external-dns](https://github.com/kubernetes-sigs/external-dns) synchronizes Kubernetes resources (Gateway, Service, Ingress) with DNS providers, automatically creating A/CNAME records.

### How It Works

1. external-dns watches Gateway and Service resources for hostname annotations
2. When a Gateway gets an external IP, external-dns creates a DNS A record
3. Records are updated or removed as resources change

### Configuration by Provider

<Tabs>
  <Tab title="GCP Cloud DNS">
    ```yaml theme={null}
    provider: google
    google:
      project: your-project-id
    domainFilters:
      - runtime.dev.emergence.ai
    policy: sync
    sources:
      - gateway-httproute
      - service
    ```

    Requires a Workload Identity service account with `dns.admin` role.
  </Tab>

  <Tab title="AWS Route 53">
    ```yaml theme={null}
    provider: aws
    aws:
      region: us-east-1
    domainFilters:
      - runtime.dev.emergence.ai
    policy: sync
    sources:
      - gateway-httproute
      - service
    ```

    Requires an IRSA role with Route 53 `ChangeResourceRecordSets` permission.
  </Tab>

  <Tab title="On-Prem (RFC2136)">
    ```yaml theme={null}
    provider: rfc2136
    rfc2136:
      host: dns.example.com
      port: 53
      zone: example.com
      tsigKeyname: external-dns
      tsigSecretAlg: hmac-sha256
    policy: sync
    sources:
      - gateway-httproute
      - service
    ```

    Requires a TSIG key configured on the BIND DNS server.
  </Tab>
</Tabs>

## TLS Requirements

| Boundary                | Requirement                        |
| ----------------------- | ---------------------------------- |
| Client to load balancer | TLS 1.2+ (certificate required)    |
| Load balancer to pods   | HTTP (in-cluster; TLS optional)    |
| Services to PostgreSQL  | SSL recommended (private network)  |
| Services to Redis       | TLS supported (`rediss://` scheme) |

## Network Policies

For on-premises deployments, configure Kubernetes NetworkPolicies for defense in depth:

```yaml theme={null}
# Restrict data-insights egress to LLM API endpoints
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: talk2data-egress
  namespace: em-runtime
spec:
  podSelector:
    matchLabels:
      app: data-insights
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - port: 443
      protocol: TCP
```

<Note>
  Restrict data-insights egress to allowlisted LLM API endpoints (OpenAI, Anthropic, Vertex AI) for SSRF mitigation.
</Note>

## Firewall Rules

Ensure the following traffic is permitted:

| Direction            | Port | Protocol | Purpose                           |
| -------------------- | ---- | -------- | --------------------------------- |
| **Inbound**          | 443  | TCP      | HTTPS from clients                |
| **Inbound**          | 80   | TCP      | HTTP redirect to HTTPS            |
| **Cluster to DB**    | 5432 | TCP      | PostgreSQL                        |
| **Cluster to Cache** | 6379 | TCP      | Redis                             |
| **Cluster to S3**    | 443  | TCP      | Object storage                    |
| **Cluster to LLM**   | 443  | TCP      | LLM API endpoints (data-insights) |

## DNS Architecture

One DNS A record pointing the platform hostname to the load balancer IP/CNAME:

```text theme={null}
api.example.com  →  A  →  <load-balancer-ip>
```

For GKE deployments, external-dns creates this automatically when the Gateway receives an IP address. For on-prem, configure the DNS record manually or use external-dns with RFC2136.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Certificate not provisioning">
    ```bash theme={null}
    # Check cert-manager logs
    kubectl logs -n cert-manager -l app=cert-manager

    # Check certificate and challenge status
    kubectl describe certificate -n gateway-system
    kubectl get challenges -n gateway-system
    ```
  </Accordion>

  <Accordion title="Gateway not getting external IP">
    ```bash theme={null}
    # Check Gateway status
    kubectl describe gateway em-runtime-gateway -n gateway

    # Verify the gateway controller is running
    kubectl get pods -n gateway-system
    ```
  </Accordion>

  <Accordion title="DNS records not created">
    ```bash theme={null}
    # Check external-dns logs
    kubectl logs -l app.kubernetes.io/name=external-dns

    # Verify IAM / Workload Identity
    kubectl describe sa external-dns -n external-dns
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Prerequisites" icon="list-check" href="/deployment/prerequisites">
    Complete networking prerequisites and firewall rules.
  </Card>
</CardGroup>
