ship audit logs to auth server via SSH endpoint

This commit is contained in:
klizhentas 2015-05-11 17:04:22 -07:00
parent 7ca30fa403
commit 09734cea3e
16 changed files with 487 additions and 59 deletions

4
Godeps/Godeps.json generated
View file

@ -44,7 +44,7 @@
},
{
"ImportPath": "github.com/gravitational/form",
"Rev": "733f5ef69c2641b29fd765c52c560550607d58bb"
"Rev": "c517afe011873196a66137df5ab729187250a4f0"
},
{
"ImportPath": "github.com/gravitational/memlog",
@ -52,7 +52,7 @@
},
{
"ImportPath": "github.com/gravitational/roundtrip",
"Rev": "300fc85b1d938507e5b5b2c021ded3e3e84da302"
"Rev": "e7bee8354256fe4ae0d847e4c5d946d3e86f22af"
},
{
"ImportPath": "github.com/gravitational/session",

View file

@ -3,6 +3,8 @@ package form
import (
"fmt"
"mime"
"mime/multipart"
"net/http"
"time"
@ -32,9 +34,19 @@ type Param func(r *http.Request) error
// // handle error here
// }
func Parse(r *http.Request, params ...Param) error {
if err := r.ParseForm(); err != nil {
mtype, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return err
}
if mtype == "multipart/form-data" {
if err := r.ParseMultipartForm(maxMemoryBytes); err != nil {
return err
}
} else {
if err := r.ParseForm(); err != nil {
return err
}
}
for _, p := range params {
if err := p(r); err != nil {
return err
@ -51,7 +63,7 @@ func Duration(name string, out *time.Duration, predicates ...Predicate) Param {
return err
}
}
v := r.PostForm.Get(name)
v := r.Form.Get(name)
if v == "" {
return nil
}
@ -72,7 +84,7 @@ func String(name string, out *string, predicates ...Predicate) Param {
return err
}
}
*out = r.PostForm.Get(name)
*out = r.Form.Get(name)
return nil
}
}
@ -80,7 +92,7 @@ func String(name string, out *string, predicates ...Predicate) Param {
// Int extracts the integer argument in decimal format e.g. "10"
func Int(name string, out *int, predicates ...Predicate) Param {
return func(r *http.Request) error {
v := r.PostForm.Get(name)
v := r.Form.Get(name)
for _, p := range predicates {
if err := p.Pass(name, r); err != nil {
return err
@ -98,6 +110,58 @@ func Int(name string, out *int, predicates ...Predicate) Param {
}
}
// StringSlice extracts the string slice of arguments by name
func StringSlice(name string, out *[]string, predicates ...Predicate) Param {
return func(r *http.Request) error {
for _, p := range predicates {
if err := p.Pass(name, r); err != nil {
return err
}
}
*out = make([]string, len(r.Form[name]))
copy(*out, r.Form[name])
return nil
}
}
// FileSlice reads the files uploaded with name parameter and initialized
// the slice of files. The files should be closed by the callee after
// usage, by executing f.Close() on each of them
// files slice will be nil if there's an error
func FileSlice(name string, files *Files, predicates ...Predicate) Param {
return func(r *http.Request) error {
err := r.ParseMultipartForm(maxMemoryBytes)
if err != nil {
return err
}
if r.MultipartForm == nil && r.MultipartForm.File == nil {
return fmt.Errorf("missing form")
}
for _, p := range predicates {
if err := p.Pass(name, r); err != nil {
return err
}
}
fhs := r.MultipartForm.File[name]
if len(fhs) == 0 {
*files = []multipart.File{}
return nil
}
*files = make([]multipart.File, len(fhs))
for i, fh := range fhs {
f, err := fh.Open()
if err != nil {
files.Close()
return err
}
(*files)[i] = f
}
return nil
}
}
// Predicate provides an extensible way to check various conditions on a variable
// e.g. setting minimums and maximums, or parsing some regular expressions
type Predicate interface {
@ -115,7 +179,7 @@ func (p PredicateFunc) Pass(param string, r *http.Request) error {
// it returns MissingParameterError when parameter is not present
func Required() Predicate {
return PredicateFunc(func(param string, r *http.Request) error {
if r.PostForm.Get(param) == "" {
if r.Form.Get(param) == "" {
return &MissingParameterError{Param: param}
}
return nil
@ -142,3 +206,32 @@ type BadParameterError struct {
func (p *BadParameterError) Error() string {
return fmt.Sprintf("bad parameter '%v', error: %v", p.Param, p.Message)
}
const maxMemoryBytes = 64 * 1024
// Files is a slice of multipart.File that provides additional
// convenient method to close all files as a single operation
type Files []multipart.File
func (fs *Files) Close() error {
e := &FilesCloseError{}
for _, f := range *fs {
if f != nil {
if err := f.Close(); err != nil {
e.Errors = append(e.Errors, err)
}
}
}
if len(e.Errors) != 0 {
return e
}
return nil
}
type FilesCloseError struct {
Errors []error
}
func (p *FilesCloseError) Error() string {
return fmt.Sprintf("failed to close files, error: %v", p.Errors)
}

View file

@ -1,6 +1,11 @@
package form
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/http/httptest"
"net/url"
@ -82,6 +87,103 @@ func (s *FormSuite) TestDurationInvalidFormat(c *C) {
c.Assert(err, FitsTypeOf, &BadParameterError{})
}
func (s *FormSuite) TestMultipartFormOK(c *C) {
var err error
var str string
var i int
var d time.Duration
srv := serveHandler(func(w http.ResponseWriter, r *http.Request) {
err = Parse(r,
String("svar", &str),
Int("ivar", &i),
Duration("dvar", &d),
)
})
defer srv.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
writer.WriteField("svar", "hello")
writer.WriteField("ivar", "77")
writer.WriteField("dvar", "100s")
boundary := writer.Boundary()
c.Assert(writer.Close(), IsNil)
r, err := http.NewRequest("POST", srv.URL, body)
c.Assert(err, IsNil)
r.Header.Set("Content-Type", fmt.Sprintf(`multipart/form-data;boundary="%v"`, boundary))
_, err = http.DefaultClient.Do(r)
c.Assert(err, IsNil)
c.Assert(str, Equals, "hello")
c.Assert(i, Equals, 77)
c.Assert(d, Equals, 100*time.Second)
}
func (s *FormSuite) TestStringSliceOK(c *C) {
var err error
var slice []string
var empty []string
srv := serveHandler(func(w http.ResponseWriter, r *http.Request) {
err = Parse(r,
StringSlice("slice", &slice),
StringSlice("empty", &empty),
)
})
defer srv.Close()
http.PostForm(srv.URL, url.Values{
"slice": []string{"hello1", "hello2"},
})
c.Assert(err, IsNil)
c.Assert(slice, DeepEquals, []string{"hello1", "hello2"})
c.Assert(empty, DeepEquals, []string{})
}
func (s *FormSuite) TestFileSliceOK(c *C) {
var err error
var files Files
var values []string
srv := serveHandler(func(w http.ResponseWriter, r *http.Request) {
err = Parse(r,
FileSlice("file", &files),
)
c.Assert(err, IsNil)
values = make([]string, len(files))
for i, f := range files {
out, err := ioutil.ReadAll(f)
c.Assert(err, IsNil)
values[i] = string(out)
}
})
defer srv.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// upload multiple files with the same name
for _, data := range []string{"file 1", "file 2"} {
w, err := writer.CreateFormFile("file", "file.json")
c.Assert(err, IsNil)
_, err = io.WriteString(w, data)
c.Assert(err, IsNil)
}
boundary := writer.Boundary()
c.Assert(writer.Close(), IsNil)
req, err := http.NewRequest("POST", srv.URL, body)
req.Header.Set("Content-Type",
fmt.Sprintf(`multipart/form-data;boundary="%v"`, boundary))
c.Assert(err, IsNil)
_, err = http.DefaultClient.Do(req)
c.Assert(err, IsNil)
c.Assert(values, DeepEquals, []string{"file 1", "file 2"})
}
func serveHandler(f http.HandlerFunc) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(f))
}

View file

@ -24,6 +24,7 @@ import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
@ -85,11 +86,47 @@ func (c *Client) Endpoint(params ...string) string {
//
// c.PostForm(c.Endpoint("users"), url.Values{"name": []string{"John"}})
//
func (c *Client) PostForm(endpoint string, vals url.Values) (*Response, error) {
func (c *Client) PostForm(endpoint string, vals url.Values, files ...File) (*Response, error) {
return c.RoundTrip(func() (*http.Response, error) {
return c.client.Post(
endpoint, "application/x-www-form-urlencoded",
strings.NewReader(vals.Encode()))
if len(files) == 0 {
return c.client.Post(
endpoint, "application/x-www-form-urlencoded",
strings.NewReader(vals.Encode()))
}
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// write simple fields
for name, vals := range vals {
for _, val := range vals {
if err := writer.WriteField(name, val); err != nil {
return nil, err
}
}
}
// add files
for _, f := range files {
w, err := writer.CreateFormFile(f.Name, f.Filename)
if err != nil {
return nil, err
}
_, err = io.Copy(w, f.Reader)
if err != nil {
return nil, err
}
}
boundary := writer.Boundary()
if err := writer.Close(); err != nil {
return nil, err
}
req, err := http.NewRequest("POST", endpoint, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type",
fmt.Sprintf(`multipart/form-data;boundary="%v"`, boundary))
return c.client.Do(req)
})
}
@ -166,3 +203,10 @@ func (r *Response) Reader() io.Reader {
func (r *Response) Bytes() []byte {
return r.body.Bytes()
}
// File is a file-like object that can be posted to the files
type File struct {
Name string
Filename string
Reader io.Reader
}

View file

@ -2,9 +2,11 @@ package roundtrip
import (
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
@ -99,6 +101,58 @@ func (s *ClientSuite) TestCustomClient(c *C) {
c.Assert(err, NotNil)
}
func (s *ClientSuite) TestPostMultipartForm(c *C) {
var u *url.URL
var params url.Values
var method string
var data []string
srv := serveHandler(func(w http.ResponseWriter, r *http.Request) {
u = r.URL
c.Assert(r.ParseMultipartForm(64<<20), IsNil)
params = r.Form
method = r.Method
c.Assert(r.MultipartForm, NotNil)
c.Assert(len(r.MultipartForm.File["a"]), Not(Equals), 0)
fhs := r.MultipartForm.File["a"]
for _, fh := range fhs {
f, err := fh.Open()
c.Assert(err, IsNil)
val, err := ioutil.ReadAll(f)
c.Assert(err, IsNil)
data = append(data, string(val))
}
io.WriteString(w, "hello back")
})
defer srv.Close()
clt := newC(srv.URL, "v1")
values := url.Values{"a": []string{"b"}}
out, err := clt.PostForm(
clt.Endpoint("a", "b"),
values,
File{
Name: "a",
Filename: "a.json",
Reader: strings.NewReader("file 1")},
File{
Name: "a",
Filename: "a.json",
Reader: strings.NewReader("file 2")},
)
c.Assert(err, IsNil)
c.Assert(string(out.Bytes()), Equals, "hello back")
c.Assert(u.String(), DeepEquals, "/v1/a/b")
c.Assert(method, Equals, "POST")
c.Assert(params, DeepEquals, values)
c.Assert(data, DeepEquals, []string{"file 1", "file 2"})
}
func newC(addr, version string) *testClient {
c, err := NewClient(addr, version)
if err != nil {

View file

@ -4,6 +4,7 @@ import (
"net"
"net/http"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/memlog"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/log"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/oxy/trace"
"github.com/gravitational/teleport/utils"
@ -15,7 +16,8 @@ func StartHTTPServer(a string, srv *AuthServer) error {
return err
}
t, err := trace.New(
NewAPIServer(srv), log.GetLogger().Writer(log.SeverityInfo))
NewAPIServer(srv, memlog.New()),
log.GetLogger().Writer(log.SeverityInfo))
if err != nil {
return err
}

View file

@ -22,10 +22,12 @@ import (
type Authority interface {
GenerateKeyPair(passphrase string) (privKey []byte, pubKey []byte, err error)
// GenerateHostCert generates host certificate, it takes pkey as a signing private key (host certificate authority)
// GenerateHostCert generates host certificate, it takes pkey as a signing
// private key (host certificate authority)
GenerateHostCert(pkey, key []byte, id, hostname string, ttl time.Duration) ([]byte, error)
// GenerateHostCert generates user certificate, it takes pkey as a signing private key (user certificate authority)
// GenerateHostCert generates user certificate, it takes pkey as a signing
// private key (user certificate authority)
GenerateUserCert(pkey, key []byte, id, username string, ttl time.Duration) ([]byte, error)
}
@ -43,7 +45,8 @@ func NewAuthServer(b backend.Backend, a Authority, scrt *secret.Service) *AuthSe
}
}
// AuthServer implements key signing, generation and ACL functionality used by teleport
// AuthServer implements key signing, generation and ACL functionality
// used by teleport
type AuthServer struct {
b backend.Backend
a Authority
@ -59,9 +62,12 @@ func (s *AuthServer) GetServers() ([]backend.Server, error) {
}
// UpsertUserKey takes user's public key, generates certificate for it
// and adds it to the authorized keys database. It returns certificate signed by user CA in case of success,
// error otherwise. The certificate will be valid for the duration of the ttl passed in.
func (s *AuthServer) UpsertUserKey(user string, key backend.AuthorizedKey, ttl time.Duration) ([]byte, error) {
// and adds it to the authorized keys database. It returns certificate signed
// by user CA in case of success, error otherwise. The certificate will be
// valid for the duration of the ttl passed in.
func (s *AuthServer) UpsertUserKey(
user string, key backend.AuthorizedKey, ttl time.Duration) ([]byte, error) {
cert, err := s.GenerateUserCert(key.Value, key.ID, user, ttl)
if err != nil {
return nil, err
@ -90,7 +96,7 @@ func (s *AuthServer) DeleteUserKey(user, key string) error {
return s.b.DeleteUserKey(user, key)
}
// GenerateKeyPair generates private and public key pair of OpenSSH style certificates
// GenerateKeyPair generates private and public key pair of OpenSSH certs
func (s *AuthServer) GenerateKeyPair(pass string) ([]byte, []byte, error) {
return s.a.GenerateKeyPair(pass)
}
@ -123,8 +129,11 @@ func (s *AuthServer) GetUserCAPub() ([]byte, error) {
return s.b.GetUserCAPub()
}
// GenerateHostCert generates host certificate, it takes pkey as a signing private key (host certificate authority)
func (s *AuthServer) GenerateHostCert(key []byte, id, hostname string, ttl time.Duration) ([]byte, error) {
// GenerateHostCert generates host certificate, it takes pkey as a signing
// private key (host certificate authority)
func (s *AuthServer) GenerateHostCert(
key []byte, id, hostname string, ttl time.Duration) ([]byte, error) {
hk, err := s.b.GetHostCA()
if err != nil {
return nil, err
@ -132,8 +141,11 @@ func (s *AuthServer) GenerateHostCert(key []byte, id, hostname string, ttl time.
return s.a.GenerateHostCert(hk.Priv, key, id, hostname, ttl)
}
// GenerateHostCert generates user certificate, it takes pkey as a signing private key (user certificate authority)
func (s *AuthServer) GenerateUserCert(key []byte, id, username string, ttl time.Duration) ([]byte, error) {
// GenerateHostCert generates user certificate, it takes pkey as a signing
// private key (user certificate authority)
func (s *AuthServer) GenerateUserCert(
key []byte, id, username string, ttl time.Duration) ([]byte, error) {
hk, err := s.b.GetUserCA()
if err != nil {
return nil, err
@ -287,18 +299,21 @@ func (s *AuthServer) DeleteWebTun(prefix string) error {
return s.b.DeleteWebTun(prefix)
}
// make sure password satisfies our requirements (relaxed), mostly to avoid putting garbage in
// make sure password satisfies our requirements (relaxed),
// mostly to avoid putting garbage in
func verifyPassword(password []byte) error {
if len(password) < MinPasswordLength {
return &BadParameterError{
Param: "password",
Msg: fmt.Sprintf("password is too short, min length is %v", MinPasswordLength),
Msg: fmt.Sprintf(
"password is too short, min length is %v", MinPasswordLength),
}
}
if len(password) > MaxPasswordLength {
return &BadParameterError{
Param: "password",
Msg: fmt.Sprintf("password is too long, max length is %v", MaxPasswordLength),
Msg: fmt.Sprintf(
"password is too long, max length is %v", MaxPasswordLength),
}
}
return nil

View file

@ -1,6 +1,7 @@
package auth
import (
"bytes"
"encoding/json"
"fmt"
"net"
@ -51,8 +52,8 @@ func (c *Client) convertResponse(re *roundtrip.Response, err error) (*roundtrip.
return re, nil
}
func (c *Client) PostForm(endpoint string, vals url.Values) (*roundtrip.Response, error) {
return c.convertResponse(c.Client.PostForm(endpoint, vals))
func (c *Client) PostForm(endpoint string, vals url.Values, files ...roundtrip.File) (*roundtrip.Response, error) {
return c.convertResponse(c.Client.PostForm(endpoint, vals, files...))
}
func (c *Client) Get(u string, params url.Values) (*roundtrip.Response, error) {
@ -78,6 +79,31 @@ func (c *Client) GenerateToken(fqdn string, ttl time.Duration) (string, error) {
return re.Token, nil
}
func (c *Client) SubmitEvents(events [][]byte) error {
files := make([]roundtrip.File, len(events))
for i, e := range events {
files[i] = roundtrip.File{
Name: "event",
Filename: "event.json",
Reader: bytes.NewReader(e),
}
}
_, err := c.PostForm(c.Endpoint("events"), url.Values{}, files...)
return err
}
func (c *Client) GetEvents() ([]interface{}, error) {
out, err := c.Get(c.Endpoint("events"), url.Values{})
if err != nil {
return nil, err
}
var re *eventsResponse
if err := json.Unmarshal(out.Bytes(), &re); err != nil {
return nil, err
}
return re.Events, nil
}
func (c *Client) UpsertServer(s backend.Server, ttl time.Duration) error {
_, err := c.PostForm(c.Endpoint("servers"), url.Values{
"id": []string{string(s.ID)},
@ -326,3 +352,22 @@ func (c *Client) ResetUserCA() error {
_, err := c.PostForm(c.Endpoint("ca", "user", "keys"), url.Values{})
return err
}
func (c *Client) GetLogWriter() *LogWriter {
return &LogWriter{clt: c}
}
type LogWriter struct {
clt *Client
}
func (w *LogWriter) Write(data []byte) (int, error) {
if err := w.clt.SubmitEvents([][]byte{data}); err != nil {
return 0, err
}
return len(data), nil
}
func (w *LogWriter) LastEvents() ([]interface{}, error) {
return w.clt.GetEvents()
}

View file

@ -3,10 +3,13 @@ package auth
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/form"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/memlog"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/roundtrip"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/session"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/log"
@ -21,12 +24,14 @@ type Config struct {
// APISrv implements http API server for authority
type APIServer struct {
httprouter.Router
s *AuthServer
s *AuthServer
elog memlog.Logger
}
func NewAPIServer(s *AuthServer) *APIServer {
func NewAPIServer(s *AuthServer, elog memlog.Logger) *APIServer {
srv := &APIServer{
s: s,
s: s,
elog: elog,
}
srv.Router = *httprouter.New()
@ -75,6 +80,10 @@ func NewAPIServer(s *AuthServer) *APIServer {
// Tokens
srv.POST("/v1/tokens", srv.generateToken)
// Events
srv.POST("/v1/events", srv.submitEvents)
srv.GET("/v1/events", srv.getEvents)
return srv
}
@ -435,6 +444,51 @@ func (s *APIServer) generateToken(w http.ResponseWriter, r *http.Request, _ http
reply(w, http.StatusOK, tokenResponse{Token: string(token)})
}
func (s *APIServer) submitEvents(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var events form.Files
err := form.Parse(r,
form.FileSlice("event", &events))
if err != nil {
reply(w, http.StatusBadRequest, err.Error())
}
if len(events) == 0 {
reply(w, http.StatusBadRequest,
fmt.Errorf("at least one event is required"))
}
defer func() {
// don't let get the error get lost
if err := events.Close(); err != nil {
log.Errorf("failed to close files: %v", err)
}
}()
// submit events
for _, e := range events {
data, err := ioutil.ReadAll(e)
if err != nil {
log.Errorf("failed to read event: %v", err)
reply(w, http.StatusBadRequest, fmt.Errorf("failed to read event"))
return
}
if _, err := s.elog.Write(data); err != nil {
log.Errorf("failed to write event: %v", err)
reply(w, http.StatusInternalServerError,
fmt.Errorf("failed to write event"))
return
}
}
reply(w, http.StatusOK, message("events submitted"))
}
func (s *APIServer) getEvents(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
roundtrip.ReplyJSON(
w, http.StatusOK, &eventsResponse{Events: s.elog.LastEvents()})
}
func replyErr(w http.ResponseWriter, e error) {
switch err := e.(type) {
case *backend.NotFoundError:
@ -505,6 +559,10 @@ type tokenResponse struct {
Token string `json:"token"`
}
type eventsResponse struct {
Events []interface{} `json:"events"`
}
func message(msg string) map[string]interface{} {
return map[string]interface{}{"message": msg}
}

View file

@ -10,6 +10,7 @@ import (
"github.com/gravitational/teleport/backend"
"github.com/gravitational/teleport/backend/membk"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/memlog"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/lemma/secret"
. "github.com/gravitational/teleport/Godeps/_workspace/src/gopkg.in/check.v1"
)
@ -37,7 +38,7 @@ func (s *APISuite) SetUpSuite(c *C) {
func (s *APISuite) SetUpTest(c *C) {
s.bk = membk.New()
s.a = NewAuthServer(s.bk, openssh.New(), s.scrt)
s.srv = httptest.NewServer(NewAPIServer(s.a))
s.srv = httptest.NewServer(NewAPIServer(s.a, memlog.New()))
clt, err := NewClient(s.srv.URL)
c.Assert(err, IsNil)
s.clt = clt
@ -232,6 +233,22 @@ func (s *APISuite) TestServers(c *C) {
c.Assert(servers, DeepEquals, expected)
}
func (s *APISuite) TestEvents(c *C) {
err := s.clt.SubmitEvents(
[][]byte{
[]byte(`{"e": "event 1"}`),
[]byte(`{"e": "event 2"}`)})
c.Assert(err, IsNil)
out, err := s.clt.GetEvents()
c.Assert(err, IsNil)
expected := []interface{}{
map[string]interface{}{"e": "event 2"},
map[string]interface{}{"e": "event 1"},
}
c.Assert(out, DeepEquals, expected)
}
func (s *APISuite) TestTokens(c *C) {
out, err := s.clt.GenerateToken("a.example.com", 0)
c.Assert(err, IsNil)

View file

@ -14,6 +14,7 @@ import (
"github.com/gravitational/teleport/sshutils"
"github.com/gravitational/teleport/utils"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/memlog"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/lemma/secret"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/log"
"github.com/gravitational/teleport/Godeps/_workspace/src/golang.org/x/crypto/ssh"
@ -48,7 +49,7 @@ func (s *TunSuite) TearDownTest(c *C) {
func (s *TunSuite) SetUpTest(c *C) {
s.bk = membk.New()
s.a = NewAuthServer(s.bk, openssh.New(), s.scrt)
s.srv = httptest.NewServer(NewAPIServer(s.a))
s.srv = httptest.NewServer(NewAPIServer(s.a, memlog.New()))
// set up host private key and certificate
c.Assert(s.a.ResetHostCA(""), IsNil)
@ -81,7 +82,7 @@ func (s *TunSuite) TestUnixServerClient(c *C) {
l, err := net.Listen("unix", socketPath)
c.Assert(err, IsNil)
h := NewAPIServer(s.a)
h := NewAPIServer(s.a, memlog.New())
srv := &httptest.Server{
Listener: l,
Config: &http.Server{

View file

@ -597,7 +597,7 @@ func assets_static_js_grv_servers_js() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "assets/static/js/grv/servers.js", size: 3751, mode: os.FileMode(436), modTime: time.Unix(1431289694, 0)}
info := bindata_file_info{name: "assets/static/js/grv/servers.js", size: 3751, mode: os.FileMode(436), modTime: time.Unix(1431295739, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -617,7 +617,7 @@ func assets_static_js_grv_webtuns_js() (*asset, error) {
return nil, err
}
info := bindata_file_info{name: "assets/static/js/grv/webtuns.js", size: 6230, mode: os.FileMode(436), modTime: time.Unix(1431290735, 0)}
info := bindata_file_info{name: "assets/static/js/grv/webtuns.js", size: 6230, mode: os.FileMode(436), modTime: time.Unix(1431295739, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

View file

@ -11,7 +11,6 @@ import (
"github.com/gravitational/teleport/utils"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/form"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/memlog"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/roundtrip"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/log"
@ -20,14 +19,12 @@ import (
// cpHandler implements methods for control panel
type cpHandler struct {
httprouter.Router
events memlog.Logger
host string
authServers []utils.NetAddr
}
func newCPHandler(host string, auth []utils.NetAddr, events memlog.Logger) *cpHandler {
func newCPHandler(host string, auth []utils.NetAddr) *cpHandler {
h := &cpHandler{
events: events,
authServers: auth,
host: host,
}
@ -141,7 +138,13 @@ func (s *cpHandler) upsertWebTun(w http.ResponseWriter, r *http.Request, _ httpr
}
func (s *cpHandler) getEvents(w http.ResponseWriter, r *http.Request, _ httprouter.Params, c *ctx) {
roundtrip.ReplyJSON(w, http.StatusOK, s.events.LastEvents())
events, err := c.clt.GetEvents()
if err != nil {
log.Errorf("failed to retrieve events: %v")
replyErr(w, http.StatusInternalServerError, err)
return
}
roundtrip.ReplyJSON(w, http.StatusOK, events)
}
func (s *cpHandler) keysIndex(w http.ResponseWriter, r *http.Request, _ httprouter.Params, _ *ctx) {

View file

@ -4,7 +4,6 @@ import (
"fmt"
"net/http"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/memlog"
"github.com/gravitational/teleport/utils"
)
@ -16,7 +15,6 @@ type CPServer struct {
type Config struct {
AuthSrv []utils.NetAddr
LogSrv memlog.Logger
Host string
}
@ -24,13 +22,10 @@ func NewServer(cfg Config) (*CPServer, error) {
if len(cfg.AuthSrv) == 0 {
return nil, fmt.Errorf("need at least one auth server")
}
if cfg.LogSrv == nil {
return nil, fmt.Errorf("need an event logger service")
}
if cfg.Host == "" {
return nil, fmt.Errorf("need an base host")
}
cp := newCPHandler(cfg.Host, cfg.AuthSrv, cfg.LogSrv)
cp := newCPHandler(cfg.Host, cfg.AuthSrv)
proxy := newProxyHandler(cp, cfg.AuthSrv, cfg.Host)
return &CPServer{
cfg: cfg,

View file

@ -20,7 +20,6 @@ import (
type TeleportService struct {
Supervisor
log memlog.Logger
}
func NewTeleport(cfg Config) (*TeleportService, error) {
@ -34,7 +33,6 @@ func NewTeleport(cfg Config) (*TeleportService, error) {
t := &TeleportService{}
t.Supervisor = *New()
t.log = memlog.New()
if cfg.Auth.Enabled {
if err := initAuth(t, cfg); err != nil {
@ -77,7 +75,7 @@ func initAuth(t *TeleportService, cfg Config) error {
// register HTTP API endpoint
t.RegisterFunc(func() error {
apisrv := auth.NewAPIServer(asrv)
apisrv := auth.NewAPIServer(asrv, memlog.New())
t, err := trace.New(apisrv, log.GetLogger().Writer(log.SeverityInfo))
if err != nil {
log.Fatalf("failed to start: %v", err)
@ -115,7 +113,6 @@ func initCP(t *TeleportService, cfg Config) error {
}
csrv, err := cp.NewServer(cp.Config{
AuthSrv: cfg.AuthServers,
LogSrv: t.log,
Host: cfg.CP.Domain,
})
if err != nil {
@ -150,17 +147,17 @@ func initSSH(t *TeleportService, cfg Config) error {
func initSSHEndpoint(t *TeleportService, cfg Config) error {
signer, err := auth.ReadKeys(cfg.FQDN, cfg.DataDir)
elog := &FanOutEventLogger{
Loggers: []lunk.EventLogger{
lunk.NewTextEventLogger(log.GetLogger().Writer(log.SeverityInfo)),
lunk.NewJSONEventLogger(t.log),
}}
client, err := auth.NewTunClient(
cfg.AuthServers[0],
cfg.FQDN,
[]ssh.AuthMethod{ssh.PublicKeys(signer)})
elog := &FanOutEventLogger{
Loggers: []lunk.EventLogger{
lunk.NewTextEventLogger(log.GetLogger().Writer(log.SeverityInfo)),
lunk.NewJSONEventLogger(client.GetLogWriter()),
}}
s, err := srv.New(cfg.SSH.Addr,
[]ssh.Signer{signer},
client,

View file

@ -9,12 +9,14 @@ import (
"strings"
"testing"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/lemma/secret"
"github.com/gravitational/teleport/auth"
"github.com/gravitational/teleport/auth/openssh"
"github.com/gravitational/teleport/backend/membk"
"github.com/gravitational/teleport/utils"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/gravitational/memlog"
"github.com/gravitational/teleport/Godeps/_workspace/src/github.com/mailgun/lemma/secret"
. "github.com/gravitational/teleport/Godeps/_workspace/src/gopkg.in/check.v1"
)
@ -46,7 +48,7 @@ func (s *CmdSuite) SetUpSuite(c *C) {
func (s *CmdSuite) SetUpTest(c *C) {
s.bk = membk.New()
s.asrv = auth.NewAuthServer(s.bk, openssh.New(), s.scrt)
s.srv = httptest.NewServer(auth.NewAPIServer(s.asrv))
s.srv = httptest.NewServer(auth.NewAPIServer(s.asrv, memlog.New()))
u, err := url.Parse(s.srv.URL)
c.Assert(err, IsNil)