Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kuma-cp) server for managing provided ca #473

Merged
merged 10 commits into from
Dec 11, 2019
42 changes: 39 additions & 3 deletions pkg/admin-server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import (
"context"
"fmt"
admin_server "github.com/Kong/kuma/pkg/config/admin-server"
config_core "github.com/Kong/kuma/pkg/config/core"
"github.com/Kong/kuma/pkg/core"
"github.com/Kong/kuma/pkg/core/ca/provided/rest"
ca_provided_rest "github.com/Kong/kuma/pkg/core/ca/provided/rest"
"github.com/Kong/kuma/pkg/core/runtime"
"github.com/Kong/kuma/pkg/tokens/builtin"
tokens_server "github.com/Kong/kuma/pkg/tokens/builtin/server"
"github.com/emicklei/go-restful"
"github.com/pkg/errors"
"go.uber.org/multierr"
"net/http"
)
Expand Down Expand Up @@ -72,7 +76,39 @@ func (a *AdminServer) startHttpServer() (*http.Server, chan error) {
}

func SetupServer(rt runtime.Runtime) error {
ws := rest.NewWebservice(rt.ProvidedCaManager())
srv := NewAdminServer(*rt.Config().AdminServer, ws)
var webservices []*restful.WebService

ws := ca_provided_rest.NewWebservice(rt.ProvidedCaManager())
webservices = append(webservices, ws)

ws, err := dataplaneTokenWs(rt)
if err != nil {
return err
}
if ws != nil {
webservices = append(webservices, ws)
}

srv := NewAdminServer(*rt.Config().AdminServer, webservices...)
return rt.Add(srv)
}

func dataplaneTokenWs(rt runtime.Runtime) (*restful.WebService, error) {
if !rt.Config().AdminServer.DataplaneTokenWs.Enabled {
log.Info("Dataplane Token Webservice disabled")
return nil, nil
}

switch env := rt.Config().Environment; env {
case config_core.KubernetesEnvironment:
return nil, nil
case config_core.UniversalEnvironment:
generator, err := builtin.NewDataplaneTokenIssuer(rt)
if err != nil {
return nil, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a swallowed error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

}
return tokens_server.NewWebservice(generator), nil
default:
return nil, errors.Errorf("unknown environment type %s", env)
}
}
37 changes: 29 additions & 8 deletions pkg/config/admin-server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,60 @@ import (

func DefaultAdminServerConfig() *AdminServerConfig {
return &AdminServerConfig{
Enabled: true,
Local: DefaultLocalAdminServerConfig(),
Public: DefaultPublicAdminServerConfig(),
DataplaneTokenWs: DefaultDataplaneTokenWsConfig(),
Local: DefaultLocalAdminServerConfig(),
Public: DefaultPublicAdminServerConfig(),
}
}

var _ config.Config = &AdminServerConfig{}

// Admin Server configuration
type AdminServerConfig struct {
// If true then Admin Server and token verification is enabled
Enabled bool `yaml:"enabled" envconfig:"kuma_admin_server_enabled"`
// Configuration for Dataplane Token Webservice
DataplaneTokenWs *DataplaneTokenWsConfig `yaml:"dataplaneTokenWs"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name dataplaneTokenWs that will appear in YAML and env variables is too implementation-specific.

Let's use the same approach as in Catalog.

E.g.,

type AdminServerConfig struct {
   Apis *ApisConfg
}

type ApisConfig struct {
  DataplaneToken *DataplaneTokenApiConfig
}

type DataplaneTokenApiConfig struct {}

// Local configuration of server that is available only on localhost
Local *LocalAdminServerConfig `yaml:"local"`
// Public configuration of server that is available on public interface
Public *PublicAdminServerConfig `yaml:"public"`
}

var _ config.Config = &DataplaneTokenWsConfig{}

func DefaultDataplaneTokenWsConfig() *DataplaneTokenWsConfig {
return &DataplaneTokenWsConfig{
Enabled: true,
}
}

type DataplaneTokenWsConfig struct {
// If true then Dataplane Token WS and token verification is enabled
Enabled bool `yaml:"enabled" envconfig:"kuma_admin_server_dataplane_token_ws_enabled"`
}

func (d *DataplaneTokenWsConfig) Sanitize() {
}

func (d *DataplaneTokenWsConfig) Validate() error {
return nil
}

func (i *AdminServerConfig) Sanitize() {
i.DataplaneTokenWs.Sanitize()
i.Public.Sanitize()
i.Local.Sanitize()
}

func (i *AdminServerConfig) Validate() error {
if err := i.DataplaneTokenWs.Validate(); err != nil {
return errors.Wrap(err, "Dataplane Token Ws validation failed")
}
if err := i.Local.Validate(); err != nil {
return errors.Wrap(err, "Local validation failed")
}
if err := i.Public.Validate(); err != nil {
return errors.Wrap(err, "Public validation failed")
}
if !i.Enabled && i.Public.Enabled {
return errors.New("Public.Enabled cannot be true when server is disabled.")
}
return nil
}

Expand Down
61 changes: 61 additions & 0 deletions pkg/tokens/builtin/server/webservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package server

import (
"github.com/Kong/kuma/pkg/core/rest/errors"
"github.com/Kong/kuma/pkg/core/validators"
"github.com/Kong/kuma/pkg/tokens/builtin/issuer"
"github.com/Kong/kuma/pkg/tokens/builtin/server/types"
"github.com/emicklei/go-restful"
"net/http"
)

type DataplaneTokenWebService struct {
Issuer issuer.DataplaneTokenIssuer
}

func NewWebservice(issuer issuer.DataplaneTokenIssuer) *restful.WebService {
ws := DataplaneTokenWebService{
Issuer: issuer,
}
return ws.createWs()
}

func (d *DataplaneTokenWebService) createWs() *restful.WebService {
ws := new(restful.WebService).
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
ws.Path("/tokens").
Route(ws.POST("").To(d.handleIdentityRequest))
return ws
}

func (d *DataplaneTokenWebService) handleIdentityRequest(request *restful.Request, response *restful.Response) {
idReq := types.DataplaneTokenRequest{}
if err := request.ReadEntity(&idReq); err != nil {
log.Error(err, "Could not read a request")
response.WriteHeader(http.StatusInternalServerError)
return
}
verr := validators.ValidationError{}
if idReq.Name == "" {
verr.AddViolation("name", "cannot be empty")
}
if idReq.Mesh == "" {
verr.AddViolation("mesh", "cannot be empty")
}
if verr.HasViolations() {
errors.HandleError(response, verr.OrNil(), "Invalid request")
return
}

token, err := d.Issuer.Generate(idReq.ToProxyId())
if err != nil {
errors.HandleError(response, err, "Could not issue a token")
return
}

response.Header().Set("content-type", "text/plain")
if _, err := response.Write([]byte(token)); err != nil {
log.Error(err, "Could write a response")
}
}