lawik

lawik

Nerves Core Team

This is some ongoing work I’m doing for [ REDACTED ] (one of my favorite clients, makes me seem very cool and mysterious). I am reporting the progress and procedure here to gives some findable context for others who want to do this stuff and also some context for the PR.

Wired 802.1x using EAP-TLS with device certificates is a pretty decent bump in network security and control, or so I hear.

I’ve confirmed the Linux setup for wired 802.1x using this helpful note and a freeradius server along with a Unifi managed switch. So we have a Raspbian OS install doing the right song and dance.

Currently we are hacking apart VintageNetWiFi and VintageNetEthernet to reproduce the config and setup.

We got the thing working but hit a fairly unexpected snag. No wired driver for wpa_supplicant.

So Add wired driver to WPA Supplicant by tomielee · Pull Request #234 · nerves-project/nerves_system_rpi4 · GitHub should address the immediate need we have and we’re currently building that system to see if it works out. It passes then smell check at least.

@fhunleth is this handled in each system separately or should this go somewhere in nerves_system_br?

Showing Posts 4 to 1

lawik

lawik OP

Nerves Core Team

BOOM!

Quick note. It works:

{:ok, engine} = NervesKey.PKCS11.load_engine()
{:ok, i2c} = ATECC508A.Transport.I2C.init([])

signer_cert = X509.Certificate.to_der(NervesKey.signer_cert(i2c))
cert = X509.Certificate.to_der(NervesKey.device_cert(i2c))
%{key_id: uri} = NervesKey.PKCS11.private_key(engine, {:i2c, 1})
File.write("/tmp/client_cert.der", cert)
File.write("/tmp/ca_cert.der", signer_cert)

path = Path.join(Application.app_dir(:nerves_key_pkcs11), "priv/nerves_key_pkcs11.so")
VintageNet.configure("eth0", %{
       type: VintageNetEthernet,
       ipv4: %{method: :dhcp},
       vintage_net_ethernet: %{
        wpa_supplicant_conf: """
        pkcs11_engine_path=/usr/lib/engines/libpkcs11.so
        pkcs11_module_path=#{path}
        network={
          key_mgmt=WPA-EAP
          identity="User"
          eap=TLS
          eapol_flags=3
          client_cert="/tmp/client_cert.der"
          private_key="#{uri}"
        }
        """
       }
     })

Skip ca_cert to avoid CA verification unless you are doing more CA-related stuff and it matters to you. If it matters, add your signer cert to your CA list. Should not be hard.

On the freeradius side (what I tested against). I only really had to replace the ca cert in freeradius with the signer cert and then also add the signer cert to my system CA list so that it was in the root of trust or whatever that’s called.

Will post updates require on the system as well as the forked vintage_net stuff that will later need to be integrated. I did this on nerves_system_rpi3 but it should work on any.

lawik

lawik OP

Nerves Core Team

Using those forks of VintageNet I’ve had no real issue doing PEAP-MSCHAPv2 over ethernet.

Not my goal.

I want EAP-TLS using NervesKey.

Last run gave some promising results:

{:ok, engine} = NervesKey.PKCS11.load_engine()
{:ok, i2c} = ATECC508A.Transport.I2C.init([])

signer_cert = X509.Certificate.to_der(NervesKey.signer_cert(i2c))
cert = X509.Certificate.to_der(NervesKey.device_cert(i2c))
%{key_id: uri} = NervesKey.PKCS11.private_key(engine, {:i2c, 1})
File.write("/tmp/client_cert.der", cert)
File.write("/tmp/ca_cert.der", signer_cert)

path = Path.join(Application.app_dir(:nerves_key_pkcs11), "priv/nerves_key_pkcs11.so")
VintageNet.configure("eth0", %{
       type: VintageNetEthernet,
       ipv4: %{method: :dhcp},
       vintage_net_ethernet: %{
        wpa_supplicant_conf: """
        pkcs11_module_path="#{path}"
        network={
          key_mgmt=WPA-EAP
          identity="User"
          eap=TLS
          eapol_flags=3
          ca_cert="/tmp/ca_cert.der"
          client_cert="/tmp/client_cert.der"
          private_key="#{uri}"
        }
        """
       }
     })

The wrinkle is that OpenSSL doesn’t seem to have engine support compiled in (curious how Erlang does that differently) so I need to add something to Buildroot:

