Skip to content

Commit

Permalink
Add HTTPS capabilities and document proxy/management commands (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
arekkas authored Nov 9, 2017
1 parent 4863a82 commit 98ef623
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 6 deletions.
39 changes: 39 additions & 0 deletions cmd/helper_messages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package cmd

var corsMessage = `CORS CONTROLS
==============
- CORS_ALLOWED_ORIGINS: A list of origins (comma separated values) a cross-domain request can be executed from.
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.
- CORS_DEBUG: Debugging flag adds additional output to debug server side CORS issues.
- 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_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.`

var databaseUrl = `- DATABASE_URL: A URL to a persistent backend. Hydra 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.`
23 changes: 21 additions & 2 deletions cmd/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func runManagement(c *managementConfig) {
handler.SetRoutes(router)

n := negroni.New()
n.Use(negronilogrus.NewMiddlewareFromLogger(logger, "oahtkeeper-management"))
n.Use(negronilogrus.NewMiddlewareFromLogger(logger, "oathkeeper-management"))
n.UseHandler(router)

addr := c.address
Expand All @@ -59,7 +59,26 @@ func runManagement(c *managementConfig) {

// managementCmd represents the management command
var managementCmd = &cobra.Command{
Use: "management",
Use: "management",
Short: "Starts the ORY Oathkeeper management REST API",
Long: `This starts a HTTP/2 REST API for managing ORY Oathkeeper.
CORE CONTROLS
=============
` + databaseUrl + `
HTTP CONTROLS
==============
- MANAGEMENT_HOST: The host to listen on.
Default: PROXY_HOST="" (all interfaces)
- MANAGEMENT_PORT: The port to listen on.
Default: PROXY_PORT="4456"
` + corsMessage,
Run: func(cmd *cobra.Command, args []string) {
rules, err := newRuleManager(viper.GetString("DATABASE_URL"))
if err != nil {
Expand Down
86 changes: 82 additions & 4 deletions cmd/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import (
"net/http/httputil"
"net/url"

"crypto/tls"

"encoding/base64"

"github.com/meatballhat/negroni-logrus"
"github.com/ory/graceful"
"github.com/ory/hydra/sdk/go/hydra"
Expand All @@ -27,12 +31,65 @@ type proxyConfig struct {
refreshDelay string
rules rule.Manager
bearerTokenSecret string
tlsCert string
tlsKey string
}

// proxyCmd represents the proxy command
var proxyCmd = &cobra.Command{
Use: "proxy",
Short: "Runs the reverse proxy",
Short: "Starts the ORY Oathkeeper firewall reverse proxy",
Long: `This starts a HTTP/2 reverse proxy capable of deciding whether to forward API requests or to block them based on a set of rules.
This command exposes a variety of controls via environment variables. You can
set environments using "export KEY=VALUE" (Linux/macOS) or "set KEY=VALUE" (Windows). On Linux,
you can also set environments by prepending key value pairs: "KEY=VALUE KEY2=VALUE2 hydra"
All possible controls are listed below.
REQUIRED CONTROLS
=============
` + databaseUrl + `
- HYDRA_CLIENT_ID: The OAuth 2.0 Client ID to be used to connect to ORY Hydra. The client must allowed to request the
hydra.warden OAuth 2.0 Scope and allowed to access the warden resources.
- HYDRA_CLIENT_SECRET: The OAuth 2.0 Client Secret of the Client ID referenced aboce.
- HYDRA_URL: The URL of ORY Hydra.
Example: HYDRA_URL=https://hydra.com/
- BACKEND_URL: The URL where requests will be forwarded to, if access is granted.
Example: BACKEND_URL=https://my-backend.com/
- JWT_SHARED_SECRET: The shared secret to be used to encrypt the Authorization Bearer JSON Web Token. Use this
secret to validate that the Bearer Token was indeed issued by this ORY Oathkeeper instance.
HTTP(S) CONTROLS
==============
- HTTP_TLS_KEY: Base64 encoded (without padding) string of the private key (PEM encoded) to be used for HTTP over TLS (HTTPS).
If not set, HTTPS will be disabled and instead HTTP will be served.
- HTTP_TLS_CERT: Base64 encoded (without padding) string of the TLS certificate (PEM encoded) to be used for HTTP over TLS (HTTPS).
If not set, HTTPS will be disabled and instead HTTP will be served.
- PROXY_HOST: The host to listen on.
Default: PROXY_HOST="" (all interfaces)
- PROXY_PORT: The port to listen on.
Default: PROXY_PORT="4455"
OTHER CONTROLS
==============
- REFRESH_DELAY: ORY Oathkeeper stores rules in memory for faster access. This value sets the database polling interval.
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
Default: REFRESH_DELY=5s
` + corsMessage,
Run: func(cmd *cobra.Command, args []string) {
rules, err := newRuleManager(viper.GetString("DATABASE_URL"))
if err != nil {
Expand All @@ -51,6 +108,8 @@ var proxyCmd = &cobra.Command{
cors: parseCorsOptions(""),
address: fmt.Sprintf("%s:%s", viper.GetString("PROXY_HOST"), viper.GetString("PROXY_PORT")),
refreshDelay: viper.GetString("REFRESH_DELAY"),
tlsKey: viper.GetString("HTTP_TLS_KEY"),
tlsCert: viper.GetString("HTTP_TLS_CERT"),
}

runProxy(config)
Expand Down Expand Up @@ -89,17 +148,36 @@ func runProxy(c *proxyConfig) {

ch := cors.New(c.cors).Handler(n)

var cert tls.Certificate
if c.tlsCert != "" && c.tlsKey != "" {
if tlsCert, err := base64.StdEncoding.DecodeString(c.tlsCert); err != nil {
logger.WithError(err).Fatalln("Unable to base64 decode the TLS Certificate.")
} else if tlsKey, err := base64.StdEncoding.DecodeString(c.tlsKey); err != nil {
logger.WithError(err).Fatalln("Unable to base64 decode the TLS Private Key.")
} else if cert, err = tls.X509KeyPair(tlsCert, tlsKey); err != nil {
logger.WithError(err).Fatalln("Unable to load X509 key pair.")
}
}

server := graceful.WithDefaults(&http.Server{
Addr: c.address,
Handler: ch,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
},
})

logger.Printf("Listening on %s.\n", c.address)
if err := graceful.Graceful(server.ListenAndServe, server.Shutdown); err != nil {
logger.Fatalf("Unable to gracefully shutdown HTTP server because %s.\n", err)
if err := graceful.Graceful(func() error {
if c.tlsCert != "" && c.tlsKey != "" {
return server.ListenAndServeTLS("", "")
}
return server.ListenAndServe()
}, server.Shutdown); err != nil {
logger.Fatalf("Unable to gracefully shutdown HTTP(s) server because %s.\n", err)
return
}
logger.Println("HTTP server was shutdown gracefully")
logger.Println("HTTP(s) server was shutdown gracefully")
}

func init() {
Expand Down

0 comments on commit 98ef623

Please sign in to comment.