我有一台运行 Debian Jessie 的 PCEngines APU。
我正在尝试配置 hostapd 以针对在 2008 R2 上运行 NPS 的远程域控制器执行有线 802.1x 身份验证。
我希望它的 2 个网络端口在经过身份验证后加入桥接接口,该接口将配置我们的子网、运行 DHCP 中继以及我们的客户端可以通信的路由器 IP。
经过一番挖掘,hostapd 中的“bridge”配置选项似乎仅适用于某些 WiFi 驱动程序,而不适用于有线驱动程序。
如果我在启动时将端口添加到网桥,但在每个接口上运行 hostapd,则用户无需身份验证即可传递流量并使用接口。
如果不这样做,客户端可以正确连接和验证,但显然没有网络可供他们通信(我尝试过桥接配置选项,希望它能自动加入桥接,但没有)。
我的 hostapd 配置如下:
interface=eth1
driver=wired
ieee8021x=1
use_pae_group_addr=1
eap_reauth_period=3600
eapol_version=2
# RADIUS authentication server
auth_server_addr=**secret**
auth_server_port=1812
auth_server_shared_secret=**secret**
# RADIUS accounting server
acct_server_addr=**secret**
acct_server_port=1813
acct_server_shared_secret=**secret**
logger_syslog=-1
logger_syslog_level=2
有人知道我所追求的是否有可能,或者我做错了什么?
编辑:
我读到 hostapd 没有为“有线”驱动程序实现完整的身份验证器堆栈,因此不能用于保护开箱即用的 802.1x 端口。
我还读到应该可以使用控制接口来监听事件,然后使用外部程序来控制桥梁。
我这样做了,一开始是有效的,但大约 3 分钟后客户端总是断开连接。系统日志中记录了由于不活动而导致的连接中断。
即使如此,我还是会在下面附上我的 CGo 代码:
package main
/*
#cgo CFLAGS: -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libbridge.h"
#include "wpa_ctrl.h"
*/
import "C"
import "unsafe"
import "fmt"
import "time"
import "strings"
var briface string = "br0"
var auiface string = "eth1"
var hostapd_path string = "/var/run/hostapd/"
var connected bool
var current_mac string
var wpa_ctl *C.struct_wpa_ctrl
func main() {
br_del()
hostapd_connect()
defer func(){
if wpa_ctl != nil {
C.wpa_ctrl_detach(wpa_ctl)
C.wpa_ctrl_close(wpa_ctl)
}
}()
for {
for C.wpa_ctrl_pending(wpa_ctl) > 0 {
log(fmt.Sprintf("Reading message from hostapd..."))
var buf [256]C.char
var llen C.size_t = C.size_t(unsafe.Sizeof(buf) - 1)
if C.wpa_ctrl_recv(wpa_ctl, &buf[0], &llen) == 0 {
null := C.CString("\000")
buf[llen] = *null
C.free(unsafe.Pointer(null))
//fmt.Printf("%s\n", C.GoString(&buf[0]))
msg := C.GoString(&buf[0])
mData := strings.Split(msg, " ")
if len(mData) < 2 {
log(fmt.Sprintf("Event data too short when processing message: %s", msg))
continue
}
switch (mData[0]){
case "<3>AP-STA-CONNECTED":
log(fmt.Sprintf("Got Device Authentication, adding to bridge..."))
br_add()
connected = true
current_mac = mData[1]
case "<3>AP-STA-DISCONNECTED":
log(fmt.Sprintf("Got Device Disconnect, removing from bridge..."))
br_del()
connected = false
case "<3>CTRL-EVENT-EAP-STARTED":
if connected {
if mData[1] != current_mac {
log(fmt.Sprintf("Got active MAC different from current MAC. %s vs %s - Disconnecting current.", mData[1], current_mac))
hostapd_disconnect(current_mac)
}
}
}
} else {
break
}
}
if ! hostapd_ping() {
C.wpa_ctrl_detach(wpa_ctl)
C.wpa_ctrl_close(wpa_ctl)
log(fmt.Sprintf("Lost connection to hostapd, reconnecting..."))
hostapd_connect()
}
time.Sleep(time.Millisecond * 100)
}
}
func hostapd_connect(){
ci := C.CString(hostapd_path + auiface)
defer C.free(unsafe.Pointer(ci))
for {
wpa_ctl = C.wpa_ctrl_open(ci)
if wpa_ctl != nil {
log(fmt.Sprintf("Connected to hostapd OK, attach..."))
if C.wpa_ctrl_attach(wpa_ctl) == 0 {
log(fmt.Sprintf("Attached event listener..."))
break
} else {
fmt.Printf("Failed to attach to event listener.")
C.wpa_ctrl_close(wpa_ctl)
}
} else {
log(fmt.Sprintf("Failed to connect to hostapd control socket, waiting for retry..."))
}
time.Sleep(time.Millisecond * 100)
}
}
func hostapd_disconnect(mac string){
ci := C.CString(hostapd_path + auiface)
defer C.free(unsafe.Pointer(ci))
dc := C.wpa_ctrl_open(ci)
if dc == nil {
log(fmt.Sprintf("Error opening connection to disconnect current station."))
return
}
defer C.wpa_ctrl_close(dc)
var buf [4096]C.char
var len C.size_t = C.size_t(unsafe.Sizeof(buf) - 1)
cping := C.CString("deauthenticate " + mac)
defer C.free(unsafe.Pointer(cping))
ret := C.wpa_ctrl_request(dc, cping, C.strlen(cping), &buf[0], &len, nil)
if (ret == -2) {
log(fmt.Sprintf("Station disconnect failed with timeout..."))
} else if (ret < 0) {
log(fmt.Sprintf("Station disconnect failed..."))
}
log(fmt.Sprintf("Station disconnect requested."))
}
func hostapd_ping() (bool) {
var buf [4096]C.char
var len C.size_t = C.size_t(unsafe.Sizeof(buf) - 1)
cping := C.CString("PING")
defer C.free(unsafe.Pointer(cping))
ret := C.wpa_ctrl_request(wpa_ctl, cping, C.strlen(cping), &buf[0], &len, nil)
if (ret == -2) {
log(fmt.Sprintf("PING failed with timeout..."))
return false
} else if (ret < 0) {
log(fmt.Sprintf("PING failed..."))
return false
}
return true
}
func br_add(){
br := C.CString(briface)
defer C.free(unsafe.Pointer(br))
ifa := C.CString(auiface)
defer C.free(unsafe.Pointer(ifa))
if ( C.br_init() > 0 ) {
log(fmt.Sprintf("Can't setup bridge control in br_add. Failed to add interface to bridge."))
return
}
ret := C.br_add_interface(br, ifa)
if ret > 0 {
log(fmt.Sprintf("Failed to add interface to bridge, error code: %d", ret))
return
}
}
func br_del(){
br := C.CString(briface)
defer C.free(unsafe.Pointer(br))
ifa := C.CString(auiface)
defer C.free(unsafe.Pointer(ifa))
if ( C.br_init() > 0 ) {
log(fmt.Sprintf("Can't setup bridge control in br_del. Failed to remove interface from bridge."))
return
}
ret := C.br_del_interface(br, ifa)
if ret > 0 {
log(fmt.Sprintf("Failed to remove interface from bridge, error code: %d", ret))
return
}
}
func log(data string) {
fmt.Printf("%s\n", data)
}
答案1
我目前也在寻找基于 Linux 的有线网络 NAC 身份验证器解决方案。我刚刚偶然发现了一个Red Hat 指南这似乎至少为基于 hostapd 的实现提供了合理的基础。