BR2_PACKAGE_OPENSSL_ENGINES=y

in nerves_defconfig is my theory.

The log was wildly misleading. The big read error seems irrelevant and is probably due to negotiations breaking down somehow. The real stuff is about failing to load and missing engine support by SSL and TLS higher up.

14:14:44.705 [debug] wpa_supplicant: eth0: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=13

14:14:44.706 [debug] WPASupplicant ignoring {:event, "CTRL-EVENT-EAP-PROPOSED-METHOD", %{"method" => "13", "vendor" => "0"}}

14:14:44.707 [debug] wpa_supplicant: OpenSSL: tls_connection_ca_cert - Failed to load root certificates error:05800088:x509 certificate routines::no certificate or crl found

14:14:44.712 [debug] wpa_supplicant: SSL: Configuration uses engine, but engine support was not compiled in

14:14:44.712 [debug] wpa_supplicant: TLS: Failed to load private key

14:14:44.712 [debug] wpa_supplicant: TLS: Failed to set TLS connection parameters

14:14:44.712 [debug] wpa_supplicant: EAP-TLS: Failed to initialize SSL.

14:14:44.713 [debug] wpa_supplicant: eth0: CTRL-REQ-PASSPHRASE-0:Private key passphrase needed for SSID 

14:14:44.713 [debug] wpa_supplicant: eth0: EAP: Failed to initialize EAP method: vendor 0 method 13 (TLS)

14:14:44.715 [debug] WPASupplicant ignoring {:event, "CTRL-EVENT-EAP-STATUS", %{"parameter" => "PASSPHRASE", "status" => "eap parameter needed"}}

14:14:44.715 [error] GenServer {VintageNet.Interface.Registry, {VintageNetSupplicant.WPASupplicant, "eth0"}} terminating
** (MatchError) no match of right hand side value: ["PASSPHRASE", "0:Private key passphrase needed for SSID "]
    (vintage_net_supplicant 0.1.0) lib/vintage_net_supplicant/wpa_supplicant_decoder.ex:18: VintageNetSupplicant.WPASupplicantDecoder.decode_notification/1
    (vintage_net_supplicant 0.1.0) lib/vintage_net_supplicant/wpa_supplicant.ex:247: VintageNetSupplicant.WPASupplicant.handle_info/2
    (stdlib 5.2.3) gen_server.erl:1095: :gen_server.try_handle_info/3
    (stdlib 5.2.3) gen_server.erl:1183: :gen_server.handle_msg/6
    (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
Last message: {VintageNetSupplicant.WPASupplicantLL, 3, "CTRL-REQ-PASSPHRASE-0:Private key passphrase needed for SSID "}
State: %{driver: "wired", verbose: false, ll: #PID<0.12619.0>, ifname: "eth0", clients: [], access_points: %{}, peers: [], control_dir: "/tmp/vintage_net/wpa_supplicant", ap_mode: false, keep_alive_interval: 60000, wpa_supplicant: "wpa_supplicant", wpa_supplicant_conf_path: "/tmp/vintage_net/wpa_supplicant.conf.eth0", bssid_requester: #PID<0.12620.0>, current_ap: nil, eap_status: %VintageNet.Interface.EAPStatus{status: :started, method: "TLS", timestamp: ~U[2024-08-28 14:14:44.706670Z], remote_certificate_verified?: false}}
lawik

lawik OP

Nerves Core Team

@fhunleth the separate supplicant library we discussed is taking shape here: GitHub - underjord/vintage_net_supplicant: Vintage Net Supplicant · GitHub

We are not 100% certain about the dividing line between Supplicant and WiFi libraries. Currently we brought a lot of WiFi-stuff over to the Supplicant library, because that worked and it all seemed relevant to the supplicant’s work. But much of it is pointless for the Supplicant under Ethernet. I think that’s fine but let us know what you think.

Plenty of tidying up before you get PRs for WiFi, Ethernet and we consider the Supplicant library ready to move over into the nerves-networking org but it is on it’s way.

lawik

lawik OP

Nerves Core Team

It worked!

— All posts loaded —

Where Next? Top

Trending in Discussions Top

cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
achempion
I’ve been using Emacs as my main code editor for more than a two years. It’s a custom build version although I’ve tried doom emacs and sp...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews