nJcx's Blog

十年生死两茫茫,写程序,到天亮。相顾无言,惟有泪千行

企业安全建设之搭建有线无线802.1x网络认证与客户端开发


介绍

IEEE 802.1X是由IEEE制定的关于用户接入网络的认证标准,全称是“基于端口的网络接入控制”。它于2001年正式颁布,最初是为有线网络设计,之后为了配合无线网络的接入进行修订改版,并于2004年完成。  
  当主机接到开启了IEEE 802.1x认证的接口上,就有可能被认证,否则就有可能被拒绝访问网络。在接口上开启IEEE 802.1x认证后,在没有通过认证之前,只有IEEE 802.1x认证消息,CDP,以及STP的数据包能够通过。 因为主机接到开启了IEEE 802.1x认证的接口后,需要通过认证才能访问网络,要通过认证,只要输入合法的用户名和密码即可。交换机收到用户输入的账户信息后,要判断该账户是否合法,就必须和用户数据库做个较对,如果是数据库中存在的,则认证通过,否则认证失败,最后拒绝该用户访问网络。交换机提供给IEEE 802.1x认证的数据库可以是交换机本地的,也可以是远程服务器上的,这需要通过AAA来定义,如果AAA指定认证方式为本地用户数据库(Local),则读取本地的账户信息,如果AAA指定的认证方式为远程服务器,则读取远程服务器的账户信息,AAA为IEEE 802.1x提供的远程服务器认证只能是RADIUS服务器,该RADIUS服务器只要运行Access Control Server Version 3.0(ACS 3.0)或更高版本即可。  


  RADIUS 是一种 C/S 结构的协议,它的客户端最初就是 NAS ( Net Access Server )服务器,现在任何运行 RADIUS 客户端软件的计算机都可以成为 RADIUS 的客户端。 RADIUS 协议认证机制灵活,可以采用 PAP 、 CHAP 或者 Unix 登录认证等多种方式。 RADIUS 是一种可扩展的协议,它进行的全部工作都是基于 Attribute-Length-Value 的向量进行的。 RADIUS 也支持厂商扩充厂家专有属性。

RADIUS 的基本工作原理。用户接入 NAS , NAS 向 RADIUS 服务器使用 Access-Require 数据包提交用户信息,包括用户名、密码等相关信息,其中用户密码是经过 MD5 加密的,双方使用共享密钥,这个密钥不经过网络传播; RADIUS 服务器对用户名和密码的合法性进行检验,必要时可以提出一个Challenge ,要求进一步对用户认证,也可以对 NAS 进行类似的认证;如果合法,给 NAS 返回 Access-Accept 数据包,允许用户进行下一步工作,否则返回 Access-Reject 数据包,拒绝用户访问;如果允许访问, NAS 向RADIUS 服务器提出计费请求 Account- Require , RADIUS 服务器响应Account-Accept ,对用户的计费开始,同时用户可以进行自己的相关操作。

RADIUS 还支持代理和漫游功能。简单地说,代理就是一台服务器,可以作为其他 RADIUS 服务器的代理,负责转发 RADIUS 认证和计费数据包。所谓漫游功能,就是代理的一个具体实现,这样可以让用户通过本来和其无关的RADIUS 服务器进行认证,用户到非归属运营商所在地也可以得到服务,也可以实现虚拟运营。

RADIUS 服务器和 NAS 服务器通过 UDP 协议进行通信, RADIUS 服务器的 1812 端口负责认证, 1813 端口负责计费工作。采用 UDP 的基本考虑是因为 NAS 和 RADIUS 服务器大多在同一个局域网中,使用 UDP 更加快捷方便。

RADIUS 协议还规定了重传机制。如果 NAS 向某个 RADIUS 服务器提交请求没有收到返回信息,那么可以要求备份 RADIUS 服务器重传。由于有多个备份 RADIUS 服务器,因此 NAS 进行重传的时候,可以采用轮询的方法。如果备份 RADIUS 服务器的密钥和以前 RADIUS 服务器的密钥不同,则需要重新进行认证。  由于 RADIUS 协议简单明确,可扩充,因此得到了广泛应用,包括普通电话上网、 ADSL 上网、小区宽带上网、 IP 电话、 VPDN (Virtual Private Dialup Networks ,基于拨号用户的虚拟专用拨号网业务)、移动电话预付费等业务。最近 IEEE提出了 802.1x 标准,这是一种基于端口的标准,用于对无线网络的接入认证,在认证时也采用 RADIUS 协议。   

安装

# radius 安装

yum -y install freeradius freeradius-utils  freeradius-ldap
# ldap 安装


yum install -y openldap openldap-clients openldap-servers

# 复制一个默认配置到指定目录下,并授权,这一步一定要做,然后再启动服务,不然生产密码时会报错

cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG

# 授权给ldap用户,此用户yum安装时便会自动创建

