Skip to content

Commit d4fe9a4

Browse files
committed
Add config file support
1 parent 899749a commit d4fe9a4

8 files changed

+242
-117
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ language: go
22
install:
33
- go get github.com/bmizerany/assert
44
- go get github.com/bitly/go-simplejson
5+
- go get github.com/mreiferson/go-options
6+
- go get github.com/BurntSushi/toml
57
notifications:
68
email: false
79

README.md

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,28 +42,40 @@ intend to run `google_auth_proxy` on.
4242
5. Take note of the **Client ID** and **Client Secret**
4343

4444

45-
## Command Line Options
45+
## Configuration
46+
47+
`google_auth_proxy` can be configured via [config file](#config-file), [command line options](#command-line-options) or [environment variables](#environment-variables).
48+
49+
### Config File
50+
51+
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`
52+
53+
### Command Line Options
4654

4755
```
48-
Usage of ./google_auth_proxy:
56+
Usage of google_auth_proxy:
4957
-authenticated-emails-file="": authenticate against emails via file (one per line)
5058
-client-id="": the Google OAuth Client ID: ie: "123456.apps.googleusercontent.com"
5159
-client-secret="": the OAuth Client Secret
52-
-cookie-domain="": an optional cookie domain to force cookies to
53-
-cookie-expire=168h: expire timeframe for cookie
60+
-config="": path to config file
61+
-cookie-domain="": an optional cookie domain to force cookies to (ie: .yourcompany.com)
62+
-cookie-expire=168h0m0s: expire timeframe for cookie
5463
-cookie-https-only=false: set HTTPS only cookie
5564
-cookie-secret="": the seed string for secure cookies
56-
-google-apps-domain=[]: authenticate against the given google apps domain (may be given multiple times)
65+
-google-apps-domain=: authenticate against the given Google apps domain (may be given multiple times)
5766
-htpasswd-file="": additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
5867
-http-address="127.0.0.1:4180": <addr>:<port> to listen on for HTTP clients
59-
-pass-basic-auth=true: pass HTTP Basic Auth information to upstream
68+
-pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
6069
-redirect-url="": the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback"
61-
-upstream=[]: the http url(s) of the upstream endpoint. If multiple, routing is based on path
70+
-upstream=: the http url(s) of the upstream endpoint. If multiple, routing is based on path
6271
-version=false: print version string
6372
```
6473

74+
### Environment variables
6575

66-
## Example Configuration
76+
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.
77+
78+
### Example Nginx Configuration
6779

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

108-
## Environment variables
109-
110-
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.
111120

112121
## Endpoint Documentation
113122

114-
Google Auth Proxy responds directly to the following endpoints. All other endpoints will be authenticated.
123+
Google Auth Proxy responds directly to the following endpoints. All other endpoints will be proxied upstream when authenticated.
115124

116125
* /ping - returns an 200 OK response
117126
* /oauth2/sign_in - the login page, which also doubles as a sign out page (it clears cookies)

contrib/google_auth_proxy.cfg.example

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## Google Auth Proxy Config File
2+
## https://github.com/bitly/google_auth_proxy
3+
4+
## <addr>:<port> to listen on for HTTP clients
5+
# http_address = "127.0.0.1:4180"
6+
7+
## the OAuth Redirect URL.
8+
# redirect_url = "https://internalapp.yourcompany.com/oauth2/callback"
9+
10+
## the http url(s) of the upstream endpoint. If multiple, routing is based on path
11+
# upstreams = [
12+
# "http://127.0.0.1:8080/"
13+
# ]
14+
15+
## pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
16+
# pass_basic_auth = true
17+
18+
## Google Apps Domains to allow authentication for
19+
# google_apps_domains = [
20+
# "yourcompany.com"
21+
# ]
22+
23+
24+
## The Google OAuth Client ID, Secret
25+
# client_id = "123456.apps.googleusercontent.com"
26+
# client_secret = ""
27+
28+
## Authenticated Email Addresses File (one email per line)
29+
# authenticated_emails_file = ""
30+
31+
## Htpasswd File (optional)
32+
## Additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
33+
## enabling exposes a username/login signin form
34+
# htpasswd_file = ""
35+
36+
37+
## Cookie Settings
38+
## Secret - the seed string for secure cookies
39+
## Domain - optional cookie domain to force cookies to (ie: .yourcompany.com)
40+
## Expire - expire timeframe for cookie
41+
# cookie_secret = ""
42+
# cookie_domain = ""
43+
# cookie_expire = "168h"
44+
# cookie_https_only = false

main.go

Lines changed: 65 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,109 +6,105 @@ import (
66
"log"
77
"net"
88
"net/http"
9-
"net/url"
109
"os"
1110
"strings"
1211
"time"
13-
)
1412

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

34-
func init() {
35-
flag.Var(&googleAppsDomains, "google-apps-domain", "authenticate against the given google apps domain (may be given multiple times)")
36-
flag.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path")
37-
}
38-
3917
func main() {
18+
flagSet := flag.NewFlagSet("google_auth_proxy", flag.ExitOnError)
19+
20+
googleAppsDomains := StringArray{}
21+
upstreams := StringArray{}
22+
23+
config := flagSet.String("config", "", "path to config file")
24+
showVersion := flagSet.Bool("version", false, "print version string")
25+
26+
flagSet.String("http-address", "127.0.0.1:4180", "<addr>:<port> to listen on for HTTP clients")
27+
flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
28+
flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path")
29+
flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
30+
31+
flagSet.Var(&googleAppsDomains, "google-apps-domain", "authenticate against the given Google apps domain (may be given multiple times)")
32+
flagSet.String("client-id", "", "the Google OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
33+
flagSet.String("client-secret", "", "the OAuth Client Secret")
34+
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
35+
flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption")
4036

41-
flag.Parse()
37+
flagSet.String("cookie-secret", "", "the seed string for secure cookies")
38+
flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)")
39+
flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
40+
flagSet.Bool("cookie-https-only", false, "set HTTPS only cookie")
41+
42+
flagSet.Parse(os.Args[1:])
43+
44+
opts := NewOptions()
45+
46+
var cfg map[string]interface{}
47+
if *config != "" {
48+
_, err := toml.DecodeFile(*config, &cfg)
49+
if err != nil {
50+
log.Fatalf("ERROR: failed to load config file %s - %s", *config, err)
51+
}
52+
}
53+
54+
options.Resolve(opts, flagSet, cfg)
4255

4356
// Try to use env for secrets if no flag is set
44-
if *clientID == "" {
45-
*clientID = os.Getenv("google_auth_client_id")
57+
// TODO: better parsing of these values
58+
if opts.ClientID == "" {
59+
opts.ClientID = os.Getenv("google_auth_client_id")
4660
}
47-
if *clientSecret == "" {
48-
*clientSecret = os.Getenv("google_auth_secret")
61+
if opts.ClientSecret == "" {
62+
opts.ClientSecret = os.Getenv("google_auth_secret")
4963
}
50-
if *cookieSecret == "" {
51-
*cookieSecret = os.Getenv("google_auth_cookie_secret")
64+
if opts.CookieSecret == "" {
65+
opts.CookieSecret = os.Getenv("google_auth_cookie_secret")
5266
}
5367

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

59-
if len(upstreams) < 1 {
60-
log.Fatal("missing --upstream")
61-
}
62-
if *cookieSecret == "" {
63-
log.Fatal("missing --cookie-secret")
64-
}
65-
if *clientID == "" {
66-
log.Fatal("missing --client-id")
67-
}
68-
if *clientSecret == "" {
69-
log.Fatal("missing --client-secret")
70-
}
71-
72-
var upstreamUrls []*url.URL
73-
for _, u := range upstreams {
74-
upstreamUrl, err := url.Parse(u)
75-
if err != nil {
76-
log.Fatalf("error parsing --upstream %s", err.Error())
77-
}
78-
upstreamUrls = append(upstreamUrls, upstreamUrl)
79-
}
80-
redirectUrl, err := url.Parse(*redirectUrl)
73+
err := opts.Validate()
8174
if err != nil {
82-
log.Fatalf("error parsing --redirect-url %s", err.Error())
75+
log.Printf("%s", err)
76+
os.Exit(1)
8377
}
8478

85-
validator := NewValidator(googleAppsDomains, *authenticatedEmailsFile)
86-
oauthproxy := NewOauthProxy(upstreamUrls, *clientID, *clientSecret, validator)
87-
oauthproxy.SetRedirectUrl(redirectUrl)
88-
if len(googleAppsDomains) != 0 && *authenticatedEmailsFile == "" {
89-
if len(googleAppsDomains) > 1 {
90-
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(googleAppsDomains, ", "))
79+
validator := NewValidator(opts.GoogleAppsDomains, opts.AuthenticatedEmailsFile)
80+
oauthproxy := NewOauthProxy(opts, validator)
81+
82+
if len(opts.GoogleAppsDomains) != 0 && opts.AuthenticatedEmailsFile == "" {
83+
if len(opts.GoogleAppsDomains) > 1 {
84+
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.GoogleAppsDomains, ", "))
9185
} else {
92-
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", googleAppsDomains[0])
86+
oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", opts.GoogleAppsDomains[0])
9387
}
9488
}
95-
if *htpasswdFile != "" {
96-
oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(*htpasswdFile)
89+
90+
if opts.HtpasswdFile != "" {
91+
oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile)
9792
if err != nil {
98-
log.Fatalf("FATAL: unable to open %s %s", *htpasswdFile, err.Error())
93+
log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err)
9994
}
10095
}
101-
listener, err := net.Listen("tcp", *httpAddr)
96+
97+
listener, err := net.Listen("tcp", opts.HttpAddress)
10298
if err != nil {
103-
log.Fatalf("FATAL: listen (%s) failed - %s", *httpAddr, err.Error())
99+
log.Fatalf("FATAL: listen (%s) failed - %s", opts.HttpAddress, err)
104100
}
105-
log.Printf("listening on %s", *httpAddr)
101+
log.Printf("listening on %s", opts.HttpAddress)
106102

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

113-
log.Printf("HTTP: closing %s", listener.Addr().String())
109+
log.Printf("HTTP: closing %s", listener.Addr())
114110
}

0 commit comments

Comments
 (0)