Commit f4a09b17 authored by Tomas Hlavacek's avatar Tomas Hlavacek

Add support for router ACL.

Add support for ACL that could be associated with a router to restrict
users that are eligible to run commands on the router.

Just add acl=['username1','username2',...] to the configuration
into the parameters of the router.
parent 9268ed2e
...@@ -21,6 +21,6 @@ from ulgbird import * ...@@ -21,6 +21,6 @@ from ulgbird import *
routers = [ routers = [
CiscoRouter(host='testrouter1.core.company.com', user='xyz', password='xyz', asn=1234), CiscoRouter(host='testrouter1.core.company.com', user='xyz', password='xyz', asn=1234),
CiscoRouter(host='testrouter2.core.company.com', user='xyz', password='xyz', asn=2345), CiscoRouter(host='testrouter2.core.company.com', user='xyz', password='xyz', asn=2345, acl=['user1','user2']),
BirdRouterLocal('/var/run/bird.ctl', asn=3456) BirdRouterLocal('/var/run/bird.ctl', asn=3456)
] ]
...@@ -56,6 +56,7 @@ STRING_COMMAND='Command' ...@@ -56,6 +56,7 @@ STRING_COMMAND='Command'
STRING_ERROR_COMMANDRUN='Error encountered while preparing or running command' STRING_ERROR_COMMANDRUN='Error encountered while preparing or running command'
STRING_BAD_PARAMS='Verification of command or parameters failed.' STRING_BAD_PARAMS='Verification of command or parameters failed.'
STRING_SESSION_OVERLIMIT = "<em>Limit of maximum concurrently running sessions and/or queries has been reached. The command can not be executed now. Please try again later.</em>" STRING_SESSION_OVERLIMIT = "<em>Limit of maximum concurrently running sessions and/or queries has been reached. The command can not be executed now. Please try again later.</em>"
STRING_SESSION_ACCESSDENIED = "<em>Access denied. The command can not be executed. Please contact the administrator.</em>"
STRING_ARBITRARY_ERROR = "Error encountered. Operation aborted. See log for further details." STRING_ARBITRARY_ERROR = "Error encountered. Operation aborted. See log for further details."
STRING_IPADDRESS = "IP address" STRING_IPADDRESS = "IP address"
STRING_IPSUBNET = "IP subnet" STRING_IPSUBNET = "IP subnet"
......
...@@ -139,18 +139,20 @@ ...@@ -139,18 +139,20 @@
<label for="routerselect">1. Router selection:</label> <label for="routerselect">1. Router selection:</label>
<select id="routerselect" name="routerid" onchange="updateFormCommands()"> <select id="routerselect" name="routerid" onchange="updateFormCommands()">
<py:for each="ridx,rtr in enumerate(routers)"> <py:for each="ridx,rtr in enumerate(routers)">
<py:choose> <py:if test="rtr.checkACL(user)">
<py:when test="ridx == default_routerid"> <py:choose>
<option value="$ridx" selected="selected"> <py:when test="ridx == default_routerid">
${rtr.getName()} <option value="$ridx" selected="selected">
</option> ${rtr.getName()}
</py:when> </option>
<py:otherwise> </py:when>
<option value="$ridx"> <py:otherwise>
${rtr.getName()} <option value="$ridx">
</option> ${rtr.getName()}
</py:otherwise> </option>
</py:choose> </py:otherwise>
</py:choose>
</py:if>
</py:for> </py:for>
</select> </select>
......
...@@ -377,13 +377,14 @@ class DecoratorHelper: ...@@ -377,13 +377,14 @@ class DecoratorHelper:
class ULGCgi: class ULGCgi:
def __init__(self): def __init__(self,user=None):
self.loader=TemplateLoader( self.loader=TemplateLoader(
os.path.join(os.path.dirname(__file__), defaults.template_dir), os.path.join(os.path.dirname(__file__), defaults.template_dir),
auto_reload=True auto_reload=True
) )
self.decorator_helper = DecoratorHelper() self.decorator_helper = DecoratorHelper()
self.user = user
def print_text_html(self): def print_text_html(self):
...@@ -456,6 +457,10 @@ class ULGCgi: ...@@ -456,6 +457,10 @@ class ULGCgi:
session.setResult(defaults.STRING_SESSION_OVERLIMIT) session.setResult(defaults.STRING_SESSION_OVERLIMIT)
session.setFinished() session.setFinished()
def stopSessionAccessDenied(self,session):
session.setResult(defaults.STRING_SESSION_ACCESSDENIED)
session.setFinished()
def getRefreshInterval(self,datalength=None): def getRefreshInterval(self,datalength=None):
if(datalength): if(datalength):
return (datalength/(1024*100))*defaults.refresh_interval + defaults.refresh_interval return (datalength/(1024*100))*defaults.refresh_interval + defaults.refresh_interval
...@@ -483,7 +488,7 @@ class ULGCgi: ...@@ -483,7 +488,7 @@ class ULGCgi:
</body> </body>
</html>""" % url </html>""" % url
def runCommand(self,session): def runCommand(self,session,user=None):
class FakeSessionFile(object): class FakeSessionFile(object):
def __init__(self,session): def __init__(self,session):
self.session = session self.session = session
...@@ -505,6 +510,12 @@ class ULGCgi: ...@@ -505,6 +510,12 @@ class ULGCgi:
session.setFinished() session.setFinished()
decreaseUsageMethod() decreaseUsageMethod()
# check router ACL
if user:
if not session.getRouter().checkACL(user):
self.stopSessionAccessDenied(session)
return
# try to increase usage counter # try to increase usage counter
if(self.increaseUsage()): if(self.increaseUsage()):
# start new thread if needed # start new thread if needed
...@@ -534,6 +545,7 @@ class ULGCgi: ...@@ -534,6 +545,7 @@ class ULGCgi:
else: else:
# stop and report no-op # stop and report no-op
self.stopSessionOverlimit(session) self.stopSessionOverlimit(session)
return
def renderULGIndex(self,routerid=0,commandid=0,sessionid=None): def renderULGIndex(self,routerid=0,commandid=0,sessionid=None):
...@@ -545,7 +557,8 @@ class ULGCgi: ...@@ -545,7 +557,8 @@ class ULGCgi:
default_routerid=routerid, default_routerid=routerid,
default_commandid=commandid, default_commandid=commandid,
default_sessionid=sessionid, default_sessionid=sessionid,
getFormURL=self.decorator_helper.getRuncommandURL getFormURL=self.decorator_helper.getRuncommandURL,
user=self.user
).render('html', doctype='html', encoding='utf-8') ).render('html', doctype='html', encoding='utf-8')
...@@ -638,6 +651,7 @@ class ULGCgi: ...@@ -638,6 +651,7 @@ class ULGCgi:
getFormURL=self.decorator_helper.getRuncommandURL, getFormURL=self.decorator_helper.getRuncommandURL,
resrange=str(session.getRange()), resrange=str(session.getRange()),
resrangeb=getRangeStepURLs(session,self.decorator_helper), resrangeb=getRangeStepURLs(session,self.decorator_helper),
user=self.user,
).render('html', doctype='html', encoding='utf-8') ).render('html', doctype='html', encoding='utf-8')
def renderULGError(self,sessionid=None,**params): def renderULGError(self,sessionid=None,**params):
...@@ -657,7 +671,8 @@ class ULGCgi: ...@@ -657,7 +671,8 @@ class ULGCgi:
default_sessionid=None, default_sessionid=None,
result=Markup(result_text) if(result_text) else None, result=Markup(result_text) if(result_text) else None,
refresh=0, refresh=0,
getFormURL=self.decorator_helper.getRuncommandURL getFormURL=self.decorator_helper.getRuncommandURL,
user=self.user
).render('html', doctype='html', encoding='utf-8') ).render('html', doctype='html', encoding='utf-8')
def renderULGDebug(self,**params): def renderULGDebug(self,**params):
...@@ -676,7 +691,8 @@ class ULGCgi: ...@@ -676,7 +691,8 @@ class ULGCgi:
default_sessionid=None, default_sessionid=None,
result=Markup(result_text) if(result_text) else None, result=Markup(result_text) if(result_text) else None,
refresh=0, refresh=0,
getFormURL=self.decorator_helper.getRuncommandURL() getFormURL=self.decorator_helper.getRuncommandURL(),
user=self.user
).render('html', doctype='html', encoding='utf-8') ).render('html', doctype='html', encoding='utf-8')
...@@ -752,7 +768,10 @@ class ULGCgi: ...@@ -752,7 +768,10 @@ class ULGCgi:
if __name__=="__main__": if __name__=="__main__":
try: try:
form = cgi.FieldStorage() form = cgi.FieldStorage()
handler = ULGCgi() user=None
if 'REMOTE_USER' in os.environ:
user=os.environ['REMOTE_USER']
handler = ULGCgi(user)
action = form.getvalue('action',None) action = form.getvalue('action',None)
params = dict([(k,form.getvalue(k)) for k in form.keys() if k != 'action']) params = dict([(k,form.getvalue(k)) for k in form.keys() if k != 'action'])
......
...@@ -460,8 +460,8 @@ class BirdRouter(ulgmodel.Router): ...@@ -460,8 +460,8 @@ class BirdRouter(ulgmodel.Router):
class BirdRouterLocal(ulgmodel.LocalRouter,BirdRouter): class BirdRouterLocal(ulgmodel.LocalRouter,BirdRouter):
def __init__(self,sock=defaults.default_bird_sock,commands=None,proto_fltr=None,asn='My ASN',name='localhost'): def __init__(self,sock=defaults.default_bird_sock,commands=None,proto_fltr=None,asn='My ASN',name='localhost',acl=None):
ulgmodel.LocalRouter.__init__(self) ulgmodel.LocalRouter.__init__(self,acl=acl)
BirdRouter.__init__(self) BirdRouter.__init__(self)
self.sock = sock self.sock = sock
...@@ -577,8 +577,8 @@ class BirdRouterRemote(ulgmodel.RemoteRouter,BirdRouter): ...@@ -577,8 +577,8 @@ class BirdRouterRemote(ulgmodel.RemoteRouter,BirdRouter):
PS_KEY_BGP = '-bgppeers' PS_KEY_BGP = '-bgppeers'
PS_KEY_RT = '-routetab' PS_KEY_RT = '-routetab'
def __init__(self,host,user,password='',port=22,commands=None,proto_fltr=None,asn='My ASN',name=None,bin_birdc=None,bin_ssh=None): def __init__(self,host,user,password='',port=22,commands=None,proto_fltr=None,asn='My ASN',name=None,bin_birdc=None,bin_ssh=None,acl=None):
ulgmodel.RemoteRouter.__init__(self) ulgmodel.RemoteRouter.__init__(self,acl=acl)
BirdRouter.__init__(self) BirdRouter.__init__(self)
self.setHost(host) self.setHost(host)
...@@ -615,6 +615,7 @@ class BirdRouterRemote(ulgmodel.RemoteRouter,BirdRouter): ...@@ -615,6 +615,7 @@ class BirdRouterRemote(ulgmodel.RemoteRouter,BirdRouter):
else: else:
self.setCommands(self._getDefaultCommands()) self.setCommands(self._getDefaultCommands())
def getForkNeeded(self): def getForkNeeded(self):
return True return True
......
...@@ -795,7 +795,9 @@ class CiscoRouter(ulgmodel.RemoteRouter): ...@@ -795,7 +795,9 @@ class CiscoRouter(ulgmodel.RemoteRouter):
] ]
def __init__(self, host, user, password, port=22, commands=None, enable_bgp=True, asn='My ASN', name=None): def __init__(self, host, user, password, port=22, commands=None, enable_bgp=True, asn='My ASN', name=None, acl=None):
ulgmodel.RemoteRouter.__init__(self,acl=acl)
self.setHost(host) self.setHost(host)
self.setPort(port) self.setPort(port)
self.setUser(user) self.setUser(user)
......
...@@ -415,9 +415,10 @@ class AnyCommand(TextCommand): ...@@ -415,9 +415,10 @@ class AnyCommand(TextCommand):
return c return c
class Router(object): class Router(object):
def __init__(self): def __init__(self,acl=None):
self.setCommands([]) self.setCommands([])
self.setName('') self.setName('')
self.acl = acl
def setName(self,name): def setName(self,name):
self.name=name self.name=name
...@@ -435,6 +436,14 @@ class Router(object): ...@@ -435,6 +436,14 @@ class Router(object):
for c in self.listCommands(): for c in self.listCommands():
c.rescanHook(self) c.rescanHook(self)
def checkACL(self,user):
log("Checking ACL: user="+str(user)+" acl="+str(self.acl))
if self.acl:
if not user in self.acl:
return False
return True
def returnError(self,error=None): def returnError(self,error=None):
r = defaults.STRING_ERROR_COMMANDRUN r = defaults.STRING_ERROR_COMMANDRUN
r = r + ': '+error if error else r r = r + ': '+error if error else r
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment