diff --git a/etcdmain/etcd.go b/etcdmain/etcd.go index 87e9b25bb0f..30d8e032ae4 100644 --- a/etcdmain/etcd.go +++ b/etcdmain/etcd.go @@ -218,9 +218,16 @@ func startProxy(cfg *config) error { } cfg.ec.Dir = filepath.Join(cfg.ec.Dir, "proxy") - err = os.MkdirAll(cfg.ec.Dir, fileutil.PrivateDirMode) - if err != nil { - return err + if fileutil.Exist(cfg.ec.Dir) { + err := fileutil.CheckDirPermission(cfg.ec.Dir, fileutil.PrivateDirMode) + if err != nil { + return err + } + } else { + err = os.MkdirAll(cfg.ec.Dir, fileutil.PrivateDirMode) + if err != nil { + return err + } } var peerURLs []string diff --git a/pkg/fileutil/fileutil.go b/pkg/fileutil/fileutil.go index fce5126c695..99297dd2cd8 100644 --- a/pkg/fileutil/fileutil.go +++ b/pkg/fileutil/fileutil.go @@ -65,14 +65,22 @@ func ReadDir(dirpath string) ([]string, error) { // TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory // does not exists. TouchDirAll also ensures the given directory is writable. func TouchDirAll(dir string) error { - // If path is already a directory, MkdirAll does nothing - // and returns nil. - err := os.MkdirAll(dir, PrivateDirMode) - if err != nil { - // if mkdirAll("a/text") and "text" is not - // a directory, this will return syscall.ENOTDIR - return err + // If path is already a directory, MkdirAll does nothing and returns nil, so, + // first check if dir exist with an expected permission mode. + if Exist(dir) { + err := CheckDirPermission(dir, PrivateDirMode) + if err != nil { + return err + } + } else { + err := os.MkdirAll(dir, PrivateDirMode) + if err != nil { + // if mkdirAll("a/text") and "text" is not + // a directory, this will return syscall.ENOTDIR + return err + } } + return IsDirWriteable(dir) } @@ -120,3 +128,22 @@ func ZeroToEnd(f *os.File) error { _, err = f.Seek(off, io.SeekStart) return err } + +// CheckDirPermission checks permission on an existing dir. +// Returns error if dir is empty or exist with a different permission than specified. +func CheckDirPermission(dir string, perm os.FileMode) error { + if !Exist(dir) { + return fmt.Errorf("directory %q empty, cannot check permission.", dir) + } + //check the existing permission on the directory + dirInfo, err := os.Stat(dir) + if err != nil { + return err + } + dirMode := dirInfo.Mode().Perm() + if dirMode != perm { + err = fmt.Errorf("directory %q exist without desired file permission. %q", dir, dirInfo.Mode()) + return err + } + return nil +} diff --git a/pkg/fileutil/fileutil_test.go b/pkg/fileutil/fileutil_test.go index cde2f516ad9..111d6f10cdb 100644 --- a/pkg/fileutil/fileutil_test.go +++ b/pkg/fileutil/fileutil_test.go @@ -163,3 +163,21 @@ func TestZeroToEnd(t *testing.T) { } } } + +func TestDirPermission(t *testing.T) { + tmpdir, err := ioutil.TempDir(os.TempDir(), "foo") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + tmpdir2 := filepath.Join(tmpdir, "testpermission") + // create a new dir with 0700 + if err = CreateDirAll(tmpdir2); err != nil { + t.Fatal(err) + } + // check dir permission with mode different than created dir + if err = CheckDirPermission(tmpdir2, 0600); err == nil { + t.Errorf("expected error, got nil") + } +} diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index 60a2f7a3345..1e31d28dbf5 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -32,6 +32,7 @@ import ( "time" "github.com/coreos/etcd/pkg/tlsutil" + "go.etcd.io/etcd/pkg/fileutil" ) func NewListener(addr, scheme string, tlsinfo *TLSInfo) (l net.Listener, err error) { @@ -101,8 +102,15 @@ func (info TLSInfo) Empty() bool { } func SelfCert(dirpath string, hosts []string, additionalUsages ...x509.ExtKeyUsage) (info TLSInfo, err error) { - if err = os.MkdirAll(dirpath, 0700); err != nil { - return + if fileutil.Exist(dirpath) { + err = fileutil.CheckDirPermission(dirpath, fileutil.PrivateDirMode) + if err != nil { + return + } + } else { + if err = os.MkdirAll(dirpath, fileutil.PrivateDirMode); err != nil { + return + } } certPath := filepath.Join(dirpath, "cert.pem")