Skip to content

Commit

Permalink
Remove --sudoers, improve --sudoers-no-modify
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
skuhl authored and brianmay committed Mar 12, 2022
1 parent 9431bb7 commit 5719d42
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 222 deletions.
84 changes: 0 additions & 84 deletions bin/sudoers-add

This file was deleted.

3 changes: 1 addition & 2 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,5 @@ Installation
Optionally after installation
-----------------------------

- Add to sudoers file::
- Install sudoers configuration. For details, see the "Sudoers File" section in :doc:`usage`

sshuttle --sudoers
29 changes: 12 additions & 17 deletions docs/manpage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -262,28 +262,23 @@ Options
makes it a lot easier to debug and test the :option:`--auto-hosts`
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

sshuttle will auto generate the proper sudoers.d config and print it to
stdout. The option will not modify the system at all.
sshuttle prints a 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.

.. option:: --sudoers-user
Use --sudoers-user to modify the user that it applies to.

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
--sudoers or --sudoers-no-modify option.

.. option:: --sudoers-filename
.. option:: --sudoers-user

Set the file name for the sudoers.d file to be added. Default is
"sshuttle_auto". Only works with --sudoers.
Set the user name or group with %group_name for passwordless
operation. Default is the current user. Set to ALL for all users
(NOT RECOMMENDED: See note about security in --sudoers-no-modify
documentation above). Only works with the --sudoers-no-modify
option.

.. option:: -t <mark>, --tmark=<mark>

Expand Down
49 changes: 14 additions & 35 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,44 +71,23 @@ admin access on the server.

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
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::
To print a sudo configuration file and see a suggested way to install it, run::

sshuttle --sudoers-no-modify

This will simply sprint the generated configuration to STDOUT. Example::

08:40 PM william$ sshuttle --sudoers-no-modify

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
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`::

william ALL=NOPASSWD: SSHUTTLE304
sshuttle --sudoers-no-modify --sudoers-user mike
sshuttle --sudoers-no-modify --sudoers-user %sudo
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ def version_scheme(version):
"Programming Language :: Python :: 3.9",
"Topic :: System :: Networking",
],
scripts=['bin/sudoers-add'],
entry_points={
'console_scripts': [
'sshuttle = sshuttle.cmdline:main',
Expand Down
18 changes: 3 additions & 15 deletions sshuttle/cmdline.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import re
import socket
import platform
import sshuttle.helpers as helpers
import sshuttle.client as client
import sshuttle.firewall as firewall
Expand All @@ -14,20 +13,9 @@
def main():
opt = parser.parse_args()

if opt.sudoers or opt.sudoers_no_modify:
if platform.platform().startswith('OpenBSD'):
log('Automatic sudoers does not work on BSD')
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.sudoers_no_modify:
# sudoers() calls exit() when it completes
sudoers(user_name=opt.sudoers_user)

if opt.daemon:
opt.syslog = 1
Expand Down
24 changes: 6 additions & 18 deletions sshuttle/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,35 +396,23 @@ def convert_arg_line_to_args(self, arg_line):
(internal use only)
"""
)
parser.add_argument(
"--sudoers",
action="store_true",
help="""
Add sshuttle to the sudoers for this user
"""
)
parser.add_argument(
"--sudoers-no-modify",
action="store_true",
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(
"--sudoers-user",
default="",
help="""
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
--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.
Default is the current user. Only works with the --sudoers-no-modify option.
"""
)
parser.add_argument(
Expand Down
75 changes: 25 additions & 50 deletions sshuttle/sudoers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,45 @@
import sys
import getpass
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
command_alias = 'SSHUTTLE%(num)s' % {'num': uuid4().hex[-3:].upper()}
def build_config(user_name):
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.
# Template for the sudoers file
template = '''
Cmnd_Alias %(ca)s = /usr/bin/env PYTHONPATH=%(dist_packages)s %(py)s %(path)s *
%(user_name)s ALL=NOPASSWD: %(ca)s
'''

warning_msg = "# WARNING: When you allow a user to run sshuttle as root,\n" \
"# they can then use sshuttle's --ssh-cmd option to run any\n" \
"# command as root.\n"


def build_config(user_name):
content = warning_msg
content += template % {
'ca': command_alias,
'dist_packages': path_to_dist_packages,
content = template % {
# randomize command alias to avoid collisions
'ca': 'SSHUTTLE%(num)s' % {'num': uuid4().hex[-3:].upper()},
'dist_packages': os.path.dirname(os.path.abspath(__file__))[:-9],
'py': sys.executable,
'path': path_to_sshuttle,
'path': sys.argv[0],
'user_name': user_name,
}

return content


def save_config(content, file_name):
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):
def sudoers(user_name=None):
user_name = user_name or getpass.getuser()
content = build_config(user_name)

if no_modify:
sys.stdout.write(content)
exit(0)
else:
sys.stdout.write(warning_msg)
save_config(content, file_name)
sys.stdout.write(content)
exit(0)

0 comments on commit 5719d42

Please sign in to comment.