Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8baa47fe1e |
@@ -55,11 +55,11 @@ Operational documentation:
|
||||
- [Domain monitoring, usage reports, and six-month retention](docs/monitoring.md)
|
||||
- [Decision: do not persist password verifiers in Redis](docs/decisions/0001-no-password-cache.md)
|
||||
|
||||
| Prefix | Role | Default OU |
|
||||
|---|---|---|
|
||||
| `DO` | Professor / docente | `OU=Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx` |
|
||||
| `AL` | Student / alumno | `OU=Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx` |
|
||||
| `AD` | Administrative | `OU=Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx` |
|
||||
| Prefix | Role | Default OU | Security group in the same OU |
|
||||
|---|---|---|---|
|
||||
| `DO` | Professor / docente | `OU=Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx` | `SGU-Docentes` |
|
||||
| `AL` | Student / alumno | `OU=Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx` | `SGU-Alumnos` |
|
||||
| `AD` | Administrative | `OU=Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx` | `SGU-Administrativos` |
|
||||
|
||||
If the broker or institutional NTLM authority is unavailable, the provider
|
||||
submits the unchanged credentials to Windows for normal AD/cached-domain
|
||||
|
||||
@@ -76,8 +76,11 @@ password outcome.
|
||||
|
||||
Every synchronization also enforces one idempotent security-group membership
|
||||
from the classified institutional prefix: `AL` to `SGU-Alumnos`, `AD` to
|
||||
`SGU-Administrativos`, and `DO` to `SGU-Docentes`. This happens synchronously
|
||||
inside the broker before the institutional password is written to AD. A missing
|
||||
`SGU-Administrativos`, and `DO` to `SGU-Docentes`. Each role group is stored
|
||||
inside its corresponding user OU. During an upgrade,
|
||||
the bootstrap moves a legacy group from the `Usuarios-SGU` root while preserving
|
||||
its SID and memberships instead of creating a duplicate. Membership enforcement
|
||||
happens synchronously inside the broker before the institutional password is written to AD. A missing
|
||||
or inaccessible role group therefore fails provisioning instead of leaving a
|
||||
new usable account without its authorization classification. Existing accounts
|
||||
are repaired automatically on their next successful SGU authentication.
|
||||
|
||||
@@ -76,16 +76,23 @@ if (-not $serverCertificate.Verify()) {
|
||||
}
|
||||
|
||||
Import-Module ActiveDirectory -ErrorAction Stop
|
||||
|
||||
function ConvertTo-LdapFilterValue {
|
||||
param([Parameter(Mandatory)][string]$Value)
|
||||
|
||||
return $Value.Replace('\', '\5c').Replace('*', '\2a').Replace('(', '\28').Replace(')', '\29').Replace(([string][char]0), '\00')
|
||||
}
|
||||
|
||||
$usersOuName = 'Usuarios-SGU'
|
||||
$usersOuDn = "OU=$usersOuName,$BaseDn"
|
||||
if ([string]::IsNullOrWhiteSpace($ProfessorGroupDn)) {
|
||||
$ProfessorGroupDn = "CN=SGU-Docentes,$usersOuDn"
|
||||
$ProfessorGroupDn = "CN=SGU-Docentes,OU=Docentes,$usersOuDn"
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($StudentGroupDn)) {
|
||||
$StudentGroupDn = "CN=SGU-Alumnos,$usersOuDn"
|
||||
$StudentGroupDn = "CN=SGU-Alumnos,OU=Alumnos,$usersOuDn"
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($AdministrativeGroupDn)) {
|
||||
$AdministrativeGroupDn = "CN=SGU-Administrativos,$usersOuDn"
|
||||
$AdministrativeGroupDn = "CN=SGU-Administrativos,OU=Administrativos,$usersOuDn"
|
||||
}
|
||||
|
||||
if ($CreateMissingOus) {
|
||||
@@ -152,13 +159,30 @@ foreach ($definition in $roleGroupDefinitions) {
|
||||
throw "$($definition.Role)GroupDn must start with a simple CN component."
|
||||
}
|
||||
$groupName = $groupDnMatch.Groups['Name'].Value
|
||||
$groupPath = $groupDnMatch.Groups['Path'].Value
|
||||
if ($groupName.Length -gt 20) {
|
||||
throw "$($definition.Role) group name exceeds the 20-character sAMAccountName limit."
|
||||
}
|
||||
New-ADGroup -Name $groupName -SamAccountName $groupName `
|
||||
-GroupCategory Security -GroupScope Global `
|
||||
-Path $groupDnMatch.Groups['Path'].Value `
|
||||
-Description $definition.Description -Server $LdapHost | Out-Null
|
||||
|
||||
$matchingGroups = @(Get-ADGroup `
|
||||
-LDAPFilter "(sAMAccountName=$(ConvertTo-LdapFilterValue -Value $groupName))" `
|
||||
-SearchBase $BaseDn -SearchScope Subtree -Server $LdapHost -ErrorAction Stop)
|
||||
if ($matchingGroups.Count -gt 1) {
|
||||
throw "More than one Active Directory group uses sAMAccountName $groupName; the bootstrap cannot select one safely."
|
||||
}
|
||||
if ($matchingGroups.Count -eq 1) {
|
||||
if ($matchingGroups[0].GroupCategory -ne 'Security') {
|
||||
throw "$($definition.Role)GroupDn must identify a security group."
|
||||
}
|
||||
Move-ADObject -Identity $matchingGroups[0].DistinguishedName `
|
||||
-TargetPath $groupPath -Server $LdapHost -Confirm:$false -ErrorAction Stop
|
||||
}
|
||||
else {
|
||||
New-ADGroup -Name $groupName -SamAccountName $groupName `
|
||||
-GroupCategory Security -GroupScope Global `
|
||||
-Path $groupPath `
|
||||
-Description $definition.Description -Server $LdapHost | Out-Null
|
||||
}
|
||||
$roleGroup = Get-ADGroup -Identity $definition.Dn -Server $LdapHost -ErrorAction Stop
|
||||
}
|
||||
if (-not $roleGroup) {
|
||||
|
||||
@@ -116,7 +116,7 @@ Bootstrap reproducible para el laboratorio SGU.
|
||||
- `sgu-azure-infrastructure-$Version.zip`: despliega mediante Bicep una VM Windows Server 2025, red privada, IP pública protegida por NSG y Azure VPN Gateway P2S; también genera certificados por equipo y descarga el perfil de cliente.
|
||||
- El bootstrap Azure conserva la IP privada administrada por la NIC de Azure, autoriza el pool P2S en los firewalls SGU y nunca publica LDAP, Kerberos, SMB, RPC, WinRM ni el Auth Broker directamente a Internet.
|
||||
- Los Windows 11 Pro pueden instalar un perfil IKEv2 de todos los usuarios con certificado de máquina, DNS dividido para `lci.lasalle.mx` y ejecutarlo desde la pantalla de inicio de sesión antes de autenticar una cuenta de dominio nueva.
|
||||
- El Auth Broker clasifica sin tareas programadas cada cuenta autenticada: `AL` se agrega a `SGU-Alumnos`, `AD` a `SGU-Administrativos` y `DO` a `SGU-Docentes`; el bootstrap crea estos grupos de seguridad de forma idempotente.
|
||||
- El Auth Broker clasifica sin tareas programadas cada cuenta autenticada: `AL` se agrega a `SGU-Alumnos`, `AD` a `SGU-Administrativos` y `DO` a `SGU-Docentes`; el bootstrap crea cada grupo dentro de la OU de su rol y migra idempotentemente cualquier grupo heredado sin cambiar su SID.
|
||||
- El enriquecimiento obtiene el sexo de los módulos SGU de personal/alumnos, lo conserva como la línea administrada `SGU-Gender: Male|Female` en Notas de AD y adapta el fondo de Windows/Linux; cuando falta utiliza redacción neutral.
|
||||
- El servidor configura WEF/WEC para registrar sesiones y fallos, inventariar el estado alcanzable de las máquinas cada cinco minutos y conservar durante 183 días tanto esos eventos como el diagnóstico estructurado del Auth Broker.
|
||||
- Windows Home se detecta y se rechaza con una explicación, ya que no admite unión a Active Directory ni RDP host.
|
||||
|
||||
@@ -176,11 +176,11 @@ public sealed class ActiveDirectoryOptions
|
||||
|
||||
public string AdministrativeOuDn { get; init; } = "OU=Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx";
|
||||
|
||||
public string ProfessorGroupDn { get; init; } = "CN=SGU-Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx";
|
||||
public string ProfessorGroupDn { get; init; } = "CN=SGU-Docentes,OU=Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx";
|
||||
|
||||
public string StudentGroupDn { get; init; } = "CN=SGU-Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx";
|
||||
public string StudentGroupDn { get; init; } = "CN=SGU-Alumnos,OU=Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx";
|
||||
|
||||
public string AdministrativeGroupDn { get; init; } = "CN=SGU-Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx";
|
||||
public string AdministrativeGroupDn { get; init; } = "CN=SGU-Administrativos,OU=Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx";
|
||||
|
||||
public string RemoteDesktopGroupDn { get; init; } = string.Empty;
|
||||
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
"ProfessorOuDn": "OU=Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"StudentOuDn": "OU=Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"AdministrativeOuDn": "OU=Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"ProfessorGroupDn": "CN=SGU-Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"StudentGroupDn": "CN=SGU-Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"AdministrativeGroupDn": "CN=SGU-Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"ProfessorGroupDn": "CN=SGU-Docentes,OU=Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"StudentGroupDn": "CN=SGU-Alumnos,OU=Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"AdministrativeGroupDn": "CN=SGU-Administrativos,OU=Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx",
|
||||
"RemoteDesktopGroupDn": "",
|
||||
"DefaultCompany": "La Salle",
|
||||
"CreateMissingOus": false
|
||||
|
||||
@@ -31,9 +31,9 @@ public sealed class BrokerOptionsTests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(InstitutionalRole.Student, "CN=SGU-Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx")]
|
||||
[InlineData(InstitutionalRole.Administrative, "CN=SGU-Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx")]
|
||||
[InlineData(InstitutionalRole.Professor, "CN=SGU-Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx")]
|
||||
[InlineData(InstitutionalRole.Student, "CN=SGU-Alumnos,OU=Alumnos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx")]
|
||||
[InlineData(InstitutionalRole.Administrative, "CN=SGU-Administrativos,OU=Administrativos,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx")]
|
||||
[InlineData(InstitutionalRole.Professor, "CN=SGU-Docentes,OU=Docentes,OU=Usuarios-SGU,DC=lci,DC=lasalle,DC=mx")]
|
||||
public void DefaultRoleGroupMappingsMatchInstitutionalPrefixes(InstitutionalRole role, string expectedGroupDn)
|
||||
{
|
||||
ActiveDirectoryOptions options = new();
|
||||
|
||||
Reference in New Issue
Block a user