First, ensure that your /opt/myscripts/ and all the scripts within are owned by root, but can be read and executed by CFML. Likely you’ll use CFML’s group for this, or a supplemental group. For sake of illustration let’s create a supplemental group.
groupadd -r cfscript
usermod -a -G cfscript cfml
chown -R root.cfscript /opt/myscripts
chmod 750 /opt/myscripts /opt/myscripts/*.sh
Here’s what this does.
- Creates a new group called cfscript.
- Adds the user “cfml” to the group
- Sets ownership of /opt/myscripts/ to user root, group cfscript
- Sets permissions such that only root and cfscript members can enter the directory, and execute scripts.
If you then run id cfml
you’ll see all the groups that cfml is a member of. You should restart lucee. It’s probably also worth running cfexecute id
to verify that the lucee server has the supplemental group cfscript. If it doesn’t, nothing else is going to work - you’d need to look into how the service is started, specifically how it runs at cfml - things initiated from a root systemd need to set the user of the service somehow. If it’s systemd, you should be fine or can set additional groups in the .service file… If it’s su, you’re probably fine. If it’s setuidgid, it only does primary groups, if it’s chpst, it’ll set what you tell it to so you need to tweak command line options.
But I digress let’s assume you’ve run cfexecute id, and you HAVE the cfscript supplemental group, and all is well. If it isn’t then we’ll have to dig down the rabbit hole further.
The reason we set the ownership to root is because we want to ensure only root can modify these files. We want “root” to decide what the delegated access to the cfscript group is - so only root should modify the files, and root should make it possible for cfscript group to execute the files. If they’re owned by… cfml, for instance, cfml could just change the script and do whatever they want.
Since you have a shebang line as line 1 of your scripts, you don’t need to run sh explicitly, that, in combination with the executable bits applied earlier means we can run them as /opt/myscripts/closeSSHPort.sh directly, without calling sh first.
Now we’ll tell sudo that passwordless is fine, but ONLY for these scripts.
If your distro supports it you can create a new file in /etc/sudoers.d/, perhaps called cfscript. Otherwise you’d have to modify the main /etc/sudoers file, but you’ll want to add something like this:
%cfscript ALL=NOPASSWD:/opt/myscripts/closeSSHPort.sh
%cfscript ALL=NOPASSWD:/opt/myscripts/openSSHPort.sh
This tells sudo that members of the cfscript group, can run the /opt/myscripts/closeSSHPort.sh and /opt/myscripts/closeSSHPort.sh scripts, without requiring a password, as any user they want (but ultimately as root, I just don’t bother to specify)
This opens the smallest security “hole” possible to enable what you want to do.
You can now test this from the shell as the cfml user.
First id
to make sure you have the cfscript group. Adding the group to a user only takes effect after restarting the service, or reconnecting to the ssh console, or restarting your X11 session.
Assuming you HAVE the cfscript group
sudo /opt/myscripts/closeSSHPort.sh
It should complete without asking for a password.
During testing it may be useful to echo the commands executed - if /bin/sh is bash, you can just add “set -x” to your script, i.e. on line 2 before your ufw line, and it’ll echo the commands as they’re run to STDERR.
You may also have to explicitly reference ufw - i.e. /sbin/ufw or /usr/sbin/ufw (use which ufw
as root to figure out where it is) because the user’s PATH argument won’t include those directories, and/or just to be specific about what you want to execute.
From CF, then you just execute name=“sudo” arguments=“/opt/myscripts/closeSSHPort.sh”