diff --git a/.changes/bugfix-2.3.0.md b/.changes/bugfix-2.3.0.md new file mode 100644 index 00000000..86ad8eb5 --- /dev/null +++ b/.changes/bugfix-2.3.0.md @@ -0,0 +1,4 @@ + +BUG FIXES: + +* **resource/junos_system**: fix value validator (also accept `@`, `.`) on `ciphers`, `hostkey_algorithm`, `key_exchange` and `macs` attributes in `ssh` block in `services` block (Fix [#570](https://github.com/jeremmfr/terraform-provider-junos/issues/570)) diff --git a/internal/providerfwk/resource_system.go b/internal/providerfwk/resource_system.go index bcae18b7..ea619f4a 100644 --- a/internal/providerfwk/resource_system.go +++ b/internal/providerfwk/resource_system.go @@ -1037,7 +1037,7 @@ func (rsc *system) Schema( setvalidator.SizeAtLeast(1), setvalidator.ValueStringsAre( stringvalidator.LengthAtLeast(1), - tfvalidator.StringFormat(tfvalidator.DefaultFormat), + tfvalidator.StringFormat(tfvalidator.AlgorithmFormat), ), }, }, @@ -1077,7 +1077,7 @@ func (rsc *system) Schema( setvalidator.SizeAtLeast(1), setvalidator.ValueStringsAre( stringvalidator.LengthAtLeast(1), - tfvalidator.StringFormat(tfvalidator.DefaultFormat), + tfvalidator.StringFormat(tfvalidator.AlgorithmFormat), ), }, }, @@ -1089,7 +1089,7 @@ func (rsc *system) Schema( setvalidator.SizeAtLeast(1), setvalidator.ValueStringsAre( stringvalidator.LengthAtLeast(1), - tfvalidator.StringFormat(tfvalidator.DefaultFormat), + tfvalidator.StringFormat(tfvalidator.AlgorithmFormat), ), }, }, @@ -1108,7 +1108,7 @@ func (rsc *system) Schema( setvalidator.SizeAtLeast(1), setvalidator.ValueStringsAre( stringvalidator.LengthAtLeast(1), - tfvalidator.StringFormat(tfvalidator.DefaultFormat), + tfvalidator.StringFormat(tfvalidator.AlgorithmFormat), ), }, }, @@ -3506,16 +3506,16 @@ func (block *systemBlockServices) configSet() ( configSet = append(configSet, setPrefix+"ssh fingerprint-hash "+v) } for _, v := range block.SSH.HostkeyAlgorithm { - configSet = append(configSet, setPrefix+"ssh hostkey-algorithm "+v.ValueString()) + configSet = append(configSet, setPrefix+"ssh hostkey-algorithm \""+v.ValueString()+"\"") } for _, v := range block.SSH.KeyExchange { - configSet = append(configSet, setPrefix+"ssh key-exchange "+v.ValueString()) + configSet = append(configSet, setPrefix+"ssh key-exchange \""+v.ValueString()+"\"") } if block.SSH.LogKeyChanges.ValueBool() { configSet = append(configSet, setPrefix+"ssh log-key-changes") } for _, v := range block.SSH.Macs { - configSet = append(configSet, setPrefix+"ssh macs "+v.ValueString()) + configSet = append(configSet, setPrefix+"ssh macs \""+v.ValueString()+"\"") } if !block.SSH.MaxPreAuthenticationPackets.IsNull() { configSet = append(configSet, setPrefix+"ssh max-pre-authentication-packets "+ @@ -4354,13 +4354,13 @@ func (block *systemBlockServicesBlockSSH) read(itemTrim string) (err error) { case balt.CutPrefixInString(&itemTrim, "fingerprint-hash "): block.FingerprintHash = types.StringValue(itemTrim) case balt.CutPrefixInString(&itemTrim, "hostkey-algorithm "): - block.HostkeyAlgorithm = append(block.HostkeyAlgorithm, types.StringValue(itemTrim)) + block.HostkeyAlgorithm = append(block.HostkeyAlgorithm, types.StringValue(strings.Trim(itemTrim, "\""))) case balt.CutPrefixInString(&itemTrim, "key-exchange "): - block.KeyExchange = append(block.KeyExchange, types.StringValue(itemTrim)) + block.KeyExchange = append(block.KeyExchange, types.StringValue(strings.Trim(itemTrim, "\""))) case itemTrim == "log-key-changes": block.LogKeyChanges = types.BoolValue(true) case balt.CutPrefixInString(&itemTrim, "macs "): - block.Macs = append(block.Macs, types.StringValue(itemTrim)) + block.Macs = append(block.Macs, types.StringValue(strings.Trim(itemTrim, "\""))) case balt.CutPrefixInString(&itemTrim, "max-pre-authentication-packets "): block.MaxPreAuthenticationPackets, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { diff --git a/internal/providerfwk/resource_system_test.go b/internal/providerfwk/resource_system_test.go index 09497a18..2948ccda 100644 --- a/internal/providerfwk/resource_system_test.go +++ b/internal/providerfwk/resource_system_test.go @@ -99,11 +99,13 @@ func TestAccResourceSystem_basic(t *testing.T) { resource.TestCheckResourceAttr("junos_system.testacc_system", "services.ssh.authentication_order.0", "password"), resource.TestCheckResourceAttr("junos_system.testacc_system", - "services.ssh.ciphers.#", "2"), + "services.ssh.ciphers.#", "3"), resource.TestCheckTypeSetElemAttr("junos_system.testacc_system", "services.ssh.ciphers.*", "aes256-ctr"), resource.TestCheckTypeSetElemAttr("junos_system.testacc_system", "services.ssh.ciphers.*", "aes256-cbc"), + resource.TestCheckTypeSetElemAttr("junos_system.testacc_system", + "services.ssh.ciphers.*", "aes256-gcm@openssh.com"), resource.TestCheckResourceAttr("junos_system.testacc_system", "services.ssh.client_alive_count_max", "10"), resource.TestCheckResourceAttr("junos_system.testacc_system", @@ -121,9 +123,11 @@ func TestAccResourceSystem_basic(t *testing.T) { resource.TestCheckTypeSetElemAttr("junos_system.testacc_system", "services.ssh.key_exchange.*", "ecdh-sha2-nistp256"), resource.TestCheckResourceAttr("junos_system.testacc_system", - "services.ssh.macs.#", "1"), + "services.ssh.macs.#", "2"), resource.TestCheckTypeSetElemAttr("junos_system.testacc_system", "services.ssh.macs.*", "hmac-sha2-256"), + resource.TestCheckTypeSetElemAttr("junos_system.testacc_system", + "services.ssh.macs.*", "hmac-sha2-256-etm@openssh.com"), resource.TestCheckResourceAttr("junos_system.testacc_system", "services.ssh.max_pre_authentication_packets", "10000"), resource.TestCheckResourceAttr("junos_system.testacc_system", diff --git a/internal/providerfwk/testdata/TestAccResourceSystem_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceSystem_basic/1/main.tf index 4e5b88c7..3859d5be 100644 --- a/internal/providerfwk/testdata/TestAccResourceSystem_basic/1/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceSystem_basic/1/main.tf @@ -125,14 +125,14 @@ resource "junos_system" "testacc_system" { } ssh { authentication_order = ["password"] - ciphers = ["aes256-ctr", "aes256-cbc"] + ciphers = ["aes256-ctr", "aes256-cbc", "aes256-gcm@openssh.com"] client_alive_count_max = 10 client_alive_interval = 30 connection_limit = 10 fingerprint_hash = "md5" hostkey_algorithm = ["no-ssh-dss"] key_exchange = ["ecdh-sha2-nistp256"] - macs = ["hmac-sha2-256"] + macs = ["hmac-sha2-256", "hmac-sha2-256-etm@openssh.com"] max_pre_authentication_packets = 10000 max_sessions_per_connection = 100 port = 22 diff --git a/internal/tfvalidator/string_format.go b/internal/tfvalidator/string_format.go index c1a31a6e..cb544055 100644 --- a/internal/tfvalidator/string_format.go +++ b/internal/tfvalidator/string_format.go @@ -20,13 +20,15 @@ const ( InterfaceWithWildcardFormat HexadecimalFormat ASPathRegularExpression + AlgorithmFormat ) func (f stringFormat) invalidRune() func(rune) bool { switch f { case DefaultFormat: return func(r rune) bool { - return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && r != '-' && r != '_' + return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && + r != '-' && r != '_' } case AddressNameFormat: return func(r rune) bool { @@ -58,6 +60,11 @@ func (f stringFormat) invalidRune() func(rune) bool { r != '+' && r != '?' && r != '{' && r != '}' && r != '.' && r != '[' && r != ']' && r != '(' && r != ')' && (r < '0' || r > '9') && r != ' ' } + case AlgorithmFormat: + return func(r rune) bool { + return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && + r != '-' && r != '_' && r != '@' && r != '.' + } default: return func(r rune) bool { return true @@ -81,6 +88,8 @@ func (f stringFormat) String() string { return "A-F or a-f letters and numbers" case ASPathRegularExpression: return "regular expression characters, numbers and spaces" + case AlgorithmFormat: + return "letters, numbers, dashes, underscores, at symbol and dots" default: return "" } diff --git a/internal/tfvalidator/string_format_internal_test.go b/internal/tfvalidator/string_format_internal_test.go index bf18edee..aaf10716 100644 --- a/internal/tfvalidator/string_format_internal_test.go +++ b/internal/tfvalidator/string_format_internal_test.go @@ -101,6 +101,16 @@ func TestStringFormat(t *testing.T) { format: ASPathRegularExpression, expectError: true, }, + "AlgorithmFormat_valid": { + val: types.StringValue("ok@ok.net"), + format: AlgorithmFormat, + expectError: false, + }, + "AlgorithmFormat_invalid": { + val: types.StringValue("not ok@ok.net"), + format: AlgorithmFormat, + expectError: true, + }, } for name, test := range tests {