github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/keyupdater/authorisedkeys.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package keyupdater 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/utils/ssh" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/facade" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/state" 15 "github.com/juju/juju/state/watcher" 16 ) 17 18 // KeyUpdater defines the methods on the keyupdater API end point. 19 type KeyUpdater interface { 20 AuthorisedKeys(args params.Entities) (params.StringsResults, error) 21 WatchAuthorisedKeys(args params.Entities) (params.NotifyWatchResults, error) 22 } 23 24 // KeyUpdaterAPI implements the KeyUpdater interface and is the concrete 25 // implementation of the api end point. 26 type KeyUpdaterAPI struct { 27 state *state.State 28 model *state.Model 29 resources facade.Resources 30 authorizer facade.Authorizer 31 getCanRead common.GetAuthFunc 32 } 33 34 var _ KeyUpdater = (*KeyUpdaterAPI)(nil) 35 36 // NewKeyUpdaterAPI creates a new server-side keyupdater API end point. 37 func NewKeyUpdaterAPI( 38 st *state.State, 39 resources facade.Resources, 40 authorizer facade.Authorizer, 41 ) (*KeyUpdaterAPI, error) { 42 // Only machine agents have access to the keyupdater service. 43 if !authorizer.AuthMachineAgent() { 44 return nil, common.ErrPerm 45 } 46 // No-one else except the machine itself can only read a machine's own credentials. 47 getCanRead := func() (common.AuthFunc, error) { 48 return authorizer.AuthOwner, nil 49 } 50 m, err := st.Model() 51 if err != nil { 52 return nil, errors.Trace(err) 53 } 54 return &KeyUpdaterAPI{state: st, model: m, resources: resources, authorizer: authorizer, getCanRead: getCanRead}, nil 55 } 56 57 // WatchAuthorisedKeys starts a watcher to track changes to the authorised ssh keys 58 // for the specified machines. 59 // The current implementation relies on global authorised keys being stored in the model config. 60 // This will change as new user management and authorisation functionality is added. 61 func (api *KeyUpdaterAPI) WatchAuthorisedKeys(arg params.Entities) (params.NotifyWatchResults, error) { 62 results := make([]params.NotifyWatchResult, len(arg.Entities)) 63 64 canRead, err := api.getCanRead() 65 if err != nil { 66 return params.NotifyWatchResults{}, err 67 } 68 for i, entity := range arg.Entities { 69 tag, err := names.ParseTag(entity.Tag) 70 if err != nil { 71 results[i].Error = common.ServerError(err) 72 continue 73 } 74 // 1. Check permissions 75 if !canRead(tag) { 76 results[i].Error = common.ServerError(common.ErrPerm) 77 continue 78 } 79 // 2. Check entity exists 80 if _, err := api.state.FindEntity(tag); err != nil { 81 if errors.IsNotFound(err) { 82 results[i].Error = common.ServerError(common.ErrPerm) 83 } else { 84 results[i].Error = common.ServerError(err) 85 } 86 continue 87 } 88 // 3. Watch for changes 89 watch := api.model.WatchForModelConfigChanges() 90 // Consume the initial event. 91 if _, ok := <-watch.Changes(); ok { 92 results[i].NotifyWatcherId = api.resources.Register(watch) 93 } else { 94 err = watcher.EnsureErr(watch) 95 } 96 results[i].Error = common.ServerError(err) 97 } 98 return params.NotifyWatchResults{Results: results}, nil 99 } 100 101 // AuthorisedKeys reports the authorised ssh keys for the specified machines. 102 // The current implementation relies on global authorised keys being stored in the model config. 103 // This will change as new user management and authorisation functionality is added. 104 func (api *KeyUpdaterAPI) AuthorisedKeys(arg params.Entities) (params.StringsResults, error) { 105 if len(arg.Entities) == 0 { 106 return params.StringsResults{}, nil 107 } 108 results := make([]params.StringsResult, len(arg.Entities)) 109 110 // For now, authorised keys are global, common to all machines. 111 var keys []string 112 config, configErr := api.model.ModelConfig() 113 if configErr == nil { 114 keys = ssh.SplitAuthorisedKeys(config.AuthorizedKeys()) 115 } 116 117 canRead, err := api.getCanRead() 118 if err != nil { 119 return params.StringsResults{}, err 120 } 121 for i, entity := range arg.Entities { 122 tag, err := names.ParseTag(entity.Tag) 123 if err != nil { 124 results[i].Error = common.ServerError(err) 125 continue 126 } 127 // 1. Check permissions 128 if !canRead(tag) { 129 results[i].Error = common.ServerError(common.ErrPerm) 130 continue 131 } 132 // 2. Check entity exists 133 if _, err := api.state.FindEntity(tag); err != nil { 134 if errors.IsNotFound(err) { 135 results[i].Error = common.ServerError(common.ErrPerm) 136 } else { 137 results[i].Error = common.ServerError(err) 138 } 139 continue 140 } 141 // 3. Get keys 142 if configErr == nil { 143 results[i].Result = keys 144 } else { 145 err = configErr 146 } 147 results[i].Error = common.ServerError(err) 148 } 149 return params.StringsResults{Results: results}, nil 150 }