2020-06-29 18:16:55 +00:00
|
|
|
package pkger_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
2020-07-24 16:10:58 +00:00
|
|
|
"encoding/json"
|
2020-06-29 18:16:55 +00:00
|
|
|
"fmt"
|
2020-07-24 16:10:58 +00:00
|
|
|
"io"
|
2020-06-29 18:16:55 +00:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
2022-04-13 20:24:27 +00:00
|
|
|
"os"
|
2020-06-29 18:16:55 +00:00
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2020-07-24 16:10:58 +00:00
|
|
|
"github.com/go-chi/chi"
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
fluxurl "github.com/influxdata/flux/dependencies/url"
|
2021-09-13 19:12:35 +00:00
|
|
|
"github.com/influxdata/influxdb/v2"
|
2020-07-24 16:10:58 +00:00
|
|
|
pcontext "github.com/influxdata/influxdb/v2/context"
|
2021-09-13 19:12:35 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
2021-12-30 17:55:45 +00:00
|
|
|
influxerror "github.com/influxdata/influxdb/v2/kit/platform/errors"
|
2020-07-24 16:10:58 +00:00
|
|
|
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
|
2020-06-29 18:16:55 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/mock"
|
|
|
|
"github.com/influxdata/influxdb/v2/pkg/testttp"
|
|
|
|
"github.com/influxdata/influxdb/v2/pkger"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.uber.org/zap"
|
2021-12-30 17:55:45 +00:00
|
|
|
"go.uber.org/zap/zaptest/observer"
|
2020-07-24 16:10:58 +00:00
|
|
|
"gopkg.in/yaml.v3"
|
2020-06-29 18:16:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestPkgerHTTPServerTemplate(t *testing.T) {
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
2022-04-13 20:24:27 +00:00
|
|
|
b, err := os.ReadFile(strings.TrimPrefix(r.URL.Path, "/"))
|
2020-06-29 18:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), 500)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Write(b)
|
|
|
|
})
|
|
|
|
filesvr := httptest.NewServer(mux)
|
|
|
|
defer filesvr.Close()
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
defaultClient := pkger.NewDefaultHTTPClient(fluxurl.PassValidator{})
|
|
|
|
|
2020-06-29 18:16:55 +00:00
|
|
|
newPkgURL := func(t *testing.T, svrURL string, pkgPath string) string {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
u, err := url.Parse(svrURL)
|
|
|
|
require.NoError(t, err)
|
|
|
|
u.Path = path.Join(u.Path, pkgPath)
|
|
|
|
return u.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
strPtr := func(s string) *string {
|
|
|
|
return &s
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("create pkg", func(t *testing.T) {
|
|
|
|
t.Run("should successfully return with valid req body", func(t *testing.T) {
|
|
|
|
fakeLabelSVC := mock.NewLabelService()
|
2021-03-30 18:10:02 +00:00
|
|
|
fakeLabelSVC.FindLabelByIDFn = func(ctx context.Context, id platform.ID) (*influxdb.Label, error) {
|
2020-06-29 18:16:55 +00:00
|
|
|
return &influxdb.Label{
|
|
|
|
ID: id,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
svc := pkger.NewService(pkger.WithLabelSVC(fakeLabelSVC))
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2020-06-29 18:16:55 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/export", pkger.ReqExport{
|
|
|
|
Resources: []pkger.ResourceToClone{
|
|
|
|
{
|
|
|
|
Kind: pkger.KindLabel,
|
|
|
|
ID: 1,
|
|
|
|
Name: "new name",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}).
|
|
|
|
Headers("Content-Type", "application/json").
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusOK).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
pkg, err := pkger.Parse(pkger.EncodingJSON, pkger.FromReader(buf))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.NotNil(t, pkg)
|
|
|
|
require.NoError(t, pkg.Validate())
|
|
|
|
|
|
|
|
assert.Len(t, pkg.Objects, 1)
|
|
|
|
assert.Len(t, pkg.Summary().Labels, 1)
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("should be invalid if not org ids or resources provided", func(t *testing.T) {
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), nil, defaultClient)
|
2020-06-29 18:16:55 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/export", pkger.ReqExport{}).
|
|
|
|
Headers("Content-Type", "application/json").
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusUnprocessableEntity)
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("dry run pkg", func(t *testing.T) {
|
2021-12-30 17:55:45 +00:00
|
|
|
t.Run("jsonnet disabled", func(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
contentType string
|
|
|
|
reqBody pkger.ReqApply
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "app jsonnet disabled",
|
|
|
|
contentType: "application/x-jsonnet",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJsonnet),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "retrieves package from a URL (jsonnet disabled)",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
Remotes: []pkger.ReqTemplateRemote{{
|
|
|
|
URL: newPkgURL(t, filesvr.URL, "testdata/bucket_associates_labels_one.jsonnet"),
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "app json with jsonnet disabled remote",
|
|
|
|
contentType: "application/json",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
RawTemplate: bucketPkgJsonWithJsonnetRemote(t),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
fn := func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
|
|
|
var opt pkger.ApplyOpt
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&opt)
|
|
|
|
}
|
|
|
|
pkg, err := pkger.Combine(opt.Templates)
|
|
|
|
if err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := pkg.Validate(); err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
sum := pkg.Summary()
|
|
|
|
var diff pkger.Diff
|
|
|
|
for _, b := range sum.Buckets {
|
|
|
|
diff.Buckets = append(diff.Buckets, pkger.DiffBucket{
|
|
|
|
DiffIdentifier: pkger.DiffIdentifier{
|
|
|
|
MetaName: b.Name,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return pkger.ImpactSummary{
|
|
|
|
Summary: sum,
|
|
|
|
Diff: diff,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
core, sink := observer.New(zap.InfoLevel)
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.New(core), svc, defaultClient)
|
2021-12-30 17:55:45 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", tt.reqBody).
|
|
|
|
Headers("Content-Type", tt.contentType).
|
|
|
|
WithCtx(ctx).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusUnprocessableEntity).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
|
|
|
|
assert.Len(t, resp.Summary.Buckets, 0)
|
|
|
|
assert.Len(t, resp.Diff.Buckets, 0)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Verify logging when jsonnet is disabled
|
|
|
|
entries := sink.TakeAll() // resets to 0
|
|
|
|
if tt.contentType == "application/x-jsonnet" {
|
|
|
|
require.Equal(t, 1, len(entries))
|
|
|
|
// message 0
|
|
|
|
require.Equal(t, zap.ErrorLevel, entries[0].Entry.Level)
|
|
|
|
require.Equal(t, "api error encountered", entries[0].Entry.Message)
|
|
|
|
assert.ElementsMatch(t, []zap.Field{
|
|
|
|
zap.Error(&influxerror.Error{
|
|
|
|
Code: influxerror.EUnprocessableEntity,
|
|
|
|
Msg: "template from source(s) had an issue: invalid encoding provided",
|
|
|
|
},
|
|
|
|
)}, entries[0].Context)
|
|
|
|
} else if len(tt.reqBody.Remotes) == 1 && strings.HasSuffix(tt.reqBody.Remotes[0].URL, "jsonnet") {
|
|
|
|
require.Equal(t, 1, len(entries))
|
|
|
|
// message 0
|
|
|
|
require.Equal(t, zap.ErrorLevel, entries[0].Entry.Level)
|
|
|
|
require.Equal(t, "api error encountered", entries[0].Entry.Message)
|
|
|
|
expMsg := fmt.Sprintf("template from url[\"%s\"] had an issue: invalid encoding provided", tt.reqBody.Remotes[0].URL)
|
|
|
|
assert.ElementsMatch(t, []zap.Field{
|
|
|
|
zap.Error(&influxerror.Error{
|
|
|
|
Code: influxerror.EUnprocessableEntity,
|
|
|
|
Msg: expMsg,
|
|
|
|
},
|
|
|
|
)}, entries[0].Context)
|
|
|
|
} else if len(tt.reqBody.RawTemplate.Sources) == 1 && strings.HasSuffix(tt.reqBody.RawTemplate.Sources[0], "jsonnet") {
|
|
|
|
require.Equal(t, 1, len(entries))
|
|
|
|
// message 0
|
|
|
|
require.Equal(t, zap.ErrorLevel, entries[0].Entry.Level)
|
|
|
|
require.Equal(t, "api error encountered", entries[0].Entry.Message)
|
|
|
|
expMsg := fmt.Sprintf("template from url[\"%s\"] had an issue: invalid encoding provided", tt.reqBody.RawTemplate.Sources[0])
|
|
|
|
assert.ElementsMatch(t, []zap.Field{
|
|
|
|
zap.Error(&influxerror.Error{
|
|
|
|
Code: influxerror.EUnprocessableEntity,
|
|
|
|
Msg: expMsg,
|
|
|
|
},
|
|
|
|
)}, entries[0].Context)
|
|
|
|
} else {
|
|
|
|
require.Equal(t, 0, len(entries))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.Run(tt.name, fn)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-06-29 18:16:55 +00:00
|
|
|
t.Run("json", func(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
contentType string
|
|
|
|
reqBody pkger.ReqApply
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "app json",
|
|
|
|
contentType: "application/json",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(9000).String(),
|
2020-06-29 18:16:55 +00:00
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJSON),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "defaults json when no content type",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(9000).String(),
|
2020-06-29 18:16:55 +00:00
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJSON),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2021-12-30 17:55:45 +00:00
|
|
|
name: "retrieves package from a URL (json)",
|
2020-06-29 18:16:55 +00:00
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(9000).String(),
|
2020-06-29 18:16:55 +00:00
|
|
|
Remotes: []pkger.ReqTemplateRemote{{
|
|
|
|
URL: newPkgURL(t, filesvr.URL, "testdata/remote_bucket.json"),
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
fn := func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
2021-03-30 18:10:02 +00:00
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
2020-06-29 18:16:55 +00:00
|
|
|
var opt pkger.ApplyOpt
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&opt)
|
|
|
|
}
|
2020-06-30 21:54:00 +00:00
|
|
|
pkg, err := pkger.Combine(opt.Templates)
|
2020-06-29 18:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := pkg.Validate(); err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
sum := pkg.Summary()
|
|
|
|
var diff pkger.Diff
|
|
|
|
for _, b := range sum.Buckets {
|
|
|
|
diff.Buckets = append(diff.Buckets, pkger.DiffBucket{
|
|
|
|
DiffIdentifier: pkger.DiffIdentifier{
|
2020-06-29 21:59:30 +00:00
|
|
|
MetaName: b.Name,
|
2020-06-29 18:16:55 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return pkger.ImpactSummary{
|
|
|
|
Summary: sum,
|
|
|
|
Diff: diff,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-12-30 17:55:45 +00:00
|
|
|
core, _ := observer.New(zap.InfoLevel)
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.New(core), svc, defaultClient)
|
2020-06-29 18:16:55 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", tt.reqBody).
|
|
|
|
Headers("Content-Type", tt.contentType).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusOK).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
|
|
|
|
assert.Len(t, resp.Summary.Buckets, 1)
|
|
|
|
assert.Len(t, resp.Diff.Buckets, 1)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
t.Run(tt.name, fn)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("yml", func(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
contentType string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "app yml",
|
|
|
|
contentType: "application/x-yaml",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "text yml",
|
|
|
|
contentType: "text/yml",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
fn := func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
2021-03-30 18:10:02 +00:00
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
2020-06-29 18:16:55 +00:00
|
|
|
var opt pkger.ApplyOpt
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&opt)
|
|
|
|
}
|
2020-06-30 21:54:00 +00:00
|
|
|
pkg, err := pkger.Combine(opt.Templates)
|
2020-06-29 18:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := pkg.Validate(); err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
sum := pkg.Summary()
|
|
|
|
var diff pkger.Diff
|
|
|
|
for _, b := range sum.Buckets {
|
|
|
|
diff.Buckets = append(diff.Buckets, pkger.DiffBucket{
|
|
|
|
DiffIdentifier: pkger.DiffIdentifier{
|
2020-06-29 21:59:30 +00:00
|
|
|
MetaName: b.Name,
|
2020-06-29 18:16:55 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return pkger.ImpactSummary{
|
|
|
|
Diff: diff,
|
|
|
|
Summary: sum,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2020-06-29 18:16:55 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
body := newReqApplyYMLBody(t, platform.ID(9000), true)
|
2020-06-29 18:16:55 +00:00
|
|
|
|
|
|
|
testttp.
|
|
|
|
Post(t, "/api/v2/templates/apply", body).
|
|
|
|
Headers("Content-Type", tt.contentType).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusOK).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
|
|
|
|
assert.Len(t, resp.Summary.Buckets, 1)
|
|
|
|
assert.Len(t, resp.Diff.Buckets, 1)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run(tt.name, fn)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-07-06 17:24:53 +00:00
|
|
|
t.Run("all diff and summary resource collections are non null", func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
2021-03-30 18:10:02 +00:00
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
2020-07-06 17:24:53 +00:00
|
|
|
// return zero value pkg
|
|
|
|
return pkger.ImpactSummary{}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2020-07-06 17:24:53 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", pkger.ReqApply{
|
|
|
|
DryRun: true,
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(1).String(),
|
2020-07-06 17:24:53 +00:00
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJSON),
|
|
|
|
}).
|
|
|
|
Headers("Content-Type", "application/json").
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusOK).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
assertNonZeroApplyResp(t, resp)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-06-29 18:16:55 +00:00
|
|
|
t.Run("with multiple pkgs", func(t *testing.T) {
|
|
|
|
newBktPkg := func(t *testing.T, bktName string) pkger.ReqRawTemplate {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
pkgStr := fmt.Sprintf(`[
|
|
|
|
{
|
|
|
|
"apiVersion": "%[1]s",
|
|
|
|
"kind": "Bucket",
|
|
|
|
"metadata": {
|
|
|
|
"name": %q
|
|
|
|
},
|
|
|
|
"spec": {}
|
|
|
|
}
|
|
|
|
]`, pkger.APIVersion, bktName)
|
|
|
|
|
|
|
|
pkg, err := pkger.Parse(pkger.EncodingJSON, pkger.FromString(pkgStr))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
pkgBytes, err := pkg.Encode(pkger.EncodingJSON)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return pkger.ReqRawTemplate{
|
|
|
|
ContentType: pkger.EncodingJSON.String(),
|
|
|
|
Sources: pkg.Sources(),
|
2020-06-30 21:54:00 +00:00
|
|
|
Template: pkgBytes,
|
2020-06-29 18:16:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
reqBody pkger.ReqApply
|
|
|
|
expectedBkts []string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "retrieves package from a URL and raw pkgs",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(9000).String(),
|
2020-06-29 18:16:55 +00:00
|
|
|
Remotes: []pkger.ReqTemplateRemote{{
|
|
|
|
ContentType: "json",
|
|
|
|
URL: newPkgURL(t, filesvr.URL, "testdata/remote_bucket.json"),
|
|
|
|
}},
|
|
|
|
RawTemplates: []pkger.ReqRawTemplate{
|
|
|
|
newBktPkg(t, "bkt1"),
|
|
|
|
newBktPkg(t, "bkt2"),
|
|
|
|
newBktPkg(t, "bkt3"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedBkts: []string{"bkt1", "bkt2", "bkt3", "rucket-11"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "retrieves packages from raw single and list",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(9000).String(),
|
2020-06-29 18:16:55 +00:00
|
|
|
RawTemplate: newBktPkg(t, "bkt4"),
|
|
|
|
RawTemplates: []pkger.ReqRawTemplate{
|
|
|
|
newBktPkg(t, "bkt1"),
|
|
|
|
newBktPkg(t, "bkt2"),
|
|
|
|
newBktPkg(t, "bkt3"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedBkts: []string{"bkt1", "bkt2", "bkt3", "bkt4"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
fn := func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
2021-03-30 18:10:02 +00:00
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
2020-06-29 18:16:55 +00:00
|
|
|
var opt pkger.ApplyOpt
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&opt)
|
|
|
|
}
|
2020-06-30 21:54:00 +00:00
|
|
|
pkg, err := pkger.Combine(opt.Templates)
|
2020-06-29 18:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := pkg.Validate(); err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
sum := pkg.Summary()
|
|
|
|
var diff pkger.Diff
|
|
|
|
for _, b := range sum.Buckets {
|
|
|
|
diff.Buckets = append(diff.Buckets, pkger.DiffBucket{
|
|
|
|
DiffIdentifier: pkger.DiffIdentifier{
|
2020-06-29 21:59:30 +00:00
|
|
|
MetaName: b.Name,
|
2020-06-29 18:16:55 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return pkger.ImpactSummary{
|
|
|
|
Diff: diff,
|
|
|
|
Summary: sum,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2020-06-29 18:16:55 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", tt.reqBody).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusOK).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
|
|
|
|
require.Len(t, resp.Summary.Buckets, len(tt.expectedBkts))
|
|
|
|
for i, expected := range tt.expectedBkts {
|
|
|
|
assert.Equal(t, expected, resp.Summary.Buckets[i].Name)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run(tt.name, fn)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("validation failures", func(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
contentType string
|
|
|
|
reqBody pkger.ReqApply
|
|
|
|
expectedStatusCode int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "invalid org id",
|
|
|
|
contentType: "application/json",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
|
|
|
OrgID: "bad org id",
|
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJSON),
|
|
|
|
},
|
|
|
|
expectedStatusCode: http.StatusBadRequest,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid stack id",
|
|
|
|
contentType: "application/json",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(9000).String(),
|
2020-06-29 18:16:55 +00:00
|
|
|
StackID: strPtr("invalid stack id"),
|
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJSON),
|
|
|
|
},
|
|
|
|
expectedStatusCode: http.StatusBadRequest,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
fn := func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
2021-03-30 18:10:02 +00:00
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
2020-06-29 18:16:55 +00:00
|
|
|
var opt pkger.ApplyOpt
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&opt)
|
|
|
|
}
|
2020-06-30 21:54:00 +00:00
|
|
|
pkg, err := pkger.Combine(opt.Templates)
|
2020-06-29 18:16:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
return pkger.ImpactSummary{
|
|
|
|
Summary: pkg.Summary(),
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2020-06-29 18:16:55 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", tt.reqBody).
|
|
|
|
Headers("Content-Type", tt.contentType).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(tt.expectedStatusCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run(tt.name, fn)
|
|
|
|
}
|
|
|
|
})
|
2022-02-11 22:28:49 +00:00
|
|
|
|
|
|
|
t.Run("resp apply err response", func(t *testing.T) {
|
|
|
|
tests := []struct {
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
name string
|
2022-02-11 22:28:49 +00:00
|
|
|
contentType string
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
reqBody pkger.ReqApply
|
2022-02-11 22:28:49 +00:00
|
|
|
}{
|
|
|
|
{
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
name: "invalid json",
|
2022-02-11 22:28:49 +00:00
|
|
|
contentType: "application/json",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
DryRun: true,
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
RawTemplate: simpleInvalidBody(t, pkger.EncodingJSON),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
fn := func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
|
|
|
var opt pkger.ApplyOpt
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&opt)
|
|
|
|
}
|
|
|
|
pkg, err := pkger.Combine(opt.Templates)
|
|
|
|
if err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := pkg.Validate(); err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
|
|
|
sum := pkg.Summary()
|
|
|
|
var diff pkger.Diff
|
|
|
|
for _, b := range sum.Buckets {
|
|
|
|
diff.Buckets = append(diff.Buckets, pkger.DiffBucket{
|
|
|
|
DiffIdentifier: pkger.DiffIdentifier{
|
|
|
|
MetaName: b.Name,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return pkger.ImpactSummary{
|
|
|
|
Summary: sum,
|
|
|
|
Diff: diff,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2022-02-11 22:28:49 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", tt.reqBody).
|
|
|
|
Headers("Content-Type", tt.contentType).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusUnprocessableEntity).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApplyErr
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
require.Equal(t, "unprocessable entity", resp.Code)
|
|
|
|
require.Greater(t, len(resp.Message), 0)
|
|
|
|
require.NotNil(t, resp.Summary)
|
|
|
|
require.NotNil(t, resp.Diff)
|
|
|
|
require.Greater(t, len(resp.Errors), 0)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
t.Run(tt.name, fn)
|
|
|
|
}
|
|
|
|
})
|
2020-06-29 18:16:55 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("apply a pkg", func(t *testing.T) {
|
2020-07-06 17:24:53 +00:00
|
|
|
t.Run("happy path", func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
2021-03-30 18:10:02 +00:00
|
|
|
applyFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
2020-07-06 17:24:53 +00:00
|
|
|
var opt pkger.ApplyOpt
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&opt)
|
|
|
|
}
|
2020-06-29 18:16:55 +00:00
|
|
|
|
2020-07-06 17:24:53 +00:00
|
|
|
pkg, err := pkger.Combine(opt.Templates)
|
|
|
|
if err != nil {
|
|
|
|
return pkger.ImpactSummary{}, err
|
|
|
|
}
|
2020-06-29 18:16:55 +00:00
|
|
|
|
2020-07-06 17:24:53 +00:00
|
|
|
sum := pkg.Summary()
|
2020-06-29 18:16:55 +00:00
|
|
|
|
2020-07-06 17:24:53 +00:00
|
|
|
var diff pkger.Diff
|
|
|
|
for _, b := range sum.Buckets {
|
|
|
|
diff.Buckets = append(diff.Buckets, pkger.DiffBucket{
|
|
|
|
DiffIdentifier: pkger.DiffIdentifier{
|
|
|
|
MetaName: b.Name,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
for key := range opt.MissingSecrets {
|
|
|
|
sum.MissingSecrets = append(sum.MissingSecrets, key)
|
|
|
|
}
|
2020-06-29 18:16:55 +00:00
|
|
|
|
2020-07-06 17:24:53 +00:00
|
|
|
return pkger.ImpactSummary{
|
|
|
|
Diff: diff,
|
|
|
|
Summary: sum,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2020-07-06 17:24:53 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", pkger.ReqApply{
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(9000).String(),
|
2020-07-06 17:24:53 +00:00
|
|
|
Secrets: map[string]string{"secret1": "val1"},
|
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJSON),
|
|
|
|
}).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusCreated).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
2020-06-29 18:16:55 +00:00
|
|
|
|
2020-07-06 17:24:53 +00:00
|
|
|
assert.Len(t, resp.Summary.Buckets, 1)
|
|
|
|
assert.Len(t, resp.Diff.Buckets, 1)
|
|
|
|
assert.Equal(t, []string{"secret1"}, resp.Summary.MissingSecrets)
|
|
|
|
assert.Nil(t, resp.Errors)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("all diff and summary resource collections are non null", func(t *testing.T) {
|
|
|
|
svc := &fakeSVC{
|
2021-03-30 18:10:02 +00:00
|
|
|
applyFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
2020-07-06 17:24:53 +00:00
|
|
|
// return zero value pkg
|
|
|
|
return pkger.ImpactSummary{}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc, defaultClient)
|
2020-07-06 17:24:53 +00:00
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", pkger.ReqApply{
|
2021-03-30 18:10:02 +00:00
|
|
|
OrgID: platform.ID(1).String(),
|
2020-07-06 17:24:53 +00:00
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJSON),
|
|
|
|
}).
|
|
|
|
Headers("Content-Type", "application/json").
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(http.StatusCreated).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
assertNonZeroApplyResp(t, resp)
|
|
|
|
})
|
|
|
|
})
|
2020-06-29 18:16:55 +00:00
|
|
|
})
|
2021-12-30 17:55:45 +00:00
|
|
|
|
|
|
|
t.Run("Templates()", func(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
reqBody pkger.ReqApply
|
|
|
|
encoding pkger.Encoding
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "jsonnet disabled",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingJsonnet),
|
|
|
|
},
|
|
|
|
encoding: pkger.EncodingJsonnet,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "jsonnet remote disabled",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
Remotes: []pkger.ReqTemplateRemote{{
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
URL: newPkgURL(t, filesvr.URL, "testdata/bucket_associates_labels.jsonnet"),
|
2021-12-30 17:55:45 +00:00
|
|
|
}},
|
|
|
|
},
|
|
|
|
encoding: pkger.EncodingJsonnet,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "jsonnet disabled remote source",
|
|
|
|
reqBody: pkger.ReqApply{
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
RawTemplate: bucketPkgJsonWithJsonnetRemote(t),
|
|
|
|
},
|
|
|
|
encoding: pkger.EncodingJSON,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
tmpl, err := tt.reqBody.Templates(tt.encoding, defaultClient)
|
2021-12-30 17:55:45 +00:00
|
|
|
assert.Nil(t, tmpl)
|
|
|
|
require.Error(t, err)
|
|
|
|
assert.Equal(t, "unprocessable entity", influxerror.ErrorCode(err))
|
feat: add --hardening-enabled option to limit flux/pkger HTTP requests (#23207)
Flux HTTP and template fetching requests do not perform IP address
checks for local addresses. This behavior on the one hand allows SSRF
(Server Side Request Forgery) attacks via authenticated requests but on
the other hand is useful for scenarios that have legitimate requirements
to fetch from private addresses (eg, hosting templates internally or
performing flux queries to local resources during development).
To not break existing installations, the default behavior will remain
the same but a new --hardening-enabled option is added to influxd to
turn on IP address verification and limit both flux and template
fetching HTTP requests to non-private addresses. We plan to enable new
security features that aren't suitable for the default install with this
option. Put another way, this new option is intended to be used to make
it easy to turn on all security options when running in production
environments. The 'Manage security and authorization' section of the
docs will also be updated for this option.
Specifically for flux, when --hardening-enabled is specified, we now
pass in PrivateIPValidator{} to the flux dependency configuration. The
flux url validator will then tap into the http.Client 'Control'
mechanism to validate the IP address since it is called after DNS lookup
but before the connection starts.
For pkger (template fetching), when --hardening-enabled is specified,
the template parser's HTTP client will be configured to also use
PrivateIPValidator{}. Note that /api/v2/stacks POST ('init', aka create)
and PATCH ('update') only store the new url to be applied later with
/api/v2/templates/apply. While it is possible to have InitStack() and
UpdateStack() mimic net.DialContext() to setup a go routine to perform a
DNS lookup and then loop through the returned addresses to verify none
are for a private IP before storing the url, this would add considerable
complexity to the stacks implementation. Since the stack's urls are
fetched when it is applied and the IP address is verified as part of
apply (see above), for now we'll keep this simple and not validate the
IPs of the stack's urls during init or update.
Lastly, update pkger/http_server_template_test.go's Templates() test for
disabled jsonnet to also check the contents of the 422 error (since the
flux validator also returns a 422 with different message). Also, fix the
URL in one of these tests to use a valid path.
2022-03-18 14:25:31 +00:00
|
|
|
assert.Contains(t, influxerror.ErrorMessage(err), "invalid encoding provided: jsonnet")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Templates() remotes with IP validation", func(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
client *http.Client
|
|
|
|
expCode int
|
|
|
|
expErr string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "no filter ip",
|
|
|
|
client: pkger.NewDefaultHTTPClient(fluxurl.PassValidator{}),
|
|
|
|
expCode: http.StatusOK,
|
|
|
|
expErr: "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "filter ip",
|
|
|
|
client: pkger.NewDefaultHTTPClient(fluxurl.PrivateIPValidator{}),
|
|
|
|
expCode: http.StatusUnprocessableEntity,
|
|
|
|
expErr: "no such host",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
svc := &fakeSVC{
|
|
|
|
dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) {
|
|
|
|
return pkger.ImpactSummary{}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
core, sink := observer.New(zap.InfoLevel)
|
|
|
|
pkgHandler := pkger.NewHTTPServerTemplates(zap.New(core), svc, tt.client)
|
|
|
|
svr := newMountedHandler(pkgHandler, 1)
|
|
|
|
|
|
|
|
reqBody := pkger.ReqApply{
|
|
|
|
DryRun: true,
|
|
|
|
OrgID: platform.ID(9000).String(),
|
|
|
|
Remotes: []pkger.ReqTemplateRemote{{
|
|
|
|
URL: newPkgURL(t, filesvr.URL, "testdata/remote_bucket.json"),
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
testttp.
|
|
|
|
PostJSON(t, "/api/v2/templates/apply", reqBody).
|
|
|
|
Headers("Content-Type", "application/json").
|
|
|
|
WithCtx(ctx).
|
|
|
|
Do(svr).
|
|
|
|
ExpectStatus(tt.expCode).
|
|
|
|
ExpectBody(func(buf *bytes.Buffer) {
|
|
|
|
var resp pkger.RespApply
|
|
|
|
decodeBody(t, buf, &resp)
|
|
|
|
|
|
|
|
assert.Len(t, resp.Summary.Buckets, 0)
|
|
|
|
assert.Len(t, resp.Diff.Buckets, 0)
|
|
|
|
})
|
|
|
|
|
|
|
|
if tt.expErr != "" {
|
|
|
|
// Verify logging output has the expected generic flux message
|
|
|
|
entries := sink.TakeAll() // resets to 0
|
|
|
|
fmt.Printf("%+v\n", entries)
|
|
|
|
require.Equal(t, 1, len(entries))
|
|
|
|
assert.Contains(t, fmt.Sprintf("%s", entries[0].Context[0].Interface), tt.expErr)
|
|
|
|
}
|
2021-12-30 17:55:45 +00:00
|
|
|
}
|
|
|
|
})
|
2020-06-29 18:16:55 +00:00
|
|
|
}
|
2020-07-06 17:24:53 +00:00
|
|
|
|
|
|
|
func assertNonZeroApplyResp(t *testing.T, resp pkger.RespApply) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
assert.NotNil(t, resp.Sources)
|
|
|
|
|
|
|
|
assert.NotNil(t, resp.Diff.Buckets)
|
|
|
|
assert.NotNil(t, resp.Diff.Checks)
|
|
|
|
assert.NotNil(t, resp.Diff.Dashboards)
|
|
|
|
assert.NotNil(t, resp.Diff.Labels)
|
|
|
|
assert.NotNil(t, resp.Diff.LabelMappings)
|
|
|
|
assert.NotNil(t, resp.Diff.NotificationEndpoints)
|
|
|
|
assert.NotNil(t, resp.Diff.NotificationRules)
|
|
|
|
assert.NotNil(t, resp.Diff.Tasks)
|
|
|
|
assert.NotNil(t, resp.Diff.Telegrafs)
|
|
|
|
assert.NotNil(t, resp.Diff.Variables)
|
|
|
|
|
|
|
|
assert.NotNil(t, resp.Summary.Buckets)
|
|
|
|
assert.NotNil(t, resp.Summary.Checks)
|
|
|
|
assert.NotNil(t, resp.Summary.Dashboards)
|
|
|
|
assert.NotNil(t, resp.Summary.Labels)
|
|
|
|
assert.NotNil(t, resp.Summary.LabelMappings)
|
|
|
|
assert.NotNil(t, resp.Summary.NotificationEndpoints)
|
|
|
|
assert.NotNil(t, resp.Summary.NotificationRules)
|
|
|
|
assert.NotNil(t, resp.Summary.Tasks)
|
|
|
|
assert.NotNil(t, resp.Summary.TelegrafConfigs)
|
|
|
|
assert.NotNil(t, resp.Summary.Variables)
|
|
|
|
}
|
2020-07-24 16:10:58 +00:00
|
|
|
|
|
|
|
func bucketPkgKinds(t *testing.T, encoding pkger.Encoding) pkger.ReqRawTemplate {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
var pkgStr string
|
|
|
|
switch encoding {
|
|
|
|
case pkger.EncodingJsonnet:
|
|
|
|
pkgStr = `
|
|
|
|
local Bucket(name, desc) = {
|
|
|
|
apiVersion: '%[1]s',
|
|
|
|
kind: 'Bucket',
|
|
|
|
metadata: {
|
|
|
|
name: name
|
|
|
|
},
|
|
|
|
spec: {
|
|
|
|
description: desc
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
[
|
|
|
|
Bucket(name="rucket-1", desc="bucket 1 description"),
|
|
|
|
]
|
|
|
|
`
|
|
|
|
case pkger.EncodingJSON:
|
|
|
|
pkgStr = `[
|
|
|
|
{
|
|
|
|
"apiVersion": "%[1]s",
|
|
|
|
"kind": "Bucket",
|
|
|
|
"metadata": {
|
|
|
|
"name": "rucket-11"
|
|
|
|
},
|
|
|
|
"spec": {
|
|
|
|
"description": "bucket 1 description"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
`
|
|
|
|
case pkger.EncodingYAML:
|
|
|
|
pkgStr = `apiVersion: %[1]s
|
|
|
|
kind: Bucket
|
|
|
|
metadata:
|
|
|
|
name: rucket-11
|
|
|
|
spec:
|
|
|
|
description: bucket 1 description
|
|
|
|
`
|
|
|
|
default:
|
|
|
|
require.FailNow(t, "invalid encoding provided: "+encoding.String())
|
|
|
|
}
|
|
|
|
|
2021-12-30 17:55:45 +00:00
|
|
|
pkg, err := pkger.Parse(encoding, pkger.FromString(fmt.Sprintf(pkgStr, pkger.APIVersion)), pkger.EnableJsonnet())
|
2020-07-24 16:10:58 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
b, err := pkg.Encode(encoding)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return pkger.ReqRawTemplate{
|
|
|
|
ContentType: encoding.String(),
|
|
|
|
Sources: pkg.Sources(),
|
|
|
|
Template: b,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-30 17:55:45 +00:00
|
|
|
func bucketPkgJsonWithJsonnetRemote(t *testing.T) pkger.ReqRawTemplate {
|
|
|
|
pkgStr := `[
|
|
|
|
{
|
|
|
|
"apiVersion": "%[1]s",
|
|
|
|
"kind": "Bucket",
|
|
|
|
"metadata": {
|
|
|
|
"name": "rucket-11"
|
|
|
|
},
|
|
|
|
"spec": {
|
|
|
|
"description": "bucket 1 description"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
`
|
|
|
|
// Create a json template and then add a jsonnet remote raw template
|
|
|
|
pkg, err := pkger.Parse(pkger.EncodingJSON, pkger.FromString(fmt.Sprintf(pkgStr, pkger.APIVersion)))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
b, err := pkg.Encode(pkger.EncodingJSON)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return pkger.ReqRawTemplate{
|
|
|
|
ContentType: pkger.EncodingJsonnet.String(),
|
|
|
|
Sources: []string{"file:///nonexistent.jsonnet"},
|
|
|
|
Template: b,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-11 22:28:49 +00:00
|
|
|
func simpleInvalidBody(t *testing.T, encoding pkger.Encoding) pkger.ReqRawTemplate {
|
|
|
|
t.Helper()
|
|
|
|
b := bytes.Buffer{}
|
|
|
|
b.WriteString("[ {}, {} ]")
|
|
|
|
return pkger.ReqRawTemplate{
|
|
|
|
ContentType: encoding.String(),
|
|
|
|
Sources: []string{"test1.json"},
|
|
|
|
Template: b.Bytes(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
func newReqApplyYMLBody(t *testing.T, orgID platform.ID, dryRun bool) *bytes.Buffer {
|
2020-07-24 16:10:58 +00:00
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err := yaml.NewEncoder(&buf).Encode(pkger.ReqApply{
|
|
|
|
DryRun: dryRun,
|
|
|
|
OrgID: orgID.String(),
|
|
|
|
RawTemplate: bucketPkgKinds(t, pkger.EncodingYAML),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
return &buf
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeBody(t *testing.T, r io.Reader, v interface{}) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
if err := json.NewDecoder(r).Decode(v); err != nil {
|
|
|
|
require.FailNow(t, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
func newMountedHandler(rh kithttp.ResourceHandler, userID platform.ID) chi.Router {
|
2020-07-24 16:10:58 +00:00
|
|
|
r := chi.NewRouter()
|
|
|
|
r.Mount(rh.Prefix(), authMW(userID)(rh))
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
func authMW(userID platform.ID) func(http.Handler) http.Handler {
|
2020-07-24 16:10:58 +00:00
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
r = r.WithContext(pcontext.SetAuthorizer(r.Context(), &influxdb.Session{UserID: userID}))
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
return http.HandlerFunc(fn)
|
|
|
|
}
|
|
|
|
}
|