Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/influxd/run/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ type Config struct {

// TLS provides configuration options for all https endpoints.
TLS tlsconfig.Config `toml:"tls"`

// HardeningEnabled enables hardening options, such as restricting flux
// HTTP requests to non-private (public) addresses. It is disabled by
// default to preserve existing behavior. In the future this may gate
// additional security features.
HardeningEnabled bool `toml:"hardening-enabled"`
}

// NewConfig returns an instance of Config with reasonable defaults.
Expand Down Expand Up @@ -215,6 +221,7 @@ func (c *Config) Diagnostics() (*diagnostics.Diagnostics, error) {
return diagnostics.RowFromMap(map[string]interface{}{
"reporting-disabled": c.ReportingDisabled,
"bind-address": c.BindAddress,
"hardening-enabled": c.HardeningEnabled,
}), nil
}

Expand Down
12 changes: 11 additions & 1 deletion cmd/influxd/run/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/influxdata/flux"
"github.com/influxdata/flux/dependencies/testing"
"github.com/influxdata/flux/dependencies/url"
"github.com/influxdata/flux/execute/executetest"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/coordinator"
Expand Down Expand Up @@ -325,7 +326,16 @@ func (s *Server) appendHTTPDService(c httpd.Config) error {
ss := storage.NewStore(s.TSDBStore, s.MetaClient)
srv.Handler.Store = ss
if s.config.HTTPD.FluxEnabled {
storageDep, err := influxdb2.NewDependencies(s.MetaClient, reads.NewReader(ss), authorizer, c.AuthEnabled, s.PointsWriter)
// When hardening is enabled, use an HTTP IP validator that restricts
// flux HTTP requests to non-private addresses.
var urlValidator url.Validator
if s.config.HardeningEnabled {
urlValidator = url.PrivateIPValidator{}
} else {
urlValidator = url.PassValidator{}
}

storageDep, err := influxdb2.NewDependencies(s.MetaClient, reads.NewReader(ss), authorizer, c.AuthEnabled, s.PointsWriter, influxdb2.WithURLValidator(urlValidator))
if err != nil {
return err
}
Expand Down
5 changes: 5 additions & 0 deletions etc/config.sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
# Bind address to use for the RPC service for backup and restore.
# bind-address = "127.0.0.1:8088"

# Enable hardening options. When enabled, flux HTTP requests are restricted to
# non-private (public) addresses to mitigate SSRF (Server Side Request Forgery)
# attacks. Disabled by default to preserve existing behavior.
# hardening-enabled = false

###
### [meta]
###
Expand Down
20 changes: 20 additions & 0 deletions flux/stdlib/influxdata/influxdb/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,27 @@ func (d Dependencies) Inject(ctx context.Context) context.Context {
return d.StorageDeps.Inject(ctx)
}

// FluxDepOption modifies the flux dependencies used to construct the
// query Dependencies.
type FluxDepOption func(*flux.Deps)

// WithURLValidator overrides the URL validator (and the HTTP client that
// enforces it) used by flux dependencies. Pass url.PrivateIPValidator{} to
// restrict flux HTTP requests to non-private addresses.
func WithURLValidator(v url.Validator) FluxDepOption {
return func(d *flux.Deps) {
d.Deps.URLValidator = v
d.Deps.HTTPClient = http.NewDefaultClient(v)
}
}

func NewDependencies(
mc MetaClient,
reader Reader,
auth Authorizer,
authEnabled bool,
writer PointsWriter,
fluxOpts ...FluxDepOption,
) (Dependencies, error) {
validator := &url.PassValidator{}
fdeps := dependencies.NewDefaultDependencies("")
Expand All @@ -79,6 +94,11 @@ func NewDependencies(
URLValidator: validator,
},
}
// Apply any flux dependency options (eg, a stricter URL validator when
// hardening is enabled) before assigning fdeps to deps.
for _, opt := range fluxOpts {
opt(&fdeps.Deps)
}
deps := Dependencies{FluxDeps: fdeps}
deps.StorageDeps = StorageDependencies{
Reader: reader,
Expand Down
47 changes: 47 additions & 0 deletions flux/stdlib/influxdata/influxdb/dependencies_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package influxdb

import (
"testing"

"github.com/influxdata/flux"
"github.com/influxdata/flux/dependencies/url"
"github.com/influxdata/influxdb/coordinator"
"github.com/influxdata/influxdb/services/meta"
"github.com/stretchr/testify/require"
)

// stub implementations satisfy the dependency interfaces well enough to pass
// StorageDependencies.Validate (which only checks for non-nil values).
type stubReader struct{ Reader }

type stubMetaClient struct{}

func (stubMetaClient) Databases() []meta.DatabaseInfo { return nil }
func (stubMetaClient) Database(string) *meta.DatabaseInfo { return nil }

type stubPointsWriter struct{}

func (stubPointsWriter) WritePointsInto(*coordinator.IntoWriteRequest) error { return nil }

func TestWithURLValidator(t *testing.T) {
var d flux.Deps
WithURLValidator(url.PrivateIPValidator{})(&d)

v, err := d.URLValidator()
require.NoError(t, err)
require.IsType(t, url.PrivateIPValidator{}, v)
require.NotNil(t, d.Deps.HTTPClient, "expected HTTP client to be set alongside the validator")
}

func TestNewDependencies_AcceptsURLValidatorOption(t *testing.T) {
// Both the default (no option) and hardened (PrivateIPValidator) paths
// must construct without error; server.go selects between them based on
// the hardening-enabled config option.
for _, opts := range [][]FluxDepOption{
nil,
{WithURLValidator(url.PrivateIPValidator{})},
} {
_, err := NewDependencies(stubMetaClient{}, stubReader{}, nil, false, stubPointsWriter{}, opts...)
require.NoError(t, err)
}
}
Loading