Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.

Commit e76a351

Browse files
committed
SDI-2730: Add SSL/TLC capability to snap-cli
1 parent af85fe2 commit e76a351

6 files changed

Lines changed: 204 additions & 27 deletions

File tree

README.md

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,94 @@ $ snaptel -p plugin list
300300
$ snaptel -p metric list
301301
$ snaptel -p plugin load /opt/snap/plugins/snap-plugin-collector-mock1
302302
$ snaptel -p task create -t mock-file.yml
303-
```
303+
```
304+
305+
### Secure GRPC plugins
306+
Snap supports TLS for GRPC plugins. Referring to [secure plugin communication](https://github.com/intelsdi-x/snap/blob/master/docs/SECURE_PLUGIN_COMMUNICATION.md) for details. How to setup TLS on both server and client? The [Setup TLS Certificates](https://github.com/intelsdi-x/snap/blob/master/docs/SETUP_TLS_CERTIFICATES.md) has everything.
307+
308+
#### Sample Use Cases
309+
310+
| Flag | Description |
311+
| ------ | ------ |
312+
| tls-cert | TLS client certificate |
313+
| tls-key | TLS client private key |
314+
| ca-cert-paths | TLS client CA certificates |
315+
| plugin-cert | TLS server certificate |
316+
| plugin-key | TLS server private key |
317+
| plugin-ca-certs | TLS server CA certificates |
318+
319+
##### Case 1: Start `snapteld` with TLS certs
320+
321+
Snap is a client for all GRPC plugins. Note that Snap loads CA certificates from your OS certificate trust store if it's not specified.
322+
323+
```sh
324+
$snapteld -t 0 -l 1 --tls-cert snaptest-cli.crt --tls-key snaptest-cli.key --ca-cert-paths snaptest-ca.crt
325+
```
326+
##### Case 1: Run `snaptel`
327+
328+
```sh
329+
▶ snaptel plugin load --plugin-cert snaptest-srv.crt --plugin-key snaptest-srv.key --plugin-ca-certs snaptest-ca.crt ../snap-plugin-lib-go/rand-collector
330+
Error: Both plugin certification and key are mandatory. The request has to use HTTPS
331+
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
332+
```
333+
334+
> :collision: Urgh! Loading a secured GRPC plugin has to use HTTPS
335+
336+
```sh
337+
▶ snaptel --url https://localhost:8181 plugin load --plugin-cert snaptest-srv.crt --plugin-key snaptest-srv.key --plugin-ca-certs snaptest-ca.crt ../snap-plugin-lib-go/rand-collector
338+
Error: Error: Post https://localhost:8181/v2/plugins: http: server gave HTTP response to HTTPS client
339+
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
340+
```
341+
342+
> :collision: The server was not started using HTTPs
343+
344+
##### Case 2: Start `snapteld` with TLS certs and HTTPS
345+
Snap only requires the server certificate verificate for HTTPS.
346+
347+
```sh
348+
▶ snapteld -t 0 -l 1 --rest-https --rest-cert snaphttps-srv.crt --rest-key snaphttps-srv.key --tls-cert snaptest-cli.crt --tls-key snaptest-cli.key --ca-cert-paths snaptest-ca.crt
349+
```
350+
351+
> :white_check_mark: using this setting to start `snapteld` for a seured GRPC plugin communication.
352+
353+
##### Case 2: Run `snaptel`
354+
355+
```sh
356+
▶ snaptel --url https://localhost:8181 plugin load --plugin-cert snaptest-srv.crt --plugin-key snaptest-srv.key --plugin-ca-certs snaptest-ca.crt ../snap-plugin-lib-go/rand-collector
357+
Error: Error: Post https://localhost:8181/v2/plugins: x509: certificate signed by unknown authority
358+
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
359+
```
360+
361+
> :collision: Urgh! HTTPS does not have a trusted CA. There is no way to specify a CA using a flag for HTTPS currently. Putting the trusted CA in your OS trust store in production. Using --insecure flag for your testing convenience.
362+
363+
```sh
364+
▶ snaptel --url https://localhost:8181 --insecure plugin load --plugin-cert snaptest-srv.crt --plugin-key snaptest-srv.key --plugin-ca-certs snaptest-ca.crt ../snap-plugin-lib-go/rand-collector
365+
Plugin loaded
366+
Name: test-rand-collector
367+
Version: 1
368+
Type: collector
369+
Signed: false
370+
Loaded Time: Wed, 02 Aug 2017 15:23:09 PDT
371+
```
372+
373+
>:white_check_mark: The secured GRPC plugin loaded! You may omit the `plugin-ca-certs` flag if it's in the trust store of your OS/App.
374+
375+
Only loading a GRPC plugin requires TLS certs. Not any other commands.
376+
377+
```sh
378+
▶ snaptel --url https://localhost:8181 --insecure plugin list
379+
NAME VERSION TYPE SIGNED STATUS LOADED TIME
380+
test-rand-collector 1 collector false loaded Wed, 02 Aug 2017 15:23:09 PDT
381+
```
382+
383+
##### Case 3: Caveat
384+
Starting `snapteld` same as case 2. Loading a non GRPC plugin.
385+
386+
```sh
387+
▶ snaptel --url https://localhost:8181 --insecure plugin load --plugin-cert snaptest-srv.crt --plugin-key snaptest-srv.key --plugin-ca-certs snaptest-ca.crt ../snap/build/darwin/x86_64/plugins/snap-plugin-collector-mock1
388+
Error: secure framework can't connect to insecure plugin; plugin_name: mock
389+
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
390+
```
391+
392+
>:collision: Urgh! Currently, no TLS is available for non-grpc plugins. Restarting `snapteld` without TLS to load non-grpc plugins.
393+

glide.yaml

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,8 @@ import:
1515
version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
1616
- package: github.com/ghodss/yaml
1717
version: c3eb24aeea63668ebdac08d2e252f20df8b6b1ae
18-
- package: github.com/golang/protobuf
19-
version: 888eb0692c857ec880338addf316bd662d5e630e
20-
subpackages:
21-
- proto
22-
- package: github.com/hashicorp/go-msgpack
23-
version: fa3f63826f7c23912c15263591e65d54d080b458
24-
subpackages:
25-
- codec
26-
- package: github.com/hashicorp/memberlist
27-
version: a93fbd426dd831f5a66db3adc6a5ffa6f44cc60a
28-
- package: github.com/intelsdi-x/gomit
29-
- package: github.com/julienschmidt/httprouter
30-
version: 8c199fb6259ffc1af525cc3ad52ee60ba8359669
31-
- package: github.com/pborman/uuid
32-
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
3318
- package: github.com/robfig/cron
3419
version: 32d9c273155a0506d27cf73dd1246e86a470997e
35-
- package: github.com/vrischmann/jsonutil
36-
version: 694784f9315ee9fc763c1d30f28753cba21307aa
37-
- package: github.com/xeipuuv/gojsonschema
38-
version: d3178baac32433047aa76f07317f84fbe2be6cda
3920
- package: golang.org/x/crypto
4021
version: aedad9a179ec1ea11b7064c57cbc6dc30d7724ec
4122
subpackages:
@@ -47,8 +28,6 @@ import:
4728
- context
4829
- trace
4930
- http2
50-
- package: google.golang.org/grpc
51-
version: 0032a855ba5c8a3c8e0d71c2deef354b70af1584
5231
- package: gopkg.in/yaml.v2
5332
version: c1cd2254a6dd314c9d73c338c12688c9325d85c6
5433
- package: github.com/intelsdi-x/snap-client-go/client

main.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ limitations under the License.
2020
package main
2121

2222
import (
23+
"crypto/tls"
2324
"fmt"
25+
"net/http"
2426
"net/url"
2527
"os"
2628
"sort"
2729

30+
openapiclient "github.com/go-openapi/runtime/client"
2831
"github.com/golang/glog"
2932
"github.com/intelsdi-x/snap-cli/snaptel"
3033
"github.com/intelsdi-x/snap-client-go/client"
@@ -35,6 +38,10 @@ var (
3538
gitversion string
3639
)
3740

41+
type tlsClientOptions struct {
42+
insecureSkipVerify bool
43+
}
44+
3845
func main() {
3946
app := cli.NewApp()
4047
app.Name = "snaptel"
@@ -66,13 +73,30 @@ func beforeAction(ctx *cli.Context) error {
6673
glog.Fatal(err)
6774
}
6875

69-
c := client.NewHTTPClientWithConfig(nil, &client.TransportConfig{Host: u.Host, BasePath: snaptel.FlAPIVer.Value, Schemes: []string{u.Scheme}})
76+
tlcOpts := tlsClientOptions{insecureSkipVerify: ctx.Bool("insecure")}
77+
tlcClient := tlsClient(tlcOpts)
78+
rt := openapiclient.NewWithClient(u.Host, snaptel.FlAPIVer.Value, []string{u.Scheme}, tlcClient)
79+
c := client.New(rt, nil)
7080
snaptel.SetClient(c)
81+
snaptel.SetScheme(u.Scheme)
7182
snaptel.SetAuthInfo(snaptel.BasicAuth(ctx))
7283

7384
return nil
7485
}
7586

87+
// tlsClient creates a http.Client
88+
func tlsClient(opts tlsClientOptions) *http.Client {
89+
transport := tlsTransport(opts)
90+
return &http.Client{Transport: transport}
91+
}
92+
93+
func tlsTransport(opts tlsClientOptions) http.RoundTripper {
94+
cfg := &tls.Config{}
95+
cfg.InsecureSkipVerify = opts.insecureSkipVerify
96+
cfg.BuildNameToCertificate()
97+
return &http.Transport{TLSClientConfig: cfg}
98+
}
99+
76100
// ByCommand contains array of CLI commands.
77101
type ByCommand []cli.Command
78102

snaptel/common.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"encoding/json"
2424
"fmt"
2525
"io/ioutil"
26+
"strings"
2627

2728
"golang.org/x/crypto/ssh/terminal"
2829

@@ -39,6 +40,7 @@ var (
3940
client *snapClient.Snap
4041
authInfoWriter runtime.ClientAuthInfoWriter
4142
password string
43+
scheme string
4244
)
4345

4446
// UsageError defines the error message and CLI context
@@ -61,7 +63,7 @@ func newUsageError(s string, ctx *cli.Context) UsageError {
6163
return UsageError{s, ctx}
6264
}
6365

64-
// SetClient provides a way to set the private snapClient in this package.
66+
// SetClient sets the private HTTP Client in this package.
6567
func SetClient(cl *snapClient.Snap) {
6668
client = cl
6769
}
@@ -71,6 +73,11 @@ func SetAuthInfo(aw runtime.ClientAuthInfoWriter) {
7173
authInfoWriter = aw
7274
}
7375

76+
// SetScheme sets the request protocol.
77+
func SetScheme(s string) {
78+
scheme = s
79+
}
80+
7481
// GetFirstChar gets the first character of a giving string.
7582
func GetFirstChar(s string) string {
7683
firstChar := ""
@@ -142,6 +149,10 @@ func getErrorDetail(err error, ctx *cli.Context) error {
142149
case *tasks.UpdateTaskStateUnauthorized:
143150
return newUsageError(fmt.Sprintf("%v", err.(*tasks.UpdateTaskStateUnauthorized).Payload.Message), ctx)
144151
default:
152+
// this is a hack
153+
if strings.Contains(err.Error(), "tls: oversized record") || strings.Contains(err.Error(), "malformed HTTP response") {
154+
return newUsageError(extractError(err.Error()), ctx)
155+
}
145156
return newUsageError(fmt.Sprintf("Error: %v", err), ctx)
146157
}
147158
}
@@ -210,3 +221,19 @@ func BasicAuth(ctx *cli.Context) runtime.ClientAuthInfoWriter {
210221
}
211222
return nil
212223
}
224+
225+
// extractError is a hack for SSL/TLS handshake error.
226+
func extractError(m string) string {
227+
ts := strings.Split(m, "\"")
228+
229+
var tss []string
230+
if len(ts) > 0 {
231+
tss = strings.Split(ts[0], "malformed")
232+
}
233+
234+
errMsg := "Error connecting to API. Do you have an http/https mismatching API request?"
235+
if len(tss) > 0 {
236+
errMsg = tss[0] + errMsg
237+
}
238+
return errMsg
239+
}

snaptel/plugin.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,49 @@ func loadPlugin(ctx *cli.Context) error {
4747
}
4848

4949
params := plugins.NewLoadPluginParamsWithTimeout(FlTimeout.Value)
50-
f, err := os.Open(filepath.Join(paths...))
50+
51+
// Sets the plugin data.
52+
f, err := os.Open(filepath.Join(paths[0]))
5153
if err != nil {
5254
return newUsageError("Cannot open the plugin", ctx)
5355
}
5456
defer f.Close()
5557
params.SetPluginData(f)
5658

59+
if !hasValidFlags(ctx.IsSet("plugin-cert"), ctx.IsSet("plugin-key"), scheme) {
60+
return newUsageError("Both plugin certification and key are mandatory. The request has to use HTTPS", ctx)
61+
}
62+
63+
// Sets the plugin certificate.
64+
if ctx.IsSet("plugin-cert") {
65+
cert, err := os.Open(ctx.String("plugin-cert"))
66+
if err != nil {
67+
return newUsageError("Cannot open the plugin certificate", ctx)
68+
}
69+
defer cert.Close()
70+
params.SetPluginCert(cert)
71+
}
72+
73+
// Sets the plugin key.
74+
if ctx.IsSet("plugin-key") {
75+
key, err := os.Open(ctx.String("plugin-key"))
76+
if err != nil {
77+
return newUsageError("Cannot open the plugin key", ctx)
78+
}
79+
defer key.Close()
80+
params.SetPluginKey(key)
81+
}
82+
83+
// Sets the CA ceritificate.
84+
if ctx.IsSet("plugin-ca-certs") {
85+
caCert, err := os.Open(ctx.String("plugin-ca-certs"))
86+
if err != nil {
87+
return newUsageError("Cannot open the CA certificate", ctx)
88+
}
89+
defer caCert.Close()
90+
params.SetCaCerts(caCert)
91+
}
92+
5793
resp, err := client.Plugins.LoadPlugin(params, authInfoWriter)
5894
if err != nil {
5995
return getErrorDetail(err, ctx)
@@ -154,3 +190,16 @@ func listPlugins(ctx *cli.Context) error {
154190

155191
return nil
156192
}
193+
194+
func hasValidFlags(key, cert bool, scheme string) bool {
195+
// Validats TLS plugin load flags
196+
if key && cert && scheme == "https" {
197+
return true
198+
}
199+
200+
// Don't block normal flow
201+
if !key && !cert {
202+
return true
203+
}
204+
return false
205+
}

snaptel/watch.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package snaptel
2222
import (
2323
"bufio"
2424
"bytes"
25+
"crypto/tls"
2526
"encoding/json"
2627
"fmt"
2728
"io"
@@ -54,8 +55,15 @@ func watchTask(ctx *cli.Context) error {
5455
// Therefore no timeout for this request.
5556
req, err := http.NewRequest("GET", url, nil)
5657
req.SetBasicAuth("snap", password)
57-
cli := &http.Client{}
58-
resp, err := cli.Do(req)
58+
if err != nil {
59+
return err
60+
}
61+
62+
tr := http.Transport{
63+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
64+
}
65+
wtClient := http.Client{Transport: &tr}
66+
resp, err := wtClient.Do(req)
5967
if err != nil {
6068
return err
6169
}

0 commit comments

Comments
 (0)