Merge pull request #20 from jehiah/config_support_20
add option/flag to specify config file in place of commandline options
This commit is contained in:
commit
01969eebdc
@ -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
README.md
33
README.md
@ -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
|
||||
@ -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)
|
||||
|
44
contrib/google_auth_proxy.cfg.example
Normal file
44
contrib/google_auth_proxy.cfg.example
Normal file
@ -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
|
136
main.go
136
main.go
@ -6,49 +6,63 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/mreiferson/go-options"
|
||||
)
|
||||
|
||||
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{}
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
flag.Parse()
|
||||
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")
|
||||
|
||||
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 {
|
||||
@ -56,59 +70,41 @@ func main() {
|
||||
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())
|
||||
}
|
||||
|
@ -22,9 +22,12 @@ const oauthStartPath = "/oauth2/start"
|
||||
const oauthCallbackPath = "/oauth2/callback"
|
||||
|
||||
type OauthProxy struct {
|
||||
CookieSeed string
|
||||
CookieKey string
|
||||
Validator func(string) bool
|
||||
CookieSeed string
|
||||
CookieKey string
|
||||
CookieDomain string
|
||||
CookieHttpsOnly bool
|
||||
CookieExpire time.Duration
|
||||
Validator func(string) bool
|
||||
|
||||
redirectUrl *url.URL // the url to receive requests at
|
||||
oauthRedemptionUrl *url.URL // endpoint to redeem the code
|
||||
@ -35,40 +38,41 @@ type OauthProxy struct {
|
||||
SignInMessage string
|
||||
HtpasswdFile *HtpasswdFile
|
||||
serveMux *http.ServeMux
|
||||
PassBasicAuth bool
|
||||
}
|
||||
|
||||
func NewOauthProxy(proxyUrls []*url.URL, clientID string, clientSecret string, validator func(string) bool) *OauthProxy {
|
||||
func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy {
|
||||
login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth")
|
||||
redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token")
|
||||
serveMux := http.NewServeMux()
|
||||
for _, u := range proxyUrls {
|
||||
for _, u := range opts.proxyUrls {
|
||||
path := u.Path
|
||||
if len(path) == 0 {
|
||||
path = "/"
|
||||
}
|
||||
u.Path = ""
|
||||
log.Printf("mapping %s => %s", path, u)
|
||||
log.Printf("mapping path %q => upstream %q", path, u)
|
||||
serveMux.Handle(path, httputil.NewSingleHostReverseProxy(u))
|
||||
}
|
||||
return &OauthProxy{
|
||||
CookieKey: "_oauthproxy",
|
||||
CookieSeed: *cookieSecret,
|
||||
Validator: validator,
|
||||
redirectUrl := opts.redirectUrl
|
||||
redirectUrl.Path = oauthCallbackPath
|
||||
|
||||
clientID: clientID,
|
||||
clientSecret: clientSecret,
|
||||
return &OauthProxy{
|
||||
CookieKey: "_oauthproxy",
|
||||
CookieSeed: opts.CookieSecret,
|
||||
CookieDomain: opts.CookieDomain,
|
||||
CookieHttpsOnly: opts.CookieHttpsOnly,
|
||||
CookieExpire: opts.CookieExpire,
|
||||
Validator: validator,
|
||||
|
||||
clientID: opts.ClientID,
|
||||
clientSecret: opts.ClientSecret,
|
||||
oauthScope: "profile email",
|
||||
oauthRedemptionUrl: redeem,
|
||||
oauthLoginUrl: login,
|
||||
serveMux: serveMux,
|
||||
redirectUrl: redirectUrl,
|
||||
PassBasicAuth: opts.PassBasicAuth,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *OauthProxy) SetRedirectUrl(redirectUrl *url.URL) {
|
||||
redirectUrl.Path = oauthCallbackPath
|
||||
p.redirectUrl = redirectUrl
|
||||
}
|
||||
|
||||
func (p *OauthProxy) GetLoginURL(redirectUrl string) string {
|
||||
params := url.Values{}
|
||||
params.Add("redirect_uri", p.redirectUrl.String())
|
||||
@ -164,8 +168,8 @@ func jwtDecodeSegment(seg string) ([]byte, error) {
|
||||
|
||||
func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
|
||||
domain := strings.Split(req.Host, ":")[0]
|
||||
if *cookieDomain != "" && strings.HasSuffix(domain, *cookieDomain) {
|
||||
domain = *cookieDomain
|
||||
if p.CookieDomain != "" && strings.HasSuffix(domain, p.CookieDomain) {
|
||||
domain = p.CookieDomain
|
||||
}
|
||||
cookie := &http.Cookie{
|
||||
Name: p.CookieKey,
|
||||
@ -181,8 +185,8 @@ func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
|
||||
func (p *OauthProxy) SetCookie(rw http.ResponseWriter, req *http.Request, val string) {
|
||||
|
||||
domain := strings.Split(req.Host, ":")[0] // strip the port (if any)
|
||||
if *cookieDomain != "" && strings.HasSuffix(domain, *cookieDomain) {
|
||||
domain = *cookieDomain
|
||||
if p.CookieDomain != "" && strings.HasSuffix(domain, p.CookieDomain) {
|
||||
domain = p.CookieDomain
|
||||
}
|
||||
cookie := &http.Cookie{
|
||||
Name: p.CookieKey,
|
||||
@ -190,8 +194,8 @@ func (p *OauthProxy) SetCookie(rw http.ResponseWriter, req *http.Request, val st
|
||||
Path: "/",
|
||||
Domain: domain,
|
||||
HttpOnly: true,
|
||||
Secure: *cookieHttpsOnly,
|
||||
Expires: time.Now().Add(*cookieExpire),
|
||||
Secure: p.CookieHttpsOnly,
|
||||
Expires: time.Now().Add(p.CookieExpire),
|
||||
}
|
||||
http.SetCookie(rw, cookie)
|
||||
}
|
||||
@ -267,11 +271,11 @@ func (p *OauthProxy) GetRedirect(req *http.Request) (string, error) {
|
||||
|
||||
func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
// check if this is a redirect back at the end of oauth
|
||||
remoteIP := req.Header.Get("X-Real-IP")
|
||||
if remoteIP == "" {
|
||||
remoteIP = req.RemoteAddr
|
||||
remoteAddr := req.RemoteAddr
|
||||
if req.Header.Get("X-Real-IP") != "" {
|
||||
remoteAddr += fmt.Sprintf(" (%q)", req.Header.Get("X-Real-IP"))
|
||||
}
|
||||
log.Printf("%s %s %s", remoteIP, req.Method, req.URL.Path)
|
||||
log.Printf("%s %s %s", remoteAddr, req.Method, req.URL.RequestURI())
|
||||
|
||||
var ok bool
|
||||
var user string
|
||||
@ -322,7 +326,7 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
_, email, err := p.redeemCode(req.Form.Get("code"))
|
||||
if err != nil {
|
||||
log.Printf("error redeeming code %s", err)
|
||||
log.Printf("%s error redeeming code %s", remoteAddr, err)
|
||||
p.ErrorPage(rw, 500, "Internal Error", err.Error())
|
||||
return
|
||||
}
|
||||
@ -334,7 +338,7 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// set cookie, or deny
|
||||
if p.Validator(email) {
|
||||
log.Printf("authenticating %s completed", email)
|
||||
log.Printf("%s authenticating %s completed", remoteAddr, email)
|
||||
p.SetCookie(rw, req, email)
|
||||
http.Redirect(rw, req, redirect, 302)
|
||||
return
|
||||
@ -362,13 +366,13 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
if !ok {
|
||||
log.Printf("invalid cookie")
|
||||
log.Printf("%s - invalid cookie session", remoteAddr)
|
||||
p.SignInPage(rw, req, 403)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, the user is authenticated. proxy normally
|
||||
if *passBasicAuth {
|
||||
if p.PassBasicAuth {
|
||||
req.SetBasicAuth(user, "")
|
||||
req.Header["X-Forwarded-User"] = []string{user}
|
||||
req.Header["X-Forwarded-Email"] = []string{email}
|
||||
|
67
options.go
Normal file
67
options.go
Normal file
@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Configuration Options that can be set by Command Line Flag, or Config File
|
||||
type Options struct {
|
||||
HttpAddress string `flag:"http-address" cfg:"http_address"`
|
||||
RedirectUrl string `flag:"redirect-url" cfg:"redirect_url"`
|
||||
ClientID string `flag:"client-id" cfg:"client_id"`
|
||||
ClientSecret string `flag:"client-secret" cfg:"client_secret"`
|
||||
PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
|
||||
HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"`
|
||||
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret"`
|
||||
CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain"`
|
||||
CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire"`
|
||||
CookieHttpsOnly bool `flag:"cookie-https-only" cfg:"cookie_https_only"`
|
||||
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
||||
GoogleAppsDomains []string `flag:"google-apps-domain" cfg:"google_apps_domains"`
|
||||
Upstreams []string `flag:"upstream" cfg:"upstreams"`
|
||||
|
||||
// internal values that are set after config validation
|
||||
redirectUrl *url.URL
|
||||
proxyUrls []*url.URL
|
||||
}
|
||||
|
||||
func NewOptions() *Options {
|
||||
return &Options{}
|
||||
}
|
||||
|
||||
func (o *Options) Validate() error {
|
||||
if len(o.Upstreams) < 1 {
|
||||
return errors.New("missing -upstream")
|
||||
}
|
||||
if o.CookieSecret == "" {
|
||||
errors.New("missing -cookie-secret")
|
||||
}
|
||||
if o.ClientID == "" {
|
||||
return errors.New("missing -client-id")
|
||||
}
|
||||
if o.ClientSecret == "" {
|
||||
return errors.New("missing -client-secret")
|
||||
}
|
||||
|
||||
redirectUrl, err := url.Parse(o.RedirectUrl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing -redirect-url=%q %s", o.RedirectUrl, err)
|
||||
}
|
||||
o.redirectUrl = redirectUrl
|
||||
|
||||
for _, u := range o.Upstreams {
|
||||
upstreamUrl, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing -upstream=%q %s", upstreamUrl, err)
|
||||
}
|
||||
if upstreamUrl.Path == "" {
|
||||
upstreamUrl.Path = "/"
|
||||
}
|
||||
o.proxyUrls = append(o.proxyUrls, upstreamUrl)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StringArray []string
|
||||
@ -12,5 +12,5 @@ func (a *StringArray) Set(s string) error {
|
||||
}
|
||||
|
||||
func (a *StringArray) String() string {
|
||||
return fmt.Sprint(*a)
|
||||
return strings.Join(*a, ",")
|
||||
}
|
||||
|
3
version.go
Normal file
3
version.go
Normal file
@ -0,0 +1,3 @@
|
||||
package main
|
||||
|
||||
const VERSION = "0.1.0"
|
Loading…
Reference in New Issue
Block a user