Lokasi ngalangkungan proxy:   [ UP ]  
[Ngawartoskeun bug]   [Panyetelan cookie]                
Skip to content

Commit 96c20f0

Browse files
committed
communicator/{ssh,winrm}: seed random script paths
Without a seed, the "random" script path locations for the remote-exec provisioner were actually deterministic! Every rand.Int31() would return the same pseudorandom chain starting w/ the numbers: 1298498081, 2019727887, 1427131847, 939984059, ... So here we properly seed the communicators so the script paths are actually random, and multiple runs on a single remote host have much less chance of clobbering each other. Fixes hashicorp#4186 Kudos to @DustinChaloupka for the correct hunch leading to this fix!
1 parent ebb9b7a commit 96c20f0

4 files changed

Lines changed: 60 additions & 4 deletions

File tree

communicator/ssh/communicator.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type Communicator struct {
3434
config *sshConfig
3535
conn net.Conn
3636
address string
37+
rand *rand.Rand
3738
}
3839

3940
type sshConfig struct {
@@ -68,6 +69,8 @@ func New(s *terraform.InstanceState) (*Communicator, error) {
6869
comm := &Communicator{
6970
connInfo: connInfo,
7071
config: config,
72+
// Seed our own rand source so that script paths are not deterministic
73+
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
7174
}
7275

7376
return comm, nil
@@ -185,7 +188,7 @@ func (c *Communicator) Timeout() time.Duration {
185188
func (c *Communicator) ScriptPath() string {
186189
return strings.Replace(
187190
c.connInfo.ScriptPath, "%RAND%",
188-
strconv.FormatInt(int64(rand.Int31()), 10), -1)
191+
strconv.FormatInt(int64(c.rand.Int31()), 10), -1)
189192
}
190193

191194
// Start implementation of communicator.Communicator interface

communicator/ssh/communicator_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,18 @@ func TestScriptPath(t *testing.T) {
225225
}
226226

227227
for _, tc := range cases {
228-
comm := &Communicator{connInfo: &connectionInfo{ScriptPath: tc.Input}}
228+
r := &terraform.InstanceState{
229+
Ephemeral: terraform.EphemeralState{
230+
ConnInfo: map[string]string{
231+
"type": "winrm",
232+
"script_path": tc.Input,
233+
},
234+
},
235+
}
236+
comm, err := New(r)
237+
if err != nil {
238+
t.Fatalf("err: %s", err)
239+
}
229240
output := comm.ScriptPath()
230241

231242
match, err := regexp.Match(tc.Pattern, []byte(output))
@@ -238,6 +249,20 @@ func TestScriptPath(t *testing.T) {
238249
}
239250
}
240251

252+
func TestScriptPath_randSeed(t *testing.T) {
253+
// Pre GH-4186 fix, this value was the deterministic start the pseudorandom
254+
// chain of unseeded math/rand values for Int31().
255+
staticSeedPath := "/tmp/terraform_1298498081.sh"
256+
c, err := New(&terraform.InstanceState{})
257+
if err != nil {
258+
t.Fatalf("err: %s", err)
259+
}
260+
path := c.ScriptPath()
261+
if path == staticSeedPath {
262+
t.Fatalf("rand not seeded! got: %s", path)
263+
}
264+
}
265+
241266
const testClientPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
242267
MIIEpQIBAAKCAQEAxOgNXOJ/jrRDxBZTSk2X9otNy9zcpUmJr5ifDi5sy7j2ZiQS
243268
beBt1Wf+tLNWis8Cyq06ttEvjjRuM75yucyD6GrqDTXVCSm4PeOIQeDhPhw26wYZ

communicator/winrm/communicator.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type Communicator struct {
2424
connInfo *connectionInfo
2525
client *winrm.Client
2626
endpoint *winrm.Endpoint
27+
rand *rand.Rand
2728
}
2829

2930
// New creates a new communicator implementation over WinRM.
@@ -44,6 +45,8 @@ func New(s *terraform.InstanceState) (*Communicator, error) {
4445
comm := &Communicator{
4546
connInfo: connInfo,
4647
endpoint: endpoint,
48+
// Seed our own rand source so that script paths are not deterministic
49+
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
4750
}
4851

4952
return comm, nil
@@ -121,7 +124,7 @@ func (c *Communicator) Timeout() time.Duration {
121124
func (c *Communicator) ScriptPath() string {
122125
return strings.Replace(
123126
c.connInfo.ScriptPath, "%RAND%",
124-
strconv.FormatInt(int64(rand.Int31()), 10), -1)
127+
strconv.FormatInt(int64(c.rand.Int31()), 10), -1)
125128
}
126129

127130
// Start implementation of communicator.Communicator interface

communicator/winrm/communicator_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,18 @@ func TestScriptPath(t *testing.T) {
131131
}
132132

133133
for _, tc := range cases {
134-
comm := &Communicator{connInfo: &connectionInfo{ScriptPath: tc.Input}}
134+
r := &terraform.InstanceState{
135+
Ephemeral: terraform.EphemeralState{
136+
ConnInfo: map[string]string{
137+
"type": "winrm",
138+
"script_path": tc.Input,
139+
},
140+
},
141+
}
142+
comm, err := New(r)
143+
if err != nil {
144+
t.Fatalf("err: %s", err)
145+
}
135146
output := comm.ScriptPath()
136147

137148
match, err := regexp.Match(tc.Pattern, []byte(output))
@@ -143,3 +154,17 @@ func TestScriptPath(t *testing.T) {
143154
}
144155
}
145156
}
157+
158+
func TestScriptPath_randSeed(t *testing.T) {
159+
// Pre GH-4186 fix, this value was the deterministic start the pseudorandom
160+
// chain of unseeded math/rand values for Int31().
161+
staticSeedPath := "C:/Temp/terraform_1298498081.cmd"
162+
c, err := New(&terraform.InstanceState{})
163+
if err != nil {
164+
t.Fatalf("err: %s", err)
165+
}
166+
path := c.ScriptPath()
167+
if path == staticSeedPath {
168+
t.Fatalf("rand not seeded! got: %s", path)
169+
}
170+
}

0 commit comments

Comments
 (0)