Пример #1
0
// LoadClientKeys loads the client SSH keys from the
// specified directory, and caches them as a process-wide
// global. If the directory does not exist, it is created;
// if the directory did not exist, or contains no keys, it
// is populated with a new key pair.
//
// If the directory exists, then all pairs of files where one
// has the same name as the other + ".pub" will be loaded as
// private/public key pairs.
//
// Calls to LoadClientKeys will clear the previously loaded
// keys, and recompute the keys.
func LoadClientKeys(dir string) error {
	clientKeysMutex.Lock()
	defer clientKeysMutex.Unlock()
	dir, err := utils.NormalizePath(dir)
	if err != nil {
		return err
	}
	if _, err := os.Stat(dir); err == nil {
		keys, err := loadClientKeys(dir)
		if err != nil {
			return err
		} else if len(keys) > 0 {
			clientKeys = keys
			return nil
		}
		// Directory exists but contains no keys;
		// fall through and create one.
	}
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}
	keyfile, key, err := generateClientKey(dir)
	if err != nil {
		os.RemoveAll(dir)
		return err
	}
	clientKeys = map[string]ssh.Signer{keyfile: key}
	return nil
}
Пример #2
0
// maybeReadAttrFromFile sets defined[attr] to:
//
// 1) The content of the file defined[attr+"-path"], if that's set
// 2) The value of defined[attr] if it is already set.
// 3) The content of defaultPath if it exists and defined[attr] is unset
// 4) Preserves the content of defined[attr], otherwise
//
// The defined[attr+"-path"] key is always deleted.
func maybeReadAttrFromFile(defined map[string]interface{}, attr, defaultPath string) error {
	pathAttr := attr + "-path"
	path, _ := defined[pathAttr].(string)
	delete(defined, pathAttr)
	hasPath := path != ""
	if !hasPath {
		// No path and attribute is already set; leave it be.
		if s, _ := defined[attr].(string); s != "" {
			return nil
		}
		path = defaultPath
	}
	path, err := utils.NormalizePath(path)
	if err != nil {
		return err
	}
	if !filepath.IsAbs(path) {
		path = osenv.JujuHomePath(path)
	}
	data, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) && !hasPath {
			// If the default path isn't found, it's
			// not an error.
			return nil
		}
		return err
	}
	if len(data) == 0 {
		return fmt.Errorf("file %q is empty", path)
	}
	defined[attr] = string(data)
	return nil
}
Пример #3
0
func opensshOptions(options *Options, commandKind opensshCommandKind) []string {
	args := append([]string{}, opensshCommonOptions...)
	if options == nil {
		options = &Options{}
	}
	if len(options.proxyCommand) > 0 {
		args = append(args, "-o", "ProxyCommand "+utils.CommandString(options.proxyCommand...))
	}
	if !options.passwordAuthAllowed {
		args = append(args, "-o", "PasswordAuthentication no")
	}

	// We must set ServerAliveInterval or the server may
	// think we've become unresponsive on long running
	// command executions such as "apt-get upgrade".
	args = append(args, "-o", "ServerAliveInterval 30")

	if options.allocatePTY {
		args = append(args, "-t", "-t") // twice to force
	}
	if options.knownHostsFile != "" {
		args = append(args, "-o", "UserKnownHostsFile "+utils.CommandString(options.knownHostsFile))
	}
	identities := append([]string{}, options.identities...)
	if pk := PrivateKeyFiles(); len(pk) > 0 {
		// Add client keys as implicit identities
		identities = append(identities, pk...)
	}
	// If any identities are specified, the
	// default ones must be explicitly specified.
	if len(identities) > 0 {
		// Restrict SSH to only the explicitly provided identity files.
		// Otherwise we may run out of authentication attempts if the
		// user has many identity files.
		args = append(args, "-o", "IdentitiesOnly yes")
		for _, identity := range defaultIdentities {
			path, err := utils.NormalizePath(identity)
			if err != nil {
				logger.Warningf("failed to normalize path %q: %v", identity, err)
				continue
			}
			if _, err := os.Stat(path); err == nil {
				identities = append(identities, path)
			}
		}
	}
	for _, identity := range identities {
		args = append(args, "-i", identity)
	}
	if options.port != 0 {
		port := fmt.Sprint(options.port)
		if commandKind == scpKind {
			// scp uses -P instead of -p (-p means preserve).
			args = append(args, "-P", port)
		} else {
			args = append(args, "-p", port)
		}
	}
	return args
}
Пример #4
0
func checkFiles(c *gc.C, obtained, expected []string) {
	var err error
	for i, e := range expected {
		expected[i], err = utils.NormalizePath(e)
		c.Assert(err, jc.ErrorIsNil)
	}
	c.Assert(obtained, jc.SameContents, expected)
}
Пример #5
0
func writeAuthorisedKeys(username string, keys []string) error {
	keyDir := fmt.Sprintf(authKeysDir, username)
	keyDir, err := utils.NormalizePath(keyDir)
	if err != nil {
		return err
	}
	err = os.MkdirAll(keyDir, os.FileMode(0755))
	if err != nil {
		return fmt.Errorf("cannot create ssh key directory: %v", err)
	}
	keyData := strings.Join(keys, "\n") + "\n"

	// Get perms to use on auth keys file
	sshKeyFile := filepath.Join(keyDir, authKeysFile)
	perms := os.FileMode(0644)
	info, err := os.Stat(sshKeyFile)
	if err == nil {
		perms = info.Mode().Perm()
	}

	logger.Debugf("writing authorised keys file %s", sshKeyFile)
	err = utils.AtomicWriteFile(sshKeyFile, []byte(keyData), perms)
	if err != nil {
		return err
	}

	// TODO (wallyworld) - what to do on windows (if anything)
	// TODO(dimitern) - no need to use user.Current() if username
	// is "" - it will use the current user anyway.
	if runtime.GOOS != "windows" {
		// Ensure the resulting authorised keys file has its ownership
		// set to the specified username.
		var u *user.User
		if username == "" {
			u, err = user.Current()
		} else {
			u, err = user.Lookup(username)
		}
		if err != nil {
			return err
		}
		// chown requires ints but user.User has strings for windows.
		uid, err := strconv.Atoi(u.Uid)
		if err != nil {
			return err
		}
		gid, err := strconv.Atoi(u.Gid)
		if err != nil {
			return err
		}
		err = os.Chown(sshKeyFile, uid, gid)
		if err != nil {
			return err
		}
	}
	return nil
}
Пример #6
0
func CreateTestKey(c *gc.C) func(*gc.C) {
	keyFile := fmt.Sprintf("~/.ssh/%s", testKeyFileName)
	keyFilePath, err := utils.NormalizePath(keyFile)
	c.Assert(err, jc.ErrorIsNil)
	err = ioutil.WriteFile(keyFilePath, []byte(testPrivateKey), 400)
	c.Assert(err, jc.ErrorIsNil)
	return func(c *gc.C) {
		os.Remove(keyFilePath)
	}
}
Пример #7
0
// Read returns the contents of the file.
func (f *FileVar) Read(ctx *Context) ([]byte, error) {
	if f.Path == "" {
		return nil, ErrNoPath
	}
	path, err := utils.NormalizePath(f.Path)
	if err != nil {
		return nil, err
	}
	return ioutil.ReadFile(ctx.AbsPath(path))
}
Пример #8
0
// Validate implements environs.EnvironProvider.Validate.
func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
	// Check for valid changes for the base config values.
	if err := config.Validate(cfg, old); err != nil {
		return nil, err
	}
	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
	if err != nil {
		return nil, fmt.Errorf("failed to validate unknown attrs: %v", err)
	}
	localConfig := newEnvironConfig(cfg, validated)
	// Before potentially creating directories, make sure that the
	// root directory has not changed.
	containerType := localConfig.container()
	if old != nil {
		oldLocalConfig, err := provider.newConfig(old)
		if err != nil {
			return nil, fmt.Errorf("old config is not a valid local config: %v", old)
		}
		if containerType != oldLocalConfig.container() {
			return nil, fmt.Errorf("cannot change container from %q to %q",
				oldLocalConfig.container(), containerType)
		}
		if localConfig.rootDir() != oldLocalConfig.rootDir() {
			return nil, fmt.Errorf("cannot change root-dir from %q to %q",
				oldLocalConfig.rootDir(),
				localConfig.rootDir())
		}
		if localConfig.networkBridge() != oldLocalConfig.networkBridge() {
			return nil, fmt.Errorf("cannot change network-bridge from %q to %q",
				oldLocalConfig.rootDir(),
				localConfig.rootDir())
		}
		if localConfig.storagePort() != oldLocalConfig.storagePort() {
			return nil, fmt.Errorf("cannot change storage-port from %v to %v",
				oldLocalConfig.storagePort(),
				localConfig.storagePort())
		}
	}
	// Currently only supported containers are "lxc" and "kvm".
	if containerType != instance.LXC && containerType != instance.KVM {
		return nil, fmt.Errorf("unsupported container type: %q", containerType)
	}
	dir, err := utils.NormalizePath(localConfig.rootDir())
	if err != nil {
		return nil, err
	}
	if dir == "." {
		dir = osenv.JujuHomePath(cfg.Name())
	}
	// Always assign the normalized path.
	localConfig.attrs["root-dir"] = dir

	// Apply the coerced unknown values back into the config.
	return cfg.Apply(localConfig.attrs)
}
Пример #9
0
func authKeysDir(username string) (string, error) {
	homeDir, err := utils.UserHomeDir(username)
	if err != nil {
		return "", err
	}
	homeDir, err = utils.NormalizePath(homeDir)
	if err != nil {
		return "", err
	}
	return filepath.Join(homeDir, ".ssh"), nil
}
Пример #10
0
// Open opens the file.
func (f *FileVar) Open(ctx *Context) (io.ReadCloser, error) {
	if f.Path == "" {
		return nil, ErrNoPath
	}
	if f.IsStdin() {
		return ioutil.NopCloser(ctx.Stdin), nil
	}

	path, err := utils.NormalizePath(f.Path)
	if err != nil {
		return nil, err
	}
	return os.Open(ctx.AbsPath(path))
}
Пример #11
0
func (*fileSuite) TestNormalizePath(c *gc.C) {
	home := filepath.FromSlash(c.MkDir())
	err := utils.SetHome(home)
	c.Assert(err, gc.IsNil)
	// TODO (frankban) bug 1324841: improve the isolation of this suite.
	currentUser, err := user.Current()
	c.Assert(err, gc.IsNil)
	for i, test := range []struct {
		path     string
		expected string
		err      string
	}{{
		path:     filepath.FromSlash("/var/lib/juju"),
		expected: filepath.FromSlash("/var/lib/juju"),
	}, {
		path:     "~/foo",
		expected: filepath.Join(home, "foo"),
	}, {
		path:     "~/foo//../bar",
		expected: filepath.Join(home, "bar"),
	}, {
		path:     "~",
		expected: home,
	}, {
		path:     "~" + currentUser.Username,
		expected: currentUser.HomeDir,
	}, {
		path:     "~" + currentUser.Username + "/foo",
		expected: filepath.Join(currentUser.HomeDir, "foo"),
	}, {
		path:     "~" + currentUser.Username + "/foo//../bar",
		expected: filepath.Join(currentUser.HomeDir, "bar"),
	}, {
		path:     filepath.FromSlash("foo~bar/baz"),
		expected: filepath.FromSlash("foo~bar/baz"),
	}, {
		path: "~foobar/path",
		err:  ".*" + utils.NoSuchUserErrRegexp,
	}} {
		c.Logf("test %d: %s", i, test.path)
		actual, err := utils.NormalizePath(test.path)
		if test.err != "" {
			c.Check(err, gc.ErrorMatches, test.err)
		} else {
			c.Check(err, gc.IsNil)
			c.Check(actual, gc.Equals, test.expected)
		}
	}
}
Пример #12
0
func userPublicSigningKey() (string, error) {
	signingKeyFile := os.Getenv("JUJU_STREAMS_PUBLICKEY_FILE")
	signingKey := ""
	if signingKeyFile != "" {
		path, err := utils.NormalizePath(signingKeyFile)
		if err != nil {
			return "", errors.Annotatef(err, "cannot expand key file path: %s", signingKeyFile)
		}
		b, err := ioutil.ReadFile(path)
		if err != nil {
			return "", errors.Annotatef(err, "invalid public key file: %s", path)
		}
		signingKey = string(b)
	}
	return signingKey, nil
}
Пример #13
0
func (s *ConfigSuite) TestPrepareWithDefaultKeyFile(c *gc.C) {
	ctx := coretesting.Context(c)
	// By default "private-key-path isn't set until after validateConfig has been called.
	attrs := validAttrs().Delete("private-key-path", "private-key")
	keyFilePath, err := utils.NormalizePath(jp.DefaultPrivateKey)
	c.Assert(err, gc.IsNil)
	err = ioutil.WriteFile(keyFilePath, []byte(testPrivateKey), 400)
	c.Assert(err, gc.IsNil)
	defer os.Remove(keyFilePath)
	testConfig := newConfig(c, attrs)
	preparedConfig, err := jp.Provider.Prepare(ctx, testConfig)
	c.Assert(err, gc.IsNil)
	attrs = preparedConfig.Config().AllAttrs()
	c.Check(attrs["private-key-path"], gc.Equals, jp.DefaultPrivateKey)
	c.Check(attrs["private-key"], gc.Equals, testPrivateKey)
}
Пример #14
0
func opensshOptions(options *Options, commandKind opensshCommandKind) []string {
	args := append([]string{}, opensshCommonOptions...)
	if options == nil {
		options = &Options{}
	}
	if len(options.proxyCommand) > 0 {
		args = append(args, "-o", "ProxyCommand "+utils.CommandString(options.proxyCommand...))
	}
	if !options.passwordAuthAllowed {
		args = append(args, "-o", "PasswordAuthentication no")
	}
	if options.allocatePTY {
		args = append(args, "-t", "-t") // twice to force
	}
	identities := append([]string{}, options.identities...)
	if pk := PrivateKeyFiles(); len(pk) > 0 {
		// Add client keys as implicit identities
		identities = append(identities, pk...)
	}
	// If any identities are specified, the
	// default ones must be explicitly specified.
	if len(identities) > 0 {
		for _, identity := range defaultIdentities {
			path, err := utils.NormalizePath(identity)
			if err != nil {
				logger.Warningf("failed to normalize path %q: %v", identity, err)
				continue
			}
			if _, err := os.Stat(path); err == nil {
				identities = append(identities, path)
			}
		}
	}
	for _, identity := range identities {
		args = append(args, "-i", identity)
	}
	if options.port != 0 {
		port := fmt.Sprint(options.port)
		if commandKind == scpKind {
			// scp uses -P instead of -p (-p means preserve).
			args = append(args, "-P", port)
		} else {
			args = append(args, "-p", port)
		}
	}
	return args
}
Пример #15
0
// NewFileStorageReader returns a new storage reader for
// a directory inside the local file system.
func NewFileStorageReader(path string) (reader storage.StorageReader, err error) {
	var p string
	if p, err = utils.NormalizePath(path); err != nil {
		return nil, err
	}
	if p, err = filepath.Abs(p); err != nil {
		return nil, err
	}
	fi, err := os.Stat(p)
	if err != nil {
		return nil, err
	}
	if !fi.Mode().IsDir() {
		return nil, fmt.Errorf("specified source path is not a directory: %s", path)
	}
	return &fileStorageReader{p}, nil
}
Пример #16
0
// ValidateFileAttrValue returns the normalised file path, so
// long as the specified path is valid and not a directory.
func ValidateFileAttrValue(path string) (string, error) {
	if !filepath.IsAbs(path) && !strings.HasPrefix(path, "~") {
		return "", errors.Errorf("file path must be an absolute path: %s", path)
	}
	absPath, err := utils.NormalizePath(path)
	if err != nil {
		return "", err
	}
	info, err := os.Stat(absPath)
	if err != nil {
		return "", errors.Errorf("invalid file path: %s", absPath)
	}
	if info.IsDir() {
		return "", errors.Errorf("file path must be a file: %s", absPath)
	}
	return absPath, nil
}
Пример #17
0
Файл: x509.go Проект: juju/utils
// LoadClientCert generates client cert for x509 authentication
// if the directory files are not already there , if they are already there
// it will load them into memory
func (x *X509) LoadClientCert(certFile, keyFile string) error {
	x.mu.Lock()
	defer x.mu.Unlock()

	b1, key := filepath.Split(keyFile)
	b2, cert := filepath.Split(certFile)
	if strings.Compare(b1, b2) != 0 {
		return fmt.Errorf("Cert and Key base paths dosen't match")
	}

	base, err := utils.NormalizePath(b1)
	if err != nil {
		return err
	}
	logger.Debugf("Init winrm credentials path for the module %s", base)
	logger.Debugf("Init winrm path key %s", keyFile)
	logger.Debugf("Init winrm path cert %s", certFile)

	if err = x.read(base, key, cert); err != nil &&
		err != errNoClientCert &&
		err != errNoX509Folder &&
		err != errNoClientPrivateKey {
		return err
	}

	if err == errNoClientCert ||
		err == errNoX509Folder ||
		err == errNoClientPrivateKey {
		if err = os.RemoveAll(base); err != nil {
			return err
		}
	}

	if err := os.MkdirAll(base, 0700); err != nil {
		return err
	}
	if err = x.write(base, key, cert); err != nil {
		return err
	}

	return nil
}
Пример #18
0
// ReadAttrs reads attributes from the specified files, and then overlays
// the results with the k=v attributes.
func (f *ConfigFlag) ReadAttrs(ctx *cmd.Context) (map[string]interface{}, error) {
	attrs := make(map[string]interface{})
	for _, f := range f.files {
		path, err := utils.NormalizePath(f)
		if err != nil {
			return nil, errors.Trace(err)
		}
		data, err := ioutil.ReadFile(ctx.AbsPath(path))
		if err != nil {
			return nil, errors.Trace(err)
		}
		if err := yaml.Unmarshal(data, &attrs); err != nil {
			return nil, err
		}
	}
	for k, v := range f.attrs {
		attrs[k] = v
	}
	return attrs, nil
}
Пример #19
0
// GetCredentials returns a curated set of credential values for a given cloud.
// The credential key values are read from the credentials store and the provider
// finalises the values to resolve things like json files.
// If region is not specified, the default credential region is used.
func GetCredentials(
	store jujuclient.CredentialGetter, region, credentialName, cloudName, cloudType string,
) (_ *cloud.Credential, chosenCredentialName, regionName string, _ error) {

	credential, credentialName, defaultRegion, err := credentialByName(
		store, cloudName, credentialName,
	)
	if err != nil {
		return nil, "", "", errors.Trace(err)
	}

	regionName = region
	if regionName == "" {
		regionName = defaultRegion
	}

	readFile := func(f string) ([]byte, error) {
		f, err := utils.NormalizePath(f)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return ioutil.ReadFile(f)
	}

	// Finalize credential against schemas supported by the provider.
	provider, err := environs.Provider(cloudType)
	if err != nil {
		return nil, "", "", errors.Trace(err)
	}

	credential, err = cloud.FinalizeCredential(
		*credential, provider.CredentialSchemas(), readFile,
	)
	if err != nil {
		return nil, "", "", errors.Annotatef(
			err, "validating %q credential for cloud %q",
			credentialName, cloudName,
		)
	}
	return credential, credentialName, regionName, nil
}
Пример #20
0
// ReadAuthorizedKeys implements the standard juju behaviour for finding
// authorized_keys. It returns a set of keys in in authorized_keys format
// (see sshd(8) for a description).  If path is non-empty, it names the
// file to use; otherwise the user's .ssh directory will be searched.
// Home directory expansion will be performed on the path if it starts with
// a ~; if the expanded path is relative, it will be interpreted relative
// to $HOME/.ssh.
//
// The result of utils/ssh.PublicKeyFiles will always be prepended to the
// result. In practice, this means ReadAuthorizedKeys never returns an
// error when the call originates in the CLI.
//
// If no SSH keys are found, ReadAuthorizedKeys returns
// ErrNoAuthorizedKeys.
func ReadAuthorizedKeys(ctx *cmd.Context, path string) (string, error) {
	files := ssh.PublicKeyFiles()
	if path == "" {
		files = append(files, "id_dsa.pub", "id_rsa.pub", "identity.pub")
	} else {
		files = append(files, path)
	}
	var firstError error
	var keyData []byte
	for _, f := range files {
		f, err := utils.NormalizePath(f)
		if err != nil {
			if firstError == nil {
				firstError = err
			}
			continue
		}
		if !filepath.IsAbs(f) {
			f = filepath.Join(utils.Home(), ".ssh", f)
		}
		data, err := ioutil.ReadFile(f)
		if err != nil {
			if firstError == nil && !os.IsNotExist(err) {
				firstError = err
			}
			continue
		}
		keyData = append(keyData, bytes.Trim(data, "\n")...)
		keyData = append(keyData, '\n')
		ctx.Verbosef("Adding contents of %q to authorized-keys", f)
	}
	if len(keyData) == 0 {
		if firstError == nil {
			firstError = ErrNoAuthorizedKeys
		}
		return "", firstError
	}
	return string(keyData), nil

}
Пример #21
0
Файл: config.go Проект: bac/juju
// readFileAttr reads the contents of an attribute from a file, if the
// corresponding "-path" attribute is set, or otherwise from a default
// path.
func readFileAttr(attrs map[string]interface{}, key, defaultPath string) (content string, userSpecified bool, _ error) {
	path, ok := attrs[key+"-path"].(string)
	if ok {
		userSpecified = true
	} else {
		path = defaultPath
	}
	absPath, err := utils.NormalizePath(path)
	if err != nil {
		return "", userSpecified, errors.Trace(err)
	}
	if !filepath.IsAbs(absPath) {
		absPath = osenv.JujuXDGDataHomePath(absPath)
	}
	data, err := ioutil.ReadFile(absPath)
	if err != nil {
		return "", userSpecified, errors.Annotatef(err, "%q not set, and could not read from %q", key, path)
	}
	if len(data) == 0 {
		return "", userSpecified, errors.Errorf("file %q is empty", path)
	}
	return string(data), userSpecified, nil
}
Пример #22
0
func readAuthorisedKeys(username string) ([]string, error) {
	keyDir := fmt.Sprintf(authKeysDir, username)
	sshKeyFile, err := utils.NormalizePath(filepath.Join(keyDir, authKeysFile))
	if err != nil {
		return nil, err
	}
	logger.Debugf("reading authorised keys file %s", sshKeyFile)
	keyData, err := ioutil.ReadFile(sshKeyFile)
	if os.IsNotExist(err) {
		return []string{}, nil
	}
	if err != nil {
		return nil, fmt.Errorf("reading ssh authorised keys file: %v", err)
	}
	var keys []string
	for _, key := range strings.Split(string(keyData), "\n") {
		if len(strings.Trim(key, " \r")) == 0 {
			continue
		}
		keys = append(keys, key)
	}
	return keys, nil
}
Пример #23
0
// Validate implements environs.EnvironProvider.Validate.
func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
	// Check for valid changes for the base config values.
	if err := config.Validate(cfg, old); err != nil {
		return nil, err
	}
	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
	if err != nil {
		return nil, errors.Annotatef(err, "failed to validate unknown attrs")
	}
	localConfig := newEnvironConfig(cfg, validated)
	// Set correct default network bridge if needed
	// fix for http://pad.lv/1394450
	localConfig.setDefaultNetworkBridge()
	// Before potentially creating directories, make sure that the
	// root directory has not changed.
	if localConfig.namespace() == "" {
		return nil, errors.New("missing namespace, config not prepared")
	}
	containerType := localConfig.container()
	if old != nil {
		oldLocalConfig, err := provider.newConfig(old)
		if err != nil {
			return nil, errors.Annotatef(err, "old config is not a valid local config: %v", old)
		}
		if containerType != oldLocalConfig.container() {
			return nil, errors.Errorf("cannot change container from %q to %q",
				oldLocalConfig.container(), containerType)
		}
		if localConfig.rootDir() != oldLocalConfig.rootDir() {
			return nil, errors.Errorf("cannot change root-dir from %q to %q",
				oldLocalConfig.rootDir(),
				localConfig.rootDir())
		}
		if localConfig.networkBridge() != oldLocalConfig.networkBridge() {
			return nil, errors.Errorf("cannot change network-bridge from %q to %q",
				oldLocalConfig.rootDir(),
				localConfig.rootDir())
		}
		if localConfig.storagePort() != oldLocalConfig.storagePort() {
			return nil, errors.Errorf("cannot change storage-port from %v to %v",
				oldLocalConfig.storagePort(),
				localConfig.storagePort())
		}
		if localConfig.namespace() != oldLocalConfig.namespace() {
			return nil, errors.Errorf("cannot change namespace from %v to %v",
				oldLocalConfig.namespace(),
				localConfig.namespace())
		}
	}
	// Currently only supported containers are "lxc" and "kvm".
	if containerType != instance.LXC && containerType != instance.KVM {
		return nil, errors.Errorf("unsupported container type: %q", containerType)
	}
	dir, err := utils.NormalizePath(localConfig.rootDir())
	if err != nil {
		return nil, err
	}
	if dir == "." {
		dir = osenv.JujuHomePath(cfg.Name())
	}
	// Always assign the normalized path.
	localConfig.attrs["root-dir"] = dir

	// If the user hasn't already specified a value, set it to the
	// given value.
	defineIfNot := func(keyName string, value interface{}) {
		if _, defined := cfg.AllAttrs()[keyName]; !defined {
			logger.Infof("lxc-clone is enabled. Switching %s to %v", keyName, value)
			localConfig.attrs[keyName] = value
		}
	}

	// If we're cloning, and the user hasn't specified otherwise,
	// prefer to skip update logic.
	if useClone, _ := localConfig.LXCUseClone(); useClone && containerType == instance.LXC {
		defineIfNot("enable-os-refresh-update", true)
		defineIfNot("enable-os-upgrade", false)
	}

	// Apply the coerced unknown values back into the config.
	return cfg.Apply(localConfig.attrs)
}
Пример #24
0
// GetCredentials returns a curated set of credential values for a given cloud.
// The credential key values are read from the credentials store and the provider
// finalises the values to resolve things like json files.
// If region is not specified, the default credential region is used.
func GetCredentials(
	ctx *cmd.Context,
	store jujuclient.CredentialGetter,
	args GetCredentialsParams,
) (_ *cloud.Credential, chosenCredentialName, regionName string, _ error) {

	credential, credentialName, defaultRegion, err := credentialByName(
		store, args.CloudName, args.CredentialName,
	)
	if err != nil {
		return nil, "", "", errors.Trace(err)
	}

	regionName = args.CloudRegion
	if regionName == "" {
		regionName = defaultRegion
		if regionName == "" && len(args.Cloud.Regions) > 0 {
			// No region was specified, use the first region
			// in the list.
			regionName = args.Cloud.Regions[0].Name
		}
	}

	cloudEndpoint := args.Cloud.Endpoint
	cloudIdentityEndpoint := args.Cloud.IdentityEndpoint
	if regionName != "" {
		region, err := cloud.RegionByName(args.Cloud.Regions, regionName)
		if err != nil {
			return nil, "", "", errors.Trace(err)
		}
		cloudEndpoint = region.Endpoint
		cloudIdentityEndpoint = region.IdentityEndpoint
	}

	readFile := func(f string) ([]byte, error) {
		f, err := utils.NormalizePath(f)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return ioutil.ReadFile(f)
	}

	// Finalize credential against schemas supported by the provider.
	provider, err := environs.Provider(args.Cloud.Type)
	if err != nil {
		return nil, "", "", errors.Trace(err)
	}

	credential, err = cloud.FinalizeCredential(
		*credential, provider.CredentialSchemas(), readFile,
	)
	if err != nil {
		return nil, "", "", errors.Annotatef(
			err, "finalizing %q credential for cloud %q",
			credentialName, args.CloudName,
		)
	}

	credential, err = provider.FinalizeCredential(
		ctx, environs.FinalizeCredentialParams{
			Credential:            *credential,
			CloudEndpoint:         cloudEndpoint,
			CloudIdentityEndpoint: cloudIdentityEndpoint,
		},
	)
	if err != nil {
		return nil, "", "", errors.Annotatef(
			err, "finalizing %q credential for cloud %q",
			credentialName, args.CloudName,
		)
	}

	return credential, credentialName, regionName, nil
}
Пример #25
0
func validateConfig(cfg, old *config.Config) (*environConfig, error) {
	// Check for valid changes for the base config values.
	if err := config.Validate(cfg, old); err != nil {
		return nil, err
	}

	newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
	if err != nil {
		return nil, err
	}
	envConfig := &environConfig{cfg, newAttrs}
	// If an old config was supplied, check any immutable fields have not changed.
	if old != nil {
		oldEnvConfig, err := validateConfig(old, nil)
		if err != nil {
			return nil, err
		}
		for _, field := range configImmutableFields {
			if oldEnvConfig.attrs[field] != envConfig.attrs[field] {
				return nil, fmt.Errorf(
					"%s: cannot change from %v to %v",
					field, oldEnvConfig.attrs[field], envConfig.attrs[field],
				)
			}
		}
	}

	// Read env variables to fill in any missing fields.
	for field, envVar := range environmentVariables {
		// If field is not set, get it from env variables
		if fieldValue, ok := envConfig.attrs[field]; !ok || fieldValue == "" {
			localEnvVariable := os.Getenv(envVar)
			if localEnvVariable != "" {
				envConfig.attrs[field] = localEnvVariable
			} else {
				if field != "private-key-path" {
					return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar)
				}
			}
		}
	}

	// Ensure private-key-path is set - if it's not in config or an env var, use a default value.
	if v, ok := envConfig.attrs["private-key-path"]; !ok || v == "" {
		v = os.Getenv(environmentVariables["private-key-path"])
		if v == "" {
			v = DefaultPrivateKey
		}
		envConfig.attrs["private-key-path"] = v
	}
	// Now that we've ensured private-key-path is properly set, we go back and set
	// up the private key - this is used to sign requests.
	if fieldValue, ok := envConfig.attrs["private-key"]; !ok || fieldValue == "" {
		keyFile, err := utils.NormalizePath(envConfig.attrs["private-key-path"].(string))
		if err != nil {
			return nil, err
		}
		privateKey, err := ioutil.ReadFile(keyFile)
		if err != nil {
			return nil, err
		}
		envConfig.attrs["private-key"] = string(privateKey)
	}

	// Check for missing fields.
	for field := range configFields {
		if envConfig.attrs[field] == "" {
			return nil, fmt.Errorf("%s: must not be empty", field)
		}
	}
	return envConfig, nil
}
Пример #26
0
func opensshOptions(options *Options, commandKind opensshCommandKind) []string {
	if options == nil {
		options = &Options{}
	}
	var args []string

	var hostChecks string
	switch options.strictHostKeyChecking {
	case StrictHostChecksYes:
		hostChecks = "yes"
	case StrictHostChecksNo:
		hostChecks = "no"
	case StrictHostChecksAsk:
		hostChecks = "ask"
	default:
		// StrictHostChecksUnset and invalid values are handled the
		// same way (the option doesn't get included).
	}
	if hostChecks != "" {
		args = append(args, "-o", "StrictHostKeyChecking "+hostChecks)
	}

	if len(options.proxyCommand) > 0 {
		args = append(args, "-o", "ProxyCommand "+utils.CommandString(options.proxyCommand...))
	}

	if !options.passwordAuthAllowed {
		args = append(args, "-o", "PasswordAuthentication no")
	}

	// We must set ServerAliveInterval or the server may
	// think we've become unresponsive on long running
	// command executions such as "apt-get upgrade".
	args = append(args, "-o", "ServerAliveInterval 30")

	if options.allocatePTY {
		args = append(args, "-t", "-t") // twice to force
	}
	if options.knownHostsFile != "" {
		args = append(args, "-o", "UserKnownHostsFile "+utils.CommandString(options.knownHostsFile))
	}
	identities := append([]string{}, options.identities...)
	if pk := PrivateKeyFiles(); len(pk) > 0 {
		// Add client keys as implicit identities
		identities = append(identities, pk...)
	}
	// If any identities are specified, the
	// default ones must be explicitly specified.
	if len(identities) > 0 {
		for _, identity := range defaultIdentities {
			path, err := utils.NormalizePath(identity)
			if err != nil {
				logger.Warningf("failed to normalize path %q: %v", identity, err)
				continue
			}
			if _, err := os.Stat(path); err == nil {
				identities = append(identities, path)
			}
		}
	}
	for _, identity := range identities {
		args = append(args, "-i", identity)
	}
	if options.port != 0 {
		port := fmt.Sprint(options.port)
		if commandKind == scpKind {
			// scp uses -P instead of -p (-p means preserve).
			args = append(args, "-P", port)
		} else {
			args = append(args, "-p", port)
		}
	}
	return args
}