A commercial-grade RADIUS protocol library implemented in Go, with full support for the core RADIUS RFCs plus a command-line tool supporting both client and server modes.
| RFC | Title |
|---|---|
| 2865 | Remote Authentication Dial In User Service (RADIUS) |
| 2866 | RADIUS Accounting |
| 2867 | RADIUS Accounting Modifications for Tunnel Protocol Support |
| 2868 | RADIUS Attributes for Tunnel Protocol Support |
| 2869 | RADIUS Extensions |
| 3162 | RADIUS and IPv6 |
| 5176 | Dynamic Authorization Extensions to RADIUS |
| 6613 | RADIUS over TCP |
| 6614 | RADIUS over TLS |
| 6929 | RADIUS Protocol Extensions |
| 7360 | RADIUS over DTLS |
| 9445 | RADIUS Extensions for DHCP-Configured Services |
Stable v2.0.0. The core packet, crypto, transport (UDP/TCP/TLS/DTLS), protocol, client, server, dictionary parser/generator, and vendor sub-package layers are complete and tested against the RFCs listed above. The public API follows semantic versioning. See CHANGELOG.md for the full change history, including the v2.0.0 breaking fix to Message-Authenticator (RFC 3579 §3.2) interoperability.
go get github.com/wxccs/radius@v2.0.0package main
import (
"context"
"log"
"net"
"time"
"github.com/wxccs/radius/client"
"github.com/wxccs/radius/packet"
"github.com/wxccs/radius/protocol"
"github.com/wxccs/radius/types"
)
func main() {
c, err := client.NewUDPClient(
&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1812},
[]byte("shared-secret"),
client.Config{Timeout: 5 * time.Second},
)
if err != nil {
log.Fatal(err)
}
defer c.Close()
resp, err := c.Authenticate(context.Background(), &protocol.AccessRequest{
Attributes: []packet.Attribute{
packet.NewString(types.AttrUserName, "alice"),
packet.NewString(types.AttrUserPassword, "hunter2"),
},
Method: protocol.AuthPAP,
})
if err != nil {
log.Fatal(err)
}
log.Printf("reply: %s", resp.Code)
}package main
import (
"context"
"log"
"net"
"github.com/wxccs/radius/packet"
"github.com/wxccs/radius/server"
"github.com/wxccs/radius/types"
)
func main() {
handler := server.HandlerFunc(func(_ context.Context, req *server.Request) (*packet.Packet, error) {
// Replace with real credential lookup.
return &packet.Packet{
Code: types.AccessAccept,
Identifier: req.Identifier,
Authenticator: req.Authenticator,
}, nil
})
srv, err := server.NewUDPServer("udp4",
&net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 1812},
handler, server.StaticSecret([]byte("shared-secret")))
if err != nil {
log.Fatal(err)
}
if err := srv.Serve(context.Background()); err != nil {
log.Fatal(err)
}
}The protocol.Client exposes Account, SendCoA, and SendDisconnect
methods mirroring Authenticate; the server-side Handler receives the
raw *server.Request and can branch on req.Code. See the
package docs for the full API.
The transport/ package exposes four transports, all of which use the
existing 2-byte Length field at offset 2..3 of the RADIUS header for
framing (RFC 6613 §2.1):
| Transport | Listener / Dialer | RFC |
|---|---|---|
| UDP | ListenUDP, DialUDP |
2865, 3162 |
| TCP | ListenTCP, DialTCP |
6613 |
| TLS | ListenTLS, DialTLS |
6614 |
| DTLS | ListenDTLS, DialDTLS |
7360 |
// TLS server
ln, err := transport.ListenTLS("tcp4", laddr, tlsConfig)
// TLS client
client, err := transport.DialTLS("tcp4", server, tlsConfig)
// DTLS server (pion options API)
ln, err := transport.ListenDTLS("udp4", laddr,
piondtls.WithCertificates(cert), piondtls.WithFlightInterval(100*time.Millisecond))
// DTLS client
client, err := transport.DialDTLS("udp4", server,
piondtls.WithInsecureSkipVerify(true))The vendors/ package provides typed constructors for common vendor
sub-attributes, each in its own sub-package keyed by SMI Private Enterprise
Code:
| Sub-package | Vendor | Code |
|---|---|---|
vendors/cisco |
Cisco | 9 |
vendors/h3c |
H3C | 25506 |
vendors/huawei |
Huawei | 2011 |
vendors/juniper |
Juniper | 2636 |
vendors/alcatel |
Alcatel | 800 |
vendors/redback |
Redback | 2352 |
vendors/microsoft |
Microsoft | 311 |
vendors/paloalto |
PaloAlto | 25461 |
// Microsoft MS-CHAP2-Success VSA, computed from MS-CHAPv2 inputs.
attr := microsoft.NewMSCHAP2SuccessFromAuth(
authChallenge, peerChallenge, ntResponse, "alice", "password")The vendors/microsoft package also covers the NAP/NPAS VSAs defined by
the Microsoft Open Specifications [MS-RNAP] (Vendor-Type 0x22-0x3F) and
[MS-RNAS] (adds 0x41), plus the SSTP vendor-specific value for the
standard Tunnel-Type attribute:
// MS-RNAS: assign the user's IPv4 address and an Azure P2S policy id.
attr := microsoft.NewUserIPv4Address(net.IPv4(10, 0, 0, 1))
attr := microsoft.NewAzurePolicyID("azure-p2s-policy-42")
// MS-RNAS §2.2.2.1: standard Tunnel-Type (Type 64) = 0x00013701 for SSTP.
attr := microsoft.NewTunnelTypeSSTP()Shared helpers vendors.NewVSA, vendors.DecodeVSA, and
vendors.MatchVSA implement the RFC 2865 §5.26 wire format for vendors
not covered by a dedicated sub-package.
The dictionary/parser/ package parses FreeRADIUS dictionary files
($INCLUDE, ATTRIBUTE, VALUE, VENDOR, BEGIN-VENDOR/END-VENDOR,
ALIAS, BEGIN-TLV, BEGIN-ENUM) and returns a *parser.Dict that
can be registered at runtime via (*Dictionary).RegisterFromDict.
The dictionary/gen/ package and the cmd/dict-gen CLI emit typed Go
source (constants + accessor pairs) from a parsed dictionary, so you can
reference attributes by name at compile time:
go install ./cmd/dict-gen
dict-gen --in dictionary.freeradius --out attrs.go --pkg attrs// In your application:
attrs.AddUserName(p, "alice")
if v, ok := attrs.GetNASPort(p); ok { /* ... */ }The radius-tool binary supports client mode (access, account, coa,
disconnect) and a lightweight test server mode. Build it with:
go build -o radius-tool ./cmd/radius-tool
./radius-tool --helpLicensed under the MIT License. See LICENSE for the full text.
Third-party dependencies are listed in THIRD_PARTY_LICENSES.md.