The term "ingress" in the context of infrastructure as code, specifically Terraform, carries a dual semantic weight that often confounds practitioners transitioning from general networking to cloud-native architectures. On one hand, it refers to the fundamental network traffic policy regarding inbound connections into a compute environment, a concept critical to securing enterprise-grade software such as Terraform Enterprise. On the other hand, in the Kubernetes ecosystem, an Ingress object represents a higher-level abstraction that manages external access to HTTP and HTTPS services within a cluster. This article dissects both dimensions, exploring the precise network port requirements for self-managed Terraform instances, the deployment mechanics of Kubernetes Ingress Controllers via Terraform, and the specialized approach of using Terraform itself to manage external Load Balancer As A Service (LBaaS) resources. Understanding the distinction between configuring internal network flows and deploying the gateways that process external traffic is essential for building resilient, secure, and scalable infrastructure stacks.
Network Security Perimeters: Terraform Enterprise Ingress and Egress
When organizations deploy Terraform Enterprise (TFE) on their own Linux instances, the security posture of the host is dictated by strict inbound and outbound network configurations. The Linux instance running Terraform Enterprise requires specific network configurations to allow incoming access for users and administrators, while simultaneously requiring outbound access to several external services for software updates and resource downloads. Misconfiguration of these ports can lead to complete service outages or security vulnerabilities, making precise firewall rules a mandatory step in the provisioning process.
The ingress requirements for a Terraform Enterprise instance are comprehensive, covering administrative, application, and internal communication layers. The following table details the mandatory inbound ports and their specific functions:
| Port | Protocol | Function | Accessibility |
|---|---|---|---|
| 22 | TCP | Allows SSH access to the instance for administration and debugging. | Restricted to administrators |
| 80 | TCP | Allows access to the Terraform Enterprise application via HTTP. Redirects to port 443. | Public or Internal |
| 443 | TCP | Allows access to the Terraform Enterprise application via HTTPS. | Public or Internal |
| 8800 | TCP | Allows access to the installer dashboard. | Restricted to administrators |
| 9870-9880 | TCP | Used for internal communication on the host and its subnet. | Not publicly accessible |
| 23000-23100 | TCP | Used for internal communication on the host and its subnet. | Not publicly accessible |
It is critical to note that while ports 80 and 443 are standard for web access, the ranges 9870-9880 and 23000-23100 are reserved for internal host and subnet communication. These ranges must never be exposed to the public internet. Exposing these ports creates a vector for lateral movement within the network stack, as they handle critical internal processes. Therefore, security groups or firewalls must explicitly deny inbound traffic to these ranges from any source outside the designated subnet.
In parallel, egress requirements depend on the deployment mode. If Terraform Enterprise is installed in "online mode," it requires outbound access to specific external hostnames to facilitate software updates. In offline mode, these restrictions can be relaxed, but the ingress ports remain unchanged. The distinction between these modes dictates the complexity of the network egress rules, requiring DevOps teams to map the specific DNS records and IP addresses for HashiCorp update services if operating in a restricted egress environment.
Kubernetes Ingress Controllers: The Gateway to Cluster Services
In the Kubernetes domain, an ingress controller is the gateway to the cluster. It receives external traffic and routes it to the right services based on hostnames, paths, and other rules. Without an ingress controller, Kubernetes services are only accessible from inside the cluster, rendering them useless for external clients or even for services that need to be exposed via a load balancer. Deploying an ingress controller through Terraform ensures that this critical component is consistently configured, versioned, and integrated with the cloud provider's load balancer infrastructure.
Terraform modules simplify the deployment of these controllers by abstracting the underlying Helm chart values and provider-specific configurations. Two of the most popular ingress controllers, NGINX and Traefik, offer distinct advantages. The NGINX ingress controller is the most widely used option, with the community-maintained chart from the kubernetes/ingress-nginx project serving as the standard choice. Its ubiquity stems from its mature feature set, extensive documentation, and broad compatibility with cloud provider load balancers.
When deploying the NGINX ingress controller via Terraform, the configuration often involves setting up a dedicated namespace and a Helm release with specific values for high availability and resource management. A production-ready configuration typically includes multiple replicas for fault tolerance, resource limits to prevent node starvation, and metrics enabled for monitoring.
The following code block demonstrates a robust Terraform configuration for deploying the NGINX ingress controller with high availability, autoscaling, and metrics support:
```hcl
Create the ingress namespace
resource "kubernetes_namespace" "ingress" {
metadata {
name = "ingress-nginx"
labels = {
"app.kubernetes.io/managed-by" = "terraform"
}
}
}
Deploy NGINX Ingress Controller
resource "helmrelease" "nginxingress" {
name = "ingress-nginx"
repository = "https://kubernetes.github.io/ingress-nginx"
chart = "ingress-nginx"
namespace = kubernetes_namespace.ingress.metadata[0].name
version = "4.9.0"
values = [
yamlencode({
controller = {
# Run multiple replicas for high availability
replicaCount = 2
# Resource limits
resources = {
requests = {
cpu = "100m"
memory = "128Mi"
}
limits = {
memory = "256Mi"
}
}
# Pod disruption budget
minAvailable = 1
# Metrics for monitoring
metrics = {
enabled = true
serviceMonitor = {
enabled = true
}
}
# Autoscaling based on load
autoscaling = {
enabled = true
minReplicas = 2
maxReplicas = 10
targetCPUUtilizationPercentage = 70
targetMemoryUtilizationPercentage = 80
}
}
})
]
wait = true
timeout = 300
}
```
This configuration highlights the power of Terraform in managing Kubernetes resources. By using yamlencode, complex nested configurations are rendered cleanly, ensuring that the Helm chart receives the exact parameters needed. The autoscaling block demonstrates how the ingress controller can scale dynamically based on CPU and memory utilization, ensuring that the gateway never becomes a bottleneck during traffic spikes.
Cloud-Specific Optimizations: AWS and GCP
Cloud providers offer distinct load balancer services, each with its own integration requirements for ingress controllers. On Amazon Web Services (AWS), a Network Load Balancer (NLB) is typically preferred over an Application Load Balancer (ALB) for ingress controllers due to better performance and support for static IPs. The Terraform configuration for AWS requires specific annotations to instruct the Kubernetes service to provision an NLB.
The terraform-iaac/nginx-controller/helm module provides a streamlined way to handle these provider-specific configurations. It allows users to set additional Helm values via the additional_set input. For example, on AWS, one can set annotations to enable the NLB type and cross-zone load balancing:
```hcl
module "nginx-controller" {
source = "terraform-iaac/nginx-controller/helm"
additional_set = [
{
name = "controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-type"
value = "nlb"
type = "string"
},
{
name = "controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-cross-zone-load-balancing-enabled"
value = "true"
type = "string"
}
]
}
```
On Google Cloud Platform (GCP), the focus shifts to managing static IP addresses. The module supports the ip_address input, allowing users to assign a pre-provisioned static IP to the ingress controller. This is crucial for whitelisting purposes or when migrating services that require a consistent public endpoint.
```hcl
Static IP
resource "googlecomputeaddress" "ingressipaddress" {
name = "nginx-controller"
}
module "nginx-controller" {
source = "terraform-iaac/nginx-controller/helm"
# Optional
ipaddress = googlecomputeaddress.ingressip_address.address
}
```
Similarly, on Azure, a static public IP can be provisioned and assigned to the ingress controller to ensure endpoint stability. The module supports this via the azurerm_public_ip resource, passing the resulting IP address to the ip_address input of the module.
| Cloud Provider | Load Balancer Type | Key Configuration Feature | Use Case |
|---|---|---|---|
| AWS | Network Load Balancer (NLB) | additional_set for NLB annotations |
High performance, static IP retention |
| GCP | External Load Balancer | ip_address for static IP assignment |
Consistent public endpoint |
| Azure | Standard Load Balancer | ip_address for static IP assignment |
Consistent public endpoint |
The Terraform Ingress Controller: Managing LBaaS Resources
A unique approach to ingress management is provided by the kayrus/ingress-terraform project. Unlike traditional ingress controllers that reside inside the Kubernetes cluster and receive traffic directly, the Terraform ingress controller manages Load Balancer As A Service (LBaaS) resources externally. It does not receive ingress traffic itself; rather, it uses Terraform to configure and update external load balancers in the cloud provider.
This architectural distinction has significant implications for reliability. Since the LBaaS resources are not part of the Kubernetes cluster, regular services cannot be accessed directly by the load balancer. Therefore, services must be exposed as NodePorts. However, if the Terraform ingress controller is down, the load balancer will still work, as it is an independent external resource. This decoupling provides a resilience benefit: the data path (load balancer to service) remains operational even if the control path (Terraform controller) fails.
The project, currently in early alpha, supports specific OpenStack environments, including Barbican for TLS certificate support. It allows for the creation of one load balancer for multiple services, offering flexibility that standard Kubernetes cloud provider integrations often lack. The following table lists the supported annotation configurations for the Terraform ingress controller:
| Annotation | Type | Default | Description |
|---|---|---|---|
terraform.ingress.kubernetes.io/internal |
boolean | false | Whether to assign a floating IP to the load balancer. |
terraform.ingress.kubernetes.io/tcp-configmap |
string | N/A | A config map name with a TCP service ports map. |
terraform.ingress.kubernetes.io/udp-configmap |
string | N/A | A config map name with a UDP service ports map (Octavia API only). |
terraform.ingress.kubernetes.io/template |
string | N/A | A config map name with a custom terraform script template. |
terraform.ingress.kubernetes.io/skip-http-listener |
boolean | false | Whether to skip the HTTP (80 TCP port) listener creation. |
terraform.ingress.kubernetes.io/use-octavia |
boolean | false | Whether Terraform provider should use Octavia API instead of Neutron LBaaS v2. |
terraform.ingress.kubernetes.io/lb-method |
string | ROUND_ROBIN | Load balancer method: ROUNDROBIN, LEASTCONNECTIONS, or SOURCE_IP. |
terraform.ingress.kubernetes.io/proxy-protocol |
boolean | false | Whether to use PROXY protocol for pool members (Octavia API only). |
terraform.ingress.kubernetes.io/lock-timeout |
string | 0s | Specifies the -lock-timeout Terraform CLI argument. |
kubernetes.io/ingress.class |
string | N/A | Must have the terraform value to be processed by the controller. |
The configuration for the Terraform ingress controller itself requires specific authentication parameters for the OpenStack environment. A ConfigMap is used to define the cluster name, OpenStack authentication details, and Terraform-specific network IDs. The following example illustrates the required configuration structure:
yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: terraform-ingress-controller-config
data:
config: |
cluster-name: terraform-ingress-cluster
openstack:
auth-url: %os_auth_url%
username: %os_username%
password: %os_password%
project-id: %os_project_id%
domain-id: %os_domain_id%
user-domain-id: %os_domain_id%
terraform:
subnet-id: 058d9dce-7a62-4d8c-ac82-6b217d697e81
floating-network-id: 8f408a7c-4d03-4355-81c1-07713fa0caec
floating-subnet-id: 9206c010-882f-4059-914e-f25b33139c40
manage-security-groups: true
create-monitor: true
monitor-delay: "5"
monitor-timeout: "3"
monitor-max-retries: 3
When specifying a config map name for annotations, it is mandatory that the ConfigMap exists within the same namespace as the ingress resource. This locality constraint ensures that the controller can retrieve the necessary configuration without cross-namespace permission complexities.
Operational Best Practices and Troubleshooting
Effective ingress management in Terraform extends beyond initial deployment to ongoing operational hygiene. For standard Kubernetes ingress controllers, monitoring is paramount. The NGINX configuration shown earlier enables metrics and service monitors, allowing integration with Prometheus and Grafana. This visibility into request rates, latency, and error codes is critical for identifying misconfigurations or traffic anomalies.
For the Terraform Enterprise instance, regular auditing of security groups is necessary to ensure that the internal port ranges (9870-9880 and 23000-23100) remain strictly internal. Any deviation should be treated as a security incident. Additionally, the egress rules for online mode must be validated to ensure that the instance can reach all necessary update endpoints. If the instance is in a hybrid cloud setup, DNS resolution for these external hostnames must be verified.
For those utilizing the kayrus/ingress-terraform module, troubleshooting often revolves around the Terraform CLI lock behavior. The lock-timeout annotation allows customization of this behavior, which is useful in multi-user environments where state locking might occur. Since the project is in alpha, users should expect potential code and behavior changes and should pin versions strictly in production environments.
The deployment of the Terraform ingress controller follows a standard Kubernetes pattern. First, the configuration and service account are applied:
bash
kubectl -n kube-system apply -f config.yaml
kubectl -n kube-system apply -f serviceaccount.yaml
kubectl -n kube-system apply -f deployment.yaml
Subsequently, services are exposed as NodePorts, and ingress resources are deployed to trigger the creation of the external load balancers. This workflow highlights the separation of concerns: Kubernetes manages the service exposure, while Terraform manages the external network infrastructure.
Conclusion
The management of ingress in Terraform is a multifaceted discipline that spans from low-level network port security for self-managed platforms to high-level Kubernetes abstraction for cloud-native services. For Terraform Enterprise, the precision of ingress port configuration is non-negotiable, with specific internal ranges requiring strict isolation to maintain security integrity. In the Kubernetes sphere, the choice of ingress controller—whether NGINX, Traefik, or a Terraform-managed LBaaS controller—dictates the architecture's resilience, scalability, and complexity.
Terraform serves as the unifying thread, providing a declarative method to enforce these configurations consistently across environments. By leveraging modules like terraform-iaac/nginx-controller/helm, organizations can standardize ingress deployments while accommodating provider-specific nuances such as AWS NLBs or GCP static IPs. The emerging capability to use Terraform as the controller for external load balancers offers a unique paradigm where the control plane is decoupled from the data plane, enhancing reliability. As infrastructure evolves, the ability to codify these network boundaries with the precision of Terraform remains a cornerstone of modern DevOps practices, ensuring that traffic flows securely and efficiently from the external world into the core of the application stack.