// 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 }
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, gc.IsNil) } c.Assert(obtained, jc.SameContents, expected) }
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 }
// 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)) }
func CreateTestKey(c *gc.C) func(*gc.C) { keyFile := fmt.Sprintf("~/.ssh/%s", testKeyFileName) keyFilePath, err := utils.NormalizePath(keyFile) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(keyFilePath, []byte(testPrivateKey), 400) c.Assert(err, gc.IsNil) return func(c *gc.C) { os.Remove(keyFilePath) } }
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) }
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 }
// 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 }
func (*fileSuite) TestNormalizePath(c *gc.C) { currentUser, err := user.Current() c.Assert(err, gc.IsNil) for _, test := range []struct { path string expected string err string }{{ path: "/var/lib/juju", expected: "/var/lib/juju", }, { path: "~/foo", expected: "/home/test-user/foo", }, { path: "~/foo//../bar", expected: "/home/test-user/bar", }, { path: "~", expected: "/home/test-user", }, { path: "~" + currentUser.Username, expected: currentUser.HomeDir, }, { path: "~" + currentUser.Username + "/foo", expected: currentUser.HomeDir + "/foo", }, { path: "~" + currentUser.Username + "/foo//../bar", expected: currentUser.HomeDir + "/bar", }, { path: "~foobar/path", err: "user: unknown user foobar", }} { 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) } } }
// 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. func ReadAuthorizedKeys(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(osenv.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') } if len(keyData) == 0 { if firstError == nil { firstError = fmt.Errorf("no public ssh keys found") } return "", firstError } return string(keyData), nil }
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 }
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 }
// 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 if containerType != instance.KVM { fastOptionAvailable := useFastLXC(containerType) if _, found := localConfig.attrs["lxc-clone"]; !found { localConfig.attrs["lxc-clone"] = fastOptionAvailable } } // Apply the coerced unknown values back into the config. return cfg.Apply(localConfig.attrs) }