Skip to content

Commit

Permalink
feat: adds option to append certificate file to root ca for upstream …
Browse files Browse the repository at this point in the history
…connections (ory#181)
  • Loading branch information
wraix committed Apr 30, 2021
1 parent e8efa64 commit f126ca0
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 2 deletions.
16 changes: 16 additions & 0 deletions .schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,22 @@
"timeout": {
"$ref": "#/definitions/serverTimeout"
},
"upstream": {
"type": "object",
"title": "HTTP Upstream",
"additionalProperties": false,
"properties": {
"ca_append_crt_path": {
"type": "string",
"default": "",
"examples": [
"./self-signed.crt"
],
"title": "CA Certificate",
"description": "The file containing the CA certificates to append to the Root CA when using upstream connections."
}
}
},
"cors": {
"$ref": "#/definitions/cors"
},
Expand Down
22 changes: 22 additions & 0 deletions docs/docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,28 @@ serve:
#
read: 5s

## HTTP Upstream ##
#
# Control the HTTP upstream.
#
upstream:
## Append Certificate To Root CA ##
#
# The path to a certificate file to append to the Root Certificate Authority for the upstream connection. Use this to accept self-signed certificates on the upstream only, keeping the host system certificate authority unaltered.
#
# Default value: ""
#
# Examples:
# - self-signed.crt
#
# Set this value using environment variables on
# - Linux/macOS:
# $ export SERVE_PROXY_UPSTREAM_CA_APPEND_CRT_PATH=<value>
# - Windows Command Line (CMD):
# > set SERVE_PROXY_UPSTREAM_CA_APPEND_CRT_PATH=<value>
#
ca_append_crt_path: ""

## Cross Origin Resource Sharing (CORS) ##
#
# Configure [Cross Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) using the following options.
Expand Down
3 changes: 2 additions & 1 deletion driver/configuration/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net/url"
"time"

"github.com/gobuffalo/packr/v2"
packr "github.com/gobuffalo/packr/v2"

"github.com/ory/fosite"
"github.com/ory/x/tracing"
Expand Down Expand Up @@ -41,6 +41,7 @@ type Provider interface {
ProxyReadTimeout() time.Duration
ProxyWriteTimeout() time.Duration
ProxyIdleTimeout() time.Duration
ProxyServeUpstreamCaAppendCrtPath() string

APIReadTimeout() time.Duration
APIWriteTimeout() time.Duration
Expand Down
5 changes: 5 additions & 0 deletions driver/configuration/provider_viper.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const (
ViperKeyProxyIdleTimeout = "serve.proxy.timeout.idle"
ViperKeyProxyServeAddressHost = "serve.proxy.host"
ViperKeyProxyServeAddressPort = "serve.proxy.port"
ViperKeyProxyUpstreamCaAppendCrtPath = "serve.proxy.upstream.ca_append_crt_path"
ViperKeyAPIServeAddressHost = "serve.api.host"
ViperKeyAPIServeAddressPort = "serve.api.port"
ViperKeyAPIReadTimeout = "serve.api.timeout.read"
Expand Down Expand Up @@ -177,6 +178,10 @@ func (v *ViperProvider) ProxyServeAddress() string {
)
}

func (v *ViperProvider) ProxyServeUpstreamCaAppendCrtPath() string {
return viperx.GetString(v.l, ViperKeyProxyUpstreamCaAppendCrtPath, "")
}

func (v *ViperProvider) APIReadTimeout() time.Duration {
return viperx.GetDuration(v.l, ViperKeyAPIReadTimeout, time.Second*5)
}
Expand Down
39 changes: 39 additions & 0 deletions driver/registry_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package driver

import (
"context"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"sync"
"time"

Expand Down Expand Up @@ -102,6 +106,41 @@ func (r *RegistryMemory) RuleMatcher() rule.Matcher {
return r.ruleRepository
}

func (r *RegistryMemory) UpstreamTransport(req *http.Request) (http.RoundTripper, error) {

// Use req to decide the transport per request iff need be.

certFile := r.c.ProxyServeUpstreamCaAppendCrtPath()
if certFile == "" {
return http.DefaultTransport, nil
}

transport := &(*http.DefaultTransport.(*http.Transport)) // shallow copy

// Get the SystemCertPool or continue with an empty pool on error
rootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}

certs, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, err
}

// Append our cert to the system pool
if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
return nil, errors.New("No certs appended, only system certs present, did you specify the correct cert file?")
}

transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: false,
RootCAs: rootCAs,
}

return transport, nil
}

func NewRegistryMemory() *RegistryMemory {
return &RegistryMemory{}
}
Expand Down
46 changes: 45 additions & 1 deletion proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ package proxy

import (
"context"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"net/url"
Expand All @@ -41,6 +43,7 @@ type proxyRegistry interface {

ProxyRequestHandler() *RequestHandler
RuleMatcher() rule.Matcher
UpstreamTransport(r *http.Request) (http.RoundTripper, error)
}

func NewProxy(r proxyRegistry) *Proxy {
Expand Down Expand Up @@ -88,7 +91,18 @@ func (d *Proxy) RoundTrip(r *http.Request) (*http.Response, error) {
Header: rw.header,
}, nil
} else if err == nil {
res, err := http.DefaultTransport.RoundTrip(r)

transport, err := d.r.UpstreamTransport(r)
if err != nil {
d.r.Logger().
WithError(errors.WithStack(err)).
WithField("granted", false).
WithFields(fields).
Warn("Access request denied because upstream transport creation failed")
return nil, err
}

res, err := transport.RoundTrip(r)
if err != nil {
d.r.Logger().
WithError(errors.WithStack(err)).
Expand Down Expand Up @@ -194,3 +208,33 @@ func ConfigureBackendURL(r *http.Request, rl *rule.Rule) error {

return nil
}

// Allow for extending the Root CA chain
// Use to avoid the error: "http: proxy error: x509: certificate signed by unknown authority" for self-signed
// certificates upstream.
func useTransportWithExtendedRootCa(certFile string) (transport *http.Transport, err error) {
transport = &(*http.DefaultTransport.(*http.Transport)) // shallow copy

// Get the SystemCertPool or continue with an empty pool on error
rootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}

certs, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, err
}

// Append our cert to the system pool
if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
return nil, errors.New("No certs appended, only system certs present, did you specifi the correct cert file?")
}

transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: false,
RootCAs: rootCAs,
}

return transport, nil
}

0 comments on commit f126ca0

Please sign in to comment.