diff --git a/.changes/next-release/bugfix-iam-84710.json b/.changes/next-release/bugfix-iam-84710.json new file mode 100644 index 000000000000..a74600cb4853 --- /dev/null +++ b/.changes/next-release/bugfix-iam-84710.json @@ -0,0 +1,5 @@ +{ + "type": "bugfix", + "category": "``iam``", + "description": "Tighten file permissions for virtual MFA bootstrap output" +} diff --git a/awscli/customizations/iamvirtmfa.py b/awscli/customizations/iamvirtmfa.py index c46fbd6ad3c9..6ecd9c297ba2 100644 --- a/awscli/customizations/iamvirtmfa.py +++ b/awscli/customizations/iamvirtmfa.py @@ -24,7 +24,9 @@ """ import base64 +import os +from awscli.compat import compat_open from awscli.customizations.arguments import ( StatefulArgument, is_parsed_result_successful, @@ -81,7 +83,9 @@ def _save_file(self, parsed, **kwargs): outfile = self._outfile.value if method in parsed['VirtualMFADevice']: body = parsed['VirtualMFADevice'][method] - with open(outfile, 'wb') as fp: + with compat_open(outfile, 'wb', access_permissions=0o600) as fp: + if hasattr(os, 'fchmod'): + os.fchmod(fp.fileno(), 0o600) fp.write(base64.b64decode(body)) for choice in CHOICES: if choice in parsed['VirtualMFADevice']: diff --git a/tests/functional/iam/test_create_virtual_mfa_device.py b/tests/functional/iam/test_create_virtual_mfa_device.py index 00aa0ab627d6..5677ebd07945 100644 --- a/tests/functional/iam/test_create_virtual_mfa_device.py +++ b/tests/functional/iam/test_create_virtual_mfa_device.py @@ -13,7 +13,7 @@ # language governing permissions and limitations under the License. import os -from awscli.testutils import BaseAWSCommandParamsTest +from awscli.testutils import BaseAWSCommandParamsTest, skip_if_windows class TestCreateVirtualMFADevice(BaseAWSCommandParamsTest): @@ -161,3 +161,32 @@ def test_bad_response(self): stderr_contains=self.parsed_response['Error']['Message'], expected_rc=254, ) + + @skip_if_windows("Permissions test not valid on Windows.") + def test_output_file_permissions(self): + outfile = self.getpath('fiebaz_perms.b32') + self.addCleanup(self.remove_file_if_exists, outfile) + cmdline = self.prefix + cmdline += ' --virtual-mfa-device-name fiebaz' + cmdline += ( + ' --outfile %s --bootstrap-method Base32StringSeed' % outfile + ) + result = {"VirtualMFADeviceName": 'fiebaz'} + self.assert_params_for_cmd(cmdline, result) + self.assertEqual(os.stat(outfile).st_mode & 0xFFF, 0o600) + + @skip_if_windows("Permissions test not valid on Windows.") + def test_output_file_permissions_existing_file(self): + outfile = self.getpath('fiebaz_perms_existing.b32') + self.addCleanup(self.remove_file_if_exists, outfile) + with open(outfile, 'wb') as f: + f.write(b'existing') + os.chmod(outfile, 0o644) + cmdline = self.prefix + cmdline += ' --virtual-mfa-device-name fiebaz' + cmdline += ( + ' --outfile %s --bootstrap-method Base32StringSeed' % outfile + ) + result = {"VirtualMFADeviceName": 'fiebaz'} + self.assert_params_for_cmd(cmdline, result) + self.assertEqual(os.stat(outfile).st_mode & 0xFFF, 0o600)