support TLS directly
This commit is contained in:
parent
5a5d6dff7e
commit
f5b2b20f67
52
README.md
52
README.md
@ -9,18 +9,18 @@ to validate accounts by email, domain or group.
|
||||
[![Build Status](https://secure.travis-ci.org/bitly/oauth2_proxy.png?branch=master)](http://travis-ci.org/bitly/oauth2_proxy)
|
||||
|
||||
|
||||
![sign_in_page](https://cloud.githubusercontent.com/assets/45028/4970624/7feb7dd8-6886-11e4-93e0-c9904af44ea8.png)
|
||||
![Sign In Page](https://cloud.githubusercontent.com/assets/45028/4970624/7feb7dd8-6886-11e4-93e0-c9904af44ea8.png)
|
||||
|
||||
## Architecture
|
||||
|
||||
![oauth2_proxy_arch](https://cloud.githubusercontent.com/assets/45028/7749664/35fef390-ff9d-11e4-8d51-21a7ba78f857.png)
|
||||
![OAuth2 Proxy Architecture](https://cloud.githubusercontent.com/assets/45028/8027702/bd040b7a-0d6a-11e5-85b9-f8d953d04f39.png)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download [Prebuilt Binary](https://github.com/bitly/oauth2_proxy/releases) (current release is `v1.1.1`) or build with `$ go get github.com/bitly/oauth2_proxy` which will put the binary in `$GOROOT/bin`
|
||||
2. Register an OAuth Application with a Provider
|
||||
3. Configure Oauth2 Proxy using config file, command line options, or environment variables
|
||||
4. Deploy behind a SSL endpoint (example provided for Nginx)
|
||||
2. Select a Provider and Register an OAuth Application with a Provider
|
||||
3. Configure OAuth2 Proxy using config file, command line options, or environment variables
|
||||
4. Configure SSL or Deploy behind a SSL endpoint (example provided for Nginx)
|
||||
|
||||
## OAuth Provider Configuration
|
||||
|
||||
@ -76,6 +76,10 @@ For LinkedIn, the registration steps are:
|
||||
|
||||
The [MyUSA](https://alpha.my.usa.gov) authentication service ([GitHub](https://github.com/18F/myusa))
|
||||
|
||||
## Email Authentication
|
||||
|
||||
To authorize by email domain use `--email-domain=yourcompany.com`. To authorize individual email addresses use `--authenticated-emails-file=/path/to/file` with one email per line. To authorize all email addresse use `--email-domain=*`.
|
||||
|
||||
## Configuration
|
||||
|
||||
`oauth2_proxy` can be configured via [config file](#config-file), [command line options](#command-line-options) or [environment variables](#environment-variables).
|
||||
@ -107,18 +111,21 @@ Usage of oauth2_proxy:
|
||||
-github-team="": restrict logins to members of this team
|
||||
-htpasswd-file="": additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
|
||||
-http-address="127.0.0.1:4180": [http://]<addr>:<port> or unix://<path> to listen on for HTTP clients
|
||||
-https-address=":443": <addr>:<port> to listen on for HTTPS clients
|
||||
-login-url="": Authentication endpoint
|
||||
-pass-access-token=false: pass OAuth access_token to upstream via X-Forwarded-Access-Token header
|
||||
-pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
|
||||
-pass-host-header=true: pass the request Host Header to upstream
|
||||
-profile-url="": Profile access endpoint
|
||||
-provider="": Oauth provider (defaults to Google)
|
||||
-provider="google": OAuth provider
|
||||
-proxy-prefix="/oauth2": the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in)
|
||||
-redeem-url="": Token redemption endpoint
|
||||
-redirect-url="": the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback"
|
||||
-request-logging=true: Log requests to stdout
|
||||
-scope="": Oauth scope specification
|
||||
-skip-auth-regex=: bypass authentication for requests path's that match (may be given multiple times)
|
||||
-tls-cert="": path to certificate file
|
||||
-tls-key="": path to private key file
|
||||
-upstream=: the http url(s) of the upstream endpoint. If multiple, routing is based on path
|
||||
-validate-url="": Access token validation endpoint
|
||||
-version=false: print version string
|
||||
@ -130,10 +137,32 @@ See below for provider specific options
|
||||
|
||||
The environment variables `OAUTH2_PROXY_CLIENT_ID`, `OAUTH2_PROXY_CLIENT_SECRET`, `OAUTH2_PROXY_COOKIE_SECRET`, `OAUTH2_PROXY_COOKIE_DOMAIN` and `OAUTH2_PROXY_COOKIE_EXPIRE` can be used in place of the corresponding command-line arguments.
|
||||
|
||||
### Example Nginx Configuration
|
||||
## SSL Configuration
|
||||
|
||||
This example has a [Nginx](http://nginx.org/) SSL endpoint proxying to `oauth2_proxy` on port `4180`.
|
||||
`oauth2_proxy` then authenticates requests for an upstream application running on port `8080`. The external
|
||||
There are two recommended configurations.
|
||||
|
||||
1) Configure SSL Terminiation with OAuth2 Proxy by providing a `--tls-cert=/path/to/cert.pem` and `--tls-key=/path/to/cert.key`.
|
||||
|
||||
The command line to run `oauth2_proxy` in this configuration would look like this:
|
||||
|
||||
```bash
|
||||
./oauth2_proxy \
|
||||
--email-domain="yourcompany.com" \
|
||||
--upstream=http://127.0.0.1:8080/ \
|
||||
--tls-cert=/path/to/cert.pem \
|
||||
--tls-key=/path/to/cert.key \
|
||||
--cookie-secret=... \
|
||||
--cookie-secure=true \
|
||||
--provider=... \
|
||||
--client-id=... \
|
||||
--client-secret=...
|
||||
```
|
||||
|
||||
|
||||
2) Configure SSL Termination with [Nginx](http://nginx.org/) (example config below) or Amazon ELB, or ....
|
||||
|
||||
Nginx will listen on port `443` and handle SSL connections while proxying to `oauth2_proxy` on port `4180`.
|
||||
`oauth2_proxy` which will then authenticate requests for an upstream application. The external
|
||||
endpoint for this example would be `https://internal.yourcompany.com/`.
|
||||
|
||||
An example Nginx config follows. Note the use of `Strict-Transport-Security` header to pin requests to SSL
|
||||
@ -159,7 +188,7 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
The command line to run `oauth2_proxy` would look like this:
|
||||
The command line to run `oauth2_proxy` in this configuration would look like this:
|
||||
|
||||
```bash
|
||||
./oauth2_proxy \
|
||||
@ -167,6 +196,7 @@ The command line to run `oauth2_proxy` would look like this:
|
||||
--upstream=http://127.0.0.1:8080/ \
|
||||
--cookie-secret=... \
|
||||
--cookie-secure=true \
|
||||
--provider=... \
|
||||
--client-id=... \
|
||||
--client-secret=...
|
||||
```
|
||||
@ -174,7 +204,7 @@ The command line to run `oauth2_proxy` would look like this:
|
||||
|
||||
## Endpoint Documentation
|
||||
|
||||
OAuth2 Proxy responds directly to the following endpoints. All other endpoints will be proxied upstream when authenticated.
|
||||
OAuth2 Proxy responds directly to the following endpoints. All other endpoints will be proxied upstream when authenticated. The `/oauth2` prefix can be changed with the `--proxy-prefix` config variable.
|
||||
|
||||
* /robots.txt - returns a 200 OK response that disallows all User-agents from all paths; see [robotstxt.org](http://www.robotstxt.org/) for more info
|
||||
* /ping - returns an 200 OK response
|
||||
|
@ -1,8 +1,13 @@
|
||||
## OAuth2 Proxy Config File
|
||||
## https://github.com/bitly/oauth2_proxy
|
||||
|
||||
## <addr>:<port> to listen on for HTTP clients
|
||||
## <addr>:<port> to listen on for HTTP/HTTPS clients
|
||||
# http_address = "127.0.0.1:4180"
|
||||
# https_address = ":443"
|
||||
|
||||
## TLS Settings
|
||||
# tls_cert_file = ""
|
||||
# tls_key_file = ""
|
||||
|
||||
## the OAuth Redirect URL.
|
||||
# defaults to the "https://" + requested host header + "/oauth2/callback"
|
||||
|
106
http.go
Normal file
106
http.go
Normal file
@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Handler http.Handler
|
||||
Opts *Options
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe() {
|
||||
if s.Opts.TLSKeyFile != "" || s.Opts.TLSCertFile != "" {
|
||||
s.ServeHTTPS()
|
||||
} else {
|
||||
s.ServeHTTP()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP() {
|
||||
u, err := url.Parse(s.Opts.HttpAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: could not parse %#v: %v", s.Opts.HttpAddress, err)
|
||||
}
|
||||
|
||||
var networkType string
|
||||
switch u.Scheme {
|
||||
case "", "http":
|
||||
networkType = "tcp"
|
||||
default:
|
||||
networkType = u.Scheme
|
||||
}
|
||||
listenAddr := strings.TrimPrefix(u.String(), u.Scheme+"://")
|
||||
|
||||
listener, err := net.Listen(networkType, listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: listen (%s, %s) failed - %s", networkType, listenAddr, err)
|
||||
}
|
||||
log.Printf("HTTP: listening on %s", listenAddr)
|
||||
|
||||
server := &http.Server{Handler: s.Handler}
|
||||
err = server.Serve(listener)
|
||||
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
log.Printf("ERROR: http.Serve() - %s", err)
|
||||
}
|
||||
|
||||
log.Printf("HTTP: closing %s", listener.Addr())
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTPS() {
|
||||
addr := s.Opts.HttpsAddress
|
||||
config := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
MaxVersion: tls.VersionTLS12,
|
||||
}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
var err error
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(s.Opts.TLSCertFile, s.Opts.TLSKeyFile)
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: loading tls config (%s, %s) failed - %s", s.Opts.TLSCertFile, s.Opts.TLSKeyFile, err)
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: listen (%s) failed - %s", addr, err)
|
||||
}
|
||||
log.Printf("HTTPS: listening on %s", ln.Addr())
|
||||
|
||||
tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
|
||||
srv := &http.Server{Handler: s.Handler}
|
||||
err = srv.Serve(tlsListener)
|
||||
|
||||
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
log.Printf("ERROR: https.Serve() - %s", err)
|
||||
}
|
||||
|
||||
log.Printf("HTTPS: closing %s", tlsListener.Addr())
|
||||
}
|
||||
|
||||
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
|
||||
// connections. It's used by ListenAndServe and ListenAndServeTLS so
|
||||
// dead TCP connections (e.g. closing laptop mid-download) eventually
|
||||
// go away.
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
return tc, nil
|
||||
}
|
38
main.go
38
main.go
@ -4,9 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -28,6 +25,9 @@ func main() {
|
||||
showVersion := flagSet.Bool("version", false, "print version string")
|
||||
|
||||
flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> to listen on for HTTP clients")
|
||||
flagSet.String("https-address", ":443", "<addr>:<port> to listen on for HTTPS clients")
|
||||
flagSet.String("tls-cert", "", "path to certificate file")
|
||||
flagSet.String("tls-key", "", "path to private key file")
|
||||
flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
|
||||
flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path")
|
||||
flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
|
||||
@ -57,7 +57,7 @@ func main() {
|
||||
|
||||
flagSet.Bool("request-logging", true, "Log requests to stdout")
|
||||
|
||||
flagSet.String("provider", "", "Oauth provider (defaults to Google)")
|
||||
flagSet.String("provider", "google", "OAuth provider")
|
||||
flagSet.String("login-url", "", "Authentication endpoint")
|
||||
flagSet.String("redeem-url", "", "Token redemption endpoint")
|
||||
flagSet.String("profile-url", "", "Profile access endpoint")
|
||||
@ -109,31 +109,9 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
u, err := url.Parse(opts.HttpAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: could not parse %#v: %v", opts.HttpAddress, err)
|
||||
s := &Server{
|
||||
Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging),
|
||||
Opts: opts,
|
||||
}
|
||||
|
||||
var networkType string
|
||||
switch u.Scheme {
|
||||
case "", "http":
|
||||
networkType = "tcp"
|
||||
default:
|
||||
networkType = u.Scheme
|
||||
}
|
||||
listenAddr := strings.TrimPrefix(u.String(), u.Scheme+"://")
|
||||
|
||||
listener, err := net.Listen(networkType, listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: listen (%s, %s) failed - %s", networkType, listenAddr, err)
|
||||
}
|
||||
log.Printf("listening on %s", listenAddr)
|
||||
|
||||
server := &http.Server{Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging)}
|
||||
err = server.Serve(listener)
|
||||
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
log.Printf("ERROR: http.Serve() - %s", err)
|
||||
}
|
||||
|
||||
log.Printf("HTTP: closing %s", listener.Addr())
|
||||
s.ListenAndServe()
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy {
|
||||
redirectUrl := opts.redirectUrl
|
||||
redirectUrl.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix)
|
||||
|
||||
log.Printf("OauthProxy configured for %s", opts.ClientID)
|
||||
log.Printf("OauthProxy configured for %s Client ID: %s", opts.provider.Data().ProviderName, opts.ClientID)
|
||||
domain := opts.CookieDomain
|
||||
if domain == "" {
|
||||
domain = "<default>"
|
||||
@ -114,7 +114,7 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy {
|
||||
opts.CookieSecure = opts.CookieHttpsOnly
|
||||
}
|
||||
|
||||
log.Printf("Cookie settings: secure (https):%v httponly:%v expiry:%s domain:%s", opts.CookieSecure, opts.CookieHttpOnly, opts.CookieExpire, domain)
|
||||
log.Printf("Cookie settings: name:%s secure (https):%v httponly:%v expiry:%s domain:%s", opts.CookieKey, opts.CookieSecure, opts.CookieHttpOnly, opts.CookieExpire, domain)
|
||||
|
||||
var aes_cipher cipher.Block
|
||||
if opts.PassAccessToken || (opts.CookieRefresh != time.Duration(0)) {
|
||||
|
@ -14,9 +14,12 @@ import (
|
||||
type Options struct {
|
||||
ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy-prefix"`
|
||||
HttpAddress string `flag:"http-address" cfg:"http_address"`
|
||||
HttpsAddress string `flag:"https-address" cfg:"https_address"`
|
||||
RedirectUrl string `flag:"redirect-url" cfg:"redirect_url"`
|
||||
ClientID string `flag:"client-id" cfg:"client_id" env:"OAUTH2_PROXY_CLIENT_ID"`
|
||||
ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"OAUTH2_PROXY_CLIENT_SECRET"`
|
||||
TLSCertFile string `flag:"tls-cert" cfg:"tls_cert_file"`
|
||||
TLSKeyFile string `flag:"tls-key" cfg:"tls_key_file"`
|
||||
|
||||
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
||||
EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
|
||||
@ -63,6 +66,7 @@ func NewOptions() *Options {
|
||||
return &Options{
|
||||
ProxyPrefix: "/oauth2",
|
||||
HttpAddress: "127.0.0.1:4180",
|
||||
HttpsAddress: ":443",
|
||||
DisplayHtpasswdForm: true,
|
||||
CookieKey: "_oauthproxy",
|
||||
CookieHttpsOnly: true,
|
||||
|
Loading…
Reference in New Issue
Block a user