Extreme

From RadiusExpert

Jump to: navigation, search

Contents


Compatible Switches

RADIUS compatible devices from Extreme Networks

  • Extreme i-Series Extreme Networks i-series Extremeware based switches (Blackdiamond 6800 series, Summit i-series, Summit 300 series, Summit 200 series, Alpine 3800 series).
  • Extreme XOS Extreme Networks XOS based switches (Blackdiamond 10000, 12000 and 8000 series, X450 series, X250 series)
  • Extreme WM Extreme Networks Wireless Management appliances and associated access points (WM100, WM1000, WM200, WM2000)

Extreme CLI Authorisation

The Extremeware and XOS based switches support per-command authorisation in which each command typed at the switch console is authorised against the radius server:

The following AuthEXTREME.pm adds support for this feature to Radiator.

# AuthEXTREME.pm
#
# Object for handling Extreme Per Command Authorization.
#
# This file will be 'require'd only one time when the first Realm
# with an AuthType of EXTREME is found in the config file
#
# In this example accounting packets are ignored.
#
# Author: Martin Burton (mvb@sanger.ac.uk)
# Copyright (C) 2004 Martin Burton
# $Id:$

package Radius::AuthEXTREME;
@ISA = qw(Radius::AuthGeneric);
use Radius::AuthGeneric;
use strict;


#####################################################################
# This hash describes all the standards types of keywords understood by this
# class. If a keyword is not present in ConfigKeywords for this
# class, or any of its superclasses, Configurable will call sub keyword
# to parse the keyword
%Radius::AuthEXTREME::ConfigKeywords =
    (
     'ProfileFile' => 'string',
     );

# Just a name for useful printing
my $class = 'AuthEXTREME';

&main::log($main::LOG_DEBUG, "$class loaded");

#####################################################################
# Constructs a new handler
# This will be called one for each <Realm ...> or <Handler ...> that
# specifies <AuthEXTREME ...>
# This instance will be destroyed when the server is reinitialised
sub new
{
    my ($class, @args) = @_;

    my $self = $class->SUPER::new(@args);

    $self->log($main::LOG_DEBUG, "$class instantiated");

    $self->log($main::LOG_DEBUG,
               "$class using profile file $self->{ProfileFile}" );

    #read the profiles file to populate $self->{Profiles}
    $self->readProfileFile($self->{ProfileFile});

    return $self;
}

#####################################################################
# Do per-instance default initialization
sub initialize
{
    my ($self) = @_;

    $self->SUPER::initialize;
    $self->{ProfileFile} = './profiles';
}

