From 91cbd466a55c730a5689d1079c3e551237df0419 Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Fri, 13 Dec 2019 16:33:21 -0800 Subject: [PATCH 1/6] Option to add staticPasswords from environment variables --- cmd/dex/config.go | 15 ++-- cmd/dex/config_test.go | 167 ++++++++++++++++++++++++++++++++++++++++- storage/storage.go | 3 + 3 files changed, 179 insertions(+), 6 deletions(-) diff --git a/cmd/dex/config.go b/cmd/dex/config.go index 2519f6f5..c0394e37 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -85,10 +85,11 @@ type password storage.Password func (p *password) UnmarshalJSON(b []byte) error { var data struct { - Email string `json:"email"` - Username string `json:"username"` - UserID string `json:"userID"` - Hash string `json:"hash"` + Email string `json:"email"` + Username string `json:"username"` + UserID string `json:"userID"` + Hash string `json:"hash"` + HashFromEnv string `json:"hashFromEnv"` } if err := json.Unmarshal(b, &data); err != nil { return err @@ -99,7 +100,11 @@ func (p *password) UnmarshalJSON(b []byte) error { UserID: data.UserID, }) if len(data.Hash) == 0 { - return fmt.Errorf("no password hash provided") + if len(data.HashFromEnv) > 0 { + data.Hash = os.Getenv(data.HashFromEnv) + } else { + return fmt.Errorf("no password hash provided") + } } // If this value is a valid bcrypt, use it. diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index d7875ad1..feb23144 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -1,9 +1,11 @@ package main import ( - "github.com/dexidp/dex/server" + "os" "testing" + "github.com/dexidp/dex/server" + "github.com/ghodss/yaml" "github.com/kylelemons/godebug/pretty" @@ -15,6 +17,8 @@ import ( var _ = yaml.YAMLToJSON +const testHashStaticPasswordEnv = "FOO_PASSWORD" + func TestValidConfiguration(t *testing.T) { configuration := Config{ Issuer: "http://127.0.0.1:5556/dex", @@ -213,3 +217,164 @@ logger: } } + +func TestUnmarshalConfigWithEnv(t *testing.T) { + staticPasswordEnv := os.Getenv(testHashStaticPasswordEnv) + if staticPasswordEnv == "" { + t.Skipf("test environment variable %q not set, skipping", testHashStaticPasswordEnv) + } + rawConfig := []byte(` +issuer: http://127.0.0.1:5556/dex +storage: + type: postgres + config: + host: 10.0.0.1 + port: 65432 + maxOpenConns: 5 + maxIdleConns: 3 + connMaxLifetime: 30 + connectionTimeout: 3 +web: + http: 127.0.0.1:5556 + +frontend: + dir: ./web + extra: + foo: bar + +staticClients: +- id: example-app + redirectURIs: + - 'http://127.0.0.1:5555/callback' + name: 'Example App' + secret: ZXhhbXBsZS1hcHAtc2VjcmV0 + +oauth2: + alwaysShowLoginScreen: true + +connectors: +- type: mockCallback + id: mock + name: Example +- type: oidc + id: google + name: Google + config: + issuer: https://accounts.google.com + clientID: foo + clientSecret: bar + redirectURI: http://127.0.0.1:5556/dex/callback/google + +enablePasswordDB: true +staticPasswords: +- email: "admin@example.com" + # bcrypt hash of the string "password" + hash: "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy" + username: "admin" + userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" +- email: "foo@example.com" + hashFromEnv: "FOO_PASSWORD" + username: "foo" + userID: "41331323-6f44-45e6-b3b9-2c4b60c02be5" + +expiry: + signingKeys: "7h" + idTokens: "25h" + authRequests: "25h" + +logger: + level: "debug" + format: "json" +`) + + want := Config{ + Issuer: "http://127.0.0.1:5556/dex", + Storage: Storage{ + Type: "postgres", + Config: &sql.Postgres{ + NetworkDB: sql.NetworkDB{ + Host: "10.0.0.1", + Port: 65432, + MaxOpenConns: 5, + MaxIdleConns: 3, + ConnMaxLifetime: 30, + ConnectionTimeout: 3, + }, + }, + }, + Web: Web{ + HTTP: "127.0.0.1:5556", + }, + Frontend: server.WebConfig{ + Dir: "./web", + Extra: map[string]string{ + "foo": "bar", + }, + }, + StaticClients: []storage.Client{ + { + ID: "example-app", + Secret: "ZXhhbXBsZS1hcHAtc2VjcmV0", + Name: "Example App", + RedirectURIs: []string{ + "http://127.0.0.1:5555/callback", + }, + }, + }, + OAuth2: OAuth2{ + AlwaysShowLoginScreen: true, + }, + StaticConnectors: []Connector{ + { + Type: "mockCallback", + ID: "mock", + Name: "Example", + Config: &mock.CallbackConfig{}, + }, + { + Type: "oidc", + ID: "google", + Name: "Google", + Config: &oidc.Config{ + Issuer: "https://accounts.google.com", + ClientID: "foo", + ClientSecret: "bar", + RedirectURI: "http://127.0.0.1:5556/dex/callback/google", + }, + }, + }, + EnablePasswordDB: true, + StaticPasswords: []password{ + { + Email: "admin@example.com", + Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"), + Username: "admin", + UserID: "08a8684b-db88-4b73-90a9-3cd1661f5466", + }, + { + Email: "foo@example.com", + Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"), + Username: "foo", + UserID: "41331323-6f44-45e6-b3b9-2c4b60c02be5", + }, + }, + Expiry: Expiry{ + SigningKeys: "7h", + IDTokens: "25h", + AuthRequests: "25h", + }, + Logger: Logger{ + Level: "debug", + Format: "json", + }, + } + + var c Config + if err := yaml.Unmarshal(rawConfig, &c); err != nil { + t.Fatalf("failed to decode config: %v", err) + } + if diff := pretty.Compare(c, want); diff != "" { + t.Errorf("got!=want: %s", diff) + } + +} diff --git a/storage/storage.go b/storage/storage.go index cb2a7e0c..42ecd8ed 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -292,6 +292,9 @@ type Password struct { // Bcrypt encoded hash of the password. This package enforces a min cost value of 10 Hash []byte `json:"hash"` + // Bcrypt encoded hash of the password set in environment variable of this name. + HashFromEnv string `json:"hashFromEnv"` + // Optional username to display. NOT used during login. Username string `json:"username"` From af9c2880a6a843e52d830d8fa87a278c5f132e5c Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Fri, 13 Dec 2019 16:52:10 -0800 Subject: [PATCH 2/6] Corrects validation logic for static password check --- cmd/dex/config.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/dex/config.go b/cmd/dex/config.go index c0394e37..9db0ce3f 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -99,12 +99,11 @@ func (p *password) UnmarshalJSON(b []byte) error { Username: data.Username, UserID: data.UserID, }) + if len(data.Hash) == 0 && len(data.HashFromEnv) > 0 { + data.Hash = os.Getenv(data.HashFromEnv) + } if len(data.Hash) == 0 { - if len(data.HashFromEnv) > 0 { - data.Hash = os.Getenv(data.HashFromEnv) - } else { - return fmt.Errorf("no password hash provided") - } + return fmt.Errorf("no password hash provided") } // If this value is a valid bcrypt, use it. From 1fd5dd7b0eb2dc9ffdd1265d675a1e0abd77847d Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Fri, 13 Dec 2019 17:03:56 -0800 Subject: [PATCH 3/6] Change env var prefix to DEX and add to ci.yaml --- .github/workflows/ci.yml | 1 + cmd/dex/config_test.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3027be5f..4f038690 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,7 @@ jobs: - name: Run tests run: make testall env: + DEX_FOO_USER_PASSWORD: \$2a\$10\$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy DEX_MYSQL_DATABASE: dex DEX_MYSQL_USER: root DEX_MYSQL_PASSWORD: root diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index feb23144..c5c3c479 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -17,7 +17,7 @@ import ( var _ = yaml.YAMLToJSON -const testHashStaticPasswordEnv = "FOO_PASSWORD" +const testHashStaticPasswordEnv = "DEX_FOO_USER_PASSWORD" func TestValidConfiguration(t *testing.T) { configuration := Config{ @@ -273,7 +273,7 @@ staticPasswords: username: "admin" userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" - email: "foo@example.com" - hashFromEnv: "FOO_PASSWORD" + hashFromEnv: "DEX_FOO_USER_PASSWORD" username: "foo" userID: "41331323-6f44-45e6-b3b9-2c4b60c02be5" From 9aec1e7db2ffe6cde3ac8de1aea7d173b52b091a Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Fri, 13 Dec 2019 17:25:56 -0800 Subject: [PATCH 4/6] Avoids unnecessary escape characters in ENV var for ci.yaml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f038690..57657e8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: - name: Run tests run: make testall env: - DEX_FOO_USER_PASSWORD: \$2a\$10\$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy + DEX_FOO_USER_PASSWORD: $2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy DEX_MYSQL_DATABASE: dex DEX_MYSQL_USER: root DEX_MYSQL_PASSWORD: root From 2d5619e4e8fac802a191475421c1af1d97583a37 Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Tue, 7 Jan 2020 11:48:35 -0800 Subject: [PATCH 5/6] Corrects imports after merge --- cmd/dex/config_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index f0f8d7de..694c7c51 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -4,8 +4,6 @@ import ( "os" "testing" - "github.com/dexidp/dex/server" - "github.com/ghodss/yaml" "github.com/kylelemons/godebug/pretty" From 321790870f0214419d0d1758482cab6d444c9f27 Mon Sep 17 00:00:00 2001 From: krishnadurai Date: Tue, 7 Jan 2020 16:34:32 -0800 Subject: [PATCH 6/6] Fixes lint --- cmd/dex/config_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index 694c7c51..12c7b218 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -374,5 +374,4 @@ logger: if diff := pretty.Compare(c, want); diff != "" { t.Errorf("got!=want: %s", diff) } - }