chown -R ldap. /var/lib/ldap/DB_CONFIG

# 启动服务,先启动服务,配置后面再进行修改
systemctl start slapd
systemctl enable slapd

# 查看状态,正常启动则ok
systemctl status slapd

安装openldap后,会有三个命令用于修改配置文件,分别为ldapadd, ldapmodify, ldapdelete,顾名思义就是添加,修改和删除。而需要修改或增加配置时,则需要先写一个ldif后缀的配置文件,然后通过命令将写的配置更新到slapd.d目录下的配置文件中去,完整的配置过程如下,跟着我做就可以了:

# 生成管理员密码,记录下这个密码,后面需要用到
slappasswd -s 123456
{SSHA}LSgYPTUW4zjGtIVtuZ8cRUqqFRv1tWpE
# 新增修改密码文件,ldif为后缀,文件名随意,不要在/etc/openldap/slapd.d/目录下创建类似文件
# 生成的文件为需要通过命令去动态修改ldap现有配置,如下,我在家目录下,创建文件
cd ~
vim changepwd.ldif
----------------------------------------------------------------------
dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootPW
olcRootPW: {SSHA}LSgYPTUW4zjGtIVtuZ8cRUqqFRv1tWpE
----------------------------------------------------------------------
# 这里解释一下这个文件的内容:
# 第一行执行配置文件,这里就表示指定为 cn=config/olcDatabase={0}config 文件。你到/etc/openldap/slapd.d/目录下就能找到此文件
# 第二行 changetype 指定类型为修改
# 第三行 add 表示添加 olcRootPW 配置项
# 第四行指定 olcRootPW 配置项的值
# 在执行下面的命令前,你可以先查看原本的olcDatabase={0}config文件,里面是没有olcRootPW这个项的,执行命令后,你再看就会新增了olcRootPW项,而且内容是我们文件中指定的值加密后的字符串
# 执行命令,修改ldap配置,通过-f执行文件
ldapadd -Y EXTERNAL -H ldapi:/// -f changepwd.ldif
# 我们需要向 LDAP 中导入一些基本的 Schema。这些 Schema 文件位于 /etc/openldap/schema/ 目录中,schema控制着条目拥有哪些对象类和属性,可以自行选择需要的进行导入,
# 依次执行下面的命令,导入基础的一些配置,我这里将所有的都导入一下,其中core.ldif是默认已经加载了的,不用导入
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/collective.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/corba.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/duaconf.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/dyngroup.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/java.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/misc.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/openldap.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/pmi.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/ppolicy.ldif
————————————————



# 修改域名,新增changedomain.ldif, 这里我自定义的域名为 njcx.com,管理员用户账号为admin。
# 如果要修改,则修改文件中相应的dc=njcx,dc=com为自己的域名
vim changedomain.ldif
-------------------------------------------------------------------------
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by dn.base="cn=admin,dc=njcx,dc=com" read by * none

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=njcx,dc=com

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=admin,dc=njcx,dc=com

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: {SSHA}LSgYPTUW4zjGtIVtuZ8cRUqqFRv1tWpE

dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to attrs=userPassword,shadowLastChange by dn="cn=admin,dc=njcx,dc=com" write by anonymous auth by self write by * none
olcAccess: {1}to dn.base="" by * read
olcAccess: {2}to * by dn="cn=admin,dc=njcx,dc=com" write by * read
-------------------------------------------------------------------------


# 执行命令,修改配置
ldapmodify -Y EXTERNAL -H ldapi:/// -f changedomain.ldif
# 新增add-memberof.ldif, #开启memberof支持并新增用户支持memberof配置
vim add-memberof.ldif
-------------------------------------------------------------
dn: cn=module{0},cn=config
cn: modulle{0}
objectClass: olcModuleList
objectclass: top
olcModuleload: memberof.la
olcModulePath: /usr/lib64/openldap

dn: olcOverlay={0}memberof,olcDatabase={2}hdb,cn=config
objectClass: olcConfig
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: top
olcOverlay: memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfUniqueNames
olcMemberOfMemberAD: uniqueMember
olcMemberOfMemberOfAD: memberOf
-------------------------------------------------------------

# 新增refint1.ldif文件
vim refint1.ldif
-------------------------------------------------------------
dn: cn=module{0},cn=config
add: olcmoduleload
olcmoduleload: refint
-------------------------------------------------------------

# 新增refint2.ldif文件
vim refint2.ldif
-------------------------------------------------------------
dn: olcOverlay=refint,olcDatabase={2}hdb,cn=config
objectClass: olcConfig
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
objectClass: top
olcOverlay: refint
olcRefintAttribute: memberof uniqueMember  manager owner
-------------------------------------------------------------

# 依次执行下面命令,加载配置,顺序不能错
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f add-memberof.ldif
ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f refint1.ldif
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f refint2.ldif
————————————————

