Nvm-windows: nvm should be able to run without admin rights

Created on 13 Jun 2019  Âˇ  19Comments  Âˇ  Source: coreybutler/nvm-windows

I installed nvm yesterday on a machine where I have no admin rights. I decided for separate paths for nvm and npm than the standard Windows paths so I can overcome the need for admin rights.

nvm was able to download versions, but the symlink process did not work, although I could manually create the link with mklink without any trouble.

I decided to check the code, and changed the elevate.cmd file to this:

@setlocal
@echo off
set CMD=%*
set APP=%1
start %CMD%

This version of the file is now working without admin rights and doing it´s job. It would be great to be able to configure this on the settings.txt with some flag, so people who install in differnt paths can work without admin rights.

wontfix

Most helpful comment

@ChrisKader You should just publish your script as a separate tool.

All 19 comments

It is possible your environment does grant the appropriate rights, but by default, user accounts must be part of the Adminstrator group in order to use mklink. See https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links.

Is there any reason to use mklink in place of local user account environment variables? If mklink fails the files could just be copied. Switching, and the directory is not a link, asking to copy changes back.

https://unix.stackexchange.com/questions/96907/how-do-i-check-if-a-file-is-a-symbolic-link-to-a-directory

It's not as simple as just copying node.exe. The install root contains (by default) the global node_modules directory and a number of other scripts. So, either _all_ of that needs to be copied/cleaned up, or the npm client config needs to be updated to identify the location of the global node_modules directory.

Copying takes an enormous amount of time and creates tremendous file system churn when you have mapped/network drives or roaming profiles. Bottom line: there's no guarantee the files will actually be stored on a local hard drive. They could be on a NAS/SAN/etc. Not to mention, it's not uncommon to see gigabytes worth of node modules in each installed version, so even copying on a local hard drive could take quite awhile.

Given how many different versions of npm exist (not to mention it's a commercially-backed tool), manipulating the npm environment to understand where the global node_modules directory exists is outside the scope of this project. Plus, there's also yarn to consider.

Local environment variables are used. The PATH contains the symlink path. It's not a good idea to be modifying the PATH any more than absolutely necessary (which still requires admin rights). If there is a glitch or error during the process, the entire PATH can be nuked (I tried this in the early days of NVM4W). However; it is possible to change the destination of the symlink, making it unnecessary to modify the PATH environment variable. That's why mklink is used.

So network drives are not supported and Fat32 flash drives can't be supported (does not support mklink). A prompt asking if you really wish to copy files would solve the "file churn" issue. User can enter admin mode. Or they can choose to file churn. Or cancel.

If you copy files, path shouldn't need set that path more than once. And powershell might be able to change the current users (not system) environment path without admin rights. And, if that path gets nuked, the system path will still be safe. Seems a lot less dangerous.

That will take some experimenting. I will give it a shot at work on break or lunch. I've switched to Linux at home.

Doing some more research: "Any Windows 10 user with Developer Mode enabled will be able to create symlinks using either the mklink". That could also be a possible answer. Convince the folks at work that I am a developer and need developer mode enabled.

The question is, will admin mode be needed if developer mode is enabled?

I can't answer authoritatively whether developer mode will negate the need for admin privileges. My guess is yes. However; any operations touching C:\Program Files require admin privileges (unless developer mode also waives that restriction).

Network drives are supported with mklink when used as a junction. There has been alot of back/forth on the ROI of using junctions, so it has never been implemented.

Remember, Powershell isn't guaranteed to be available in all environments.

I'm not explicitly opposed to the file copying approach, as long as it is a) behind a flag/CLI prompt and b) does not require a visual interface. That last part is important, because there are a number of headless CI services using this.

Why I tend to install development tools c:\programs\ (no admin rights and no spaces in path). c:\programs\nvm and configure node to install to c:\programs\nodejs.

powershell.exe -command '[Environment]::SetEnvironmentVariable("PATH",[Environment]::GetEnvironmentVariable("PATH","User")+"c:\programs\nvm","User")' || XSET PATH "%PATH%;c:\programs\nvm"
Seems to work ok? xset seemed to work too (had to wait a few minutes before they showed up with xset)? I have UAC turned off on the windows pc I am using now. Tomorrow, at work, I'll get to check this out.

tl;dr https://github.com/PowerShell supports Linux (not that I would ever use it there). So I can't think of a environment that powershell can't support that nvm also can't support. node and powershell both run scripts. There is no baked-in, required-signing certificates for node to validate the script is legit and hasn't been tampered with. Powershell has this. npm audit is reactive. imho, node isn't server ready if you need full security. However, most people don't. Proxies are good enough.

It is possible your environment does grant the appropriate rights, but by default, user accounts must be part of the Adminstrator group in order to use mklink. See https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links.

Creating symlinks doesn't require admin access anymore start from Windows 10 Build 14972, what's more you can use Junction instead of symlinks to link, so it is able to work without admin rights when making symlinks in user space like C:\Users\<user>. Touching C:\Program Files still require admin rights though.

My question is why not making nvm-windows away from admin rights by default, and if one user is trying to do something with C:\Program Files or other paths which requires admin rights, then do the permission elevation, instead of requesting admin rights everywhere.

To @mschmiedel If you want to use nvm-windows without admin rights, I have a fork(release) for you, but it can't deal with system-wide Node.js (I didn't test it, it might work under an elevated prompt).

related to #79 #383

@h404bi why not keep admin elevation (I don't think elevate.vbs / .bat shouldn't be blanked). But only execute admin elevation when junction fails and retry is done? The only reason it should fail is due to permissions failing. Such as node or nvm being installed to c:\program files. Otherwise, the pull request looks promising (just looking at the diff).

https://github.com/tamusjroyce/nvm-windows - my attempt at it. But I have never touched golang. I'm pretty sure this will crash and burn. bin/install.cmd as well (though I don't understand how it can unzip the archive it is within?). Not sure why my git corrupted the binaries. But fair warning (best if binaries are separated from source control).

On install, recursively call the install script with elevate.cmd if write permissions to NVM_PATH fails.
echo "Check if nvm path requires admin writes to create file" > %NVM_PATH%\checkpermissions.txt || elevate.cmd install.cmd %NVM_PATH%

https://github.com/tamusjroyce/nvm-windows/commit/7cde75d0ef375cc68c763404b4b3f20b99bbee52

@h404bi My only hesitation with moving away from admin rights by default is the number of users who can take advantage of this are still in the minority, for now. However; times change, and I certainly wouldn't mind getting away from requiring admin privileges. As long as the user experience isn't degraded, it's something I could see being a nice step forward.

That said, there still needs to be a flag for using junctions. This has been on my to-do list for a very long time now. It should be a configuration option... which leads to the bigger challenge. I've been wanting to get rid of the settings.txt file pretty much ever since the initial release. I've been experimenting with a SQLite approach, which works well in other apps I've been building, but it's not ready for NVM4W yet.

I'll take a look at the forks as soon as I can. I'm not sure when I'll have time though. In the meantime, the best way to get something merged is to make sure it works at least back through Win 8.1.

There are a couple of ways to make mklink work without being admin (up to explicitely granting SeCreateSymbolicLinkPrivilege).
Why not simply try running it and only if it fails retry with elevation?

It's so annoying to enter local admin user credentials twice for each change of node...

My only hesitation with moving away from admin rights by default is the number of users who can take advantage of this are still in the minority, for now.

I am installing nvm using scoop package manager. Scoop by default install packages to ~/.scoop. Currently, installing nvm using scoop prompts for admin rights. It would be nice to have this fixed.

I currently have a PowerShell script (It has grown a lot) I made for use by myself and other developers at work (about 200 or so people). I call it “PNM: PowerShell NodeJS Manager”. This was created as a result of an industry shift in which developers environments are more secured and as a result developers have no admin access (If they do, its usually a different account). I also firmly believe that programs should not be run in with elevated privileges unless absolutely required (regardless if the user has admin rights or not). When I noticed that nvm does not require elevated privileges on other platforms (Linux, OS X, ect) I set out to create a solution that would work the same on Windows.

While it is true that Windows 10 no longer requires admin rights to create/modify symlinks, it DOES require the system to be in “developer mode” (which requires admin rights to enable). Any environment in which a developer doesn’t have admin rights probably not have their workstation in “developer mode”.

As such, this powershell script takes advantage of user env vars.

The script does the following:

  • Not every switch and exact detail listed.

    • This PS script has expanded over the years to provide A LOT of customization options

  • Executing “pnm ins v10.0.1 productZ [dft] [node-gyp,nextGlobalPackage,nextGlobalPackage2]” will

    • Download and extract nodeJS version 10.0.1 and place it in its own folder with the nodejs subdirectory.

    • Name the folder v10.0.1_productA and allow the use an instance name or node version



      • “pnm use productZ”


      • “pnm use v10.0.1”


      • If more than one instance of 10.0.1 exists, it will use the one that was marked as default when it was installed.


      • If no default was specified, the user will be prompted to select which instance



    • Install “node-gyp”, “nextGlobalPackage”, and “nextGlobalPackage2” into the specified instances global node_modules folder.

    • Update the config file with the instance info and modules installed.

    • If conflicts, user will be promoted to resolve.

  • “pnm sync [dry] productZ [>|=] productA”

    • dry



      • Will provide a listing of changes based on the parameters provided but will NOT make changes (non-destructive)



    • >



      • This will “npm install” any global modules installed in the “productZ” instance into the “productA” instance.


      • Will NOT uninstall modules from productA that don’t exist in productZ



    • =



      • Will sync productA with modules installed in productZ


      • WILL DELETE all modules from productA that do not exist in productZ



    • <=



      • Opposite of above.



  • “pnm rst [full|mods] productZ”

    • Command will update the config file with the latest updates to the node_modules folder before executing below.

    • full



      • will delete the whole directory and redownload (or use cached version) nodeJS and install modules



    • mods



      • rmdir -rf the node_modules folder and reinstall from list in config



  • “pnm ls rmt”

    • List nodeJS (and npm) versions available for download AND install



      • Will check for update and download or use file in cache folder.



  • “pnm ls lcl”

    • List nodeJS versions available for install (already downloaded)

  • “pnm cfg sys [mgt ‘C:\’] [cln] [save]”

    • Requires admin rights

    • Sets up PNM to be used in the system env var

    • Removes PNM env vars from the user level

    • Updates PNM env vars at the system level

    • Using migrate and providing a path will move the .pnm folder to the path

    • Specifying clean will create a .pnm folder at the path, setup the folder structure and copy over config.json.

    • It will then install each instance based on the config.json file

    • Once complete and verified, the old .pnm folder is deleted, unless the save flag is specified

  • “pnm cfg usr [username]”

    • Requires admin

    • Reverts changes made for system level configuration and applies them to the username specified or current user if non specified.

  • “pnm init [sys|usr] [username]”

    • The init flag can be combined with a path to an existing config.json file

    • Will setup for specified username (If user has access).

    • The PNM install will then be configured as detailed in the config file



      • This includes setting up each instance in the file (and installing all global modules in the instances node_modules folder).



    • Creates a folder called “.pnm” in %USERPROFILE%



      • config.json





        • Contains default settings for all triggers (example below)



        • arch: x86 or x64



        • chcDir



        • Can be any user readable and/or writable directory



        • Allows you to create a network share containing all nodeJS installs to use by whoever, as an example



        • If the user does not have write access, the directory will be used to install whatever version is specified





      • alwsNew: true or false





        • Will always create a new nodejs instance when the version being installed already exists



        • If no instance name is provided, it simply keeps increasing by 1.





      • instMap: true or false





        • Controls if instance mapping is updated in the config file when instances are installed or configured.



        • This allows each instance install to be saved to the config file while documenting



        • nodeJS version



        • npm version



        • installed modules



        • This is updated by the user running “pnm updMap”



        • This gives the user a central point that contains all of their instance configurations to allow for easy backup and/or reinstall later.



        • Works similar to package.json in a nodeJS project



        • Users can create a second node named “userOverrides” that allows them to override defaults with values they want.





      • nodejs





        • This folder contains each nodejs instance



        • Naming structure: “v[nodeJS Version]_[NodeJS Instance Name]



        • While PNM allows users to install multiple versions of NodeJS, allows them to also install multiple copies of the same versions.



        • This allows for users to do a lot of creative things but also allow them to use names or versions when swapping



        • “pnm use acmeInstance”



        • Swaps to an instance with the name of “acmeInstance”



        • “pnm use v10.0.1 acmeInstance”



        • Swaps to an instance with the name of “achmeInstance” AND nodeJS version 10.0.1





      • cache





        • This folder contains a cached version of https://nodejs.org/download/release/index.json



        • When “pnm list avl” is executed, PNM inserts the creation\time of the index.json file into the “If-Modified-Since” header of the web request.



        • If nodejs.org replies back with a 304, that means the file has not been updated and PNM uses values within the saved JSON.



        • If it has been updated, the reply back will contain the JSON.



        • This dramatically reduces bandwidth utilization and makes for a much smoother user experience.



        • If specified in the config.json file, this will also contain a folder with each nodeJS version ZIP file that is downloaded to.





      • pnm.ps1





        • Primary powershell script






    • Creates a user env var named “PNM” and “PNMNJS”

    • PNM points to the .pnm director in the user profile folder.

    • PNMNJS points to the currently selected nodeJS instance.



      • This user env var is updated to the path of the instance specified when “pnm use” is executed.



    • Places the new PNM and PNMNJS vars at the start of the PATH user env

    • Updates the users powershell profile to include the primary PNM powershell script.

