Skip to content

Commit

Permalink
Add TLS support for prototool grpc command (uber#364)
Browse files Browse the repository at this point in the history
  • Loading branch information
yinzara authored and bufdev committed Mar 9, 2019
1 parent b34b2f1 commit 2609cdd
Show file tree
Hide file tree
Showing 26 changed files with 848 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add file locking around the `protoc` downloader to eliminate concurrency
issues where multiple `prototool` invocations may be accessing the cache
at the same time.
- Add TLS support to the `grpc` command.
- Add `--details` flag to the `grpc` command to output headers, trailers,
and statuses as well as the responses.
- Unix domain sockets can now be specified for the `--address` flag of the
Expand Down
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ $(PROTOC_GEN_GO):
@cd $(PROTOC_GEN_GO_TMP); go get github.com/golang/protobuf/protoc-gen-go@$(PROTOC_GEN_GO_VERSION)
@rm -rf $(PROTOC_GEN_GO_TMP)

CERTSTRAP_VERSION := v1.1.1
CERTSTRAP := $(TMP_BIN)/certstrap
$(CERTSTRAP):
$(eval CERTSTRAP_TMP := $(shell mktemp -d))
@cd $(CERTSTRAP_TMP); go get github.com/square/certstrap@$(CERTSTRAP_VERSION)
@rm -rf $(CERTSTRAP_TMP)

unexport GOPATH
export GO111MODULE := on
export GOBIN := $(abspath $(TMP_BIN))
Expand Down Expand Up @@ -131,6 +138,10 @@ internalgen: install $(PROTOC_GEN_GO)
bazelgen: $(BAZEL)
bash etc/bin/bazelgen.sh

.PHONY: grpcgen
grpcgen: $(CERTSTRAP)
bash etc/bin/grpcgen.sh

.PHONY: generate
generate: license golden example internalgen bazelgen
go mod tidy -v
Expand Down
45 changes: 45 additions & 0 deletions docs/grpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ The `prototool grpc` command calls a gRPC endpoint using a JSON input. What this

All these steps take on the order of milliseconds, for example the overhead for a file with four dependencies is about 30ms, so there is little overhead for CLI calls to gRPC.

## Example

There is a full example for gRPC in the [example](../example) directory. Run `make example` to make sure everything is installed and generated.

Start the example server in a separate terminal by doing `go run example/cmd/excited/main.go`.
Expand Down Expand Up @@ -68,3 +70,46 @@ $ prototool grpc example \
{"response":{"value":"o"}}
{"response":{"value":"!"}}
```

## TLS Connections

To enable TLS connections to the server, use the `--tls` command line flag.

If not specified, setting any of the following flags will produce an error.

The following flags mirror the flags of the same names from from the `curl` command line tool.

By default host validation is enabled. To disable, pass the `--insecure` command line flag. If using this tool in any
a production setting (which is not recommended to begin with), it is HIGHLY recommended not to use the `--insecure`
flag as it allows for multiple network exploits.

If `--insecure` is specified, setting any of the following flags will produce an error.

If the certificate presented by the server is not generated by a system trusted CA, you must also set the `--cacert`
flag to a pem encoded file of the CA public certificate that generated the server certificate or the server public
certificate itself. The cacert file may contain multiple certificates or certificate authorities appended together
(new line separated). You must also include any intermediary certificates if required to validate the server
certificate against the given CA certificate (generally only true in commercially generated certificates,
not self signed ones).

By default, the "Common Name" (and any "Subject Alternative Names") will be used to validate the authenticity of the
server connection based upon the `--address` specified. If your server certificate's common name does not match the
address you are accessing it from (for example, if your address is an IP address), pass `--server-name` command line
flag with the value expected in the "Common Name" field of the server certificate.

To use mutual TLS with a client key and certificate, pass the `--key` and `--cert` command line flag to the path of
the files in pem format. If one is specified the other must also be specified. Unlike curl, you cannot pass a file with
the key and cert concatenated together as the `--cert` flag. Also unlike curl, you cannot pass the `-E` flag as an alias
for the `--cert` flag.

```bash
$ prototool grpc path/to/root \
--address 192.168.1.15:443 \
--method uber.foo.v1.BarAPI/Baz \
--data '{"value":"hello"}' \
--tls \
--cacert server.crt \
--cert client.crt \
--key client.key \
--server-name foo.bar.com
```
57 changes: 57 additions & 0 deletions etc/bin/grpcgen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash

set -euo pipefail

DIR="$(cd "$(dirname "${0}")/../.." && pwd)"
cd "${DIR}"

# https://bbengfort.github.io/programmer/2017/03/03/secure-grpc.html

check_which() {
if ! command -v "${1}" >/dev/null 2>/dev/null; then
echo "${1} is not installed" >&2
exit1
fi
}

check_which openssl
check_which certstrap

cd internal/cmd/testdata/grpc
rm -rf tls

certstrap --depot-path tls init --common-name "cacert" --passphrase ""
certstrap --depot-path tls request-cert --ip 127.0.0.1 --cn server --passphrase ""
certstrap --depot-path tls sign server --CA cacert --expires "100 years"
certstrap --depot-path tls request-cert --cn client --passphrase ""
certstrap --depot-path tls sign client --CA cacert --expires "100 years"

openssl req -new -newkey rsa:2048 -x509 -sha256 -nodes -days 36500 -extensions v3_req \
-keyout tls/self-signed-server.key -out tls/self-signed-server.crt \
-config <(cat <<-EOF
[req]
default_bits = 2048
req_extensions = v3_req
prompt = no
distinguished_name = dn
[ dn ]
C=US
ST=Denial
L=Springfield
O=Dis
[email protected]
CN = server.local
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[ alt_names ]
IP.1 = 127.0.0.1
EOF
)
openssl req -new -newkey rsa:2048 -x509 -sha256 -nodes -days 36500 \
-subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=client.local" \
-keyout tls/self-signed-client.key -out tls/self-signed-client.crt
1 change: 1 addition & 0 deletions internal/cmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ go_test(
"@com_github_stretchr_testify//require:go_default_library",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//credentials:go_default_library",
],
)
Loading

0 comments on commit 2609cdd

Please sign in to comment.