Esempio n. 1
0
// SetUp is defined on the worker.NotifyWatchHandler interface.
func (kw *keyupdaterWorker) SetUp() (watcher.NotifyWatcher, error) {
	// Record the keys Juju knows about.
	// TODO(dfc)
	jujuKeys, err := kw.st.AuthorisedKeys(kw.tag.String())
	if err != nil {
		return nil, errors.LoggedErrorf(logger, "reading Juju ssh keys for %q: %v", kw.tag, err)
	}
	kw.jujuKeys = set.NewStrings(jujuKeys...)

	// Read the keys currently in ~/.ssh/authorised_keys.
	sshKeys, err := ssh.ListKeys(SSHUser, ssh.FullKeys)
	if err != nil {
		return nil, errors.LoggedErrorf(logger, "reading ssh authorized keys for %q: %v", kw.tag, err)
	}
	// Record any keys not added by Juju.
	for _, key := range sshKeys {
		_, comment, err := ssh.KeyFingerprint(key)
		// Also record keys which we cannot parse.
		if err != nil || !strings.HasPrefix(comment, ssh.JujuCommentPrefix) {
			kw.nonJujuKeys = append(kw.nonJujuKeys, key)
		}
	}
	// Write out the ssh authorised keys file to match the current state of the world.
	if err := kw.writeSSHKeys(jujuKeys); err != nil {
		return nil, errors.LoggedErrorf(logger, "adding current Juju keys to ssh authorised keys: %v", err)
	}

	w, err := kw.st.WatchAuthorisedKeys(kw.tag.String())
	if err != nil {
		return nil, errors.LoggedErrorf(logger, "starting key updater worker: %v", err)
	}
	logger.Infof("%q key updater worker started", kw.tag)
	return w, nil
}
Esempio n. 2
0
// runSSHKeyImport uses ssh-import-id to find the ssh keys for the specified key ids.
func runSSHKeyImport(keyIds []string) map[string][]importedSSHKey {
	importResults := make(map[string][]importedSSHKey, len(keyIds))
	for _, keyId := range keyIds {
		keyInfo := []importedSSHKey{}
		output, err := RunSSHImportId(keyId)
		if err != nil {
			keyInfo = append(keyInfo, importedSSHKey{err: err})
			continue
		}
		lines := strings.Split(output, "\n")
		hasKey := false
		for _, line := range lines {
			if !strings.HasPrefix(line, "ssh-") {
				continue
			}
			hasKey = true
			// ignore key comment (e.g., [email protected])
			fingerprint, _, err := ssh.KeyFingerprint(line)
			keyInfo = append(keyInfo, importedSSHKey{
				key:         line,
				fingerprint: fingerprint,
				err:         errors.Annotatef(err, "invalid ssh key for %s", keyId),
			})
		}
		if !hasKey {
			keyInfo = append(keyInfo, importedSSHKey{
				err: errors.Errorf("invalid ssh key id: %s", keyId),
			})
		}
		importResults[keyId] = keyInfo
	}
	return importResults
}
Esempio n. 3
0
// currentKeyDataForDelete gathers data used when deleting ssh keys.
func (api *KeyManagerAPI) currentKeyDataForDelete() (
	keys map[string]string, invalidKeys []string, comments map[string]string, err error) {

	cfg, err := api.state.EnvironConfig()
	if err != nil {
		return nil, nil, nil, fmt.Errorf("reading current key data: %v", err)
	}
	// For now, authorised keys are global, common to all users.
	existingSSHKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys())

	// Build up a map of keys indexed by fingerprint, and fingerprints indexed by comment
	// so we can easily get the key represented by each keyId, which may be either a fingerprint
	// or comment.
	keys = make(map[string]string)
	comments = make(map[string]string)
	for _, key := range existingSSHKeys {
		fingerprint, comment, err := ssh.KeyFingerprint(key)
		if err != nil {
			logger.Debugf("keeping unrecognised existing ssh key %q: %v", key, err)
			invalidKeys = append(invalidKeys, key)
			continue
		}
		keys[fingerprint] = key
		if comment != "" {
			comments[comment] = fingerprint
		}
	}
	return keys, invalidKeys, comments, nil
}
Esempio n. 4
0
func updateAuthorizedKeys(context Context, publicKey string) error {
	// Look for an existing authorized key.
	logger.Infof("setting new authorized key for %q", publicKey)
	keyManager := keymanager.NewClient(context.APIState())

	result, err := keyManager.ListKeys(ssh.FullKeys, config.JujuSystemKey)
	if err != nil {
		return errors.Trace(err)
	}
	if result[0].Error != nil {
		return errors.Trace(result[0].Error)
	}
	keys := result[0].Result

	// Loop through the keys. If we find a key that matches the publicKey
	// then we are good, and done.  If the comment on the key is for the system identity
	// but it is not the same, remove it.
	var keysToRemove []string
	for _, key := range keys {
		// The list of keys returned don't have carriage returns, but the
		// publicKey does, so add one one before testing for equality.
		if (key + "\n") == publicKey {
			logger.Infof("system identity key already in authorized list")
			return nil
		}

		fingerprint, comment, err := ssh.KeyFingerprint(key)
		if err != nil {
			// Log the error, but it doesn't stop us doing what we need to do.
			logger.Errorf("bad key in authorized keys: %v", err)
		} else if comment == config.JujuSystemKey {
			keysToRemove = append(keysToRemove, fingerprint)
		}
	}
	if keysToRemove != nil {
		logger.Infof("removing %d keys", len(keysToRemove))
		results, err := keyManager.DeleteKeys(config.JujuSystemKey, keysToRemove...)
		if err != nil {
			// Log the error but continue.
			logger.Errorf("failed to remove keys: %v", err)
		} else {
			for _, err := range results {
				if err.Error != nil {
					// Log the error but continue.
					logger.Errorf("failed to remove key: %v", err.Error)
				}
			}
		}
	}

	errResults, err := keyManager.AddKeys(config.JujuSystemKey, publicKey)
	if err != nil {
		return errors.Annotate(err, "failed to update authorised keys with new system key")
	}
	if err := errResults[0].Error; err != nil {
		return errors.Annotate(err, "failed to update authorised keys with new system key")
	}
	return nil
}
Esempio n. 5
0
func (s *FingerprintSuite) TestKeyFingerprint(c *gc.C) {
	keys := []sshtesting.SSHKey{
		sshtesting.ValidKeyOne,
		sshtesting.ValidKeyTwo,
		sshtesting.ValidKeyThree,
	}
	for _, k := range keys {
		fingerprint, _, err := ssh.KeyFingerprint(k.Key)
		c.Assert(err, gc.IsNil)
		c.Assert(fingerprint, gc.Equals, k.Fingerprint)
	}
}
Esempio n. 6
0
// currentKeyDataForAdd gathers data used when adding ssh keys.
func (api *KeyManagerAPI) currentKeyDataForAdd() (keys []string, fingerprints set.Strings, err error) {
	fingerprints = make(set.Strings)
	cfg, err := api.state.EnvironConfig()
	if err != nil {
		return nil, nil, fmt.Errorf("reading current key data: %v", err)
	}
	keys = ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys())
	for _, key := range keys {
		fingerprint, _, err := ssh.KeyFingerprint(key)
		if err != nil {
			logger.Warningf("ignoring invalid ssh key %q: %v", key, err)
		}
		fingerprints.Add(fingerprint)
	}
	return keys, fingerprints, nil
}
Esempio n. 7
0
func parseKeys(keys []string, mode ssh.ListMode) (keyInfo []string) {
	for _, key := range keys {
		fingerprint, comment, err := ssh.KeyFingerprint(key)
		if err != nil {
			keyInfo = append(keyInfo, fmt.Sprintf("Invalid key: %v", key))
		} else {
			if mode == ssh.FullKeys {
				keyInfo = append(keyInfo, key)
			} else {
				shortKey := fingerprint
				if comment != "" {
					shortKey += fmt.Sprintf(" (%s)", comment)
				}
				keyInfo = append(keyInfo, shortKey)
			}
		}
	}
	return keyInfo
}
Esempio n. 8
0
// AddKeys adds new authorised ssh keys for the specified user.
func (api *KeyManagerAPI) AddKeys(arg params.ModifyUserSSHKeys) (params.ErrorResults, error) {
	result := params.ErrorResults{
		Results: make([]params.ErrorResult, len(arg.Keys)),
	}
	if len(arg.Keys) == 0 {
		return result, nil
	}

	canWrite, err := api.getCanWrite()
	if err != nil {
		return params.ErrorResults{}, common.ServerError(err)
	}
	if !canWrite(arg.User) {
		return params.ErrorResults{}, common.ServerError(common.ErrPerm)
	}

	// For now, authorised keys are global, common to all users.
	sshKeys, currentFingerprints, err := api.currentKeyDataForAdd()
	if err != nil {
		return params.ErrorResults{}, common.ServerError(fmt.Errorf("reading current key data: %v", err))
	}

	// Ensure we are not going to add invalid or duplicate keys.
	result.Results = make([]params.ErrorResult, len(arg.Keys))
	for i, key := range arg.Keys {
		fingerprint, _, err := ssh.KeyFingerprint(key)
		if err != nil {
			result.Results[i].Error = common.ServerError(fmt.Errorf("invalid ssh key: %s", key))
			continue
		}
		if currentFingerprints.Contains(fingerprint) {
			result.Results[i].Error = common.ServerError(fmt.Errorf("duplicate ssh key: %s", key))
			continue
		}
		sshKeys = append(sshKeys, key)
	}
	err = api.writeSSHKeys(sshKeys)
	if err != nil {
		return params.ErrorResults{}, common.ServerError(err)
	}
	return result, nil
}
Esempio n. 9
0
// runSSHKeyImport uses ssh-import-id to find the ssh keys for the specified key ids.
func runSSHKeyImport(keyIds []string) []importedSSHKey {
	keyInfo := make([]importedSSHKey, len(keyIds))
	for i, keyId := range keyIds {
		output, err := RunSSHImportId(keyId)
		if err != nil {
			keyInfo[i].err = err
			continue
		}
		lines := strings.Split(output, "\n")
		for _, line := range lines {
			if !strings.HasPrefix(line, "ssh-") {
				continue
			}
			keyInfo[i].fingerprint, _, keyInfo[i].err = ssh.KeyFingerprint(line)
			if err == nil {
				keyInfo[i].key = line
			}
		}
		if keyInfo[i].key == "" {
			keyInfo[i].err = fmt.Errorf("invalid ssh key id: %s", keyId)
		}
	}
	return keyInfo
}
Esempio n. 10
0
func (s *FingerprintSuite) TestKeyFingerprintError(c *gc.C) {
	_, _, err := ssh.KeyFingerprint("invalid key")
	c.Assert(err, gc.ErrorMatches, `generating key fingerprint: invalid authorized_key "invalid key"`)
}