blob: 1ad57b6e78b84662d575adcab16e5f2dacdd682d [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package impl
2
3import (
Bogdan Capritac87a9142014-07-21 10:38:13 -07004 "fmt"
Gautham82bb9952014-08-28 14:11:51 -07005 "os"
Robert Kroegeracc778b2014-11-03 17:17:21 -08006 "path"
Gautham82bb9952014-08-28 14:11:51 -07007 "path/filepath"
Bogdan Caprita4d67c042014-08-19 10:41:19 -07008 "strings"
Gautham6fe61e52014-09-16 13:58:17 -07009 "sync"
Jiri Simsa24e87aa2014-06-09 09:27:34 -070010
Jiri Simsa764efb72014-12-25 20:57:03 -080011 "v.io/core/veyron/security/agent"
12 "v.io/core/veyron/security/agent/keymgr"
Robert Kroegere95ed6d2015-01-14 17:41:04 -080013 "v.io/core/veyron/security/flag"
Jiri Simsa764efb72014-12-25 20:57:03 -080014 idevice "v.io/core/veyron/services/mgmt/device"
15 "v.io/core/veyron/services/mgmt/device/config"
Robert Kroegerebfb62a2014-12-10 14:42:09 -080016 "v.io/core/veyron/services/mgmt/lib/acls"
Jiri Simsa764efb72014-12-25 20:57:03 -080017 logsimpl "v.io/core/veyron/services/mgmt/logreader/impl"
Jiri Simsa24e87aa2014-06-09 09:27:34 -070018
Jiri Simsa764efb72014-12-25 20:57:03 -080019 "v.io/core/veyron2/ipc"
20 "v.io/core/veyron2/naming"
21 "v.io/core/veyron2/security"
22 "v.io/core/veyron2/services/mgmt/device"
23 "v.io/core/veyron2/services/mgmt/pprof"
24 "v.io/core/veyron2/services/mgmt/stats"
25 "v.io/core/veyron2/services/security/access"
26 verror "v.io/core/veyron2/verror2"
27 "v.io/core/veyron2/vlog"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070028)
29
Bogdan Caprita2b219362014-12-09 17:03:33 -080030// internalState wraps state shared between different device manager
Bogdan Caprita4d67c042014-08-19 10:41:19 -070031// invocations.
32type internalState struct {
Bogdan Caprita29a3b352015-01-16 16:28:49 -080033 callback *callbackState
34 updating *updatingState
35 securityAgent *securityAgentState
36 restartHandler func()
Bogdan Caprita4d67c042014-08-19 10:41:19 -070037}
38
Bogdan Caprita2b219362014-12-09 17:03:33 -080039// dispatcher holds the state of the device manager dispatcher.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070040type dispatcher struct {
Bogdan Caprita4d67c042014-08-19 10:41:19 -070041 // internal holds the state that persists across RPC method invocations.
Bogdan Capritac87a9142014-07-21 10:38:13 -070042 internal *internalState
Bogdan Caprita2b219362014-12-09 17:03:33 -080043 // config holds the device manager's (immutable) configuration state.
Bogdan Caprita4d67c042014-08-19 10:41:19 -070044 config *config.State
Gautham6fe61e52014-09-16 13:58:17 -070045 // dispatcherMutex is a lock for coordinating concurrent access to some
46 // dispatcher methods.
Bogdan Caprita7f491672014-11-13 14:51:08 -080047 mu sync.RWMutex
48 // TODO(rjkroege): Consider moving this inside internal.
Robert Kroegere95ed6d2015-01-14 17:41:04 -080049 uat BlessingSystemAssociationStore
50 locks *acls.Locks
51 principal security.Principal
Jiri Simsa5293dcb2014-05-10 09:56:38 -070052}
53
Benjamin Prosnitzfdfbf7b2014-10-08 09:47:21 -070054var _ ipc.Dispatcher = (*dispatcher)(nil)
55
Bogdan Caprita4d67c042014-08-19 10:41:19 -070056const (
57 appsSuffix = "apps"
Bogdan Caprita9c4aa222014-12-10 14:46:30 -080058 deviceSuffix = "device"
Bogdan Caprita4d67c042014-08-19 10:41:19 -070059 configSuffix = "cfg"
Todd Wang34ed4c62014-11-26 15:15:52 -080060
Jiri Simsa764efb72014-12-25 20:57:03 -080061 pkgPath = "v.io/core/veyron/services/mgmt/device/impl"
Bogdan Caprita4d67c042014-08-19 10:41:19 -070062)
63
64var (
Mike Burrowsd65df962014-12-17 10:01:19 -080065 ErrInvalidSuffix = verror.Register(pkgPath+".InvalidSuffix", verror.NoRetry, "{1:}{2:} invalid suffix{:_}")
66 ErrOperationFailed = verror.Register(pkgPath+".OperationFailed", verror.NoRetry, "{1:}{2:} operation failed{:_}")
67 ErrOperationInProgress = verror.Register(pkgPath+".OperationInProgress", verror.NoRetry, "{1:}{2:} operation in progress{:_}")
68 ErrAppTitleMismatch = verror.Register(pkgPath+".AppTitleMismatch", verror.NoRetry, "{1:}{2:} app title mismatch{:_}")
69 ErrUpdateNoOp = verror.Register(pkgPath+".UpdateNoOp", verror.NoRetry, "{1:}{2:} update is no op{:_}")
70 ErrInvalidOperation = verror.Register(pkgPath+".InvalidOperation", verror.NoRetry, "{1:}{2:} invalid operation{:_}")
71 ErrInvalidBlessing = verror.Register(pkgPath+".InvalidBlessing", verror.NoRetry, "{1:}{2:} invalid blessing{:_}")
Bogdan Caprita4d67c042014-08-19 10:41:19 -070072)
73
Bogdan Caprita2b219362014-12-09 17:03:33 -080074// NewDispatcher is the device manager dispatcher factory.
Bogdan Caprita29a3b352015-01-16 16:28:49 -080075func NewDispatcher(principal security.Principal, config *config.State, restartHandler func()) (*dispatcher, error) {
Bogdan Capritac87a9142014-07-21 10:38:13 -070076 if err := config.Validate(); err != nil {
Gautham6fe61e52014-09-16 13:58:17 -070077 return nil, fmt.Errorf("invalid config %v: %v", config, err)
Bogdan Capritac87a9142014-07-21 10:38:13 -070078 }
Bogdan Capritac7e72b62015-01-07 19:22:23 -080079 // TODO(caprita): use some mechansim (a file lock or presence of entry
80 // in mounttable) to ensure only one device manager is running in an
81 // installation?
82 mi := &managerInfo{
83 MgrName: naming.Join(config.Name, deviceSuffix),
84 Pid: os.Getpid(),
85 }
86 if err := saveManagerInfo(filepath.Join(config.Root, "device-manager"), mi); err != nil {
87 return nil, fmt.Errorf("failed to save info: %v", err)
88 }
Robert Kroeger1cb4a0d2014-10-20 11:55:38 -070089 uat, err := NewBlessingSystemAssociationStore(config.Root)
90 if err != nil {
91 return nil, fmt.Errorf("cannot create persistent store for identity to system account associations: %v", err)
92 }
Gautham82bb9952014-08-28 14:11:51 -070093 d := &dispatcher{
Bogdan Capritac87a9142014-07-21 10:38:13 -070094 internal: &internalState{
Bogdan Caprita29a3b352015-01-16 16:28:49 -080095 callback: newCallbackState(config.Name),
96 updating: newUpdatingState(),
97 restartHandler: restartHandler,
Jiri Simsa70c32052014-06-18 11:38:21 -070098 },
Robert Kroegere95ed6d2015-01-14 17:41:04 -080099 config: config,
100 uat: uat,
101 locks: acls.NewLocks(),
102 principal: principal,
Gautham82bb9952014-08-28 14:11:51 -0700103 }
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800104
105 tam, err := flag.TaggedACLMapFromFlag()
106 if err != nil {
107 return nil, err
108 }
109 if tam != nil {
110 if err := d.locks.SetPathACL(principal, d.getACLDir(), tam, ""); err != nil {
Gautham6fe61e52014-09-16 13:58:17 -0700111 return nil, err
112 }
Gautham82bb9952014-08-28 14:11:51 -0700113 }
Bogdan Caprita7f491672014-11-13 14:51:08 -0800114 // If we're in 'security agent mode', set up the key manager agent.
115 if len(os.Getenv(agent.FdVarName)) > 0 {
116 if keyMgrAgent, err := keymgr.NewAgent(); err != nil {
117 return nil, fmt.Errorf("NewAgent() failed: %v", err)
118 } else {
119 d.internal.securityAgent = &securityAgentState{
120 keyMgrAgent: keyMgrAgent,
121 }
122 }
123 }
Gautham82bb9952014-08-28 14:11:51 -0700124 return d, nil
125}
126
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800127func (d *dispatcher) getACLDir() string {
128 return filepath.Join(d.config.Root, "device-manager", "device-data", "acls")
Gautham82bb9952014-08-28 14:11:51 -0700129}
130
Robin Thellend888f8cf2014-12-15 16:19:10 -0800131func (d *dispatcher) claimDeviceManager(ctx ipc.ServerContext) error {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800132 // TODO(rjkroege): Scrub the state tree of installation and instance ACL files.
Robin Thellend888f8cf2014-12-15 16:19:10 -0800133
134 // Get the blessings to be used by the claimant.
135 blessings := ctx.Blessings()
136 if blessings == nil {
Matt Rosencrantz8f9fca12014-12-19 14:02:31 -0800137 return verror.Make(ErrInvalidBlessing, ctx.Context())
Robin Thellend888f8cf2014-12-15 16:19:10 -0800138 }
139 principal := ctx.LocalPrincipal()
140 if err := principal.AddToRoots(blessings); err != nil {
141 vlog.Errorf("principal.AddToRoots(%s) failed: %v", blessings, err)
Matt Rosencrantz8f9fca12014-12-19 14:02:31 -0800142 return verror.Make(ErrInvalidBlessing, ctx.Context())
Robin Thellend888f8cf2014-12-15 16:19:10 -0800143 }
144 names := blessings.ForContext(ctx)
Asim Shankar8f05c222014-10-06 22:08:19 -0700145 if len(names) == 0 {
Robin Thellend888f8cf2014-12-15 16:19:10 -0800146 vlog.Errorf("No names for claimer(%v) are trusted", blessings)
Mike Burrowsd65df962014-12-17 10:01:19 -0800147 return verror.Make(ErrOperationFailed, nil)
Gautham82bb9952014-08-28 14:11:51 -0700148 }
Robin Thellend888f8cf2014-12-15 16:19:10 -0800149 principal.BlessingStore().Set(blessings, security.AllPrincipals)
150 principal.BlessingStore().SetDefault(blessings)
Bogdan Caprita2b219362014-12-09 17:03:33 -0800151 // Create ACLs to transfer devicemanager permissions to the provided identity.
Asim Shankar68885192014-11-26 12:48:35 -0800152 acl := make(access.TaggedACLMap)
153 for _, n := range names {
154 for _, tag := range access.AllTypicalTags() {
Ankur2b61d352015-01-27 14:59:37 -0800155 // TODO(caprita, ataly, ashankar): Do we really need the NonExtendable restriction
156 // below?
157 acl.Add(security.BlessingPattern(n).MakeNonExtendable(), string(tag))
Asim Shankar68885192014-11-26 12:48:35 -0800158 }
Gautham82bb9952014-08-28 14:11:51 -0700159 }
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800160 if err := d.locks.SetPathACL(principal, d.getACLDir(), acl, ""); err != nil {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800161 vlog.Errorf("Failed to setACL:%v", err)
Mike Burrowsd65df962014-12-17 10:01:19 -0800162 return verror.Make(ErrOperationFailed, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800163 }
164 return nil
165}
166
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800167// TODO(rjkroege): Consider refactoring authorizer implementations to
168// be shareable with other components.
169func newAuthorizer(principal security.Principal, dir string, locks *acls.Locks) (security.Authorizer, error) {
170 rootTam, _, err := locks.GetPathACL(principal, dir)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800171
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800172 if err != nil && os.IsNotExist(err) {
173 vlog.Errorf("GetPathACL(%s) failed: %v", dir, err)
174 return allowEveryone{}, nil
175 } else if err != nil {
176 return nil, err
Robert Kroegeracc778b2014-11-03 17:17:21 -0800177 }
Robert Kroegeracc778b2014-11-03 17:17:21 -0800178
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800179 auth, err := access.TaggedACLAuthorizer(rootTam, access.TypicalTagType())
Robert Kroegeracc778b2014-11-03 17:17:21 -0800180 if err != nil {
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800181 vlog.Errorf("Successfully obtained an ACL from the filesystem but TaggedACLAuthorizer couldn't use it: %v", err)
182 return nil, err
Robert Kroegeracc778b2014-11-03 17:17:21 -0800183 }
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800184 return auth, nil
Robert Kroegeracc778b2014-11-03 17:17:21 -0800185
Gautham6fe61e52014-09-16 13:58:17 -0700186}
187
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700188// DISPATCHER INTERFACE IMPLEMENTATION
Robin Thellenda02fe8f2014-11-19 09:58:29 -0800189func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700190 components := strings.Split(suffix, "/")
191 for i := 0; i < len(components); i++ {
192 if len(components[i]) == 0 {
193 components = append(components[:i], components[i+1:]...)
194 i--
195 }
196 }
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800197 auth, err := newAuthorizer(d.principal, d.getACLDir(), d.locks)
198 if err != nil {
199 return nil, nil, err
200 }
201
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700202 if len(components) == 0 {
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800203 return ipc.ChildrenGlobberInvoker(deviceSuffix, appsSuffix), auth, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700204 }
Bogdan Caprita2b219362014-12-09 17:03:33 -0800205 // The implementation of the device manager is split up into several
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700206 // invokers, which are instantiated depending on the receiver name
207 // prefix.
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700208 switch components[0] {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800209 case deviceSuffix:
Bogdan Capritaa456f472014-12-10 10:18:03 -0800210 receiver := device.DeviceServer(&deviceService{
Bogdan Caprita29a3b352015-01-16 16:28:49 -0800211 callback: d.internal.callback,
212 updating: d.internal.updating,
213 restartHandler: d.internal.restartHandler,
214 config: d.config,
215 disp: d,
216 uat: d.uat,
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700217 })
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800218 return receiver, auth, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700219 case appsSuffix:
Robin Thellend8a0f04f2014-11-17 10:40:24 -0800220 // Requests to apps/*/*/*/logs are handled locally by LogFileService.
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700221 // Requests to apps/*/*/*/pprof are proxied to the apps' __debug/pprof object.
222 // Requests to apps/*/*/*/stats are proxied to the apps' __debug/stats object.
Robin Thellend9bc8fcb2014-11-17 10:23:04 -0800223 // Everything else is handled by the Application server.
Robin Thellendac7128c2014-11-11 09:58:28 -0800224 if len(components) >= 5 {
Robin Thellend4c5266e2014-10-27 13:19:29 -0700225 appInstanceDir, err := instanceDir(d.config.Root, components[1:4])
226 if err != nil {
227 return nil, nil, err
228 }
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700229 switch kind := components[4]; kind {
230 case "logs":
231 logsDir := filepath.Join(appInstanceDir, "logs")
232 suffix := naming.Join(components[5:]...)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800233 return logsimpl.NewLogFileService(logsDir, suffix), auth, nil
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700234 case "pprof", "stats":
235 info, err := loadInstanceInfo(appInstanceDir)
236 if err != nil {
237 return nil, nil, err
238 }
239 if !instanceStateIs(appInstanceDir, started) {
Mike Burrowsd65df962014-12-17 10:01:19 -0800240 return nil, nil, verror.Make(ErrInvalidSuffix, nil)
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700241 }
Todd Wangf519f8f2015-01-21 10:07:41 -0800242 var desc []ipc.InterfaceDesc
243 switch kind {
244 case "pprof":
245 desc = pprof.PProfServer(nil).Describe__()
246 case "stats":
247 desc = stats.StatsServer(nil).Describe__()
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700248 }
249 suffix := naming.Join("__debug", naming.Join(components[4:]...))
250 remote := naming.JoinAddressName(info.AppCycleMgrName, suffix)
Todd Wangf519f8f2015-01-21 10:07:41 -0800251 invoker := newProxyInvoker(remote, access.Debug, desc)
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800252 return invoker, auth, nil
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700253 }
Robin Thellend4c5266e2014-10-27 13:19:29 -0700254 }
Robert Kroegeracc778b2014-11-03 17:17:21 -0800255 if err != nil {
256 return nil, nil, err
257 }
Bogdan Capritaa456f472014-12-10 10:18:03 -0800258 receiver := device.ApplicationServer(&appService{
Bogdan Caprita7f491672014-11-13 14:51:08 -0800259 callback: d.internal.callback,
260 config: d.config,
261 suffix: components[1:],
262 uat: d.uat,
263 locks: d.locks,
Bogdan Caprita7f491672014-11-13 14:51:08 -0800264 securityAgent: d.internal.securityAgent,
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700265 })
Robert Kroegere95ed6d2015-01-14 17:41:04 -0800266 appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:])
Robert Kroegeracc778b2014-11-03 17:17:21 -0800267 if err != nil {
268 return nil, nil, err
269 }
Cosmos Nicolaou710daa22014-11-11 19:39:18 -0800270 return receiver, appSpecificAuthorizer, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700271 case configSuffix:
272 if len(components) != 2 {
Mike Burrowsd65df962014-12-17 10:01:19 -0800273 return nil, nil, verror.Make(ErrInvalidSuffix, nil)
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700274 }
Bogdan Capritaa456f472014-12-10 10:18:03 -0800275 receiver := idevice.ConfigServer(&configService{
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700276 callback: d.internal.callback,
277 suffix: components[1],
278 })
Bogdan Capritab9501d12014-10-10 15:02:03 -0700279 // The nil authorizer ensures that only principals blessed by
Bogdan Caprita2b219362014-12-09 17:03:33 -0800280 // the device manager can talk back to it. All apps started by
281 // the device manager should fall in that category.
Bogdan Capritab9501d12014-10-10 15:02:03 -0700282 //
283 // TODO(caprita,rjkroege): We should further refine this, by
284 // only allowing the app to update state referring to itself
285 // (and not other apps).
Cosmos Nicolaou710daa22014-11-11 19:39:18 -0800286 return receiver, nil, nil
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700287 default:
Mike Burrowsd65df962014-12-17 10:01:19 -0800288 return nil, nil, verror.Make(ErrInvalidSuffix, nil)
Bogdan Caprita4d67c042014-08-19 10:41:19 -0700289 }
Jiri Simsa24e87aa2014-06-09 09:27:34 -0700290}
Robert Kroegeracc778b2014-11-03 17:17:21 -0800291
292func newAppSpecificAuthorizer(sec security.Authorizer, config *config.State, suffix []string) (security.Authorizer, error) {
Bogdan Caprita2b219362014-12-09 17:03:33 -0800293 // TODO(rjkroege): This does not support <appname>.Start() to start all
294 // instances. Correct this.
Robert Kroegeracc778b2014-11-03 17:17:21 -0800295
Bogdan Caprita2b219362014-12-09 17:03:33 -0800296 // If we are attempting a method invocation against "apps/", we use the
297 // device-manager wide ACL.
Robert Kroegeracc778b2014-11-03 17:17:21 -0800298 if len(suffix) == 0 || len(suffix) == 1 {
299 return sec, nil
300 }
301 // Otherwise, we require a per-installation and per-instance ACL file.
Robert Kroegeracc778b2014-11-03 17:17:21 -0800302 if len(suffix) == 2 {
303 p, err := installationDirCore(suffix, config.Root)
304 if err != nil {
305 vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
306 return nil, err
307 }
Asim Shankar68885192014-11-26 12:48:35 -0800308 return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
309 }
310 if len(suffix) > 2 {
Robert Kroegeracc778b2014-11-03 17:17:21 -0800311 p, err := instanceDir(config.Root, suffix[0:3])
312 if err != nil {
313 vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
314 return nil, err
315 }
Asim Shankar68885192014-11-26 12:48:35 -0800316 return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
Robert Kroegeracc778b2014-11-03 17:17:21 -0800317 }
Mike Burrowsd65df962014-12-17 10:01:19 -0800318 return nil, verror.Make(ErrInvalidSuffix, nil)
Robert Kroegeracc778b2014-11-03 17:17:21 -0800319}
Asim Shankar68885192014-11-26 12:48:35 -0800320
321// allowEveryone implements the authorization policy that allows all principals
322// access.
323type allowEveryone struct{}
324
Robin Thellend888f8cf2014-12-15 16:19:10 -0800325func (allowEveryone) Authorize(ctx security.Context) error {
326 vlog.Infof("Device manager is unclaimed. Allow %q.%s() from %q.", ctx.Suffix(), ctx.Method(), ctx.RemoteBlessings())
327 return nil
328}