Skip to content

Commit

Permalink
Reduces setup complexity by making strategies configurable
Browse files Browse the repository at this point in the history
This patch adds another ID Token signing algorithm (HS256) which is easier to set up as it does not rely on ORY Hydra but instead on a shared secret.

Additionally the ability to specify which ID Token singing algorithm to use has been added. Environmental variables to configure the behvaiour have been added as well.

Further, the ORY Keto Warden Authorizer strategy is now optional and disabled when the environment variable `AUTHORIZER_KETO_WARDEN_KETO_URL` is empty.

Closes #71
  • Loading branch information
arekkas authored and arekkas committed Jun 14, 2018
1 parent b2cc5d2 commit 6626f8f
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 90 deletions.
5 changes: 3 additions & 2 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ before finalizing the upgrade process.
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [0.1.0](#010)
- [1.0.0-beta.2](#100-beta2)
- [Changes to the CLI](#changes-to-the-cli)
- [`migrate`](#migrate)
- [Not compatible with ORY Hydra < 1.0.0](#not-compatible-with-ory-hydra--100)
- [0.11.12](#01112)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## 0.1.0
## 1.0.0-beta.2

This release introduces serious breaking changes. If you are upgrading, you will - unfortunately - need to
re-create the database schema and migrate your rules manually. While this is frustrating, there are a ton of features
Expand Down
88 changes: 84 additions & 4 deletions cmd/helper_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,38 +31,118 @@ var corsMessage = `CORS CONTROLS
If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*)
to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
Only one wildcard can be used per origin. The default value is *.
--------------------------------------------------------------
Example: CORS_ALLOWED_ORIGINS=http://*.domain.com,http://*.domain2.com
--------------------------------------------------------------
- CORS_ALLOWED_METHODS: A list of methods (comma separated values) the client is allowed to use with cross-domain
requests. Default value is simple methods (GET and POST).
--------------------------------------------------------------
Example: CORS_ALLOWED_METHODS=POST,GET,PUT
--------------------------------------------------------------
- CORS_ALLOWED_CREDENTIALS: Indicates whether the request can include user credentials like cookies, HTTP authentication
or client side SSL certificates. The default is false.
or client side SSL certificates.
--------------------------------------------------------------
Default: CORS_ALLOWED_CREDENTIALS=false
Example: CORS_ALLOWED_CREDENTIALS=true
--------------------------------------------------------------
- CORS_DEBUG: Debugging flag adds additional output to debug server side CORS issues.
--------------------------------------------------------------
Default: CORS_DEBUG=false
Example: CORS_DEBUG=true
--------------------------------------------------------------
- CORS_MAX_AGE: Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 which stands for no max age.
- CORS_MAX_AGE: Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0
which stands for no max age.
--------------------------------------------------------------
Default: CORS_MAX_AGE=0
Example: CORS_MAX_AGE=10
--------------------------------------------------------------
- CORS_ALLOWED_HEADERS: A list of non simple headers (comma separated values) the client is allowed to use with cross-domain requests.
- CORS_ALLOWED_HEADERS: A list of non simple headers (comma separated values) the client is allowed to use with
cross-domain requests.
- CORS_EXPOSED_HEADERS: Indicates which headers (comma separated values) are safe to expose to the API of a CORS API specification.`
- CORS_EXPOSED_HEADERS: Indicates which headers (comma separated values) are safe to expose to the API of a
CORS API specification.`

var databaseUrl = `- DATABASE_URL: A URL to a persistent backend. ORY Oathkeeper supports various backends:
- Memory: If DATABASE_URL is "memory", data will be written to memory and is lost when you restart this instance.
--------------------------------------------------------------
Example: DATABASE_URL=memory
--------------------------------------------------------------
- Postgres: If DATABASE_URL is a DSN starting with postgres:// PostgreSQL will be used as storage backend.
--------------------------------------------------------------
Example: DATABASE_URL=postgres://user:password@host:123/database
--------------------------------------------------------------
If PostgreSQL is not serving TLS, append ?sslmode=disable to the url:
--------------------------------------------------------------
DATABASE_URL=postgres://user:password@host:123/database?sslmode=disable
--------------------------------------------------------------
- MySQL: If DATABASE_URL is a DSN starting with mysql:// MySQL will be used as storage backend.
--------------------------------------------------------------
Example: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true
--------------------------------------------------------------
Be aware that the ?parseTime=true parameter is mandatory, or timestamps will not work.`

var credentialsIssuer = `CREDENTIALS ISSUERS
==============
- ID Token Credentials Issuer:
- CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN: How long the ID token will be active. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
--------------------------------------------------------------
Default: CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=10m
--------------------------------------------------------------
- CREDENTIALS_ISSUER_ID_TOKEN_ISSUER: Who issued the token - this will be the value of the "iss" claim in the ID Token.
--------------------------------------------------------------
Example: CREDENTIALS_ISSUER_ID_TOKEN_ISSUER=http://oathkeeper-url/
--------------------------------------------------------------
- CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM: The algorithm to be used for signing the ID Token. Supports HS256 (shared secret),
"ORY-HYDRA" (uses ORY Hydra to create, store, and fetch RSA Keys for signing).
--------------------------------------------------------------
Default: CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM=HS256
--------------------------------------------------------------
Example: CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM=HS256
Example: CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM=ORY-HYDRA
--------------------------------------------------------------
Depending on which algorithm you use, there are different configuration options necessary.
HS256 ALGORITHM
===============
- CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET: The shared secret to be used for the HS256 algorithm. The secret
must be 32 characters long.
--------------------------------------------------------------
Example: CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET=dYmTueb6zg8TphfZbOUpOewd0gt7u0SH
--------------------------------------------------------------
ORY-HYDRA ALGORITHM
===============
- CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_URL: The URL where ORY Hydra is located.
--------------------------------------------------------------
Example: CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_URL=http://hydra-url/
--------------------------------------------------------------
- CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_REFRESH_INTERVAL: This value sets how often ORY Oathkeeper checks if a new
key for signing is available at ORY Hydra. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
--------------------------------------------------------------
Default: CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_REFRESH_INTERVAL=5m
--------------------------------------------------------------
- CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_JWK_SET_ID: The JSON Web Key set identifier that will be used to create,
store, and retrieve the JSON Web Key from ORY Hydra.
--------------------------------------------------------------
Default: CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_JWK_SET_ID=oathkeeper:id-token
--------------------------------------------------------------`

func fatalf(msg string, args ...interface{}) {
fmt.Printf(msg+"\n", args...)
os.Exit(1)
Expand Down
27 changes: 26 additions & 1 deletion cmd/helper_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"github.com/ory/hydra/sdk/go/hydra"
"github.com/ory/oathkeeper/rsakey"
"github.com/ory/oathkeeper/rule"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
)

Expand Down Expand Up @@ -74,7 +76,7 @@ func refreshKeys(k rsakey.Manager, fails int) {
}

if err := k.Refresh(); err != nil {
logger.WithError(err).WithField("retry", fails).Errorln("Unable to refresh RSA keys for signing ID Token, 'id_token' credentials issuer will not work.")
logger.WithError(err).WithField("retry", fails).Errorln("Unable to refresh keys for signing ID Token, 'id_token' credentials issuer will not work.")
//if fails > 15 {
// logger.WithError(err).WithField("retry", fails).Fatalf("Terminating after retry %d\n", fails)
//}
Expand All @@ -93,3 +95,26 @@ func refreshKeys(k rsakey.Manager, fails int) {

refreshKeys(k, 1)
}

func keyManagerFactory(l logrus.FieldLogger) (keyManager rsakey.Manager, err error) {
switch a := strings.ToLower(viper.GetString("CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM")); a {
case "hs256":
secret := []byte(viper.GetString("CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET"))
if len(secret) < 32 {
return nil, errors.New("The secret set in CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET must be 32 characters long.")
}
keyManager = rsakey.NewLocalHS256Manager(secret)
//case "rs256":
// keyManager = &rsakey.LocalRS256Manager{KeyStrength: 4096}
case "ory-hydra":
sdk := getHydraSDK()
keyManager = &rsakey.HydraManager{
SDK: sdk,
Set: viper.GetString("CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_JWK_SET_ID"),
}
default:
return nil, errors.Errorf("Unknown ID Token singing algorithm %s", a)
}

return keyManager, nil
}
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ func initConfig() {

viper.SetDefault("CREDENTIALS_ISSUER_ID_TOKEN_JWK_REFRESH_INTERVAL", "5s")
viper.SetDefault("CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_JWK_SET_ID", "oathkeeper:id-token")
viper.SetDefault("CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM", "HS256")

viper.SetDefault("AUTHENTICATOR_ANONYMOUS_USERNAME", "anonymous")
viper.SetDefault("CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN", "10m")
viper.SetDefault("CREDENTIALS_ISSUER_ID_TOKEN_ISSUER", "http://localhost:"+viper.GetString("PORT"))

// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
Expand Down
4 changes: 1 addition & 3 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ func TestCommandLineInterface(t *testing.T) {
var osArgs = make([]string, len(os.Args))
os.Setenv("PORT", "4456")
os.Setenv("DATABASE_URL", "memory")
os.Setenv("CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_URL", "http://does-not-exist.com/")
os.Setenv("CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_ID", "does-not-exist")
os.Setenv("CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_SECRET", "does-not-exist")
os.Setenv("CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET", "dYmTueb6zg8TphfZbOUpOewd0gt7u0SH")
copy(osArgs, os.Args)

for _, c := range []struct {
Expand Down
13 changes: 7 additions & 6 deletions cmd/serve_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ CORE CONTROLS
` + databaseUrl + `
` + credentialsIssuer + `
HTTP CONTROLS
==============
Expand All @@ -65,11 +68,9 @@ HTTP CONTROLS
logger.WithError(err).Fatalln("Unable to connect to rule backend")
}

sdk := getHydraSDK()

keyManager := &rsakey.HydraManager{
SDK: sdk,
Set: viper.GetString("CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_JWK_SET_ID"),
keyManager, err := keyManagerFactory(logger)
if err != nil {
logger.WithError(err).Fatalln("Unable to initialize the ID Token signing algorithm")
}

writer := herodot.NewJSONWriter(logger)
Expand Down Expand Up @@ -109,7 +110,7 @@ HTTP CONTROLS
Handler: ch,
})

logger.Printf("Listening on %s.\n", addr)
logger.Printf("Listening on %s", addr)
if err := graceful.Graceful(server.ListenAndServe, server.Shutdown); err != nil {
logger.Fatalf("Unable to gracefully shutdown HTTP server because %s.\n", err)
return
Expand Down
Loading

0 comments on commit 6626f8f

Please sign in to comment.