1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Files
CumulusNetworks-ifupdown2/sbin/ifupdown
Sam Tannous 1e6d7bd76c add param in ifupdown2.conf to prevent fupdown2 users from specify interface config file on the CLI
Ticket: CM-7066
Reviewed By: scotte,roopa,olson
Testing Done: Unit testing and regression testing

This patch does two things:

1. It moves the interfaces config file name to the ifupdown2.conf file in /etc/network/ifupdown2.
This should allow administrators to specify a config file location different from the default and allow
subsets of users to use it without giving them access to specifying their own with the -i option in ifup/ifdown.

2. It also adds a new config setting called "disable_cli_interfacesfile" used to prevent users
from specifying their own interfaces file. This defaults to "1" (even if it is not configured).

Note: this new default takes away users ability to specify an interfaces file.

This should close the vulnerability where users could specify their own interfaces file
and add arbitrary user commands.

This leaves the shell=True option in the user commands add-on module since the ifup/ifdown/ifreload/ifquery
commands already require root access to run and the interfaces config file also requires root access to modify.
2015-08-20 22:59:44 -04:00

515 lines
22 KiB
Python
Executable File

#!/usr/bin/python
#
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
# ifupdown --
# tool to configure network interfaces
#
import sys
import os
import argcomplete
import argparse
import ConfigParser
import StringIO
import logging
import logging.handlers
import resource
from ifupdown.ifupdownmain import *
from ifupdown.utils import *
lockfile="/run/network/.lock"
configfile="/etc/network/ifupdown2/ifupdown2.conf"
configmap_g=None
logger = None
interfacesfileiobuf=None
interfacesfilename=None
ENVPATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
def run_up(args):
logger.debug('args = %s' %str(args))
try:
iflist = args.iflist
if len(args.iflist) == 0:
iflist = None
logger.debug('creating ifupdown object ..')
cachearg=(False if (iflist or args.nocache or
args.perfmode or args.noact)
else True)
ifupdown_handle = ifupdownMain(config=configmap_g,
force=args.force,
withdepends=args.withdepends,
perfmode=args.perfmode,
dryrun=args.noact,
cache=cachearg,
addons_enable=not args.noaddons,
statemanager_enable=not args.noaddons,
interfacesfile=interfacesfilename,
interfacesfileiobuf=interfacesfileiobuf,
interfacesfileformat=args.interfacesfileformat)
if args.noaddons:
ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
syntaxcheck=args.syntaxcheck, type=args.type,
skipupperifaces=args.skipupperifaces)
else:
ifupdown_handle.up(['pre-up', 'up', 'post-up'],
args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
syntaxcheck=args.syntaxcheck, type=args.type,
skipupperifaces=args.skipupperifaces)
except:
raise
def run_down(args):
logger.debug('args = %s' %str(args))
try:
iflist = args.iflist
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdownMain(config=configmap_g, force=args.force,
withdepends=args.withdepends,
perfmode=args.perfmode,
dryrun=args.noact,
addons_enable=not args.noaddons,
statemanager_enable=not args.noaddons,
interfacesfile=interfacesfilename,
interfacesfileiobuf=interfacesfileiobuf,
interfacesfileformat=args.interfacesfileformat)
ifupdown_handle.down(['pre-down', 'down', 'post-down'],
args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
usecurrentconfig=args.usecurrentconfig,
type=args.type)
except:
raise
def run_query(args):
logger.debug('args = %s' %str(args))
try:
iflist = args.iflist
if args.checkcurr:
qop='query-checkcurr'
elif args.running:
qop='query-running'
elif args.raw:
qop='query-raw'
elif args.syntaxhelp:
qop = 'query-syntax'
elif args.printdependency:
qop = 'query-dependency'
elif args.printsavedstate:
qop = 'query-savedstate'
else:
qop='query'
cachearg=(False if (iflist or args.nocache or
args.perfmode or args.syntaxhelp or
(qop != 'query-checkcurr' and
qop != 'query-running')) else True)
if not iflist and qop == 'query-running':
iflist = [i for i in os.listdir('/sys/class/net/')
if os.path.isdir('/sys/class/net/%s' %i)]
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdownMain(config=configmap_g,
withdepends=args.withdepends,
perfmode=args.perfmode,
cache=cachearg,
interfacesfile=interfacesfilename,
interfacesfileiobuf=interfacesfileiobuf,
interfacesfileformat=args.interfacesfileformat)
ifupdown_handle.query([qop], args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
format=args.format, type=args.type)
except:
raise
def run_reload(args):
logger.debug('args = %s' %str(args))
try:
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdownMain(config=configmap_g,
interfacesfile=interfacesfilename,
withdepends=args.withdepends,
perfmode=args.perfmode)
ifupdown_handle.reload(['pre-up', 'up', 'post-up'],
['pre-down', 'down', 'post-down'],
auto=args.all, allow=None, ifacenames=None,
excludepats=args.excludepats,
usecurrentconfig=args.usecurrentconfig,
currentlyup=args.currentlyup)
except:
raise
def init(args):
global logger
global interfacesfileiobuf
global interfacesfilename
log_level = logging.WARNING
if args.verbose:
log_level = logging.INFO
if args.debug:
log_level = logging.DEBUG
try:
if hasattr(args, 'syslog') and args.syslog:
root_logger = logging.getLogger()
syslog_handler = logging.handlers.SysLogHandler(address='/dev/log',
facility=logging.handlers.SysLogHandler.LOG_DAEMON)
logging.addLevelName(logging.ERROR, 'error')
logging.addLevelName(logging.WARNING, 'warning')
logging.addLevelName(logging.DEBUG, 'debug')
logging.addLevelName(logging.INFO, 'info')
root_logger.setLevel(log_level)
syslog_handler.setFormatter(logging.Formatter(
'%(name)s: %(levelname)s: %(message)s'))
root_logger.addHandler(syslog_handler)
logger = logging.getLogger('ifupdown')
else:
logging.basicConfig(level=log_level,
format='%(levelname)s: %(message)s')
logging.addLevelName(logging.ERROR, 'error')
logging.addLevelName(logging.WARNING, 'warning')
logging.addLevelName(logging.DEBUG, 'debug')
logging.addLevelName(logging.INFO, 'info')
logger = logging.getLogger('ifupdown')
except:
raise
if hasattr(args, 'interfacesfile') and args.interfacesfile != None:
# Check to see if -i option is allowed by config file
if configmap_g.get('disable_cli_interfacesfile','1') == '1':
logger.error('disable_cli_interfacesfile is set so users '
'not allowed to specify interfaces file on cli.')
exit(1)
if args.interfacesfile == '-':
# If interfaces file is stdin, read
interfacesfileiobuf = sys.stdin.read()
else:
interfacesfilename = args.interfacesfile
else:
# if the ifupdown2 config file does not have it, default to standard
interfacesfilename = configmap_g.get('default_interfaces_configfile',
'/etc/network/interfaces')
def deinit():
{}
def update_argparser(argparser):
""" base parser, common to all commands """
argparser.add_argument('-a', '--all', action='store_true', required=False,
help='process all interfaces marked \"auto\"')
argparser.add_argument('iflist', metavar='IFACE',
nargs='*', help='interface list separated by spaces. ' +
'IFACE list is mutually exclusive with -a option.')
argparser.add_argument('-v', '--verbose', dest='verbose',
action='store_true', help='verbose')
argparser.add_argument('-d', '--debug', dest='debug',
action='store_true',
help='output debug info')
argparser.add_argument('-q', '--quiet', dest='quiet',
action='store_true',
help=argparse.SUPPRESS)
argparser.add_argument('--allow', dest='CLASS',
help='ignore non-\"allow-CLASS\" interfaces')
argparser.add_argument('-w', '--with-depends', dest='withdepends',
action='store_true', help='run with all dependent interfaces.'+
' This option is redundant when \'-a\' is specified. With ' +
'\'-a\' interfaces are always executed in dependency order')
argparser.add_argument('--perfmode', dest='perfmode',
action='store_true', help=argparse.SUPPRESS)
#argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
# default=-1, choices=range(1,12), help=argparse.SUPPRESS)
argparser.add_argument('--nocache', dest='nocache', action='store_true',
help=argparse.SUPPRESS)
argparser.add_argument('-X', '--exclude', dest='excludepats',
action='append',
help='Exclude interfaces from the list of interfaces' +
' to operate on. Can be specified multiple times.')
argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
default=None,
help='Specify interfaces file instead of file defined ' +
'in ifupdown2.conf file')
argparser.add_argument('-t', '--interfaces-format',
dest='interfacesfileformat',
default='native',
choices=['native', 'json'],
help='interfaces file format')
argparser.add_argument('-T', '--type',
dest='type',
default=None,
choices=['iface', 'vlan'],
help='type of interface entry (iface or vlan).' +
'This option can be used in case of ambiguity between ' +
'a vlan interface and an iface interface of the same name')
def update_ifupdown_argparser(argparser):
""" common arg parser for ifup and ifdown """
argparser.add_argument('-f', '--force', dest='force',
action='store_true',
help='force run all operations')
argparser.add_argument('-l', '--syslog', dest='syslog',
action='store_true',
help=argparse.SUPPRESS)
group = argparser.add_mutually_exclusive_group(required=False)
group.add_argument('-n', '--no-act', dest='noact',
action='store_true', help='print out what would happen,' +
'but don\'t do it')
group.add_argument('-p', '--print-dependency',
dest='printdependency', choices=['list', 'dot'],
help='print iface dependency')
group.add_argument('--no-scripts', '--admin-state',
dest='noaddons', action='store_true',
help='dont run any addon modules/scripts. Only bring the ' +
'interface administratively up/down')
def update_ifup_argparser(argparser):
argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
action='store_true',
help='Only run the interfaces file parser')
argparser.add_argument('-k', '--skip-upperifaces', dest='skipupperifaces',
action='store_true',
help='ifup by default tries to add newly created interfaces' +
' into its upper/parent interfaces. Eg. if a bridge port is' +
' created as a result of ifup on the port, ifup automatically' +
' adds the port to the bridge. This option can be used to ' +
'disable this default behaviour')
update_ifupdown_argparser(argparser)
def update_ifdown_argparser(argparser):
update_ifupdown_argparser(argparser)
argparser.add_argument('-u', '--use-current-config',
dest='usecurrentconfig', action='store_true',
help='By default ifdown looks at the saved state for ' +
'interfaces to bring down. This option allows ifdown to ' +
'look at the current interfaces file. Useful when your ' +
'state file is corrupted or you want down to use the latest '
'from the interfaces file')
def update_ifquery_argparser(argparser):
""" arg parser for ifquery options """
# -l is same as '-a', only here for backward compatibility
argparser.add_argument('-l', '--list', action='store_true', dest='all',
help=argparse.SUPPRESS)
group = argparser.add_mutually_exclusive_group(required=False)
group.add_argument('-r', '--running', dest='running',
action='store_true',
help='query running state of an interface')
group.add_argument('-c', '--check', dest='checkcurr',
action='store_true',
help='check interface file contents against ' +
'running state of an interface')
group.add_argument('-x', '--raw', action='store_true', dest='raw',
help='print raw config file entries')
group.add_argument('--print-savedstate', action='store_true',
dest='printsavedstate',
help=argparse.SUPPRESS)
argparser.add_argument('-o', '--format', dest='format', default='native',
choices=['native', 'json'],
help='interface display format')
argparser.add_argument('-p', '--print-dependency',
dest='printdependency', choices=['list', 'dot'],
help='print interface dependency')
argparser.add_argument('-s', '--syntax-help', action='store_true',
dest='syntaxhelp',
help='print supported interface config syntax')
def update_ifreload_argparser(argparser):
""" parser for ifreload """
group = argparser.add_mutually_exclusive_group(required=True)
group.add_argument('-a', '--all', action='store_true',
help='process all interfaces marked \"auto\"')
group.add_argument('-c', '--currently-up', dest='currentlyup',
action='store_true',
help='only reload auto and other interfaces that are ' +
'currently up')
argparser.add_argument('iflist', metavar='IFACE',
nargs='*', help=argparse.SUPPRESS)
argparser.add_argument('-n', '--no-act', dest='noact',
action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('-v', '--verbose', dest='verbose',
action='store_true', help='verbose')
argparser.add_argument('-d', '--debug', dest='debug',
action='store_true',
help='output debug info')
argparser.add_argument('-w', '--with-depends', dest='withdepends',
action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('--perfmode', dest='perfmode',
action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('--nocache', dest='nocache', action='store_true',
help=argparse.SUPPRESS)
argparser.add_argument('-X', '--exclude', dest='excludepats',
action='append',
help=argparse.SUPPRESS)
#argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
# default=-1, choices=range(1,12), help=argparse.SUPPRESS)
#argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
# default='/etc/network/interfaces',
# help='use interfaces file instead of default ' +
# '/etc/network/interfaces')
argparser.add_argument('-u', '--use-current-config',
dest='usecurrentconfig', action='store_true',
help='By default ifreload looks at saved state for ' +
'interfaces to bring down. With this option ifreload will'
' only look at the current interfaces file. Useful when your ' +
'state file is corrupted or you want down to use the latest '
'from the interfaces file')
argparser.add_argument('-l', '--syslog', dest='syslog',
action='store_true',
help=argparse.SUPPRESS)
argparser.add_argument('-f', '--force', dest='force',
action='store_true',
help='force run all operations')
def parse_args(argsv, op):
if op == 'query':
descr = 'query interfaces (all or interface list)'
elif op == 'reload':
descr = 'reload interface configuration.'
else:
descr = 'interface management'
argparser = argparse.ArgumentParser(description=descr)
if op == 'reload':
update_ifreload_argparser(argparser)
else:
update_argparser(argparser)
if op == 'up':
update_ifup_argparser(argparser)
elif op == 'down':
update_ifdown_argparser(argparser)
elif op == 'query':
update_ifquery_argparser(argparser)
elif op == 'reload':
update_ifreload_argparser(argparser)
argcomplete.autocomplete(argparser)
return argparser.parse_args(argsv)
handlers = {'up' : run_up,
'down' : run_down,
'query' : run_query,
'reload' : run_reload }
def validate_args(op, args):
#if op == 'up' and args.syntaxcheck:
# if args.iflist or args.all:
# print 'ignoring interface options ..'
# return True
if op == 'query' and args.syntaxhelp:
return True
if op == 'reload':
if not args.all and not args.currentlyup:
print '\'-a\' or \'-c\' option is required'
return False
elif (not args.iflist and
not args.all and not args.CLASS):
print '\'-a\' option or interface list are required'
return False
if args.iflist and args.all:
print '\'-a\' option and interface list are mutually exclusive'
return False
if op != 'reload' and args.CLASS and (args.all or args.iflist):
print ('\'--allow\' option is mutually exclusive ' +
'with interface list and \'-a\'')
return False
return True
def read_config(args):
global configmap_g
config = open(configfile, 'r').read()
configStr = '[ifupdown2]\n' + config
configFP = StringIO.StringIO(configStr)
parser = ConfigParser.RawConfigParser()
parser.readfp(configFP)
configmap_g = dict(parser.items('ifupdown2'))
# Preprocess config map
configval = configmap_g.get('multiple_vlan_aware_bridge_support', '0')
if configval == '0':
# if multiple bridges not allowed, set the bridge-vlan-aware
# attribute in the 'no_repeats' config, so that the ifupdownmain
# module can catch it appropriately
configmap_g['no_repeats'] = {'bridge-vlan-aware' : 'yes'}
configval = configmap_g.get('link_master_slave', '0')
if configval == '1':
# link_master_slave is only valid when all is set
if hasattr(args, 'all') and not args.all:
configmap_g['link_master_slave'] = '0'
configval = configmap_g.get('delay_admin_state_change', '0')
if configval == '1':
# reset link_master_slave if delay_admin_state_change is on
configmap_g['link_master_slave'] = '0'
def main(argv):
""" main function """
args = None
try:
op = None
if argv[0].endswith('ifup'):
op = 'up'
elif argv[0].endswith('ifdown'):
op = 'down'
elif argv[0].endswith('ifquery'):
op = 'query'
elif argv[0].endswith('ifreload'):
op = 'reload'
else:
print ('Unexpected executable.' +
' Should be \'ifup\' or \'ifdown\' or \'ifquery\'')
exit(1)
# Command line arg parser
args = parse_args(argv[1:], op)
if not validate_args(op, args):
exit(1)
if not sys.argv[0].endswith('ifquery') and not os.geteuid() == 0:
print 'error: must be root to run this command'
exit(1)
if not sys.argv[0].endswith('ifquery') and not utils.lockFile(lockfile):
print 'Another instance of this program is already running.'
exit(0)
read_config(args)
init(args)
handlers.get(op)(args)
except Exception, e:
if not str(e):
exit(1)
if args and args.debug:
raise
else:
if logger:
logger.error(str(e))
else:
print str(e)
#if args and not args.debug:
# print '\nrerun the command with \'-d\' for a detailed errormsg'
exit(1)
finally:
deinit()
if __name__ == "__main__":
# required during boot
os.putenv('PATH', ENVPATH)
resource.setrlimit(resource.RLIMIT_CORE, (0,0))
main(sys.argv)