Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,65 @@ $ snaptel -p plugin list
$ snaptel -p metric list
$ snaptel -p plugin load /opt/snap/plugins/snap-plugin-collector-mock1
$ snaptel -p task create -t mock-file.yml
```
```

### Secure GRPC plugins
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.

#### Examples

##### Definition of flags

| Flag | Description |
| ------ | ------ |
| plugin-cert | TLS server certificate |
| plugin-key | TLS server private key |
| plugin-ca-certs | TLS server CA certificates |

##### Starting `snapteld`

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.

```sh
$snapteld -t 0 -l 1 --tls-cert snaptest-cli.crt --tls-key snaptest-cli.key --ca-cert-paths snaptest-ca.crt
```

##### Running `snaptel`

```sh
▶ 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
Plugin loaded
Name: test-rand-collector
Version: 1
Type: collector
Signed: false
Loaded Time: Mon, 14 Aug 2017 22:25:16 PDT
```

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.

##### Case 1: Missing plugin key

```sh
▶ snaptel plugin load --plugin-cert=snaptest-srv.crt --plugin-ca-certs=snaptest-ca.crt ../snap-plugin-lib-go/rand-collector
Error: Both plugin certification and key are mandatory.
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
```

##### Case 2: Using untrusted CA

```sh
▶ 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
Error: rpc error: code = Internal desc = connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority"
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]

```

##### Case 3: Trying to set TLS GRPC communication for non-GRPC plugin

```sh
▶ snaptel plugin load --plugin-cert snaptest-srv.crt --plugin-key snaptest-srv.key --plugin-ca-certs snaptest-ca.crt ../snap/snap-plugin-collector-mock1
Error: secure framework can't connect to insecure plugin; plugin_name: mock
Usage: load <plugin_path> [--plugin-cert=<plugin_cert_path> --plugin-key=<plugin_key_path> --plugin-ca-certs=<ca_cert_paths>]
```

21 changes: 0 additions & 21 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,8 @@ import:
version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
- package: github.com/ghodss/yaml
version: c3eb24aeea63668ebdac08d2e252f20df8b6b1ae
- package: github.com/golang/protobuf
version: 888eb0692c857ec880338addf316bd662d5e630e
subpackages:
- proto
- package: github.com/hashicorp/go-msgpack
version: fa3f63826f7c23912c15263591e65d54d080b458
subpackages:
- codec
- package: github.com/hashicorp/memberlist
version: a93fbd426dd831f5a66db3adc6a5ffa6f44cc60a
- package: github.com/intelsdi-x/gomit
- package: github.com/julienschmidt/httprouter
version: 8c199fb6259ffc1af525cc3ad52ee60ba8359669
- package: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- package: github.com/robfig/cron
version: 32d9c273155a0506d27cf73dd1246e86a470997e
- package: github.com/vrischmann/jsonutil
version: 694784f9315ee9fc763c1d30f28753cba21307aa
- package: github.com/xeipuuv/gojsonschema
version: d3178baac32433047aa76f07317f84fbe2be6cda
- package: golang.org/x/crypto
version: aedad9a179ec1ea11b7064c57cbc6dc30d7724ec
subpackages:
Expand All @@ -47,8 +28,6 @@ import:
- context
- trace
- http2
- package: google.golang.org/grpc
version: 0032a855ba5c8a3c8e0d71c2deef354b70af1584
- package: gopkg.in/yaml.v2
version: c1cd2254a6dd314c9d73c338c12688c9325d85c6
- package: github.com/intelsdi-x/snap-client-go/client
26 changes: 25 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ limitations under the License.
package main

import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"sort"

openapiclient "github.com/go-openapi/runtime/client"
"github.com/golang/glog"
"github.com/intelsdi-x/snap-cli/snaptel"
"github.com/intelsdi-x/snap-client-go/client"
Expand All @@ -35,6 +38,10 @@ var (
gitversion string
)

type tlsClientOptions struct {
insecureSkipVerify bool
}

func main() {
app := cli.NewApp()
app.Name = "snaptel"
Expand Down Expand Up @@ -66,13 +73,30 @@ func beforeAction(ctx *cli.Context) error {
glog.Fatal(err)
}

c := client.NewHTTPClientWithConfig(nil, &client.TransportConfig{Host: u.Host, BasePath: snaptel.FlAPIVer.Value, Schemes: []string{u.Scheme}})
tlsOpts := tlsClientOptions{insecureSkipVerify: ctx.Bool("insecure")}
tlsClient := tlsClient(tlsOpts)
rt := openapiclient.NewWithClient(u.Host, snaptel.FlAPIVer.Value, []string{u.Scheme}, tlsClient)
c := client.New(rt, nil)
snaptel.SetClient(c)
snaptel.SetScheme(u.Scheme)
snaptel.SetAuthInfo(snaptel.BasicAuth(ctx))

return nil
}

