Skip to content

Commit

Permalink
fix: use process-isolated Jsonnet VM (#2869)
Browse files Browse the repository at this point in the history
Co-authored-by: hperl <34397+hperl@users.noreply.github.com>
  • Loading branch information
aeneasr and hperl authored Nov 8, 2022
1 parent 8fceadc commit 9eeedc0
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 74 deletions.
3 changes: 2 additions & 1 deletion cmd/jsonnet/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"

"github.com/google/go-jsonnet"
"github.com/google/go-jsonnet/linter"
"github.com/spf13/cobra"

Expand All @@ -30,7 +31,7 @@ func NewJsonnetLintCmd() *cobra.Command {
` + GlobHelp,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
vm := jsonnetsecure.MakeSecureVM()
vm := jsonnetsecure.MakeSecureVM().(*jsonnet.VM)

for _, pattern := range args {
files, err := filepath.Glob(pattern)
Expand Down
5 changes: 4 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"os"

"github.com/ory/kratos/cmd/cleanup"

"github.com/ory/kratos/driver/config"
"github.com/ory/x/jsonnetsecure"

"github.com/ory/kratos/cmd/courier"
"github.com/ory/kratos/cmd/hashers"
Expand Down Expand Up @@ -44,6 +44,9 @@ func NewRootCmd() (cmd *cobra.Command) {
cmd.AddCommand(identities.NewValidateCmd())
cmd.AddCommand(cmdx.Version(&config.Version, &config.Commit, &config.Date))

// Registers a hidden "jsonnet" subcommand for process-isolated Jsonnet VMs.
cmd.AddCommand(jsonnetsecure.NewJsonnetCmd())

return cmd
}

Expand Down
2 changes: 2 additions & 0 deletions courier/courier.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/ory/kratos/courier/template"
"github.com/ory/x/jsonnetsecure"

"github.com/cenkalti/backoff"
"github.com/gofrs/uuid"
Expand All @@ -21,6 +22,7 @@ type (
x.LoggingProvider
ConfigProvider
x.HTTPClientProvider
jsonnetsecure.VMProvider
}

Courier interface {
Expand Down
4 changes: 2 additions & 2 deletions courier/sms.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ func (c *courier) dispatchSMS(ctx context.Context, msg Message) error {
return err
}

builder, err := request.NewBuilder(c.smsClient.RequestConfig, c.deps.HTTPClient(ctx), c.deps.Logger())
builder, err := request.NewBuilder(c.smsClient.RequestConfig, c.deps)
if err != nil {
return err
}

req, err := builder.BuildRequest(&sendSMSRequestBody{
req, err := builder.BuildRequest(ctx, &sendSMSRequestBody{
To: msg.Recipient,
From: c.deps.CourierConfig().CourierSMSFrom(ctx),
Body: body,
Expand Down
3 changes: 3 additions & 0 deletions driver/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/ory/x/contextx"
"github.com/ory/x/jsonnetsecure"
"github.com/ory/x/otelx"
prometheus "github.com/ory/x/prometheusx"

Expand Down Expand Up @@ -48,6 +49,7 @@ type Registry interface {
Init(ctx context.Context, ctxer contextx.Contextualizer, opts ...RegistryOption) error

WithLogger(l *logrusx.Logger) Registry
WithJsonnetVMProvider(jsonnetsecure.VMProvider) Registry

WithCSRFHandler(c nosurf.Handler)
WithCSRFTokenGenerator(cg x.CSRFToken)
Expand All @@ -73,6 +75,7 @@ type Registry interface {
x.WriterProvider
x.LoggingProvider
x.HTTPClientProvider
jsonnetsecure.VMProvider

continuity.ManagementProvider
continuity.PersistenceProvider
Expand Down
17 changes: 16 additions & 1 deletion driver/registry_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/ory/x/contextx"
"github.com/ory/x/jsonnetsecure"

"github.com/ory/x/popx"

Expand Down Expand Up @@ -154,6 +155,15 @@ type RegistryDefault struct {
buildDate string

csrfTokenGenerator x.CSRFToken

jsonnetVMProvider jsonnetsecure.VMProvider
}

func (m *RegistryDefault) JsonnetVM(ctx context.Context) (jsonnetsecure.VM, error) {
if m.jsonnetVMProvider == nil {
m.jsonnetVMProvider = &jsonnetsecure.DefaultProvider{Subcommand: "jsonnet"}
}
return m.jsonnetVMProvider.JsonnetVM(ctx)
}

func (m *RegistryDefault) Audit() *logrusx.Logger {
Expand Down Expand Up @@ -223,6 +233,11 @@ func (m *RegistryDefault) WithLogger(l *logrusx.Logger) Registry {
return m
}

func (m *RegistryDefault) WithJsonnetVMProvider(p jsonnetsecure.VMProvider) Registry {
m.jsonnetVMProvider = p
return m
}

func (m *RegistryDefault) LogoutHandler() *logout.Handler {
if m.selfserviceLogoutHandler == nil {
m.selfserviceLogoutHandler = logout.NewHandler(m)
Expand Down Expand Up @@ -350,7 +365,7 @@ func (m *RegistryDefault) AllLoginStrategies() login.Strategies {
return loginStrategies
}

func (m *RegistryDefault) ActiveCredentialsCounterStrategies(ctx context.Context) (activeCredentialsCounterStrategies []identity.ActiveCredentialsCounter) {
func (m *RegistryDefault) ActiveCredentialsCounterStrategies(_ context.Context) (activeCredentialsCounterStrategies []identity.ActiveCredentialsCounter) {
for _, strategy := range m.selfServiceStrategies() {
if s, ok := strategy.(identity.ActiveCredentialsCounter); ok {
activeCredentialsCounterStrategies = append(activeCredentialsCounterStrategies, s)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ require (
github.com/ory/kratos-client-go v0.6.3-alpha.1
github.com/ory/mail/v3 v3.0.0
github.com/ory/nosurf v1.2.7
github.com/ory/x v0.0.510
github.com/ory/x v0.0.511-0.20221108105728-3fed9bc99daf
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,8 @@ github.com/ory/x v0.0.506 h1:kqkGwRuhZsyLf2zgjIyOkuY1nRfbWu2aPOQTk03j6ZQ=
github.com/ory/x v0.0.506/go.mod h1:xUtRpoiRARyJNPVk/fcCNKzyp25Foxt9GPlj8pd7egY=
github.com/ory/x v0.0.510 h1:DksQNoq7ssdR76mhcNxzaGPKmzaQEKWg2kannGsD8+w=
github.com/ory/x v0.0.510/go.mod h1:xUtRpoiRARyJNPVk/fcCNKzyp25Foxt9GPlj8pd7egY=
github.com/ory/x v0.0.511-0.20221108105728-3fed9bc99daf h1:sPEIGYHzmEu4tPiRUYgKfXPOAqneNuB1aaKvrT6aBXE=
github.com/ory/x v0.0.511-0.20221108105728-3fed9bc99daf/go.mod h1:xUtRpoiRARyJNPVk/fcCNKzyp25Foxt9GPlj8pd7egY=
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
Expand Down
2 changes: 2 additions & 0 deletions internal/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/ory/x/contextx"
"github.com/ory/x/jsonnetsecure"

"github.com/gofrs/uuid"

Expand Down Expand Up @@ -61,6 +62,7 @@ func NewFastRegistryWithMocks(t *testing.T) (*config.Config, *driver.RegistryDef
return &hook.Error{Config: c.Config}
},
})
reg.WithJsonnetVMProvider(jsonnetsecure.NewTestProvider(t))

require.NoError(t, reg.Persister().MigrateUp(context.Background()))
require.NotEqual(t, uuid.Nil, reg.Persister().NetworkID(context.Background()))
Expand Down
75 changes: 45 additions & 30 deletions request/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ package request

import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"reflect"
"strings"

"github.com/ory/x/jsonnetsecure"

"github.com/pkg/errors"

"github.com/google/go-jsonnet"
"github.com/hashicorp/go-retryablehttp"
"github.com/pkg/errors"

"github.com/ory/kratos/x"
"github.com/ory/x/fetcher"
"github.com/ory/x/logrusx"
"github.com/ory/x/jsonnetsecure"
)

var ErrCancel = errors.New("request cancel by JsonNet")
Expand All @@ -27,14 +26,20 @@ const (
ContentTypeJSON = "application/json"
)

type Builder struct {
r *retryablehttp.Request
log *logrusx.Logger
conf *Config
fetchClient *retryablehttp.Client
}
type (
Dependencies interface {
x.LoggingProvider
x.HTTPClientProvider
jsonnetsecure.VMProvider
}
Builder struct {
r *retryablehttp.Request
conf *Config
Dependencies
}
)

func NewBuilder(config json.RawMessage, client *retryablehttp.Client, l *logrusx.Logger) (*Builder, error) {
func NewBuilder(config json.RawMessage, deps Dependencies) (*Builder, error) {
c, err := parseConfig(config)
if err != nil {
return nil, err
Expand All @@ -46,10 +51,9 @@ func NewBuilder(config json.RawMessage, client *retryablehttp.Client, l *logrusx
}

return &Builder{
r: r,
log: l,
conf: c,
fetchClient: client,
r: r,
conf: c,
Dependencies: deps,
}, nil
}

Expand All @@ -66,7 +70,7 @@ func (b *Builder) addAuth() error {
return nil
}

func (b *Builder) addBody(body interface{}) error {
func (b *Builder) addBody(ctx context.Context, body interface{}) error {
if isNilInterface(body) {
return nil
}
Expand All @@ -77,18 +81,18 @@ func (b *Builder) addBody(body interface{}) error {
return errors.New("got empty template path for request with body")
}

tpl, err := b.readTemplate()
tpl, err := b.readTemplate(ctx)
if err != nil {
return err
}

switch contentType {
case ContentTypeForm:
if err := b.addURLEncodedBody(tpl, body); err != nil {
if err := b.addURLEncodedBody(ctx, tpl, body); err != nil {
return err
}
case ContentTypeJSON:
if err := b.addJSONBody(tpl, body); err != nil {
if err := b.addJSONBody(ctx, tpl, body); err != nil {
return err
}
default:
Expand All @@ -98,7 +102,7 @@ func (b *Builder) addBody(body interface{}) error {
return nil
}

func (b *Builder) addJSONBody(template *bytes.Buffer, body interface{}) error {
func (b *Builder) addJSONBody(ctx context.Context, template *bytes.Buffer, body interface{}) error {
buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
Expand All @@ -108,10 +112,16 @@ func (b *Builder) addJSONBody(template *bytes.Buffer, body interface{}) error {
return errors.WithStack(err)
}

vm := jsonnetsecure.MakeSecureVM()
vm, err := b.JsonnetVM(ctx)
if err != nil {
return errors.WithStack(err)
}
vm.TLACode("ctx", buf.String())

res, err := vm.EvaluateAnonymousSnippet(b.conf.TemplateURI, template.String())
res, err := vm.EvaluateAnonymousSnippet(
b.conf.TemplateURI,
template.String(),
)
if err != nil {
// Unfortunately we can not use errors.As / errors.Is, see:
// https://github.com/google/go-jsonnet/issues/592
Expand All @@ -130,7 +140,7 @@ func (b *Builder) addJSONBody(template *bytes.Buffer, body interface{}) error {
return nil
}

func (b *Builder) addURLEncodedBody(template *bytes.Buffer, body interface{}) error {
func (b *Builder) addURLEncodedBody(ctx context.Context, template *bytes.Buffer, body interface{}) error {
buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
Expand All @@ -140,7 +150,10 @@ func (b *Builder) addURLEncodedBody(template *bytes.Buffer, body interface{}) er
return err
}

vm := jsonnetsecure.MakeSecureVM()
vm, err := b.JsonnetVM(ctx)
if err != nil {
return err
}
vm.TLACode("ctx", buf.String())

res, err := vm.EvaluateAnonymousSnippet(b.conf.TemplateURI, template.String())
Expand All @@ -167,7 +180,7 @@ func (b *Builder) addURLEncodedBody(template *bytes.Buffer, body interface{}) er
return nil
}

func (b *Builder) BuildRequest(body interface{}) (*retryablehttp.Request, error) {
func (b *Builder) BuildRequest(ctx context.Context, body interface{}) (*retryablehttp.Request, error) {
b.r.Header = b.conf.Header
if err := b.addAuth(); err != nil {
return nil, err
Expand All @@ -176,28 +189,30 @@ func (b *Builder) BuildRequest(body interface{}) (*retryablehttp.Request, error)
// According to the HTTP spec any request method, but TRACE is allowed to
// have a body. Even this is a bad practice for some of them, like for GET
if b.conf.Method != http.MethodTrace {
if err := b.addBody(body); err != nil {
if err := b.addBody(ctx, body); err != nil {
return nil, err
}
}

return b.r, nil
}

func (b *Builder) readTemplate() (*bytes.Buffer, error) {
func (b *Builder) readTemplate(ctx context.Context) (*bytes.Buffer, error) {
templateURI := b.conf.TemplateURI

if templateURI == "" {
return nil, nil
}

f := fetcher.NewFetcher(fetcher.WithClient(b.fetchClient))
f := fetcher.NewFetcher(fetcher.WithClient(b.HTTPClient(ctx)))

tpl, err := f.Fetch(templateURI)
if errors.Is(err, fetcher.ErrUnknownScheme) {
// legacy filepath
templateURI = "file://" + templateURI
b.log.WithError(err).Warnf("support for filepaths without a 'file://' scheme will be dropped in the next release, please use %s instead in your config", templateURI)
b.Logger().WithError(err).Warnf(
"support for filepaths without a 'file://' scheme will be dropped in the next release, please use %s instead in your config",
templateURI)

tpl, err = f.Fetch(templateURI)
}
Expand Down
Loading

0 comments on commit 9eeedc0

Please sign in to comment.