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

Commit c64c6fb

Browse files
candysmurfIzabellaRaulin
authored andcommitted
Add TLS capability to snap-cli (#6)
Add SSL/TLC capability to snap-cli * changed the certs and key to use paths * incorporated code review feedback * incorporated code review feedback 8/16 * changed tlc to tls * resolved the merge conflicts and removed the fix for third party loading a plugin error
1 parent 5bf1023 commit c64c6fb

6 files changed

Lines changed: 171 additions & 27 deletions

File tree

README.md

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,65 @@ $ 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+
#### Examples
309+
310+
##### Definition of flags
311+
312+
| Flag | Description |
313+
| ------ | ------ |
314+
| plugin-cert | TLS server certificate |
315+
| plugin-key | TLS server private key |
316+
| plugin-ca-certs | TLS server CA certificates |
317+
318+
##### Starting `snapteld`
319+
320+
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.
321+
322+
```sh
323+
$snapteld -t 0 -l 1 --tls-cert snaptest-cli.crt --tls-key snaptest-cli.key --ca-cert-paths snaptest-ca.crt
324+
```
325+
326+
##### Running `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+
Plugin loaded
331+
Name: test-rand-collector
332+
Version: 1
333+
Type: collector
334+
Signed: false
335+
Loaded Time: Mon, 14 Aug 2017 22:25:16 PDT
336+
```
337+
338+
Notice that only GRPC plugins are supported. There is also a requirement to use trusted CA and providing both plugin-cert and plugin-key. Below common error messages are presented that you might receive if one of those requirements are not fulfilled.
339+
340+
##### Case 1: Missing plugin key
341+
342+
```sh
343+
▶ snaptel plugin load --plugin-cert=snaptest-srv.crt --plugin-ca-certs=snaptest-ca.crt ../snap-plugin-lib-go/rand-collector
344+
Error: Both plugin certification and key are mandatory.
345+
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
346+
```
347+
348+
##### Case 2: Using untrusted CA
349+
350+
```sh
351+
▶ 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
352+
Error: rpc error: code = Internal desc = connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority"
353+
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
354+
355+
```
356+
357+
##### Case 3: Trying to set TLS GRPC communication for non-GRPC plugin
358+
359+
```sh
360+
▶ snaptel plugin load --plugin-cert snaptest-srv.crt --plugin-key snaptest-srv.key --plugin-ca-certs snaptest-ca.crt ../snap/snap-plugin-collector-mock1
361+
Error: secure framework can't connect to insecure plugin; plugin_name: mock
362+
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
363+
```
364+

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+
tlsOpts := tlsClientOptions{insecureSkipVerify: ctx.Bool("insecure")}
77+
tlsClient := tlsClient(tlsOpts)
78+
rt := openapiclient.NewWithClient(u.Host, snaptel.FlAPIVer.Value, []string{u.Scheme}, tlsClient)
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: 27 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,9 @@ 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+
if strings.Contains(err.Error(), "tls: oversized record") || strings.Contains(err.Error(), "malformed HTTP response") {
153+
return newUsageError(extractError(err.Error()), ctx)
154+
}
145155
return newUsageError(fmt.Sprintf("Error: %v", err), ctx)
146156
}
147157
}
@@ -210,3 +220,19 @@ func BasicAuth(ctx *cli.Context) runtime.ClientAuthInfoWriter {
210220
}
211221
return nil
212222
}
223+
224+
// extractError is a hack for SSL/TLS handshake error.
225+
func extractError(m string) string {
226+
ts := strings.Split(m, "\"")
227+
228+
var tss []string
229+
if len(ts) > 0 {
230+
tss = strings.Split(ts[0], "malformed")
231+
}
232+
233+
errMsg := "Error connecting to API. Do you have an http/https mismatching API request?"
234+
if len(tss) > 0 {
235+
errMsg = tss[0] + errMsg
236+
}
237+
return errMsg
238+
}

snaptel/plugin.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,46 @@ func loadPlugin(ctx *cli.Context) error {
5151
}
5252

5353
params := plugins.NewLoadPluginParamsWithTimeout(FlTimeout.Value)
54-
f, err := os.Open(filepath.Join(paths...))
54+
55+
// Sets the plugin data.
56+
f, err := os.Open(filepath.Join(paths[0]))
5557
if err != nil {
5658
return newUsageError("Cannot open the plugin", ctx)
5759
}
5860
defer f.Close()
5961
params.SetPluginData(f)
6062

63+
if !hasValidFlags(ctx.IsSet("plugin-cert"), ctx.IsSet("plugin-key")) {
64+
return newUsageError("Both plugin certification and key are mandatory.", ctx)
65+
}
66+
67+
// Sets the plugin certificate.
68+
if ctx.IsSet("plugin-cert") {
69+
pCert := ctx.String("plugin-cert")
70+
if _, err := os.Stat(pCert); os.IsNotExist(err) {
71+
return newUsageError("Cannot reach the plugin certificate", ctx)
72+
}
73+
params.SetPluginCert(&pCert)
74+
}
75+
76+
// Sets the plugin key.
77+
if ctx.IsSet("plugin-key") {
78+
pKey := ctx.String("plugin-key")
79+
if _, err := os.Stat(pKey); os.IsNotExist(err) {
80+
return newUsageError("Cannot reach the plugin key", ctx)
81+
}
82+
params.SetPluginKey(&pKey)
83+
}
84+
85+
// Sets the CA ceritificate.
86+
if ctx.IsSet("plugin-ca-certs") {
87+
caCerts := ctx.String("plugin-ca-certs")
88+
if _, err := os.Stat(caCerts); os.IsNotExist(err) {
89+
return newUsageError("Cannot reach the CA certificates", ctx)
90+
}
91+
params.SetCaCerts(&caCerts)
92+
}
93+
6194
resp, err := client.Plugins.LoadPlugin(params, authInfoWriter)
6295
if err != nil {
6396
return getErrorDetail(err, ctx)
@@ -169,3 +202,16 @@ func listPlugins(ctx *cli.Context) error {
169202

170203
return nil
171204
}
205+
206+
func hasValidFlags(key, cert bool) bool {
207+
// Validats TLS plugin loading mandatory flags.
208+
if key && cert {
209+
return true
210+
}
211+
212+
// Don't block normal flow which has no certs at all.
213+
if !key && !cert {
214+
return true
215+
}
216+
return false
217+
}

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)