Flows to Manage SSH Keys?

I wanted to create some basic SSH key management via NR, to avoid using something like Ansible, just to deploy public keys. The goal, is generate and deploy a public key to my NAS server, from any one of my NR remote instances on-demand. This is not a high security design, just basic password-less authentication when I need it.

I have a flow that generates the (new) key, deploys it to the NAS server. And flow that disables use of said key, as well as cleans up the authorized_keys file on the server. If anyone has any suggestions for these, chime in please. Like I noted, this is just interval network stuff, to make password-less authentication with SSH easier, not for use in any external or highly secure setup.

At some point before finalizing I will get the static settings into global context or flow context as applicable. Obscure the password setting as well.

Client Side Logic...

[{"id":"6e497121.b61f3","type":"tab","label":"Public Key 0.0.1","disabled":false,"info":""},{"id":"d9a9b6ed.29d288","type":"comment","z":"6e497121.b61f3","name":"Generate Key","info":"","x":570,"y":120,"wires":[]},{"id":"9fc437b9.d0528","type":"comment","z":"6e497121.b61f3","name":"Add Key","info":"","x":1020,"y":100,"wires":[]},{"id":"503c3dbd.d53a3c","type":"comment","z":"6e497121.b61f3","name":"Remove Public Key From Remote Server","info":"","x":180,"y":260,"wires":[]},{"id":"448739b8.a638a8","type":"comment","z":"6e497121.b61f3","name":"Install sshpass","info":"","x":100,"y":60,"wires":[]},{"id":"79ad7745.b9058","type":"inject","z":"6e497121.b61f3","name":"Add","props":[{"p":"device","v":"pi4modelb1.[redacted]","vt":"str"},{"p":"password","v":"_P@ssw0rd","vt":"str"},{"p":"path","v":"/home/pi/.ssh/","vt":"str"},{"p":"file","v":"id_rsa","vt":"str"},{"p":"type","v":"rsa","vt":"str"},{"p":"id","v":"pi","vt":"str"},{"p":"size","v":"4096","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":90,"y":180,"wires":[["1b671844.1afb18"]]},{"id":"e0e64e0b.e6f62","type":"exec","z":"6e497121.b61f3","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Generate","x":720,"y":160,"wires":[["f07626da.971ab"],[],["c2c91f3.6780b6"]]},{"id":"4a1f3d21.1a242c","type":"function","z":"6e497121.b61f3","name":"Generate","func":"const PERIOD = '.',\n      EMPTY = '';\n\nconst ZERO = 0;\n\nconst HOST = global.get('os').hostname().split(PERIOD)[ZERO];\n\nmsg.payload = `ssh-keygen -q -t ${msg.type} -b ${msg.size} -f ${msg.path}/${msg.file} -N '${EMPTY}' -C ${HOST}`;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":560,"y":160,"wires":[["e0e64e0b.e6f62"]]},{"id":"f07626da.971ab","type":"fs-ops-access","z":"6e497121.b61f3","name":"Key","path":"path","pathType":"msg","filename":"file","filenameType":"msg","read":true,"write":false,"throwerror":false,"x":870,"y":140,"wires":[["c43b02c4.e7db5"],[]]},{"id":"6b3afaef.54addc","type":"debug","z":"6e497121.b61f3","name":"Message","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1480,"y":140,"wires":[]},{"id":"e4562135.303be8","type":"exec","z":"6e497121.b61f3","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Remove","x":400,"y":180,"wires":[["4a1f3d21.1a242c"],[],["869c5d31.7e2af8"]]},{"id":"1b671844.1afb18","type":"function","z":"6e497121.b61f3","name":"Remove","func":"msg.payload = `rm -rf ${msg.path}/${msg.file}*`;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":240,"y":180,"wires":[["e4562135.303be8"]]},{"id":"e550fbb8.dac81","type":"link in","z":"6e497121.b61f3","name":"","links":["869c5d31.7e2af8","c2c91f3.6780b6","596393c5.8fcaac","f1c26fc8.ac4e7","2098ada6.658982","af5471a2.48c93","635b33b2.93d1ac","ab6e6860.8cc8a8"],"x":1355,"y":140,"wires":[["6b3afaef.54addc"]]},{"id":"869c5d31.7e2af8","type":"link out","z":"6e497121.b61f3","name":"","links":["e550fbb8.dac81"],"x":515,"y":200,"wires":[]},{"id":"c2c91f3.6780b6","type":"link out","z":"6e497121.b61f3","name":"","links":["e550fbb8.dac81"],"x":835,"y":180,"wires":[]},{"id":"c43b02c4.e7db5","type":"function","z":"6e497121.b61f3","name":"Add","func":"msg.payload = `sshpass -p ${msg.password} ssh-copy-id -o StrictHostKeyChecking=no -i ${msg.path}/${msg.file}.pub ${msg.id}@${msg.device}`;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1010,"y":140,"wires":[["df2493bc.8d5c1"]]},{"id":"df2493bc.8d5c1","type":"exec","z":"6e497121.b61f3","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Add","x":1170,"y":140,"wires":[["f1c26fc8.ac4e7"],[],["596393c5.8fcaac"]]},{"id":"596393c5.8fcaac","type":"link out","z":"6e497121.b61f3","name":"","links":["e550fbb8.dac81"],"x":1275,"y":160,"wires":[]},{"id":"f1c26fc8.ac4e7","type":"link out","z":"6e497121.b61f3","name":"","links":["e550fbb8.dac81"],"x":1275,"y":120,"wires":[]},{"id":"ec725b30.a4c968","type":"comment","z":"6e497121.b61f3","name":"Add Public Key To Remote Server","info":"","x":160,"y":140,"wires":[]},{"id":"b70ec02f.2ce3e","type":"function","z":"6e497121.b61f3","name":"Remove","func":"const PERIOD = '.',\n      EMPTY = '';\n\nconst ZERO = 0;\n\nconst HOST = global.get('os').hostname().split(PERIOD)[ZERO];\n\nmsg.payload = `ssh-keygen -R ${HOST}`;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":260,"y":340,"wires":[["e30be81e.412cb8"]]},{"id":"d0c11be.b1213e8","type":"inject","z":"6e497121.b61f3","name":"Remove","props":[{"p":"device","v":"pi4modelb1.[redacted]","vt":"str"},{"p":"password","v":"_P@ssw0rd","vt":"str"},{"p":"path","v":"/home/pi/.ssh/","vt":"str"},{"p":"file","v":"id_rsa","vt":"str"},{"p":"type","v":"rsa","vt":"str"},{"p":"id","v":"pi","vt":"str"},{"p":"size","v":"4096","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":100,"y":340,"wires":[["b70ec02f.2ce3e"]]},{"id":"e30be81e.412cb8","type":"exec","z":"6e497121.b61f3","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Remove","x":420,"y":340,"wires":[["21450a8a.74c2c6"],[],["2098ada6.658982"]]},{"id":"2098ada6.658982","type":"link out","z":"6e497121.b61f3","name":"","links":["e550fbb8.dac81"],"x":535,"y":360,"wires":[]},{"id":"21450a8a.74c2c6","type":"function","z":"6e497121.b61f3","name":"Delete","func":"msg.payload = `rm -rf  ${msg.path}/known_hosts.old`;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":570,"y":320,"wires":[["5338aec9.ef1f3"]]},{"id":"5338aec9.ef1f3","type":"exec","z":"6e497121.b61f3","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Delete","x":710,"y":320,"wires":[["8790f61a.ef2598"],[],["635b33b2.93d1ac"]]},{"id":"635b33b2.93d1ac","type":"link out","z":"6e497121.b61f3","name":"","links":["e550fbb8.dac81"],"x":815,"y":340,"wires":[]},{"id":"23052a8c.c75d56","type":"comment","z":"6e497121.b61f3","name":"Remove Key","info":"","x":270,"y":300,"wires":[]},{"id":"a88b688d.e7cb98","type":"comment","z":"6e497121.b61f3","name":"Delete Host","info":"","x":590,"y":280,"wires":[]},{"id":"e0510099.1cc22","type":"mqtt out","z":"6e497121.b61f3","name":"Broker (/<Host>/key/remove)","topic":"","qos":"","retain":"","broker":"98414585.30b018","x":1120,"y":300,"wires":[]},{"id":"8790f61a.ef2598","type":"function","z":"6e497121.b61f3","name":"Remove Device","func":"const PERIOD = '.';\n\nconst ZERO = 0;\n\nconst HOST = global.get('os').hostname().split(PERIOD)[ZERO];\n\nmsg.topic = `/${HOST}/key/remove`;\nmsg.payload = `${HOST}`;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":880,"y":300,"wires":[["e0510099.1cc22"]]},{"id":"111bf35d.86bf8d","type":"inject","z":"6e497121.b61f3","name":"Test","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":460,"wires":[["edf83383.fe5f4"]]},{"id":"edf83383.fe5f4","type":"exec","z":"6e497121.b61f3","command":"ssh pi@pi4modelb1.[redacted] ls -l /home/pi/","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"List","x":230,"y":460,"wires":[["ab6e6860.8cc8a8"],[],[]]},{"id":"ab6e6860.8cc8a8","type":"link out","z":"6e497121.b61f3","name":"","links":["e550fbb8.dac81"],"x":335,"y":460,"wires":[]},{"id":"c97956ec.843018","type":"comment","z":"6e497121.b61f3","name":"Remove Device","info":"","x":880,"y":260,"wires":[]},{"id":"85385549.ff6d68","type":"comment","z":"6e497121.b61f3","name":"Test Public Key On Remote Server","info":"","x":160,"y":420,"wires":[]},{"id":"98414585.30b018","type":"mqtt-broker","name":"Broker","broker":"broker.[redacted]","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

Server Side Logic...

[{"id":"4322a105.41164","type":"tab","label":"Public Key Control 0.0.1","disabled":false,"info":""},{"id":"4ed8686f.c02b28","type":"mqtt in","z":"4322a105.41164","name":"Broker (/+/key/remove)","topic":"/+/key/remove","qos":"2","datatype":"auto","broker":"f90bdac5.277a18","x":120,"y":60,"wires":[["ce8a728c.cf43","351a1758.14d4f8"]]},{"id":"176c83de.b9328c","type":"function","z":"4322a105.41164","name":"Remove","func":"msg.payload = `cat ${msg.path}/${msg.file} | grep -v ${msg.payload} > ${msg.path}/${msg.file}`;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":460,"y":60,"wires":[["60036250.01e4fc","6ba3d555.8d0fac"]]},{"id":"ce8a728c.cf43","type":"change","z":"4322a105.41164","name":"Set","rules":[{"t":"set","p":"path","pt":"msg","to":"/home/pi/.ssh","tot":"str"},{"t":"set","p":"file","pt":"msg","to":"authorized_keys","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":60,"wires":[["176c83de.b9328c","1c994bea.6e8514"]]},{"id":"60036250.01e4fc","type":"exec","z":"4322a105.41164","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Remove","x":620,"y":60,"wires":[["4d3ca60e.4d7d28"],[],["f17d2c28.d6965"]]},{"id":"f17d2c28.d6965","type":"link out","z":"4322a105.41164","name":"","links":["c27a7d5c.a3a32"],"x":735,"y":80,"wires":[]},{"id":"4d3ca60e.4d7d28","type":"link out","z":"4322a105.41164","name":"","links":["c27a7d5c.a3a32"],"x":735,"y":40,"wires":[]},{"id":"2175e3ae.6a24fc","type":"debug","z":"4322a105.41164","name":"Complete","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":940,"y":60,"wires":[]},{"id":"c27a7d5c.a3a32","type":"link in","z":"4322a105.41164","name":"","links":["4d3ca60e.4d7d28","f17d2c28.d6965","6ba3d555.8d0fac","1c994bea.6e8514","351a1758.14d4f8"],"x":815,"y":60,"wires":[["2175e3ae.6a24fc"]]},{"id":"351a1758.14d4f8","type":"link out","z":"4322a105.41164","name":"","links":["c27a7d5c.a3a32"],"x":275,"y":100,"wires":[]},{"id":"1c994bea.6e8514","type":"link out","z":"4322a105.41164","name":"","links":["c27a7d5c.a3a32"],"x":415,"y":100,"wires":[]},{"id":"6ba3d555.8d0fac","type":"link out","z":"4322a105.41164","name":"","links":["c27a7d5c.a3a32"],"x":575,"y":120,"wires":[]},{"id":"f90bdac5.277a18","type":"mqtt-broker","z":"","name":"Broker","broker":"broker.[redacted]","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

What else do you need then?

Not much via my limited use case, just though I would ask it anyone saw anything good or bad with the idea I came up with? Maybe a custom node someone was thinking about creating to do this, or such? This might encourage some ideas or interest in the idea. Also, if someone needed a way to do the same thing, this is published for reference. :slight_smile:

Moving to #share-your-projects

Thanks.... I should have done that.

Personally, I would not do this. Your keys need to be kept secure or they are worthless. As a general purpose platform, this is hard under Node-RED and would be very easy to compromise the keys.

Just because you have a hammer, not everything is a nail :smiley:

I agree in concept, but not in practice here. In a local network, home network, this is completely acceptable if the goal is to embed SSH in automation or scripting versus embedding static passwords for example. The goal of this example, is open a window for automation, do work, and close it.

There is no expectation that this is enterprise level, more along the context of how node-red is typically used in local home automation... how many NR users secure NR to any significant degree from the defaults?

Of course, using a ticket based system like kerberos, is a serious alternate, since once the ticket expires the access/authentication expires.

Given the public key is designed to be public, I don't understand what you concern is about keeping keys private. The private key is not published. Or am I misunderstanding? Using the ssh copy method I used, is the recommend method to deploy public keys.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.