Asim Shankar | 220a015 | 2014-10-30 21:21:09 -0700 | [diff] [blame] | 1 | // HTTP server that uses OAuth to create security.Blessings objects. |
Suharsh Sivakumar | 91bc52e | 2015-02-06 10:59:50 -0800 | [diff] [blame] | 2 | // For more information on our setup of the identity server see: |
| 3 | // https://docs.google.com/document/d/1ebQ1sQn95cFu8yQM36rpJ8mQvsU29aa1o03ADhi52BM |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 4 | package main |
| 5 | |
| 6 | import ( |
Suharsh Sivakumar | 38a1a7b | 2014-11-17 15:14:38 -0800 | [diff] [blame] | 7 | "database/sql" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 8 | "flag" |
| 9 | "fmt" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 10 | "os" |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 11 | "time" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 12 | |
Suharsh Sivakumar | 19fbf99 | 2015-01-23 11:02:27 -0800 | [diff] [blame] | 13 | "v.io/core/veyron2" |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 14 | "v.io/core/veyron2/vlog" |
Cosmos Nicolaou | d6c3c9c | 2014-09-30 15:42:53 -0700 | [diff] [blame] | 15 | |
Suharsh Sivakumar | 19fbf99 | 2015-01-23 11:02:27 -0800 | [diff] [blame] | 16 | _ "v.io/core/veyron/profiles/static" |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 17 | "v.io/core/veyron/services/identity/auditor" |
| 18 | "v.io/core/veyron/services/identity/blesser" |
| 19 | "v.io/core/veyron/services/identity/caveats" |
| 20 | "v.io/core/veyron/services/identity/oauth" |
| 21 | "v.io/core/veyron/services/identity/revocation" |
| 22 | "v.io/core/veyron/services/identity/server" |
Asim Shankar | c195b6d | 2015-02-03 11:26:55 -0800 | [diff] [blame] | 23 | "v.io/core/veyron/services/identity/util" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 24 | ) |
| 25 | |
| 26 | var ( |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 27 | // Configuration for various Google OAuth-based clients. |
Suharsh Sivakumar | d308c7e | 2014-10-03 12:46:50 -0700 | [diff] [blame] | 28 | googleConfigWeb = flag.String("google_config_web", "", "Path to JSON-encoded OAuth client configuration for the web application that renders the audit log for blessings provided by this provider.") |
| 29 | googleConfigChrome = flag.String("google_config_chrome", "", "Path to the JSON-encoded OAuth client configuration for Chrome browser applications that obtain blessings from this server (via the OAuthBlesser.BlessUsingAccessToken RPC) from this server.") |
| 30 | googleConfigAndroid = flag.String("google_config_android", "", "Path to the JSON-encoded OAuth client configuration for Android applications that obtain blessings from this server (via the OAuthBlesser.BlessUsingAccessToken RPC) from this server.") |
Asim Shankar | c195b6d | 2015-02-03 11:26:55 -0800 | [diff] [blame] | 31 | emailClassifier util.EmailClassifier |
Nicolas LaCasse | 1b0c8ba | 2015-01-07 17:49:25 -0800 | [diff] [blame] | 32 | |
| 33 | // Flags controlling the HTTP server |
| 34 | host = flag.String("host", defaultHost(), "Hostname the HTTP server listens on. This can be the name of the host running the webserver, but if running behind a NAT or load balancer, this should be the host name that clients will connect to. For example, if set to 'x.com', Veyron identities will have the IssuerName set to 'x.com' and clients can expect to find the root name and public key of the signer at 'x.com/blessing-root'.") |
| 35 | httpaddr = flag.String("httpaddr", "localhost:8125", "Address on which the HTTP server listens on.") |
Nicolas LaCasse | 098a985 | 2015-01-08 13:41:45 -0800 | [diff] [blame] | 36 | tlsconfig = flag.String("tlsconfig", "", "Comma-separated list of TLS certificate and private key files, in that order. This must be provided.") |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 37 | ) |
| 38 | |
| 39 | func main() { |
Asim Shankar | c195b6d | 2015-02-03 11:26:55 -0800 | [diff] [blame] | 40 | flag.Var(&emailClassifier, "email_classifier", "A comma-separated list of <domain>=<prefix> pairs. For example 'google.com=internal,v.io=trusted'. When specified, then the blessings generated for email address of <domain> will use the extension <prefix>/<email> instead of the default extension of users/<email>.") |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 41 | flag.Usage = usage |
Suharsh Sivakumar | 38a1a7b | 2014-11-17 15:14:38 -0800 | [diff] [blame] | 42 | flag.Parse() |
| 43 | |
| 44 | var sqlDB *sql.DB |
| 45 | var err error |
Suharsh Sivakumar | 91bc52e | 2015-02-06 10:59:50 -0800 | [diff] [blame] | 46 | if len(*sqlConf) > 0 { |
| 47 | if sqlDB, err = dbFromConfigFile(*sqlConf); err != nil { |
Suharsh Sivakumar | 38a1a7b | 2014-11-17 15:14:38 -0800 | [diff] [blame] | 48 | vlog.Fatalf("failed to create sqlDB: %v", err) |
| 49 | } |
| 50 | } |
| 51 | |
Asim Shankar | c195b6d | 2015-02-03 11:26:55 -0800 | [diff] [blame] | 52 | googleoauth, err := oauth.NewGoogleOAuth(*googleConfigWeb) |
Matt Rosencrantz | 110cf22 | 2014-12-01 16:08:54 -0800 | [diff] [blame] | 53 | if err != nil { |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 54 | vlog.Fatalf("Failed to setup GoogleOAuth: %v", err) |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 55 | } |
| 56 | |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 57 | auditor, reader, err := auditor.NewSQLBlessingAuditor(sqlDB) |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 58 | if err != nil { |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 59 | vlog.Fatalf("Failed to create sql auditor from config: %v", err) |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 60 | } |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 61 | |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 62 | revocationManager, err := revocation.NewRevocationManager(sqlDB) |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 63 | if err != nil { |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 64 | vlog.Fatalf("Failed to start RevocationManager: %v", err) |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 65 | } |
Suharsh Sivakumar | d308c7e | 2014-10-03 12:46:50 -0700 | [diff] [blame] | 66 | |
Suharsh Sivakumar | 19fbf99 | 2015-01-23 11:02:27 -0800 | [diff] [blame] | 67 | ctx, shutdown := veyron2.Init() |
| 68 | defer shutdown() |
Matt Rosencrantz | f1c3b44 | 2015-01-12 17:53:08 -0800 | [diff] [blame] | 69 | |
Suharsh Sivakumar | 19fbf99 | 2015-01-23 11:02:27 -0800 | [diff] [blame] | 70 | listenSpec := veyron2.GetListenSpec(ctx) |
Nicolas LaCasse | 1b0c8ba | 2015-01-07 17:49:25 -0800 | [diff] [blame] | 71 | s := server.NewIdentityServer( |
Suharsh Sivakumar | a76dba6 | 2014-12-22 16:00:34 -0800 | [diff] [blame] | 72 | googleoauth, |
| 73 | auditor, |
| 74 | reader, |
| 75 | revocationManager, |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 76 | googleOAuthBlesserParams(googleoauth, revocationManager), |
Asim Shankar | c195b6d | 2015-02-03 11:26:55 -0800 | [diff] [blame] | 77 | caveats.NewBrowserCaveatSelector(), |
| 78 | &emailClassifier) |
Suharsh Sivakumar | 19fbf99 | 2015-01-23 11:02:27 -0800 | [diff] [blame] | 79 | s.Serve(ctx, &listenSpec, *host, *httpaddr, *tlsconfig) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | func usage() { |
Asim Shankar | b18a44f | 2014-10-21 20:25:07 -0700 | [diff] [blame] | 83 | fmt.Fprintf(os.Stderr, `%s starts an HTTP server that brokers blessings after authenticating through OAuth. |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 84 | |
| 85 | To generate TLS certificates so the HTTP server can use SSL: |
Nicolas LaCasse | 1b0c8ba | 2015-01-07 17:49:25 -0800 | [diff] [blame] | 86 | go run $(go list -f {{.Dir}} "crypto/tls")/generate_cert.go --host <IP address> |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 87 | |
Asim Shankar | b18a44f | 2014-10-21 20:25:07 -0700 | [diff] [blame] | 88 | To use Google as an OAuth provider the --google_config_* flags must be set to point to |
| 89 | the a JSON file obtained after registering the application with the Google Developer Console |
| 90 | at https://cloud.google.com/console |
Asim Shankar | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 91 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 92 | More details on Google OAuth at: |
| 93 | https://developers.google.com/accounts/docs/OAuth2Login |
| 94 | |
| 95 | Flags: |
| 96 | `, os.Args[0]) |
| 97 | flag.PrintDefaults() |
| 98 | } |
| 99 | |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 100 | func googleOAuthBlesserParams(oauthProvider oauth.OAuthProvider, revocationManager revocation.RevocationManager) blesser.OAuthBlesserParams { |
| 101 | params := blesser.OAuthBlesserParams{ |
| 102 | OAuthProvider: oauthProvider, |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 103 | BlessingDuration: 365 * 24 * time.Hour, |
Asim Shankar | c195b6d | 2015-02-03 11:26:55 -0800 | [diff] [blame] | 104 | EmailClassifier: &emailClassifier, |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 105 | RevocationManager: revocationManager, |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 106 | } |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 107 | if clientID, err := getOAuthClientID(*googleConfigChrome); err != nil { |
| 108 | vlog.Info(err) |
| 109 | } else { |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 110 | params.AccessTokenClients = append(params.AccessTokenClients, oauth.AccessTokenClient{Name: "chrome", ClientID: clientID}) |
Asim Shankar | b18a44f | 2014-10-21 20:25:07 -0700 | [diff] [blame] | 111 | } |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 112 | if clientID, err := getOAuthClientID(*googleConfigAndroid); err != nil { |
| 113 | vlog.Info(err) |
| 114 | } else { |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 115 | params.AccessTokenClients = append(params.AccessTokenClients, oauth.AccessTokenClient{Name: "android", ClientID: clientID}) |
Asim Shankar | b18a44f | 2014-10-21 20:25:07 -0700 | [diff] [blame] | 116 | } |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 117 | return params |
Suharsh Sivakumar | 305626a | 2014-11-10 09:50:24 -0800 | [diff] [blame] | 118 | } |
| 119 | |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 120 | func getOAuthClientID(configFile string) (clientID string, err error) { |
| 121 | f, err := os.Open(configFile) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 122 | if err != nil { |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 123 | return "", fmt.Errorf("failed to open %q: %v", configFile, err) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 124 | } |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 125 | defer f.Close() |
| 126 | clientID, err = oauth.ClientIDFromJSON(f) |
| 127 | if err != nil { |
| 128 | return "", fmt.Errorf("failed to decode JSON in %q: %v", configFile, err) |
| 129 | } |
| 130 | return clientID, nil |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 131 | } |
Nicolas LaCasse | 1b0c8ba | 2015-01-07 17:49:25 -0800 | [diff] [blame] | 132 | |
| 133 | func defaultHost() string { |
| 134 | host, err := os.Hostname() |
| 135 | if err != nil { |
| 136 | vlog.Fatalf("os.Hostname() failed: %v", err) |
| 137 | } |
| 138 | return host |
| 139 | } |