Skip to content

Conversation

@anjannath
Copy link
Member

@anjannath anjannath commented Aug 22, 2022

Needs crc-org/machine#66

Solution/Idea

the default CIFS/SMB feature provided from windows
can be used to share directories over the network
we can use it to share files between host and crc
vm

Adds the following config options:

  • enable-shared-dirs (was present on macOS and linux now added to windows)
  • smb-share-password

Testing

  1. use the msi from this PR to install crc
  2. run crc config set enable-shared-dirs true and follow the instructions from the validation msg (it should ask to configure smb-share-password first)
  3. run crc config set preset podman and crc setup
  4. crc start with podman preset and bundle from Add cifs-utils package to hyperV bundle snc#565 should succeed (download from openshift CI artifacts)
  5. container with a volume mount should work, e.g: podman run -v /mnt/home/crc/conf:/etc/http/conf httpd

@anjannath
Copy link
Member Author

still need to test on a clean vm if the msi is able to set everything up, and also verify the assumption that Private network profile has "File and Printer Sharing" enabled by default on window 10

@anjannath anjannath changed the title Add CIFS/SMB based file sharing for windows [WIP] Add CIFS/SMB based file sharing for windows Aug 23, 2022
@anjannath anjannath force-pushed the cifs branch 4 times, most recently from 97fd763 to 2b4517a Compare August 24, 2022 08:08
@anjannath anjannath changed the title [WIP] Add CIFS/SMB based file sharing for windows Add CIFS/SMB based file sharing for windows Aug 24, 2022
@anjannath
Copy link
Member Author

@gbraad PTAL

Before="EnableSMBFeature"
Sequence="execute"/>
<CustomAction Id="EnableSMBFeature" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Impersonate="no" Return="ignore" />
<SetProperty Action="CAEnableFileAndPrinterSharing"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it required to enable the sharing both for private and public? We are going to only use it for host -> VM communication, so maybe one of the 2 profiles will be enough?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think its needed to enable for both, because if we don't enable for both then we have a pre-condition that users need to be using the profile for which we enabled this to use shared dirs which i wanted avoid,

the way the profiles are set is If user's don't select a profile (when first time connecting to a new network) then windows will set the "Public" profile

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which is a security feature. you don't wanna bypass this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not a bypass, its just that I want the shared folders to be accessible in both "Private" and "Public" network profiles as we cannot predict what profile will be active on the user's machine

also the share is created giving access to only the login/current user and needs a password to access

Copy link
Contributor

@gbraad gbraad Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bypass as you do not need 'public' access, as that is based on what the user has chosen.
If the users clicks this away, that is a 'user error'.

we cannot predict what profile will be active on the user's machine

Yes you can. Inform the user of proper use.

This is why you need to check for reachability of the share before using.

Password or not, that is a different problem

Copy link
Member Author

@anjannath anjannath Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bypass as you do not need 'public' access,

The network profiles in windows are about what type of network you are connected to (home aka private network vs cafe wifi aka public network) and not in the sense of if something is publicly accessible, based on the user's selection of the network profile some default settings are applied

Enabling it for both the profiles, lets the user have shared folders without worrying about what network profile is currently active, otherwise if we only enable it for one profile, "Private" network profile, as said we need the pre-condition "To use shared folders in windows users need to set the Private profile as the active network profile"

This is why you need to check for reachability of the share before using.

If we have properly setup the SMB share, there won't be any need to do this check, if we only enable sharing for "Private" network we can just check if the current active network profile is "Private"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on the user's selection of the network profile some default settings are applied

And that is the point. I am not unfamiliar to these settings:

image

It is based on the user's selection, with the default being 'Public', which prevents access to these services, unless correctly set.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as said we need the pre-condition "To use shared folders in windows users need to set the Private profile as the active network profile"

Can you check for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes there's a cmd Get-NetConnectionProfile but it also needs an interface (seems profiles are per interface, so we need to first find the active interface)
https://docs.microsoft.com/en-us/powershell/module/netconnection/get-netconnectionprofile?view=windowsserver2022-ps#example-1-get-a-connection-profile

dir.Target = convertToUnixPath(dir.Target)
dir.Type = "cifs"
dir.Tag = smbShareUNCPathFromTag(dir.Tag)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo this should be done in pkg/crc/machine/hyperv/driver_windows.go / pkg/crc/machine/config/driver.go when we are setting up the driver, rather than doing it lazily in GetSharedDirs. Better to have valid directory sharing information from the start.

Copy link
Member Author

