Skip to content

Commit

Permalink
add no_decode_secrets provider attribute
Browse files Browse the repository at this point in the history
to disable decoding secret hashes by Junos device
when reading resource data
Fix #688
  • Loading branch information
jeremmfr committed Aug 29, 2024
1 parent 1097994 commit 0d80b85
Show file tree
Hide file tree
Showing 40 changed files with 308 additions and 81 deletions.
4 changes: 4 additions & 0 deletions .changes/issue-688.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!-- markdownlint-disable-file MD013 MD041 -->
FEATURES:

* add **no_decode_secrets** provider attribute to disable decoding secret hashes by Junos device when reading resource data (Fix [#688](https://github.com/jeremmfr/terraform-provider-junos/issues/688))
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ linters-settings:
min-occurrences: 25
# Ignore test files.
ignore-tests: true
gocritic:
enabled-checks:
- ruleguard
settings:
ruleguard:
rules: '${configDir}/internal/ruleguard/*.go'
gocyclo:
# minimal code complexity to report, 30 by default
min-complexity: 100
Expand Down
17 changes: 11 additions & 6 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ The following arguments are supported in the `provider` block:
It can also be sourced from the `JUNOS_GROUP_INTERFACE_DELETE` environment variable.
Defaults to empty.

- **no_decode_secrets** (Optional, Boolean)
Disable decoding secret hashes by Junos device when reading resource data.
So **encoded** secrets need to be set in the resources config to
avoid drift between Terraform config and state.
It can also be enabled from the `JUNOS_NO_DECODE_SECRETS` environment variable and
its value is `1`, `t` or `true`.

-> **Note:**
Two SSH authentication methods (keys / password) are possible and tried with the `sshkey_pem`,
`sshkeyfile` arguments or the keys provided by a SSH agent through the `SSH_AUTH_SOCK`
Expand Down Expand Up @@ -248,9 +255,8 @@ The following arguments are supported in the `provider` block:
line when it should be necessary.
- **junos_null_commit_file**, the skip doesn’t of course concern this resource.

It can also be sourced from the `JUNOS_FAKEUPDATE_ALSO` environment variable and
its value is `true`.
Defaults to `false`.
It can also be enabled from the `JUNOS_FAKEUPDATE_ALSO` environment variable and
its value is `1`, `t` or `true`.

- **fake_delete_also** (Optional, Boolean, **don't use in normal terraform run**)
As with `create` and `fake_create_with_setfile`, when this option is true, the normal
Expand All @@ -264,9 +270,8 @@ The following arguments are supported in the `provider` block:
line when it should be necessary.
- **junos_null_commit_file**, the skip doesn’t of course concern this resource.

It can also be sourced from the `JUNOS_FAKEDELETE_ALSO` environment variable and
its value is `true`.
Defaults to `false`.
It can also be enabled from the `JUNOS_FAKEDELETE_ALSO` environment variable and
its value is `1`, `t` or `true`.

## Interface specifications

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/jeremmfr/go-netconf v0.5.0
github.com/jeremmfr/go-utils v0.12.0
github.com/jeremmfr/junosdecode v1.1.1
github.com/quasilyte/go-ruleguard/dsl v0.3.22
golang.org/x/crypto v0.26.0
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
Expand Down
8 changes: 8 additions & 0 deletions internal/junos/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Client struct {
junosSSHKeyFile string
junosSSHKeyPass string
groupIntDel string
decodeSecrets bool
sleepShort int
sleepLock int
junosCommitConfirmed int
Expand All @@ -41,6 +42,7 @@ func NewClient(ip string) *Client {
junosSSHKeyFile: "",
junosSSHKeyPass: "",
groupIntDel: "",
decodeSecrets: true,
sleepShort: 100,
sleepLock: 10,
junosCommitConfirmed: 0,
Expand Down Expand Up @@ -99,6 +101,12 @@ func (clt *Client) WithGroupInterfaceDelete(groupIntDel string) *Client {
return clt
}

func (clt *Client) WithoutDecodeSecrets() *Client {
clt.decodeSecrets = false

return clt
}

func (clt *Client) WithSleepShort(sleep int) *Client {
clt.sleepShort = sleep

Expand Down
4 changes: 3 additions & 1 deletion internal/junos/client_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (clt *Client) StartNewSession(ctx context.Context) (*Session, error) {
message = "[" + sess.localAddress + "->" + sess.remoteAddress + "]" + message
clt.logFile(message)
}
sess.decodeSecrets = clt.decodeSecrets
sess.sleepLock = clt.sleepLock
sess.sleepShort = clt.sleepShort
sess.sleepSSHClosed = clt.sleepSSHClosed
Expand All @@ -72,7 +73,8 @@ func (clt *Client) StartNewSession(ctx context.Context) (*Session, error) {

func (clt *Client) NewSessionWithoutNetconf(_ context.Context) *Session {
sess := Session{
logFile: clt.logFile,
logFile: clt.logFile,
decodeSecrets: clt.decodeSecrets,
}
if clt.fakeCreateSetFile != "" {
sess.fakeSetFile = clt.appendFakeCreateSetFile
Expand Down
1 change: 1 addition & 0 deletions internal/junos/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const (
EnvFakecreateSetfile = "JUNOS_FAKECREATE_SETFILE"
EnvFakeupdateAlso = "JUNOS_FAKEUPDATE_ALSO"
EnvFakedeleteAlso = "JUNOS_FAKEDELETE_ALSO"
EnvNoDecodeSecrets = "JUNOS_NO_DECODE_SECRETS"

DefaultInterfaceTestAcc = "ge-0/0/3"
DefaultInterfaceTestAcc2 = "ge-0/0/4"
Expand Down
16 changes: 16 additions & 0 deletions internal/junos/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import (
"os"
"time"

"github.com/jeremmfr/terraform-provider-junos/internal/tfdata"
"github.com/jeremmfr/terraform-provider-junos/internal/utils"

"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/jeremmfr/go-netconf/netconf"
"golang.org/x/crypto/ssh"
)
Expand All @@ -22,6 +25,7 @@ type Session struct {
localAddress string
remoteAddress string
logFile func(string)
decodeSecrets bool
fakeSetFile func([]string) error
sleepShort int
sleepLock int
Expand Down Expand Up @@ -326,3 +330,15 @@ func (sess *Session) Close() {
}
}
}

func (sess *Session) JunosDecode(
str, errMsg string,
) (
basetypes.StringValue, error,
) {
if !sess.decodeSecrets {
return types.StringValue(str), nil
}

return tfdata.JunosDecode(str, errMsg)
}
35 changes: 29 additions & 6 deletions internal/providerfwk/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"os"
"strconv"
"strings"

"github.com/jeremmfr/terraform-provider-junos/internal/junos"
"github.com/jeremmfr/terraform-provider-junos/internal/tfdiag"
Expand Down Expand Up @@ -41,6 +40,7 @@ type junosProviderModel struct {
SSHKeyFile types.String `tfsdk:"sshkeyfile"`
SSHKeyPass types.String `tfsdk:"keypass"`
GroupIntDel types.String `tfsdk:"group_interface_delete"`
NoDecodeSecrets types.Bool `tfsdk:"no_decode_secrets"`
CmdSleepShort types.Int64 `tfsdk:"cmd_sleep_short"`
CmdSleepLock types.Int64 `tfsdk:"cmd_sleep_lock"`
CommitConfirmed types.Int64 `tfsdk:"commit_confirmed"`
Expand Down Expand Up @@ -116,6 +116,13 @@ func (p *junosProvider) Schema(
Description: "This is the Junos group used to remove configuration on a physical interface." +
" May also be provided via " + junos.EnvGroupInterfaceDelete + " environment variable.",
},
"no_decode_secrets": schema.BoolAttribute{
Optional: true,
Description: "Disable decoding secret hashes by Junos device when read resource data." +
" So encoded secrets need to be set in resources config to " +
"avoid drift between Terraform config and state." +
" May also be enabled via " + junos.EnvNoDecodeSecrets + " environment variable.",
},
"cmd_sleep_short": schema.Int64Attribute{
Optional: true,
Description: "Milliseconds to wait after Terraform provider executes an action on the Junos device." +
Expand Down Expand Up @@ -197,14 +204,14 @@ func (p *junosProvider) Schema(
Description: "The normal process to update resources skipped to generate set/delete lines, " +
"append them to the same file as `fake_create_with_setfile`, " +
"and respond with a `fake` successful update of resources to Terraform." +
" May also be provided via " + junos.EnvFakeupdateAlso + " environment variable.",
" May also be enabled via " + junos.EnvFakeupdateAlso + " environment variable.",
},
"fake_delete_also": schema.BoolAttribute{
Optional: true,
Description: "The normal process to delete resources skipped to generate delete lines, " +
"append them to the same file as `fake_create_with_setfile`, " +
"and respond with a `fake` successful delete of resources to Terraform." +
" May also be provided via " + junos.EnvFakedeleteAlso + " environment variable.",
" May also be enabled via " + junos.EnvFakedeleteAlso + " environment variable.",
},
},
}
Expand Down Expand Up @@ -316,7 +323,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource
}
}

func (p *junosProvider) Configure(
func (p *junosProvider) Configure( //nolint:gocyclo
ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse,
) {
var config junosProviderModel
Expand Down Expand Up @@ -393,6 +400,14 @@ func (p *junosProvider) Configure(
fmt.Sprintf(instructionUnknownMessage, junos.EnvGroupInterfaceDelete),
)
}
if config.NoDecodeSecrets.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("no_decode_secrets"),
tfdiag.UnknownJunosAttrErrSummary,
unknownValueErrorMessage+"for 'no_decode_secrets' attribute."+
fmt.Sprintf(instructionUnknownMessage, junos.EnvNoDecodeSecrets),
)
}
if config.CmdSleepShort.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("cmd_sleep_short"),
Expand Down Expand Up @@ -602,6 +617,14 @@ func (p *junosProvider) Configure(
client.WithGroupInterfaceDelete(v)
}

if !config.NoDecodeSecrets.IsNull() {
if config.NoDecodeSecrets.ValueBool() {
client.WithoutDecodeSecrets()
}
} else if utils.ParseTrue(os.Getenv(junos.EnvNoDecodeSecrets)) {
client.WithoutDecodeSecrets()
}

client.WithSleepShort(100) // default value for cmd_sleep_short
if !config.CmdSleepShort.IsNull() {
client.WithSleepShort(int(config.CmdSleepShort.ValueInt64()))
Expand Down Expand Up @@ -864,15 +887,15 @@ func (p *junosProvider) Configure(
if config.FakeUpdateAlso.ValueBool() {
client.WithFakeUpdateAlso()
}
} else if v := os.Getenv(junos.EnvFakeupdateAlso); strings.EqualFold(v, "true") || v == "1" {
} else if utils.ParseTrue(os.Getenv(junos.EnvFakeupdateAlso)) {
client.WithFakeUpdateAlso()
}

if !config.FakeDeleteAlso.IsNull() {
if config.FakeDeleteAlso.ValueBool() {
client.WithFakeDeleteAlso()
}
} else if v := os.Getenv(junos.EnvFakedeleteAlso); strings.EqualFold(v, "true") || v == "1" {
} else if utils.ParseTrue(os.Getenv(junos.EnvFakedeleteAlso)) {
client.WithFakeDeleteAlso()
}

Expand Down
2 changes: 1 addition & 1 deletion internal/providerfwk/resource_bgp_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ func (rscData *bgpGroupData) read(
case balt.CutPrefixInString(&itemTrim, "type "):
rscData.Type = types.StringValue(itemTrim)
default:
if err := rscData.bgpAttrData.read(itemTrim); err != nil {
if err := rscData.bgpAttrData.read(itemTrim, junSess); err != nil {
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/providerfwk/resource_bgp_neighbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ func (rscData *bgpNeighborData) read(
break
}
itemTrim := strings.TrimPrefix(item, junos.SetLS)
if err := rscData.bgpAttrData.read(itemTrim); err != nil {
if err := rscData.bgpAttrData.read(itemTrim, junSess); err != nil {
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/providerfwk/resource_eventoptions_destination.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ func (rscData *eventoptionsDestinationData) read(
case balt.CutPrefixInString(&itemTrim, "archive-sites "):
itemTrimFields := strings.Split(itemTrim, " ")
if len(itemTrimFields) > 2 { // <url> password <password>
password, err := tfdata.JunosDecode(strings.Trim(itemTrimFields[2], "\""), "password")
password, err := junSess.JunosDecode(strings.Trim(itemTrimFields[2], "\""), "password")
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/providerfwk/resource_iccp.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func (rscData *iccpData) read(
case balt.CutPrefixInString(&itemTrim, "local-ip-addr "):
rscData.LocalIPAddr = types.StringValue(itemTrim)
case balt.CutPrefixInString(&itemTrim, "authentication-key "):
rscData.AuthenticationKey, err = tfdata.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
rscData.AuthenticationKey, err = junSess.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/providerfwk/resource_iccp_peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ func (rscData *iccpPeerData) read(
}
rscData.RedundancyGroupIDList = append(rscData.RedundancyGroupIDList, value)
case balt.CutPrefixInString(&itemTrim, "authentication-key "):
rscData.AuthenticationKey, err = tfdata.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
rscData.AuthenticationKey, err = junSess.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
if err != nil {
return err
}
Expand Down
8 changes: 5 additions & 3 deletions internal/providerfwk/resource_interface_logical.go
Original file line number Diff line number Diff line change
Expand Up @@ -2897,7 +2897,7 @@ func (rscData *interfaceLogicalData) read(

if len(itemTrimFields) > 1 {
balt.CutPrefixInString(&itemTrim, itemTrimFields[0]+" ")
if err := address.read(itemTrim); err != nil {
if err := address.read(itemTrim, junSess); err != nil {
return err
}
}
Expand Down Expand Up @@ -3028,7 +3028,9 @@ func (rscData *interfaceLogicalData) read(
return nil
}

func (block *interfaceLogicalBlockFamilyInetBlockAddress) read(itemTrim string) (err error) {
func (block *interfaceLogicalBlockFamilyInetBlockAddress) read(
itemTrim string, junSess *junos.Session,
) (err error) {
switch {
case itemTrim == "primary":
block.Primary = types.BoolValue(true)
Expand Down Expand Up @@ -3063,7 +3065,7 @@ func (block *interfaceLogicalBlockFamilyInetBlockAddress) read(itemTrim string)
return err
}
case balt.CutPrefixInString(&itemTrim, "authentication-key "):
vrrpGroup.AuthenticationKey, err = tfdata.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
vrrpGroup.AuthenticationKey, err = junSess.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
if err != nil {
return err
}
Expand Down
10 changes: 6 additions & 4 deletions internal/providerfwk/resource_ospf_area.go
Original file line number Diff line number Diff line change
Expand Up @@ -2315,7 +2315,7 @@ func (rscData *ospfAreaData) read(
interFace.Name = types.StringValue(itemTrimFields[0])
balt.CutPrefixInString(&itemTrim, itemTrimFields[0]+" ")

if err := interFace.read(itemTrim); err != nil {
if err := interFace.read(itemTrim, junSess); err != nil {
return err
}
rscData.Interface = append(rscData.Interface, interFace)
Expand Down Expand Up @@ -2402,10 +2402,12 @@ func (rscData *ospfAreaData) read(
return nil
}

func (block *ospfAreaBlockInterface) read(itemTrim string) (err error) {
func (block *ospfAreaBlockInterface) read(
itemTrim string, junSess *junos.Session,
) (err error) {
switch {
case balt.CutPrefixInString(&itemTrim, "authentication simple-password "):
block.AuthenticationSimplePassword, err = tfdata.JunosDecode(
block.AuthenticationSimplePassword, err = junSess.JunosDecode(
strings.Trim(itemTrim, "\""),
"authentication simple-password",
)
Expand Down Expand Up @@ -2523,7 +2525,7 @@ func (block *ospfAreaBlockInterface) read(itemTrim string) (err error) {

switch {
case balt.CutPrefixInString(&itemTrim, "key "):
authenticationMD5.Key, err = tfdata.JunosDecode(strings.Trim(itemTrim, "\""), "authentication md5 key")
authenticationMD5.Key, err = junSess.JunosDecode(strings.Trim(itemTrim, "\""), "authentication md5 key")
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/providerfwk/resource_rip_neighbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ func (rscData *ripNeighborData) read(
case itemTrim == "any-sender":
rscData.AnySender = types.BoolValue(true)
case balt.CutPrefixInString(&itemTrim, "authentication-key "):
rscData.AuthenticationKey, err = tfdata.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
rscData.AuthenticationKey, err = junSess.JunosDecode(strings.Trim(itemTrim, "\""), "authentication-key")
if err != nil {
return err
}
Expand Down Expand Up @@ -1083,7 +1083,7 @@ func (rscData *ripNeighborData) read(
balt.CutPrefixInString(&itemTrim, itemTrimFields[0]+" ")
switch {
case balt.CutPrefixInString(&itemTrim, "key "):
authenticationSelectiveMD5.Key, err = tfdata.JunosDecode(
authenticationSelectiveMD5.Key, err = junSess.JunosDecode(
strings.Trim(itemTrim, "\""),
"authentication-selective-md5 key",
)
Expand Down
Loading

0 comments on commit 0d80b85

Please sign in to comment.