{"id":1209,"date":"2013-12-30T20:23:57","date_gmt":"2013-12-30T20:23:57","guid":{"rendered":"http:\/\/joelinoff.com\/blog\/?p=1209"},"modified":"2013-12-30T20:23:57","modified_gmt":"2013-12-30T20:23:57","slug":"simple-remote-control-command-execution-tool-rctl-py","status":"publish","type":"post","link":"https:\/\/joelinoff.com\/blog\/?p=1209","title":{"rendered":"Simple remote control command execution tool (rctl.py)"},"content":{"rendered":"<p>I have written a public domain remote control tool call <a href=\"http:\/\/projects.joelinoff.com\/rctl\/\">rctl.py<\/a> that runs remote commands on one or more hosts to automate all sorts of different administration tasks. It consists of a single python script that does not require any configuration and does not require client side daemons (sometimes called minions) so no tool based client side installation is needed which makes it much simpler to setup and use compared to tools such as <a href=\"http:\/\/puppetlabs.com\/\">Puppet<\/a>, <a href=\"http:\/\/www.getchef.com\/chef\/\">Chef<\/a> or <a href=\"http:\/\/www.saltstack.com\/\">salt<\/a>. Of course it is not as powerful as those tools but it has met my modest needs for small networks (<100 hosts). I hope that you find it as useful as I have.\n<!--more--><br \/>\nInstallation is simple. You simply download the <a href=\"http:\/\/projects.joelinoff.com\/rctl\/rctl.py\">file<\/a> and run it. The host that you run it on must have python 2.7 or later installed with the paramiko package. If you have an earlier version of python it will still work but you will need to install packages like argparse. The clients must have SSH installed.<\/p>\n<p>The file checksum is 15069. Please verify it for security purposes by running the sum tool to make sure that you have an unmodified version like this.<\/p>\n<pre class=\"lang:sh decode:true \" title=\"download and check\" >$ wget http:\/\/projects.joelinoff.com\/rctl\/rctl.py\r\n$ sum rctl.py\r\n15069 23 rctl.py\r\n$ chmod 0755 rctl.py<\/pre>\n<p>It is relatively secure because it is so simple. It uses SSH for host communications. There is no logging code that captures passwords, there are no special ports required (other than SSH) and you can easily inspect the source code because it is written in python.<\/p>\n<p>To use it you simply specify the login information, the commands and the hosts like this:<\/p>\n<pre class=\"lang:default decode:true \" title=\"example 1\" >$rctl.py -u root -H host1,host2 -c 'echo -n `hostname`; uptime'\r\nPassword:<\/pre>\n<p>As you can see, you will be prompted for the password. You can avoid that by specifying the password on the command line using the -p option or you can specify a reference to a password file on the command line using the -f option.<\/p>\n<p>Specifying the password on the command line is a security risk. It should only be done inside of a secure shell script or batch file so that the password does not show up in the command history. On linux a secure shell script would have 0700 chmod permissions as shown in the use case below.<\/p>\n<pre class=\"lang:sh decode:true \" title=\"example 2\" >$ # Never use the -p switch on the command line, always use\r\n$ # it in a secure batch file (chmod 0700 file).\r\n$ ls -l dostuff.sh\r\n-rwx------  1 dude  staff  0 Dec 30 09:08 dostuff.sh*\r\n\r\n$ cat dostuff.sh\r\n#!\/bin\/bash\r\n# Run remote control commands.\r\nrctl.py -u dude -p 'secret' -H host1,host2 -c 'echo -n `hostname`; uptime'<\/pre>\n<p>Use a secure password file if you want to avoid the password prompt from the command line. That will guarantee that the password will never appear in the command history. This use case shown below.<\/p>\n<pre class=\"lang:sh decode:true \" title=\"example 3\" >$ # Use the -f switch to specify the password from the\r\n$ # command line.\r\n$ ls -l data.key\r\n-rw-------  1 dude  staff  0 Dec 30 09:12 data.key\r\n\r\n$ cat data.key\r\nsecret\r\n\r\n$ rctl.py -u dude -f data.key -H host1,host2 \\\r\n    -c 'echo -n `hostname`; uptime'<\/pre>\n<p>To handle the case where each remote host requires a different password invoke rctl.py for each host separately in a secure command file like this:<\/p>\n<pre class=\"lang:sh decode:true \" title=\"example 4\" >$ # Hosts have different passwords.\r\n$ ls -l doit.sh\r\n-rwx------  1 dude  staff  0 Dec 30 09:15 doit.sh\r\n\r\n$ cat doit.sh\r\n#!\/bin\/bash\r\nhosts=(host1, host2, host3, host4)\r\nfor host in ${hosts[@]} ; do\r\n    rctl.py -u root -f $host.key -H $host -c 'echo -n `hostname`; uptime'<\/pre>\n<p>Multiple commands be specified either by specifying the -c option multiple times or by separating the commands with &#8216;;&#8217; or &#8216;&#038;&#038;&#8217; in a single command. Both options are shown below.<\/p>\n<pre class=\"lang:sh decode:true \" title=\"example 5\" >$ # Multiple commands with one -c arg.\r\n$ rctl.py -u dude -f data.key -H host,host2 -c 'echo -n `hostname`; uptime'\r\n\r\n$ # Multiple commands with multiple -c args.\r\n$ rctl.py -u dude -f data.key -H host,host2 \\\r\n        -c 'echo host: `hostname`' \\\r\n        -c 'echo -n uptime: `uptime`'<\/pre>\n<p>You can use the -v switch print a header for each command run on each host.<\/p>\n<p>This tool is protected by the MIT license shown at the top of the source code. It can be freely used in public and commercial tools but please make sure that you include the copyright notice or a reference to this site. I would also appreciate it if you provide enhancements and bug fixes to me for the public domain but that is not required.<\/p>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have written a public domain remote control tool call rctl.py that runs remote commands on one or more hosts to automate all sorts of different administration tasks. It consists of a single python script that does not require any configuration and does not require client side daemons (sometimes called minions) so no tool based &hellip; <a href=\"https:\/\/joelinoff.com\/blog\/?p=1209\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Simple remote control command execution tool (rctl.py)<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[4,5,7],"tags":[],"_links":{"self":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1209"}],"collection":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1209"}],"version-history":[{"count":29,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1209\/revisions"}],"predecessor-version":[{"id":1238,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1209\/revisions\/1238"}],"wp:attachment":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1209"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1209"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1209"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}