Skip to content
This repository has been archived by the owner on Jan 24, 2019. It is now read-only.

Commit

Permalink
Merge pull request #20 from jehiah/config_support_20
Browse files Browse the repository at this point in the history
add option/flag to specify config file in place of commandline options
  • Loading branch information
jehiah committed Nov 10, 2014
2 parents 899749a + d4fe9a4 commit 01969ee
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 117 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ language: go
install:
- go get github.com/bmizerany/assert
- go get github.com/bitly/go-simplejson
- go get github.com/mreiferson/go-options
- go get github.com/BurntSushi/toml
notifications:
email: false

33 changes: 21 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,40 @@ intend to run `google_auth_proxy` on.
5. Take note of the **Client ID** and **Client Secret**


## Command Line Options
## Configuration

`google_auth_proxy` can be configured via [config file](#config-file), [command line options](#command-line-options) or [environment variables](#environment-variables).

### Config File

An example [google_auth_proxy.cfg](contrib/google_auth_proxy.cfg.example) config file is in the contrib directory. It can be used by specifying `-config=/etc/google_auth_proxy.cfg`

### Command Line Options

```
Usage of ./google_auth_proxy:
Usage of google_auth_proxy:
-authenticated-emails-file="": authenticate against emails via file (one per line)
-client-id="": the Google OAuth Client ID: ie: "123456.apps.googleusercontent.com"
-client-secret="": the OAuth Client Secret
-cookie-domain="": an optional cookie domain to force cookies to
-cookie-expire=168h: expire timeframe for cookie
-config="": path to config file
-cookie-domain="": an optional cookie domain to force cookies to (ie: .yourcompany.com)
-cookie-expire=168h0m0s: expire timeframe for cookie
-cookie-https-only=false: set HTTPS only cookie
-cookie-secret="": the seed string for secure cookies
-google-apps-domain=[]: authenticate against the given google apps domain (may be given multiple times)
-google-apps-domain=: authenticate against the given Google apps domain (may be given multiple times)
-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": <addr>:<port> to listen on for HTTP clients
-pass-basic-auth=true: pass HTTP Basic Auth information to upstream
-pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
-redirect-url="": the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback"
-upstream=[]: the http url(s) of the upstream endpoint. If multiple, routing is based on path
-upstream=: the http url(s) of the upstream endpoint. If multiple, routing is based on path
-version=false: print version string
```

### Environment variables

## Example Configuration
The environment variables `google_auth_client_id`, `google_auth_secret` and `google_auth_cookie_secret` can be used in place of the corresponding command-line arguments.

### Example Nginx Configuration

This example has a [Nginx](http://nginx.org/) SSL endpoint proxying to `google_auth_proxy` on port `4180`.
`google_auth_proxy` then authenticates requests for an upstream application running on port `8080`. The external
Expand Down Expand Up @@ -105,13 +117,10 @@ The command line to run `google_auth_proxy` would look like this:
--client-secret=...
```

## Environment variables

The environment variables `google_auth_client_id`, `google_auth_secret` and `google_auth_cookie_secret` can be used in place of the corresponding command-line arguments.

## Endpoint Documentation

Google Auth Proxy responds directly to the following endpoints. All other endpoints will be authenticated.
Google Auth Proxy responds directly to the following endpoints. All other endpoints will be proxied upstream when authenticated.

* /ping - returns an 200 OK response
* /oauth2/sign_in - the login page, which also doubles as a sign out page (it clears cookies)
Expand Down
44 changes: 44 additions & 0 deletions contrib/google_auth_proxy.cfg.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## Google Auth Proxy Config File
## https://github.com/bitly/google_auth_proxy

## <addr>:<port> to listen on for HTTP clients
# http_address = "127.0.0.1:4180"

## the OAuth Redirect URL.
# redirect_url = "https://internalapp.yourcompany.com/oauth2/callback"

## the http url(s) of the upstream endpoint. If multiple, routing is based on path
# upstreams = [
# "http://127.0.0.1:8080/"
# ]

## pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
# pass_basic_auth = true

## Google Apps Domains to allow authentication for
# google_apps_domains = [
# "yourcompany.com"
# ]


## The Google OAuth Client ID, Secret
# client_id = "123456.apps.googleusercontent.com"
# client_secret = ""

## Authenticated Email Addresses File (one email per line)
# authenticated_emails_file = ""

## Htpasswd File (optional)
## Additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
## enabling exposes a username/login signin form
# htpasswd_file = ""


## Cookie Settings
## Secret - the seed string for secure cookies
## Domain - optional cookie domain to force cookies to (ie: .yourcompany.com)
## Expire - expire timeframe for cookie
# cookie_secret = ""
# cookie_domain = ""
# cookie_expire = "168h"
# cookie_https_only = false
134 changes: 65 additions & 69 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,109 +6,105 @@ import (
"log"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"
)

const VERSION = "0.1.0"

var (
showVersion = flag.Bool("version", false, "print version string")
httpAddr = flag.String("http-address", "127.0.0.1:4180", "<addr>:<port> to listen on for HTTP clients")
redirectUrl = flag.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
clientID = flag.String("client-id", "", "the Google OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
clientSecret = flag.String("client-secret", "", "the OAuth Client Secret")
passBasicAuth = flag.Bool("pass-basic-auth", true, "pass HTTP Basic Auth information to upstream")
htpasswdFile = flag.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption")
cookieSecret = flag.String("cookie-secret", "", "the seed string for secure cookies")
cookieDomain = flag.String("cookie-domain", "", "an optional cookie domain to force cookies to")
cookieExpire = flag.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
cookieHttpsOnly = flag.Bool("cookie-https-only", false, "set HTTPS only cookie")
authenticatedEmailsFile = flag.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
googleAppsDomains = StringArray{}
upstreams = StringArray{}
"github.com/BurntSushi/toml"
"github.com/mreiferson/go-options"
)

func init() {
flag.Var(&googleAppsDomains, "google-apps-domain", "authenticate against the given google apps domain (may be given multiple times)")
flag.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path")
}

func main() {
flagSet := flag.NewFlagSet("google_auth_proxy", flag.ExitOnError)

googleAppsDomains := StringArray{}
upstreams := StringArray{}

config := flagSet.String("config", "", "path to config file")
showVersion := flagSet.Bool("version", false, "print version string")

flagSet.String("http-address", "127.0.0.1:4180", "<addr>:<port> to listen on for HTTP clients")
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")

flagSet.Var(&googleAppsDomains, "google-apps-domain", "authenticate against the given Google apps domain (may be given multiple times)")
flagSet.String("client-id", "", "the Google OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
flagSet.String("client-secret", "", "the OAuth Client Secret")
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption")

flag.Parse()
flagSet.String("cookie-secret", "", "the seed string for secure cookies")
flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)")
flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
flagSet.Bool("cookie-https-only", false, "set HTTPS only cookie")

flagSet.Parse(os.Args[1:])

opts := NewOptions()

var cfg map[string]interface{}
if *config != "" {
_, err := toml.DecodeFile(*config, &cfg)
if err != nil {
log.Fatalf("ERROR: failed to load config file %s - %s", *config, err)
}
}

options.Resolve(opts, flagSet, cfg)

// Try to use env for secrets if no flag is set
if *clientID == "" {
*clientID = os.Getenv("google_auth_client_id")
// TODO: better parsing of these values
if opts.ClientID == "" {
opts.ClientID = os.Getenv("google_auth_client_id")
}
if *clientSecret == "" {
*clientSecret = os.Getenv("google_auth_secret")
if opts.ClientSecret == "" {
opts.ClientSecret = os.Getenv("google_auth_secret")
}
if *cookieSecret == "" {
*cookieSecret = os.Getenv("google_auth_cookie_secret")
if opts.CookieSecret == "" {
opts.CookieSecret = os.Getenv("google_auth_cookie_secret")
}

if *showVersion {
fmt.Printf("google_auth_proxy v%s\n", VERSION)
return
}

if len(upstreams) < 1 {
log.Fatal("missing --upstream")
}
if *cookieSecret == "" {
log.Fatal("missing --cookie-secret")
}
if *clientID == "" {
log.Fatal("missing --client-id")
}
if *clientSecret == "" {
log.Fatal("missing --client-secret")
}

var upstreamUrls []*url.URL
for _, u := range upstreams {
upstreamUrl, err := url.Parse(u)
if err != nil {
log.Fatalf("error parsing --upstream %s", err.Error())
}
upstreamUrls = append(upstreamUrls, upstreamUrl)
}
redirectUrl, err := url.Parse(*redirectUrl)
err := opts.Validate()
if err != nil {
log.Fatalf("error parsing --redirect-url %s", err.Error())
log.Printf("%s", err)
os.Exit(1)
}

validator := NewValidator(googleAppsDomains, *authenticatedEmailsFile)
oauthproxy := NewOauthProxy(upstreamUrls, *clientID, *clientSecret, validator)
oauthproxy.SetRedirectUrl(redirectUrl)
if len(googleAppsDomains) != 0 && *authenticatedEmailsFile == "" {
if len(googleAppsDomains) > 1 {
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(googleAppsDomains, ", "))
validator := NewValidator(opts.GoogleAppsDomains, opts.AuthenticatedEmailsFile)
oauthproxy := NewOauthProxy(opts, validator)

if len(opts.GoogleAppsDomains) != 0 && opts.AuthenticatedEmailsFile == "" {
if len(opts.GoogleAppsDomains) > 1 {
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.GoogleAppsDomains, ", "))
} else {
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", googleAppsDomains[0])
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", opts.GoogleAppsDomains[0])
}
}
if *htpasswdFile != "" {
oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(*htpasswdFile)

if opts.HtpasswdFile != "" {
oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile)
if err != nil {
log.Fatalf("FATAL: unable to open %s %s", *htpasswdFile, err.Error())
log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err)
}
}
listener, err := net.Listen("tcp", *httpAddr)

listener, err := net.Listen("tcp", opts.HttpAddress)
if err != nil {
log.Fatalf("FATAL: listen (%s) failed - %s", *httpAddr, err.Error())
log.Fatalf("FATAL: listen (%s) failed - %s", opts.HttpAddress, err)
}
log.Printf("listening on %s", *httpAddr)
log.Printf("listening on %s", opts.HttpAddress)

server := &http.Server{Handler: oauthproxy}
err = server.Serve(listener)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Printf("ERROR: http.Serve() - %s", err.Error())
log.Printf("ERROR: http.Serve() - %s", err)
}

log.Printf("HTTP: closing %s", listener.Addr().String())
log.Printf("HTTP: closing %s", listener.Addr())
}
Loading

0 comments on commit 01969ee

Please sign in to comment.