Enhancements: Example Config File, Default Path, and Error Handling Improvements (#199)
Add default helper config Improving java-spiffe-helper Runner and Config logic Improve error handling in java-spiffe-helper Update README Signed-off-by: Max Lambrecht <maxlambrecht@gmail.com>
This commit is contained in:
parent
56bb734de3
commit
666766a90f
|
|
@ -0,0 +1,25 @@
|
|||
# Example java-spiffe-helper configuration
|
||||
|
||||
# KeyStore Path
|
||||
keyStorePath = keystore.p12
|
||||
|
||||
# Password for the KeyStore
|
||||
keyStorePass = REPLACE_WITH_YOUR_KEYSTORE_PASSWORD
|
||||
|
||||
# Password for the private key within the KeyStore
|
||||
keyPass = REPLACE_WITH_YOUR_PRIVATE_KEY_PASSWORD
|
||||
|
||||
# Path to the TrustStore file
|
||||
trustStorePath = truststore.p12
|
||||
|
||||
# TrustStore Password: Password for the TrustStore
|
||||
trustStorePass = REPLACE_WITH_YOUR_TRUSTSTORE_PASSWORD
|
||||
|
||||
# KeyStore Type: 'pkcs12' (default) or 'jks'
|
||||
keyStoreType = pkcs12
|
||||
|
||||
# Key Alias: Alias of the key within the KeyStore (Default: `spiffe`)
|
||||
keyAlias = spiffe
|
||||
|
||||
# SPIFFE Socket Path: Path to the SPIRE Agent's public API socket
|
||||
spiffeSocketPath = unix:/tmp/spire-agent/public/api.sock
|
||||
|
|
@ -10,15 +10,14 @@ The Helper automatically gets the SVID updates and stores them in the KeyStore a
|
|||
|
||||
On Linux:
|
||||
|
||||
`java -jar java-spiffe-helper-0.7.0-linux-x86_64.jar -c helper.conf`
|
||||
`java -jar java-spiffe-helper-0.8.4-linux-x86_64.jar`
|
||||
|
||||
On Mac OS:
|
||||
|
||||
`java -jar java-spiffe-helper-0.7.0-osx-x86_64.jar -c helper.conf`
|
||||
`java -jar java-spiffe-helper-0.8.4-osx-x86_64.jar`
|
||||
|
||||
Either `-c` or `--config` should be used to pass the path to the config file.
|
||||
|
||||
(The jar can be downloaded from [Github releases](https://github.com/spiffe/java-spiffe/releases/tag/v0.7.0))
|
||||
You can run the utility with the `-c` or `--config` option to specify the path to the configuration file. By default, it
|
||||
will look for a configuration file named `conf/java-spiffe-helper.properties` in the current working directory.
|
||||
|
||||
## Config file
|
||||
|
||||
|
|
@ -39,20 +38,19 @@ spiffeSocketPath = unix:/tmp/agent.sock
|
|||
|
||||
### Configuration Properties
|
||||
|
||||
|Configuration | Description | Default value |
|
||||
|------------------|--------------------------------------------------------------------------------| ------------- |
|
||||
|`keyStorePath` | Path to the Java KeyStore File for storing the Private Key and chain of certs | none |
|
||||
|`keyStorePass` | Password to protect the Java KeyStore File | none |
|
||||
|`keyPass` | Password to protect the Private Key entry in the KeyStore | none |
|
||||
|`trustStorePath` | Path to the Java TrustStore File for storing the trusted bundles | none |
|
||||
|`trustStorePass` | Password to protect the Java TrustStore File | none |
|
||||
|`keyStoreType` | Java KeyStore Type. (`pkcs12` and `jks` are supported). Case insensitive. | pkcs12 |
|
||||
|`keyAlias` | Alias for the Private Key entry | spiffe |
|
||||
|`spiffeSocketPath`| Path the Workload API | Read from the system variable: SPIFFE_ENDPOINT_SOCKET |
|
||||
|
||||
KeyStore and TrustStore **must** be in separate files. If `keyStorePath` and `trustStorePath` points to the same file, an error
|
||||
is shown
|
||||
.
|
||||
| Configuration | Description | Default value |
|
||||
|--------------------|-------------------------------------------------------------------------------|-------------------------------------------------------|
|
||||
| `keyStorePath` | Path to the Java KeyStore File for storing the Private Key and chain of certs | none |
|
||||
| `keyStorePass` | Password to protect the Java KeyStore File | none |
|
||||
| `keyPass` | Password to protect the Private Key entry in the KeyStore | none |
|
||||
| `trustStorePath` | Path to the Java TrustStore File for storing the trusted bundles | none |
|
||||
| `trustStorePass` | Password to protect the Java TrustStore File | none |
|
||||
| `keyStoreType` | Java KeyStore Type. (`pkcs12` and `jks` are supported). Case insensitive. | pkcs12 |
|
||||
| `keyAlias` | Alias for the Private Key entry | spiffe |
|
||||
| `spiffeSocketPath` | Path the Workload API | Read from the system variable: SPIFFE_ENDPOINT_SOCKET |
|
||||
|
||||
KeyStore and TrustStore **must** be in separate files. If `keyStorePath` and `trustStorePath` points to the same file,
|
||||
an error is shown.
|
||||
If the store files do not exist, they are created.
|
||||
|
||||
The default and **recommended KeyStore Type** is `PKCS12`. The same type is used for both KeyStore and TrustStore.
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ import io.spiffe.helper.exception.RunnerException;
|
|||
import io.spiffe.helper.keystore.KeyStoreHelper.KeyStoreOptions;
|
||||
import io.spiffe.helper.keystore.KeyStoreType;
|
||||
import lombok.val;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -16,17 +12,18 @@ import java.io.InputStream;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.Properties;
|
||||
|
||||
class Config {
|
||||
|
||||
private static final String DEFAULT_CONFIG_FILENAME = "conf/java-spiffe-helper.properties";
|
||||
|
||||
static final Option CONFIG_FILE_OPTION =
|
||||
Option.builder("c")
|
||||
.longOpt("config")
|
||||
.hasArg(true)
|
||||
.required(true)
|
||||
.build();
|
||||
.longOpt("config")
|
||||
.hasArg(true)
|
||||
.required(false)
|
||||
.build();
|
||||
|
||||
private Config() {
|
||||
}
|
||||
|
|
@ -42,17 +39,17 @@ class Config {
|
|||
return properties;
|
||||
}
|
||||
|
||||
static String getCliConfigOption(final String... args) throws RunnerException {
|
||||
static String getCliConfigOption(final String... args) throws ParseException {
|
||||
final Options cliOptions = new Options();
|
||||
cliOptions.addOption(CONFIG_FILE_OPTION);
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
try {
|
||||
val cmd = parser.parse(cliOptions, args);
|
||||
return cmd.getOptionValue("config");
|
||||
} catch (ParseException e) {
|
||||
val error = String.format("%s. Use -c, --config <arg>", e.getMessage());
|
||||
throw new RunnerException(error);
|
||||
}
|
||||
|
||||
CommandLine cmd = parser.parse(cliOptions, args);
|
||||
return cmd.getOptionValue("config", getDefaultConfigPath());
|
||||
}
|
||||
|
||||
private static String getDefaultConfigPath() {
|
||||
return Paths.get(System.getProperty("user.dir"), DEFAULT_CONFIG_FILENAME).toString();
|
||||
}
|
||||
|
||||
static KeyStoreOptions createKeyStoreOptions(final Properties properties) {
|
||||
|
|
@ -89,7 +86,7 @@ class Config {
|
|||
static String getProperty(final Properties properties, final String key) {
|
||||
final String value = properties.getProperty(key);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
throw new InvalidParameterException(String.format("Missing value for config property: %s", key));
|
||||
throw new IllegalArgumentException(String.format("Missing value for config property: %s", key));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ import io.spiffe.helper.exception.RunnerException;
|
|||
import io.spiffe.helper.keystore.KeyStoreHelper;
|
||||
import lombok.extern.java.Log;
|
||||
import lombok.val;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.KeyStoreException;
|
||||
|
||||
/**
|
||||
* Entry point of the CLI to run the KeyStoreHelper.
|
||||
* Entry point of the java-spiffe-helper CLI application.
|
||||
*/
|
||||
@Log
|
||||
public class Runner {
|
||||
|
|
@ -20,15 +21,19 @@ public class Runner {
|
|||
private Runner() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry method of the CLI to run the {@link KeyStoreHelper}.
|
||||
* <p>
|
||||
* In the args needs to be passed the config file option as: "-c" and "path_to_config_file"
|
||||
*
|
||||
* @param args contains the option with the config file path
|
||||
* @throws RunnerException is there is an error configuring or creating the KeyStoreHelper.
|
||||
*/
|
||||
public static void main(final String ...args) throws RunnerException {
|
||||
public static void main(final String... args) {
|
||||
try {
|
||||
runApplication(args);
|
||||
} catch (RunnerException e) {
|
||||
log.severe(ExceptionUtils.getStackTrace(e));
|
||||
System.exit(1);
|
||||
} catch (ParseException | IllegalArgumentException e) {
|
||||
log.severe(e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void runApplication(final String... args) throws RunnerException, ParseException {
|
||||
try {
|
||||
val configFilePath = Config.getCliConfigOption(args);
|
||||
val properties = Config.parseConfigFileProperties(Paths.get(configFilePath));
|
||||
|
|
@ -36,8 +41,7 @@ public class Runner {
|
|||
try (val keyStoreHelper = KeyStoreHelper.create(options)) {
|
||||
keyStoreHelper.run(true);
|
||||
}
|
||||
} catch (SocketEndpointAddressException | KeyStoreHelperException | RunnerException | InvalidParameterException | KeyStoreException e) {
|
||||
log.severe(e.getMessage());
|
||||
} catch (SocketEndpointAddressException | KeyStoreHelperException | KeyStoreException e) {
|
||||
throw new RunnerException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,23 +44,29 @@ class KeyStore {
|
|||
|
||||
private java.security.KeyStore loadKeyStore() throws KeyStoreException {
|
||||
try {
|
||||
val keyStore = java.security.KeyStore.getInstance(keyStoreType.value());
|
||||
|
||||
// Initialize KeyStore
|
||||
if (Files.exists(keyStoreFilePath)) {
|
||||
try (final InputStream inputStream = Files.newInputStream(keyStoreFilePath)) {
|
||||
keyStore.load(inputStream, keyStorePassword.toCharArray());
|
||||
}
|
||||
} else {
|
||||
//create new keyStore
|
||||
keyStore.load(null, keyStorePassword.toCharArray());
|
||||
}
|
||||
return keyStore;
|
||||
return loadKeyStoreFromFile();
|
||||
} catch (IOException | NoSuchAlgorithmException | CertificateException e) {
|
||||
throw new KeyStoreException("KeyStore cannot be created", e);
|
||||
}
|
||||
}
|
||||
|
||||
private java.security.KeyStore loadKeyStoreFromFile() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
|
||||
val keyStore = java.security.KeyStore.getInstance(keyStoreType.value());
|
||||
|
||||
// Initialize KeyStore
|
||||
if (Files.exists(keyStoreFilePath)) {
|
||||
try (final InputStream inputStream = Files.newInputStream(keyStoreFilePath)) {
|
||||
keyStore.load(inputStream, keyStorePassword.toCharArray());
|
||||
} catch (IOException e) {
|
||||
throw new KeyStoreException("KeyStore cannot be opened", e);
|
||||
}
|
||||
} else {
|
||||
// Create a new KeyStore if it doesn't exist
|
||||
keyStore.load(null, keyStorePassword.toCharArray());
|
||||
}
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a private key and X.509 certificate chain in a Java KeyStore
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import io.spiffe.helper.exception.RunnerException;
|
|||
import io.spiffe.helper.keystore.KeyStoreHelper;
|
||||
import io.spiffe.helper.keystore.KeyStoreType;
|
||||
import lombok.val;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
|
@ -12,8 +13,7 @@ import java.nio.file.Paths;
|
|||
import java.util.Properties;
|
||||
|
||||
import static io.spiffe.utils.TestUtils.toUri;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ConfigTest {
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ class ConfigTest {
|
|||
try {
|
||||
String option = Config.getCliConfigOption("-c", "test");
|
||||
assertEquals("test", option);
|
||||
} catch (RunnerException e) {
|
||||
} catch (ParseException e) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
|
@ -66,27 +66,28 @@ class ConfigTest {
|
|||
try {
|
||||
String option = Config.getCliConfigOption("--config", "example");
|
||||
assertEquals("example", option);
|
||||
} catch (RunnerException e) {
|
||||
} catch (ParseException e) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCliConfigOption_unknownOption() {
|
||||
try {
|
||||
String option = Config.getCliConfigOption("-a", "test");
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("Unrecognized option: -a. Use -c, --config <arg>", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCliConfigOption_unknownLongOption() {
|
||||
try {
|
||||
Config.getCliConfigOption("--unknown", "example");
|
||||
fail("expected parse exception");
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("Unrecognized option: --unknown. Use -c, --config <arg>", e.getMessage());
|
||||
} catch (ParseException e) {
|
||||
assertTrue(e.getMessage().startsWith("Unrecognized option: --unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCliConfigOption_unknownOption() {
|
||||
try {
|
||||
String option = Config.getCliConfigOption("-a", "test");
|
||||
fail("expected parse exception");
|
||||
} catch (ParseException e) {
|
||||
assertTrue(e.getMessage().startsWith("Unrecognized option: -a"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package io.spiffe.helper.cli;
|
|||
|
||||
import io.spiffe.helper.exception.RunnerException;
|
||||
import lombok.val;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -17,70 +18,70 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
class RunnerTest {
|
||||
|
||||
@Test
|
||||
void test_Main_KeyStorePathIsMissing() throws URISyntaxException {
|
||||
void test_Main_KeyStorePathIsMissing() throws URISyntaxException, RunnerException, ParseException {
|
||||
final Path path = Paths.get(toUri("testdata/cli/missing-keystorepath.conf"));
|
||||
try {
|
||||
Runner.main("-c", path.toString());
|
||||
Runner.runApplication("-c", path.toString());
|
||||
fail("expected exception: property is missing");
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("Missing value for config property: keyStorePath", e.getCause().getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Missing value for config property: keyStorePath", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_Main_KeyStorePassIsMissing() throws URISyntaxException {
|
||||
void test_Main_KeyStorePassIsMissing() throws URISyntaxException, RunnerException, ParseException {
|
||||
final Path path = Paths.get(toUri("testdata/cli/missing-keystorepass.conf"));
|
||||
try {
|
||||
Runner.main("-c", path.toString());
|
||||
Runner.runApplication("-c", path.toString());
|
||||
fail("expected exception: property is missing");
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("Missing value for config property: keyStorePass", e.getCause().getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Missing value for config property: keyStorePass", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_Main_KeyPassIsMissing() throws URISyntaxException {
|
||||
void test_Main_KeyPassIsMissing() throws URISyntaxException, RunnerException, ParseException {
|
||||
final Path path = Paths.get(toUri("testdata/cli/missing-keypass.conf"));
|
||||
try {
|
||||
Runner.main("-c", path.toString());
|
||||
Runner.runApplication("-c", path.toString());
|
||||
fail("expected exception: property is missing");
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("Missing value for config property: keyPass", e.getCause().getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Missing value for config property: keyPass", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_Main_TrustStorePathIsMissing() throws URISyntaxException {
|
||||
void test_Main_TrustStorePathIsMissing() throws URISyntaxException, RunnerException, ParseException {
|
||||
final Path path = Paths.get(toUri("testdata/cli/missing-truststorepath.conf"));
|
||||
try {
|
||||
Runner.main("-c", path.toString());
|
||||
Runner.runApplication("-c", path.toString());
|
||||
fail("expected exception: property is missing");
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("Missing value for config property: trustStorePath", e.getCause().getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Missing value for config property: trustStorePath", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_Main_TrustStorePassIsMissing() throws URISyntaxException {
|
||||
void test_Main_TrustStorePassIsMissing() throws URISyntaxException, RunnerException, ParseException {
|
||||
final Path path = Paths.get(toUri("testdata/cli/missing-truststorepass.conf"));
|
||||
try {
|
||||
Runner.main("-c", path.toString());
|
||||
Runner.runApplication("-c", path.toString());
|
||||
fail("expected exception: property is missing");
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("Missing value for config property: trustStorePass", e.getCause().getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Missing value for config property: trustStorePass", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_Main_throwsExceptionIfTheKeystoreCannotBeCreated() throws URISyntaxException, IOException {
|
||||
void test_Main_throwsExceptionIfTheKeystoreCannotBeCreated() throws URISyntaxException, IOException, ParseException {
|
||||
val file = new File("keystore123.p12");
|
||||
file.createNewFile();
|
||||
|
||||
val configPath = Paths.get(toUri("testdata/cli/correct.conf"));
|
||||
try {
|
||||
Runner.main("-c", configPath.toString());
|
||||
Runner.runApplication("-c", configPath.toString());
|
||||
} catch (RunnerException e) {
|
||||
assertEquals("KeyStore cannot be created", e.getCause().getMessage());
|
||||
assertEquals("KeyStore cannot be opened", e.getCause().getMessage());
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ public class KeyStoreTest {
|
|||
.keyStorePassword("example")
|
||||
.build();
|
||||
} catch (KeyStoreException e) {
|
||||
assertEquals("KeyStore cannot be created", e.getMessage());
|
||||
assertEquals("KeyStore cannot be opened", e.getMessage());
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue