sdkman / sdkman-cli

The SDKMAN! Command Line Interface
https://sdkman.io
Apache License 2.0
6.08k stars 629 forks source link

Feature: Use SDKMAN! as root to provision packages for users #818

Open christianhujer opened 3 years ago

christianhujer commented 3 years ago

Feature request It would be great if I could, as root, use SDKMAN! to provision software packages not for myself but the entire system so that other users can use them. I've tried to find information on this in:

I tried to do

export SDKMAN_DIR="/usr/local/sdkman" && curl -s "https://get.sdkman.io" | bash

as root, and that works, but it does not make sdkman easily accessible to other users. I then tried ln -s /usr/local/sdkman/bin/sdkman-init.sh /etc/profile.d/ and login as normal user, but the script would throw a lot of errors:

find: ‘/home/christian.hujer/.sdkman/src’: No such file or directory
find: ‘/home/christian.hujer/.sdkman/ext’: No such file or directory
touch: cannot touch '/home/christian.hujer/.sdkman/var/delay_upgrade': No such file or directory
-bash: /home/christian.hujer/.sdkman/var/candidates: No such file or directory
__sdkman_echo_debug: command not found

So, it would be nice if there is a documented, automated way how to use sdkman to provision for other users in the same system.

christianhujer commented 3 years ago

BTW I managed to get it working by creating a file /etc/profile.d/sdkman.sh with the following contents:

export SDKMAN_DIR=/usr/local/sdkman
source /usr/local/sdkman/bin/sdkman-init.sh

Maybe something like this could go in the documentation.

marc0der commented 3 years ago

Hi @christianhujer. It is highly recommended not to run SDKMAN as the root user. Besides allowing a bash process to make changes on your system as a root user being highly insecure, SDKMAN also maintains state on the file system. This means that an SDKMAN installation cannot and should not be shared across all the system users, resulting in a clash of state updates.

SDKMAN was expressly designed to run in the user folder where it can't make global system changes. If you want this kind of behavior, I suggest using your operating system's preferred package manager which was built expressly for this purpose.

Hope this makes sense and that I haven't scared you off :smile:

christianhujer commented 3 years ago

I get all these things, and I am aware of that. Here's a few thoughts around that:

Now say I run a more traditional Unix system, a true multiuser system. There are n users in the system. I have the following choices:

In case using SDKMAN! as root is "dangerous", there's always the alternative to do this in a centralized way without running it as root:

mkdir /usr/local/sdkman
useradd sdkman
chown sdkman /usr/local/sdkman
export SDKMAN_DIR="/usr/local/sdkman" && curl -s "https://get.sdkman.io" | su - sdkman bash

I know it will not work perfectly well. It would be great if we could make it work perfectly well for that scenario. Because SDKMAN! is just great, and that would make it even more useful.

marc0der commented 3 years ago

The fact that sdkman allows on-the-fly switching of candidate versions per user makes this all very difficult. Perhaps if the archives folder was split out to a centralized cache (and allowed configuration of its location), this could become a workable solution. That way each user would still have their own folder in their respective HOME directory, but the zip archives would be fetched from a centralized cache.

I'm open to this sort of thing, but much work would need to be done to make it happen. If you would like to contribute such functionality, please feel free to raise a pull request.

jprice-da15252 commented 2 years ago

curl -s "<URL>" | bash has long been exceedingly dangerous, and is even more so now due to all of the recent supply chain hacks. To make matters worse, gradle itself is a tool involved in the supply chain, so this install method is especially risky for a tool like SDKMan!

See https://blog.gitguardian.com/codecov-supply-chain-breach/ for a recent example that is relevent to SDKMan!.

Running sudo curl -s "<URL>" | bash is and incredibly bad idea for all of the reasons above, applied to the root used. If you are trying to automate some process, it would be much much better security-wise to create a script that performed this in a safe manner, which includes:

Either:

And: if curl or wget is used to download within the script, that the execution of either is done as a non-root user su --login <nobody> -c <curl command> -o /tmp/<scriptname>

It makes me a bit queasy thinking about all of the developers piping a script from a website straight to bash with no chance of even inspecting what they are running, but when a feature request for doing this as root appeared in my google results, I felt I had to comment.

Stay safe!

christianhujer commented 1 year ago

As an informational update, I'm using SDKMAN! successfully for quite a while running it as root and provisioning packages via /usr/local/sdkman.

Using /etc/profile.d/sdkman.sh with these contents:

export SDKMAN_DIR=/usr/local/sdkman
source /usr/local/sdkman/bin/sdkman-init.sh

(as described earlier) did the trick and works like a charm.

That said, I agree with the risks described, and I'm not promoting what I do - it is risky. The only actions that should happen as root are things like install, cp, mkdir, ln, and so on.

I'm considering creating a dedicated sdkman user and give them write access to /usr/local/sdkman.

dalbani commented 1 year ago

curl -s "<URL>" | bash has long been exceedingly dangerous

I couldn't agree more, yet, I just couldn't find any way to install SDKMAN! from versioned, checksummed source. In other words, where is the script returned by https://get.sdkman.io actually coming from? Is it stored somewhere in a Git repository? I would have expected to be part of the release tarball of sdkman-cli. Or did I miss something? And there's also the question of build reproducibility, where https://get.sdkman.io makes no guarantee on the versions it's going to install.

TL;DR: how can I install SDKMAN! from a release of sdkman-cli?

JiangHongTiao commented 11 months ago

I have also installed sdkman as root, as I would like to share sdk's with my multiple accounts on a computer to save disk space. On a Mac, you can add also permissions for multiple users, so you can avoid running sdkman with root privileges and let you access packages installed by any user:

Install SDK Man

sudo su -
export SDKMAN_DIR="/Library/sdkman"
curl -s "https://get.sdkman.io" | bash

Use Access Control Lists (ACLs) The best way to ensure that specific users always have read and write permissions, even for newly created files and folders, is to use ACLs. Let's assume you have a sdkman installed in directory /Library/sdkman and you want users user1 and user2 to have read and write access. Here's how you can do it:

chmod +a "user1 allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit" /Library/sdkman
chmod +a "user2 allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit" /Library/sdkman

Make sure existing files and folders have the right permissions After setting up the ACLs for the main directory, you may want to apply similar permissions recursively to all existing files and subdirectories:

chmod -R +a "user1 allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit" /Library/sdkman
chmod -R +a "user2 allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit" /Library/sdkman

Set the SetGID Bit This step ensures that new files and subdirectories created inside /Library/sdkman inherit the group ownership of /Library/sdkman.

chmod g+s /Library/sdkman

So, if you have a specific group (e.g., admin) you want to use, you can chown the directory to have that group:

chown -R :admin /Library/sdkman

Exit root console

exit

Modify .zshrc (or your console rc file) Add following lines into user1 and user2 config files:

#THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!!
export SDKMAN_DIR="/Library/sdkman"
[[ -s "/Library/sdkman/bin/sdkman-init.sh" ]] && source "/Library/sdkman/bin/sdkman-init.sh"