Vagrant Port Forwarding On MacHow to forward port 80 and 443 to your Vagrant box on your Mac.

If you’re using Vagrant you’ve probably noticed that you can’t bind ports 80/443 on your Mac due to security limitations.

Getting Started

Forwarding host ports < 1024 impossible:
On Unix-based hosts (e.g. Linux, Solaris, Mac OS X) it is not possible to bind to ports below 1024 from applications that are not run by root. As a result, if you try to configure such a port forwarding, the VM will refuse to start. ipfw deprecated as of mavericks

So what’s the solution?

Well, there’s a few.

The VirtualBox manual is correct that ipfw has been deprecated as of Mac OS X Mavericks, however, the method using ipfw still happens to work.

Another alternative is to use the preferred solution which happens to be using Apple’s pfctl software, and that’s what we’ll be covering in this article.

Why You Shouldn’t Automatically Bind The Port Forwarding Rules On Startup

I don’t cover it in this article, but if you want to automatically bind your ports to forward when your system boots, you can. If you modify /etc/pf.conf with your port forwarding rules (samples are listed below in this article), they ‘ll automatically bind when your system boots. 
Before you go this route, I ask that you at least hear me out.

If you’re anything like me, you have more than a single virtual box sitting on your machine (I happen to have over a dozen). Each of those boxes has its own public HTTP port which is unique to the box in question. For the sake of understanding let’s say I have 3 vagrant boxes, the HTTP ports for them would be 8080, 8081, and 8082. All of those boxes want to forward port 80 on my host machine to their respective HTTP ports, as each box is replicating its own unique environment which happens to use port 80.

At any given time in a day, I’m spinning boxes up and down for the environments that I need to gain access to. Manually typing and/or copy-pasting the port forwarding commands is cumbersome and far from convenient. I’m assuming if you’re reading this article that you’re somewhat of an engineer and it makes me want to scream “WE’RE ENGINEERS!” We build, we conquer, we automate things. I swear there’s an engineers bible somewhere that says something along the lines of:

Thou shall not manually type and/or copy-paste something over and over again to accomplish one’s goal.

I can’t cite where it’s written; but it has to be 1 of the 10 commandments of engineering (if you’re reading this, you’re probably laughing and nodding your head in agreement because you know it’s the truth).

Along with the point I just mentioned, I happen to be on Cisco’s VPN around the clock. Whenever I hop on and off the VPN, it apparently wipes all of my port forwarding rules on Mavericks every time without fail. Thus solidifying my stance that the ability to simply bind the port forwarding rules on boot isn’t an ideal solution.

Installing Vagrant Triggers

Before we update our Vagrantfile we’re going to install the vagrant-triggers plugin. This will allow us to execute commands on the host dependent upon the command that’s being directed to Vagrant. For instance, we may want to run a certain command on our host for vagrant up and a different command on our host for vagrant halt. This plugin provides us with this type of functionality.

To install vagrant-triggers, navigate from your terminal to the folder containing your Vagrantfile and enter the following command:

vagrant plugin install vagrant-triggers

Configuring Your Vagrantfile

When the vagrant-triggers plugin has finished installing, open up your Vagrantfile (in your editor of choice) and add the following lines within your configuration block:

config.trigger.after [:provision, :up, :reload] do
  system('echo "
rdr pass on lo0 inet proto tcp from any to port 80 -> port 8080  
rdr pass on lo0 inet proto tcp from any to port 443 -> port 8443  
" | sudo pfctl -f - > /dev/null 2>&1; echo "==> Fowarding Ports: 80 -> 8080, 443 -> 8443"')  

Now whenever we run vagrant upvagrant provision, or vagrant reload on our box, ports 80/443 will automatically forward to 8080/8443 after the box has finished booting/provisioning/reloading.

What about when we halt/destroy the box? How can we remove/reset our session based port forwarding rules?

To reset all of our session based rules when we halt/destroy a box add the following to your Vagrantfile configuration block:

config.trigger.after [:halt, :destroy] do
  system("sudo pfctl -f /etc/pf.conf > /dev/null 2>&1; echo '==> Removing Port Forwarding'")

The block above resets all of your session based pfctl rules and reloads your /etc/pf.conf file which is what your system loads when it starts up. Your systems rules will basically be reset to the state that it’s in when you first boot your machine.

What Did We Accomplish?

We’ve installed and applied the vagrant-triggers plugin to execute commands on our host machine specific to the commands being directed at Vagrant.

We’ve updated our Vagrantfile to automatically forward ports 80/443 on our host machine to ports 8080/8443 on our virtual box whenever we run vagrant upvagrant provision or vagrant reload. We’ve also updated our Vagrantfile to reset all of our session based port forwarding rules whenever we run vagrant halt or vagrant destroy.

If you interested in learning more about the pfctl program and how it works you can read more about it here.


About Salvatore Garbesi