Skip to content

Commit cb89b7d

Browse files
Tighten file permissions for virtual MFA bootstrap output (#10193)
Restrict MFA seed files to 0600 (owner-only) to prevent other local users from reading the bootstrap data.
1 parent 94ce3f6 commit cb89b7d

3 files changed

Lines changed: 40 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "``iam``",
4+
"description": "Tighten file permissions for virtual MFA bootstrap output"
5+
}

awscli/customizations/iamvirtmfa.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
"""
2525

2626
import base64
27+
import os
2728

29+
from awscli.compat import compat_open
2830
from awscli.customizations.arguments import (
2931
StatefulArgument,
3032
is_parsed_result_successful,
@@ -81,7 +83,9 @@ def _save_file(self, parsed, **kwargs):
8183
outfile = self._outfile.value
8284
if method in parsed['VirtualMFADevice']:
8385
body = parsed['VirtualMFADevice'][method]
84-
with open(outfile, 'wb') as fp:
86+
with compat_open(outfile, 'wb', access_permissions=0o600) as fp:
87+
if hasattr(os, 'fchmod'):
88+
os.fchmod(fp.fileno(), 0o600)
8589
fp.write(base64.b64decode(body))
8690
for choice in CHOICES:
8791
if choice in parsed['VirtualMFADevice']:

tests/functional/iam/test_create_virtual_mfa_device.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# language governing permissions and limitations under the License.
1414
import os
1515

16-
from awscli.testutils import BaseAWSCommandParamsTest
16+
from awscli.testutils import BaseAWSCommandParamsTest, skip_if_windows
1717

1818

1919
class TestCreateVirtualMFADevice(BaseAWSCommandParamsTest):
@@ -161,3 +161,32 @@ def test_bad_response(self):
161161
stderr_contains=self.parsed_response['Error']['Message'],
162162
expected_rc=254,
163163
)
164+
165+
@skip_if_windows("Permissions test not valid on Windows.")
166+
def test_output_file_permissions(self):
167+
outfile = self.getpath('fiebaz_perms.b32')
168+
self.addCleanup(self.remove_file_if_exists, outfile)
169+
cmdline = self.prefix
170+
cmdline += ' --virtual-mfa-device-name fiebaz'
171+
cmdline += (
172+
' --outfile %s --bootstrap-method Base32StringSeed' % outfile
173+
)
174+
result = {"VirtualMFADeviceName": 'fiebaz'}
175+
self.assert_params_for_cmd(cmdline, result)
176+
self.assertEqual(os.stat(outfile).st_mode & 0xFFF, 0o600)
177+
178+
@skip_if_windows("Permissions test not valid on Windows.")
179+
def test_output_file_permissions_existing_file(self):
180+
outfile = self.getpath('fiebaz_perms_existing.b32')
181+
self.addCleanup(self.remove_file_if_exists, outfile)
182+
with open(outfile, 'wb') as f:
183+
f.write(b'existing')
184+
os.chmod(outfile, 0o644)
185+
cmdline = self.prefix
186+
cmdline += ' --virtual-mfa-device-name fiebaz'
187+
cmdline += (
188+
' --outfile %s --bootstrap-method Base32StringSeed' % outfile
189+
)
190+
result = {"VirtualMFADeviceName": 'fiebaz'}
191+
self.assert_params_for_cmd(cmdline, result)
192+
self.assertEqual(os.stat(outfile).st_mode & 0xFFF, 0o600)

0 commit comments

Comments
 (0)