Browse Source

feat: Disallow unknown config fields (#4531)

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
pull/4583/head
Maksim Nabokikh 3 weeks ago committed by GitHub
parent
commit
d78d744468
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 24
      cmd/dex/config.go
  2. 8
      cmd/dex/serve.go
  3. 3
      pkg/featureflags/set.go
  4. 18
      storage/etcd/config.go
  5. 4
      storage/sql/config.go
  6. 18
      storage/storage.go

24
cmd/dex/config.go

@ -1,6 +1,7 @@
package main package main
import ( import (
"bytes"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -23,6 +24,15 @@ import (
"github.com/dexidp/dex/storage/sql" "github.com/dexidp/dex/storage/sql"
) )
func configUnmarshaller(b []byte, v interface{}) error {
if featureflags.ConfigDisallowUnknownFields.Enabled() {
return json.Unmarshal(b, v)
}
dec := json.NewDecoder(bytes.NewReader(b))
dec.DisallowUnknownFields()
return dec.Decode(v)
}
// Config is the config format for the main application. // Config is the config format for the main application.
type Config struct { type Config struct {
Issuer string `json:"issuer"` Issuer string `json:"issuer"`
@ -109,7 +119,7 @@ func (p *password) UnmarshalJSON(b []byte) error {
HashFromEnv string `json:"hashFromEnv"` HashFromEnv string `json:"hashFromEnv"`
Groups []string `json:"groups"` Groups []string `json:"groups"`
} }
if err := json.Unmarshal(b, &data); err != nil { if err := configUnmarshaller(b, &data); err != nil {
return err return err
} }
*p = password(storage.Password{ *p = password(storage.Password{
@ -333,7 +343,7 @@ func (s *Storage) UnmarshalJSON(b []byte) error {
Type string `json:"type"` Type string `json:"type"`
Config json.RawMessage `json:"config"` Config json.RawMessage `json:"config"`
} }
if err := json.Unmarshal(b, &store); err != nil { if err := configUnmarshaller(b, &store); err != nil {
return fmt.Errorf("parse storage: %v", err) return fmt.Errorf("parse storage: %v", err)
} }
f, ok := storages[store.Type] f, ok := storages[store.Type]
@ -346,7 +356,7 @@ func (s *Storage) UnmarshalJSON(b []byte) error {
data := []byte(store.Config) data := []byte(store.Config)
if featureflags.ExpandEnv.Enabled() { if featureflags.ExpandEnv.Enabled() {
var rawMap map[string]interface{} var rawMap map[string]interface{}
if err := json.Unmarshal(store.Config, &rawMap); err != nil { if err := configUnmarshaller(store.Config, &rawMap); err != nil {
return fmt.Errorf("unmarshal config for env expansion: %v", err) return fmt.Errorf("unmarshal config for env expansion: %v", err)
} }
@ -363,7 +373,7 @@ func (s *Storage) UnmarshalJSON(b []byte) error {
data = expandedData data = expandedData
} }
if err := json.Unmarshal(data, storageConfig); err != nil { if err := configUnmarshaller(data, storageConfig); err != nil {
return fmt.Errorf("parse storage config: %v", err) return fmt.Errorf("parse storage config: %v", err)
} }
} }
@ -462,7 +472,7 @@ func (c *Connector) UnmarshalJSON(b []byte) error {
Config json.RawMessage `json:"config"` Config json.RawMessage `json:"config"`
} }
if err := json.Unmarshal(b, &conn); err != nil { if err := configUnmarshaller(b, &conn); err != nil {
return fmt.Errorf("parse connector: %v", err) return fmt.Errorf("parse connector: %v", err)
} }
f, ok := server.ConnectorsConfig[conn.Type] f, ok := server.ConnectorsConfig[conn.Type]
@ -475,7 +485,7 @@ func (c *Connector) UnmarshalJSON(b []byte) error {
data := []byte(conn.Config) data := []byte(conn.Config)
if featureflags.ExpandEnv.Enabled() { if featureflags.ExpandEnv.Enabled() {
var rawMap map[string]interface{} var rawMap map[string]interface{}
if err := json.Unmarshal(conn.Config, &rawMap); err != nil { if err := configUnmarshaller(conn.Config, &rawMap); err != nil {
return fmt.Errorf("unmarshal config for env expansion: %v", err) return fmt.Errorf("unmarshal config for env expansion: %v", err)
} }
@ -492,7 +502,7 @@ func (c *Connector) UnmarshalJSON(b []byte) error {
data = expandedData data = expandedData
} }
if err := json.Unmarshal(data, connConfig); err != nil { if err := configUnmarshaller(data, connConfig); err != nil {
return fmt.Errorf("parse connector config: %v", err) return fmt.Errorf("parse connector config: %v", err)
} }
} }

8
cmd/dex/serve.go

@ -97,10 +97,16 @@ func runServe(options serveOptions) error {
} }
var c Config var c Config
if err := yaml.Unmarshal(configData, &c); err != nil {
jsonConfigData, err := yaml.YAMLToJSON(configData)
if err != nil {
return fmt.Errorf("error parse config file %s: %v", configFile, err) return fmt.Errorf("error parse config file %s: %v", configFile, err)
} }
if err := configUnmarshaller(jsonConfigData, &c); err != nil {
return fmt.Errorf("error unmarshalling config file %s: %v", configFile, err)
}
applyConfigOverrides(options, &c) applyConfigOverrides(options, &c)
logger, err := newLogger(c.Logger.Level, c.Logger.Format) logger, err := newLogger(c.Logger.Level, c.Logger.Format)

3
pkg/featureflags/set.go

@ -14,4 +14,7 @@ var (
// ContinueOnConnectorFailure allows the server to start even if some connectors fail to initialize. // ContinueOnConnectorFailure allows the server to start even if some connectors fail to initialize.
ContinueOnConnectorFailure = newFlag("continue_on_connector_failure", true) ContinueOnConnectorFailure = newFlag("continue_on_connector_failure", true)
// ConfigDisallowUnknownFields enables to forbid unknown fields in the config while unmarshaling.
ConfigDisallowUnknownFields = newFlag("config_disallow_unknown_fields", false)
) )

18
storage/etcd/config.go

@ -15,10 +15,10 @@ var defaultDialTimeout = 2 * time.Second
// SSL represents SSL options for etcd databases. // SSL represents SSL options for etcd databases.
type SSL struct { type SSL struct {
ServerName string `json:"serverName" yaml:"serverName"` ServerName string `json:"serverName"`
CAFile string `json:"caFile" yaml:"caFile"` CAFile string `json:"caFile"`
KeyFile string `json:"keyFile" yaml:"keyFile"` KeyFile string `json:"keyFile"`
CertFile string `json:"certFile" yaml:"certFile"` CertFile string `json:"certFile"`
} }
// Etcd options for connecting to etcd databases. // Etcd options for connecting to etcd databases.
@ -26,11 +26,11 @@ type SSL struct {
// configure an etcd namespace either via Namespace field or using `etcd grpc-proxy // configure an etcd namespace either via Namespace field or using `etcd grpc-proxy
// --namespace=<prefix>` // --namespace=<prefix>`
type Etcd struct { type Etcd struct {
Endpoints []string `json:"endpoints" yaml:"endpoints"` Endpoints []string `json:"endpoints"`
Namespace string `json:"namespace" yaml:"namespace"` Namespace string `json:"namespace"`
Username string `json:"username" yaml:"username"` Username string `json:"username"`
Password string `json:"password" yaml:"password"` Password string `json:"password"`
SSL SSL `json:"ssl" yaml:"ssl"` SSL SSL `json:"ssl"`
} }
// Open creates a new storage implementation backed by Etcd // Open creates a new storage implementation backed by Etcd

4
storage/sql/config.go

@ -78,7 +78,7 @@ type SSL struct {
type Postgres struct { type Postgres struct {
NetworkDB NetworkDB
SSL SSL `json:"ssl" yaml:"ssl"` SSL SSL `json:"ssl"`
} }
// Open creates a new storage implementation backed by Postgres. // Open creates a new storage implementation backed by Postgres.
@ -206,7 +206,7 @@ func (p *Postgres) open(logger *slog.Logger) (*conn, error) {
type MySQL struct { type MySQL struct {
NetworkDB NetworkDB
SSL SSL `json:"ssl" yaml:"ssl"` SSL SSL `json:"ssl"`
// TODO(pborzenkov): used by tests to reduce lock wait timeout. Should // TODO(pborzenkov): used by tests to reduce lock wait timeout. Should
// we make it exported and allow users to provide arbitrary params? // we make it exported and allow users to provide arbitrary params?

18
storage/storage.go

@ -149,28 +149,28 @@ type Storage interface {
// - Public clients: https://developers.google.com/api-client-library/python/auth/installed-app // - Public clients: https://developers.google.com/api-client-library/python/auth/installed-app
type Client struct { type Client struct {
// Client ID and secret used to identify the client. // Client ID and secret used to identify the client.
ID string `json:"id" yaml:"id"` ID string `json:"id"`
IDEnv string `json:"idEnv" yaml:"idEnv"` IDEnv string `json:"idEnv"`
Secret string `json:"secret" yaml:"secret"` Secret string `json:"secret"`
SecretEnv string `json:"secretEnv" yaml:"secretEnv"` SecretEnv string `json:"secretEnv"`
// A registered set of redirect URIs. When redirecting from dex to the client, the URI // A registered set of redirect URIs. When redirecting from dex to the client, the URI
// requested to redirect to MUST match one of these values, unless the client is "public". // requested to redirect to MUST match one of these values, unless the client is "public".
RedirectURIs []string `json:"redirectURIs" yaml:"redirectURIs"` RedirectURIs []string `json:"redirectURIs"`
// TrustedPeers are a list of peers which can issue tokens on this client's behalf using // TrustedPeers are a list of peers which can issue tokens on this client's behalf using
// the dynamic "oauth2:server:client_id:(client_id)" scope. If a peer makes such a request, // the dynamic "oauth2:server:client_id:(client_id)" scope. If a peer makes such a request,
// this client's ID will appear as the ID Token's audience. // this client's ID will appear as the ID Token's audience.
// //
// Clients inherently trust themselves. // Clients inherently trust themselves.
TrustedPeers []string `json:"trustedPeers" yaml:"trustedPeers"` TrustedPeers []string `json:"trustedPeers"`
// Public clients must use either use a redirectURL 127.0.0.1:X or "urn:ietf:wg:oauth:2.0:oob" // Public clients must use either use a redirectURL 127.0.0.1:X or "urn:ietf:wg:oauth:2.0:oob"
Public bool `json:"public" yaml:"public"` Public bool `json:"public"`
// Name and LogoURL used when displaying this client to the end user. // Name and LogoURL used when displaying this client to the end user.
Name string `json:"name" yaml:"name"` Name string `json:"name"`
LogoURL string `json:"logoURL" yaml:"logoURL"` LogoURL string `json:"logoURL"`
} }
// Claims represents the ID Token claims supported by the server. // Claims represents the ID Token claims supported by the server.

Loading…
Cancel
Save