def _GenerateSshKey(self, key_type, key_dest): """Generate a new SSH key. Args: key_type: string, the type of the SSH key. key_dest: string, a file location to store the SSH key. """ # Create a temporary file to save the created RSA keys. with tempfile.NamedTemporaryFile(prefix=key_type, delete=True) as temp: temp_key = temp.name command = [ 'ssh-keygen', '-t', key_type, '-f', temp_key, '-N', '', '-q' ] try: self.logger.info('Generating SSH key %s.', key_dest) subprocess.check_call(command) except subprocess.CalledProcessError: self.logger.warning('Could not create SSH key %s.', key_dest) return shutil.move(temp_key, key_dest) shutil.move('%s.pub' % temp_key, '%s.pub' % key_dest) file_utils.SetPermissions(key_dest, mode=0o600) file_utils.SetPermissions('%s.pub' % key_dest, mode=0o644)
def _CreateSudoersGroup(self): """Create a Linux group for Google added sudo user accounts.""" if not self._GetGroup(self.google_sudoers_group): try: command = self.groupadd_cmd.format( group=self.google_sudoers_group) subprocess.check_call(command.split(' ')) except subprocess.CalledProcessError as e: self.logger.warning('Could not create the sudoers group. %s.', str(e)) if not os.path.exists(self.google_sudoers_file): try: with open(self.google_sudoers_file, 'w') as group: message = '%{0} ALL=(ALL:ALL) NOPASSWD:ALL'.format( self.google_sudoers_group) group.write(message) except IOError as e: self.logger.error('Could not write sudoers file. %s. %s', self.google_sudoers_file, str(e)) return file_utils.SetPermissions(self.google_sudoers_file, mode=0o440, uid=0, gid=0)
def _CreateSudoersGroup(self): """Create a Linux group for Google added sudo user accounts.""" if not self._GetGroup(self.google_sudoers_group): try: subprocess.check_call(['groupadd', self.google_sudoers_group]) except subprocess.CalledProcessError as e: self.logger.warning('Could not create the sudoers group. %s.', str(e)) with open(self.google_sudoers_file, 'a+') as group: """Check if the group is already in the sudoers file""" found = False message = '%{0} ALL=(ALL:ALL) NOPASSWD:ALL\n'.format( self.google_sudoers_group) for line in group: if message in line: found = True break if not found: group.write('\n' + message) file_utils.SetPermissions(self.google_sudoers_file, mode=0o440, uid=0, gid=0)
def _GenerateSshKey(self, key_type, key_dest): """Generate a new SSH key. Args: key_type: string, the type of the SSH key. key_dest: string, a file location to store the SSH key. Returns: tuple, key_type and public key string. """ # Create a temporary file to save the created RSA keys. with tempfile.NamedTemporaryFile(prefix=key_type, delete=True) as temp: temp_key = temp.name command = [ 'ssh-keygen', '-t', key_type, '-f', temp_key, '-N', '', '-q' ] try: self.logger.info('Generating SSH key %s.', key_dest) subprocess.check_call(command) except subprocess.CalledProcessError: self.logger.warning('Could not create SSH key %s.', key_dest) return shutil.move(temp_key, key_dest) shutil.move('%s.pub' % temp_key, '%s.pub' % key_dest) file_utils.SetPermissions(key_dest, mode=0o600) file_utils.SetPermissions('%s.pub' % key_dest, mode=0o644) with open('%s.pub' % key_dest, 'r') as pk: key_data = pk.read() key_values = key_data.split() if len(key_values) < 2: self.logger.warning('Could not read host key from %s.pub.', key_dest) return else: return key_values[0], key_values[1]
def SetConfiguredUsers(self, users): """Set the list of configured Google user accounts. Args: users: list, the username strings of the Linux accounts. """ prefix = self.logger.name + '-' with tempfile.NamedTemporaryFile( mode='w', prefix=prefix, delete=True) as updated_users: updated_users_file = updated_users.name for user in users: updated_users.write(user + '\n') updated_users.flush() if not os.path.exists(self.google_users_dir): os.makedirs(self.google_users_dir) shutil.copy(updated_users_file, self.google_users_file) file_utils.SetPermissions(self.google_users_file, mode=0o600, uid=0, gid=0)
def _UpdateAuthorizedKeys(self, user, ssh_keys): """Update the authorized keys file for a Linux user with a list of SSH keys. Args: user: string, the name of the Linux user account. ssh_keys: list, the SSH key strings associated with the user. Raises: IOError, raised when there is an exception updating a file. OSError, raised when setting permissions or writing to a read-only file system. """ pw_entry = self._GetUser(user) if not pw_entry: return uid = pw_entry.pw_uid gid = pw_entry.pw_gid home_dir = pw_entry.pw_dir ssh_dir = os.path.join(home_dir, '.ssh') file_utils.SetPermissions(ssh_dir, mode=0o700, uid=uid, gid=gid, mkdir=True) # Not all sshd's support multiple authorized_keys files so we have to # share one with the user. We add each of our entries as follows: # # Added by Google # authorized_key_entry authorized_keys_file = os.path.join(ssh_dir, 'authorized_keys') prefix = self.logger.name + '-' with tempfile.NamedTemporaryFile(mode='w', prefix=prefix, delete=True) as updated_keys: updated_keys_file = updated_keys.name if os.path.exists(authorized_keys_file): lines = open(authorized_keys_file).readlines() else: lines = [] google_lines = set() for i, line in enumerate(lines): if line.startswith(self.google_comment): google_lines.update([i, i + 1]) # Write user's authorized key entries. for i, line in enumerate(lines): if i not in google_lines and line: line += '\n' if not line.endswith('\n') else '' updated_keys.write(line) # Write the Google authorized key entries at the end of the file. # Each entry is preceded by '# Added by Google'. for ssh_key in ssh_keys: ssh_key += '\n' if not ssh_key.endswith('\n') else '' updated_keys.write('%s\n' % self.google_comment) updated_keys.write(ssh_key) # Write buffered data to the updated keys file without closing it and # update the Linux user's authorized keys file. updated_keys.flush() shutil.copy(updated_keys_file, authorized_keys_file) file_utils.SetPermissions(authorized_keys_file, mode=0o600, uid=uid, gid=gid)
def testSetPermissions(self, mock_chmod, mock_chown, mock_mkdir, mock_exists, mock_context): mocks = mock.Mock() mocks.attach_mock(mock_chmod, 'chmod') mocks.attach_mock(mock_chown, 'chown') mocks.attach_mock(mock_mkdir, 'mkdir') mocks.attach_mock(mock_exists, 'exists') mocks.attach_mock(mock_context, 'context') path = 'path' mode = 'mode' uid = 'uid' gid = 'gid' mock_exists.side_effect = [False, True, False] # Create a new directory. file_utils.SetPermissions(path, mode=mode, uid=uid, gid=gid, mkdir=True) # The path exists, so do not create a new directory. file_utils.SetPermissions(path, mode=mode, uid=uid, gid=gid, mkdir=True) # Create a new directory without a mode specified. file_utils.SetPermissions(path, uid=uid, gid=gid, mkdir=True) # Do not create the path even though it does not exist. file_utils.SetPermissions(path, mode=mode, uid=uid, gid=gid, mkdir=False) # Do not set an owner when a UID or GID is not specified. file_utils.SetPermissions(path, mode=mode, mkdir=False) # Set the SELinux context when no parameters are specified. file_utils.SetPermissions(path) expected_calls = [ # Create a new directory. mock.call.exists(path), mock.call.mkdir(path, mode), mock.call.chown(path, uid, gid), mock.call.context(path), # Attempt to create a new path but reuse existing path. mock.call.exists(path), mock.call.chmod(path, mode), mock.call.chown(path, uid, gid), mock.call.context(path), # Create a new directory with default mode. mock.call.exists(path), mock.call.mkdir(path, 0o777), mock.call.chown(path, uid, gid), mock.call.context(path), # Set permissions and owner on an existing path. mock.call.chmod(path, mode), mock.call.chown(path, uid, gid), mock.call.context(path), # Set permissions, without changing ownership, of an existing path. mock.call.chmod(path, mode), mock.call.context(path), # Set SELinux context on an existing path. mock.call.context(path), ] self.assertEqual(mocks.mock_calls, expected_calls)