Skip to content

Commit

Permalink
Watch configuration and access rule changes (#217)
Browse files Browse the repository at this point in the history
This patch allows oathkeeper to re-load any changes made to the configuraiton file and/or the access
rules to be reloaded without a restart.

Some configuration keys like serve.*, log.*, profiling however require a restart.
  • Loading branch information
aeneasr committed Jul 16, 2019
1 parent d7da8e2 commit a078e89
Show file tree
Hide file tree
Showing 49 changed files with 1,386 additions and 599 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
- run: go-acc -o coverage.txt ./... -- -failfast -timeout=20m
- run: test -z "$CIRCLE_PR_NUMBER" && goveralls -service=circle-ci -coverprofile=coverage.txt -repotoken=$COVERALLS_REPO_TOKEN || echo "forks are not allowed to push to coveralls"
- run: ./test/e2e/run.sh
- run: ./test/reload/run.sh
- run: curl -sL https://git.io/goreleaser | bash -s -- --snapshot --skip-publish --rm-dist

release:
Expand Down
741 changes: 536 additions & 205 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ documentation and upgrade instructions.
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [Installation](#installation)
- [Ecosystem](#ecosystem)
- [ORY Security Console: Administrative User Interface](#ory-security-console-administrative-user-interface)
Expand Down
3 changes: 2 additions & 1 deletion api/credential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
"net/http/httptest"
"testing"

"github.com/spf13/viper"
"github.com/square/go-jose"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ory/viper"

"github.com/ory/oathkeeper/driver/configuration"
"github.com/ory/oathkeeper/internal"
"github.com/ory/oathkeeper/x"
Expand Down
2 changes: 1 addition & 1 deletion api/decision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"net/http/httptest"
"testing"

"github.com/spf13/viper"
"github.com/ory/viper"

"github.com/ory/oathkeeper/driver/configuration"

Expand Down
95 changes: 20 additions & 75 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,16 @@ package cmd
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/ory/viper"
"github.com/ory/x/viperx"

"github.com/ory/x/logrusx"
)

var cfgFile string

var (
Version = "master"
Date = "undefined"
Expand All @@ -60,73 +57,21 @@ func Execute() {
var logger *logrus.Logger

func init() {
cobra.OnInitialize(initConfig)

// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags, which, if defined here,
// will be global for your application.

RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.oathkeeper.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// enable ability to specify config file via flag
viper.SetConfigFile(cfgFile)
} else {
path := absPathify("$HOME")
if _, err := os.Stat(filepath.Join(path, ".oathkeeper.yml")); err != nil {
_, _ = os.Create(filepath.Join(path, ".oathkeeper.yml"))
}

viper.SetConfigType("yaml")
viper.SetConfigName(".oathkeeper") // name of config file (without extension)
viper.AddConfigPath("$HOME") // adding home directory as first search path
}

viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv() // read in environment variables that match

// If a config file is found, read it in.
configErr := viper.ReadInConfig()
logger = logrusx.New()
if configErr == nil {
logger.Debugf("Using config file: %s", viper.ConfigFileUsed())
}
}

func absPathify(inPath string) string {
if strings.HasPrefix(inPath, "$HOME") {
inPath = userHomeDir() + inPath[5:]
}

if strings.HasPrefix(inPath, "$") {
end := strings.Index(inPath, string(os.PathSeparator))
inPath = os.Getenv(inPath[1:end]) + inPath[end:]
}

if filepath.IsAbs(inPath) {
return filepath.Clean(inPath)
}

p, err := filepath.Abs(inPath)
if err == nil {
return filepath.Clean(p)
}
return ""
}

func userHomeDir() string {
if runtime.GOOS == "windows" {
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
if home == "" {
home = os.Getenv("USERPROFILE")
}
return home
}
return os.Getenv("HOME")
cobra.OnInitialize(func() {
viperx.InitializeConfig("oathkeeper", "", nil)

logger = logrusx.New()
viperx.WatchConfig(logger, &viperx.WatchOptions{
Immutables: []string{"serve", "profiling", "log"},
OnImmutableChange: func(immutable string) {
logger.
WithField("key", immutable).
WithField("value", fmt.Sprintf("%v", viper.Get(immutable))).
Fatal("A configuration value marked as immutable has changed, shutting down.")

},
})
})

viperx.RegisterConfigFlag(RootCmd, "oathkeeper")
}
13 changes: 5 additions & 8 deletions cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/urfave/negroni"

"github.com/ory/x/healthx"
"github.com/ory/viper"

"github.com/ory/herodot"
"github.com/ory/x/healthx"

"github.com/ory/graceful"
"github.com/ory/x/corsx"
Expand Down Expand Up @@ -62,7 +61,7 @@ func runProxy(d driver.Driver, n *negroni.Negroni, logger *logrus.Logger) func()
logger.Printf("Listening on https://%s", addr)
return server.ListenAndServeTLS("", "")
}
logger.Printf("Listening on http://%s", addr)
logger.Infof("Listening on http://%s", addr)
return server.ListenAndServe()
}, server.Shutdown); err != nil {
logger.Fatalf("Unable to gracefully shutdown HTTP(s) server because %v", err)
Expand Down Expand Up @@ -98,7 +97,7 @@ func runAPI(d driver.Driver, n *negroni.Negroni, logger *logrus.Logger) func() {
logger.Printf("Listening on https://%s", addr)
return server.ListenAndServeTLS("", "")
}
logger.Printf("Listening on http://%s", addr)
logger.Infof("Listening on http://%s", addr)
return server.ListenAndServe()
}, server.Shutdown); err != nil {
logger.Fatalf("Unable to gracefully shutdown HTTP(s) server because %v", err)
Expand Down Expand Up @@ -153,9 +152,7 @@ func RunServe(version, build, date string) func(cmd *cobra.Command, args []strin

logger := logrusx.New()
d := driver.NewDefaultDriver(logger, version, build, date, true)
if err := d.Registry().Init(); err != nil {
herodot.DefaultErrorLogger(logger, err).Fatal("Unable to initialize.")
}
d.Registry().Init()

adminmw := negroni.New()
publicmw := negroni.New()
Expand Down
15 changes: 12 additions & 3 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@
#
#
# ORY Oathkeeper can be configured using a configuration file and passing the file location using `-c path/to/config.yaml`.
# Per default, ORY Oathkeeper will look up and load file ~/.oathkeeper.yaml. All configuration keys can be set using environment
# variables as well.
# Per default, ORY Oathkeeper will look up and load file ~/.oathkeeper.yaml.
#
# Setting environment variables is easy:
# When loading configuration files, ORY Oathkeeper will automatically watch for any changes in the configuration file.
# All configuration keys with exception of:
#
# - `serve.*`
# - `log.*`
# - `profiling`
#
# can be reloaded. If a change to one of the exception keys is detected, ORY Oathkeeper will exit with a non-zero exit code
# in hopes of being restarted by e.g. Docker or Kubernetes in order to properly reload the config.
#
# All configuration keys can be set using environment variables as well. Setting environment variables is easy:
#
## Linux / OSX
#
Expand Down
3 changes: 2 additions & 1 deletion driver/configuration/provider_viper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"testing"

"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"

"github.com/ory/viper"
)

func TestToScopeStrategy(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion driver/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

type Registry interface {
Init() error
Init()

WithConfig(c configuration.Provider) Registry
WithLogger(l logrus.FieldLogger) Registry
Expand Down
23 changes: 11 additions & 12 deletions driver/registry_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import (
"github.com/sirupsen/logrus"

"github.com/ory/herodot"
"github.com/ory/x/healthx"

"github.com/ory/oathkeeper/api"
"github.com/ory/oathkeeper/credentials"
"github.com/ory/oathkeeper/driver/configuration"
"github.com/ory/oathkeeper/pipeline/authn"
"github.com/ory/oathkeeper/pipeline/authz"
"github.com/ory/oathkeeper/pipeline/mutate"
"github.com/ory/oathkeeper/rule"
"github.com/ory/x/healthx"
)

var _ Registry = new(RegistryMemory)
Expand Down Expand Up @@ -54,19 +55,17 @@ type RegistryMemory struct {
authenticators map[string]authn.Authenticator
authorizers map[string]authz.Authorizer
mutators map[string]mutate.Mutator
}

func (r *RegistryMemory) Init() error {
rules, err := r.RuleFetcher().Fetch()
if err != nil {
return err
}

if len(rules) == 0 {
r.Logger().Warn("No access rules have been configured, all requests will be denied.")
}
ruleRepositoryLock sync.Mutex
}

return r.RuleRepository().Set(context.Background(), rules)
func (r *RegistryMemory) Init() {
go func() {
if err := r.RuleFetcher().Watch(context.Background()); err != nil {
r.Logger().WithError(err).Fatal("Access rule watcher terminated with an error.")
}
}()
_ = r.RuleRepository()
}

func (r *RegistryMemory) RuleFetcher() rule.Fetcher {
Expand Down
32 changes: 10 additions & 22 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module github.com/ory/oathkeeper

require (
cloud.google.com/go v0.37.2 // indirect
github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf
github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7
github.com/bxcodec/faker v2.0.1+incompatible
github.com/codegangsta/negroni v1.0.0 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fsnotify/fsnotify v1.4.7
github.com/ghodss/yaml v1.0.0
github.com/go-errors/errors v1.0.1
github.com/go-openapi/analysis v0.19.0 // indirect
Expand All @@ -23,15 +23,12 @@ require (
github.com/go-swagger/go-swagger v0.19.0
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 // indirect
github.com/golang/gddo v0.0.0-20190312205958-5a2505f3dbf0 // indirect
github.com/golang/mock v1.2.0
github.com/golang/protobuf v1.3.1 // indirect
github.com/golang/mock v1.3.1
github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.4.0 // indirect
github.com/gorilla/mux v1.7.1 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jessevdk/go-flags v1.4.0 // indirect
github.com/julienschmidt/httprouter v1.2.0
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/lib/pq v1.0.0
github.com/luna-duclos/instrumentedsql v0.0.0-20190316074304-ecad98b20aec // indirect
github.com/mattn/goveralls v0.0.2
Expand All @@ -44,32 +41,23 @@ require (
github.com/ory/graceful v0.1.1
github.com/ory/herodot v0.6.2
github.com/ory/ladon v1.0.1
github.com/ory/x v0.0.60
github.com/ory/viper v1.5.3
github.com/ory/x v0.0.65
github.com/pborman/uuid v1.2.0
github.com/pelletier/go-toml v1.3.0 // indirect
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pkg/errors v0.8.1
github.com/rs/cors v1.6.0
github.com/sirupsen/logrus v1.4.1
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.3.2
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518
github.com/square/go-jose v2.3.1+incompatible
github.com/stretchr/testify v1.3.0
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
github.com/toqueteos/webbrowser v1.1.0 // indirect
github.com/urfave/negroni v1.0.0
go.opencensus.io v0.20.0 // indirect
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
golang.org/x/tools v0.0.0-20190404132500-923d25813098
google.golang.org/appengine v1.5.0 // indirect
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 // indirect
google.golang.org/grpc v1.19.1 // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8
gopkg.in/square/go-jose.v2 v2.3.0
)

Expand Down
Loading

0 comments on commit a078e89

Please sign in to comment.