Summary
nodeman_client enroll generates the node's data-signing JWK without an alg
member. NodeMan stores that key as the node's public key, and mqtt-bridge
then cannot verify anything the node signs: it reads the algorithm from the
stored JWK, gets jwa.InvalidKeyAlgorithm, and discards every message.
A node enrolled with the stock client therefore publishes signed new_qname
events that never reach NATS, and the only symptom is an error on the Core
side. EDM itself logs no problem, because lestrrat-go/jwx derives EdDSA
locally from the private Ed25519 key.
nodeman/client.py already imports jwk_to_alg and applies it to the
enrollment key and to data_alg (lines 29-30 and 66), but not to the key it
generates at line 272:
data_key = JWK.generate(kty=args.kty, crv=args.crv, kid=name)
Reproduction
nodeman d1ac412, mqtt-bridge abe75e1, edm 64b50bf.
- Enroll a node with the stock client:
nodeman_client --data-jwk-file data.json --tls-cert-file tls.crt \
--tls-key-file tls.key --tls-ca-file tls-ca.crt \
enroll --file enrollment.json
jq -c 'keys' data.json
["crv","d","iss","kid","kty","x"]
There is no alg. The same omission is visible without the server:
python -c 'from jwcrypto.jwk import JWK; import json; \
print(sorted(json.loads(JWK.generate(kty="OKP", crv="Ed25519", kid="n").export(private_key=True)).keys()))'
['crv', 'd', 'kid', 'kty', 'x']
- Point EDM at that
data.json and publish one new_qname event. EDM reports
no problem and signs the message:
{"msg":"starting signing MQTT publisher","jwk_id":"node.edge.test","jwk_alg":"EdDSA"}
mqtt-bridge receives it, fetches the public key from NodeMan, and drops it:
{"level":"INFO","msg":"Key not found in cache, contacting nodeman..."}
{"level":"ERROR","msg":"Failed to verify signature on message. Discarding..."}
{"level":"ERROR","msg":"Bad signature from MQTT, err: 'WithKey() option must be
specified using jwa.SignatureAlgorithm (got jwa.InvalidKeyAlgorithm)'"}
Nothing arrives on the NATS subject. Adding alg to the same key and
re-enrolling makes the identical event flow through end to end.
Expected behaviour
A node enrolled with nodeman_client should produce messages that mqtt-bridge
can verify, without the operator having to patch the client.
Suggested fix
Set the algorithm on the generated data key, reusing the helper already
imported in this module:
data_key = JWK.generate(kty=args.kty, crv=args.crv, kid=name)
+data_key["alg"] = jwk_to_alg(data_key)
x509_key = generate_x509_key(kty=args.kty, crv=args.crv)
Normalising alg server-side when the enrolled public key is stored would also
protect nodes enrolled by other clients. mqtt-bridge deriving the algorithm
from kty/crv when the stored JWK has none would make the pipeline robust
against either side; I have filed that separately.
Summary
nodeman_client enrollgenerates the node's data-signing JWK without analgmember. NodeMan stores that key as the node's public key, and
mqtt-bridgethen cannot verify anything the node signs: it reads the algorithm from the
stored JWK, gets
jwa.InvalidKeyAlgorithm, and discards every message.A node enrolled with the stock client therefore publishes signed
new_qnameevents that never reach NATS, and the only symptom is an error on the Core
side. EDM itself logs no problem, because
lestrrat-go/jwxderivesEdDSAlocally from the private Ed25519 key.
nodeman/client.pyalready importsjwk_to_algand applies it to theenrollment key and to
data_alg(lines 29-30 and 66), but not to the key itgenerates at line 272:
Reproduction
nodeman
d1ac412, mqtt-bridgeabe75e1, edm64b50bf.nodeman_client --data-jwk-file data.json --tls-cert-file tls.crt \ --tls-key-file tls.key --tls-ca-file tls-ca.crt \ enroll --file enrollment.json jq -c 'keys' data.jsonThere is no
alg. The same omission is visible without the server:data.jsonand publish onenew_qnameevent. EDM reportsno problem and signs the message:
mqtt-bridgereceives it, fetches the public key from NodeMan, and drops it:Nothing arrives on the NATS subject. Adding
algto the same key andre-enrolling makes the identical event flow through end to end.
Expected behaviour
A node enrolled with
nodeman_clientshould produce messages thatmqtt-bridgecan verify, without the operator having to patch the client.
Suggested fix
Set the algorithm on the generated data key, reusing the helper already
imported in this module:
Normalising
algserver-side when the enrolled public key is stored would alsoprotect nodes enrolled by other clients.
mqtt-bridgederiving the algorithmfrom
kty/crvwhen the stored JWK has none would make the pipeline robustagainst either side; I have filed that separately.