From b0e4d643e7df613c8af34216df1288991a3e14aa Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Sat, 30 Sep 2017 23:09:26 +0000 Subject: [PATCH 1/3] update gemfile --- Gemfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index d36128a..f3d8f05 100644 --- a/Gemfile +++ b/Gemfile @@ -3,11 +3,12 @@ source 'https://rubygems.org' gemspec group :development do - gem 'vagrant', :git => 'https://github.com/mitchellh/vagrant.git', tag: 'v1.8.4' + gem 'vagrant', :git => 'https://github.com/mitchellh/vagrant.git', tag: 'v2.0.0' end group :plugins do - gem 'vagrant-bhyve', path: '.' + gemspec + #gem 'vagrant-bhyve', path: '.' #gem 'vagrant-sshfs', '1.1.0' #gem 'vagrant-mutate', :git => 'https://github.com/swills/vagrant-mutate' end From d20451eae40b1ee7909ac04e2f6f9abd24dd75cf Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Sat, 30 Sep 2017 22:45:03 +0000 Subject: [PATCH 2/3] Use anchors for pf configuration --- lib/vagrant-bhyve/action/forward_ports.rb | 5 +- lib/vagrant-bhyve/driver.rb | 111 ++++++++-------------- locales/en.yml | 16 ++++ 3 files changed, 60 insertions(+), 72 deletions(-) diff --git a/lib/vagrant-bhyve/action/forward_ports.rb b/lib/vagrant-bhyve/action/forward_ports.rb index 19a6ac9..9bf9251 100644 --- a/lib/vagrant-bhyve/action/forward_ports.rb +++ b/lib/vagrant-bhyve/action/forward_ports.rb @@ -13,8 +13,9 @@ def initialize(app, env) def call(env) @machine = env[:machine] @driver = @machine.provider.driver + @ui = env[:ui] - env[:ui].info I18n.t('vagrant_bhyve.action.vm.forward_ports') + @ui.info I18n.t('vagrant_bhyve.action.vm.forward_ports') env[:forwarded_ports] = compile_forwarded_ports(@machine.config) tap_device = @driver.get_attr('tap') @@ -25,7 +26,7 @@ def call(env) guest_port: item[:guest], host_port: item[:host] } - @driver.forward_port(forward_information, tap_device) + @driver.forward_port(forward_information, tap_device, @ui) end @app.call(env) end diff --git a/lib/vagrant-bhyve/driver.rb b/lib/vagrant-bhyve/driver.rb index 933d611..6d19cd6 100644 --- a/lib/vagrant-bhyve/driver.rb +++ b/lib/vagrant-bhyve/driver.rb @@ -146,6 +146,28 @@ def load_module(module_name) end end + def check_or_create_default_pfconf(ui) + if execute(true, "test -s /etc/pf.conf") != 0 + ui.warn I18n.t("vagrant_bhyve.action.vm.boot.create_default_pfconf") + + # probably this could be done in a nicer way with open and puts... + execute(false, "echo \"nat-anchor \\\"vagrant/*\\\"\" | #{@sudo} tee -a /etc/pf.conf") + execute(false, "echo \"rdr-anchor \\\"vagrant/*\\\"\" | #{@sudo} tee -a /etc/pf.conf") + execute(false, "echo \"anchor \\\"vagrant/*\\\"\" | #{@sudo} tee -a /etc/pf.conf") + restart_service('pf') + else + if execute(true, 'pfctl -sn | grep -q "nat-anchor .vagrant/"') != 0 + ui.warn I18n.t("vagrant_bhyve.errors.nat_anchor_not_found") + end + if execute(true, 'pfctl -sn | grep -q "nat-anchor .vagrant/"') != 0 + ui.warn I18n.t("vagrant_bhyve.errors.rdr_anchor_not_found") + end + if execute(true, 'pfctl -sr | grep -q "anchor .vagrant/"') != 0 + ui.error I18n.t("vagrant_bhyve.errors.anchor_not_found") + end + end + end + def create_network_device(device_name, device_type) return if device_name.length == 0 @@ -174,27 +196,8 @@ def create_network_device(device_name, device_type) pf_conf.open('w') do |f| f.puts "set skip on #{interface_name}" end - comment_mark = "# vagrant-bhyve #{interface_name}" - if execute(true, "test -s /etc/pf.conf") == 0 - if execute(true, "grep \"#{comment_mark}\" /etc/pf.conf") != 0 - comment_mark_bridge = "# vagrant-bhyve #{bridge}" - if execute(true, "grep \"#{comment_mark_bridge}\" /etc/pf.conf") != 0 - execute(false, "#{@sudo} sed -i '' '1i\\\n#{comment_mark}\n' /etc/pf.conf") - execute(false, "#{@sudo} sed -i '' '2i\\\ninclude \"#{pf_conf.to_s}\"\n' /etc/pf.conf") - else - bridge_line = execute(false, "grep -A 1 \"#{comment_mark_bridge}\" /etc/pf.conf | tail -1") - bridge_line = bridge_line.gsub("\"", "\\\"") - bridge_line = bridge_line.gsub("/", "\\/") - execute(false, "#{@sudo} sed -i '' '/#{bridge_line}/a\\\n#{comment_mark}\n' /etc/pf.conf") - execute(false, "#{@sudo} sed -i '' '/#{comment_mark}/a\\\ninclude \"#{pf_conf.to_s}\"\n' /etc/pf.conf") - end - end - else - execute(false, "echo \"#{comment_mark}\" | #{@sudo} tee -a /etc/pf.conf") - execute(false, "echo \"include \\\"#{pf_conf.to_s}\\\"\" | #{@sudo} tee -a /etc/pf.conf") - end - restart_service('pf') - #execute(false, "#{@sudo} pfctl -a '/vagrant_#{id}' -f #{pf_conf.to_s}") + check_or_create_default_pfconf(ui) + execute(false, "#{@sudo} pfctl -a 'vagrant/#{id}' -f #{pf_conf.to_s}") #if !pf_enabled? # execute(false, "#{@sudo} pfctl -e") #end @@ -225,29 +228,16 @@ def enable_nat(bridge, ui) execute(false, "#{@sudo} sysctl net.inet.ip.forwarding=1 >/dev/null 2>&1") execute(false, "#{@sudo} sysctl net.inet6.ip6.forwarding=1 >/dev/null 2>&1") - # Change pf's configuration - pf_conf = directory.join("pf.conf") - pf_conf.open("w") do |pf_file| - pf_file.puts "set skip on #{bridge_name}" + check_or_create_default_pfconf(ui) + # set up bridge pf anchor + pf_bridge_conf = "/usr/local/etc/pf.#{bridge_name}.conf" + File.open(pf_bridge_conf, "w") do |pf_file| pf_file.puts "nat on #{gateway} from {#{sub_net}.0/24} to any -> (#{gateway})" + pf_file.puts "pass quick on #{bridge_name}" end - pf_bridge_conf = "/usr/local/etc/pf.#{bridge_name}.conf" - comment_mark = "# vagrant-bhyve #{bridge_name}" - execute(false, "#{@sudo} mv #{pf_conf.to_s} #{pf_bridge_conf}") - if execute(true, "test -s /etc/pf.conf") == 0 - if execute(true, "grep \"#{comment_mark}\" /etc/pf.conf") != 0 - execute(false, "#{@sudo} sed -i '' '1i\\\n#{comment_mark}\n' /etc/pf.conf") - execute(false, "#{@sudo} sed -i '' '2i\\\ninclude \"#{pf_bridge_conf}\"\n' /etc/pf.conf") - end - else - execute(false, "echo \"#{comment_mark}\" | #{@sudo} tee -a /etc/pf.conf") - execute(false, "echo \"include \\\"#{pf_bridge_conf}\\\"\" | #{@sudo} tee -a /etc/pf.conf") - end - restart_service('pf') + # Use pfctl to enable pf rules - #execute(false, "#{@sudo} cp #{pf_conf.to_s} /usr/local/etc/pf.#{bridge_name}.conf") - #execute(false, "#{@sudo} pfctl -a '/vagrant_#{bridge_name}' -f /usr/local/etc/pf.#{bridge_name}.conf") - # execute(false, "#{@sudo} pfctl -a '/vagrant_#{bridge_name}' -sr") + execute(false, "#{@sudo} pfctl -a 'vagrant/#{bridge_name}' -f /usr/local/etc/pf.#{bridge_name}.conf") # Create a basic dnsmasq setting # Basic settings @@ -457,31 +447,19 @@ def shutdown(ui) end end - def forward_port(forward_information, tap_device) + def forward_port(forward_information, tap_device, ui) id = get_attr('id') ip_address = get_ip_address(tap_device) pf_conf = @data_dir.join('pf.conf') rule = "rdr on #{forward_information[:adapter]} proto {udp, tcp} from any to any port #{forward_information[:host_port]} -> #{ip_address} port #{forward_information[:guest_port]}" + # FIXME: does this work for multiple port forwards, or should we rather set up a list with them and template that out to the pf.conf file? pf_conf.open('a') do |pf_file| pf_file.puts rule end - # Update pf rules - comment_mark = "# vagrant-bhyve #{tap_device}" - if execute(true, "test -s /etc/pf.conf") == 0 - if execute(true, "grep \"#{comment_mark}\" /etc/pf.conf") != 0 - execute(false, "#{@sudo} sed -i '' '1i\\\n#{comment_mark}\n' /etc/pf.conf") - execute(false, "#{@sudo} sed -i '' '2i\\\ninclude \"#{pf_conf.to_s}\"\n' /etc/pf.conf") - end - else - execute(false, "echo \"#{comment_mark}\" | #{@sudo} tee -a /etc/pf.conf") - execute(false, "echo \"include \\\"#{pf_conf.to_s}\\\"\" | #{@sudo} tee -a /etc/pf.conf") - end - restart_service('pf') - #execute(false, "#{@sudo} pfctl -a '/vagrant_#{id}' -f #{pf_conf.to_s}") - #execute(false, "#{@sudo} pfctl -a '/vagrant_#{id}' -sr") - #execute(false, "#{@sudo} pfctl -a vagrant_#{id} -F all") + check_or_create_default_pfconf(ui) + execute(false, "#{@sudo} pfctl -a 'vagrant/#{id}' -f #{pf_conf.to_s}") end def cleanup @@ -497,11 +475,8 @@ def cleanup execute(false, "#{@sudo} bhyvectl --destroy --vm=#{vm_name} >/dev/null 2>&1") if state(vm_name) == :uncleaned # Clean instance-specific pf rules - #execute(false, "#{@sudo} pfctl -a '/vagrant_#{id}' -F all") - comment_mark_tap = "# vagrant-bhyve #{tap}" - if execute(true, "grep \"#{comment_mark_tap}\" /etc/pf.conf") == 0 - execute(false, "#{@sudo} sed -i '' '/#{comment_mark_tap}/,+1d' /etc/pf.conf") - end + execute(false, "#{@sudo} pfctl -a 'vagrant/#{id}' -F all") + # Destory tap interfaces execute(false, "#{@sudo} ifconfig #{tap} destroy") if execute(true, "ifconfig #{tap}") == 0 execute(false, "#{@sudo} sed -i '' '/#{mac}/d' /var/run/dnsmasq.#{bridge}.leases") if execute(true, "grep \"#{mac}\" /var/run/dnsmasq.#{bridge}.leases") == 0 @@ -516,19 +491,15 @@ def cleanup member_num = execute(false, "ifconfig #{bridge} | grep -c 'member' || true") if bridge_exist == 0 if bridge_exist != 0 || member_num.to_i < 2 - #execute(false, "#{@sudo} pfctl -a '/vagrant_#{bridge}' -F all") - comment_mark_bridge = "# vagrant-bhyve #{bridge}" - if execute(true, "grep \"#{comment_mark_bridge}\" /etc/pf.conf") == 0 - execute(false, "#{@sudo} sed -i '' '/#{comment_mark_bridge}/,+1d' /etc/pf.conf") - end - restart_service('pf') + execute(false, "#{@sudo} pfctl -a 'vagrant/#{bridge}' -F all") + #if directory.join('pf_disabled').exist? # FileUtils.rm directory.join('pf_disabled') # execute(false, "#{@sudo} pfctl -d") #end execute(false, "#{@sudo} ifconfig #{bridge} destroy") if bridge_exist == 0 - pf_conf = "/usr/local/etc/pf.#{bridge}.conf" - execute(false, "#{@sudo} rm #{pf_conf}") if execute(true, "test -e #{pf_conf}") == 0 + pf_bridge_conf = "/usr/local/etc/pf.#{bridge}.conf" + execute(false, "#{@sudo} rm #{pf_bridge_conf}") if execute(true, "test -e #{pf_bridge_conf}") == 0 if execute(true, "test -e /var/run/dnsmasq.#{bridge}.pid") == 0 dnsmasq_cmd = "dnsmasq -C /usr/local/etc/dnsmasq.#{bridge}.conf -l /var/run/dnsmasq.#{bridge}.leases -x /var/run/dnsmasq.#{bridge}.pid" dnsmasq_conf = "/var/run/dnsmasq.#{bridge}.leases" diff --git a/locales/en.yml b/locales/en.yml index 6866712..b2131f5 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -20,6 +20,9 @@ en: forward_ports: |- Setup port forwarding(if any). boot: + create_default_pfconf: |- + It seems you had no default pf configuration. A basic default + was set up for you in /etc/pf.conf setup_nat: |- Setting up a nat environment using pf and dnsmasq to provide network for VMs. You will need to have dnsmasq installed on @@ -88,3 +91,16 @@ en: not_found_leases_info: |- Unable to found IP from dnsmasq's leases file, please retry after a few seconds. + nat_anchor_not_found: |- + NAT will not work: your PF configuration does not contain a NAT + anchor. Add 'nat-anchor "vagrant/*"' to your PF configuration in + the TRANSLATION section. See pf.conf(5) for details. + rdr_anchor_not_found: |- + Port forwarding will not work: your PF configuration does not + contain a redirection anchor. Add 'rdr-anchor "vagrant/*"' to your + PF configuration in the TRANSLATION section. See pf.conf(5) for + details. + anchor_not_found: |- + Your PF configuration does not contain a filter anchor. Add + 'anchor "vagrant/*"' to your PF configuration in the TRANSLATION + section. See pf.conf(5) for details. From 2b47adad763573d6186012a95c01daf5e498a7d2 Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Sun, 1 Oct 2017 01:47:52 +0000 Subject: [PATCH 3/3] More verbose error messages for bridge handling --- lib/vagrant-bhyve/action/create_bridge.rb | 2 +- lib/vagrant-bhyve/action/create_tap.rb | 3 ++- lib/vagrant-bhyve/driver.rb | 11 ++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/vagrant-bhyve/action/create_bridge.rb b/lib/vagrant-bhyve/action/create_bridge.rb index 1a07dbc..b009ab1 100644 --- a/lib/vagrant-bhyve/action/create_bridge.rb +++ b/lib/vagrant-bhyve/action/create_bridge.rb @@ -21,7 +21,7 @@ def call(env) bridge_list = %w(vagrant_bhyve_default_bridge) # The bridge name is used as created bridge device's description bridge_list.each do |bridge| - @driver.create_network_device(bridge, "bridge") + @driver.create_network_device(bridge, "bridge", @ui) @driver.enable_nat(bridge, @ui) end @app.call(env) diff --git a/lib/vagrant-bhyve/action/create_tap.rb b/lib/vagrant-bhyve/action/create_tap.rb index e8041dc..a087c47 100644 --- a/lib/vagrant-bhyve/action/create_tap.rb +++ b/lib/vagrant-bhyve/action/create_tap.rb @@ -13,6 +13,7 @@ def initialize(app, env) def call(env) @machine = env[:machine] @driver = @machine.provider.driver + @ui = env[:ui] env[:ui].detail I18n.t('vagrant_bhyve.action.vm.boot.create_tap') vm_name = @driver.get_attr('vm_name') @@ -20,7 +21,7 @@ def call(env) tap_list = [tap_name] # The switch name is used as created bridge device's description tap_list.each do |tap| - @driver.create_network_device(tap, "tap") + @driver.create_network_device(tap, "tap", @ui) end @app.call(env) end diff --git a/lib/vagrant-bhyve/driver.rb b/lib/vagrant-bhyve/driver.rb index 6d19cd6..4503127 100644 --- a/lib/vagrant-bhyve/driver.rb +++ b/lib/vagrant-bhyve/driver.rb @@ -168,7 +168,7 @@ def check_or_create_default_pfconf(ui) end end - def create_network_device(device_name, device_type) + def create_network_device(device_name, device_type, ui) return if device_name.length == 0 # Check whether the bridge has been created @@ -189,12 +189,17 @@ def create_network_device(device_name, device_type) # with the bridge mtu = execute(false, "ifconfig #{bridge} | head -n1 | awk '{print $NF}'") execute(false, "#{@sudo} ifconfig #{interface_name} mtu #{mtu}") if mtu.length != 0 and mtu != '1500' - execute(false, "#{@sudo} ifconfig #{bridge} addm #{interface_name}") + if execute(true, "ifconfig #{bridge} | grep -q \"member: #{interface_name}\"") != 0 + execute(false, "#{@sudo} ifconfig #{bridge} addm #{interface_name}") + else + ui.warn "#{interface_name} is already a member of #{bridge}" + end + # Setup VM-specific pf rules id = get_attr('id') pf_conf = @data_dir.join('pf.conf') pf_conf.open('w') do |f| - f.puts "set skip on #{interface_name}" + f.puts "pass quick on #{interface_name}" end check_or_create_default_pfconf(ui) execute(false, "#{@sudo} pfctl -a 'vagrant/#{id}' -f #{pf_conf.to_s}")