Skip to content
Snippets Groups Projects
Commit f212d468 authored by Rohith's avatar Rohith
Browse files

- adding the mutual tls options to enforce client are signed by the CA

parent 89a843b4
No related branches found
No related tags found
No related merge requests found
......@@ -17,7 +17,7 @@ USAGE:
keycloak-proxy [global options] command [command options] [arguments...]
VERSION:
v0.0.6, git+sha: 73e0db2
v1.0.0-rc1
AUTHOR(S):
Rohith <gambol99@gmail.com>
......@@ -37,9 +37,10 @@ GLOBAL OPTIONS:
--hostname [--hostname option --hostname option] a list of hostname which the service will respond to, defaults to all
--tls-cert the path to a certificate file used for TLS
--tls-private-key the path to the private key for TLS support
--tls-ca-certificate the path to the ca certificate used for mutual TLS
--scope [--scope option --scope option] a variable list of scopes requested when authenticating the user
--claim [--claim option --claim option] a series of key pair values which must match the claims in the token present e.g. aud=myapp, iss=http://example.com etcd
--resource [--resource option --resource option] a list of resources 'uri=/admin|methods=GET|roles=role1,role2|whitelisted=(true|false)'
--resource [--resource option --resource option] a list of resources 'uri=/admin|methods=GET|roles=role1,role2'
--signin-page a custom template displayed for signin
--forbidden-page a custom template used for access forbidden
--tag [--tag option --tag option] a keypair tag which is passed to the templates when render, i.e. title='My Page',site='my name' etc
......@@ -52,6 +53,7 @@ GLOBAL OPTIONS:
--verbose switch on debug / verbose logging
--help, -h show help
--version, -v print the version
```
#### **Configuration**
......@@ -203,3 +205,8 @@ Or on the command line
--resource "uri=/some_white_listed_url,white-listed=true"
--resource "uri=/" # requires authentication on the rest
```
#### **Mutual TLS**
The proxy support enforcing mutual TLS for the clients by simply adding the --tls-ca-certificate command line option or config file option. All clients connecting must present a ceritificate
which was signed by the CA being used.
......@@ -49,6 +49,21 @@ func (r *Config) isValid() error {
if r.Listen == "" {
return fmt.Errorf("you have not specified the listening interface")
}
if r.TLSCertificate != "" && r.TLSPrivateKey == "" {
return fmt.Errorf("you have not provided a private key")
}
if r.TLSPrivateKey != "" && r.TLSCertificate == "" {
return fmt.Errorf("you have not provided a certificate file")
}
if r.TLSCertificate != "" && !fileExists(r.TLSCertificate) {
return fmt.Errorf("the tls certificate %s does not exist", r.TLSCertificate)
}
if r.TLSPrivateKey != "" && !fileExists(r.TLSPrivateKey) {
return fmt.Errorf("the tls private key %s does not exist", r.TLSPrivateKey)
}
if r.TLSCaCertificate != "" && !fileExists(r.TLSCaCertificate) {
return fmt.Errorf("the tls ca certificate file %s does not exist", r.TLSCaCertificate)
}
// step: if the skip verification is off, we need the below
if !r.SkipTokenVerification {
......@@ -136,6 +151,9 @@ func readOptions(cx *cli.Context, config *Config) (err error) {
if cx.IsSet("tls-private-key") {
config.TLSPrivateKey = cx.String("tls-private-key")
}
if cx.IsSet("tls-ca-certificate") {
config.TLSCaCertificate = cx.String("tls-ca-certificate")
}
if cx.IsSet("signin-page") {
config.SignInPage = cx.String("signin-page")
}
......@@ -261,6 +279,10 @@ func getOptions() []cli.Flag {
Name: "tls-private-key",
Usage: "the path to the private key for TLS support",
},
cli.StringFlag{
Name: "tls-ca-certificate",
Usage: "the path to the ca certificate used for mutual TLS",
},
cli.StringSliceFlag{
Name: "scope",
Usage: "a variable list of scopes requested when authenticating the user",
......
......@@ -22,7 +22,7 @@ import (
const (
prog = "keycloak-proxy"
version = "v0.0.8"
version = "v1.0.0-rc1"
author = "Rohith"
email = "gambol99@gmail.com"
description = "is a proxy using the keycloak service for auth and authorization"
......@@ -97,6 +97,8 @@ type Config struct {
TLSCertificate string `json:"tls_cert" yaml:"tls_cert"`
// TLSPrivateKey is the location of a tls private key
TLSPrivateKey string `json:"tls_private_key" yaml:"tls_private_key"`
// TLSCaCertificate is the CA certificate which the client cert must be signed
TLSCaCertificate string `json:"tls_ca_certificate" yaml:"tls_ca_certificate"`
// Upstream is the upstream endpoint i.e whom were proxying to
Upstream string `json:"upstream" yaml:"upstream"`
// TagData is passed to the templates
......
......@@ -485,7 +485,6 @@ func (r *KeycloakProxy) oauthCallbackHandler(cx *gin.Context) {
log.WithFields(log.Fields{
"email": identity.Email,
"username": identity.Name,
"expires": identity.ExpiresAt,
}).Infof("issuing a user session")
......
......@@ -22,9 +22,12 @@ import (
"os"
"sync"
"crypto/tls"
"crypto/x509"
log "github.com/Sirupsen/logrus"
"github.com/gambol99/go-oidc/oidc"
"github.com/gin-gonic/gin"
"io/ioutil"
)
// KeycloakProxy is the server component
......@@ -144,6 +147,21 @@ func (r *KeycloakProxy) initializeTemplates() {
// Run starts the proxy service
func (r *KeycloakProxy) Run() error {
tlsConfig := &tls.Config{}
// step: are we doing mutual tls?
if r.config.TLSCaCertificate != "" {
caCert, err := ioutil.ReadFile(r.config.TLSCaCertificate)
if err != nil {
return err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig.ClientCAs = caCertPool
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
go func() {
log.Infof("keycloak proxy service starting on %s", r.config.Listen)
......@@ -151,7 +169,12 @@ func (r *KeycloakProxy) Run() error {
if r.config.TLSCertificate == "" {
err = r.router.Run(r.config.Listen)
} else {
err = r.router.RunTLS(r.config.Listen, r.config.TLSCertificate, r.config.TLSPrivateKey)
server := &http.Server{
Addr: r.config.Listen,
Handler: r.router,
TLSConfig: tlsConfig,
}
err = server.ListenAndServeTLS(r.config.TLSCertificate, r.config.TLSPrivateKey)
}
if err != nil {
log.WithFields(log.Fields{"error": err.Error()}).Fatalf("failed to start the service")
......
......@@ -26,6 +26,7 @@ import (
"net/http"
"net/http/httputil"
"net/url"
"os"
"regexp"
"strconv"
"strings"
......@@ -204,6 +205,17 @@ func isValidMethod(method string) bool {
return httpMethodRegex.MatchString(method)
}
// fileExists check if a file exists
func fileExists(filename string) bool {
if _, err := os.Stat(filename); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// hasRoles checks the scopes are the same
func hasRoles(required, issued []string) bool {
for _, role := range required {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment