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  }