sub readProfileFile
{
    my ($self,$filename) = @_;

    #Blank the profiles.
    $self->{Profiles}={};

    $self->log($main::LOG_DEBUG,"$class reading $filename");

    if(!open(FILE,$filename))
    {
        $self->log($main::LOG_ERR,"Could not open profiles file: $filename");
        return;
    }

    my ($profilename, $line, @lines);

    #Set the end of paragraph marker to } to make reading the config file easier
    local $/ = '}';

    while (<FILE>)
    {
        chomp;

        if ( /^(?#...)*\s*(\w+)\s*\{\s*(.*)/s )
        {
            $profilename = $1;
            #create empty profile
            $self->{Profiles}{$profilename}=[];

            $line = $2;
            chomp($line);
            @lines = split(/\s*,?\s*\n/,$line);
            foreach my $entry (@lines)
            {
                s/\*/\.\*/g for $entry;
                push(@{$self->{Profiles}{$profilename}},$entry);
            }
        }
    }

    foreach my $pp (keys %{$self->{Profiles}})
    {
        $self->log($main::LOG_INFO,"$class PROFILE: $pp");
        foreach my $kk ( @{$self->{Profiles}{$pp}} )
        {
            $self->log($main::LOG_INFO,"$class ENTRY: $kk");
        }
    }


}

#####################################################################
# Handle a request
#
sub handle_request
{
    my ($self, $p, $dummy, $extra_checks) = @_;
    my $user_name = $p->getUserName;
    my $nas = $p->getNasId;
    if ($p->code eq 'Access-Request')
    {
        my $checkflag = 0;
        my $extreme_command = $p->get_attr('Extreme-Shell-Command');
        if(defined($extreme_command))
        {
            my $extreme_profile = $p->{rp}->get_attr('Profile-Name');
            if(defined($extreme_profile))
            {
                if(defined($self->{Profiles}->{$extreme_profile}))
                {
                    foreach my $checkline (@{$self->{Profiles}->{$extreme_profile}})
                    {
                        if ( $extreme_command =~ m/^$checkline$/ )
                        {
                            $checkflag = 1;
                        }
                    }
                    if ($checkflag eq 0)
                    {
                        $self->log($main::LOG_INFO,"$class found no match");
                    }
                }
                else
                {
                    #We haven't a clue about profile.
                    $self->log($main::LOG_INFO,"$class doesn't know about $extreme_profile");
                    $checkflag = 0;
                }
            }
            else
            {
                #user with no profile!
                $self->log($main::LOG_INFO,"$class has no profile for $user_name");
                $checkflag = 0;
            }
        }
        else {
#           $self->log($main::LOG_INFO,"$class got no Shell Command");
#           $checkflag = 1;
## The only time we get here is on first login to the switch.
            return ($main::ACCEPT);
        }

        $p->{rp}->delete_attr('Profile-Name');
        $self->adjustReply($p);

        if ($checkflag eq 1)
        {
                $self->log($main::LOG_INFO,"$user_name: executed $extreme_command on $nas");
            return ($main::ACCEPT);
        }
        else
        {
                $self->log($main::LOG_INFO,"$user_name: rejected $extreme_command on $nas");
            return ($main::REJECT);
        }

    }
    elsif ($p->code eq 'Accounting-Request')
    {
        # Handler will construct a generic reply for us
        $self->log($main::LOG_INFO,"Login: ($nas) $user_name");
        return ($main::ACCEPT);
    }
    else
    {
        # Handler will construct a generic reply for us
        return ($main::ACCEPT);
    }
}

1;

This may be specified within an AuthBy clause of the radius configuration file, at Sanger we do the following:

<Handler Realm=summits.sanger.ac.uk>
        RewriteUsername      s/^([^@]+).*/$1/
        AuthByPolicy ContinueUntilReject
        <AuthBy SYSTEM>
        </AuthBy>
        <AuthBy FILE>
                Filename /radius/Radiator/summit_users
        </AuthBy>
        <AuthBy EXTREME>
                ProfileFile /radius/Radiator/extreme_profiles
        </AuthBy>
</Handler>

Entries within the summit_users file specify that per command authorisation should be enabled and the profile of each user:

abc
        Service-Type = Administrative-User,
        Extreme-CLI-Authorization = Enabled,
        Profile-Name = ADMINUSER

def
        Service-Type = Administrative-User,
        Extreme-CLI-Authorization = Enabled,
        Profile-Name = VLANUSER

ghi
        Service-Type = Administrative-User,
        Extreme-CLI-Authorization = Enabled,
        Profile-Name = SLBUSER


The extreme_profiles file defines profiles that contain the commands that may be used by particular groups of users:

# Switch admin users. Everything allowed!
ADMINUSER {
*
}

# Normal users.
VLANUSER {
show vlan,
show vlan *,
show ports *,
show fdb *,
show ipfdb *,
configure vlan * add ports *,
configure vlan * delete ports *,
configure ports *,
enable ports *,
disable ports *,
show log *,
show switch,
ping *,
save
}

# Restricted to SLB admin commands
SLBUSER
{
show slb *,
enable slb node *,
disable slb node *,
clear slb connections,
save
}


The dictionary contains the following VSAs:

VENDORATTR      1916    Extreme-CLI-Authorization       201 integer
VENDORATTR      1916    Extreme-Shell-Command           202 string
VENDORATTR      1916    Extreme-Netlogin-Vlan           203 string
VENDORATTR      1916    Extreme-Netlogin-Url            204 string
VENDORATTR      1916    Extreme-Netlogin-Url-Desc       205 string
VENDORATTR      1916    Extreme-Netlogin-Only           206 integer
VENDORATTR      1916    Extreme-User-Location           208 string
VENDORATTR      1916    Extreme-Netlogin-VLAN-Tag       209 integer

VALUE         Extreme-CLI-Authorization         Disabled                0
VALUE         Extreme-CLI-Authorization         Enabled                 1

VALUE         Extreme-Netlogin-Only             Disabled                0
VALUE         Extreme-Netlogin-Only             Enabled                 1
Personal tools