This is an automated email from the ASF dual-hosted git repository.

apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 8dcb622f49 IGNITE-19193 Store sensitive CLI config separately (#1942)
8dcb622f49 is described below

commit 8dcb622f493493366c4ef2b29f72badc77b1375b
Author: Ivan Gagarkin <[email protected]>
AuthorDate: Tue Apr 18 10:31:52 2023 +0400

    IGNITE-19193 Store sensitive CLI config separately (#1942)
---
 ...liCommandTestNotInitializedIntegrationBase.java |   3 +-
 .../commands/questions/ItConnectToClusterTest.java |   3 +-
 .../questions/ItConnectToSslClusterTest.java       |   6 +-
 .../cli/config/CachedConfigManagerProvider.java    |   5 +-
 .../ignite/internal/cli/config/CliConfigKeys.java  |  21 ++++
 .../internal/cli/config/ConfigConstants.java       |  31 +++++-
 .../apache/ignite/internal/cli/config/Profile.java |  27 +----
 .../internal/cli/config/ini/IniConfigManager.java  |  90 +++++++++++++--
 .../ignite/internal/cli/config/ini/IniProfile.java |  79 +++++++++++--
 .../ignite/internal/cli/util/OperatingSystem.java  | 106 ++++++++++++++++++
 .../internal/cli/commands/CliCommandTestBase.java  |   5 +-
 .../cli/commands/UrlOptionsNegativeTest.java       |   5 +-
 .../cliconfig/CliConfigCommandTestBase.java        |   3 +-
 .../cliconfig/CliConfigProfileListCommandTest.java |   5 +-
 .../cliconfig/CliConfigProfileShowCommandTest.java |   3 +-
 .../cliconfig/CliConfigShowCommandTest.java        |  12 +-
 .../cli/commands/cliconfig/ConfigManagerTest.java  |  14 ++-
 .../cli/config/ini/IniConfigManagerTest.java       | 124 +++++++++++++++++++++
 .../cliconfig/TestConfigManagerHelper.java         |  26 +++++
 .../cliconfig/TestConfigManagerProvider.java       |  10 +-
 .../src/testFixtures/resources/empty_secret.ini    |   0
 21 files changed, 506 insertions(+), 72 deletions(-)

diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
index 7b6d7c14e0..d5278a6f59 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
@@ -29,7 +29,6 @@ import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper
 import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerProvider;
 import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
 import org.apache.ignite.internal.cli.config.ConfigDefaultValueProvider;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
 import org.apache.ignite.internal.cli.core.repl.Session;
 import 
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
@@ -86,7 +85,7 @@ public class CliCommandTestNotInitializedIntegrationBase 
extends CliIntegrationT
     @BeforeEach
     public void setUp(TestInfo testInfo) throws Exception {
         super.setUp(testInfo);
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createIntegrationTests());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createIntegrationTests());
         cmd = new CommandLine(getCommandClass(), new MicronautFactory(context))
                 .registerConverter(NodeNameOrUrl.class, new 
NodeNameOrUrlConverter(nodeNameRegistry));
         cmd.setDefaultValueProvider(configDefaultValueProvider);
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
index 959cf622e9..e3fad527e6 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
@@ -24,7 +24,6 @@ import java.io.IOException;
 import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper;
 import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.TestStateConfigHelper;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
@@ -64,7 +63,7 @@ class ItConnectToClusterTest extends 
ItConnectToClusterTestBase {
         assertThat(promptBefore).isEqualTo("[disconnected]> ");
 
         // And last connected URL is not equal to the default URL
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createClusterUrlNonDefault());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createClusterUrlNonDefault());
         stateConfigProvider.config = 
TestStateConfigHelper.createLastConnectedDefault();
 
         // And answer to both questions is "y"
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslClusterTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslClusterTest.java
index 34a46dc11a..07336c8407 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslClusterTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslClusterTest.java
@@ -26,7 +26,6 @@ import org.apache.ignite.internal.NodeConfig;
 import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper;
 import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.TestStateConfigHelper;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
@@ -44,8 +43,7 @@ class ItConnectToSslClusterTest extends 
ItConnectToClusterTestBase {
         assertThat(promptBefore).isEqualTo("[disconnected]> ");
 
         // And default URL is HTTPS
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createClusterUrlSsl());
-
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createClusterUrlSsl());
         // And trust store is configured
         
configManagerProvider.configManager.setProperty(CliConfigKeys.REST_TRUST_STORE_PATH.value(),
 NodeConfig.resolvedTruststorePath);
         
configManagerProvider.configManager.setProperty(CliConfigKeys.REST_TRUST_STORE_PASSWORD.value(),
 NodeConfig.trustStorePassword);
