Commit c16d2719 authored by Peter V. Saveliev's avatar Peter V. Saveliev

cli: example auth modules -- Keystone, RADIUS

parent 0c5332bf
......@@ -3,15 +3,25 @@ import json
import argparse
from pyroute2 import Console
from pyroute2 import Server
try:
from pyroute2.cli.auth.auth_keystone import OSAuthManager
except ImportError:
OSAuthManager = None
try:
from pyroute2.cli.auth.auth_radius import RadiusAuthManager
except ImportError:
RadiusAuthManager = None
argp = argparse.ArgumentParser()
for spec in (('-a', '[S] IP address to listen on'),
('-c', '[C] Command line to run'),
('-l', '[C,S] Log spec'),
('-m', 'set mode (C,S)'),
('-m', 'Set mode (C,S)'),
('-p', '[S] Port to listen on'),
('-r', '[C] Load rc file'),
('-s', '[C,S] Load sources from a json file')):
('-s', '[C,S] Load sources from a json file'),
('-x', '[S] Strict auth')):
argp.add_argument(spec[0], help=spec[1])
args = argp.parse_args()
commands = []
......@@ -25,10 +35,19 @@ if args.m in ('S', 'server'):
port = int(args.p)
else:
port = 8080
auth_plugins = {}
if OSAuthManager is not None:
auth_plugins['keystone'] = OSAuthManager
if RadiusAuthManager is not None:
auth_plugins['radius:cleartext'] = RadiusAuthManager
server = Server(address=args.a or 'localhost',
port=port,
log=args.l,
sources=sources)
sources=sources,
auth_strict=args.x,
auth_plugins=auth_plugins)
server.serve_forever()
else:
console = Console(log=args.l, sources=sources)
......
import os
import time
from dateutil.parser import parse as isodate
from keystoneauth1.identity import v3
from keystoneauth1 import session
from keystoneclient.v3 import client as ksclient
from keystoneclient.v3.tokens import TokenManager
class OSAuthManager(object):
def __init__(self, headers):
# create a Keystone password object
auth = v3.Password(auth_url=os.environ.get('OS_AUTH_URL'),
username=os.environ.get('OS_USERNAME'),
password=os.environ.get('OS_PASSWORD'),
user_domain_name=(os
.environ
.get('OS_USER_DOMAIN_NAME')),
project_id=os.environ.get('OS_PROJECT_ID'))
# create a session object
sess = session.Session(auth=auth)
# create a token manager
tmanager = TokenManager(ksclient.Client(session=sess))
# validate the token
keystone_response = tmanager.validate(headers['X-Auth-Token'])
# init attrs
self.expire = isodate(keystone_response['expires_at']).timestamp()
def check(self, obj, tag):
if time.time() > self.expire:
raise PermissionError('keystone token has been expired')
return True
import os
from pyrad.client import Client
from pyrad.dictionary import Dictionary
import pyrad.packet
class RadiusAuthManager(object):
def __init__(self, headers):
user = headers['X-Auth-User']
password = headers['X-Auth-Password']
client = Client(server=os.environ.get('RADIUS_SERVER'),
secret=os.environ.get('RADIUS_SECRET').encode('ascii'),
dict=Dictionary('dictionary'))
req = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
User_Name=user)
req['User-Password'] = req.PwCrypt(password)
reply = client.SendPacket(req)
self.auth = reply.code
def check(self, obj, tag):
return self.auth == pyrad.packet.AccessAccept
......@@ -37,24 +37,45 @@ class Handler(BaseHTTPRequestHandler):
return self.do_error(404, 'url not found')
# * content length
if 'Content-Length' not in self.headers:
return self.do_error(500, 'Content-Length')
return self.do_error(411, 'Content-Length')
# * content type
if 'Content-Type' not in self.headers:
return self.do_error(500, 'Content-Type')
return self.do_error(400, 'Content-Type')
#
content_length = int(self.headers['Content-Length'])
content_type = self.headers['Content-Type']
data = self.rfile.read(content_length)
if content_type == 'application/json':
try:
request = json.loads(data)
except ValueError:
return self.do_error(500, 'Incorrect JSON input')
return self.do_error(400, 'Incorrect JSON input')
elif content_type == 'text/plain':
request = {'commands': data.decode('utf-8').split(';')}
else:
self.do_error(400, 'Incorrect content type')
# auth plugins
if 'X-Auth-Mech' in self.headers:
auth_plugin = (self
.server
.auth_plugins
.get(self.headers['X-Auth-Mech']))
if auth_plugin is None:
return self.do_error(501, 'Authentication mechanism not found')
try:
am = auth_plugin(self.headers)
except Exception:
return self.do_error(401, 'Authentication failed')
ndb = self.server.ndb.auth_proxy(am)
elif self.server.auth_strict:
return self.do_error(401, 'Authentication required')
else:
request = {'commands': [data]}
ndb = self.server.ndb
session = Session(ndb=self.server.ndb, stdout=ProxyEncoder(self.wfile))
session = Session(ndb=ndb, stdout=ProxyEncoder(self.wfile))
self.send_response(200)
self.end_headers()
for cmd in request['commands']:
......@@ -67,8 +88,12 @@ class Server(HTTPServer):
address='localhost',
port=8080,
sources=None,
log=None):
log=None,
auth_strict=False,
auth_plugins=None):
self.sessions = {}
self.auth_strict = auth_strict
self.auth_plugins = auth_plugins or {}
self.ndb = NDB(sources=sources, log=log)
self.ndb.config = {'show_format': 'json'}
HTTPServer.__init__(self, (address, port), Handler)
......@@ -38,6 +38,7 @@ setup(name='pyroute2',
'pyroute2.bsd.rtmsocket',
'pyroute2.bsd.pf_route',
'pyroute2.cli',
'pyroute2.cli.auth',
'pyroute2.config',
'pyroute2.dhcp',
'pyroute2.ipdb',
......
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