diff --git a/Cargo.lock b/Cargo.lock index 43f8efaf..b8d083b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,6 +247,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "humantime" version = "2.1.0" @@ -369,6 +375,7 @@ dependencies = [ "digest", "env_logger", "hex", + "hex-literal", "lazy_static", "log", "merge", diff --git a/p11nethsm.example.conf b/p11nethsm.example.conf new file mode 100644 index 00000000..179e8fb4 --- /dev/null +++ b/p11nethsm.example.conf @@ -0,0 +1,51 @@ +# Set this option to true to enable the compatibility option for the C_SetAttributeValue() function. +# This allows the applications using the Java Sun PKCS11 module (like EJBCA) to generate keys. +# When using this, the names given to the keys will be ignored and the keys will have random names. +# Under the hood it will store in memory the name given to the key when calling C_SetAttributeValue(). When a certificate is uploaded it will check if the name was previously passed to C_SetAttributeValue() and translate it to the real name on the NetHSM. +enable_set_attribute_value: false + +# You can set the log file location here. +# If no value is set the module will output to stderr. +# If a value is set it will output to the file. +log_file: /tmp/p11nethsm.log +# Optional log level, acceptable values are Trace, Debug, Info, Warn and Error +log_level: Debug + +# Each "slot" represents a HSM cluster of server that share the same user and keys. +slots: + - label: LocalHSM # Name your NetHSM however you want + description: Local HSM (docker) # Optional description + + # Users connecting to the NetHSM server + operator: + username: "operator" + # If the password starts with `env:`, it will obtain the password from an environment variable: + # password: "env:LOCALHSMPASS" + password: "localpass" + administrator: + username: "admin" + + # List the NetHSM instances + instances: + - url: "https://keyfender:8443/api/v1" # URL to reach the server + # To avoid having to re-open connections on each requests, the module keeps a connection pool to each instance. If the module is used by a multithreaded application, multiple connections can be opened at the same time. + # This configures the maximum number of connections in the pool at the same time. + # Note that this does not limit the total number of open connections. + # Having a degree of parrallelism that is higher than the max number of idle connection can lead overhead as those connections will be closed an re-opened frenquently + max_idle_connections: 10 + # By default, the certificate of the HSM will be validated using the system's root certificate authority. + # When the NetHSM uses a self-signed certificate, it can be verified against an allowed list of sha256 fingerprint of the NetHSM's certificate: + sha256_fingerprints: + - "31:92:8E:A4:5E:16:5C:A7:33:44:E8:E9:8E:64:C4:AE:7B:2A:57:E5:77:43:49:F3:69:C9:8F:C4:2F:3A:3B:6E" + # Alternatively certificate checks can be skipped entirely with danger_insecure_cert option. + # This should be avoided if possible and certainly not used with a productive NetHSM. + # danger_insecure_cert: true + # Configure the network retry mechanism. If absent, no retries are attempted on a network error + retries: + # The number of retries after a network error + count: 3 + # The delay between retries, in integer seconds + delay_seconds: 1 + # Configurable timeout for network operations. If a network operation takes more than, `timeout_seconds`, consider it failed. If `retries` is configured, it will be retried. + # Defaults to infinite + timeout_seconds: 10 diff --git a/pkcs11/Cargo.toml b/pkcs11/Cargo.toml index d734ae3b..9b27ac0e 100644 --- a/pkcs11/Cargo.toml +++ b/pkcs11/Cargo.toml @@ -36,3 +36,6 @@ sha2 = { default-features = false, version = "0.10" } sha1 = { default-features = false, version = "0.10" } digest = { default-features = false, version = "0.10" } rayon = "1.8.0" + +[dev-dependencies] +hex-literal = "0.4.1" diff --git a/pkcs11/src/config/config_file.rs b/pkcs11/src/config/config_file.rs index ed203ed4..91dd9380 100644 --- a/pkcs11/src/config/config_file.rs +++ b/pkcs11/src/config/config_file.rs @@ -64,7 +64,7 @@ pub fn read_configuration() -> Result { merge_configurations(configs) } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum LogLevel { Trace, Debug, @@ -86,7 +86,7 @@ impl From<&LogLevel> for log::LevelFilter { } // representation of the config file to parse -#[derive(Debug, Clone, Serialize, Deserialize, Merge, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Merge, Default, PartialEq)] pub struct P11Config { #[merge(strategy = merge::bool::overwrite_false)] #[serde(default)] @@ -97,13 +97,13 @@ pub struct P11Config { pub slots: Vec, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct RetryConfig { pub count: u32, pub delay_seconds: u64, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HexFingerprint { pub value: Vec, } @@ -148,7 +148,7 @@ impl<'de> Deserialize<'de> for HexFingerprint { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct InstanceConfig { pub url: String, #[serde(default)] @@ -159,7 +159,7 @@ pub struct InstanceConfig { pub max_idle_connections: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct SlotConfig { pub label: String, pub operator: Option, @@ -173,7 +173,7 @@ pub struct SlotConfig { } // An user -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct UserConfig { pub username: String, #[serde(deserialize_with = "deserialize_password", default)] @@ -205,6 +205,7 @@ where #[cfg(test)] mod tests { + use hex_literal::hex; use std::fs; use super::*; @@ -310,4 +311,45 @@ password: "" assert_eq!(config.username, "test"); assert_eq!(config.password, None); } + + #[test] + fn test_deserialize_full_example_config() { + let config = include_str!("../../../p11nethsm.example.conf"); + assert_eq!( + P11Config { + enable_set_attribute_value: false, + log_file: Some("/tmp/p11nethsm.log".into()), + log_level: Some(LogLevel::Debug), + slots: vec![SlotConfig { + label: "LocalHSM".into(), + description: Some("Local HSM (docker)".into()), + operator: Some(UserConfig { + username: "operator".into(), + password: Some("localpass".into()) + }), + administrator: Some(UserConfig { + username: "admin".into(), + password: None + }), + instances: vec![InstanceConfig { + url: "https://keyfender:8443/api/v1".into(), + danger_insecure_cert: false, + sha256_fingerprints: vec![HexFingerprint { + value: hex!( + "31928EA45E165CA73344E8E98E64C4AE7B2A57E5774349F369C98FC42F3A3B6E" + ) + .into() + }], + max_idle_connections: Some(10), + }], + retries: Some(RetryConfig { + count: 3, + delay_seconds: 1 + }), + timeout_seconds: Some(10), + }] + }, + serde_yaml::from_str(config).unwrap() + ); + } }