mirror of
https://github.com/sshuttle/sshuttle.git
synced 2024-11-21 23:43:18 +01:00
Remove --sudoers, improve --sudoers-no-modify
Allowing sshuttle to add/overwrite sudoers configuration file at locations of the users' choosing adds complexity to the code compared to asking users to install the sudo configuration themselves. It requires sshuttle to make decisions about how much effort we put into ensuring that the file is written to a proper location. The current method relies on the 'realpath' program which is not installed on MacOS by default. There are serious problems when the sudo configuration is used to allow a user to *only* run sshuttle as root (with or without a password). First, that user could then use the --sudoers option to give other users sudo privileges. Second, the user can run any command as root because sshuttle accepts a --ssh-cmd parameter which allows a user to specify a program that sshuttle should run. There may also be additional issues that we have not identified. By removing the --sudoers option (and the associated sudoers-add script), this reduces the problems above. This code keeps the --sudoers-no-modify feature which prints a configuration to stdout for the user to install. It includes a clear warning about how --ssh-cmd could potentially be abused to run other programs. A warning about some of these issues has been in sshuttle since version 1.1.0. This commit also adds that warning to more locations in the documentation.
This commit is contained in:
parent
9431bb7a2f
commit
5719d424de
@ -1,84 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# William Mantly <wmantly@gmail.com>
|
|
||||||
# MIT License
|
|
||||||
# https://github.com/wmantly/sudoers-add
|
|
||||||
|
|
||||||
NEWLINE=$'\n'
|
|
||||||
CONTENT=""
|
|
||||||
ME="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
|
|
||||||
|
|
||||||
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
|
|
||||||
echo "Usage: $ME [file_path] [sudoers-file-name]"
|
|
||||||
echo "Usage: [content] | $ME sudoers-file-name"
|
|
||||||
echo "This will take a sudoers config validate it and add it to /etc/sudoers.d/{sudoers-file-name}"
|
|
||||||
echo "The config can come from a file, first usage example or piped in second example."
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" == "" ]; then
|
|
||||||
(>&2 echo "This command take at lest one argument. See $ME --help")
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$2" == "" ]; then
|
|
||||||
FILE_NAME=$1
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
FILE_NAME=$2
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
|
||||||
echo "This script must be run as root"
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
while read -r line
|
|
||||||
do
|
|
||||||
CONTENT+="${line}${NEWLINE}"
|
|
||||||
done < "${1:-/dev/stdin}"
|
|
||||||
|
|
||||||
if [ "$CONTENT" == "" ]; then
|
|
||||||
(>&2 echo "No config content specified. See $ME --help")
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$FILE_NAME" == "" ]; then
|
|
||||||
(>&2 echo "No sudoers file name specified. See $ME --help")
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify that the resulting file name begins with /etc/sudoers.d
|
|
||||||
FILE_NAME="$(realpath "/etc/sudoers.d/$FILE_NAME")"
|
|
||||||
if [[ "$FILE_NAME" != "/etc/sudoers.d/"* ]] ; then
|
|
||||||
echo -n "Invalid sudoers filename: Final sudoers file "
|
|
||||||
echo "location ($FILE_NAME) does not begin with /etc/sudoers.d"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make a temp file to hold the sudoers config
|
|
||||||
umask 077
|
|
||||||
TEMP_FILE=$(mktemp)
|
|
||||||
echo "$CONTENT" > "$TEMP_FILE"
|
|
||||||
|
|
||||||
# Make sure the content is valid
|
|
||||||
visudo_STDOUT=$(visudo -c -f "$TEMP_FILE" 2>&1)
|
|
||||||
visudo_code=$?
|
|
||||||
# The temp file is no longer needed
|
|
||||||
rm "$TEMP_FILE"
|
|
||||||
|
|
||||||
if [ $visudo_code -eq 0 ]; then
|
|
||||||
echo "$CONTENT" > "$FILE_NAME"
|
|
||||||
chmod 0440 "$FILE_NAME"
|
|
||||||
echo "The sudoers file $FILE_NAME has been successfully created!"
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
echo "Invalid sudoers config!"
|
|
||||||
echo "$visudo_STDOUT"
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
@ -19,6 +19,5 @@ Installation
|
|||||||
Optionally after installation
|
Optionally after installation
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
- Add to sudoers file::
|
- Install sudoers configuration. For details, see the "Sudoers File" section in :doc:`usage`
|
||||||
|
|
||||||
sshuttle --sudoers
|
|
||||||
|
@ -262,28 +262,23 @@ Options
|
|||||||
makes it a lot easier to debug and test the :option:`--auto-hosts`
|
makes it a lot easier to debug and test the :option:`--auto-hosts`
|
||||||
feature.
|
feature.
|
||||||
|
|
||||||
.. option:: --sudoers
|
|
||||||
|
|
||||||
sshuttle will auto generate the proper sudoers.d config file and add it.
|
|
||||||
Once this is completed, sshuttle will exit and tell the user if
|
|
||||||
it succeed or not. Do not call this options with sudo, it may generate a
|
|
||||||
incorrect config file.
|
|
||||||
|
|
||||||
.. option:: --sudoers-no-modify
|
.. option:: --sudoers-no-modify
|
||||||
|
|
||||||
sshuttle will auto generate the proper sudoers.d config and print it to
|
sshuttle prints a configuration to stdout which allows a user to
|
||||||
stdout. The option will not modify the system at all.
|
run sshuttle without a password. This option is INSECURE because,
|
||||||
|
with some cleverness, it also allows the user to run any command
|
||||||
|
as root without a password. The output also includes a suggested
|
||||||
|
method for you to install the configuration.
|
||||||
|
|
||||||
|
Use --sudoers-user to modify the user that it applies to.
|
||||||
|
|
||||||
.. option:: --sudoers-user
|
.. option:: --sudoers-user
|
||||||
|
|
||||||
Set the user name or group with %group_name for passwordless operation.
|
Set the user name or group with %group_name for passwordless
|
||||||
Default is the current user.set ALL for all users. Only works with
|
operation. Default is the current user. Set to ALL for all users
|
||||||
--sudoers or --sudoers-no-modify option.
|
(NOT RECOMMENDED: See note about security in --sudoers-no-modify
|
||||||
|
documentation above). Only works with the --sudoers-no-modify
|
||||||
.. option:: --sudoers-filename
|
option.
|
||||||
|
|
||||||
Set the file name for the sudoers.d file to be added. Default is
|
|
||||||
"sshuttle_auto". Only works with --sudoers.
|
|
||||||
|
|
||||||
.. option:: -t <mark>, --tmark=<mark>
|
.. option:: -t <mark>, --tmark=<mark>
|
||||||
|
|
||||||
|
@ -71,44 +71,23 @@ admin access on the server.
|
|||||||
|
|
||||||
Sudoers File
|
Sudoers File
|
||||||
------------
|
------------
|
||||||
sshuttle can auto-generate the proper sudoers.d file using the current user
|
|
||||||
for Linux and OSX. Doing this will allow sshuttle to run without asking for
|
|
||||||
the local sudo password and to give users who do not have sudo access
|
|
||||||
ability to run sshuttle::
|
|
||||||
|
|
||||||
sshuttle --sudoers
|
sshuttle can generate a sudoers.d file for Linux and MacOS. This
|
||||||
|
allows one or more users to run sshuttle without entering the
|
||||||
|
local sudo password. **WARNING:** This option is *insecure*
|
||||||
|
because, with some cleverness, it also allows these users to run any
|
||||||
|
command (via the --ssh-cmd option) as root without a password.
|
||||||
|
|
||||||
DO NOT run this command with sudo, it will ask for your sudo password when
|
To print a sudo configuration file and see a suggested way to install it, run::
|
||||||
it is needed.
|
|
||||||
|
|
||||||
A custom user or group can be set with the :
|
|
||||||
option:`sshuttle --sudoers --sudoers-username {user_descriptor}` option. Valid
|
|
||||||
values for this vary based on how your system is configured. Values such as
|
|
||||||
usernames, groups pre-pended with `%` and sudoers user aliases will work. See
|
|
||||||
the sudoers manual for more information on valid user specif actions.
|
|
||||||
The options must be used with `--sudoers`::
|
|
||||||
|
|
||||||
sshuttle --sudoers --sudoers-user mike
|
|
||||||
sshuttle --sudoers --sudoers-user %sudo
|
|
||||||
|
|
||||||
The name of the file to be added to sudoers.d can be configured as well. This
|
|
||||||
is mostly not necessary but can be useful for giving more than one user
|
|
||||||
access to sshuttle. The default is `sshuttle_auto`::
|
|
||||||
|
|
||||||
sshuttle --sudoer --sudoers-filename sshuttle_auto_mike
|
|
||||||
sshuttle --sudoer --sudoers-filename sshuttle_auto_tommy
|
|
||||||
|
|
||||||
You can also see what configuration will be added to your system without
|
|
||||||
modifying anything. This can be helpful if the auto feature does not work, or
|
|
||||||
you want more control. This option also works with `--sudoers-username`.
|
|
||||||
`--sudoers-filename` has no effect with this option::
|
|
||||||
|
|
||||||
sshuttle --sudoers-no-modify
|
sshuttle --sudoers-no-modify
|
||||||
|
|
||||||
This will simply sprint the generated configuration to STDOUT. Example::
|
A custom user or group can be set with the
|
||||||
|
:option:`sshuttle --sudoers-no-modify --sudoers-user {user_descriptor}`
|
||||||
|
option. Valid values for this vary based on how your system is configured.
|
||||||
|
Values such as usernames, groups pre-pended with `%` and sudoers user
|
||||||
|
aliases will work. See the sudoers manual for more information on valid
|
||||||
|
user specif actions. The option must be used with `--sudoers-no-modify`::
|
||||||
|
|
||||||
08:40 PM william$ sshuttle --sudoers-no-modify
|
sshuttle --sudoers-no-modify --sudoers-user mike
|
||||||
|
sshuttle --sudoers-no-modify --sudoers-user %sudo
|
||||||
Cmnd_Alias SSHUTTLE304 = /usr/bin/env PYTHONPATH=/usr/local/lib/python2.7/dist-packages/sshuttle-0.78.5.dev30+gba5e6b5.d20180909-py2.7.egg /usr/bin/python /usr/local/bin/sshuttle --method auto --firewall
|
|
||||||
|
|
||||||
william ALL=NOPASSWD: SSHUTTLE304
|
|
||||||
|
1
setup.py
1
setup.py
@ -55,7 +55,6 @@ setup(
|
|||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Topic :: System :: Networking",
|
"Topic :: System :: Networking",
|
||||||
],
|
],
|
||||||
scripts=['bin/sudoers-add'],
|
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'sshuttle = sshuttle.cmdline:main',
|
'sshuttle = sshuttle.cmdline:main',
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import platform
|
|
||||||
import sshuttle.helpers as helpers
|
import sshuttle.helpers as helpers
|
||||||
import sshuttle.client as client
|
import sshuttle.client as client
|
||||||
import sshuttle.firewall as firewall
|
import sshuttle.firewall as firewall
|
||||||
@ -14,20 +13,9 @@ from sshuttle.sudoers import sudoers
|
|||||||
def main():
|
def main():
|
||||||
opt = parser.parse_args()
|
opt = parser.parse_args()
|
||||||
|
|
||||||
if opt.sudoers or opt.sudoers_no_modify:
|
if opt.sudoers_no_modify:
|
||||||
if platform.platform().startswith('OpenBSD'):
|
# sudoers() calls exit() when it completes
|
||||||
log('Automatic sudoers does not work on BSD')
|
sudoers(user_name=opt.sudoers_user)
|
||||||
return 1
|
|
||||||
|
|
||||||
if not opt.sudoers_filename:
|
|
||||||
log('--sudoers-file must be set or omitted.')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
sudoers(
|
|
||||||
user_name=opt.sudoers_user,
|
|
||||||
no_modify=opt.sudoers_no_modify,
|
|
||||||
file_name=opt.sudoers_filename
|
|
||||||
)
|
|
||||||
|
|
||||||
if opt.daemon:
|
if opt.daemon:
|
||||||
opt.syslog = 1
|
opt.syslog = 1
|
||||||
|
@ -396,18 +396,15 @@ parser.add_argument(
|
|||||||
(internal use only)
|
(internal use only)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"--sudoers",
|
|
||||||
action="store_true",
|
|
||||||
help="""
|
|
||||||
Add sshuttle to the sudoers for this user
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--sudoers-no-modify",
|
"--sudoers-no-modify",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="""
|
help="""
|
||||||
Prints the sudoers config to STDOUT and DOES NOT modify anything.
|
Prints a sudo configuration to STDOUT which allows a user to
|
||||||
|
run sshuttle without a password. This option is INSECURE because,
|
||||||
|
with some cleverness, it also allows the user to run any command
|
||||||
|
as root without a password. The output also includes a suggested
|
||||||
|
method for you to install the configuration.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -415,16 +412,7 @@ parser.add_argument(
|
|||||||
default="",
|
default="",
|
||||||
help="""
|
help="""
|
||||||
Set the user name or group with %%group_name for passwordless operation.
|
Set the user name or group with %%group_name for passwordless operation.
|
||||||
Default is the current user.set ALL for all users. Only works with
|
Default is the current user. Only works with the --sudoers-no-modify option.
|
||||||
--sudoers or --sudoers-no-modify option.
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--sudoers-filename",
|
|
||||||
default="sshuttle_auto",
|
|
||||||
help="""
|
|
||||||
Set the file name for the sudoers.d file to be added. Default is
|
|
||||||
"sshuttle_auto". Only works with --sudoers or --sudoers-no-modify option.
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -2,70 +2,45 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import getpass
|
import getpass
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
from sshuttle.helpers import log, debug1
|
|
||||||
from distutils import spawn
|
|
||||||
|
|
||||||
path_to_sshuttle = sys.argv[0]
|
|
||||||
path_to_dist_packages = os.path.dirname(os.path.abspath(__file__))[:-9]
|
|
||||||
|
|
||||||
# randomize command alias to avoid collisions
|
def build_config(user_name):
|
||||||
command_alias = 'SSHUTTLE%(num)s' % {'num': uuid4().hex[-3:].upper()}
|
|
||||||
|
|
||||||
# Template for the sudoers file
|
|
||||||
template = '''
|
template = '''
|
||||||
|
# WARNING: If you intend to restrict a user to only running the
|
||||||
|
# sshuttle command as root, THIS CONFIGURATION IS INSECURE.
|
||||||
|
# When a user can run sshuttle as root (with or without a password),
|
||||||
|
# they can also run other commands as root because sshuttle itself
|
||||||
|
# can run a command specified by the user with the --ssh-cmd option.
|
||||||
|
|
||||||
|
# INSTRUCTIONS: Add this text to your sudo configuration to run
|
||||||
|
# sshuttle without needing to enter a sudo password. To use this
|
||||||
|
# configuration, run 'visudo /etc/sudoers.d/sshuttle_auto' as root and
|
||||||
|
# paste this text into the editor that it opens. If you want to give
|
||||||
|
# multiple users these privilages, you may wish to use use different
|
||||||
|
# filenames for each one (i.e., /etc/sudoers.d/sshuttle_auto_john).
|
||||||
|
|
||||||
|
# This configuration was initially generated by the
|
||||||
|
# 'sshuttle --sudoers-no-modify' command.
|
||||||
|
|
||||||
Cmnd_Alias %(ca)s = /usr/bin/env PYTHONPATH=%(dist_packages)s %(py)s %(path)s *
|
Cmnd_Alias %(ca)s = /usr/bin/env PYTHONPATH=%(dist_packages)s %(py)s %(path)s *
|
||||||
|
|
||||||
%(user_name)s ALL=NOPASSWD: %(ca)s
|
%(user_name)s ALL=NOPASSWD: %(ca)s
|
||||||
'''
|
'''
|
||||||
|
|
||||||
warning_msg = "# WARNING: When you allow a user to run sshuttle as root,\n" \
|
content = template % {
|
||||||
"# they can then use sshuttle's --ssh-cmd option to run any\n" \
|
# randomize command alias to avoid collisions
|
||||||
"# command as root.\n"
|
'ca': 'SSHUTTLE%(num)s' % {'num': uuid4().hex[-3:].upper()},
|
||||||
|
'dist_packages': os.path.dirname(os.path.abspath(__file__))[:-9],
|
||||||
|
|
||||||
def build_config(user_name):
|
|
||||||
content = warning_msg
|
|
||||||
content += template % {
|
|
||||||
'ca': command_alias,
|
|
||||||
'dist_packages': path_to_dist_packages,
|
|
||||||
'py': sys.executable,
|
'py': sys.executable,
|
||||||
'path': path_to_sshuttle,
|
'path': sys.argv[0],
|
||||||
'user_name': user_name,
|
'user_name': user_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def save_config(content, file_name):
|
def sudoers(user_name=None):
|
||||||
process = Popen([
|
|
||||||
'/usr/bin/sudo',
|
|
||||||
spawn.find_executable('sudoers-add'),
|
|
||||||
file_name,
|
|
||||||
], stdout=PIPE, stdin=PIPE)
|
|
||||||
|
|
||||||
process.stdin.write(content.encode())
|
|
||||||
|
|
||||||
streamdata = process.communicate()[0]
|
|
||||||
sys.stdout.write(streamdata.decode("ASCII"))
|
|
||||||
returncode = process.returncode
|
|
||||||
|
|
||||||
if returncode:
|
|
||||||
log('Failed updating sudoers file.')
|
|
||||||
debug1(streamdata)
|
|
||||||
exit(returncode)
|
|
||||||
else:
|
|
||||||
log('Success, sudoers file update.')
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def sudoers(user_name=None, no_modify=None, file_name=None):
|
|
||||||
user_name = user_name or getpass.getuser()
|
user_name = user_name or getpass.getuser()
|
||||||
content = build_config(user_name)
|
content = build_config(user_name)
|
||||||
|
|
||||||
if no_modify:
|
|
||||||
sys.stdout.write(content)
|
sys.stdout.write(content)
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
|
||||||
sys.stdout.write(warning_msg)
|
|
||||||
save_config(content, file_name)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user