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

Commit

Permalink
add https to rest api
Browse files Browse the repository at this point in the history
  • Loading branch information
pittma committed Oct 2, 2015
1 parent 338191d commit 5563872
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 18 deletions.
2 changes: 1 addition & 1 deletion mgmt/rest/client/client_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func startAPI(port int) string {
// Start a REST API to talk to
rest.StreamingBufferWindow = 0.01
log.SetLevel(LOG_LEVEL)
r := rest.New()
r, _ := rest.New(false, "", "")
c := control.New()
c.Start()
s := scheduler.New()
Expand Down
2 changes: 1 addition & 1 deletion mgmt/rest/rest_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ func fetchMetricsWithVersion(port int, ns string, ver int) *rbody.APIResponse {
func startAPI(port int) *restAPIInstance {
// Start a REST API to talk to
log.SetLevel(LOG_LEVEL)
r := New()
r, _ := New(false, "", "")
c := control.New()
c.Start()
s := scheduler.New()
Expand Down
45 changes: 32 additions & 13 deletions mgmt/rest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package rest

import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -45,6 +46,8 @@ const (
)

var (
ErrBadCert = errors.New("Invalid certificate given")

restLogger = log.WithField("_module", "_mgmt-rest")
)

Expand Down Expand Up @@ -79,24 +82,33 @@ type managesTribe interface {
}

type Server struct {
mm managesMetrics
mt managesTasks
tr managesTribe
n *negroni.Negroni
r *httprouter.Router
mm managesMetrics
mt managesTasks
tr managesTribe
n *negroni.Negroni
r *httprouter.Router
tls *tls
}

func New() *Server {
func New(https bool, cpath, kpath string) (*Server, error) {
s := &Server{}

if https {
var err error
s.tls, err = newtls(cpath, kpath)
if err != nil {
return nil, err
}
}

restLogger.Info(fmt.Sprintf("Loading REST API with HTTPS set to: %v", https))

n := negroni.New(
s.n = negroni.New(
NewLogger(),
negroni.NewRecovery(),
)
return &Server{
r: httprouter.New(),
n: n,
}

s.r = httprouter.New()
return s, nil
}

func (s *Server) Start(addrString string) {
Expand All @@ -105,7 +117,14 @@ func (s *Server) Start(addrString string) {

func (s *Server) run(addrString string) {
log.Printf("[pulse-rest] listening on %s\n", addrString)
http.ListenAndServe(addrString, s.n)
if s.tls != nil {
err := http.ListenAndServeTLS(addrString, s.tls.cert, s.tls.key, s.n)
if err != nil {
panic(err)
}
} else {
http.ListenAndServe(addrString, s.n)
}
}

func (s *Server) BindMetricManager(m managesMetrics) {
Expand Down
117 changes: 117 additions & 0 deletions mgmt/rest/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package rest

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"io/ioutil"
"math/big"
"os"
"time"
)

type tls struct {
cert, key string
}

func newtls(certPath, keyPath string) (*tls, error) {
t := &tls{}
if certPath != "" && keyPath != "" {
cert, err := os.Open(certPath)
if err != nil {
return nil, err
}
_, err = os.Open(keyPath)
if err != nil {
return nil, err
}

rest, err := ioutil.ReadAll(cert)
if err != nil {
return nil, err
}

// test that given keychain is valid
var block *pem.Block
for {
block, rest = pem.Decode(rest)
if block == nil {
return nil, ErrBadCert
}
_, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
if len(rest) == 0 {
break
}
}
t.cert = certPath
t.key = keyPath
} else {
err := generateCert(t)
if err != nil {
return nil, err
}
}
return t, nil
}

func generateCert(t *tls) error {
// good for 1 year
notBefore := time.Now()
notAfter := notBefore.Add(time.Hour * 24 * 365)

serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return err
}

temp := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{"Local Pulse Agent"},
},
DNSNames: []string{"localhost"},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}

priv, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return err
}

cbytes, err := x509.CreateCertificate(rand.Reader, temp, temp, &priv.PublicKey, priv)
if err != nil {
return err
}

certPath := os.TempDir() + "/cert.pem"
keyPath := os.TempDir() + "/key.pem"

cout, err := os.Create(certPath)
if err != nil {
return err
}
pem.Encode(cout, &pem.Block{Type: "CERTIFICATE", Bytes: cbytes})
cout.Close()

kout, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
pem.Encode(kout, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
kout.Close()

t.cert = certPath
t.key = keyPath

return nil
}
2 changes: 1 addition & 1 deletion mgmt/rest/tribe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ func startTribes(count int) []int {
t.SetPluginCatalog(c)
t.SetTaskManager(s)
t.Start()
r := New()
r, _ := New(false, "", "")
r.BindMetricManager(c)
r.BindTaskManager(s)
r.BindTribeManager(t)
Expand Down
41 changes: 39 additions & 2 deletions pulse.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ var (
EnvVar: "PULSE_CACHE_EXPIRATION",
Value: "500ms",
}

flRestHttps = cli.BoolFlag{
Name: "rest-https",
Usage: "start Pulse's API as https",
}
flRestCert = cli.StringFlag{
Name: "rest-cert",
Usage: "a path to a certificate to use for HTTPS deployment of Pulse's REST API.",
}
flRestKey = cli.StringFlag{
Name: "rest-key",
Usage: "a path to a key file to use for HTTPS deployment of Pulse's REST API.",
}

gitversion string
)

Expand Down Expand Up @@ -113,8 +127,23 @@ func main() {
app.Name = "pulsed"
app.Version = gitversion
app.Usage = "A powerful telemetry agent framework"
app.Flags = []cli.Flag{flAPIDisabled, flAPIPort, flLogLevel, flLogPath, flMaxProcs, flPluginVersion, flNumberOfPLs, flCache, flPluginTrust, flKeyringFile}
app.Flags = []cli.Flag{
flAPIDisabled,
flAPIPort,
flLogLevel,
flLogPath,
flMaxProcs,
flPluginVersion,
flNumberOfPLs,
flCache,
flPluginTrust,
flKeyringFile,
flRestCert,
flRestHttps,
flRestKey,
}
app.Flags = append(app.Flags, tribe.Flags...)

app.Action = action
app.Run(os.Args)
}
Expand Down Expand Up @@ -154,6 +183,10 @@ func action(ctx *cli.Context) {
log.Fatal(fmt.Sprintf("invalid cache-expiration format: %s", cachestr))
}

restHttps := ctx.Bool("rest-https")
restKey := ctx.String("rest-key")
restCert := ctx.String("rest-cert")

log.Info("Starting pulsed (version: ", gitversion, ")")

// Set Max Processors for pulsed.
Expand Down Expand Up @@ -366,7 +399,11 @@ func action(ctx *cli.Context) {

if !disableAPI {
log.Info("Rest API enabled on port ", apiPort)
r := rest.New()
r, err := rest.New(restHttps, restCert, restKey)
if err != nil {
log.Fatal(err)
return
}
r.BindMetricManager(c)
r.BindTaskManager(s)
if tr != nil {
Expand Down

0 comments on commit 5563872

Please sign in to comment.