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

Commit

Permalink
Add TLS capability to snap-cli (#6)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
candysmurf authored and IzabellaRaulin committed Aug 23, 2017
1 parent 5bf1023 commit c64c6fb
Showing 6 changed files with 171 additions and 27 deletions.
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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
@@ -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:
@@ -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
@@ -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"
@@ -35,6 +38,10 @@ var (
gitversion string
)

type tlsClientOptions struct {
insecureSkipVerify bool
}

func main() {
app := cli.NewApp()
app.Name = "snaptel"
@@ -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

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

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

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

// UsageError defines the error message and CLI context
@@ -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
}
@@ -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 := ""
@@ -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)
}
}
@@ -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
@@ -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)
@@ -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
@@ -22,6 +22,7 @@ package snaptel
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
@@ -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
}

0 comments on commit c64c6fb

Please sign in to comment.