diff --git a/cmd/common-main.go b/cmd/common-main.go index fc4e2c263..a17c73616 100644 --- a/cmd/common-main.go +++ b/cmd/common-main.go @@ -78,6 +78,42 @@ func loadLoggers() { } +func newConfigDirFromCtx(ctx *cli.Context, option string, getDefaultDir func() string) *ConfigDir { + var dir string + + switch { + case ctx.IsSet(option): + dir = ctx.String(option) + case ctx.GlobalIsSet(option): + dir = ctx.GlobalString(option) + // cli package does not expose parent's option option. Below code is workaround. + if dir == "" || dir == getDefaultDir() { + if ctx.Parent().GlobalIsSet(option) { + dir = ctx.Parent().GlobalString(option) + } + } + default: + // Neither local nor global option is provided. In this case, try to use + // default directory. + dir = getDefaultDir() + if dir == "" { + logger.FatalIf(errInvalidArgument, "%s option must be provided", option) + } + } + + if dir == "" { + logger.FatalIf(errors.New("empty directory"), "%s directory cannot be empty", option) + } + + // Disallow relative paths, figure out absolute paths. + dirAbs, err := filepath.Abs(dir) + logger.FatalIf(err, "Unable to fetch absolute path for %s=%s", option, dir) + + logger.FatalIf(mkdirAllIgnorePerm(dirAbs), "Unable to create directory specified %s=%s", option, dir) + + return &ConfigDir{path: dirAbs} +} + func handleCommonCmdArgs(ctx *cli.Context) { // Get "json" flag from command line argument and @@ -105,36 +141,12 @@ func handleCommonCmdArgs(ctx *cli.Context) { globalCLIContext.Addr = ctx.String("address") } - var configDir string - - switch { - case ctx.IsSet("config-dir"): - configDir = ctx.String("config-dir") - case ctx.GlobalIsSet("config-dir"): - configDir = ctx.GlobalString("config-dir") - // cli package does not expose parent's "config-dir" option. Below code is workaround. - if configDir == "" || configDir == getConfigDir() { - if ctx.Parent().GlobalIsSet("config-dir") { - configDir = ctx.Parent().GlobalString("config-dir") - } - } - default: - // Neither local nor global config-dir option is provided. In this case, try to use - // default config directory. - configDir = getConfigDir() - if configDir == "" { - logger.FatalIf(errors.New("missing option"), "config-dir option must be provided") - } - } - - if configDir == "" { - logger.FatalIf(errors.New("empty directory"), "Configuration directory cannot be empty") - } + // Set all config, certs and CAs directories. + globalConfigDir = newConfigDirFromCtx(ctx, "config-dir", defaultConfigDir.Get) + globalCertsDir = newConfigDirFromCtx(ctx, "certs-dir", defaultCertsDir.Get) + globalCertsCADir = &ConfigDir{path: filepath.Join(globalCertsDir.Get(), certsCADir)} - // Disallow relative paths, figure out absolute paths. - configDirAbs, err := filepath.Abs(configDir) - logger.FatalIf(err, "Unable to fetch absolute path for config directory %s", configDir) - setConfigDir(configDirAbs) + logger.FatalIf(mkdirAllIgnorePerm(globalCertsCADir.Get()), "Unable to create certs CA directory at %s", globalCertsCADir.Get()) } // Parses the given compression exclude list `extensions` or `content-types`. diff --git a/cmd/config-dir.go b/cmd/config-dir.go index 3c1d2b3a0..38dd08596 100644 --- a/cmd/config-dir.go +++ b/cmd/config-dir.go @@ -19,7 +19,6 @@ package cmd import ( "os" "path/filepath" - "sync" homedir "github.com/mitchellh/go-homedir" ) @@ -41,63 +40,9 @@ const ( privateKeyFile = "private.key" ) -// ConfigDir - configuration directory with locking. +// ConfigDir - points to a user set directory. type ConfigDir struct { - sync.Mutex - dir string -} - -// Set - saves given directory as configuration directory. -func (config *ConfigDir) Set(dir string) { - config.Lock() - defer config.Unlock() - - config.dir = dir -} - -// Get - returns current configuration directory. -func (config *ConfigDir) Get() string { - config.Lock() - defer config.Unlock() - - return config.dir -} - -func (config *ConfigDir) getCertsDir() string { - return filepath.Join(config.Get(), certsDir) -} - -// GetCADir - returns certificate CA directory. -func (config *ConfigDir) GetCADir() string { - return filepath.Join(config.getCertsDir(), certsCADir) -} - -// Create - creates configuration directory tree. -func (config *ConfigDir) Create() error { - err := os.MkdirAll(config.GetCADir(), 0700) - // It is possible in kubernetes like deployments this directory - // is already mounted and is not writable, ignore any write errors. - if err != nil { - if os.IsPermission(err) { - err = nil - } - } - return err -} - -// GetMinioConfigFile - returns absolute path of config.json file. -func (config *ConfigDir) GetMinioConfigFile() string { - return filepath.Join(config.Get(), minioConfigFile) -} - -// GetPublicCertFile - returns absolute path of public.crt file. -func (config *ConfigDir) GetPublicCertFile() string { - return filepath.Join(config.getCertsDir(), publicCertFile) -} - -// GetPrivateKeyFile - returns absolute path of private.key file. -func (config *ConfigDir) GetPrivateKeyFile() string { - return filepath.Join(config.getCertsDir(), privateKeyFile) + path string } func getDefaultConfigDir() string { @@ -109,32 +54,54 @@ func getDefaultConfigDir() string { return filepath.Join(homeDir, defaultMinioConfigDir) } -var configDir = &ConfigDir{dir: getDefaultConfigDir()} - -func setConfigDir(dir string) { - configDir.Set(dir) +func getDefaultCertsDir() string { + return filepath.Join(getDefaultConfigDir(), certsDir) } -func getConfigDir() string { - return configDir.Get() +func getDefaultCertsCADir() string { + return filepath.Join(getDefaultCertsDir(), certsCADir) } -func getCADir() string { - return configDir.GetCADir() +var ( + // Default config, certs and CA directories. + defaultConfigDir = &ConfigDir{path: getDefaultConfigDir()} + defaultCertsDir = &ConfigDir{path: getDefaultCertsDir()} + defaultCertsCADir = &ConfigDir{path: getDefaultCertsCADir()} + + // Points to current configuration directory -- deprecated, to be removed in future. + globalConfigDir = defaultConfigDir + // Points to current certs directory set by user with --certs-dir + globalCertsDir = defaultCertsDir + // Points to relative path to certs directory and is /CAs + globalCertsCADir = defaultCertsCADir +) + +// Get - returns current directory. +func (dir *ConfigDir) Get() string { + return dir.path } -func createConfigDir() error { - return configDir.Create() +// Attempts to create all directories, ignores any permission denied errors. +func mkdirAllIgnorePerm(path string) error { + err := os.MkdirAll(path, 0700) + if err != nil { + // It is possible in kubernetes like deployments this directory + // is already mounted and is not writable, ignore any write errors. + if os.IsPermission(err) { + err = nil + } + } + return err } func getConfigFile() string { - return configDir.GetMinioConfigFile() + return filepath.Join(globalConfigDir.Get(), minioConfigFile) } func getPublicCertFile() string { - return configDir.GetPublicCertFile() + return filepath.Join(globalCertsDir.Get(), publicCertFile) } func getPrivateKeyFile() string { - return configDir.GetPrivateKeyFile() + return filepath.Join(globalCertsDir.Get(), privateKeyFile) } diff --git a/cmd/config-migrate.go b/cmd/config-migrate.go index b5868a055..da2641bec 100644 --- a/cmd/config-migrate.go +++ b/cmd/config-migrate.go @@ -231,7 +231,7 @@ func migrateConfig() error { // Version '1' is not supported anymore and deprecated, safe to delete. func purgeV1() error { - configFile := filepath.Join(getConfigDir(), "fsUsers.json") + configFile := filepath.Join(globalConfigDir.Get(), "fsUsers.json") cv1 := &configV1{} _, err := Load(configFile, cv1) diff --git a/cmd/config-migrate_test.go b/cmd/config-migrate_test.go index cef0d24f7..0e5cac061 100644 --- a/cmd/config-migrate_test.go +++ b/cmd/config-migrate_test.go @@ -39,7 +39,7 @@ func TestServerConfigMigrateV1(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(rootPath) - setConfigDir(rootPath) + globalConfigDir = &ConfigDir{path: rootPath} globalObjLayerMutex.Lock() globalObjectAPI = objLayer @@ -77,7 +77,7 @@ func TestServerConfigMigrateInexistentConfig(t *testing.T) { } defer os.RemoveAll(rootPath) - setConfigDir(rootPath) + globalConfigDir = &ConfigDir{path: rootPath} if err := migrateV2ToV3(); err != nil { t.Fatal("migrate v2 to v3 should succeed when no config file is found") @@ -166,7 +166,8 @@ func TestServerConfigMigrateV2toV33(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(rootPath) - setConfigDir(rootPath) + + globalConfigDir = &ConfigDir{path: rootPath} objLayer, fsDir, err := prepareFS() if err != nil { @@ -235,7 +236,8 @@ func TestServerConfigMigrateFaultyConfig(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(rootPath) - setConfigDir(rootPath) + + globalConfigDir = &ConfigDir{path: rootPath} configPath := rootPath + "/" + minioConfigFile // Create a corrupted config file @@ -331,7 +333,8 @@ func TestServerConfigMigrateCorruptedConfig(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(rootPath) - setConfigDir(rootPath) + + globalConfigDir = &ConfigDir{path: rootPath} configPath := rootPath + "/" + minioConfigFile for i := 3; i <= 17; i++ { diff --git a/cmd/gateway-main.go b/cmd/gateway-main.go index 2b1626c54..22aabdbab 100644 --- a/cmd/gateway-main.go +++ b/cmd/gateway-main.go @@ -124,16 +124,13 @@ func StartGateway(ctx *cli.Context, gw Gateway) { // To avoid this error situation we check for port availability. logger.FatalIf(checkPortAvailability(globalMinioPort), "Unable to start the gateway") - // Create certs path. - logger.FatalIf(createConfigDir(), "Unable to create configuration directories") - // Check and load TLS certificates. var err error globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig() logger.FatalIf(err, "Invalid TLS certificate file") // Check and load Root CAs. - globalRootCAs, err = getRootCAs(getCADir()) + globalRootCAs, err = getRootCAs(globalCertsCADir.Get()) logger.FatalIf(err, "Failed to read root CAs (%v)", err) // Handle common env vars. diff --git a/cmd/main.go b/cmd/main.go index 39546249a..d4f830ced 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -31,14 +31,13 @@ import ( var globalFlags = []cli.Flag{ cli.StringFlag{ Name: "config-dir, C", - Value: getConfigDir(), - Usage: func() string { - usage := "Path to configuration directory." - if getConfigDir() == "" { - usage = usage + " This option must be set." - } - return usage - }(), + Value: defaultConfigDir.Get(), + Usage: "[DEPRECATED] Path to legacy configuration directory.", + }, + cli.StringFlag{ + Name: "certs-dir, S", + Value: defaultCertsDir.Get(), + Usage: "Path to certs directory.", }, cli.BoolFlag{ Name: "quiet", diff --git a/cmd/server-main.go b/cmd/server-main.go index e7a018d46..a95181641 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -207,16 +207,13 @@ func serverMain(ctx *cli.Context) { // Handle all server command args. serverHandleCmdArgs(ctx) - // Create certs path. - logger.FatalIf(createConfigDir(), "Unable to initialize configuration files") - // Check and load TLS certificates. var err error globalPublicCerts, globalTLSCerts, globalIsSSL, err = getTLSConfig() logger.FatalIf(err, "Unable to load the TLS configuration") // Check and load Root CAs. - globalRootCAs, err = getRootCAs(getCADir()) + globalRootCAs, err = getRootCAs(globalCertsCADir.Get()) logger.FatalIf(err, "Failed to read root CAs (%v)", err) // Handle all server environment vars. diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index 28290936b..ff629c8f8 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -404,7 +404,7 @@ func StartTestServer(t TestErrHandler, instanceType string) TestServer { // Sets the global config path to empty string. func resetGlobalConfigPath() { - setConfigDir("") + globalConfigDir = &ConfigDir{path: ""} } // sets globalObjectAPI to `nil`. diff --git a/docs/config/README.md b/docs/config/README.md index 40232d505..ac2388ada 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -2,17 +2,19 @@ ## Configuration Directory -The default configuration directory is `${HOME}/.minio`. Till the release `RELEASE.2018-08-02T23-11-36Z`, Minio server configuration file (`config.json`) was stored in the configuration directory. However for releases beyond `RELEASE.2018-08-18T03-49-57Z`, the configuration file (only), has been migrated to the storage back-end (storage back-end is the directory passed to Minio server while starting the server). +Till Minio release `RELEASE.2018-08-02T23-11-36Z`, Minio server configuration file (`config.json`) was stored in the configuration directory specified by `--config-dir` or defaulted to `${HOME}/.minio`. However from releases after `RELEASE.2018-08-18T03-49-57Z`, the configuration file (only), has been migrated to the storage backend (storage backend is the directory passed to Minio server while starting the server). -You can override the default configuration directory using `--config-dir` command-line option. Please note that this won't have an effect on the `config.json` file as it is always stored on the backend storage, along with data. Minio server generates a new `config.json` with auto-generated access credentials when its started for the first time. +You can specify the location of your existing config using `--config-dir`, Minio will migrate the `config.json` to your backend storage. Your current `config.json` will be renamed upon successful migration as `config.json.deprecated` in your current `--config-dir`. All your existing configurations are honored after this migration. + +Additionally `--config-dir` is now a legacy option which will is scheduled for removal in future, so please update your local startup, ansible scripts accordingly. ```sh -minio server --config-dir /etc/minio /data +minio server /data ``` ### Certificate Directory -TLS certificates are stored under ``${HOME}/.minio/certs`` directory. You need to place certificates here to enable `HTTPS` based access. Read more about [How to secure access to Minio server with TLS](https://docs.minio.io/docs/how-to-secure-access-to-minio-server-with-tls). +TLS certificates by default are stored under ``${HOME}/.minio/certs`` directory. You need to place certificates here to enable `HTTPS` based access. Read more about [How to secure access to Minio server with TLS](https://docs.minio.io/docs/how-to-secure-access-to-minio-server-with-tls). Following is the directory structure for Minio server with TLS certificates. @@ -25,6 +27,7 @@ $ tree ~/.minio │   └── public.crt ``` +You can provide a custom certs directory using `--certs-dir` command line option. ### Accessing configuration file @@ -32,8 +35,6 @@ All configuration changes can be made using [`mc admin config` get/set commands] #### Editing configuration file fields - - ##### Get current configuration for Minio deployment ```sh @@ -62,6 +63,8 @@ The `mc admin` config API will evolve soon to be able to configure specific fiel |``credential.accessKey`` | _string_ | Access key of minimum 3 characters in length. You may override this field with `MINIO_ACCESS_KEY` environment variable.| |``credential.secretKey`` | _string_ | Secret key of minimum 8 characters in length. You may override this field with `MINIO_SECRET_KEY` environment variable.| +> NOTE: In distributed setup it is mandatory to use environment variables `MINIO_ACCESS_KEY` and `MINIO_SECRET_KEY` for credentials. + Example: ```sh diff --git a/docs/multi-tenancy/README.md b/docs/multi-tenancy/README.md index 1a0337e14..f10359e1a 100644 --- a/docs/multi-tenancy/README.md +++ b/docs/multi-tenancy/README.md @@ -15,9 +15,9 @@ To host multiple tenants on a single machine, run one Minio Server per tenant wi Use the following commands to host 3 tenants on a single drive: ```sh -minio --config-dir ~/tenant1 server --address :9001 /data/tenant1 -minio --config-dir ~/tenant2 server --address :9002 /data/tenant2 -minio --config-dir ~/tenant3 server --address :9003 /data/tenant3 +minio server --address :9001 /data/tenant1 +minio server --address :9002 /data/tenant2 +minio server --address :9003 /data/tenant3 ``` ![Example-1](https://github.com/minio/minio/blob/master/docs/screenshots/Example-1.jpg?raw=true) @@ -27,9 +27,9 @@ minio --config-dir ~/tenant3 server --address :9003 /data/tenant3 Use the following commands to host 3 tenants on multiple drives: ```sh -minio --config-dir ~/tenant1 server --address :9001 /disk1/data/tenant1 /disk2/data/tenant1 /disk3/data/tenant1 /disk4/data/tenant1 -minio --config-dir ~/tenant2 server --address :9002 /disk1/data/tenant2 /disk2/data/tenant2 /disk3/data/tenant2 /disk4/data/tenant2 -minio --config-dir ~/tenant3 server --address :9003 /disk1/data/tenant3 /disk2/data/tenant3 /disk3/data/tenant3 /disk4/data/tenant3 +minio server --address :9001 /disk1/data/tenant1 /disk2/data/tenant1 /disk3/data/tenant1 /disk4/data/tenant1 +minio server --address :9002 /disk1/data/tenant2 /disk2/data/tenant2 /disk3/data/tenant2 /disk4/data/tenant2 +minio server --address :9003 /disk1/data/tenant3 /disk2/data/tenant3 /disk3/data/tenant3 /disk4/data/tenant3 ``` ![Example-2](https://github.com/minio/minio/blob/master/docs/screenshots/Example-2.jpg?raw=true) @@ -45,15 +45,15 @@ Use the following commands to host 3 tenants on a 4-node distributed configurati ```sh export MINIO_ACCESS_KEY= export MINIO_SECRET_KEY= -minio --config-dir ~/tenant1 server --address :9001 http://192.168.10.11/data/tenant1 http://192.168.10.12/data/tenant1 http://192.168.10.13/data/tenant1 http://192.168.10.14/data/tenant1 +minio server --address :9001 http://192.168.10.11/data/tenant1 http://192.168.10.12/data/tenant1 http://192.168.10.13/data/tenant1 http://192.168.10.14/data/tenant1 export MINIO_ACCESS_KEY= export MINIO_SECRET_KEY= -minio --config-dir ~/tenant2 server --address :9002 http://192.168.10.11/data/tenant2 http://192.168.10.12/data/tenant2 http://192.168.10.13/data/tenant2 http://192.168.10.14/data/tenant2 +minio server --address :9002 http://192.168.10.11/data/tenant2 http://192.168.10.12/data/tenant2 http://192.168.10.13/data/tenant2 http://192.168.10.14/data/tenant2 export MINIO_ACCESS_KEY= export MINIO_SECRET_KEY= -minio --config-dir ~/tenant3 server --address :9003 http://192.168.10.11/data/tenant3 http://192.168.10.12/data/tenant3 http://192.168.10.13/data/tenant3 http://192.168.10.14/data/tenant3 +minio server --address :9003 http://192.168.10.11/data/tenant3 http://192.168.10.12/data/tenant3 http://192.168.10.13/data/tenant3 http://192.168.10.14/data/tenant3 ``` **Note:** Execute the commands on all 4 nodes. diff --git a/docs/tls/README.md b/docs/tls/README.md index 37c986e3b..4e4c82853 100644 --- a/docs/tls/README.md +++ b/docs/tls/README.md @@ -15,9 +15,11 @@ Install Minio Server using the instructions in the [Minio Quickstart Guide](http This section describes how to use a private key and public certificate that have been obtained from a certificate authority (CA). If these files have not been obtained, skip to [3. Generate Self-signed Certificates](#generate-use-self-signed-keys-certificates) or generate them with [Let's Encrypt](https://letsencrypt.org) using these instructions: [https://docs.minio.io/docs/generate-let-s-encypt-certificate-using-concert-for-minio](https://docs.minio.io/docs/). -Copy the existing private key and public certificate to the `certs` directory within the Minio configuration directory. The default configuration directory is: -* **Linux:** `${HOME}/.minio/` -* **Windows:** `%%USERPROFILE%%\.minio\` +Copy the existing private key and public certificate to the `certs` directory. The default certs directory is: +* **Linux:** `${HOME}/.minio/certs` +* **Windows:** `%%USERPROFILE%%\.minio\certs` + +> NOTE: Location of custom certs directory can be specified using `--certs-dir` command line option. **Note:** * The key and certificate files must be appended with `.key` and `.crt`, respectively.