aboutsummaryrefslogtreecommitdiff
path: root/bbot
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-06-01 19:52:54 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-06-02 12:40:32 +0300
commit983eb4b40d63427b619f90e7fe05b4717129a927 (patch)
tree78ed66944acd13f69d9af4995894181c920f3493 /bbot
parent6a67e021019a415b69d5f85bb157c566973413c7 (diff)
Add support for bbot agent authentication
Diffstat (limited to 'bbot')
-rw-r--r--bbot/agent.cli58
-rw-r--r--bbot/agent.cxx86
-rw-r--r--bbot/bbot-agent@.service3
3 files changed, 127 insertions, 20 deletions
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
+ {
+ "<file>",
+ "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"
+ {
+ "<path>",
+ "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
+ {
+ "<opt>",
+ "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
+ {
+ "<num>",
+ "Number of CPUs (threads) to use, 1 by default."
+ }
+
+ size_t --ram (1024 * 1024) // 1G
+ {
+ "<num>",
+ "Amount of RAM (in kB) to use, 1G by default."
+ }
+
string --toolchain-name = "default"
{
"<str>",
@@ -75,18 +121,6 @@ namespace bbot
"Trust repository certificate with a SHA256 <fingerprint>."
}
- size_t --cpu = 1
- {
- "<num>",
- "Number of CPUs (threads) to use, 1 by default."
- }
-
- size_t --ram (1024 * 1024) // 1G
- {
- "<num>",
- "Amount of RAM (in kB) to use, 1G by default."
- }
-
dir_path --machines = "/build/machines/"
{
"<dir>",
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 <iostream>
#include <libbutl/pager.hxx>
+#include <libbutl/sha256.hxx>
+#include <libbutl/openssl.hxx>
#include <libbutl/filesystem.hxx> // dir_iterator
#include <libbbot/manifest.hxx>
@@ -870,11 +872,46 @@ try
fail << "unable to set signal handler: "
<< system_error (errno, generic_category ()); // Sanitize.
+ optional<string> 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<char> 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<vector<char>> 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} \