I would be more than happy to help roll these changes into your version of nvm, if you are interested.

@h404bi @coreybutler I am not sure why "symlink no longer requires admin rights" keeps being repeated. It requires you to be in "Developer Mode", which requires admin rights to enable.

Users can be granted access to create symlinks with a permission but it still does not allow the creation of symlinks in restricted directory's (such as program files) without admin access.

@h404bi @coreybutler I am not sure why "symlink no longer requires admin rights" keeps being repeated. It requires you to be in "Developer Mode", which requires admin rights to enable.

Users can be granted access to create symlinks with a permission but it still does not allow the creation of symlinks in restricted directory's (such as program files) without admin access.

NTFS junctions does NOT require admin rights/Developer Mode. You can fallback to use junction while detecting it's not in dev mode on Windows 10.

I was aware of NTFS Junctions but I did not see them mentioned or linked in this thread. Though, I skimmed it rather quickly. Its an interesting concept but experimentation resulted in NTFS Junctions not being cross platform compatible and not working well with network shares. Both of which where required use cases for my environment.

The main reason I prefer to add %PNMNJS% to the user path and update that env var is because its REALLY easy to implement (and account for) across environments (My script supports OSX and Linux through PowerShell Core) and since environment variables can be nested into others, I never have to update PATH directly.

Closing this since the original title issue won't be resolved. I'm not preventing comments though.

@ChrisKader You should just publish your script as a separate tool.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ctsstc picture ctsstc  Âˇ  3Comments

Deilan picture Deilan  Âˇ  4Comments

SufyanParkar picture SufyanParkar  Âˇ  4Comments

tomByrer picture tomByrer  Âˇ  4Comments

flpms picture flpms  Âˇ  4Comments