aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bbot/agent.cli58
-rw-r--r--bbot/agent.cxx86
-rw-r--r--bbot/bbot-agent@.service3
-rw-r--r--doc/manual.cli2
4 files changed, 128 insertions, 21 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} \
diff --git a/doc/manual.cli b/doc/manual.cli
index f1172cc..d581264 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -434,7 +434,7 @@ starts with the task request manifest followed by a list of machine manifests.
\li|\n\c{fingerprint: <agent-fingerprint>}\n
The SHA256 fingerprint of the agent's public key. An agent may be configured
- not to use the certificate-based authentication in which case it does not
+ not to use the public key-based authentication in which case it does not
include this value. However, the controller may be configured to require
the authentication in which case it will respond with the
401 (unauthorized) HTTP status code.||