// tlsClient creates a http.Client
func tlsClient(opts tlsClientOptions) *http.Client {
transport := tlsTransport(opts)
return &http.Client{Transport: transport}
}

func tlsTransport(opts tlsClientOptions) http.RoundTripper {
cfg := &tls.Config{}
cfg.InsecureSkipVerify = opts.insecureSkipVerify
cfg.BuildNameToCertificate()
return &http.Transport{TLSClientConfig: cfg}
}

// ByCommand contains array of CLI commands.
type ByCommand []cli.Command

Expand Down
28 changes: 27 additions & 1 deletion snaptel/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"

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

Expand All @@ -39,6 +40,7 @@ var (
client *snapClient.Snap
authInfoWriter runtime.ClientAuthInfoWriter
password string
scheme string
)

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

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

// SetScheme sets the request protocol.
func SetScheme(s string) {
scheme = s
}

// GetFirstChar gets the first character of a giving string.
func GetFirstChar(s string) string {
firstChar := ""
Expand Down Expand Up @@ -142,6 +149,9 @@ func getErrorDetail(err error, ctx *cli.Context) error {
case *tasks.UpdateTaskStateUnauthorized:
return newUsageError(fmt.Sprintf("%v", err.(*tasks.UpdateTaskStateUnauthorized).Payload.Message), ctx)
default:
if strings.Contains(err.Error(), "tls: oversized record") || strings.Contains(err.Error(), "malformed HTTP response") {
return newUsageError(extractError(err.Error()), ctx)
}
return newUsageError(fmt.Sprintf("Error: %v", err), ctx)
}
}
Expand Down Expand Up @@ -210,3 +220,19 @@ func BasicAuth(ctx *cli.Context) runtime.ClientAuthInfoWriter {
}
return nil
}

// extractError is a hack for SSL/TLS handshake error.
func extractError(m string) string {
ts := strings.Split(m, "\"")

var tss []string
if len(ts) > 0 {
tss = strings.Split(ts[0], "malformed")
}

errMsg := "Error connecting to API. Do you have an http/https mismatching API request?"
if len(tss) > 0 {
errMsg = tss[0] + errMsg
}
return errMsg
}
48 changes: 47 additions & 1 deletion snaptel/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,46 @@ func loadPlugin(ctx *cli.Context) error {
}

params := plugins.NewLoadPluginParamsWithTimeout(FlTimeout.Value)
f, err := os.Open(filepath.Join(paths...))

// Sets the plugin data.
f, err := os.Open(filepath.Join(paths[0]))
if err != nil {
return newUsageError("Cannot open the plugin", ctx)
}
defer f.Close()
params.SetPluginData(f)

if !hasValidFlags(ctx.IsSet("plugin-cert"), ctx.IsSet("plugin-key")) {
return newUsageError("Both plugin certification and key are mandatory.", ctx)
}

// Sets the plugin certificate.
if ctx.IsSet("plugin-cert") {
pCert := ctx.String("plugin-cert")
if _, err := os.Stat(pCert); os.IsNotExist(err) {
return newUsageError("Cannot reach the plugin certificate", ctx)
}
params.SetPluginCert(&pCert)
}

// Sets the plugin key.
if ctx.IsSet("plugin-key") {
pKey := ctx.String("plugin-key")
if _, err := os.Stat(pKey); os.IsNotExist(err) {
return newUsageError("Cannot reach the plugin key", ctx)
}
params.SetPluginKey(&pKey)
}

// Sets the CA ceritificate.
if ctx.IsSet("plugin-ca-certs") {
caCerts := ctx.String("plugin-ca-certs")
if _, err := os.Stat(caCerts); os.IsNotExist(err) {
return newUsageError("Cannot reach the CA certificates", ctx)
}
params.SetCaCerts(&caCerts)
}

resp, err := client.Plugins.LoadPlugin(params, authInfoWriter)
if err != nil {
return getErrorDetail(err, ctx)
Expand Down Expand Up @@ -169,3 +202,16 @@ func listPlugins(ctx *cli.Context) error {

return nil
}

func hasValidFlags(key, cert bool) bool {
// Validats TLS plugin loading mandatory flags.
if key && cert {
return true
}

// Don't block normal flow which has no certs at all.
if !key && !cert {
return true
}
return false
}
12 changes: 10 additions & 2 deletions snaptel/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package snaptel
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -54,8 +55,15 @@ func watchTask(ctx *cli.Context) error {
// Therefore no timeout for this request.
req, err := http.NewRequest("GET", url, nil)
req.SetBasicAuth("snap", password)
cli := &http.Client{}
resp, err := cli.Do(req)
if err != nil {
return err
}

tr := http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
wtClient := http.Client{Transport: &tr}
resp, err := wtClient.Do(req)
if err != nil {
return err
}
Expand Down