Enable public Azure Files roaming deployment
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
# SGU Credential Provider 0.6.1
|
||||||
|
|
||||||
|
Continúa la serie publicada después de `0.6.0`.
|
||||||
|
|
||||||
|
## Cambios
|
||||||
|
|
||||||
|
- El configurador de roaming admite explícitamente Azure Files mediante endpoint
|
||||||
|
público con `-EndpointAccess Public`; el modo privado sigue siendo el valor
|
||||||
|
predeterminado.
|
||||||
|
- Se corrige la generación del nombre de la identidad de almacenamiento para
|
||||||
|
respetar el límite de 15 caracteres de una cuenta de equipo de Active
|
||||||
|
Directory.
|
||||||
|
- Las ACL de `profiles` y `redirected` aplican mínimo privilegio en la raíz: los
|
||||||
|
grupos autorizados pueden crear su directorio, pero no modificar ni eliminar
|
||||||
|
directorios ajenos; `CREATOR OWNER` conserva el control dentro del directorio
|
||||||
|
propio.
|
||||||
|
- Se documenta la secuencia de despliegue con firewall de Azure Storage en modo
|
||||||
|
`Deny`, autorizando finalmente sólo la red institucional `200.13.89.0/24`.
|
||||||
|
|
||||||
|
## Validación del despliegue
|
||||||
|
|
||||||
|
- Azure Files usa autenticación AD DS con Kerberos AES-256.
|
||||||
|
- `AF-02` y `AF-03` validaron ticket Kerberos, TCP 445 y lectura/escritura en los
|
||||||
|
shares `profiles` y `redirected`.
|
||||||
|
- FSLogix 26.08 (`3.26.826.17182`) quedó instalado y activo en ambos clientes.
|
||||||
|
- La GPO de FSLogix mantiene una base deshabilitada y se habilita sólo para los
|
||||||
|
SID de `SGU-Docentes` y `SGU-Administrativos`.
|
||||||
|
- La GPO de Alumnos redirige únicamente Escritorio y Documentos.
|
||||||
|
|
||||||
|
## Descargas
|
||||||
|
|
||||||
|
- `sgu-windows-client-bootstrap-0.6.1.zip`
|
||||||
|
- `sgu-server-bootstrap-0.6.1.zip`
|
||||||
|
- `sgu-linux-client-bootstrap-0.6.1.zip`
|
||||||
|
- `sgu-azure-infrastructure-0.6.1.zip`
|
||||||
|
- `SHA256SUMS-0.6.1.txt`
|
||||||
|
|
||||||
|
Verifique los hashes SHA-256 antes de ejecutar los paquetes. No se incluyen
|
||||||
|
credenciales, claves de almacenamiento ni artefactos temporales del despliegue.
|
||||||
@@ -26,6 +26,16 @@ VNet. El modo de enrolamiento público directo no puede usar estos recursos y el
|
|||||||
script de despliegue exige `-DeployUserRoaming $false` cuando no se despliega el
|
script de despliegue exige `-DeployUserRoaming $false` cuando no se despliega el
|
||||||
gateway P2S.
|
gateway P2S.
|
||||||
|
|
||||||
|
Para una cuenta SMB ya existente cuyo endpoint público se limite mediante el
|
||||||
|
firewall de Storage a las redes institucionales, el configurador admite
|
||||||
|
`-EndpointAccess Public`. En ese modo no exige una dirección RFC1918, pero sigue
|
||||||
|
comprobando resolución DNS y TCP 445. La creación de los shares y las reglas de
|
||||||
|
red de la cuenta existente se realiza antes de ejecutar el configurador. El host
|
||||||
|
que aplica la integración AD/ACL debe conservar acceso temporal a SMB durante
|
||||||
|
esa ejecución; después se establece `defaultAction=Deny` y se deja únicamente la
|
||||||
|
red institucional autorizada. En el despliegue LCI actual esa regla final es
|
||||||
|
`200.13.89.0/24`.
|
||||||
|
|
||||||
Azure Files se integra con el AD DS SGU, no con cuentas o claves guardadas en
|
Azure Files se integra con el AD DS SGU, no con cuentas o claves guardadas en
|
||||||
cada cliente. El script configura AES-256 para Kerberos, una ACL de mínimo
|
cada cliente. El script configura AES-256 para Kerberos, una ACL de mínimo
|
||||||
privilegio con `CREATOR OWNER`, administradores y los grupos del rol, y permiso
|
privilegio con `CREATOR OWNER`, administradores y los grupos del rol, y permiso
|
||||||
@@ -33,6 +43,11 @@ SMB predeterminado para identidades autenticadas. La ACL NTFS mantiene aislado
|
|||||||
el contenido de cada usuario. La clave de la cuenta se usa en memoria sólo para
|
el contenido de cada usuario. La clave de la cuenta se usa en memoria sólo para
|
||||||
crear las ACL iniciales y no se escribe en el dominio, en GPO ni en disco.
|
crear las ACL iniciales y no se escribe en el dominio, en GPO ni en disco.
|
||||||
|
|
||||||
|
Los grupos de rol sólo pueden enumerar la raíz y crear su propio directorio; no
|
||||||
|
reciben `Modify` en la raíz compartida. El directorio creado hereda `CREATOR
|
||||||
|
OWNER`, de modo que un usuario no puede modificar ni eliminar el contenedor de
|
||||||
|
otro usuario.
|
||||||
|
|
||||||
## 1. Desplegar la infraestructura
|
## 1. Desplegar la infraestructura
|
||||||
|
|
||||||
El soporte está activado por omisión para despliegues P2S:
|
El soporte está activado por omisión para despliegues P2S:
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ param(
|
|||||||
[string]$AdministrativeGroupName = 'SGU-Administrativos',
|
[string]$AdministrativeGroupName = 'SGU-Administrativos',
|
||||||
[string]$StudentGpoName = 'SGU - AL redirected folders',
|
[string]$StudentGpoName = 'SGU - AL redirected folders',
|
||||||
[string]$StaffGpoName = 'SGU - AD-DO FSLogix profiles',
|
[string]$StaffGpoName = 'SGU - AD-DO FSLogix profiles',
|
||||||
|
[ValidateSet('Private', 'Public')]
|
||||||
|
[string]$EndpointAccess = 'Private',
|
||||||
[ValidateRange(1024, 1048576)]
|
[ValidateRange(1024, 1048576)]
|
||||||
[int]$FsLogixProfileSizeMiB = 30000,
|
[int]$FsLogixProfileSizeMiB = 30000,
|
||||||
[string]$AzFilesHybridModulePath,
|
[string]$AzFilesHybridModulePath,
|
||||||
@@ -76,13 +78,13 @@ function Import-SguAzFilesHybrid {
|
|||||||
function Get-SguStorageSamAccountName {
|
function Get-SguStorageSamAccountName {
|
||||||
param([Parameter(Mandatory)][string]$StorageName)
|
param([Parameter(Mandatory)][string]$StorageName)
|
||||||
|
|
||||||
if ($StorageName.Length -le 20) {
|
if ($StorageName.Length -le 15) {
|
||||||
return $StorageName
|
return $StorageName
|
||||||
}
|
}
|
||||||
$sha256 = [Security.Cryptography.SHA256]::Create()
|
$sha256 = [Security.Cryptography.SHA256]::Create()
|
||||||
try {
|
try {
|
||||||
$hash = $sha256.ComputeHash([Text.Encoding]::UTF8.GetBytes($StorageName))
|
$hash = $sha256.ComputeHash([Text.Encoding]::UTF8.GetBytes($StorageName))
|
||||||
$suffix = ([BitConverter]::ToString($hash) -replace '-', '').Substring(0, 15).ToLowerInvariant()
|
$suffix = ([BitConverter]::ToString($hash) -replace '-', '').Substring(0, 10).ToLowerInvariant()
|
||||||
return "sgufs$suffix"
|
return "sgufs$suffix"
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@@ -322,15 +324,19 @@ if ($PSCmdlet.ShouldProcess($StorageAccountName, 'Grant authenticated AD identit
|
|||||||
-DefaultSharePermission StorageFileDataSmbShareContributor
|
-DefaultSharePermission StorageFileDataSmbShareContributor
|
||||||
}
|
}
|
||||||
|
|
||||||
$privateAddresses = @(Resolve-DnsName -Name $fileEndpointHost -Type A -ErrorAction Stop |
|
$endpointAddresses = @(Resolve-DnsName -Name $fileEndpointHost -Type A -ErrorAction Stop |
|
||||||
Where-Object IPAddress | Select-Object -ExpandProperty IPAddress)
|
Where-Object IPAddress | Select-Object -ExpandProperty IPAddress)
|
||||||
if ($privateAddresses.Count -eq 0 -or @($privateAddresses | Where-Object {
|
if ($endpointAddresses.Count -eq 0) {
|
||||||
$_ -match '^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)'
|
throw "$fileEndpointHost did not resolve to an IPv4 address."
|
||||||
}).Count -eq 0) {
|
}
|
||||||
|
$privateAddresses = @($endpointAddresses | Where-Object {
|
||||||
|
$_ -match '^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)'
|
||||||
|
})
|
||||||
|
if ($EndpointAccess -eq 'Private' -and $privateAddresses.Count -eq 0) {
|
||||||
throw "$fileEndpointHost did not resolve to a private endpoint. Verify the privatelink.file.core.windows.net VNet link and the DC Azure DNS forwarder."
|
throw "$fileEndpointHost did not resolve to a private endpoint. Verify the privatelink.file.core.windows.net VNet link and the DC Azure DNS forwarder."
|
||||||
}
|
}
|
||||||
if (-not (Test-NetConnection -ComputerName $fileEndpointHost -Port 445 -InformationLevel Quiet)) {
|
if (-not (Test-NetConnection -ComputerName $fileEndpointHost -Port 445 -InformationLevel Quiet)) {
|
||||||
throw "The domain controller cannot reach $fileEndpointHost on TCP 445 through the private endpoint."
|
throw "The domain controller cannot reach $fileEndpointHost on TCP 445 using the selected $EndpointAccess endpoint."
|
||||||
}
|
}
|
||||||
|
|
||||||
$storageKey = @(Get-AzStorageAccountKey -ResourceGroupName $ResourceGroupName `
|
$storageKey = @(Get-AzStorageAccountKey -ResourceGroupName $ResourceGroupName `
|
||||||
@@ -344,23 +350,24 @@ $storageCredential = [PSCredential]::new(
|
|||||||
(ConvertTo-SecureString -String $storageKey -AsPlainText -Force))
|
(ConvertTo-SecureString -String $storageKey -AsPlainText -Force))
|
||||||
$profilesSharePath = "\\$fileEndpointHost\$FsLogixProfilesShareName"
|
$profilesSharePath = "\\$fileEndpointHost\$FsLogixProfilesShareName"
|
||||||
$redirectedFoldersSharePath = "\\$fileEndpointHost\$RedirectedFoldersShareName"
|
$redirectedFoldersSharePath = "\\$fileEndpointHost\$RedirectedFoldersShareName"
|
||||||
|
$perUserRootRights = [Security.AccessControl.FileSystemRights]::CreateDirectories -bor
|
||||||
|
[Security.AccessControl.FileSystemRights]::ListDirectory -bor
|
||||||
|
[Security.AccessControl.FileSystemRights]::ReadAttributes -bor
|
||||||
|
[Security.AccessControl.FileSystemRights]::ReadExtendedAttributes -bor
|
||||||
|
[Security.AccessControl.FileSystemRights]::ReadPermissions -bor
|
||||||
|
[Security.AccessControl.FileSystemRights]::Traverse -bor
|
||||||
|
[Security.AccessControl.FileSystemRights]::Synchronize
|
||||||
try {
|
try {
|
||||||
if ($PSCmdlet.ShouldProcess($profilesSharePath, 'Apply isolated FSLogix root ACLs')) {
|
if ($PSCmdlet.ShouldProcess($profilesSharePath, 'Apply isolated FSLogix root ACLs')) {
|
||||||
Set-SguAzureFileRootAcl -UncPath $profilesSharePath -Credential $storageCredential `
|
Set-SguAzureFileRootAcl -UncPath $profilesSharePath -Credential $storageCredential `
|
||||||
-DomainAdminsSid $domainAdminsSid `
|
-DomainAdminsSid $domainAdminsSid `
|
||||||
-ContributorSids @($professorGroup.SID, $administrativeGroup.SID)
|
-ContributorSids @($professorGroup.SID, $administrativeGroup.SID) `
|
||||||
|
-ContributorRights $perUserRootRights
|
||||||
}
|
}
|
||||||
if ($PSCmdlet.ShouldProcess($redirectedFoldersSharePath, 'Apply isolated student-folder root ACLs')) {
|
if ($PSCmdlet.ShouldProcess($redirectedFoldersSharePath, 'Apply isolated student-folder root ACLs')) {
|
||||||
$studentRootRights = [Security.AccessControl.FileSystemRights]::CreateDirectories -bor
|
|
||||||
[Security.AccessControl.FileSystemRights]::ListDirectory -bor
|
|
||||||
[Security.AccessControl.FileSystemRights]::ReadAttributes -bor
|
|
||||||
[Security.AccessControl.FileSystemRights]::ReadExtendedAttributes -bor
|
|
||||||
[Security.AccessControl.FileSystemRights]::ReadPermissions -bor
|
|
||||||
[Security.AccessControl.FileSystemRights]::Traverse -bor
|
|
||||||
[Security.AccessControl.FileSystemRights]::Synchronize
|
|
||||||
Set-SguAzureFileRootAcl -UncPath $redirectedFoldersSharePath -Credential $storageCredential `
|
Set-SguAzureFileRootAcl -UncPath $redirectedFoldersSharePath -Credential $storageCredential `
|
||||||
-DomainAdminsSid $domainAdminsSid -ContributorSids @($studentGroup.SID) `
|
-DomainAdminsSid $domainAdminsSid -ContributorSids @($studentGroup.SID) `
|
||||||
-ContributorRights $studentRootRights
|
-ContributorRights $perUserRootRights
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@@ -415,6 +422,8 @@ foreach ($staffGroup in @($professorGroup, $administrativeGroup)) {
|
|||||||
[pscustomobject]@{
|
[pscustomobject]@{
|
||||||
StorageAccountName = $StorageAccountName
|
StorageAccountName = $StorageAccountName
|
||||||
FileEndpoint = $fileEndpointHost
|
FileEndpoint = $fileEndpointHost
|
||||||
|
EndpointAccess = $EndpointAccess
|
||||||
|
EndpointAddresses = $endpointAddresses
|
||||||
PrivateEndpointAddresses = $privateAddresses
|
PrivateEndpointAddresses = $privateAddresses
|
||||||
DirectoryService = $directoryService
|
DirectoryService = $directoryService
|
||||||
KerberosEncryption = 'AES256'
|
KerberosEncryption = 'AES256'
|
||||||
|
|||||||
@@ -77,6 +77,16 @@ Describe 'SGU Azure user-roaming infrastructure' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Describe 'SGU role-specific roaming policies' {
|
Describe 'SGU role-specific roaming policies' {
|
||||||
|
It 'supports an explicitly selected public SMB endpoint while keeping private as the default' {
|
||||||
|
(((Get-Command $configurationPath).Parameters['EndpointAccess'].Attributes |
|
||||||
|
Where-Object { $_ -is [Management.Automation.ValidateSetAttribute] }).ValidValues -join ',') |
|
||||||
|
Should Be 'Private,Public'
|
||||||
|
$source = Get-Content -LiteralPath $configurationPath -Raw
|
||||||
|
$source | Should Match ([regex]::Escape("[string]`$EndpointAccess = 'Private'"))
|
||||||
|
$source | Should Match ([regex]::Escape("`$EndpointAccess -eq 'Private'"))
|
||||||
|
$source | Should Match 'selected \$EndpointAccess endpoint'
|
||||||
|
}
|
||||||
|
|
||||||
It 'uses an AD computer identity with AES-256 Kerberos' {
|
It 'uses an AD computer identity with AES-256 Kerberos' {
|
||||||
$source = Get-Content -LiteralPath $configurationPath -Raw
|
$source = Get-Content -LiteralPath $configurationPath -Raw
|
||||||
$source | Should Match "DomainAccountType = 'ComputerAccount'"
|
$source | Should Match "DomainAccountType = 'ComputerAccount'"
|
||||||
@@ -90,6 +100,9 @@ Describe 'SGU role-specific roaming policies' {
|
|||||||
$source | Should Match "SecurityIdentifier\]::new\('S-1-3-0'\)"
|
$source | Should Match "SecurityIdentifier\]::new\('S-1-3-0'\)"
|
||||||
$source | Should Match 'PropagationFlags\]::InheritOnly'
|
$source | Should Match 'PropagationFlags\]::InheritOnly'
|
||||||
$source | Should Match 'SetAccessRuleProtection\(\$true, \$false\)'
|
$source | Should Match 'SetAccessRuleProtection\(\$true, \$false\)'
|
||||||
|
$source | Should Match '\$perUserRootRights'
|
||||||
|
$source | Should Match 'CreateDirectories'
|
||||||
|
$source | Should Match 'ContributorRights \$perUserRootRights'
|
||||||
}
|
}
|
||||||
|
|
||||||
It 'redirects only the student Desktop and Documents folders' {
|
It 'redirects only the student Desktop and Documents folders' {
|
||||||
@@ -118,11 +131,11 @@ Describe 'SGU role-specific roaming policies' {
|
|||||||
|
|
||||||
It 'derives valid deterministic AD names for long storage account names' {
|
It 'derives valid deterministic AD names for long storage account names' {
|
||||||
$name = Get-SguStorageSamAccountName -StorageName 'abcdefghijklmnopqrstuvwx'
|
$name = Get-SguStorageSamAccountName -StorageName 'abcdefghijklmnopqrstuvwx'
|
||||||
$name.Length | Should Be 20
|
$name.Length | Should Be 15
|
||||||
$name | Should Match '^sgufs[0-9a-f]{15}$'
|
$name | Should Match '^sgufs[0-9a-f]{10}$'
|
||||||
(Get-SguStorageSamAccountName -StorageName 'abcdefghijklmnopqrstuvwx') | Should Be $name
|
(Get-SguStorageSamAccountName -StorageName 'abcdefghijklmnopqrstuvwx') | Should Be $name
|
||||||
(Get-SguStorageSamAccountName -StorageName 'sguroam1234567890123') |
|
(Get-SguStorageSamAccountName -StorageName 'sguroam1234567') |
|
||||||
Should Be 'sguroam1234567890123'
|
Should Be 'sguroam1234567'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user