@anjannath anjannath Aug 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the existing SharedDirs struct setup from pkg/crc/machine/config/driver.go and added to machine/vfkit/driver_darwin.go and machine/libvirt/driver_linux.go its the same code duplicated cba3a6c

next commit follows the same pattern and configures shared dirs for hyperv in machine/hyperv/driver_windows.go


func convertToUnixPath(path string) string {
// converts a path like c:\users\crc to /mnt/c/users/crc
return fmt.Sprintf("/mnt/c%s", strings.TrimLeftFunc(filepath.ToSlash(path), func(r rune) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The podman function does not hardcode c as the drive, maybe we could reuse this helper directly rather than reimplementing it? (assuming vendoring this code does not make the binary a lot bigger).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a 9MB increase in the output binary which is not a lot, but the amount of code that is pulled in is huge! thinking to just c&p the relevent part from that function

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather see us re-use code. c&p is not just unethical, we are supposed to share and combine most of the codebase over time anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, apart from the extra code in vendor, we don't need the full functionality of that Function in https://github.com/containers/podman/blob/67c4068bb35fd1aad95b1701c94ed11183d0fd66/pkg/specgen/winpath.go#L23-L41

i still think just c&p the relevant line is better then vendoring the whole podman codebase!

func smbShareUNCPathFromTag(tag string) string {
hostname, err := cmdOut("hostname")
if err == nil {
return fmt.Sprintf("//%s.mshome.net/%s", strings.TrimSpace(hostname), tag)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we could use an IP there instead of hostname.mshome.net? Not sure it's better, maybe it would make the code simpler?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have to figure out the ip address on one of the interfaces of the host or the "Default Switch" the default switch name might be localized, but this gives the ip address for default switch (Get-NetIPAddress -InterfaceAlias "vEthernet (Default Switch)").IPv4Address

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In minishift we extracted the IP address, verified reachability before allowing it to be used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to use an ip address instead of hostname.mshome.net for the UNC path

@anjannath anjannath force-pushed the cifs branch 3 times, most recently from 526670c to 427a8cc Compare August 29, 2022 18:03
KubeAdminPassword = "kubeadmin-password"
Preset = "preset"
EnableSharedDirs = "enable-shared-dirs"
SharedDirPassword = "shared-dir-password" // #nosec G101
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gbraad Changed this to shared-dir-password instead of smb-share-password more generic, can be reused when we do the sshfs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

password aren't shared among dirs, as they apply to each individual share.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh, but i couldn't find a way to set password per share in windows, either from the GUI or from the the smbshare powerhsell cmdlets

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows bases that on the UNC path; they can be assigned a different user and password combination. In the current case of CIFS use you only deal with a USERFOLDER and not 'arbitrary' options given by the user.

@gbraad
Copy link
Contributor

gbraad commented Aug 30, 2022

Just based on some of the comments from Christophe I say there is still some open issues; code re-use, IP address for use, etc.


// Shared directories configs
if runtime.GOOS == "windows" {
validateSmbPasswordSet := func(value interface{}) (bool, string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not a regular named function inside a _windows.go file?

Copy link
Member Author

@anjannath anjannath Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its following the same pattern that's in the config package.. and we can't use a _windows.go in there without a lot of refactor because of how we are registering the settings

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so it is impossible to create a file named settings_windows.go and place this inside as a named function ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah looks like, if we do that we have to duplicate all of the common settings in settings_windows.go

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or RegisterSettings could call a registerOSSpecificSettings(), this should avoid the duplication.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would prefer to do this refactor separate from this PR :) moved the function definition near the other similar functions, hopefully the code looks a bit better then before

SSHKeyPath string
KubeConfig string
SharedDirs []string
SharedDirPassword string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so when we deal with sshfs, you are only able to use a single password for each of them?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to #3308 (comment) i was not thinking about sshfs internally that much, was only thinking about the user facing config options as of now, when implementing sshfs we might have to change this

KubeConfig string
SharedDirs []string
SharedDirPassword string
SharedDirUsername string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto for the username?


sharedDir := []libmachine.SharedDir{}
for _, dir := range driver.SharedDirs {
dir.Password = password
Copy link
Contributor

@gbraad gbraad Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we assume all shares share the same password.
Which username is associated... do not see this relation in this part of code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, for windows its based on the user account, so shares themselves don't have passwords i am not considering about sshfs here, when we implement that we might have to change this

Target: convertToUnixPath(dir),
Tag: smbShareUNCPathFromTag("crc-dir0"), // smb share 'crc-dir0' is created in the msi
Type: "cifs",
Username: machineConfig.SharedDirUsername,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we associate the username at this point, but not the password? Why do we need to do this in the previous part in an update function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because we don't want to store the password in the machine config file, so that's handled by the update function

Copy link
Contributor

@gbraad gbraad Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are concerned about the password, this is an association as a credential (username + password). It would make more sense to place the username in the update function too. Otherwise you are still 'leaking' information about the user.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not as much about leaking information but storing a secret on disk unencrypted which we want to prevent, on the user's machine any non-privilege process can just run whoami and get that username, so it should be fine to store in the machine config?

btw this was suggested in #3308 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any non-privilege process can just run whoami and get that username,

People share the virtual machine folder as we do not offer an export function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh, but that'd be a very special unsupported use case, assume most of our users don't do this, also we don't test it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out to be different from what I suggested earlier, but maybe this should all be coming from the keyring then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only the password needs to be securely stored, username we can derive during start, we just skip writing it to machine config json same as the password

@anjannath
Copy link
Member Author

PR rebased on top of #3333

Copy link
Contributor

@gbraad gbraad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved for podman bundle
Unable to test for the OpenShift bundle

Follow-up issues needed for the initial flow. Can't be enabled by default as an explicit password is needed.

@anjannath
Copy link
Member Author

/retest

@anjannath
Copy link
Member Author

Approved for podman bundle Unable to test for the OpenShift bundle

tested with the new 4.11.7 openshift bundle and working as expected

Copy link
Contributor

@cfergeau cfergeau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this waiting for a second ACK? Gerard approved this a while ago.

if !cfg.Get(HostNetworkAccess).AsBool() {
return false, fmt.Sprintf("%s can only be used with %s set to 'true'",
EnableSharedDirs, HostNetworkAccess)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to restrict this to only the ports needed for file sharing?
The VM having access to all the services running on the host, and also knowing the user login/password does not sound great from a security perspective. We've removed a big part of the isolation between host and guest imo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't know if its possible to filter for ports, i guess that will also need changes to gvisor-tap-vsock

maybe we could try adding a :port to the NAT table entries setup during daemon start and see if that works!

func RequiresCleanupAndSetupMsg(key string, _ interface{}) string {
return fmt.Sprintf("Changes to configuration property '%s' are only applied during 'crc setup'.\n"+
"Please run 'crc cleanup' followed by 'crc setup' for this configuration to take effect.", key)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up issue to make this more dynamic would be useful to have.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@openshift-ci
Copy link

openshift-ci bot commented Oct 10, 2022

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: cfergeau, gbraad

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

This enbles file and printer sharing for the
'Private' and 'Public' network profiles

Sharing is enabled during install and disabled
during uninstall
This creates a samba share named 'crc-dir0' of the logon user's
home folder during installation
options needed for the shared dirs such as the source path, target path
and types etc. differs between drivers and its best to configure  those
at the specific driver level

although currently both libvirt and vfkit have the same options for their
shared directories, hyperv will have different options to use smb instead
of virtiofs
it allows users to mount the host's home folder at
/mnt/c/users/$username directory in the crc vm

to determine if folder sharing is supported/enabled it
checks that a smb share named 'crc-dir0' exists which
should've been created during msi installation

since CIFS/SMB is a network file system and needs a username
and password for getting access two new fields 'SharedDirUsername'
and 'SharedDirPassword' is added to the MachineConfig and StartConfig
structs
this adds a new error type MaskedSecretError to the errors package
currently we need this to get the username in windows
for mounting the shared directories, which is CIFS/SMB
based and needs the username for access
this adds an additional config 'shared-dir-password' to  store
the needed password for mounting the samba share and  modifies
the help message of 'enable-shared-dirs' config for linux  and
macOS

the 'shared-dir-password' config is a secret config and is not
listed in the reponse from the config api when getting all the
config options
since we filter out the secret configs in the response from config api
we need to also skip them in the api client test case to make it  pass

addition of the 'enable-shared-dirs' secret config makes the test fail
in the previous commit

this was missed in 105ad05
changes to 'host-network-access' only works when the daemon is restarted
for this to happen user needs to run 'crc cleanup' and then  'crc setup'

this adds a new function 'RequiresCleanupAndSetupMsg' config package
@openshift-ci
Copy link

openshift-ci bot commented Oct 11, 2022

New changes are detected. LGTM label has been removed.

@anjannath anjannath merged commit a1ff049 into crc-org:main Oct 11, 2022
@anjannath anjannath deleted the cifs branch October 11, 2022 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved os/windows status/peer review required Peer review by assignee is required before being merged;

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CIFS/SMB based file sharing for windows

5 participants