到此,配置修改完了,在上述基础上,我们来创建一个叫做 njcx company 的组织,并在其下创建一个 admin 的组织角色(该组织角色内的用户具有管理整个 LDAP 的权限)和 People 和 Group 两个组织单元:

# 新增配置文件
vim base.ldif
----------------------------------------------------------
dn: dc=njcx,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: njcx Company
dc: njcx

dn: cn=admin,dc=njcx,dc=com
objectClass: organizationalRole
cn: admin

dn: ou=People,dc=njcx,dc=com
objectClass: organizationalUnit
ou: People

dn: ou=Group,dc=njcx,dc=com
objectClass: organizationalRole
cn: Group
----------------------------------------------------------
# 执行命令,添加配置, 这里要注意修改域名为自己配置的域名,然后需要输入上面我们生成的密码
ldapadd -x -D cn=admin,dc=njcx,dc=com -W -f base.ldif
————————————————

通过以上的所有步骤,我们就设置好了一个 LDAP 目录树:其中基准 dc=njcx,dc=com 是该树的根节点,其下有一个管理域 cn=admin,dc=njcx,dc=com 和两个组织单元 ou=People,dc=njcx,dc=com 及 ou=Group,dc=njcx,dc=com。

打通 radius 和 ldap

/etc/raddb/mods-available/ldap文件

ldap {
    server = '111.111.111.111'
    port = 389
    identity = 'ou=People,dc=njcx,dc=com'
    password = 123456
    base_dn = 'dc=njcx,dc=com'
    sasl {
    }
    update {
        control:Password-With-Header    += 'userPassword'
        control:            += 'radiusControlAttribute'
        request:            += 'radiusRequestAttribute'
        reply:              += 'radiusReplyAttribute'
    }
    user {
        base_dn = "${..base_dn}"
        filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
        sasl {
        }
    }
    group {
        base_dn = "${..base_dn}"
        filter = '(objectClass=posixGroup)'
        membership_attribute = 'memberOf'
    }
    profile {
    }
    client {
        base_dn = "${..base_dn}"
        filter = '(objectClass=radiusClient)'
        template {
        }
        attribute {
            ipaddr              = 'radiusClientIdentifier'
            secret              = 'radiusClientSecret'
        }
    }
    accounting {
        reference = "%{tolower:type.%{Acct-Status-Type}}"
        type {
            start {
                update {
                    description := "Online at %S"
                }
            }
            interim-update {
                update {
                    description := "Last seen at %S"
                }
            }
            stop {
                update {
                    description := "Offline at %S"
                }
            }
        }
    }
    post-auth {
        update {
            description := "Authenticated at %S"
        }
    }
    options {
        chase_referrals = yes
        rebind = yes
        res_timeout = 10
        srv_timelimit = 3
        net_timeout = 1
        idle = 60
        probes = 3
        interval = 3
        ldap_debug = 0x0028
    }
    tls {
    }
    pool {
        start = ${thread[pool].start_servers}
        min = ${thread[pool].min_spare_servers}
        max = ${thread[pool].max_servers}
        spare = ${thread[pool].max_spare_servers}
        uses = 0
        retry_delay = 30
        lifetime = 0
        idle_timeout = 60
    }
}
 /etc/raddb/sites-available/site_ldap文件内容如下

cat  /etc/raddb/sites-available/site_ldap
server site_ldap {
    listen {
         ipaddr = 0.0.0.0
         port = 1833
         type = auth
     }
     authorize {
         update {
             control:Auth-Type := ldap
             }
         }
     authenticate {
         Auth-Type ldap {
             ldap
             }
         }
     post-auth {
         Post-Auth-Type Reject {
             }
         }
 }
 

ln -s /etc/raddb/sites-available/site_ldap /etc/raddb/sites-enabled/

修改/etc/raddb/sites-enabled/default的配置信息,如果你的ldap是注释状态,你需要把注释去掉,保存退出。

重启systemctl restart radiusd

思科交换机配置

本例采用的Cisco交换机,要开启aaa,配置radius服务器和认证组,并在端口启用dot1x认证和guest vlan,具体配置如下:

交换机全局配置

enable aaa new-model
aaa authentication dot1x default group radius
aaa authorization network default group radius
dot1x system-auth-control
dot1x guest-vlan supplicant
radius-server host 192.168.2.16 auth-port 1812 acct-port 1813 timeout 3
radius-server retransmit 2
radius-server key 7 cisco
radius-server vsa send authentication        #如果需要802.1x指定端口vlan需要配置这个

端口配置,interface G1/0/1

(config-if)# switchport mode access
(config-if)# switchport access vlan 10       #配置该端口默认为vlan10
(config-if)# dot1x port-control auto
(config-if)# authentication event no-response action authorize vlan 771 #配置guest vlan,当检测到该机器没加入域,则将其划到vlan 771中