Expose Peer SPIFFE Identity

pull/22863/head
ZhenLian 5 years ago
parent de8aa7e9df
commit 9f92365657
  1. 1
      include/grpc/grpc_security_constants.h
  2. 38
      src/core/lib/security/security_connector/ssl_utils.cc
  3. 1
      src/core/lib/security/security_connector/ssl_utils.h
  4. 12
      src/core/tsi/ssl_transport_security.cc
  5. 3
      src/core/tsi/ssl_transport_security.h
  6. 101
      test/core/security/security_connector_test.cc

@ -32,6 +32,7 @@ extern "C" {
#define GRPC_X509_PEM_CERT_CHAIN_PROPERTY_NAME "x509_pem_cert_chain"
#define GRPC_SSL_SESSION_REUSED_PROPERTY "ssl_session_reused"
#define GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME "security_level"
#define GRPC_PEER_SPIFFE_ID_PROPERTY_NAME "peer_spiffe_id"
/** Environment variable that points to the default SSL roots file. This file
must be a PEM encoded file with all the roots such as the one that can be

@ -25,6 +25,8 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <vector>
#include "src/core/ext/transport/chttp2/alpn/alpn.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/string.h"
@ -220,6 +222,27 @@ int grpc_ssl_cmp_target_name(absl::string_view target_name,
return overridden_target_name.compare(other_overridden_target_name);
}
bool isSpiffeID(absl::string_view spiffe_uri) {
if (spiffe_uri.size() > 2048) {
gpr_log(GPR_INFO, "Invalid SPIFFE ID: ID longer than 2048 bytes.");
return false;
}
std::vector<absl::string_view> splits = absl::StrSplit(spiffe_uri, '/');
if (splits.size() < 4 || splits[0] != "spiffe:" || splits[1] != "") {
gpr_log(GPR_INFO, "Invalid SPIFFE ID: invalid format.");
return false;
}
if (splits[3] == "") {
gpr_log(GPR_INFO, "Invalid SPIFFE ID: workload id is empty.");
return false;
}
if (splits[2].size() > 255) {
gpr_log(GPR_INFO, "Invalid SPIFFE ID: domain longer than 255 characters.");
return false;
}
return true;
}
grpc_core::RefCountedPtr<grpc_auth_context> grpc_ssl_peer_to_auth_context(
const tsi_peer* peer, const char* transport_security_type) {
size_t i;
@ -232,6 +255,9 @@ grpc_core::RefCountedPtr<grpc_auth_context> grpc_ssl_peer_to_auth_context(
grpc_auth_context_add_cstring_property(
ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
transport_security_type);
const char* spiffe_data = nullptr;
size_t spiffe_length;
int spiffe_id_count = 0;
for (i = 0; i < peer->property_count; i++) {
const tsi_peer_property* prop = &peer->properties[i];
if (prop->name == nullptr) continue;
@ -263,12 +289,24 @@ grpc_core::RefCountedPtr<grpc_auth_context> grpc_ssl_peer_to_auth_context(
grpc_auth_context_add_property(
ctx.get(), GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME,
prop->value.data, prop->value.length);
} else if (strcmp(prop->name, TSI_X509_URI_PEER_PROPERTY) == 0) {
absl::string_view spiffe_id(prop->value.data, prop->value.length);
if (isSpiffeID(spiffe_id)) {
spiffe_data = prop->value.data;
spiffe_length = prop->value.length;
spiffe_id_count += 1;
}
}
}
if (peer_identity_property_name != nullptr) {
GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
ctx.get(), peer_identity_property_name) == 1);
}
// SPIFFE ID should be unique.
if (spiffe_id_count == 1 && spiffe_length > 0 && spiffe_data != nullptr) {
grpc_auth_context_add_property(ctx.get(), GRPC_PEER_SPIFFE_ID_PROPERTY_NAME,
spiffe_data, spiffe_length);
}
return ctx;
}

@ -23,6 +23,7 @@
#include <stdbool.h>
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc_security.h>

@ -33,9 +33,6 @@
#include <sys/socket.h>
#endif
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@ -43,6 +40,9 @@
#include <grpc/support/sync.h>
#include <grpc/support/thd_id.h>
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
extern "C" {
#include <openssl/bio.h>
#include <openssl/crypto.h> /* For OPENSSL_free */
@ -378,6 +378,12 @@ static tsi_result add_subject_alt_names_properties_to_peer(
TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY,
reinterpret_cast<const char*>(name), static_cast<size_t>(name_size),
&peer->properties[peer->property_count++]);
if (subject_alt_name->type == GEN_URI) {
result = tsi_construct_string_peer_property(
TSI_X509_URI_PEER_PROPERTY, reinterpret_cast<const char*>(name),
static_cast<size_t>(name_size),
&peer->properties[peer->property_count++]);
}
OPENSSL_free(name);
} else if (subject_alt_name->type == GEN_IPADD) {
char ntop_buf[INET6_ADDRSTRLEN];

@ -22,7 +22,6 @@
#include <grpc/support/port_platform.h>
#include "absl/strings/string_view.h"
#include "src/core/tsi/transport_security_interface.h"
extern "C" {
@ -44,6 +43,8 @@ extern "C" {
#define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol"
#define TSI_X509_URI_PEER_PROPERTY "x509_uri"
/* --- tsi_ssl_root_certs_store object ---
This object stores SSL root certificates. It can be shared by multiple SSL

@ -16,19 +16,19 @@
*
*/
#include <stdio.h>
#include <string.h>
#include "src/core/lib/security/security_connector/security_connector.h"
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <stdio.h>
#include <string.h>
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gpr/tmpfile.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/security/security_connector/security_connector.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
#include "src/core/lib/slice/slice_string_helpers.h"
#include "src/core/tsi/ssl_transport_security.h"
@ -222,6 +222,35 @@ static int check_x509_pem_cert_chain(const grpc_auth_context* ctx,
return 1;
}
static int check_spiffe_id(const grpc_auth_context* ctx,
const char* expected_spiffe_id,
bool expect_spiffe_id) {
grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(
ctx, GRPC_PEER_SPIFFE_ID_PROPERTY_NAME);
const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
if (prop == nullptr && !expect_spiffe_id) {
return 1;
}
if (prop != nullptr && !expect_spiffe_id) {
gpr_log(GPR_ERROR, "SPIFFE ID not expected, but got %s.", prop->value);
return 0;
}
if (prop == nullptr && expect_spiffe_id) {
gpr_log(GPR_ERROR, "SPIFFE ID expected, but got nullptr.");
return 0;
}
if (strncmp(prop->value, expected_spiffe_id, prop->value_length) != 0) {
gpr_log(GPR_ERROR, "Expected SPIFFE ID %s but got %s.", expected_spiffe_id,
prop->value);
return 0;
}
if (grpc_auth_property_iterator_next(&it) != nullptr) {
gpr_log(GPR_ERROR, "Expected only one property for SPIFFE ID.");
return 0;
}
return 1;
}
static void test_cn_only_ssl_peer_to_auth_context(void) {
tsi_peer peer;
tsi_peer rpeer;
@ -415,6 +444,71 @@ static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context(
ctx.reset(DEBUG_LOCATION, "test");
}
static void test_spiffe_id_peer_to_auth_context(void) {
// Invalid SPIFFE IDs should not be plumbed.
std::string long_id(2050, 'x');
std::string long_domain(256, 'x');
tsi_peer invalid_peer;
std::vector<std::string> invalid_spiffe_id = {
"",
"spi://",
"sfiffe://domain/wl",
"spiffe://domain",
"spiffe://domain/",
long_id,
"spiffe://" + long_domain + "/wl"};
size_t i;
GPR_ASSERT(tsi_construct_peer(invalid_spiffe_id.size(), &invalid_peer) ==
TSI_OK);
for (i = 0; i < invalid_spiffe_id.size(); i++) {
GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
TSI_X509_URI_PEER_PROPERTY, invalid_spiffe_id[i].c_str(),
&invalid_peer.properties[i]) == TSI_OK);
}
grpc_core::RefCountedPtr<grpc_auth_context> invalid_ctx =
grpc_ssl_peer_to_auth_context(&invalid_peer,
GRPC_SSL_TRANSPORT_SECURITY_TYPE);
GPR_ASSERT(invalid_ctx != nullptr);
GPR_ASSERT(check_spiffe_id(invalid_ctx.get(), "", false));
tsi_peer_destruct(&invalid_peer);
invalid_ctx.reset(DEBUG_LOCATION, "test");
// A valid SPIFFE ID with other URI fields should be plumbed.
tsi_peer valid_peer;
std::vector<std::string> valid_spiffe_id = {"spiffe://foo.bar.com/wl",
"https://xyz"};
GPR_ASSERT(tsi_construct_peer(valid_spiffe_id.size(), &valid_peer) == TSI_OK);
for (i = 0; i < valid_spiffe_id.size(); i++) {
GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
TSI_X509_URI_PEER_PROPERTY, valid_spiffe_id[i].c_str(),
&valid_peer.properties[i]) == TSI_OK);
}
grpc_core::RefCountedPtr<grpc_auth_context> valid_ctx =
grpc_ssl_peer_to_auth_context(&valid_peer,
GRPC_SSL_TRANSPORT_SECURITY_TYPE);
GPR_ASSERT(valid_ctx != nullptr);
GPR_ASSERT(check_spiffe_id(valid_ctx.get(), "spiffe://foo.bar.com/wl", true));
tsi_peer_destruct(&valid_peer);
valid_ctx.reset(DEBUG_LOCATION, "test");
// Multiple SPIFFE IDs should not be plumbed.
tsi_peer multiple_peer;
std::vector<std::string> multiple_spiffe_id = {
"spiffe://foo.bar.com/wl", "https://xyz", "spiffe://foo.bar.com/wl2"};
GPR_ASSERT(tsi_construct_peer(multiple_spiffe_id.size(), &multiple_peer) ==
TSI_OK);
for (i = 0; i < multiple_spiffe_id.size(); i++) {
GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
TSI_X509_URI_PEER_PROPERTY, multiple_spiffe_id[i].c_str(),
&multiple_peer.properties[i]) == TSI_OK);
}
grpc_core::RefCountedPtr<grpc_auth_context> multiple_ctx =
grpc_ssl_peer_to_auth_context(&multiple_peer,
GRPC_SSL_TRANSPORT_SECURITY_TYPE);
GPR_ASSERT(multiple_ctx != nullptr);
GPR_ASSERT(check_spiffe_id(multiple_ctx.get(), "", false));
tsi_peer_destruct(&multiple_peer);
multiple_ctx.reset(DEBUG_LOCATION, "test");
}
static const char* roots_for_override_api = "roots for override api";
static grpc_ssl_roots_override_result override_roots_success(
@ -562,6 +656,7 @@ int main(int argc, char** argv) {
test_cn_and_one_san_ssl_peer_to_auth_context();
test_cn_and_multiple_sans_ssl_peer_to_auth_context();
test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context();
test_spiffe_id_peer_to_auth_context();
test_ipv6_address_san();
test_default_ssl_roots();
test_peer_alpn_check();

Loading…
Cancel
Save