diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buildtool/auth/TARGETS | 9 | ||||
-rw-r--r-- | src/buildtool/auth/authentication.hpp | 128 | ||||
-rw-r--r-- | src/buildtool/common/cli.hpp | 23 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/TARGETS | 1 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bazel_ac_client.cpp | 7 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bazel_ac_client.hpp | 5 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp | 9 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bazel_cas_client.hpp | 5 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bazel_client_common.hpp | 19 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bazel_execution_client.cpp | 6 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bazel_execution_client.hpp | 5 | ||||
-rw-r--r-- | src/buildtool/execution_api/remote/bazel/bytestream_client.hpp | 7 | ||||
-rw-r--r-- | src/buildtool/main/TARGETS | 1 | ||||
-rw-r--r-- | src/buildtool/main/main.cpp | 45 |
14 files changed, 228 insertions, 42 deletions
diff --git a/src/buildtool/auth/TARGETS b/src/buildtool/auth/TARGETS new file mode 100644 index 00000000..6faf688c --- /dev/null +++ b/src/buildtool/auth/TARGETS @@ -0,0 +1,9 @@ +{ "auth": + { "type": ["@", "rules", "CC", "library"] + , "name": ["auth"] + , "hdrs": ["authentication.hpp"] + , "deps": + [["@", "gsl-lite", "", "gsl-lite"], ["src/buildtool/logging", "logging"]] + , "stage": ["src", "buildtool", "auth"] + } +} diff --git a/src/buildtool/auth/authentication.hpp b/src/buildtool/auth/authentication.hpp new file mode 100644 index 00000000..c3f6eb71 --- /dev/null +++ b/src/buildtool/auth/authentication.hpp @@ -0,0 +1,128 @@ +// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDED_SRC_BUILDTOOL_AUTH_AUTHENTICATION_HPP +#define INCLUDED_SRC_BUILDTOOL_AUTH_AUTHENTICATION_HPP +#include <cstdint> +#include <filesystem> +#include <fstream> +#include <optional> +#include <streambuf> +#include <string> +#include <utility> + +#include <gsl-lite/gsl-lite.hpp> + +#include "src/buildtool/logging/logger.hpp" +enum class AuthMethod : std::uint8_t { kNONE, kTLS }; + +class Auth { + public: + [[nodiscard]] static auto Instance() noexcept -> Auth& { + static Auth instance{}; + return instance; + } + + static void SetAuthMethod(AuthMethod x) { Instance().auth_ = x; } + [[nodiscard]] static auto GetAuthMethod() noexcept -> AuthMethod { + return Instance().auth_; + } + + class TLS { + public: + [[nodiscard]] static auto Instance() noexcept -> TLS& { + static TLS instance{}; + return instance; + } + + [[nodiscard]] static auto CACert() noexcept -> const std::string& { + return Instance().ca_cert_; + } + + [[nodiscard]] static auto ClientCert() noexcept -> const std::string& { + return Instance().client_cert_; + } + + [[nodiscard]] static auto ClientKey() noexcept -> const std::string& { + return Instance().client_key_; + } + + [[nodiscard]] static auto SetCACertificate( + std::filesystem::path const& cert_file) noexcept -> bool { + return set(cert_file, &Instance().ca_cert_); + } + + [[nodiscard]] static auto SetClientCertificate( + std::filesystem::path const& cert_file) noexcept -> bool { + return set(cert_file, &Instance().client_cert_); + } + + [[nodiscard]] static auto SetClientKey( + std::filesystem::path const& key_file) noexcept -> bool { + return set(key_file, &Instance().client_key_); + } + + // must be called after the parsing of cmd line arguments + // we ensure that either both tls_client_cert or tls_client_key are set + // or none of the two. + [[nodiscard]] static auto Validate() noexcept -> bool { + if (CACert().empty()) { + Logger::Log(LogLevel::Error, "Please provide tls-ca-cert"); + return false; + } + + // to enable mTLS, both tls_client_{ceritifcate,key} must be + // supplied + if (ClientCert().empty() && not(ClientKey().empty())) { + Logger::Log(LogLevel::Error, + "Please also provide tls-client-cert"); + return false; + } + if (not(ClientCert().empty()) && ClientKey().empty()) { + Logger::Log(LogLevel::Error, + "Please also provide tls-client-key"); + return false; + } + return true; + } + + private: + std::string ca_cert_; + std::string client_cert_; + std::string client_key_; + + // auxiliary function to set the content of the members of this class + [[nodiscard]] static auto set( + std::filesystem::path const& x, + gsl::not_null<std::string*> const& member) noexcept -> bool { + Auth::SetAuthMethod(AuthMethod::kTLS); + try { + // if the file does not exist, it will throw an exception + auto file = std::filesystem::canonical(x); + std::ifstream cert{file}; + std::string tmp((std::istreambuf_iterator<char>(cert)), + std::istreambuf_iterator<char>()); + *member = std::move(tmp); + } catch (std::exception const& e) { + Logger::Log(LogLevel::Error, e.what()); + return false; + } + return true; + } + }; + + private: + AuthMethod auth_{AuthMethod::kNONE}; +}; +#endif diff --git a/src/buildtool/common/cli.hpp b/src/buildtool/common/cli.hpp index 4650f749..b9c2feb9 100644 --- a/src/buildtool/common/cli.hpp +++ b/src/buildtool/common/cli.hpp @@ -125,6 +125,14 @@ struct GraphArguments { std::optional<std::filesystem::path> git_cas{}; }; +/// \brief Arguments for authentication methods. +struct AuthArguments { + // CA certificate used to verify server's identity + std::optional<std::filesystem::path> tls_ca_cert{std::nullopt}; + std::optional<std::filesystem::path> tls_client_cert{std::nullopt}; + std::optional<std::filesystem::path> tls_client_key{std::nullopt}; +}; + static inline auto SetupCommonArguments( gsl::not_null<CLI::App*> const& app, gsl::not_null<CommonArguments*> const& clargs) { @@ -457,4 +465,19 @@ static inline auto SetupCompatibilityArguments( "remote build execution protocol. As the change affects identifiers, " "the flag must be used consistently for all related invocations."); } + +static inline auto SetupAuthArguments( + gsl::not_null<CLI::App*> const& app, + gsl::not_null<AuthArguments*> const& authargs) { + app->add_option("--tls-ca-cert", + authargs->tls_ca_cert, + "Path to a TLS CA certificate that is trusted to sign the " + "server certificate."); + app->add_option("--tls-client-cert", + authargs->tls_client_cert, + "Path to the TLS client certificate."); + app->add_option("--tls-client-key", + authargs->tls_client_key, + "Path to the TLS client key."); +} #endif // INCLUDED_SRC_BUILDTOOL_COMMON_CLI_HPP diff --git a/src/buildtool/execution_api/remote/TARGETS b/src/buildtool/execution_api/remote/TARGETS index 4bf96ea0..3f7961d5 100644 --- a/src/buildtool/execution_api/remote/TARGETS +++ b/src/buildtool/execution_api/remote/TARGETS @@ -27,6 +27,7 @@ , ["@", "gsl-lite", "", "gsl-lite"] , ["src/buildtool/common", "bazel_types"] , ["src/buildtool/execution_api/bazel_msg", "bazel_msg"] + , ["src/buildtool/auth", "auth"] ] , "proto": [ ["@", "bazel_remote_apis", "", "remote_execution_proto"] diff --git a/src/buildtool/execution_api/remote/bazel/bazel_ac_client.cpp b/src/buildtool/execution_api/remote/bazel/bazel_ac_client.cpp index d96db45a..6eac5adf 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_ac_client.cpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_ac_client.cpp @@ -18,12 +18,9 @@ #include "src/buildtool/common/bazel_types.hpp" #include "src/buildtool/execution_api/remote/bazel/bazel_client_common.hpp" -BazelAcClient::BazelAcClient(std::string const& server, - Port port, - std::string const& user, - std::string const& pwd) noexcept { +BazelAcClient::BazelAcClient(std::string const& server, Port port) noexcept { stub_ = bazel_re::ActionCache::NewStub( - CreateChannelWithCredentials(server, port, user, pwd)); + CreateChannelWithCredentials(server, port)); } auto BazelAcClient::GetActionResult( diff --git a/src/buildtool/execution_api/remote/bazel/bazel_ac_client.hpp b/src/buildtool/execution_api/remote/bazel/bazel_ac_client.hpp index 8712beb8..c9914e77 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_ac_client.hpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_ac_client.hpp @@ -29,10 +29,7 @@ /// https://github.com/bazelbuild/bazel/blob/4b6ad34dbba15dacebfb6cbf76fa741649cdb007/third_party/remoteapis/build/bazel/remote/execution/v2/remote_execution.proto#L137 class BazelAcClient { public: - BazelAcClient(std::string const& server, - Port port, - std::string const& user = "", - std::string const& pwd = "") noexcept; + BazelAcClient(std::string const& server, Port port) noexcept; [[nodiscard]] auto GetActionResult( std::string const& instance_name, diff --git a/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp b/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp index 8158ee55..232c5c08 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp @@ -31,13 +31,10 @@ namespace { } // namespace -BazelCasClient::BazelCasClient(std::string const& server, - Port port, - std::string const& user, - std::string const& pwd) noexcept - : stream_{std::make_unique<ByteStreamClient>(server, port, user, pwd)} { +BazelCasClient::BazelCasClient(std::string const& server, Port port) noexcept + : stream_{std::make_unique<ByteStreamClient>(server, port)} { stub_ = bazel_re::ContentAddressableStorage::NewStub( - CreateChannelWithCredentials(server, port, user, pwd)); + CreateChannelWithCredentials(server, port)); } auto BazelCasClient::FindMissingBlobs( diff --git a/src/buildtool/execution_api/remote/bazel/bazel_cas_client.hpp b/src/buildtool/execution_api/remote/bazel/bazel_cas_client.hpp index b40d84d2..bcf6df7e 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_cas_client.hpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_cas_client.hpp @@ -33,10 +33,7 @@ /// https://github.com/bazelbuild/bazel/blob/4b6ad34dbba15dacebfb6cbf76fa741649cdb007/third_party/remoteapis/build/bazel/remote/execution/v2/remote_execution.proto#L243 class BazelCasClient { public: - BazelCasClient(std::string const& server, - Port port, - std::string const& user = "", - std::string const& pwd = "") noexcept; + BazelCasClient(std::string const& server, Port port) noexcept; /// \brief Find missing blobs /// \param[in] instance_name Name of the CAS instance diff --git a/src/buildtool/execution_api/remote/bazel/bazel_client_common.hpp b/src/buildtool/execution_api/remote/bazel/bazel_client_common.hpp index 9df99b97..60fb7860 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_client_common.hpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_client_common.hpp @@ -22,6 +22,7 @@ #include <string> #include "grpcpp/grpcpp.h" +#include "src/buildtool/auth/authentication.hpp" #include "src/buildtool/common/bazel_types.hpp" #include "src/buildtool/execution_api/bazel_msg/bazel_common.hpp" #include "src/buildtool/execution_api/remote/config.hpp" @@ -29,18 +30,20 @@ [[maybe_unused]] [[nodiscard]] static inline auto CreateChannelWithCredentials( std::string const& server, - Port port, - std::string const& user = "", - [[maybe_unused]] std::string const& pwd = "") noexcept { - std::shared_ptr<grpc::ChannelCredentials> cred; + Port port) noexcept { + + std::shared_ptr<grpc::ChannelCredentials> creds; std::string address = server + ':' + std::to_string(port); - if (user.empty()) { - cred = grpc::InsecureChannelCredentials(); + if (Auth::GetAuthMethod() == AuthMethod::kTLS) { + auto tls_opts = grpc::SslCredentialsOptions{Auth::TLS::CACert(), + Auth::TLS::ClientKey(), + Auth::TLS::ClientCert()}; + creds = grpc::SslCredentials(tls_opts); } else { - // TODO(oreiche): set up authentication credentials + creds = grpc::InsecureChannelCredentials(); } - return grpc::CreateChannel(address, cred); + return grpc::CreateChannel(address, creds); } [[maybe_unused]] static inline void LogStatus(Logger const* logger, diff --git a/src/buildtool/execution_api/remote/bazel/bazel_execution_client.cpp b/src/buildtool/execution_api/remote/bazel/bazel_execution_client.cpp index a1e031a8..b1244e0c 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_execution_client.cpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_execution_client.cpp @@ -47,11 +47,9 @@ void LogExecutionStatus(gsl::not_null<Logger const*> const& logger, } // namespace BazelExecutionClient::BazelExecutionClient(std::string const& server, - Port port, - std::string const& user, - std::string const& pwd) noexcept { + Port port) noexcept { stub_ = bazel_re::Execution::NewStub( - CreateChannelWithCredentials(server, port, user, pwd)); + CreateChannelWithCredentials(server, port)); } auto BazelExecutionClient::Execute(std::string const& instance_name, diff --git a/src/buildtool/execution_api/remote/bazel/bazel_execution_client.hpp b/src/buildtool/execution_api/remote/bazel/bazel_execution_client.hpp index 06b3cfdc..480caf9c 100644 --- a/src/buildtool/execution_api/remote/bazel/bazel_execution_client.hpp +++ b/src/buildtool/execution_api/remote/bazel/bazel_execution_client.hpp @@ -52,10 +52,7 @@ class BazelExecutionClient { } }; - BazelExecutionClient(std::string const& server, - Port port, - std::string const& user = "", - std::string const& pwd = "") noexcept; + BazelExecutionClient(std::string const& server, Port port) noexcept; [[nodiscard]] auto Execute(std::string const& instance_name, bazel_re::Digest const& action_digest, diff --git a/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp b/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp index 85e26078..8edaef3e 100644 --- a/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp +++ b/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp @@ -71,12 +71,9 @@ class ByteStreamClient { } }; - ByteStreamClient(std::string const& server, - Port port, - std::string const& user = "", - std::string const& pwd = "") noexcept { + ByteStreamClient(std::string const& server, Port port) noexcept { stub_ = google::bytestream::ByteStream::NewStub( - CreateChannelWithCredentials(server, port, user, pwd)); + CreateChannelWithCredentials(server, port)); } [[nodiscard]] auto IncrementalRead( diff --git a/src/buildtool/main/TARGETS b/src/buildtool/main/TARGETS index 92a0b9a3..70e361a2 100644 --- a/src/buildtool/main/TARGETS +++ b/src/buildtool/main/TARGETS @@ -16,6 +16,7 @@ , ["src/buildtool/multithreading", "task_system"] , ["src/utils/cpp", "concepts"] , ["src/utils/cpp", "json"] + , ["src/buildtool/auth", "auth"] , "common" , "version" , "analyse" diff --git a/src/buildtool/main/main.cpp b/src/buildtool/main/main.cpp index 0c9f047a..cf98bcf3 100644 --- a/src/buildtool/main/main.cpp +++ b/src/buildtool/main/main.cpp @@ -36,6 +36,7 @@ #include "src/buildtool/main/exit_codes.hpp" #include "src/buildtool/main/install_cas.hpp" #ifndef BOOTSTRAP_BUILD_TOOL +#include "src/buildtool/auth/authentication.hpp" #include "src/buildtool/graph_traverser/graph_traverser.hpp" #include "src/buildtool/progress_reporting/base_progress_reporter.hpp" #endif @@ -78,6 +79,7 @@ struct CommandLineArguments { RebuildArguments rebuild; FetchArguments fetch; GraphArguments graph; + AuthArguments auth; }; /// \brief Setup arguments for sub command "just describe". @@ -110,6 +112,7 @@ auto SetupBuildCommandArguments( SetupLogArguments(app, &clargs->log); SetupAnalysisArguments(app, &clargs->analysis); SetupEndpointArguments(app, &clargs->endpoint); + SetupAuthArguments(app, &clargs->auth); SetupBuildArguments(app, &clargs->build); SetupCompatibilityArguments(app); } @@ -136,6 +139,7 @@ auto SetupInstallCasCommandArguments( gsl::not_null<CommandLineArguments*> const& clargs) { SetupCompatibilityArguments(app); SetupEndpointArguments(app, &clargs->endpoint); + SetupAuthArguments(app, &clargs->auth); SetupFetchArguments(app, &clargs->fetch); SetupLogArguments(app, &clargs->log); } @@ -147,6 +151,7 @@ auto SetupTraverseCommandArguments( SetupCommonArguments(app, &clargs->common); SetupLogArguments(app, &clargs->log); SetupEndpointArguments(app, &clargs->endpoint); + SetupAuthArguments(app, &clargs->auth); SetupGraphArguments(app, &clargs->graph); // instead of analysis SetupBuildArguments(app, &clargs->build); SetupStageArguments(app, &clargs->stage); @@ -239,6 +244,7 @@ void SetupLogging(LogArguments const& clargs) { #ifndef BOOTSTRAP_BUILD_TOOL void SetupExecutionConfig(EndpointArguments const& eargs, + AuthArguments const& authargs, BuildArguments const& bargs, RebuildArguments const& rargs) { using LocalConfig = LocalExecutionConfig; @@ -276,6 +282,39 @@ void SetupExecutionConfig(EndpointArguments const& eargs, std::exit(kExitFailure); } } + auto use_tls = false; + if (authargs.tls_ca_cert) { + use_tls = true; + if (not Auth::TLS::SetCACertificate(*authargs.tls_ca_cert)) { + Logger::Log(LogLevel::Error, + "Could not read '{}' certificate.", + authargs.tls_ca_cert->string()); + std::exit(kExitFailure); + } + } + if (authargs.tls_client_cert) { + use_tls = true; + if (not Auth::TLS::SetClientCertificate(*authargs.tls_client_cert)) { + Logger::Log(LogLevel::Error, + "Could not read '{}' certificate.", + authargs.tls_client_cert->string()); + std::exit(kExitFailure); + } + } + if (authargs.tls_client_key) { + use_tls = true; + if (not Auth::TLS::SetClientKey(*authargs.tls_client_key)) { + Logger::Log(LogLevel::Error, + "Could not read '{}' key.", + authargs.tls_client_key->string()); + std::exit(kExitFailure); + } + } + if (use_tls) { + if (not Auth::TLS::Validate()) { + std::exit(kExitFailure); + } + } } void SetupHashFunction() { @@ -1097,8 +1136,10 @@ auto main(int argc, char* argv[]) -> int { } #ifndef BOOTSTRAP_BUILD_TOOL SetupHashFunction(); - SetupExecutionConfig( - arguments.endpoint, arguments.build, arguments.rebuild); + SetupExecutionConfig(arguments.endpoint, + arguments.auth, + arguments.build, + arguments.rebuild); #endif auto jobs = arguments.build.build_jobs > 0 ? arguments.build.build_jobs |