Restrict synced folder access to DrvFs file systems only within WSL

This commit is contained in:
Chris Roberts 2018-06-30 09:58:19 -07:00
parent 1f99da7a11
commit 859d48d5f5
5 changed files with 188 additions and 0 deletions

View File

@ -483,6 +483,41 @@ module Vagrant
path.to_s.start_with?(wsl_windows_accessible_path.to_s)
end
# Mount pattern for extracting local mount information
MOUNT_PATTERN = /^(?<device>.+?) on (?<mount>.+?) type (?<type>.+?) \((?<options>.+)\)/.freeze
# Get list of local mount paths that are DrvFs file systems
#
# @return [Array<String>]
def wsl_drvfs_mounts
if !defined?(@_wsl_drvfs_mounts)
@_wsl_drvfs_mounts = []
if wsl?
result = Util::Subprocess.execute("mount")
result.stdout.each_line do |line|
info = line.match(MOUNT_PATTERN)
if info && info[:type] == "drvfs"
@_wsl_drvfs_mounts << info[:mount]
end
end
end
end
@_wsl_drvfs_mounts
end
# Check if given path is located on DrvFs file system
#
# @param [String, Pathname] path Path to check
# @return [Boolean]
def wsl_drvfs_path?(path)
if wsl?
wsl_drvfs_mounts.each do |mount_path|
return true if path.to_s.start_with?(mount_path)
end
end
false
end
# If running within the Windows Subsystem for Linux, this will provide
# simple setup to allow sharing of the user's VAGRANT_HOME directory
# within the subsystem

View File

@ -3,6 +3,7 @@ require "securerandom"
require "set"
require "vagrant"
require "vagrant/action/builtin/mixin_synced_folders"
require "vagrant/config/v2/util"
require "vagrant/util/platform"
require "vagrant/util/presence"
@ -757,6 +758,33 @@ module VagrantPlugins
end
end
# If running from the Windows Subsystem for Linux, validate that configured
# hostpaths for synced folders are on DrvFs file systems, or the synced
# folder implementation explicitly supports non-DrvFs file system types
# within the WSL
if Vagrant::Util::Platform.wsl?
# Create a helper that will with the synced folders mixin
# from the builtin action to get the correct implementation
# to be used for each folder
sf_helper = Class.new do
include Vagrant::Action::Builtin::MixinSyncedFolders
end.new
folders = sf_helper.synced_folders(machine, config: self)
folders.each do |impl_name, data|
data.each do |_, fs|
hostpath = File.expand_path(fs[:hostpath], machine.env.root_path)
if !Vagrant::Util::Platform.wsl_drvfs_path?(hostpath)
sf_klass = sf_helper.plugins[impl_name.to_sym].first
if sf_klass.respond_to?(:wsl_allow_non_drvfs?) && sf_klass.wsl_allow_non_drvfs?
next
end
errors["vm"] << I18n.t("vagrant.config.vm.shared_folder_wsl_not_drvfs",
path: fs[:hostpath])
end
end
end
end
# Validate sub-VMs if there are any
@__defined_vms.each do |name, _|
if name =~ /[\[\]\{\}\/]/

View File

@ -1794,6 +1794,10 @@ en:
be an array of options.
shared_folder_requires_guestpath_or_name: |-
Shared folder options must include a guestpath and/or name.
shared_folder_wsl_not_drvfs: |-
The host path of the shared folder is not supported from WSL. Host
path of the shared folder must be located on a file system with
DrvFs type. Host path: %{path}
#-------------------------------------------------------------------------------
# Translations for commands. e.g. `vagrant x`

View File

