-
Notifications
You must be signed in to change notification settings - Fork 251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds gRPC Server-side TLS Support #204
Conversation
I don't think we should put self signed certs in the release docker image maybe it can be a volume/mount like the json data dir ? for testing we can have indeed a script generating a sample |
Thank you for taking a look at the PR.
Agreed, that is why I did not update release/Dockerfile.
I'm not familiar with how the json data dir is mounted. Do you have a pointer?
Agreed, I wanted to make sure we can perform e2e testing on the feature. This is why I updated the non-release Docker image. Maybe I don't understand how the non-release and release images work with one another. |
I haven't yet, really. will now
https://github.com/istio/fortio/blob/master/Dockerfile#L33
Dockerfile is the release for istio/fortio |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tbh in addition to all the technical issues/details below,
I'm really not a fan of adding all this code - it might grow on me though once simplified and working but... not sure.... I'll give it some more thoughts
Dockerfile
Outdated
@@ -20,10 +20,17 @@ RUN /usr/local/go/bin/go version | |||
RUN CGO_ENABLED=0 GOOS=darwin /usr/local/go/bin/go build -a -ldflags -s -o fortio.go18.mac istio.io/fortio | |||
# Just check it stays compiling on Windows (would need to set the rsrcDir too) | |||
RUN CGO_ENABLED=0 GOOS=windows go build -a -o fortio.exe istio.io/fortio | |||
# Generate TLS assets for secure grpc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undo all this
ftls/ftls.go
Outdated
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package ftls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need a brand new top level package just for this
it can be a file inside fnet
ftls/ftls.go
Outdated
|
||
// NewCertPool creates an x509 certPool with the provided CA files. | ||
func NewCertPool(CAFiles []string) (*x509.CertPool, error) { | ||
certPool := x509.NewCertPool() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems overly complicated for a testing tool we only need
https://godoc.org/google.golang.org/grpc/credentials#NewClientTLSFromFile
no ?
(though maybe it is worth having this for http tls too)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this would be needed to setup one or more trusted CA for authorizing client/server connections.
fgrpc/grpcrunner.go
Outdated
} | ||
creds := credentials.NewTLS(tlsCfg) | ||
opts = grpc.WithTransportCredentials(creds) | ||
case strings.HasPrefix(serverAddr, "https://"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the switch between standard CA vs custom CA should be based on https:// prefix
it's a config option, to be added to grpcoptions
fgrpc/pingsrv.go
Outdated
grpcServer := grpc.NewServer() | ||
var opts []grpc.ServerOption | ||
if secure { | ||
tlsCfg, err := ftls.NewCredentials(false, ftls.DefaultServerCert, ftls.DefaultServerKey, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if they are hard coded what is the point of passing constants.
we need some grpcoptions struct to control tls instead of just 1 boolean
ftls/info.go
Outdated
// ClientConfig returns a tls.Config for client use. | ||
func (info *TLSInfo) ClientConfig() (*tls.Config, error) { | ||
// CA for verifying the server | ||
pool, err := NewCertPool([]string{info.CAFile}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how would you put both the standard CA and additional one in 1 config ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change CAFile
to be of type []string
that represent one or more paths to trusted CA certs. Provide the slice to NewCertPool
. Create a cmdline flag that allows a user to specify multiple comma-separated cert paths. Is that acceptable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd rather have just the NewClientTLSFromFile for now I think; and less code
Codecov Report
@@ Coverage Diff @@
## master #204 +/- ##
========================================
+ Coverage 90.2% 90.5% +0.2%
========================================
Files 10 10
Lines 1906 1899 -7
========================================
- Hits 1720 1718 -2
+ Misses 119 116 -3
+ Partials 67 65 -2
Continue to review full report at Codecov.
|
@ldemailly Thanks for your review. I addressed your comments and have updated the PR. The purpose of this PR is to address |
OK. I'll strip most of this stuff out and update the PR. |
Sorry been crazy busy... will look more soon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's use --key --cert --cacert
like curl
and
https://istio.io/docs/tasks/security/mutual-tls.html#testing-the-authentication-setup
README.md
Outdated
@@ -77,8 +77,12 @@ target is a url (http load tests) or host:port (grpc health test). flags are: | |||
-grpc-port string | |||
grpc server port. Can be in the form of host:port, ip:port or port. | |||
(default "8079") | |||
-grpc-secure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we still need a way to standard TLS no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR provides backwards compatibility. Instead of passing the -grpc-secure
bool, Dial
will use the one of the following:
A. The cert from -server-cert
if it's provided. I can easily add the flag back in, but IMO it no longer adds value.
B. Us nil
creds if dest is an https prefix (this provides backwards compatibility)
C. Insecure Dial if A or C do not apply.
I thought this is a cleaner approach than setting the bool and cert/key flags. IMO having both sets of flags causes user confusion. Using the legacy bool flag made sense, a user flips the secure/insecure switch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not following - how do I get the previous behavior exactly ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous behavior is accomplished by using an https prefix with the grpc destination.
For example:
grpc client
fortio grpcping https://fortio.istio.io
or any client that uses GRPCRunnerOptions
fortio curl -loglevel debug http://localhost:8080/fortio/fetch/localhost:8080/fortio/?url=https://fortio.istio.io:443&load=Start&qps=-1&json=on&n=100&runner=grpc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ic I missed that - does it work with fortio load -grpc https://... too ? (it should I guess as destination is passed down unchanged if I recall)
we should put tls/no tls in the (json) result object
we should probably document that behavior somewhere (that grpc host:port is insecure unless a cert is given and https://host:port is secure)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it work with fortio load -grpc https://... too ?
It does. I extensively tested this patch today. I documented the details here.
we should probably document that behavior somewhere (that grpc host:port is insecure unless a cert is given and https://host:port is secure)
I updated the Readme.
fortio_main.go
Outdated
goMaxProcsFlag = flag.Int("gomaxprocs", 0, "Setting for runtime.GOMAXPROCS, <1 doesn't change the default") | ||
profileFlag = flag.String("profile", "", "write .cpu and .mem profiles to file") | ||
grpcFlag = flag.Bool("grpc", false, "Use GRPC (health check) for load testing") | ||
serverCertFlag = flag.String("server-cert", "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a new line to minimize the diff
(go can be annoying with reformatting to align - also as mentioned I think the grpc-secure flag is still needed, the cert,ca,key are optional)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do. For clarity, the ca flag was dropped since refactoring this PR based on your initial feedback.
fortio_main.go
Outdated
@@ -162,7 +165,7 @@ func main() { | |||
} | |||
case "server": | |||
isServer = true | |||
fgrpc.PingServer(*grpcPortFlag, fgrpc.DefaultHealthServiceName) | |||
fgrpc.PingServer(*grpcPortFlag, *serverCertFlag, *serverKeyFlag, fgrpc.DefaultHealthServiceName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking we should add some GRPC(Server)Options instead of 5 string params
(shared between the client and server maybe)
but maybe that can be a followup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to address that in a follow-on PR. This PR has been around for a while. I opened issue
#220 and assigned myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k, ty
fortio_main.go
Outdated
@@ -249,7 +252,7 @@ func fortioLoad(justCurl bool, percList []float64) { | |||
o := fgrpc.GRPCRunnerOptions{ | |||
RunnerOptions: ro, | |||
Destination: url, | |||
Secure: *grpcSecureFlag, | |||
Cert: *serverCertFlag, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the 2 are different
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. See my response above and let me know your thoughts. I want to make sure we're on the same page before I rebase this PR (it was a PITA the last time I rebased).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k
ui/uihandler.go
Outdated
@@ -153,6 +153,7 @@ func Handler(w http.ResponseWriter, r *http.Request) { | |||
qps, _ := strconv.ParseFloat(r.FormValue("qps"), 64) // nolint: gas | |||
durStr := r.FormValue("t") | |||
grpcSecure := (r.FormValue("grpc-secure") == "on") | |||
cert := r.FormValue("cert") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for this I think we should just use the flags (if you want a fortio UI using different cert, start a new one with different cert path)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I see, the challenge with that approach is:
uihandler.go
creates instanceo
of thefgrpc.GRPCRunnerOptions
object .- The flags created in
fortio_main.go
are not available touihandler.go
or any other pkg for that matter. Therefore, theCert
field ino
can not be populated based on any flags.
I believe what you are asking for can be accomplished by putting Fortio flags into a pkg (i.e. cli ) and having fortio_main.go
and any other dependent pkg's import cli
. If this has your interest, please create an Issue, assign me and I'll work on refactoring the Fortio cli into a separate pkg. However, I don't think that work should block this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could also pass those global options to ui.Serve()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, assuming the cli options are in a separate pkg. As I mentioned, I am happy to take on this Fortio cli refactoring. Just create an Issue and assign me. Until then, I think providing a field to enter the cert path is a reasonable option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the follow-up review!
README.md
Outdated
@@ -77,8 +77,12 @@ target is a url (http load tests) or host:port (grpc health test). flags are: | |||
-grpc-port string | |||
grpc server port. Can be in the form of host:port, ip:port or port. | |||
(default "8079") | |||
-grpc-secure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR provides backwards compatibility. Instead of passing the -grpc-secure
bool, Dial
will use the one of the following:
A. The cert from -server-cert
if it's provided. I can easily add the flag back in, but IMO it no longer adds value.
B. Us nil
creds if dest is an https prefix (this provides backwards compatibility)
C. Insecure Dial if A or C do not apply.
I thought this is a cleaner approach than setting the bool and cert/key flags. IMO having both sets of flags causes user confusion. Using the legacy bool flag made sense, a user flips the secure/insecure switch.
fortio_main.go
Outdated
goMaxProcsFlag = flag.Int("gomaxprocs", 0, "Setting for runtime.GOMAXPROCS, <1 doesn't change the default") | ||
profileFlag = flag.String("profile", "", "write .cpu and .mem profiles to file") | ||
grpcFlag = flag.Bool("grpc", false, "Use GRPC (health check) for load testing") | ||
serverCertFlag = flag.String("server-cert", "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do. For clarity, the ca flag was dropped since refactoring this PR based on your initial feedback.
fortio_main.go
Outdated
@@ -162,7 +165,7 @@ func main() { | |||
} | |||
case "server": | |||
isServer = true | |||
fgrpc.PingServer(*grpcPortFlag, fgrpc.DefaultHealthServiceName) | |||
fgrpc.PingServer(*grpcPortFlag, *serverCertFlag, *serverKeyFlag, fgrpc.DefaultHealthServiceName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to address that in a follow-on PR. This PR has been around for a while. I opened issue
#220 and assigned myself.
fortio_main.go
Outdated
@@ -249,7 +252,7 @@ func fortioLoad(justCurl bool, percList []float64) { | |||
o := fgrpc.GRPCRunnerOptions{ | |||
RunnerOptions: ro, | |||
Destination: url, | |||
Secure: *grpcSecureFlag, | |||
Cert: *serverCertFlag, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. See my response above and let me know your thoughts. I want to make sure we're on the same page before I rebase this PR (it was a PITA the last time I rebased).
ui/uihandler.go
Outdated
@@ -153,6 +153,7 @@ func Handler(w http.ResponseWriter, r *http.Request) { | |||
qps, _ := strconv.ParseFloat(r.FormValue("qps"), 64) // nolint: gas | |||
durStr := r.FormValue("t") | |||
grpcSecure := (r.FormValue("grpc-secure") == "on") | |||
cert := r.FormValue("cert") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I see, the challenge with that approach is:
uihandler.go
creates instanceo
of thefgrpc.GRPCRunnerOptions
object .- The flags created in
fortio_main.go
are not available touihandler.go
or any other pkg for that matter. Therefore, theCert
field ino
can not be populated based on any flags.
I believe what you are asking for can be accomplished by putting Fortio flags into a pkg (i.e. cli ) and having fortio_main.go
and any other dependent pkg's import cli
. If this has your interest, please create an Issue, assign me and I'll work on refactoring the Fortio cli into a separate pkg. However, I don't think that work should block this PR.
fgrpc/grpcrunner.go
Outdated
log.Infof("Using server certificate %v to construct TLS credentials", cert) | ||
opts = append(opts, grpc.WithTransportCredentials(creds)) | ||
case strings.HasPrefix(serverAddr, "https://"): | ||
creds := credentials.NewTLS(nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ic this is where the https/tls behaviour is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, all the intelligence is in Dial
.
README.md
Outdated
@@ -77,8 +77,12 @@ target is a url (http load tests) or host:port (grpc health test). flags are: | |||
-grpc-port string | |||
grpc server port. Can be in the form of host:port, ip:port or port. | |||
(default "8079") | |||
-grpc-secure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ic I missed that - does it work with fortio load -grpc https://... too ? (it should I guess as destination is passed down unchanged if I recall)
we should put tls/no tls in the (json) result object
we should probably document that behavior somewhere (that grpc host:port is insecure unless a cert is given and https://host:port is secure)
fortio_main.go
Outdated
@@ -162,7 +165,7 @@ func main() { | |||
} | |||
case "server": | |||
isServer = true | |||
fgrpc.PingServer(*grpcPortFlag, fgrpc.DefaultHealthServiceName) | |||
fgrpc.PingServer(*grpcPortFlag, *serverCertFlag, *serverKeyFlag, fgrpc.DefaultHealthServiceName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k, ty
fortio_main.go
Outdated
@@ -249,7 +252,7 @@ func fortioLoad(justCurl bool, percList []float64) { | |||
o := fgrpc.GRPCRunnerOptions{ | |||
RunnerOptions: ro, | |||
Destination: url, | |||
Secure: *grpcSecureFlag, | |||
Cert: *serverCertFlag, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k
fortio_main.go
Outdated
@@ -312,20 +315,19 @@ func fortioLoad(justCurl bool, percList []float64) { | |||
} | |||
|
|||
func grpcClient() { | |||
if len(flag.Args()) != 1 { | |||
if len(flag.Args()) < 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which case is that change for ?
fgrpc/pingsrv_test.go
Outdated
iPort := PingServer("0", "", "", "foo") | ||
iAddr := fmt.Sprintf("localhost:%d", iPort) | ||
t.Logf("test insecure grpc ping server running, will connect to %s", iAddr) | ||
sPort := PingServer("0", "../testdata/server.crt", "../testdata/server.key", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this relative path making success dependent on where it is ran from ? (somehow doubt it works from both parent or different directories?)
not sure we need a new directory - tbd if go testing has provision for files relative to test files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested by copying the cert/key from the testdata directory to the project's root directory and updating the following in grpcrunner_test.go
:
svrCrt = "../server.crt"
svrKey = "../server.key"
The tests passed.
not sure we need a new directory - tbd if go testing has provision for files relative to test files?
I like the idea of storing all test data for a project in a specific directory. I think it's clean and makes it very apparent to devs. Let me know where I should move the test cert/key if you want me to remove the directory.
this is good, do you have time to raise the coverage ? (for instance pass invalid credentials, connect to the wrong port/settings,...) |
where is -cacert ? I see in the example you pass a cert to the client, I assume that implies it expects that cert, but it should be a CA instead ? |
README.md
Outdated
Use secure transport (tls) for GRPC | ||
-cert | ||
Full path to the server certificate required for secure grpc. | ||
Used by grpcping and server. (default "") means secure grpc is disabled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add:
"unless https:// destination is used, in which case standard TLS is expected (with valid server certificates signed by the internet standard CAs)"
or some such
fgrpc/grpcrunner.go
Outdated
//var creds credentials.TransportCredentials | ||
switch { | ||
case cert != "": | ||
creds, err := credentials.NewClientTLSFromFile(cert, "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder here if we're confusing client and server cert
we should be able to do mTLS by providing an optional client cert; and expect a server cert signed by a custom CA
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the name of this PR to properly reflect the implemented functionality. This PR originally provided mTLS, but I simplified things based on your initial feedback. This PR will allow clients (i.e. grpcping) to authenticate the Fortio gRPC server and encrypt the data exchanged. In my original PR, the server cert was fetched from the CA (Fortio server). Since refactoring, the server cert must be shipped with the client-side application. This blog provides details of the original and current approach for this PR. Let me know how you would like to proceed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section provides a detailed explanation of the basic TLS handshake implemented by this PR.
@ldemailly will this PR be ready to merge after I fix the conflicts? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
almost, see below
Makefile
Outdated
# go test ./... and others run in vendor/ and cause problems (!) | ||
PACKAGES:=$(shell find . -type d -print | egrep -v "/(\.|vendor|static|templates|release|docs|json)") | ||
PACKAGES:=$(shell find . -type d -print | egrep -v "/(\.|vendor|static|templates|release|docs|json|cert-tmp)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$(CERT_TEMP) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
Makefile
Outdated
@@ -14,25 +14,41 @@ TAG:=$(USER)$(shell date +%y%m%d_%H%M%S) | |||
|
|||
DOCKER_TAG = $(DOCKER_PREFIX)$(IMAGE):$(TAG) | |||
|
|||
CERT_TEMP := ./cert-tmp/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CERT_TEMP_DIR := ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
README.md
Outdated
UI starting - visit: | ||
http://localhost:8080/fortio/ | ||
Https redirector running on :8081 | ||
Fortio 0.9.0 grpc ping server listening on port :8079 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when merging make sure there is 0.9.1 everywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like docs are now using 0.10.0. I have changed to 0.10.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's actually 0.10.1 now :-)
README.md
Outdated
* A `grpcping` using TLS. First, start Fortio server with the `-cert` and `-key` flags. | ||
`/path/to/fortio/server.crt` and `/path/to/fortio/server.key` are paths to the TLS certificate and key that | ||
you are responsible for providing. Creating a TLS certificate is outside the scope of this document. This example uses | ||
`localhost` in the `Subject Alternative Name` (SAN) of the certificate. The SAN should match the domain name that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove that part (localhost san) as it doesn't matter / is also out of scope ? - or mentioned the makefile can generate test only certs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
README.md
Outdated
Next, run the `load` command with the `-cacert` flag: | ||
``` | ||
$ fortio load -cacert /etc/ssl/certs/ca.crt -grpc localhost:8079 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing close 3 ` - please make sure you preview/look over the changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Webtest.sh
Outdated
CERT=/etc/ssl/certs/ca-certificates.crt | ||
TEST_CERT_VOL=/etc/ssl/certs/fortio | ||
BUILD_IMAGE_TAG=v7 | ||
BUILD_IMAGE=istio/fortio.build:$BUILD_IMAGE_TAG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change so it works with https://github.com/istio/fortio/blob/master/Makefile#L112
ie: add the file name to the list and use istio/fortio.build:v7 directly so sed finds it when we go to v8
@@ -78,8 +78,10 @@ var ( | |||
goMaxProcsFlag = flag.Int("gomaxprocs", 0, "Setting for runtime.GOMAXPROCS, <1 doesn't change the default") | |||
profileFlag = flag.String("profile", "", "write .cpu and .mem profiles to file") | |||
grpcFlag = flag.Bool("grpc", false, "Use GRPC (health check by default, add -ping for ping) for load testing") | |||
grpcSecureFlag = flag.Bool("grpc-secure", false, "Use secure transport (tls) for GRPC") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that change isn't reflected in the README.md
see release/updateFlags.sh for how to update
we also need to document somewhere that https:// replaces -grpc-secure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the readme to remove the -secure-grpc flag and ran the updateFlags.sh script.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@ldemailly thanks for the feedback. I need a couple days to wrap-up something I'm working on and I'll push a new commit that addresses your concerns. |
thanks for your patience and flexibility ! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting ready to push my latest commit that addresses your feedback.
@@ -78,8 +78,10 @@ var ( | |||
goMaxProcsFlag = flag.Int("gomaxprocs", 0, "Setting for runtime.GOMAXPROCS, <1 doesn't change the default") | |||
profileFlag = flag.String("profile", "", "write .cpu and .mem profiles to file") | |||
grpcFlag = flag.Bool("grpc", false, "Use GRPC (health check by default, add -ping for ping) for load testing") | |||
grpcSecureFlag = flag.Bool("grpc-secure", false, "Use secure transport (tls) for GRPC") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the readme to remove the -secure-grpc flag and ran the updateFlags.sh script.
README.md
Outdated
* A `grpcping` using TLS. First, start Fortio server with the `-cert` and `-key` flags. | ||
`/path/to/fortio/server.crt` and `/path/to/fortio/server.key` are paths to the TLS certificate and key that | ||
you are responsible for providing. Creating a TLS certificate is outside the scope of this document. This example uses | ||
`localhost` in the `Subject Alternative Name` (SAN) of the certificate. The SAN should match the domain name that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Makefile
Outdated
@@ -14,25 +14,41 @@ TAG:=$(USER)$(shell date +%y%m%d_%H%M%S) | |||
|
|||
DOCKER_TAG = $(DOCKER_PREFIX)$(IMAGE):$(TAG) | |||
|
|||
CERT_TEMP := ./cert-tmp/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Makefile
Outdated
# go test ./... and others run in vendor/ and cause problems (!) | ||
PACKAGES:=$(shell find . -type d -print | egrep -v "/(\.|vendor|static|templates|release|docs|json)") | ||
PACKAGES:=$(shell find . -type d -print | egrep -v "/(\.|vendor|static|templates|release|docs|json|cert-tmp)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
README.md
Outdated
UI starting - visit: | ||
http://localhost:8080/fortio/ | ||
Https redirector running on :8081 | ||
Fortio 0.9.0 grpc ping server listening on port :8079 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like docs are now using 0.10.0. I have changed to 0.10.0.
README.md
Outdated
Next, run the `load` command with the `-cacert` flag: | ||
``` | ||
$ fortio load -cacert /etc/ssl/certs/ca.crt -grpc localhost:8079 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -78,8 +78,10 @@ var ( | |||
goMaxProcsFlag = flag.Int("gomaxprocs", 0, "Setting for runtime.GOMAXPROCS, <1 doesn't change the default") | |||
profileFlag = flag.String("profile", "", "write .cpu and .mem profiles to file") | |||
grpcFlag = flag.Bool("grpc", false, "Use GRPC (health check by default, add -ping for ping) for load testing") | |||
grpcSecureFlag = flag.Bool("grpc-secure", false, "Use secure transport (tls) for GRPC") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting ready to push my latest commit that addresses your feedback.
@ldemailly do you mind reviewing when you have a moment? |
looking now |
getting some github (unicorn) errors when I try to open this PR somehow) qq: why did you remove TestExitedPingServer ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really almost there now - Thanks!
A few comment and you only need to update where I prefixed by "NeedFix" (4 places)
# switch to report mode | ||
docker stop $DOCKERID | ||
docker rm $DOCKERNAME | ||
docker stop $DOCKERSECID | ||
docker rm $DOCKERSECNAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should probably be "trap"ed too but I guess we can clean that up later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the trap is used for the final Fortio instance, the report mode server. I stop/rm the Docker assets for the secure grpc instance of Fortio server before the script starts the report mode Fortio server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trap means cleanup like a finally, in case of error in any of tbe tests
fgrpc/grpcrunner.go
Outdated
if err != nil { | ||
log.Errf("failed to conect to %s with tls %v: %v", serverAddr, tls, err) | ||
log.Errf("failed to connect to %s: %v", serverAddr, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NeedFix: when an error occurs it is very important to log all the input, please add cacert and override to the statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
fgrpc/grpcrunner.go
Outdated
// Dial dials grpc using insecure or tls transport security when serverAddr | ||
// has prefixHTTPS or cert is provided. If override is set to a non empty string, | ||
// it will override the virtual host name of authority in requests. | ||
func Dial(serverAddr string, cacert, override string) (conn *grpc.ClientConn, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NeedFix: either put the type for all 3 or only once but here it's odd to have string 2 out of 3 args
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
fgrpc/grpcrunner_test.go
Outdated
}, | ||
Destination: sDest, | ||
CACert: caCrt, | ||
CertOverride: "invalidName", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we have a positive test of CertOverride (where it is set yet works ?)
fgrpc/grpcrunner_test.go
Outdated
{ | ||
name: "insecure runner with payload", | ||
runnerOpts: GRPCRunnerOptions{ | ||
RunnerOptions: periodic.RunnerOptions{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NeedFix: there is a lot of copy paste of this section, why not have the runner options in a variable ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I find table tests easier to read when testing multiple test cases. WOuld you rather I follow the existing pattern for the test? Something like this:
func TestGRPCRunner(t *testing.T) {
log.SetLogLevel(log.Info)
iPort := PingServer("0", "", "", "bar", 0)
iDest := fmt.Sprintf("localhost:%d", iPort)
sPort := PingServer("0", svrCrt, svrKey, "bar", 0)
sDest := fmt.Sprintf("localhost:%d", sPort)
ro := periodic.RunnerOptions{
QPS: 100,
Resolution: 0.00001,
}
opts := GRPCRunnerOptions{
RunnerOptions: ro,
Destination: iDest,
Profiler: "test.profile",
}
res, err := RunGRPCTest(&opts)
if err != nil {
t.Error(err)
return
}
totalReq := res.DurationHistogram.Count
ok := res.RetCodes[grpc_health_v1.HealthCheckResponse_SERVING]
if totalReq != ok {
t.Errorf("Mismatch between requests %d and ok %v", totalReq, res.RetCodes)
}
// Test a grpc runner with TLS enabled.
opts.Destination = sDest
opts.CACert = caCrt
res, err = RunGRPCTest(&opts)
if err != nil {
t.Error(err)
return
}
totalReq = res.DurationHistogram.Count
ok = res.RetCodes[grpc_health_v1.HealthCheckResponse_SERVING]
if totalReq != ok {
t.Errorf("Mismatch between requests %d and ok %v", totalReq, res.RetCodes)
}
}
Keep in mind the above example is only exercising 2 test cases.
fgrpc/grpcrunner_test.go
Outdated
{ | ||
name: "insecure runner", | ||
runnerOpts: GRPCRunnerOptions{ | ||
RunnerOptions: periodic.RunnerOptions{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NeedFix: same
log.Fatalf("Invalid TLS credentials: %v\n", err) | ||
} | ||
log.Infof("Using server certificate %v to construct TLS credentials", cert) | ||
log.Infof("Using server key %v to construct TLS credentials", key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could probably be a single log info instead of 2 but it's ok as is too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my feedback and example refactor of TestGRPCRunner
. I would like to make sure we're on the same page before I refactor TestGRPCRunner
and TestGRPCRunnerWithError
.
# switch to report mode | ||
docker stop $DOCKERID | ||
docker rm $DOCKERNAME | ||
docker stop $DOCKERSECID | ||
docker rm $DOCKERSECNAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the trap is used for the final Fortio instance, the report mode server. I stop/rm the Docker assets for the secure grpc instance of Fortio server before the script starts the report mode Fortio server.
fgrpc/grpcrunner.go
Outdated
// Dial dials grpc using insecure or tls transport security when serverAddr | ||
// has prefixHTTPS or cert is provided. If override is set to a non empty string, | ||
// it will override the virtual host name of authority in requests. | ||
func Dial(serverAddr string, cacert, override string) (conn *grpc.ClientConn, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
fgrpc/grpcrunner.go
Outdated
if err != nil { | ||
log.Errf("failed to conect to %s with tls %v: %v", serverAddr, tls, err) | ||
log.Errf("failed to connect to %s: %v", serverAddr, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
fgrpc/grpcrunner_test.go
Outdated
{ | ||
name: "insecure runner with payload", | ||
runnerOpts: GRPCRunnerOptions{ | ||
RunnerOptions: periodic.RunnerOptions{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I find table tests easier to read when testing multiple test cases. WOuld you rather I follow the existing pattern for the test? Something like this:
func TestGRPCRunner(t *testing.T) {
log.SetLogLevel(log.Info)
iPort := PingServer("0", "", "", "bar", 0)
iDest := fmt.Sprintf("localhost:%d", iPort)
sPort := PingServer("0", svrCrt, svrKey, "bar", 0)
sDest := fmt.Sprintf("localhost:%d", sPort)
ro := periodic.RunnerOptions{
QPS: 100,
Resolution: 0.00001,
}
opts := GRPCRunnerOptions{
RunnerOptions: ro,
Destination: iDest,
Profiler: "test.profile",
}
res, err := RunGRPCTest(&opts)
if err != nil {
t.Error(err)
return
}
totalReq := res.DurationHistogram.Count
ok := res.RetCodes[grpc_health_v1.HealthCheckResponse_SERVING]
if totalReq != ok {
t.Errorf("Mismatch between requests %d and ok %v", totalReq, res.RetCodes)
}
// Test a grpc runner with TLS enabled.
opts.Destination = sDest
opts.CACert = caCrt
res, err = RunGRPCTest(&opts)
if err != nil {
t.Error(err)
return
}
totalReq = res.DurationHistogram.Count
ok = res.RetCodes[grpc_health_v1.HealthCheckResponse_SERVING]
if totalReq != ok {
t.Errorf("Mismatch between requests %d and ok %v", totalReq, res.RetCodes)
}
}
Keep in mind the above example is only exercising 2 test cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my feedback and example refactor of TestGRPCRunner
. I would like to make sure we're on the same page before I refactor TestGRPCRunner
and TestGRPCRunnerWithError
.
Webtest.sh
Outdated
@@ -14,6 +14,9 @@ DOCKERID=$(docker run -d --ulimit nofile=$FILE_LIMIT --name $DOCKERNAME istio/fo | |||
function cleanup { | |||
docker stop $DOCKERID | |||
docker rm $DOCKERNAME | |||
docker stop $DOCKERSECID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in theory should have an if that it is set/started (as it's not started right away unlike DOCKERID) but it's ok, you didn't have to change it (for that reason as well - I'll try - one test is interrupt or have a test error out and see if it still finishes correctly/cleansup without too many additional errors)
log.Errf("Invalid TLS credentials: %v\n", err) | ||
return nil, err | ||
} | ||
log.Infof("Using CA certificate %v to construct TLS credentials", cacert) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional: here too (override logging)
if totalReq != ok { | ||
t.Errorf("Mismatch between requests %d and ok %v", totalReq, res.RetCodes) | ||
for _, test := range tests { | ||
switch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't mean to have to put a big switch here :( why can't those be above directly ?
closing for #249 (this PR makes github choke so redone there) |
Add support for custom CA and certificates and key, and thus mTLS, for fortio's GRPC client/server Work by @danehans in #204 with my support/reviews/tweaks commits: * Adds gRPC mTLS Support * Addresses @ldemailly 3/15/18 feedback * Move cert log msgs and fix typo * Addresses round 2 of @ldemailly comments * Added docs and minor fixups * Updated Dial return error * Updates 2nd ping server fail test with new params * Removes unneeded var declaration in Dial func * Realigns flags in fortio_main * Updates cert/key flags and updates readme * Adds more details to docs * Updates e2e/ut, docs and Dial func to support vhost override * Implements new -ca-cert flag * Generates certs, adds e2e and -cacert flag * Removes openssl dep * Adds logs to certgen and fixes remote docker bind mount * reverts to certgen script and ui * Updates makefile/Webtest/Certgen and ui https func * Add make deps to docker compose * Fixes make command in Dockerfile.test for CI * Added Make dep and cleaned-up addHTTPS func * Adds add'l Dial func test cases * Addresses @ldemailly comments * Adds TestExitedPingServer test * Refactor PingServer test for failed crt/key * use 0.10.1 like rest of readme * Addresses ldemailly feedback * Removed docker stop/rm from trap * Fix make test 2nd make test would error out because of cert-tmp * Fix clean and error handling for test (new docker images) * Switch v to 0.11.0 Except for json which should be 0.9.0 (format as it was when it was added) * Fixing test to not check for test.name to set options (!) * Even more cleanup of duplicate * Sync the readme and the flags from the code
Adds gRPC Server-side TLS support to Fortio grpc client and server. Addresses
TODO: option to specify certs.
infgrpc/grpcrunner.go
.