Merge pull request #1407 from theopolis/tls_customization

Add 'hidden' flags to customize TLS plugins
This commit is contained in:
Teddy Reed 2015-08-28 17:21:49 -07:00
commit cd1d39b323
17 changed files with 270 additions and 109 deletions

View File

@ -83,6 +83,14 @@ The posted logger data is exactly the same as logged to disk by the **filesystem
{}
```
**Customizations**
There are several unlisted flags to further control the remote settings. These controls are helpful if using a somewhat opaque API.
`--tls_secret_always=True` will always send the enrollment secret. This will not perform an enrollment request with every config/logger attempt but rather "also" include the secret. If this is enabled, the secret is appended as a URI variable.
`--tls_enroll_override=enroll_secret` this allows one to rename the enrollment key request body or URI variable.
## Remote logging buffering
In most cases the client plugins default to 3-strikes-you're-out when attempting to POST to the configured endpoints. If a configuration cannot be retrieved the client will exit non-0 but a non-responsive logger endpoint will cause logs to buffer in RocksDB. The logging buffer size can be controlled by a [CLI flag](../installation/cli-flags.md), and if the size overflows logs will drop.

View File

@ -210,6 +210,13 @@ std::string getHostname();
*/
std::string generateHostUuid();
/**
* @brief Get a configured UUID/name that uniquely identify this machine
*
* @return string to identify this machine
*/
std::string getHostIdentifier();
/**
* @brief Getter for the current time, in a human-readable format.
*

View File

@ -83,7 +83,7 @@ std::string getNodeKey(const std::string& enroll_plugin, bool force = false);
*
* @return enroll_secret The trimmed content read from FLAGS_enroll_secret_path.
*/
std::string getEnrollSecret();
const std::string& getEnrollSecret();
/**
* @brief Enroll plugin registry.

View File

@ -41,6 +41,10 @@ FLAG(uint64,
0,
"Optional interval in seconds to re-read configuration (min=10)");
DECLARE_bool(tls_secret_always);
DECLARE_string(tls_enroll_override);
DECLARE_bool(tls_node_api);
class TLSConfigPlugin : public ConfigPlugin {
public:
Status setUp();
@ -66,13 +70,24 @@ Status TLSConfigPlugin::setUp() {
return Status(0, "OK");
}
Status makeTLSConfigRequest(const std::string& uri, pt::ptree& output) {
Status makeTLSConfigRequest(const std::string& uri,
const std::string& node_key,
pt::ptree& output) {
// Make a request to the config endpoint, providing the node secret.
pt::ptree params;
params.put<std::string>("node_key", getNodeKey("tls"));
auto request = Request<TLSTransport, JSONSerializer>(uri);
auto status = request.call(params);
// If using a GET request, append the node_key to the URI variables.
std::string uri_suffix;
if (FLAGS_tls_node_api) {
uri_suffix = "&node_key=" + node_key;
} else {
params.put<std::string>("node_key", node_key);
}
// Again check for GET to call with/without parameters.
auto request = Request<TLSTransport, JSONSerializer>(uri + uri_suffix);
auto status = (FLAGS_tls_node_api) ? request.call() : request.call(params);
if (!status.ok()) {
return status;
}
@ -84,19 +99,32 @@ Status makeTLSConfigRequest(const std::string& uri, pt::ptree& output) {
}
// Receive config or key rejection
if (output.count("node_invalid") > 0) {
if (output.count("node_invalid") > 0 || output.count("error") > 0) {
return Status(1, "Config retrieval failed: Invalid node key");
}
return Status(0, "OK");
}
Status TLSConfigPlugin::genConfig(std::map<std::string, std::string>& config) {
auto uri = "https://" + FLAGS_tls_hostname + FLAGS_config_tls_endpoint;
VLOG(1) << "TLSConfigPlugin requesting a config from: " << uri;
auto node_key = getNodeKey("tls");
auto uri = "https://" + FLAGS_tls_hostname;
if (FLAGS_tls_node_api) {
// The TLS API should treat clients as nodes.
// In this case the node_key acts as an identifier (node) and the endpoints
// (if provided) are treated as edges from the nodes.
uri += "/" + node_key;
}
uri += FLAGS_config_tls_endpoint;
// Some APIs may require persistent identification.
if (FLAGS_tls_secret_always) {
uri += ((uri.find("?") != std::string::npos) ? "&" : "?") +
FLAGS_tls_enroll_override + "=" + getEnrollSecret();
}
pt::ptree recv;
for (size_t i = 1; i <= CONFIG_TLS_MAX_ATTEMPTS; i++) {
auto status = makeTLSConfigRequest(uri, recv);
auto status = makeTLSConfigRequest(uri, node_key, recv);
if (status.ok()) {
std::stringstream ss;
try {

View File

@ -26,6 +26,7 @@
#include <boost/uuid/uuid_io.hpp>
#include <osquery/core.h>
#include <osquery/database.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/sql.h>
@ -46,6 +47,11 @@ CLI_FLAG(bool,
false,
"Force osqueryd to kill previously-running daemons");
FLAG(string,
host_identifier,
"hostname",
"Field used to identify the host running osquery (hostname, uuid)");
std::string getHostname() {
char hostname[256] = {0}; // Linux max should be 64.
gethostname(hostname, sizeof(hostname) - 1);
@ -81,6 +87,27 @@ std::string generateHostUuid() {
#endif
}
std::string getHostIdentifier() {
if (FLAGS_host_identifier != "uuid") {
// use the hostname as the default machine identifier
return osquery::getHostname();
}
// Generate a identifier/UUID for this application launch, and persist.
static std::string ident;
if (ident.size() == 0) {
// Lookup the host identifier (UUID) previously generated and stored.
getDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
if (ident.size() == 0) {
ident = osquery::generateHostUuid();
VLOG(1) << "Using uuid " << ident << " as host identifier";
setDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
}
}
return ident;
}
std::string getAsciiTime() {
auto result = std::time(nullptr);
auto time_str = std::string(std::asctime(std::gmtime(&result)));

View File

@ -28,6 +28,19 @@ namespace fs = boost::filesystem;
namespace osquery {
std::string kFakeDirectory = "";
#ifdef DARWIN
std::string kTestWorkingDirectory = "/private/tmp/osquery-tests";
#else
std::string kTestWorkingDirectory = "/tmp/osquery-tests";
#endif
/// Most tests will use binary or disk-backed content for parsing tests.
#ifndef OSQUERY_BUILD_SDK
std::string kTestDataPath = "../../../tools/tests/";
#else
std::string kTestDataPath = "../../../../tools/tests/";
#endif
DECLARE_string(database_path);
DECLARE_string(extensions_socket);
DECLARE_string(modules_autoload);
@ -53,29 +66,21 @@ void initTesting() {
// Set safe default values for path-based flags.
// Specific unittests may edit flags temporarily.
std::string testWorkingDirectory =
kTestWorkingDirectory + std::to_string(getuid()) + "/";
kFakeDirectory = testWorkingDirectory + kFakeDirectoryName;
kTestWorkingDirectory += std::to_string(getuid()) + "/";
kFakeDirectory = kTestWorkingDirectory + kFakeDirectoryName;
fs::remove_all(testWorkingDirectory);
fs::create_directories(testWorkingDirectory);
FLAGS_database_path = testWorkingDirectory + "unittests.db";
FLAGS_extensions_socket = testWorkingDirectory + "unittests.em";
FLAGS_extensions_autoload = testWorkingDirectory + "unittests-ext.load";
FLAGS_modules_autoload = testWorkingDirectory + "unittests-mod.load";
fs::remove_all(kTestWorkingDirectory);
fs::create_directories(kTestWorkingDirectory);
FLAGS_database_path = kTestWorkingDirectory + "unittests.db";
FLAGS_extensions_socket = kTestWorkingDirectory + "unittests.em";
FLAGS_extensions_autoload = kTestWorkingDirectory + "unittests-ext.load";
FLAGS_modules_autoload = kTestWorkingDirectory + "unittests-mod.load";
FLAGS_disable_logging = true;
// Create a default DBHandle instance before unittests.
(void)DBHandle::getInstance();
}
/// Most tests will use binary or disk-backed content for parsing tests.
#ifndef OSQUERY_BUILD_SDK
std::string kTestDataPath = "../../../tools/tests/";
#else
std::string kTestDataPath = "../../../../tools/tests/";
#endif
QueryData getTestDBExpectedResults() {
QueryData d;
Row row1;

View File

@ -27,19 +27,19 @@ namespace osquery {
/// Init function for tests and benchmarks.
void initTesting();
/// Cleanup/stop function for tests and benchmarks.
void cleanupTesting();
/// Any SQL-dependent tests should use kTestQuery for a pre-populated example.
const std::string kTestQuery = "SELECT * FROM test_table";
/// Tests can be run from within the source or build directory.
/// The test initializer will attempt to discovery the current working path.
extern std::string kTestDataPath;
/// Tests should limit intermediate input/output to a working directory.
/// Config data, logging results, and intermediate database/caching usage.
#ifdef DARWIN
const std::string kTestWorkingDirectory = "/private/tmp/osquery-tests";
#else
const std::string kTestWorkingDirectory = "/tmp/osquery-tests";
#endif
extern std::string kTestWorkingDirectory;
/// A fake directory tree should be used for filesystem iterator testing.
const std::string kFakeDirectoryName = "fstree";

View File

@ -22,40 +22,10 @@
namespace osquery {
FLAG(string,
host_identifier,
"hostname",
"Field used to identify the host running osquery (hostname, uuid)");
FLAG(bool, enable_monitor, false, "Enable the schedule monitor");
FLAG(uint64, schedule_timeout, 0, "Limit the schedule, 0 for no limit")
Status getHostIdentifier(std::string& ident) {
if (FLAGS_host_identifier != "uuid") {
// use the hostname as the default machine identifier
ident = osquery::getHostname();
return Status(0, "OK");
}
// Lookup the host identifier (UUID) previously generated and stored.
auto status = getDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
if (!status.ok()) {
// The lookup failed, there is a problem accessing the database.
VLOG(1) << "Could not access database; using hostname as host identifier";
ident = osquery::getHostname();
return Status(0, "OK");
}
if (ident.size() == 0) {
// There was no uuid stored in the database, generate one and store it.
ident = osquery::generateHostUuid();
VLOG(1) << "Using uuid " << ident << " as host identifier";
return setDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
}
return status;
}
inline SQL monitor(const std::string& name, const ScheduledQuery& query) {
// Snapshot the performance and times for the worker before running.
auto pid = std::to_string(getpid());
@ -91,11 +61,7 @@ void launchQuery(const std::string& name, const ScheduledQuery& query) {
}
// Fill in a host identifier fields based on configuration or availability.
std::string ident;
auto status = getHostIdentifier(ident);
if (!status.ok() || ident.empty()) {
ident = "<unknown>";
}
std::string ident = getHostIdentifier();
// A query log item contains an optional set of differential results or
// a copy of the most-recent execution alongside some query metadata.
@ -118,7 +84,7 @@ void launchQuery(const std::string& name, const ScheduledQuery& query) {
// Add this execution's set of results to the database-tracked named query.
// We can then ask for a differential from the last time this named query
// was executed by exact matching each row.
status = dbQuery.addNewResults(sql.rows(), diff_results);
auto status = dbQuery.addNewResults(sql.rows(), diff_results);
if (!status.ok()) {
LOG(ERROR) << "Error adding new results to database: " << status.what();
return;

View File

@ -25,26 +25,28 @@
namespace osquery {
const std::string kRealTestPath = kTestWorkingDirectory + "inotify-trigger";
const std::string kRealTestDir = kTestWorkingDirectory + "inotify-triggers";
const std::string kRealTestDirPath = kRealTestDir + "/1";
const std::string kRealTestSubDir = kRealTestDir + "/2";
const std::string kRealTestSubDirPath = kRealTestSubDir + "/1";
int kMaxEventLatency = 3000;
const int kMaxEventLatency = 3000;
class INotifyTests : public testing::Test {
protected:
void SetUp() {
real_test_path = kTestWorkingDirectory + "inotify-trigger";
real_test_dir = kTestWorkingDirectory + "inotify-triggers";
real_test_dir_path = real_test_dir + "/1";
real_test_sub_dir = real_test_dir + "/2";
real_test_sub_dir_path = real_test_sub_dir + "/1";
}
void TearDown() {
// End the event loops, and join on the threads.
boost::filesystem::remove_all(kRealTestPath);
boost::filesystem::remove_all(kRealTestDir);
boost::filesystem::remove_all(real_test_path);
boost::filesystem::remove_all(real_test_dir);
}
void StartEventLoop() {
event_pub_ = std::make_shared<INotifyEventPublisher>();
auto status = EventFactory::registerEventPublisher(event_pub_);
FILE* fd = fopen(kRealTestPath.c_str(), "w");
FILE* fd = fopen(real_test_path.c_str(), "w");
fclose(fd);
temp_thread_ = boost::thread(EventFactory::run, "inotify");
}
@ -88,8 +90,17 @@ class INotifyTests : public testing::Test {
fclose(fd);
}
protected:
// Internal state managers.
std::shared_ptr<INotifyEventPublisher> event_pub_;
boost::thread temp_thread_;
// Transient paths.
std::string real_test_path;
std::string real_test_dir;
std::string real_test_dir_path;
std::string real_test_sub_dir;
std::string real_test_sub_dir_path;
};
TEST_F(INotifyTests, test_register_event_pub) {
@ -217,7 +228,7 @@ TEST_F(INotifyTests, test_inotify_run) {
EXPECT_TRUE(status.ok());
// Create a temporary file to watch, open writeable
FILE* fd = fopen(kRealTestPath.c_str(), "w");
FILE* fd = fopen(real_test_path.c_str(), "w");
// Create a subscriber.
auto sub = std::make_shared<TestINotifyEventSubscriber>();
@ -225,7 +236,7 @@ TEST_F(INotifyTests, test_inotify_run) {
// Create a subscriptioning context
auto mc = std::make_shared<INotifySubscriptionContext>();
mc->path = kRealTestPath;
mc->path = real_test_path;
status = EventFactory::addSubscription(
"inotify", Subscription::create("TestINotifyEventSubscriber", mc));
EXPECT_TRUE(status.ok());
@ -252,10 +263,10 @@ TEST_F(INotifyTests, test_inotify_fire_event) {
sub->init();
// Create a subscriptioning context, note the added Event to the symbol
auto sc = sub->GetSubscription(kRealTestPath, 0);
auto sc = sub->GetSubscription(real_test_path, 0);
sub->subscribe(&TestINotifyEventSubscriber::SimpleCallback, sc, nullptr);
TriggerEvent(kRealTestPath);
TriggerEvent(real_test_path);
sub->WaitForEvents(kMaxEventLatency);
// Make sure our expected event fired (aka subscription callback was called).
@ -269,10 +280,10 @@ TEST_F(INotifyTests, test_inotify_event_action) {
auto sub = std::make_shared<TestINotifyEventSubscriber>();
sub->init();
auto sc = sub->GetSubscription(kRealTestPath, 0);
auto sc = sub->GetSubscription(real_test_path, 0);
sub->subscribe(&TestINotifyEventSubscriber::Callback, sc, nullptr);
TriggerEvent(kRealTestPath);
TriggerEvent(real_test_path);
sub->WaitForEvents(kMaxEventLatency, 4);
// Make sure the inotify action was expected.
@ -287,15 +298,15 @@ TEST_F(INotifyTests, test_inotify_event_action) {
TEST_F(INotifyTests, test_inotify_optimization) {
// Assume event type is registered.
StartEventLoop();
boost::filesystem::create_directory(kRealTestDir);
boost::filesystem::create_directory(real_test_dir);
// Adding a descriptor to a directory will monitor files within.
SubscriptionAction(kRealTestDir);
EXPECT_TRUE(event_pub_->isPathMonitored(kRealTestDirPath));
SubscriptionAction(real_test_dir);
EXPECT_TRUE(event_pub_->isPathMonitored(real_test_dir_path));
// Adding a subscription to a file within a monitored directory is fine
// but this will NOT cause an additional INotify watch.
SubscriptionAction(kRealTestDirPath);
SubscriptionAction(real_test_dir_path);
EXPECT_EQ(event_pub_->numDescriptors(), 1);
StopEventLoop();
}
@ -306,17 +317,17 @@ TEST_F(INotifyTests, test_inotify_recursion) {
auto sub = std::make_shared<TestINotifyEventSubscriber>();
sub->init();
boost::filesystem::create_directory(kRealTestDir);
boost::filesystem::create_directory(kRealTestSubDir);
boost::filesystem::create_directory(real_test_dir);
boost::filesystem::create_directory(real_test_sub_dir);
// Subscribe to the directory inode
auto mc = sub->createSubscriptionContext();
mc->path = kRealTestDir;
mc->path = real_test_dir;
mc->recursive = true;
sub->subscribe(&TestINotifyEventSubscriber::Callback, mc, nullptr);
// Trigger on a subdirectory's file.
TriggerEvent(kRealTestSubDirPath);
TriggerEvent(real_test_sub_dir_path);
sub->WaitForEvents(kMaxEventLatency, 1);
EXPECT_TRUE(sub->count() > 0);

View File

@ -24,12 +24,11 @@ namespace osquery {
const int kDelayUS = 2000;
const int kTimeoutUS = 1000000;
const std::string kTestManagerSocket = kTestWorkingDirectory + "test.em";
class ExtensionsTest : public testing::Test {
protected:
void SetUp() {
socket_path = kTestManagerSocket + std::to_string(rand());
socket_path = kTestWorkingDirectory + "test.em" + std::to_string(rand());
remove(socket_path);
if (pathExists(socket_path).ok()) {
throw std::domain_error("Cannot test sockets: " + socket_path);

View File

@ -30,11 +30,16 @@ namespace pt = boost::property_tree;
namespace osquery {
FLAG(string, logger_tls_endpoint, "", "TLS/HTTPS endpoint for results logging");
FLAG(int32,
logger_tls_period,
4,
"Seconds between flushing logs over TLS/HTTPS");
DECLARE_bool(tls_secret_always);
DECLARE_string(tls_enroll_override);
DECLARE_bool(tls_node_api);
/**
* @brief Control the number of backing-store buffered logs.
*
@ -227,7 +232,20 @@ inline void clearLogs(bool results, const std::vector<std::string>& indexes) {
}
void TLSLogForwarderRunner::start() {
auto uri = "https://" + FLAGS_tls_hostname + FLAGS_logger_tls_endpoint;
auto uri = "https://" + FLAGS_tls_hostname;
if (FLAGS_tls_node_api) {
// The TLS API should treat clients as nodes.
// In this case the node_key acts as an identifier (node) and the endpoints
// (if provided) are treated as edges from the nodes.
uri += "/" + node_key_;
}
uri += FLAGS_logger_tls_endpoint;
// Some APIs may require persistent identification.
if (FLAGS_tls_secret_always) {
uri += ((uri.find("?") != std::string::npos) ? "&" : "?") +
FLAGS_tls_enroll_override + "=" + getEnrollSecret();
}
while (true) {
// Get a list of all the buffered log items.

View File

@ -61,11 +61,11 @@ std::string getNodeKey(const std::string& enroll_plugin, bool force) {
return node_key;
}
std::string getEnrollSecret() {
const std::string& getEnrollSecret() {
static std::string enrollment_secret;
if (enrollment_secret.size() == 0) {
// Secret has not been read
// Secret has not been read yet.
if (FLAGS_enroll_secret_path != "") {
osquery::readFile(FLAGS_enroll_secret_path, enrollment_secret);
boost::trim(enrollment_secret);

View File

@ -28,6 +28,18 @@ CLI_FLAG(string,
"",
"TLS/HTTPS endpoint for client enrollment");
/// Undocumented feature for TLS access token passing.
HIDDEN_FLAG(bool,
tls_secret_always,
false,
"Include TLS enroll secret in every request");
/// Undocumented feature to override TLS enrollment key name.
HIDDEN_FLAG(string,
tls_enroll_override,
"enroll_secret",
"Override the TLS enroll secret key name");
class TLSEnrollPlugin : public EnrollPlugin {
private:
/// Enroll called, return cached key or if no key cached, call requestKey.
@ -47,6 +59,11 @@ REGISTER(TLSEnrollPlugin, "enroll", "tls");
std::string TLSEnrollPlugin::enroll(bool force) {
// If no node secret has been negotiated, try a TLS request.
auto uri = "https://" + FLAGS_tls_hostname + FLAGS_enroll_tls_endpoint;
if (FLAGS_tls_secret_always) {
uri += ((uri.find("?") != std::string::npos) ? "&" : "?") +
FLAGS_tls_enroll_override + "=" + getEnrollSecret();
}
if (node_secret_key_.size() == 0 || force) {
VLOG(1) << "TLSEnrollPlugin requesting a node enroll key from: " << uri;
for (size_t i = 1; i <= ENROLL_TLS_MAX_ATTEMPTS; i++) {
@ -67,7 +84,8 @@ std::string TLSEnrollPlugin::enroll(bool force) {
Status TLSEnrollPlugin::requestKey(const std::string& uri) {
// Read the optional enrollment secret data (sent with an enrollment request).
boost::property_tree::ptree params;
params.put<std::string>("enroll_secret", getEnrollSecret());
params.put<std::string>(FLAGS_tls_enroll_override, getEnrollSecret());
params.put<std::string>("host_identifier", getHostIdentifier());
auto request = Request<TLSTransport, JSONSerializer>(uri);
auto status = request.call(params);
@ -82,13 +100,16 @@ Status TLSEnrollPlugin::requestKey(const std::string& uri) {
return status;
}
// Support multiple response keys as a node key (identifier).
if (recv.count("node_key") > 0) {
// Set the enroll key, should be stored in the RocksDB cache.
// TODO: Store this response key in RocksDB.
node_secret_key_ = recv.get<std::string>("node_key", "");
return Status(0, "OK");
} else {
node_secret_key_ = recv.get("node_key", "");
} else if (recv.count("id") > 0) {
node_secret_key_ = recv.get("id", "");
}
if (node_secret_key_.size() == 0) {
return Status(1, "No enrollment key returned from TLS enroll plugin");
}
return Status(0, "OK");
}
}

View File

@ -13,11 +13,15 @@
#include <osquery/core.h>
#include <osquery/database.h>
#include <osquery/enroll.h>
#include <osquery/filesystem.h>
#include <osquery/flags.h>
#include "osquery/core/test_util.h"
namespace osquery {
DECLARE_string(enroll_secret_path);
class EnrollTests : public testing::Test {
public:
void SetUp() {
@ -48,6 +52,23 @@ class SimpleEnrollPlugin : public EnrollPlugin {
// Register our simple enroll plugin.
REGISTER(SimpleEnrollPlugin, "enroll", "test_simple");
TEST_F(EnrollTests, test_enroll_secret_retrieval) {
// Write an example secret (deploy key).
FLAGS_enroll_secret_path = kTestWorkingDirectory + "secret.txt";
writeTextFile(FLAGS_enroll_secret_path, "test_secret\n", 0600, false);
// Make sure the file content was read and trimmed.
auto secret = getEnrollSecret();
EXPECT_EQ(secret, "test_secret");
// Now change the file path.
FLAGS_enroll_secret_path = kTestWorkingDirectory + "not_a_secret.txt";
// And for good measure, write some content.
writeTextFile(FLAGS_enroll_secret_path, "test_not_a_secret", 0600, false);
// The enrollment key should not update.
secret = getEnrollSecret();
EXPECT_EQ(secret, "test_secret");
}
TEST_F(EnrollTests, test_enroll_key_retrieval) {
FLAGS_disable_enrollment = true;
// Without enrollment, and with an empty nodeKey storage value, no node key

View File

@ -89,6 +89,11 @@ class Transport {
return response_params_;
}
template <typename T>
void setOption(const std::string& name, const T& value) {
options_.put(name, value);
}
/**
* @brief Virtual destructor
*/
@ -106,6 +111,9 @@ class Transport {
/// storage for response parameters
boost::property_tree::ptree response_params_;
/// options from request call (use defined by specific transport)
boost::property_tree::ptree options_;
};
/**
@ -252,6 +260,11 @@ class Request {
return transport_->getResponseStatus();
}
template <typename T>
void setOption(const std::string& name, const T& value) {
transport_->setOption(name, value);
}
private:
/// storage for the resource destination
std::string destination_;

View File

@ -55,6 +55,16 @@ CLI_FLAG(string,
"",
"Optional path to a TLS client-auth PEM private key");
#if defined(DEBUG)
HIDDEN_FLAG(bool,
tls_allow_unsafe,
false,
"Allow TLS server certificate trust failures");
#endif
/// Undocumented feature to override TLS endpoints.
HIDDEN_FLAG(bool, tls_node_api, false, "Use node key as TLS endpoints");
TLSTransport::TLSTransport() : verify_peer_(true) {
if (FLAGS_tls_server_certs.size() > 0) {
server_certificate_file_ = FLAGS_tls_server_certs;
@ -85,6 +95,13 @@ http::client TLSTransport::getClient() {
ciphers += ":!CBC:!SHA";
#endif
#if defined(DEBUG)
// Configuration may allow unsafe TLS testing if compiled as a debug target.
if (FLAGS_tls_allow_unsafe) {
options.always_verify_peer(false);
}
#endif
options.openssl_ciphers(ciphers);
options.openssl_options(SSL_OP_NO_SSLv3 | SSL_OP_NO_SSLv2 | SSL_OP_ALL);
@ -153,9 +170,20 @@ Status TLSTransport::sendRequest(const std::string& params) {
http::client::request r(destination_);
decorateRequest(r);
// Allow request calls to override the default HTTP POST verb.
HTTPVerb verb = HTTP_POST;
if (options_.count("verb") > 0) {
verb = (HTTPVerb)options_.get<int>("verb", HTTP_POST);
}
try {
VLOG(1) << "TLS/HTTPS POST request to URI: " << destination_;
response_ = client.post(r, params);
VLOG(1) << "TLS/HTTPS " << ((verb == HTTP_POST) ? "POST" : "PUT")
<< " request to URI: " << destination_;
if (verb == HTTP_POST) {
response_ = client.post(r, params);
} else {
response_ = client.put(r, params);
}
response_status_ =
serializer_->deserialize(body(response_), response_params_);
} catch (const std::exception& e) {

View File

@ -37,17 +37,25 @@ DECLARE_string(tls_client_cert);
/// TLS server hostname.
DECLARE_string(tls_hostname);
/**
* @brief HTTP verb selections.
*/
enum HTTPVerb {
HTTP_POST = 0,
HTTP_PUT,
};
/**
* @brief HTTPS (TLS) transport.
*/
class TLSTransport : public Transport {
public:
/**
* @brief Send a simple request to the destination with no parameters
*
* @return An instance of osquery::Status indicating the success or failure
* of the operation
* @return A status indicating socket, network, or transport success/error.
* Return code (1) for general connectivity problems, return code (2) for TLS
* specific errors.
*/
Status sendRequest();
@ -56,8 +64,9 @@ class TLSTransport : public Transport {
*
* @param params A string representing the serialized parameters
*
* @return An instance of osquery::Status indicating the success or failure
* of the operation
* @return A status indicating socket, network, or transport success/error.
* Return code (1) for general connectivity problems, return code (2) for TLS
* specific errors.
*/
Status sendRequest(const std::string& params);