@ -589,6 +589,79 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
sf = subject.synced_folders
expect(sf["my-vagrant-folder"][:guestpath]).to be_nil
end
context "WSL host paths" do
let(:valid_path){ "/mnt/c/path" }
let(:invalid_path){ "/home/vagrant/path" }
let(:synced_folder_impl){ double("synced_folder_impl", new: double("synced_folder_inst", usable?: true)) }
let(:fs_config){ double("fs_config", vm: double("fs_vm", allowed_synced_folder_types: nil)) }
let(:plugin){ double("plugin", manager: manager) }
let(:manager){ double("manager", synced_folders: {sf_impl: [synced_folder_impl, 1]}) }
let(:stub_pathname){ double("stub_pathname", directory?: true, relative?: false) }
before do
allow(Pathname).to receive(:new).and_return(stub_pathname)
allow(stub_pathname).to receive(:expand_path).and_return(stub_pathname)
allow(Vagrant::Util::Platform).to receive(:wsl?).and_return(true)
allow(Vagrant::Util::Platform).to receive(:wsl_drvfs_path?).with(valid_path).and_return(true)
allow(Vagrant::Util::Platform).to receive(:wsl_drvfs_path?).with(invalid_path).and_return(false)
allow(machine).to receive(:config).and_return(fs_config)
allow(Vagrant).to receive(:plugin).with("2").and_return(plugin)
subject.synced_folder(".", "/vagrant", disabled: true)
end
it "is valid when located on DrvFs" do
subject.synced_folder(valid_path, "/guest/path")
subject.finalize!
assert_valid
end
it "is invalid when not located on DrvFs" do
subject.synced_folder(invalid_path, "/guest/path")
subject.finalize!
assert_invalid
end
context "when synced folder defines support for non-DrvFs" do
let(:support_nondrvfs){ true }
before do
allow(synced_folder_impl).to receive(:respond_to?).with(:wsl_allow_non_drvfs?).and_return(true)
allow(synced_folder_impl).to receive(:wsl_allow_non_drvfs?).and_return(support_nondrvfs)
end
context "and is supported" do
it "is valid when located on DrvFs" do
subject.synced_folder(valid_path, "/guest/path")
subject.finalize!
assert_valid
end
it "is valid when not located on DrvFs" do
subject.synced_folder(invalid_path, "/guest/path")
subject.finalize!
assert_valid
end
end
context "and is not supported" do
let(:support_nondrvfs){ false }
it "is valid when located on DrvFs" do
subject.synced_folder(valid_path, "/guest/path")
subject.finalize!
assert_valid
end
it "is invalid when not located on DrvFs" do
subject.synced_folder(invalid_path, "/guest/path")
subject.finalize!
assert_invalid
end
end
end
end
end
describe "#usable_port_range" do

View File

@ -357,5 +357,53 @@ describe Vagrant::Util::Platform do
end
end
end
describe ".wsl_drvfs_mounts" do
let(:mount_output) { <<-EOF
rootfs on / type lxfs (rw,noatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,noatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,noatime)
none on /dev type tmpfs (rw,noatime,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,noatime)
none on /run type tmpfs (rw,nosuid,noexec,noatime,mode=755)
none on /run/lock type tmpfs (rw,nosuid,nodev,noexec,noatime)
none on /run/shm type tmpfs (rw,nosuid,nodev,noatime)
none on /run/user type tmpfs (rw,nosuid,nodev,noexec,noatime,mode=755)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noatime)
C: on /mnt/c type drvfs (rw,noatime)
EOF
}
before do
expect(Vagrant::Util::Subprocess).to receive(:execute).with("mount").
and_return(Vagrant::Util::Subprocess::Result.new(0, mount_output, ""))
end
it "should locate DrvFs mount path" do
expect(subject.wsl_drvfs_mounts).to eq(["/mnt/c"])
end
context "when no DrvFs mounts exist" do
let(:mount_output){ "" }
it "should locate no paths" do
expect(subject.wsl_drvfs_mounts).to eq([])
end
end
end
describe ".wsl_drvfs_path?" do
before do
expect(subject).to receive(:wsl_drvfs_mounts).and_return(["/mnt/c"])
end
it "should return true when path prefix is found" do
expect(subject.wsl_drvfs_path?("/mnt/c/some/path")).to be_truthy
end
it "should return false when path prefix is not found" do
expect(subject.wsl_drvfs_path?("/home/vagrant/some/path")).to be_falsey
end
end
end
end