Merge pull request #3158 from tbetbetbe/grpc-ruby-server-support-multiple-certs

Grpc ruby server add support for multiple certs
pull/3162/head
Michael Lumish 10 years ago
commit 519e27d845
  1. 3
      src/ruby/bin/math_server.rb
  2. 3
      src/ruby/bin/noproto_server.rb
  3. 23
      src/ruby/ext/grpc/rb_server.c
  4. 125
      src/ruby/ext/grpc/rb_server_credentials.c
  5. 3
      src/ruby/pb/test/server.rb
  6. 17
      src/ruby/spec/client_server_spec.rb
  7. 14
      src/ruby/spec/credentials_spec.rb
  8. 2
      src/ruby/spec/generic/active_call_spec.rb
  9. 2
      src/ruby/spec/generic/client_stub_spec.rb
  10. 2
      src/ruby/spec/generic/rpc_server_spec.rb
  11. 2
      src/ruby/spec/pb/health/checker_spec.rb
  12. 39
      src/ruby/spec/server_credentials_spec.rb
  13. 12
      src/ruby/spec/server_spec.rb

@ -171,7 +171,8 @@ end
def test_server_creds
certs = load_test_certs
GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
GRPC::Core::ServerCredentials.new(
nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
end
def main

@ -77,7 +77,8 @@ end
def test_server_creds
certs = load_test_certs
GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
GRPC::Core::ServerCredentials.new(
nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
end
def main

@ -49,6 +49,9 @@ static VALUE grpc_rb_cServer = Qnil;
/* id_at is the constructor method of the ruby standard Time class. */
static ID id_at;
/* id_insecure_server is used to indicate that a server is insecure */
static VALUE id_insecure_server;
/* grpc_rb_server wraps a grpc_server. It provides a peer ruby object,
'mark' to minimize copying when a server is created from ruby. */
typedef struct grpc_rb_server {
@ -314,7 +317,7 @@ static VALUE grpc_rb_server_destroy(int argc, VALUE *argv, VALUE self) {
call-seq:
// insecure port
insecure_server = Server.new(cq, {'arg1': 'value1'})
insecure_server.add_http2_port('mydomain:50051')
insecure_server.add_http2_port('mydomain:50051', :this_port_is_insecure)
// secure port
server_creds = ...
@ -322,21 +325,22 @@ static VALUE grpc_rb_server_destroy(int argc, VALUE *argv, VALUE self) {
secure_server.add_http_port('mydomain:50051', server_creds)
Adds a http2 port to server */
static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
VALUE port = Qnil;
VALUE rb_creds = Qnil;
static VALUE grpc_rb_server_add_http2_port(VALUE self, VALUE port,
VALUE rb_creds) {
grpc_rb_server *s = NULL;
grpc_server_credentials *creds = NULL;
int recvd_port = 0;
/* "11" == 1 mandatory args, 1 (rb_creds) is optional */
rb_scan_args(argc, argv, "11", &port, &rb_creds);
TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
if (s->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "destroyed!");
return Qnil;
} else if (rb_creds == Qnil) {
} else if (TYPE(rb_creds) == T_SYMBOL) {
if (id_insecure_server != SYM2ID(rb_creds)) {
rb_raise(rb_eTypeError,
"bad creds symbol, want :this_port_is_insecure");
return Qnil;
}
recvd_port =
grpc_server_add_insecure_http2_port(s->wrapped, StringValueCStr(port));
if (recvd_port == 0) {
@ -378,8 +382,9 @@ void Init_grpc_server() {
rb_define_alias(grpc_rb_cServer, "close", "destroy");
rb_define_method(grpc_rb_cServer, "add_http2_port",
grpc_rb_server_add_http2_port,
-1);
2);
id_at = rb_intern("at");
id_insecure_server = rb_intern("this_port_is_insecure");
}
/* Gets the wrapped server from the ruby wrapper */

@ -135,63 +135,117 @@ static VALUE grpc_rb_server_credentials_init_copy(VALUE copy, VALUE orig) {
return copy;
}
/* The attribute used on the mark object to hold the pem_root_certs. */
/* The attribute used on the mark object to preserve the pem_root_certs. */
static ID id_pem_root_certs;
/* The attribute used on the mark object to hold the pem_private_key. */
static ID id_pem_private_key;
/* The attribute used on the mark object to preserve the pem_key_certs */
static ID id_pem_key_certs;
/* The attribute used on the mark object to hold the pem_private_key. */
static ID id_pem_cert_chain;
/* The key used to access the pem cert in a key_cert pair hash */
static VALUE sym_cert_chain;
/* The key used to access the pem private key in a key_cert pair hash */
static VALUE sym_private_key;
/*
call-seq:
creds = ServerCredentials.new(pem_root_certs, pem_private_key,
pem_cert_chain)
creds = ServerCredentials.new(nil, pem_private_key,
pem_cert_chain)
pem_root_certs: (required) PEM encoding of the server root certificate
pem_private_key: (optional) PEM encoding of the server's private key
pem_cert_chain: (optional) PEM encoding of the server's cert chain
creds = ServerCredentials.new(nil,
[{private_key: <pem_private_key1>,
{cert_chain: <pem_cert_chain1>}],
force_client_auth)
creds = ServerCredentials.new(pem_root_certs,
[{private_key: <pem_private_key1>,
{cert_chain: <pem_cert_chain1>}],
force_client_auth)
pem_root_certs: (optional) PEM encoding of the server root certificate
pem_private_key: (required) PEM encoding of the server's private keys
force_client_auth: indicatees
Initializes ServerCredential instances. */
static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
VALUE pem_private_key,
VALUE pem_cert_chain) {
/* TODO support multiple key cert pairs in the ruby API. */
VALUE pem_key_certs,
VALUE force_client_auth) {
grpc_rb_server_credentials *wrapper = NULL;
grpc_server_credentials *creds = NULL;
grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
TypedData_Get_Struct(self, grpc_rb_server_credentials,
&grpc_rb_server_credentials_data_type, wrapper);
if (pem_cert_chain == Qnil) {
rb_raise(rb_eRuntimeError,
"could not create a server credential: nil pem_cert_chain");
grpc_ssl_pem_key_cert_pair *key_cert_pairs = NULL;
VALUE cert = Qnil;
VALUE key = Qnil;
VALUE key_cert = Qnil;
int auth_client = 0;
int num_key_certs = 0;
int i;
if (NIL_P(force_client_auth) ||
!(force_client_auth == Qfalse || force_client_auth == Qtrue)) {
rb_raise(rb_eTypeError,
"bad force_client_auth: got:<%s> want: <True|False|nil>",
rb_obj_classname(force_client_auth));
return Qnil;
} else if (pem_private_key == Qnil) {
rb_raise(rb_eRuntimeError,
"could not create a server credential: nil pem_private_key");
}
if (NIL_P(pem_key_certs) || TYPE(pem_key_certs) != T_ARRAY) {
rb_raise(rb_eTypeError, "bad pem_key_certs: got:<%s> want: <Array>",
rb_obj_classname(pem_key_certs));
return Qnil;
}
num_key_certs = RARRAY_LEN(pem_key_certs);
if (num_key_certs == 0) {
rb_raise(rb_eTypeError, "bad pem_key_certs: it had no elements");
return Qnil;
}
key_cert_pair.private_key = RSTRING_PTR(pem_private_key);
key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain);
/* TODO Add a force_client_auth parameter and pass it here. */
for (i = 0; i < num_key_certs; i++) {
key_cert = rb_ary_entry(pem_key_certs, i);
if (key_cert == Qnil) {
rb_raise(rb_eTypeError,
"could not create a server credential: nil key_cert");
return Qnil;
} else if (TYPE(key_cert) != T_HASH) {
rb_raise(rb_eTypeError,
"could not create a server credential: want <Hash>, got <%s>",
rb_obj_classname(key_cert));
return Qnil;
} else if (rb_hash_aref(key_cert, sym_private_key) == Qnil) {
rb_raise(rb_eTypeError,
"could not create a server credential: want nil private key");
return Qnil;
} else if (rb_hash_aref(key_cert, sym_cert_chain) == Qnil) {
rb_raise(rb_eTypeError,
"could not create a server credential: want nil cert chain");
return Qnil;
}
}
auth_client = TYPE(force_client_auth) == T_TRUE;
key_cert_pairs = ALLOC_N(grpc_ssl_pem_key_cert_pair, num_key_certs);
for (i = 0; i < num_key_certs; i++) {
key_cert = rb_ary_entry(pem_key_certs, i);
key = rb_hash_aref(key_cert, sym_private_key);
cert = rb_hash_aref(key_cert, sym_cert_chain);
key_cert_pairs[i].private_key = RSTRING_PTR(key);
key_cert_pairs[i].cert_chain = RSTRING_PTR(cert);
}
TypedData_Get_Struct(self, grpc_rb_server_credentials,
&grpc_rb_server_credentials_data_type, wrapper);
if (pem_root_certs == Qnil) {
creds =
grpc_ssl_server_credentials_create(NULL, &key_cert_pair, 1, 0, NULL);
creds = grpc_ssl_server_credentials_create(NULL, key_cert_pairs,
num_key_certs,
auth_client, NULL);
} else {
creds = grpc_ssl_server_credentials_create(RSTRING_PTR(pem_root_certs),
&key_cert_pair, 1, 0, NULL);
key_cert_pairs, num_key_certs,
auth_client, NULL);
}
xfree(key_cert_pairs);
if (creds == NULL) {
rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
return Qnil;
}
wrapper->wrapped = creds;
/* Add the input objects as hidden fields to preserve them. */
rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain);
rb_ivar_set(self, id_pem_private_key, pem_private_key);
rb_ivar_set(self, id_pem_key_certs, pem_key_certs);
rb_ivar_set(self, id_pem_root_certs, pem_root_certs);
return self;
@ -211,9 +265,10 @@ void Init_grpc_server_credentials() {
rb_define_method(grpc_rb_cServerCredentials, "initialize_copy",
grpc_rb_server_credentials_init_copy, 1);
id_pem_cert_chain = rb_intern("__pem_cert_chain");
id_pem_private_key = rb_intern("__pem_private_key");
id_pem_key_certs = rb_intern("__pem_key_certs");
id_pem_root_certs = rb_intern("__pem_root_certs");
sym_private_key = ID2SYM(rb_intern("private_key"));
sym_cert_chain = ID2SYM(rb_intern("cert_chain"));
}
/* Gets the wrapped grpc_server_credentials from the ruby wrapper */

@ -64,7 +64,8 @@ end
# creates a ServerCredentials from the test certificates.
def test_server_creds
certs = load_test_certs
GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
GRPC::Core::ServerCredentials.new(
nil, [{private_key: certs[1], cert_chain: certs[2]}], false)
end
# produces a string of null chars (\0) of length l.

@ -32,12 +32,6 @@ require 'spec_helper'
include GRPC::Core
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.key', 'server1.pem']
files.map { |f| File.open(File.join(test_root, f)).read }
end
shared_context 'setup: tags' do
let(:sent_message) { 'sent message' }
let(:reply_text) { 'the reply' }
@ -402,7 +396,7 @@ describe 'the http client/server' do
@client_queue = GRPC::Core::CompletionQueue.new
@server_queue = GRPC::Core::CompletionQueue.new
@server = GRPC::Core::Server.new(@server_queue, nil)
server_port = @server.add_http2_port(server_host)
server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
@server.start
@ch = Channel.new("0.0.0.0:#{server_port}", nil)
end
@ -420,12 +414,19 @@ describe 'the http client/server' do
end
describe 'the secure http client/server' do
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.key', 'server1.pem']
files.map { |f| File.open(File.join(test_root, f)).read }
end
before(:example) do
certs = load_test_certs
server_host = '0.0.0.0:0'
@client_queue = GRPC::Core::CompletionQueue.new
@server_queue = GRPC::Core::CompletionQueue.new
server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
server_creds = GRPC::Core::ServerCredentials.new(
nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
@server = GRPC::Core::Server.new(@server_queue, nil)
server_port = @server.add_http2_port(server_host, server_creds)
@server.start

@ -29,15 +29,15 @@
require 'grpc'
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.pem', 'server1.key']
files.map { |f| File.open(File.join(test_root, f)).read }
end
describe GRPC::Core::Credentials do
Credentials = GRPC::Core::Credentials
Credentials = GRPC::Core::Credentials
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.pem', 'server1.key']
files.map { |f| File.open(File.join(test_root, f)).read }
end
describe Credentials do
describe '#new' do
it 'can be constructed with fake inputs' do
expect { Credentials.new('root_certs', 'key', 'cert') }.not_to raise_error

@ -46,7 +46,7 @@ describe GRPC::ActiveCall do
@server_queue = GRPC::Core::CompletionQueue.new
host = '0.0.0.0:0'
@server = GRPC::Core::Server.new(@server_queue, nil)
server_port = @server.add_http2_port(host)
server_port = @server.add_http2_port(host, :this_port_is_insecure)
@server.start
@ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil)
end

@ -498,7 +498,7 @@ describe 'ClientStub' do
def create_test_server
@server_queue = GRPC::Core::CompletionQueue.new
@server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port('0.0.0.0:0')
@server.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
end
def expect_server_to_be_invoked(notifier)

@ -139,7 +139,7 @@ describe GRPC::RpcServer do
@server_queue = GRPC::Core::CompletionQueue.new
server_host = '0.0.0.0:0'
@server = GRPC::Core::Server.new(@server_queue, nil)
server_port = @server.add_http2_port(server_host)
server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
@host = "localhost:#{server_port}"
@ch = GRPC::Core::Channel.new(@host, nil)
end

@ -186,7 +186,7 @@ describe Grpc::Health::Checker do
@server_queue = GRPC::Core::CompletionQueue.new
server_host = '0.0.0.0:0'
@server = GRPC::Core::Server.new(@server_queue, nil)
server_port = @server.add_http2_port(server_host)
server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
@host = "localhost:#{server_port}"
@ch = GRPC::Core::Channel.new(@host, nil)
@client_opts = { channel_override: @ch }

@ -31,8 +31,9 @@ require 'grpc'
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.pem', 'server1.key']
files.map { |f| File.open(File.join(test_root, f)).read }
files = ['ca.pem', 'server1.key', 'server1.pem']
contents = files.map { |f| File.open(File.join(test_root, f)).read }
[contents[0], [{ private_key: contents[1], cert_chain: contents[2] }], false]
end
describe GRPC::Core::ServerCredentials do
@ -40,7 +41,8 @@ describe GRPC::Core::ServerCredentials do
describe '#new' do
it 'can be constructed from a fake CA PEM, server PEM and a server key' do
expect { Creds.new('a', 'b', 'c') }.not_to raise_error
creds = Creds.new('a', [{ private_key: 'a', cert_chain: 'b' }], false)
expect(creds).to_not be_nil
end
it 'can be constructed using the test certificates' do
@ -48,21 +50,44 @@ describe GRPC::Core::ServerCredentials do
expect { Creds.new(*certs) }.not_to raise_error
end
it 'cannot be constructed without a nil key_cert pair array' do
root_cert, _, _ = load_test_certs
blk = proc do
Creds.new(root_cert, nil, false)
end
expect(&blk).to raise_error
end
it 'cannot be constructed without any key_cert pairs' do
root_cert, _, _ = load_test_certs
blk = proc do
Creds.new(root_cert, [], false)
end
expect(&blk).to raise_error
end
it 'cannot be constructed without a server cert chain' do
root_cert, server_key, _ = load_test_certs
blk = proc { Creds.new(root_cert, server_key, nil) }
blk = proc do
Creds.new(root_cert,
[{ server_key: server_key, cert_chain: nil }],
false)
end
expect(&blk).to raise_error
end
it 'cannot be constructed without a server key' do
root_cert, _, _ = load_test_certs
blk = proc { Creds.new(root_cert, nil, cert_chain) }
blk = proc do
Creds.new(root_cert,
[{ server_key: nil, cert_chain: cert_chain }])
end
expect(&blk).to raise_error
end
it 'can be constructed without a root_cret' do
_, server_key, cert_chain = load_test_certs
blk = proc { Creds.new(nil, server_key, cert_chain) }
_, cert_pairs, _ = load_test_certs
blk = proc { Creds.new(nil, cert_pairs, false) }
expect(&blk).to_not raise_error
end
end

@ -32,7 +32,8 @@ require 'grpc'
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.key', 'server1.pem']
files.map { |f| File.open(File.join(test_root, f)).read }
contents = files.map { |f| File.open(File.join(test_root, f)).read }
[contents[0], [{ private_key: contents[1], cert_chain: contents[2] }], false]
end
Server = GRPC::Core::Server
@ -104,7 +105,7 @@ describe Server do
it 'runs without failing' do
blk = proc do
s = Server.new(@cq, nil)
s.add_http2_port('localhost:0')
s.add_http2_port('localhost:0', :this_port_is_insecure)
s.close(@cq)
end
expect(&blk).to_not raise_error
@ -113,7 +114,10 @@ describe Server do
it 'fails if the server is closed' do
s = Server.new(@cq, nil)
s.close(@cq)
expect { s.add_http2_port('localhost:0') }.to raise_error(RuntimeError)
blk = proc do
s.add_http2_port('localhost:0', :this_port_is_insecure)
end
expect(&blk).to raise_error(RuntimeError)
end
end
@ -198,7 +202,7 @@ describe Server do
def start_a_server
s = Server.new(@cq, nil)
s.add_http2_port('0.0.0.0:0')
s.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
s.start
s
end

Loading…
Cancel
Save