@@ -77,7 +75,7 @@ class ItConnectToSslClusterTest extends 
ItConnectToClusterTestBase {
         assertThat(promptBefore).isEqualTo("[disconnected]> ");
 
         // And default URL is HTTPS
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createClusterUrlSsl());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createClusterUrlSsl());
 
         // And trust store is not configured
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
index c00c2353d7..6e621bbb51 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
@@ -17,6 +17,9 @@
 
 package org.apache.ignite.internal.cli.config;
 
+import static 
org.apache.ignite.internal.cli.config.ConfigConstants.getConfigFile;
+import static 
org.apache.ignite.internal.cli.config.ConfigConstants.getSecretConfigFile;
+
 import jakarta.inject.Singleton;
 import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 
@@ -25,7 +28,7 @@ import 
org.apache.ignite.internal.cli.config.ini.IniConfigManager;
  */
 @Singleton
 public class CachedConfigManagerProvider implements ConfigManagerProvider {
-    private final ConfigManager configManager = new 
IniConfigManager(ConfigConstants.getConfigFile());
+    private final ConfigManager configManager = new 
IniConfigManager(getConfigFile(), getSecretConfigFile());
 
     @Override
     public ConfigManager get() {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
index 85d8cc53ec..7fb9888d3f 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
@@ -17,6 +17,10 @@
 
 package org.apache.ignite.internal.cli.config;
 
+import static java.util.stream.Collectors.toUnmodifiableSet;
+
+import java.util.Set;
+
 /** CLI config keys and constants. */
 public enum CliConfigKeys {
 
@@ -53,6 +57,23 @@ public enum CliConfigKeys {
         return value;
     }
 
+    /**
+     * Returns all secret config keys.
+     */
+    public static Set<String> secretConfigKeys() {
+        return Set.of(
+                        REST_KEY_STORE_PASSWORD,
+                        REST_KEY_STORE_PATH,
+                        REST_TRUST_STORE_PASSWORD,
+                        REST_TRUST_STORE_PATH,
+                        BASIC_AUTHENTICATION_USERNAME,
+                        BASIC_AUTHENTICATION_PASSWORD
+                )
+                .stream()
+                .map(CliConfigKeys::value)
+                .collect(toUnmodifiableSet());
+    }
+
     /** Constants for CLI config. */
     public static final class Constants {
         public static final String CLUSTER_URL = "ignite.cluster-endpoint-url";
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ConfigConstants.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ConfigConstants.java
index d0bda13167..4075e1c371 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ConfigConstants.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ConfigConstants.java
@@ -39,11 +39,21 @@ public final class ConfigConstants {
      */
     private static final String CONFIG_FILE_NAME = "defaults";
 
+    /**
+     * Secret configuration file name.
+     */
+    private static final String SECRET_CONFIG_FILE_NAME = "secrets";
+
     /**
      * Environment variable which points to the configuration file.
      */
     private static final String IGNITE_CLI_CONFIG_FILE = 
"IGNITE_CLI_CONFIG_FILE";
 
+    /**
+     * Environment variable which points to the secret configuration file.
+     */
+    private static final String IGNITE_CLI_SECRET_CONFIG_FILE = 
"IGNITE_CLI_SECRET_CONFIG_FILE";
+
     /**
      * Environment variable which points to the CLI logs folder.
      */
@@ -58,9 +68,8 @@ public final class ConfigConstants {
     }
 
     /**
-     * Gets the {@link File} with user-specific configuration file.
-     * The file location can be overridden using {@code 
IGNITE_CLI_CONFIG_FILE} environment variable,
-     * otherwise base directory is specified by the
+     * Gets the {@link File} with user-specific configuration file. The file 
location can be overridden using
+     * {@link ConfigConstants#IGNITE_CLI_CONFIG_FILE} environment variable, 
otherwise base directory is specified by the
      * <a 
href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html";>XDG
 Base Directory Specification</a>
      * and the configuration file name is {@code ignitecli/defaults} under the 
base directory.
      *
@@ -74,6 +83,22 @@ public final class ConfigConstants {
         return 
getConfigRoot().resolve(PARENT_FOLDER_NAME).resolve(CONFIG_FILE_NAME).toFile();
     }
 
+    /**
+     * Gets the {@link File} with user-specific configuration file. The file 
location can be overridden using
+     * {@link ConfigConstants#IGNITE_CLI_SECRET_CONFIG_FILE} environment 
variable, otherwise base directory is specified by the
+     * <a 
href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html";>XDG
 Base Directory Specification</a>
+     * and the configuration file name is {@code ignitecli/secrets} under the 
base directory.
+     *
+     * @return configuration file.
+     */
+    public static File getSecretConfigFile() {
+        String configFile = System.getenv(IGNITE_CLI_SECRET_CONFIG_FILE);
+        if (configFile != null) {
+            return new File(configFile);
+        }
+        return 
getConfigRoot().resolve(PARENT_FOLDER_NAME).resolve(SECRET_CONFIG_FILE_NAME).toFile();
+    }
+
     private static Path getConfigRoot() {
         String xdgConfigHome = System.getenv(XDG_CONFIG_HOME);
         if (xdgConfigHome != null) {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/Profile.java 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/Profile.java
index 790107716e..179427100d 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/Profile.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/Profile.java
@@ -30,21 +30,12 @@ public interface Profile {
      */
     String getName();
 
-    /**
-     * Gets a {@link Config} stored in this profile.
-     *
-     * @return config
-     */
-    Config getConfig();
-
     /**
      * Convenience method to get all properties from this profile.
      *
      * @return map of all properties
      */
-    default Map<String, String> getAll() {
-        return getConfig().getAll();
-    }
+    Map<String, String> getAll();
 
     /**
      * Convenience method to get a property from this profile.
@@ -52,9 +43,7 @@ public interface Profile {
      * @param key property to get
      * @return property value or {@code null} if config doesn't contain this 
property
      */
-    default String getProperty(String key) {
-        return getConfig().getProperty(key);
-    }
+    String getProperty(String key);
 
     /**
      * Convenience method to get a property from this profile.
@@ -64,9 +53,7 @@ public interface Profile {
      *
      * @return property value or {@code defaultValue} if config doesn't 
contain this property
      */
-    default String getProperty(String key, String defaultValue) {
-        return getConfig().getProperty(key, defaultValue);
-    }
+    String getProperty(String key, String defaultValue);
 
     /**
      * Convenience method to set a property to this profile.
@@ -74,16 +61,12 @@ public interface Profile {
      * @param key property to set
      * @param value value to set
      */
-    default void setProperty(String key, String value) {
-        getConfig().setProperty(key, value);
-    }
+    void setProperty(String key, String value);
 
     /**
      * Convenience method to set properties to this profile.
      *
      * @param values map of properties to set
      */
-    default void setProperties(Map<String, String> values) {
-        getConfig().setProperties(values);
-    }
+    void setProperties(Map<String, String> values);
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
index 98ab2f9f8c..3b07171ccf 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
@@ -29,13 +29,19 @@ import static 
org.apache.ignite.internal.cli.config.ConfigConstants.CURRENT_PROF
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
 import java.util.Collection;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import org.apache.ignite.internal.cli.config.ConfigInitializationException;
 import org.apache.ignite.internal.cli.config.ConfigManager;
 import org.apache.ignite.internal.cli.config.Profile;
 import org.apache.ignite.internal.cli.config.ProfileNotFoundException;
+import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
 import org.apache.ignite.internal.cli.logger.CliLoggers;
+import org.apache.ignite.internal.cli.util.OperatingSystem;
 import org.apache.ignite.internal.logger.IgniteLogger;
 
 /**
@@ -48,24 +54,57 @@ public class IniConfigManager implements ConfigManager {
 
     private final IniFile configFile;
 
+    private final IniFile secretConfigFile;
+
     private String currentProfileName;
 
     /**
      * Constructor.
      *
-     * @param file ini file.
+     * @param configFile ini file.
+     * @param secretConfigFile secret ini file.
      */
-    public IniConfigManager(File file) {
+    public IniConfigManager(File configFile, File secretConfigFile) {
+        this.configFile = configFile(configFile);
+        this.secretConfigFile = secretConfigFile(secretConfigFile);
+        this.currentProfileName = findCurrentProfileName(this.configFile);
+    }
+
+    private IniFile configFile(File file) {
         IniFile configFile;
         try {
             configFile = new IniFile(file);
             findCurrentProfileName(configFile);
         } catch (IOException | NoSuchElementException e) {
             LOG.warn("User config is corrupted or doesn't exist.", e);
-            configFile = createDefaultConfig(file);
+            try {
+                configFile = createDefaultConfig(file);
+            } catch (Exception ex) {
+                throw new IgniteCliException("Couldn't create default config", 
ex);
+            }
         }
-        this.configFile = configFile;
-        this.currentProfileName = findCurrentProfileName(configFile);
+        return configFile;
+    }
+
+    private IniFile secretConfigFile(File file) {
+        IniFile configFile;
+        try {
+            if (OperatingSystem.current() != OperatingSystem.WINDOWS) {
+                Set<PosixFilePermission> posixFilePermissions = 
Files.getPosixFilePermissions(file.toPath());
+                if (!secretPermission().equals(posixFilePermissions)) {
+                    throw new IgniteCliException("The secret configuration 
file must have 700 permissions");
+                }
+            }
+            configFile = new IniFile(file);
+        } catch (IOException e) {
+            LOG.warn("User secret config is corrupted or doesn't exist.", e);
+            try {
+                configFile = createDefaultSecretConfig(file);
+            } catch (Exception ex) {
+                throw new IgniteCliException("Couldn't create secret default 
config", ex);
+            }
+        }
+        return configFile;
     }
 
     private static String findCurrentProfileName(IniFile configFile) {
@@ -91,12 +130,25 @@ public class IniConfigManager implements ConfigManager {
         if (section == null) {
             throw new ProfileNotFoundException(profile);
         }
-        return new IniProfile(section, configFile::store);
+
+        IniSection secretSection = secretConfigFile.getSection(profile) == null
+                ? secretConfigFile.createSection(profile)
+                : secretConfigFile.getSection(profile);
+
+        IniConfig config = new IniConfig(section, configFile::store);
+        IniConfig secretConfig = new IniConfig(secretSection, 
secretConfigFile::store);
+        return new IniProfile(section.getName(), config, secretConfig);
     }
 
     @Override
     public Profile createProfile(String profileName) {
-        return new IniProfile(configFile.createSection(profileName), 
configFile::store);
+        IniSection section = configFile.createSection(profileName);
+        IniSection secretSection = secretConfigFile.createSection(profileName);
+
+        IniConfig config = new IniConfig(section, configFile::store);
+        IniConfig secretConfig = new IniConfig(secretSection, 
secretConfigFile::store);
+
+        return new IniProfile(profileName, config, secretConfig);
     }
 
     @Override
@@ -125,6 +177,26 @@ public class IniConfigManager implements ConfigManager {
             IniSection defaultSection = 
ini.createSection(DEFAULT_PROFILE_NAME);
             defaultSection.setProperty(CLUSTER_URL.value(), 
"http://localhost:10300";);
             defaultSection.setProperty(JDBC_URL.value(), 
"jdbc:ignite:thin://127.0.0.1:10800");
+            ini.store();
+            return ini;
+        } catch (IOException e) {
+            throw new ConfigInitializationException(file.getAbsolutePath(), e);
+        }
+    }
+
+    private static IniFile createDefaultSecretConfig(File file) {
+        try {
+            file.getParentFile().mkdirs();
+            file.delete();
+
+            if (OperatingSystem.current() == OperatingSystem.WINDOWS) {
+                Files.createFile(file.toPath());
+            } else {
+                Files.createFile(file.toPath(), 
PosixFilePermissions.asFileAttribute(secretPermission()));
+            }
+
+            IniFile ini = new IniFile(file);
+            IniSection defaultSection = 
ini.createSection(DEFAULT_PROFILE_NAME);
             defaultSection.setProperty(REST_KEY_STORE_PATH.value(), "");
             defaultSection.setProperty(REST_KEY_STORE_PASSWORD.value(), "");
             defaultSection.setProperty(REST_TRUST_STORE_PATH.value(), "");
@@ -137,4 +209,8 @@ public class IniConfigManager implements ConfigManager {
             throw new ConfigInitializationException(file.getAbsolutePath(), e);
         }
     }
+
+    private static Set<PosixFilePermission> secretPermission() {
+        return Set.of(PosixFilePermission.OWNER_READ, 
PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE);
+    }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniProfile.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniProfile.java
index c1b3e5f159..0cabe5e53e 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniProfile.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniProfile.java
@@ -17,30 +17,93 @@
 
 package org.apache.ignite.internal.cli.config.ini;
 
-import org.apache.ignite.internal.cli.config.Config;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.Profile;
 
 /**
  * Implementation of {@link Profile} based on {@link IniSection}.
  */
 public class IniProfile implements Profile {
-    private final IniSection section;
+    private final String name;
     private final IniConfig config;
+    private final IniConfig secretConfig;
 
-    public IniProfile(IniSection section, Runnable saveAction) {
-        this.section = section;
-        this.config = new IniConfig(section, saveAction);
+    /**
+     * Constructor.
+     *
+     * @param name Profile name.
+     * @param config Ini config.
+     * @param secretConfig Ini secret config.
+     */
+    public IniProfile(String name, IniConfig config, IniConfig secretConfig) {
+        this.name = name;
+        this.config = config;
+        this.secretConfig = secretConfig;
     }
 
     /** {@inheritDoc} */
     @Override
     public String getName() {
-        return section.getName();
+        return name;
     }
 
     /** {@inheritDoc} */
     @Override
-    public Config getConfig() {
-        return config;
+    public Map<String, String> getAll() {
+        Map<String, String> all = new HashMap<>(config.getAll());
+        all.putAll(secretConfig.getAll());
+        return all;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getProperty(String key) {
+        if (CliConfigKeys.secretConfigKeys().contains(key)) {
+            return secretConfig.getProperty(key);
+        } else {
+            return config.getProperty(key);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getProperty(String key, String defaultValue) {
+        if (CliConfigKeys.secretConfigKeys().contains(key)) {
+            return secretConfig.getProperty(key, defaultValue);
+        } else {
+            return config.getProperty(key, defaultValue);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setProperty(String key, String value) {
+        if (CliConfigKeys.secretConfigKeys().contains(key)) {
+            secretConfig.setProperty(key, value);
+        } else {
+            config.setProperty(key, value);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setProperties(Map<String, String> values) {
+        Map<Boolean, Map<String, String>> secretToValues = 
values.entrySet().stream()
+                .collect(Collectors.groupingBy(it -> 
CliConfigKeys.secretConfigKeys().contains(it.getKey()),
+                        Collectors.toMap(Entry::getKey, Entry::getValue)));
+
+        Map<String, String> configValues = secretToValues.get(false);
+        if (configValues != null) {
+            config.setProperties(configValues);
+        }
+
+        Map<String, String> secretConfigValues = secretToValues.get(true);
+        if (secretConfigValues != null) {
+            secretConfig.setProperties(secretConfigValues);
+        }
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/util/OperatingSystem.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/util/OperatingSystem.java
new file mode 100644
index 0000000000..d8b8a99e53
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/util/OperatingSystem.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.util;
+
+import java.util.Locale;
+
+/** Utility class for detection OS. */
+public enum OperatingSystem {
+
+    /**
+     * IBM AIX operating system.
+     */
+    AIX,
+
+    /**
+     * FreeBSD operating system.
+     */
+    FREEBSD,
+
+    /**
+     * Linux-based operating system.
+     */
+    LINUX,
+
+    /**
+     * Apple Macintosh operating system (e.g., macOS).
+     */
+    MAC,
+
+    /**
+     * OpenBSD operating system.
+     */
+    OPENBSD,
+
+    /**
+     * Oracle Solaris operating system.
+     */
+    SOLARIS,
+
+    /**
+     * Microsoft Windows operating system.
+     */
+    WINDOWS,
+
+    /**
+     * An operating system other than {@link #AIX}, {@link #FREEBSD}, {@link 
#LINUX}, {@link #MAC}, {@link #OPENBSD}, {@link #SOLARIS}, or
+     * {@link #WINDOWS}.
+     */
+    OTHER;
+
+    private static final OperatingSystem CURRENT_OS = determineCurrentOs();
+
+    public static OperatingSystem current() {
+        return CURRENT_OS;
+    }
+
+    private static OperatingSystem determineCurrentOs() {
+        String osName = System.getProperty("os.name");
+        if (osName == null) {
+            throw new IllegalStateException("Unable to determine current 
operating system: system property 'os.name' is not set.");
+        }
+        return parse(osName);
+    }
+
+    private static OperatingSystem parse(String osName) {
+        osName = osName.toLowerCase(Locale.ENGLISH);
+
+        if (osName.contains("aix")) {
+            return AIX;
+        }
+        if (osName.contains("freebsd")) {
+            return FREEBSD;
+        }
+        if (osName.contains("linux")) {
+            return LINUX;
+        }
+        if (osName.contains("mac")) {
+            return MAC;
+        }
+        if (osName.contains("openbsd")) {
+            return OPENBSD;
+        }
+        if (osName.contains("sunos") || osName.contains("solaris")) {
+            return SOLARIS;
+        }
+        if (osName.contains("win")) {
+            return WINDOWS;
+        }
+        return OTHER;
+    }
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
index a0e767c2c4..a5d5decb29 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
@@ -25,6 +25,7 @@ import 
io.micronaut.test.extensions.junit5.annotation.MicronautTest;
 import jakarta.inject.Inject;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
@@ -102,9 +103,9 @@ public abstract class CliCommandTestBase {
                 .isEqualTo(expectedOutput);
     }
 
-    protected void assertOutputContains(String expectedOutput) {
+    protected void assertOutputContains(String... expectedOutput) {
         assertThat(sout.toString())
-                .as("Expected command output to contain: " + expectedOutput + 
" but was " + sout.toString())
+                .as("Expected command output to contain: " + 
Arrays.toString(expectedOutput) + " but was " + sout.toString())
                 .contains(expectedOutput);
     }
 
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
index 6e9e7d63ae..147fbe3655 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
@@ -65,7 +65,6 @@ import 
org.apache.ignite.internal.cli.commands.unit.UnitStatusCommand;
 import org.apache.ignite.internal.cli.commands.unit.UnitStatusReplCommand;
 import org.apache.ignite.internal.cli.commands.unit.UnitUndeployCommand;
 import org.apache.ignite.internal.cli.commands.unit.UnitUndeployReplCommand;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
 import 
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
 import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
@@ -104,7 +103,7 @@ public class UrlOptionsNegativeTest {
     NodeNameRegistry nodeNameRegistry;
 
     private void setUp(Class<?> cmdClass) {
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithDefaultProfile());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createSectionWithDefaultProfile());
         MicronautFactory factory = new MicronautFactory(context);
         cmd = new CommandLine(cmdClass, factory)
                 .registerConverter(NodeNameOrUrl.class, new 
NodeNameOrUrlConverter(nodeNameRegistry));
@@ -287,7 +286,7 @@ public class UrlOptionsNegativeTest {
 
     @Test
     void testConnectCommandWithoutParametersWithEmptyConfig() {
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createEmptyConfig());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createEmptyConfig());
         setUp(ConnectReplCommand.class);
         cmd.execute();
 
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigCommandTestBase.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigCommandTestBase.java
index 9a6f9b4c89..d9d558fa7b 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigCommandTestBase.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigCommandTestBase.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.cli.commands.cliconfig;
 
 import jakarta.inject.Inject;
 import org.apache.ignite.internal.cli.commands.CliCommandTestBase;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 import org.junit.jupiter.api.BeforeEach;
 
 /**
@@ -31,6 +30,6 @@ public abstract class CliConfigCommandTestBase extends 
CliCommandTestBase {
 
     @BeforeEach
     void configManagerRefresh() {
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithDefaultProfile());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createSectionWithDefaultProfile());
     }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java
index 2ae6a624b6..0385ca5454 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileListCommandTest.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.commands.cliconfig;
 import static org.junit.jupiter.api.Assertions.assertAll;
 
 import 
org.apache.ignite.internal.cli.commands.cliconfig.profile.CliConfigProfileListCommand;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 import org.junit.jupiter.api.Test;
 
 class CliConfigProfileListCommandTest extends CliConfigCommandTestBase {
@@ -43,7 +42,7 @@ class CliConfigProfileListCommandTest extends 
CliConfigCommandTestBase {
 
     @Test
     public void testSingleProfile() {
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createOneSectionWithDefaultProfile());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createOneSectionWithDefaultProfile());
         execute();
 
         String expectedResult = "default" + System.lineSeparator();
@@ -55,7 +54,7 @@ class CliConfigProfileListCommandTest extends 
CliConfigCommandTestBase {
 
     @Test
     public void testEmptyConfig() {
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createEmptyConfig());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createEmptyConfig());
         execute();
 
         String expectedResult = "default" + System.lineSeparator();
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileShowCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileShowCommandTest.java
index dcaaa7fdd4..33e8f1f4f5 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileShowCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigProfileShowCommandTest.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.commands.cliconfig;
 import static org.junit.jupiter.api.Assertions.assertAll;
 
 import 
org.apache.ignite.internal.cli.commands.cliconfig.profile.CliConfigProfileShowCommand;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 import org.junit.jupiter.api.Test;
 
 class CliConfigProfileShowCommandTest extends CliConfigCommandTestBase {
@@ -41,7 +40,7 @@ class CliConfigProfileShowCommandTest extends 
CliConfigCommandTestBase {
 
     @Test
     public void testWithoutDefaultProfile() {
-        configManagerProvider.configManager = new 
IniConfigManager(TestConfigManagerHelper.createSectionWithoutDefaultProfile());
+        
configManagerProvider.setConfigFile(TestConfigManagerHelper.createSectionWithoutDefaultProfile());
         execute();
 
         assertAll(
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java
index 29dfadeb48..04f13910dd 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigShowCommandTest.java
@@ -33,14 +33,16 @@ class CliConfigShowCommandTest extends 
CliConfigCommandTestBase {
     void noKey() {
         execute();
 
-        String expectedResult = "[database]" + System.lineSeparator()
-                + "server=127.0.0.1" + System.lineSeparator()
-                + "port=8080" + System.lineSeparator()
-                + "file=\"apache.ignite\"" + System.lineSeparator();
+        String[] expectedResult = {
+                "[database]" + System.lineSeparator(),
+                "server=127.0.0.1" + System.lineSeparator(),
+                "port=8080" + System.lineSeparator(),
+                "file=\"apache.ignite\"" + System.lineSeparator()
+        };
 
         assertAll(
                 this::assertExitCodeIsZero,
-                () -> assertOutputIs(expectedResult),
+                () -> assertOutputContains(expectedResult),
                 this::assertErrOutputIsEmpty
         );
     }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/ConfigManagerTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/ConfigManagerTest.java
index a3fae86612..b1179ceb94 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/ConfigManagerTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/ConfigManagerTest.java
@@ -27,18 +27,20 @@ class ConfigManagerTest {
     @Test
     public void testSaveLoadConfig() {
         File tempFile = 
TestConfigManagerHelper.createSectionWithDefaultProfile();
-        IniConfigManager configManager = new IniConfigManager(tempFile);
+        File tempSecretFile = 
TestConfigManagerHelper.createEmptySecretConfig();
+        IniConfigManager configManager = new IniConfigManager(tempFile, 
tempSecretFile);
 
         configManager.setProperty("ignite.cluster-endpoint-url", "test");
 
-        IniConfigManager configAfterSave = new IniConfigManager(tempFile);
+        IniConfigManager configAfterSave = new IniConfigManager(tempFile, 
tempSecretFile);
         
assertThat(configAfterSave.getCurrentProperty("ignite.cluster-endpoint-url")).isEqualTo("test");
     }
 
     @Test
     public void testLoadConfigWithoutDefaultProfile() {
         File tempFile = 
TestConfigManagerHelper.createSectionWithoutDefaultProfile();
-        IniConfigManager configManager = new IniConfigManager(tempFile);
+        File tempSecretFile = 
TestConfigManagerHelper.createEmptySecretConfig();
+        IniConfigManager configManager = new IniConfigManager(tempFile, 
tempSecretFile);
 
 
         
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("owner");
@@ -47,7 +49,8 @@ class ConfigManagerTest {
     @Test
     public void testEmptyConfigLoad() {
         File tempFile = TestConfigManagerHelper.createEmptyConfig();
-        IniConfigManager configManager = new IniConfigManager(tempFile);
+        File tempSecretFile = 
TestConfigManagerHelper.createEmptySecretConfig();
+        IniConfigManager configManager = new IniConfigManager(tempFile, 
tempSecretFile);
 
         
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("default");
     }
@@ -56,7 +59,8 @@ class ConfigManagerTest {
     @Test
     public void testRemoveConfigFileOnRuntime() {
         File tempFile = 
TestConfigManagerHelper.createSectionWithDefaultProfile();
-        IniConfigManager configManager = new IniConfigManager(tempFile);
+        File tempSecretFile = 
TestConfigManagerHelper.createEmptySecretConfig();
+        IniConfigManager configManager = new IniConfigManager(tempFile, 
tempSecretFile);
 
         
assertThat(configManager.getCurrentProfile().getName()).isEqualTo("database");
 
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/config/ini/IniConfigManagerTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/config/ini/IniConfigManagerTest.java
new file mode 100644
index 0000000000..475ed2abb0
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/config/ini/IniConfigManagerTest.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.config.ini;
+
+import static java.nio.file.Files.lines;
+import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
+import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import org.junit.jupiter.api.io.TempDir;
+
+class IniConfigManagerTest {
+
+    File configFile;
+    File secretConfigFile;
+
+    @BeforeEach
+    void setUp(@TempDir File configDir) {
+        configFile = new File(configDir, "config.ini");
+        secretConfigFile = new File(configDir, "secret.ini");
+    }
+
+    @Test
+    void managerWritesConfigKeysProperly() throws IOException {
+        // when
+        IniConfigManager manager = new IniConfigManager(configFile, 
secretConfigFile);
+
+        Arrays.stream(CliConfigKeys.values())
+                .map(CliConfigKeys::value)
+                .forEach(it -> {
+                    manager.getCurrentProfile().setProperty(it, "1234");
+                });
+
+        String[] secretKeys = CliConfigKeys.secretConfigKeys().toArray(new 
String[0]);
+
+        // then secret config file contains secret keys
+        List<String> secretConfigFileKeys = readKeysFromFile(secretConfigFile, 
2);
+
+        assertThat(secretConfigFileKeys, hasSize(secretKeys.length));
+        assertThat(secretConfigFileKeys, containsInAnyOrder(secretKeys));
+
+        // and config file does not contain secret keys
+        List<String> configFileKeys = readKeysFromFile(configFile, 3);
+        String[] notSecretConfigKeys = Arrays.stream(CliConfigKeys.values())
+                .map(CliConfigKeys::value)
+                .filter(it -> !CliConfigKeys.secretConfigKeys().contains(it))
+                .toArray(String[]::new);
+
+        assertThat(configFileKeys, hasSize(notSecretConfigKeys.length));
+        assertThat(configFileKeys, containsInAnyOrder(notSecretConfigKeys));
+
+    }
+
+    @Test
+    @DisabledOnOs(OS.WINDOWS)
+    void secretConfigFileIsCreatedWithCorrectPermissions() throws IOException {
+        // when
+        IniConfigManager manager = new IniConfigManager(configFile, 
secretConfigFile);
+
+        // then
+        Set<PosixFilePermission> permissions = 
Files.getPosixFilePermissions(secretConfigFile.toPath());
+        assertThat(permissions, containsInAnyOrder(OWNER_READ, OWNER_WRITE, 
OWNER_EXECUTE));
+    }
+
+    @Test
+    @DisabledOnOs(OS.WINDOWS)
+    void managerValidatesSecretConfigFilePermissions() throws IOException {
+        // when
+        Files.createFile(secretConfigFile.toPath(), 
PosixFilePermissions.asFileAttribute(Set.of(OWNER_READ, GROUP_READ)));
+
+        // then
+        IgniteCliException igniteCliException = 
assertThrows(IgniteCliException.class,
+                () -> new IniConfigManager(configFile, secretConfigFile));
+        assertThat(igniteCliException.getMessage(),
+                containsString("The secret configuration file must have 700 
permissions"));
+    }
+
+    private static List<String> readKeysFromFile(File file, int headerLength) 
throws IOException {
+        try (Stream<String> lines = lines(file.toPath())) {
+            return lines.skip(headerLength) // skip header
+                    .map(it -> it.split("="))
+                    .map(it -> it[0].trim())
+                    .filter(it -> !it.isBlank())
+                    .collect(Collectors.toList());
+        }
+    }
+}
diff --git 
a/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerHelper.java
 
b/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerHelper.java
index b0c97a00a6..6373e03ffb 100644
--- 
a/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerHelper.java
+++ 
b/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerHelper.java
@@ -17,6 +17,10 @@
 
 package org.apache.ignite.internal.cli.commands.cliconfig;
 
+import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -24,13 +28,18 @@ import java.io.InputStream;
 import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
 import org.apache.ignite.internal.cli.config.ConfigManager;
+import org.apache.ignite.internal.cli.util.OperatingSystem;
 
 /**
  * Test factory for {@link ConfigManager}.
  */
 public class TestConfigManagerHelper {
     private static final String EMPTY = "empty.ini";
+    private static final String EMPTY_SECRET = "empty_secret.ini";
     private static final String ONE_SECTION_WITH_DEFAULT_PROFILE = 
"one_section_with_default_profile.ini";
     private static final String TWO_SECTION_WITH_DEFAULT_PROFILE = 
"two_section_with_default_profile.ini";
     private static final String TWO_SECTION_WITHOUT_DEFAULT_PROFILE = 
"two_section_without_default_profile.ini";
@@ -44,6 +53,17 @@ public class TestConfigManagerHelper {
         return copyResourceToTempFile(EMPTY);
     }
 
+    /** Creates and returns the empty secret file config. */
+    public static File createEmptySecretConfig() {
+        File file = copyResourceToTempFile(EMPTY_SECRET);
+        try {
+            setFilePermissions(file, Set.of(OWNER_READ, OWNER_WRITE, 
OWNER_EXECUTE));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return file;
+    }
+
     public static File createOneSectionWithDefaultProfile() {
         return copyResourceToTempFile(ONE_SECTION_WITH_DEFAULT_PROFILE);
     }
@@ -90,4 +110,10 @@ public class TestConfigManagerHelper {
             throw new RuntimeException(e);
         }
     }
+
+    private static void setFilePermissions(File file, Set<PosixFilePermission> 
perms) throws IOException {
+        if (OperatingSystem.current() != OperatingSystem.WINDOWS) {
+            Files.setPosixFilePermissions(file.toPath(), perms);
+        }
+    }
 }
diff --git 
a/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerProvider.java
 
b/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerProvider.java
index 9e78b1b134..4a8e3b5b3b 100644
--- 
a/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerProvider.java
+++ 
b/modules/cli/src/testFixtures/java/org/apache/ignite/internal/cli/commands/cliconfig/TestConfigManagerProvider.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.cli.commands.cliconfig;
 
 import io.micronaut.context.annotation.Replaces;
 import jakarta.inject.Singleton;
+import java.io.File;
 import org.apache.ignite.internal.cli.config.ConfigManager;
 import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
@@ -30,10 +31,17 @@ import 
org.apache.ignite.internal.cli.config.ini.IniConfigManager;
 @Replaces(ConfigManagerProvider.class)
 public class TestConfigManagerProvider implements ConfigManagerProvider {
 
-    public ConfigManager configManager = new 
IniConfigManager(TestConfigManagerHelper.createIntegrationTests());
+    public ConfigManager configManager = new IniConfigManager(
+            TestConfigManagerHelper.createIntegrationTests(),
+            TestConfigManagerHelper.createEmptySecretConfig()
+    );
 
     @Override
     public ConfigManager get() {
         return configManager;
     }
+
+    public void setConfigFile(File configFile) {
+        configManager = new IniConfigManager(configFile, 
TestConfigManagerHelper.createEmptySecretConfig());
+    }
 }
diff --git a/modules/cli/src/testFixtures/resources/empty_secret.ini 
b/modules/cli/src/testFixtures/resources/empty_secret.ini
new file mode 100644
index 0000000000..e69de29bb2

Reply via email to