<p>I have three nodes in my cluster who are behind a firewall I do not control. This firewall has a public IP connected to it and can forward traffic to my kubernetes node. It has port 80 and 443 opened to my node.</p>
<p>Initially, I used the public IP in the MetalLB config like this:</p>
<pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 186.xx.xx.xx-186.xx.xx.xx

</code></pre>
<p>But after reading <a href="https://stackoverflow.com/questions/63935449/make-nginx-ingress-controller-publicly-available-using-metallb">this answer of another question</a> I'm guessing it is invalid since the IP used by MetalLB needs to be on the same subnet as the nodes? And they are all using private IPs.</p>
<p>When I tested locally a HTTP server listening on port 80 and ran it on the actual node (not in the cluster) then I was able get a response on the public IP from outside the network.</p>
<p>So my question is:
<strong>How do I make MetalLB or Nginx ingress controller listen on port 80 and 443 for incoming request?</strong></p>
<p>When using <code>curl 186.xx.xx.xx:80</code> on of the nodes in the cluster then I'm receiving a response from the nginx ingress controller. But not when doing it outside of the node.</p>

<p>Answering the question:</p>
<blockquote>
<p>How can I create a setup with Kubernetes cluster and separate firewall to allow users to connect to my <code>Nginx Ingress</code> controller which is exposing my application.</p>
</blockquote>
<p>Assuming the setup is basing on Kubernetes cluster provisioned in the internal network and there is a firewall between the cluster and the &quot;Internet&quot;, following points should be addressed (there could be some derivatives which I will address):</p>
<ul>
<li><code>Metallb</code> provisioned on Kubernetes cluster (assuming it's a bare metal self-managed solution)</li>
<li><code>Nginx Ingress</code> controller with modified <code>Service</code></li>
<li><code>Port-forwarding</code> set on the firewall</li>
</ul>
<hr />
<p><code>Service</code> of type <code>Loadbalancer</code> in the most part (there are some exclusions) is a resource that requires a cloud provider to assign an <strong><code>External IP</code></strong> address for your <code>Service</code>.</p>
<blockquote>
<p>A side note!</p>
<p>More reference can be found here:</p>
<ul>
<li><em><a href="https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/" rel="noreferrer">Kubernetes.io: Docs: Tasks: Access application cluster: Create external load balancer</a></em></li>
</ul>
</blockquote>
<p>For solutions that are on premise based, there is a tool called <code>metallb</code>:</p>
<blockquote>
<p>Kubernetes does not offer an implementation of network load-balancers (Services of type LoadBalancer) for bare metal clusters. The implementations of Network LB that Kubernetes does ship with are all glue code that calls out to various IaaS platforms (GCP, AWS, Azure…). If you’re not running on a supported IaaS platform (GCP, AWS, Azure…), LoadBalancers will remain in the “pending” state indefinitely when created.</p>
<p>Bare metal cluster operators are left with two lesser tools to bring user traffic into their clusters, “NodePort” and “externalIPs” services. Both of these options have significant downsides for production use, which makes bare metal clusters second class citizens in the Kubernetes ecosystem.</p>
<p>MetalLB aims to redress this imbalance by offering a Network LB implementation that integrates with standard network equipment, so that external services on bare metal clusters also “just work” as much as possible.</p>
<p><em><a href="https://metallb.universe.tf/" rel="noreferrer">Metallb.universe.tf</a></em></p>
</blockquote>
<p>Following the guide on the <a href="https://metallb.universe.tf/installation/" rel="noreferrer">installation/configuration of metallb</a>, there will be a configuration for a <strong>single internal IP address</strong> that the firewall will send the traffic to:</p>
<pre class="lang-yaml prettyprint-override"><code>apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: single-ip # &lt;-- HERE
      protocol: layer2
      addresses:
      - 10.0.0.100/32 # &lt;-- HERE
</code></pre>
<p>This IP address will be associated with the <code>Service</code> of type <code>LoadBalancer</code> of <code>Nginx Ingress</code> controller.</p>
<hr />
<p>The changes required with the <code>Nginx Ingress</code> manifest (<code>Service</code> part):</p>
<ul>
<li><em><a href="https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.44.0/deploy/static/provider/cloud/deploy.yaml" rel="noreferrer">Raw.githubusercontent.com: Kubernetes: Ingress nginx: Controller: ... : deploy.yaml</a></em></li>
</ul>
<pre><code># Source: ingress-nginx/templates/controller-service.yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    metallb.universe.tf/address-pool: single-ip # &lt;-- IMPORTANT
  labels:
    # &lt;-- OMMITED --&gt; 
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
    - name: https
      port: 443
      protocol: TCP
      targetPort: https
  selector:
    # &lt;-- OMMITED --&gt; 
</code></pre>
<p>Above changes in the <code>YAML</code> manifest will ensure that the address that was configured in a <code>metallb</code> <code>ConfigMap</code> will be used with the <code>Service</code>.</p>
<blockquote>
<p>A side note!</p>
<p>You can omit the <code>metallb</code> and use the <code>Service</code> of type <code>Nodeport</code> but this carries some disadvantages.</p>
</blockquote>
<hr />
<p>The last part is to set the port-forwarding on the firewall. The rule should be following:</p>
<ul>
<li><code>FIREWALL_IP:80</code> -&gt; <code>SINGLE_IP:80</code></li>
<li><code>FIREWALL_IP:443</code> -&gt; <code>SINGLE_IP:443</code></li>
</ul>
<p>After that you should be able to communicate with your <code>Nginx Ingress</code> controller by:</p>
<ul>
<li><code>$ curl FIREWALL_IP:80</code></li>
</ul>
<hr />
<p>Additional resources:</p>
<ul>
<li><em><a href="https://kubernetes.io/docs/concepts/services-networking/service/" rel="noreferrer">Kubernetes.io: Docs: Concepts: Services networking: Service</a></em></li>
</ul>
