Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 1 | // HTTP server that uses OAuth to create security.PrivateID objects. |
| 2 | package main |
| 3 | |
| 4 | import ( |
| 5 | "flag" |
| 6 | "fmt" |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 7 | "html/template" |
Robin Thellend | 3703680 | 2014-06-03 17:40:23 -0700 | [diff] [blame] | 8 | "net" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 9 | "net/http" |
| 10 | "os" |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 11 | "path/filepath" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 12 | "strings" |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 13 | "time" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 14 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 15 | "veyron.io/veyron/veyron/lib/signals" |
| 16 | vsecurity "veyron.io/veyron/veyron/security" |
| 17 | "veyron.io/veyron/veyron/security/audit" |
| 18 | "veyron.io/veyron/veyron/services/identity/auditor" |
| 19 | "veyron.io/veyron/veyron/services/identity/blesser" |
| 20 | "veyron.io/veyron/veyron/services/identity/googleoauth" |
| 21 | "veyron.io/veyron/veyron/services/identity/handlers" |
| 22 | "veyron.io/veyron/veyron/services/identity/revocation" |
| 23 | services "veyron.io/veyron/veyron/services/security" |
| 24 | "veyron.io/veyron/veyron/services/security/discharger" |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 25 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 26 | "veyron.io/veyron/veyron2" |
| 27 | "veyron.io/veyron/veyron2/ipc" |
| 28 | "veyron.io/veyron/veyron2/naming" |
| 29 | "veyron.io/veyron/veyron2/rt" |
| 30 | "veyron.io/veyron/veyron2/security" |
| 31 | "veyron.io/veyron/veyron2/vlog" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 32 | ) |
| 33 | |
| 34 | var ( |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 35 | httpaddr = flag.String("httpaddr", "localhost:8125", "Address on which the HTTP server listens on.") |
| 36 | tlsconfig = flag.String("tlsconfig", "", "Comma-separated list of TLS certificate and private key files. If empty, will not use HTTPS.") |
| 37 | // TODO(ashankar): Revisit the choices for -vaddr and -vprotocol once the proxy design in relation to mounttables has been finalized. |
| 38 | address = flag.String("vaddr", "proxy.envyor.com:8100", "Address on which the Veyron blessing server listens on. Enabled iff --google_config is set") |
| 39 | protocol = flag.String("vprotocol", "veyron", "Protocol used to interpret --vaddr") |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 40 | 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 public key of the signer at 'x.com/pubkey/'.") |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 41 | minExpiryDays = flag.Int("min_expiry_days", 365, "Minimum expiry time (in days) of identities issued by this server") |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 42 | |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 43 | auditprefix = flag.String("audit", "", "File prefix to files where auditing information will be written.") |
| 44 | auditfilter = flag.String("audit_filter", "", "If non-empty, instead of starting the server the audit log will be dumped to STDOUT (with the filter set to the value of this flag. '/' can be used to dump all events).") |
| 45 | |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 46 | // Configuration for various Google OAuth-based clients. |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 47 | 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.") |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 48 | googleConfigInstalled = flag.String("google_config_installed", "", "Path to the JSON-encoded OAuth client configuration for installed client applications that obtain blessings (via the OAuthBlesser.BlessUsingAuthorizationCode RPC) from this server (like the 'identity' command like tool and its 'seekblessing' command.") |
| 49 | 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.") |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 50 | 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 | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 51 | googleDomain = flag.String("google_domain", "", "An optional domain name. When set, only email addresses from this domain are allowed to authenticate via Google OAuth") |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 52 | |
| 53 | // Revoker/Discharger configuration |
| 54 | revocationDir = flag.String("revocation_dir", filepath.Join(os.TempDir(), "revocation_dir"), "Path where the revocation manager will store caveat and revocation information.") |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 55 | ) |
| 56 | |
| 57 | func main() { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 58 | flag.Usage = usage |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 59 | r := rt.Init(providerIdentity()) |
Bogdan Caprita | 4258d88 | 2014-07-02 09:15:22 -0700 | [diff] [blame] | 60 | defer r.Cleanup() |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 61 | |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 62 | if len(*auditfilter) > 0 { |
| 63 | dumpAuditLog() |
| 64 | return |
| 65 | } |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 66 | |
| 67 | // Calling with empty string returns a empty RevocationManager |
| 68 | revocationManager, err := revocation.NewRevocationManager(*revocationDir) |
| 69 | if err != nil { |
| 70 | vlog.Fatalf("Failed to start RevocationManager: %v", err) |
| 71 | } |
| 72 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 73 | // Setup handlers |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 74 | http.Handle("/pubkey/", handlers.Object{r.Identity().PublicID().PublicKey()}) // public key of this identity server |
Robin Thellend | b640609 | 2014-05-12 16:38:58 -0700 | [diff] [blame] | 75 | if enableRandomHandler() { |
| 76 | http.Handle("/random/", handlers.Random{r}) // mint identities with a random name |
| 77 | } |
| 78 | http.HandleFunc("/bless/", handlers.Bless) // use a provided PrivateID to bless a provided PublicID |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 79 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 80 | // Google OAuth |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 81 | ipcServer, ipcServerEP, err := setupGoogleBlessingDischargingServer(r, revocationManager) |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 82 | if err != nil { |
| 83 | vlog.Fatalf("Failed to setup veyron services for blessing: %v", err) |
| 84 | } |
| 85 | if ipcServer != nil { |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 86 | defer ipcServer.Stop() |
| 87 | } |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 88 | if clientID, clientSecret, ok := getOAuthClientIDAndSecret(*googleConfigWeb); ok && len(*auditprefix) > 0 { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 89 | n := "/google/" |
| 90 | http.Handle(n, googleoauth.NewHandler(googleoauth.HandlerArgs{ |
Suharsh Sivakumar | 5ee0ee6 | 2014-09-04 13:16:34 -0700 | [diff] [blame] | 91 | Addr: fmt.Sprintf("%s%s", httpaddress(), n), |
| 92 | ClientID: clientID, |
| 93 | ClientSecret: clientSecret, |
| 94 | Auditor: *auditprefix, |
| 95 | RevocationManager: revocationManager, |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 96 | })) |
| 97 | } |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 98 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
| 99 | var servers []string |
| 100 | if ipcServer != nil { |
| 101 | servers, _ = ipcServer.Published() |
| 102 | } |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 103 | if len(servers) == 0 { |
| 104 | // No addresses published, publish the endpoint instead (which may not be usable everywhere, but oh-well). |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 105 | servers = append(servers, ipcServerEP.String()) |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 106 | } |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 107 | args := struct { |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 108 | Self string |
| 109 | GoogleWeb, RandomWeb bool |
| 110 | GoogleServers, DischargeServers []string |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 111 | }{ |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 112 | Self: rt.R().Identity().PublicID().Names()[0], |
| 113 | GoogleWeb: len(*googleConfigWeb) > 0, |
| 114 | RandomWeb: enableRandomHandler(), |
| 115 | GoogleServers: appendSuffixTo(servers, "google"), |
| 116 | DischargeServers: appendSuffixTo(servers, "discharger"), |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 117 | } |
| 118 | if err := tmpl.Execute(w, args); err != nil { |
| 119 | vlog.Info("Failed to render template:", err) |
| 120 | } |
| 121 | }) |
Asim Shankar | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 122 | vlog.Infof("Running HTTP server at: %v", httpaddress()) |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 123 | go runHTTPServer(*httpaddr) |
| 124 | <-signals.ShutdownOnSignals() |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 125 | } |
| 126 | |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 127 | func appendSuffixTo(objectname []string, suffix string) []string { |
| 128 | names := make([]string, len(objectname)) |
| 129 | for i, o := range objectname { |
| 130 | names[i] = naming.JoinAddressName(o, suffix) |
| 131 | } |
| 132 | return names |
| 133 | } |
| 134 | |
| 135 | // newDispatcher returns a dispatcher for both the blessing and the discharging service. |
| 136 | // their suffix. ReflectInvoker is used to invoke methods. |
| 137 | func newDispatcher(params blesser.GoogleParams) ipc.Dispatcher { |
| 138 | blessingService := ipc.ReflectInvoker(blesser.NewGoogleOAuthBlesserServer(params)) |
| 139 | dischargerService := ipc.ReflectInvoker(services.NewServerDischarger(discharger.NewDischarger(params.R.Identity()))) |
Asim Shankar | 9f6db08 | 2014-08-27 16:44:03 -0700 | [diff] [blame] | 140 | allowEveryoneACLAuth := vsecurity.NewACLAuthorizer(security.ACL{In: map[security.BlessingPattern]security.LabelSet{ |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 141 | security.AllPrincipals: security.AllLabels, |
Asim Shankar | 9f6db08 | 2014-08-27 16:44:03 -0700 | [diff] [blame] | 142 | }}) |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 143 | return &dispatcher{blessingService, dischargerService, allowEveryoneACLAuth} |
| 144 | } |
| 145 | |
| 146 | type dispatcher struct { |
| 147 | blessingInvoker, dischargerInvoker ipc.Invoker |
| 148 | auth security.Authorizer |
| 149 | } |
| 150 | |
| 151 | func (d dispatcher) Lookup(suffix, method string) (ipc.Invoker, security.Authorizer, error) { |
| 152 | switch suffix { |
| 153 | case "google": |
| 154 | return d.blessingInvoker, d.auth, nil |
| 155 | case "discharger": |
| 156 | return d.dischargerInvoker, d.auth, nil |
| 157 | default: |
| 158 | return nil, nil, fmt.Errorf("suffix does not exist") |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | // Starts the blessing service and the discharging service on the same port. |
| 163 | func setupGoogleBlessingDischargingServer(r veyron2.Runtime, revocationManager *revocation.RevocationManager) (ipc.Server, naming.Endpoint, error) { |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 164 | var enable bool |
| 165 | params := blesser.GoogleParams{ |
| 166 | R: r, |
| 167 | BlessingDuration: time.Duration(*minExpiryDays) * 24 * time.Hour, |
| 168 | DomainRestriction: *googleDomain, |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 169 | RevocationManager: revocationManager, |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 170 | } |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 171 | if clientID, clientSecret, ok := getOAuthClientIDAndSecret(*googleConfigInstalled); ok { |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 172 | enable = true |
| 173 | params.AuthorizationCodeClient.ID = clientID |
| 174 | params.AuthorizationCodeClient.Secret = clientSecret |
| 175 | } |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 176 | if clientID, ok := getOAuthClientID(*googleConfigChrome); ok { |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 177 | enable = true |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 178 | params.AccessTokenClients = append(params.AccessTokenClients, struct{ ID string }{clientID}) |
| 179 | } |
| 180 | if clientID, ok := getOAuthClientID(*googleConfigAndroid); ok { |
| 181 | enable = true |
| 182 | params.AccessTokenClients = append(params.AccessTokenClients, struct{ ID string }{clientID}) |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 183 | } |
| 184 | if !enable { |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 185 | return nil, nil, nil |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 186 | } |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 187 | server, err := r.NewServer() |
| 188 | if err != nil { |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 189 | return nil, nil, fmt.Errorf("failed to create new ipc.Server: %v", err) |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 190 | } |
| 191 | ep, err := server.Listen(*protocol, *address) |
| 192 | if err != nil { |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 193 | return nil, nil, fmt.Errorf("server.Listen(%q, %q) failed: %v", "tcp", *address, err) |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 194 | } |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 195 | params.DischargerLocation = naming.JoinAddressName(ep.String(), "discharger") |
| 196 | dispatcher := newDispatcher(params) |
| 197 | objectname := fmt.Sprintf("identity/%s", r.Identity().PublicID().Names()[0]) |
| 198 | if err := server.Serve(objectname, dispatcher); err != nil { |
| 199 | return nil, nil, fmt.Errorf("failed to start Veyron services: %v", err) |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 200 | } |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 201 | vlog.Infof("Google blessing and discharger services enabled at endpoint %v and name %q", ep, objectname) |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 202 | return server, ep, nil |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 203 | } |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 204 | |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 205 | func enableTLS() bool { return len(*tlsconfig) > 0 } |
| 206 | func enableRandomHandler() bool { |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 207 | return len(*googleConfigInstalled)+len(*googleConfigWeb)+len(*googleConfigChrome)+len(*googleConfigAndroid) == 0 |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 208 | } |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 209 | func getOAuthClientID(config string) (clientID string, ok bool) { |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 210 | fname := config |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 211 | if len(fname) == 0 { |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 212 | return "", false |
| 213 | } |
| 214 | f, err := os.Open(fname) |
| 215 | if err != nil { |
| 216 | vlog.Fatalf("Failed to open %q: %v", fname, err) |
| 217 | } |
| 218 | defer f.Close() |
| 219 | clientID, err = googleoauth.ClientIDFromJSON(f) |
| 220 | if err != nil { |
| 221 | vlog.Fatalf("Failed to decode JSON in %q: %v", fname, err) |
| 222 | } |
| 223 | return clientID, true |
| 224 | } |
| 225 | func getOAuthClientIDAndSecret(config string) (clientID, clientSecret string, ok bool) { |
| 226 | fname := config |
| 227 | if len(fname) == 0 { |
| 228 | return "", "", false |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 229 | } |
| 230 | f, err := os.Open(fname) |
| 231 | if err != nil { |
| 232 | vlog.Fatalf("Failed to open %q: %v", fname, err) |
| 233 | } |
| 234 | defer f.Close() |
| 235 | clientID, clientSecret, err = googleoauth.ClientIDAndSecretFromJSON(f) |
| 236 | if err != nil { |
| 237 | vlog.Fatalf("Failed to decode JSON in %q: %v", fname, err) |
| 238 | } |
Srdjan Petrovic | a1e6ddb | 2014-09-08 10:18:44 -0700 | [diff] [blame] | 239 | return clientID, clientSecret, true |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 240 | } |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 241 | func runHTTPServer(addr string) { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 242 | if !enableTLS() { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 243 | if err := http.ListenAndServe(addr, nil); err != nil { |
| 244 | vlog.Fatalf("http.ListenAndServe failed: %v", err) |
| 245 | } |
| 246 | return |
| 247 | } |
| 248 | paths := strings.Split(*tlsconfig, ",") |
| 249 | if len(paths) != 2 { |
| 250 | vlog.Fatalf("Could not parse --tlsconfig. Must have exactly two components, separated by a comma") |
| 251 | } |
| 252 | vlog.Infof("Starting HTTP server with TLS using certificate [%s] and private key [%s] at https://%s", paths[0], paths[1], addr) |
| 253 | if err := http.ListenAndServeTLS(addr, paths[0], paths[1], nil); err != nil { |
| 254 | vlog.Fatalf("http.ListenAndServeTLS failed: %v", err) |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | func usage() { |
| 259 | fmt.Fprintf(os.Stderr, `%s starts an HTTP server that mints veyron identities in response to GET requests. |
| 260 | |
| 261 | To generate TLS certificates so the HTTP server can use SSL: |
| 262 | go run $GOROOT/src/pkg/crypto/tls/generate_cert.go --host <IP address> |
| 263 | |
Asim Shankar | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 264 | To generate an identity for this service itself, use: |
| 265 | go install veyron/tools/identity && ./bin/identity generate <name> ><filename> |
| 266 | and set the VEYRON_IDENTITY environment variable when running this application. |
| 267 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 268 | To enable use of Google APIs to use Google OAuth for authorization, set --google_config, |
| 269 | which must point to the contents of a JSON file obtained after registering your application |
| 270 | with the Google Developer Console at: |
| 271 | https://code.google.com/apis/console |
| 272 | More details on Google OAuth at: |
| 273 | https://developers.google.com/accounts/docs/OAuth2Login |
| 274 | |
| 275 | Flags: |
| 276 | `, os.Args[0]) |
| 277 | flag.PrintDefaults() |
| 278 | } |
| 279 | |
| 280 | func defaultHost() string { |
| 281 | host, err := os.Hostname() |
| 282 | if err != nil { |
| 283 | vlog.Fatalf("Failed to get hostname: %v", err) |
| 284 | } |
| 285 | return host |
| 286 | } |
| 287 | |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 288 | // providerIdentity returns the identity of the identity provider (i.e., this program) itself. |
| 289 | func providerIdentity() veyron2.ROpt { |
| 290 | // TODO(ashankar): This scheme of initializing a runtime just to share the "load identity" code is ridiculous. |
| 291 | // Figure out a way to update the runtime's identity with a wrapper and avoid this spurios "New" call. |
| 292 | r, err := rt.New() |
| 293 | if err != nil { |
| 294 | vlog.Fatal(err) |
| 295 | } |
| 296 | defer r.Cleanup() |
| 297 | id := r.Identity() |
| 298 | if len(*auditprefix) > 0 { |
| 299 | auditor, err := auditor.NewFileAuditor(*auditprefix) |
| 300 | if err != nil { |
| 301 | vlog.Fatal(err) |
| 302 | } |
| 303 | id = audit.NewPrivateID(id, auditor) |
| 304 | } |
| 305 | return veyron2.RuntimeID(id) |
| 306 | } |
| 307 | |
Asim Shankar | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 308 | func httpaddress() string { |
| 309 | _, port, err := net.SplitHostPort(*httpaddr) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 310 | if err != nil { |
Asim Shankar | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 311 | vlog.Fatalf("Failed to parse %q: %v", *httpaddr, err) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 312 | } |
Asim Shankar | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 313 | scheme := "http" |
| 314 | if enableTLS() { |
| 315 | scheme = "https" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 316 | } |
Asim Shankar | 1c3b181 | 2014-07-31 18:54:51 -0700 | [diff] [blame] | 317 | return fmt.Sprintf("%s://%s:%v", scheme, *host, port) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 318 | } |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 319 | |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 320 | func dumpAuditLog() { |
| 321 | if len(*auditprefix) == 0 { |
| 322 | vlog.Fatalf("Must set --audit") |
| 323 | } |
| 324 | ch, err := auditor.ReadAuditLog(*auditprefix, *auditfilter) |
| 325 | if err != nil { |
| 326 | vlog.Fatal(err) |
| 327 | } |
| 328 | idx := 0 |
| 329 | for entry := range ch { |
| 330 | fmt.Printf("%6d) %v\n", idx, entry) |
| 331 | idx++ |
| 332 | } |
| 333 | } |
| 334 | |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 335 | var tmpl = template.Must(template.New("main").Parse(`<!doctype html> |
| 336 | <html> |
| 337 | <head> |
| 338 | <meta charset="UTF-8"> |
| 339 | <title>Veyron Identity Server</title> |
| 340 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 341 | <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> |
| 342 | </head> |
| 343 | <body> |
| 344 | <div class="container"> |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 345 | <div class="page-header"><h2>{{.Self}}</h2><h4>A Veyron Identity Provider</h4></div> |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 346 | <div class="well"> |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 347 | This is a Veyron identity provider that provides blessings with the name prefix <mark>{{.Self}}</mark>. The public |
| 348 | key of this provider is available in <a class="btn btn-xs btn-primary" href="/pubkey/base64vom">base64-encoded-vom-encoded</a> format. |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 349 | </div> |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 350 | |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 351 | {{if .GoogleServers}} |
| 352 | <div class="well"> |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 353 | Blessings are provided via Veyron RPCs to: <tt>{{range .GoogleServers}}{{.}}{{end}}</tt> |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 354 | </div> |
| 355 | {{end}} |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 356 | {{if .DischargeServers}} |
| 357 | <div class="well"> |
| 358 | RevocationCaveat Discharges are provided via Veyron RPCs to: <tt>{{range .DischargeServers}}{{.}}{{end}}</tt> |
| 359 | </div> |
| 360 | {{end}} |
| 361 | |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 362 | |
| 363 | {{if .GoogleWeb}} |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 364 | <div class="well"> |
| 365 | This page provides the ability to <a class="btn btn-xs btn-primary" href="/google/auth">enumerate</a> blessings provided with your |
| 366 | email address as the name. |
| 367 | </div> |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 368 | {{end}} |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 369 | |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 370 | {{if .RandomWeb}} |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 371 | <div class="well"> |
| 372 | You can obtain a randomly assigned PrivateID <a class="btn btn-sm btn-primary" href="/random/">here</a> |
| 373 | </div> |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 374 | {{end}} |
Asim Shankar | 3afe790 | 2014-08-12 11:43:48 -0700 | [diff] [blame] | 375 | |
| 376 | <div class="well"> |
| 377 | You can use <a class="btn btn-xs btn-primary" href="/bless/">this form</a> to offload crypto for blessing to this HTTP server |
| 378 | </div> |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 379 | |
| 380 | </div> |
| 381 | </body> |
| 382 | </html>`)) |