如何在Linux环境下用PHP/Symfony给AD文件夹添加安全组?
问题与适配方案
问题背景
我有一个部署在公司内部Ubuntu服务器(Apache Web服务器)上的Symfony应用。目前已实现通过icewind/smb创建网络共享文件夹,以及通过Symfony LDAP组件在Active Directory中创建只读(RO)和读写(RW)组,但无法将这些组设置为新文件夹的安全组。现有一段仅在Windows服务器生效的旧代码,需要适配Linux环境。
当前实现代码
public function createLdapFolder(array $data): bool { $prefixMapping = [ 'OU=Section1,OU=_Company,DC=company,DC=local' => 'S1_', 'OU=Section2,OU=_Company,DC=company,DC=local' => 'S2_', 'OU=Section3,OU=_Company,DC=company,DC=local' => 'S3_', 'OU=Section4,OU=_Company,DC=company,DC=local' => 'S4_', 'general' => 'All_', 'scan' => 'Scan_', 'appstorage' => '', ]; $selectPrefix = $data['selectPrefix']; $folderName = $data['folderName']; if (!preg_match('/^[a-zA-Z0-9_\-]+$/', $folderName)) { throw new \InvalidArgumentException('Invalid Foldername.'); } if (!isset($prefixMapping[$selectPrefix])) { $this->requestStack->getCurrentRequest()->getSession()->getFlashBag()->add( 'error', 'Invalid Section.' ); return false; } $prefix = $prefixMapping[$selectPrefix]; $completeFolderName = $prefix . $folderName; $serverFactory = new ServerFactory(); $auth = new BasicAuth($_ENV['LDAP_USERNAME'], 'company', $_ENV['LDAP_PASSWORD']); $server = $serverFactory->createServer($_ENV['LDAP_IP'], $auth); $shares = $server->listShares(); foreach ($shares as $shareName) { $shareName->getName(); } if ($selectPrefix != 'appstorage') { $shareName = $_ENV['LDAP_SHARE_1']; } else { $shareName = $_ENV['LDAP_SHARE_APPSTORAGE']; } $share = $server->getShare($shareName); $share->mkdir($completeFolderName); $this->createLdapFolderGroups($folderName); return true; } public function createLdapFolderGroups(string $folderName): bool { $baseDn = 'OU=Folder,' . $_ENV['LDAP_GLOBAL_GROUPS_BASE_DN']; $entryRO = new Entry('cn=GG_Folder_' . $folderName . '-RO,' . $baseDn, [ 'sAMAccountName' => ['GG_Folder_' . $folderName . '-RO'], 'objectClass' => ['top', 'group'], 'groupType' => [-2147483646], ]); $entryRW = new Entry('cn=GG_Folder_' . $folderName . '-RW,' . $baseDn, [ 'sAMAccountName' => ['GG_Folder_' . $folderName . '-RW'], 'objectClass' => ['top', 'group'], 'groupType' => [-2147483646], ]); try { $this->ldap->getEntryManager()->add($entryRO); $this->ldap->getEntryManager()->add($entryRW); $this->logger->info("Group GG_Folder_{$folderName}-RO created."); $this->logger->info("Group GG_Folder_{$folderName}-RW created."); return true; } catch (\Exception $e) { $this->logger->error("Error by creating group" . $e->getMessage()); throw new \Exception("Error by creating group" . $e->getMessage()); return false; } }
仅Windows生效的旧代码
$globalGroupsBaseDN = 'OU=GlobalGroups,DC=testdc,DC=local'; $groupName = 'GG_' . $folderName; $groupRO = $groupName . '-RO'; $groupRW = $groupName . '-RW'; $newGroupRODN = 'CN=' . $groupRO . ',' . $globalGroupsBaseDN; $newGroupRWDN = 'CN=' . $groupRW . ',' . $globalGroupsBaseDN; $newGroupROAttributes['objectClass'] = ['group', 'top']; $newGroupROAttributes['cn'] = $groupRO; $newGroupROAttributes['sAMAccountName'] = $groupRO; $newGroupRWAttributes['objectClass'] = ['group', 'top']; $newGroupRWAttributes['cn'] = $groupRW; $newGroupRWAttributes['sAMAccountName'] = $groupRW; ldap_add($ldapConnection, $newGroupRODN, $newGroupROAttributes); ldap_add($ldapConnection, $newGroupRWDN, $newGroupRWAttributes); // Add security group to folder $groupAttributeRO = [ 'member' => [$newGroupRWDN] ]; $groupAttributeRW = [ 'member' => [$newGroupRODN] ]; ldap_mod_add($ldapConnection, $existingFolderPath, $groupAttributeRO); ldap_mod_add($ldapConnection, $existingFolderPath, $groupAttributeRW); var_dump($existingFolderPath).'<br>'; var_dump($groupAttributeRO); exit; // Set security $permissionsRO = [ 'read', 'list', 'read_property', 'execute', ]; $permissionsRW = [ 'write', 'read', 'list', 'read_property', 'execute', 'delete', ]; $securityDescriptor = 'D:P(' . implode(',', $permissionsRO) . ')'; ldap_mod_replace($ldapConnection, $existingFolderPath, ['ntSecurityDescriptor' => [$securityDescriptor]]); $securityDescriptorRW = 'D:P(' . implode(',', $permissionsRW) . ')'; ldap_mod_replace($ldapConnection, $existingFolderPath, ['ntSecurityDescriptor' => [$securityDescriptorRW]]);
Linux环境适配方案
适配原理
旧代码直接操作LDAP的ntSecurityDescriptor属性,仅适用于Windows环境下直接管理AD中的文件对象。在Linux环境下,通过SMB创建的文件夹需要通过SMB协议设置权限,无法直接修改LDAP属性,我们可以借助smbclient工具来完成权限配置。
具体实现步骤
安装依赖工具
在Ubuntu服务器上安装smbclient:sudo apt-get update && sudo apt-get install smbclient扩展Symfony代码,添加权限设置方法
在现有createLdapFolder方法中,创建文件夹并生成组后,新增权限设置逻辑:public function createLdapFolder(array $data): bool { // ... 原有代码保持不变 ... $share->mkdir($completeFolderName); $this->createLdapFolderGroups($folderName); // 新增:设置SMB文件夹权限 $this->setSmbFolderPermissions($shareName, $completeFolderName, $folderName); return true; } private function setSmbFolderPermissions(string $shareName, string $folderPath, string $folderName): void { $roGroup = 'GG_Folder_' . $folderName . '-RO'; $rwGroup = 'GG_Folder_' . $folderName . '-RW'; // 构建只读权限设置命令 $roCmd = sprintf( 'smbclient //%s/%s -U %s%%%s -c "setacl "%s" "ACL:%s:ALLOWED/I/READ""', $_ENV['LDAP_IP'], $shareName, $_ENV['LDAP_USERNAME'], $_ENV['LDAP_PASSWORD'], $folderPath, $roGroup ); // 构建读写权限设置命令 $rwCmd = sprintf( 'smbclient //%s/%s -U %s%%%s -c "setacl "%s" "ACL:%s:ALLOWED/I/CHANGE""', $_ENV['LDAP_IP'], $shareName, $_ENV['LDAP_USERNAME'], $_ENV['LDAP_PASSWORD'], $folderPath, $rwGroup ); // 执行命令并检查结果 exec($roCmd, $roOutput, $roExitCode); exec($rwCmd, $rwOutput, $rwExitCode); if ($roExitCode !== 0 || $rwExitCode !== 0) { $this->logger->error( 'SMB权限设置失败:RO组退出码' . $roExitCode . ',RW组退出码' . $rwExitCode ); throw new \RuntimeException('文件夹权限设置失败'); } $this->logger->info('文件夹' . $folderPath . '的SMB权限已配置完成'); }注意事项
- 确保Apache运行用户(通常是
www-data)有权限执行smbclient命令 - 配置的LDAP用户需要拥有修改目标SMB共享文件夹权限的权限
- 可根据需求调整ACL权限项,比如添加
DELETE权限可将CHANGE替换为FULL
- 确保Apache运行用户(通常是
内容的提问来源于stack exchange,提问作者TheQuestionmark
相关产品推荐
相关产品推荐

