From 983eb4b40d63427b619f90e7fe05b4717129a927 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 1 Jun 2017 19:52:54 +0300 Subject: Add support for bbot agent authentication --- bbot/agent.cli | 58 +++++++++++++++++++++++++------- bbot/agent.cxx | 86 +++++++++++++++++++++++++++++++++++++++++++----- bbot/bbot-agent@.service | 3 ++ 3 files changed, 127 insertions(+), 20 deletions(-) (limited to 'bbot') diff --git a/bbot/agent.cli b/bbot/agent.cli index 2f5ad65..a5dbe01 100644 --- a/bbot/agent.cli +++ b/bbot/agent.cli @@ -42,6 +42,52 @@ namespace bbot "Run as a simple systemd daemon." } + path --auth-key + { + "", + "Private key for the public key-based agent authentication. If not + specified, then the agent will not be able to request tasks from + controllers that require authentication. + + The file is expected to contain a single PEM-encoded private key + without a password. A suitable key can be generated using the + following command: + + \ + $ openssl genrsa 4096 >key.pem + \ + " + } + + path --openssl = "openssl" + { + "", + "The openssl program to be used for crypto operations. You can also + specify additional options that should be passed to the openssl program + with \cb{--openssl-option}. If the openssl program is not explicitly + specified, then \cb{bbot-agent} will use \cb{openssl} by default." + } + + strings --openssl-option + { + "", + "Additional option to be passed to the openssl program (see + \cb{--openssl} for details). Repeat this option to specify multiple + openssl options." + } + + size_t --cpu = 1 + { + "", + "Number of CPUs (threads) to use, 1 by default." + } + + size_t --ram (1024 * 1024) // 1G + { + "", + "Amount of RAM (in kB) to use, 1G by default." + } + string --toolchain-name = "default" { "", @@ -75,18 +121,6 @@ namespace bbot "Trust repository certificate with a SHA256 ." } - size_t --cpu = 1 - { - "", - "Number of CPUs (threads) to use, 1 by default." - } - - size_t --ram (1024 * 1024) // 1G - { - "", - "Amount of RAM (in kB) to use, 1G by default." - } - dir_path --machines = "/build/machines/" { "", diff --git a/bbot/agent.cxx b/bbot/agent.cxx index 234763f..117840a 100644 --- a/bbot/agent.cxx +++ b/bbot/agent.cxx @@ -20,6 +20,8 @@ #include #include +#include +#include #include // dir_iterator #include @@ -870,11 +872,46 @@ try fail << "unable to set signal handler: " << system_error (errno, generic_category ()); // Sanitize. + optional fingerprint; + + if (ops.auth_key_specified ()) + try + { + // Note that the process always prints to STDERR, so we redirect it to the + // null device. We also check for the key file existence to print more + // meaningful error message if that's not the case. + // + if (!file_exists (ops.auth_key ())) + throw_generic_error (ENOENT); + + openssl os (trace, + ops.auth_key (), path ("-"), fdnull (), + ops.openssl (), "rsa", + ops.openssl_option (), "-pubout", "-outform", "DER"); + + vector k (os.in.read_binary ()); + os.in.close (); + + if (!os.wait ()) + throw_generic_error (EIO); + + fingerprint = sha256 (k.data (), k.size ()).string (); + } + catch (const system_error& e) + { + fail << "unable to obtain authentication public key: " << e; + } + if (ops.systemd_daemon ()) { diag_record dr; - dr << info << "bbot agent " << BBOT_VERSION_ID << + dr << info << "bbot agent " << BBOT_VERSION_ID; + + if (fingerprint) + dr << info << "auth key fp " << *fingerprint; + + dr << info << "toolchain name " << tc_name << info << "toolchain num " << tc_num << info << "toolchain ver " << tc_ver.string () << @@ -907,13 +944,11 @@ try // Prepare task request. // - // @@ TODO: key fingerprint. - // task_request_manifest tq { hname, tc_name, tc_ver, - nullopt, + fingerprint, machine_header_manifests {} }; @@ -949,7 +984,7 @@ try tr = task_response_manifest { "fake-session", // Dummy session. - string (), // Empty challange. + nullopt, // No challenge. url, // Empty result URL. move (t)}; @@ -1001,6 +1036,12 @@ try continue; } + if (tr.challenge && !fingerprint) // Controller misbehaves. + { + error << "unexpected challenge from " << u << ": " << *tr.challenge; + continue; + } + if (!tr.session.empty ()) // Got a task. { url = u; @@ -1071,11 +1112,40 @@ try return 0; } - // Upload the result. + // Prepare answer to the private key challenge. // - // @@ TODO challange + optional> challenge; + + if (tr.challenge) + try + { + assert (ops.auth_key_specified ()); + + openssl os (trace, + fdstream_mode::text, path ("-"), 2, + ops.openssl (), "rsautl", + ops.openssl_option (), "-sign", "-inkey", ops.auth_key ()); + + os.out << *tr.challenge; + os.out.close (); + + challenge = os.in.read_binary (); + os.in.close (); + + if (!os.wait ()) + throw_generic_error (EIO); + } + catch (const system_error& e) + { + // The task response challenge is valid (verified by manifest parser), + // so there is something wrong with setup, and so the failure is fatal. + // + fail << "unable to sign task response challenge: " << e; + } + + // Upload the result. // - result_request_manifest rq {tr.session, nullopt, move (r)}; + result_request_manifest rq {tr.session, move (challenge), move (r)}; { const string& u (*tr.result_url); diff --git a/bbot/bbot-agent@.service b/bbot/bbot-agent@.service index 7d8c250..41486e2 100644 --- a/bbot/bbot-agent@.service +++ b/bbot/bbot-agent@.service @@ -7,6 +7,8 @@ Type=simple Environment=VERBOSE=3 +Environment=AUTH_KEY= + Environment=CPU=1 Environment=RAM=1048576 @@ -26,6 +28,7 @@ Environment="CONTROLLER_TRUST=" ExecStart=/build/bots/%i/bin/bbot-agent --systemd-daemon \ --verbose ${VERBOSE} \ + --auth-key ${AUTH_KEY} \ --cpu ${CPU} \ --ram ${RAM} \ --bootstrap-timeout ${BOOTSTRAP_TIMEOUT} \ -- cgit v1.1