Auto sudoers file (#269)

* added sudoers options to command line arguments

* added sudoers options to command line arguments

* template for sudoers file

* Added option for GUI sudo

* added support for GUI sudo

* script for auto adding sudo file

* sudoers auto add works and validates

* small change

* Clean up for CI

* removed code that belongs in another PR

* added path for package bins

* added sudoers bin

* added sudoers-add to setup file

* fixed issue with sudoers bash script

* auto sudoers now works

* added --sudoers-no-modify option

* bin now works with ./run

* removed debug print

* Updated sudoers-add script

* Fixed error passing sudoers config to script

* more dynamic building of sudoers file

* added option to specify sudoers.d file name

* fixed indent issue

* fixed indent issue

* indent issue

* clean up

* formating

* docs

* fix for flags

* Update usage.rst

* removed shell=true

* cleared CI errors

* cleared CI errors

* removed random

* cleared linter issue

* cleared linter issue

* cleared linter issue

* updated sudoers-add script

* safer temp file

* moved bin directory

* moved bin directory

* removed print

* fixed spacing issue

* sudoers commands must only containe upper case latters
This commit is contained in:
William Mantly 2019-12-12 16:15:31 -05:00 committed by Brian May
parent 6ad4473c87
commit 69d3f7dc64
9 changed files with 266 additions and 0 deletions

76
bin/sudoers-add Executable file
View File

@ -0,0 +1,76 @@
#!/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
# 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" > "/etc/sudoers.d/$FILE_NAME"
chmod 0440 "/etc/sudoers.d/$FILE_NAME"
echo "The sudoers file /etc/sudoers.d/$FILE_NAME has been successfully created!"
exit 0
else
echo "Invalid sudoers config!"
echo "$visudo_STDOUT"
exit 1
fi

View File

@ -5,8 +5,18 @@ Installation
pip install sshuttle pip install sshuttle
- Debain package manager::
sudo apt install sshuttle
- Clone:: - Clone::
git clone https://github.com/sshuttle/sshuttle.git git clone https://github.com/sshuttle/sshuttle.git
cd sshuttle cd sshuttle
./setup.py install ./setup.py install
Optionally after installation
-----------------------------
- Add to sudoers file
sshuttle --sudoers

View File

@ -234,6 +234,29 @@ 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
sshuttle will auto generate the proper sudoers.d config and print it to
stdout. The option will not modify the system at all.
.. option:: --sudoers-user
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
Set the file name for the sudoers.d file to be added. Default is
"sshuttle_auto". Only works with --sudoers.
.. option:: --version .. option:: --version
Print program version. Print program version.

View File

@ -60,3 +60,46 @@ the data back and forth through ssh.
Fun, right? A poor man's instant VPN, and you don't even have to have Fun, right? A poor man's instant VPN, and you don't even have to have
admin access on the server. 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
DO NOT run this command with sudo, it will ask for your sudo password when
it is needed.
A costume 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 helpfull is 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
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
william ALL=NOPASSWD: SSHUTTLE304

1
run
View File

@ -1,6 +1,7 @@
#!/usr/bin/env sh #!/usr/bin/env sh
set -e set -e
export PYTHONPATH="$(dirname $0):$PYTHONPATH" export PYTHONPATH="$(dirname $0):$PYTHONPATH"
export PATH="$(dirname $0)/bin:$PATH"
python_best_version() { python_best_version() {
if [ -x "$(command -v python3)" ] && if [ -x "$(command -v python3)" ] &&

View File

@ -52,6 +52,7 @@ setup(
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.5",
"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',

View File

@ -1,5 +1,6 @@
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
@ -7,11 +8,27 @@ import sshuttle.hostwatch as hostwatch
import sshuttle.ssyslog as ssyslog import sshuttle.ssyslog as ssyslog
from sshuttle.options import parser, parse_ipport from sshuttle.options import parser, parse_ipport
from sshuttle.helpers import family_ip_tuple, log, Fatal from sshuttle.helpers import family_ip_tuple, log, Fatal
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 platform.platform().startswith('OpenBSD'):
log('Automatic sudoers does not work on BSD')
exit(1)
if not opt.sudoers_filename:
log('--sudoers-file must be set or omited.')
exit(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
if opt.wrap: if opt.wrap:

View File

@ -321,6 +321,37 @@ 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(
"--sudoers-no-modify",
action="store_true",
help="""
Prints the sudoers config to STDOUT and DOES NOT modify anything.
"""
)
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.
"""
)
parser.add_argument( parser.add_argument(
"--no-sudo-pythonpath", "--no-sudo-pythonpath",
action="store_false", action="store_false",

64
sshuttle/sudoers.py Normal file
View File

@ -0,0 +1,64 @@
import os
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()}
# 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
'''
def build_config(user_name):
content = template % {
'ca': command_alias,
'dist_packages': path_to_dist_packages,
'py': sys.executable,
'path': path_to_sshuttle,
'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]
returncode = process.returncode
if returncode:
log('Failed updating sudoers file.\n')
debug1(streamdata)
exit(returncode)
else:
log('Success, sudoers file update.\n')
exit(0)
def sudoers(user_name=None, no_modify=None, file_name=None):
user_name = user_name or getpass.getuser()
content = build_config(user_name)
if no_modify:
sys.stdout.write(content)
exit(0)
else:
save_config(content, file_name)