Skip to content

Instantly share code, notes, and snippets.

@mschmitt
Last active April 1, 2020 14:51
Show Gist options
  • Save mschmitt/fdac0d557cb42b5092bc63059bdfefb9 to your computer and use it in GitHub Desktop.
Save mschmitt/fdac0d557cb42b5092bc63059bdfefb9 to your computer and use it in GitHub Desktop.
A wrapper for running apt-get and apt under sudo.
#!/usr/bin/env python3
# /usr/local/bin/packagemanagement is symlinked as /usr/local/bin/{apt,apt-get}
# and when called from sudo cleans the environment and refuses to accept
# configuration overrides and to directly install packages.
import sys
import os
import re
import syslog
import magic
syslog.openlog('packagemanagement-wrapper', logoption=syslog.LOG_PID, facility=syslog.LOG_INFO)
# Strip absolute path from my own name for later re-execution from $PATH
sys.argv[0] = os.path.basename(sys.argv[0]);
# If not running under sudo, don't make any checks but execute command as-is
if not os.getenv('SUDO_USER'):
os.environ['PATH']='/usr/sbin:/usr/bin:/sbin:/bin'
syslog.syslog(syslog.LOG_INFO, 'Skipping checks (non-sudo invocation): ' + ' '.join(sys.argv))
syslog.closelog()
os.execvp(sys.argv[0], sys.argv)
# Arguments that, if detected, will lead to an error exit.
args_to_prohibit = [
'^-o', '^--option',
'^-c', '^--config-file'
]
# Files that, if passed on the command line, will lead to an error exit
filetypes_to_prohibit = [
'application/vnd.debian.binary-package'
]
# Force environment variables (removing /usr/local/bin from Path
# so I won't infinitely call myself from myself).
os.environ['PATH']='/usr/sbin:/usr/bin:/sbin:/bin'
os.environ['LESSSECURE']='1' # LESSSECURE makes less more secure
os.environ['PAGER']='less' # Force the pager program for apt changelog
# Explicitly unset APT_CONFIG, although reset_env in sudoers
# simultaneously takes care of it.
os.unsetenv('APT_CONFIG')
# Declaration of argument regex check
def is_prohibited_arg(want_arg):
# See if the passed arg is in the list of prohibited args
for prohibited_arg in args_to_prohibit:
prohibit_re = re.compile(prohibited_arg)
if prohibit_re.search(want_arg):
return 1
return 0
# Declaration of file type check
def is_prohibited_filetype(want_arg):
mimetype = magic.detect_from_filename(want_arg).mime_type
for prohibited_filetype in filetypes_to_prohibit:
if mimetype == prohibited_filetype:
return 1
return 0
# Check each argument:
for arg in sys.argv:
if is_prohibited_arg(arg):
print("Use of this argument is prohibited: " + arg)
syslog.syslog(syslog.LOG_NOTICE,
'Denying arg: ' + arg + ' - in: ' + ' '.join(sys.argv))
exit(1)
if os.path.exists(arg) and is_prohibited_filetype(arg):
print("Use of this argument is prohibited: " + arg)
syslog.syslog(syslog.LOG_NOTICE,
'Denying file: ' + arg + ' - in: ' + ' '.join(sys.argv) +
' - cwd: ' + os.getcwd())
exit(1)
# Still here -> execute command as-is.
syslog.syslog(syslog.LOG_INFO, 'Passing control to: ' + ' '.join(sys.argv))
syslog.closelog()
os.execvp(sys.argv[0], sys.argv)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment