Badgerati / Pode.Web

Web template framework for use with the Pode PowerShell web server
MIT License
187 stars 24 forks source link

IIS Auth not displaying username #129

Closed PrzemyslawKlys closed 2 years ago

PrzemyslawKlys commented 3 years ago

I know it sounds noobish but I'm trying to get information who is the person filling up the form and whatever I do I can't get it to work. I tried Get-PodeWebUserName, $WebEvent.Auth.User and nothing is being displayed. I have IIS with Windows Auth enabled. Any tip what I'm doing wrong?

Start-PodeServer -StatusPageExceptions Show {
    Add-PodeEndpoint -Address 127.0.0.1 -Protocol Http

    $User = Add-PodeAuthIIS -Name 'IISAuth' -Sessionless -ScriptBlock {
        param($user)

        # check or load extra data

        return @{ User = $user }
    }

    New-PodeLoggingMethod -Terminal

    # set the use of templates, and set a login page
    Use-PodeWebTemplates -Title 'Administrative Requests' -Theme Dark -NoPageFilter

    $Steps = New-PodeWebSteps -Name 'AddUser' -ArgumentList $EmailParameters -Steps @(
        New-PodeWebStep -Name 'Details' -Icon 'User-Plus' -Content @(
            New-POdeWebTextBox -Name 'Please provide ID' -Type Text -Value $User.User
            New-PodeWebTextbox -Name 'FirstName' -Type Text -Value (Get-PodeWebUsername)
        )

    ) -ScriptBlock {

    }
    Add-PodeWebPage -Name 'Admin 1' -Icon 'Settings' -ScriptBlock {

    } -Layouts $Steps
}
Badgerati commented 3 years ago

Hey @PrzemyslawKlys,

for Get-PodeWebUsername and $WebEvent these only exist/work when dealing with a web request; when just creating a the elements/layouts these will always return $null as they're no request to handle.

A -ScriptBlock is something I've got noted down to add onto New-PodeWebTexbox, so the value can be generated during a request - not on server start.

Here though, you can get around that by building the New-PodeWebSteps within the Page's scriptblock - this will build the elements on the fly during a request, rather than once on server start, and the $WebEvent will be available within the Page's scriptblock:

Start-PodeServer -StatusPageExceptions Show {
    Add-PodeEndpoint -Address 127.0.0.1 -Protocol Http

    Enable-PodeSessionMiddleware -Duration 120 -Extend
    Add-PodeAuthIIS -Name 'IISAuth'

    New-PodeLoggingMethod -Terminal

    # set the use of templates, and set a login page
    Use-PodeWebTemplates -Title 'Administrative Requests' -Theme Dark -NoPageFilter

    Add-PodeWebPage -Name 'Admin 1' -Icon 'Settings' -ArgumentList $EmailParameters -ScriptBlock {
        param($EmailParameters)

        New-PodeWebSteps -Name 'AddUser' -ArgumentList $EmailParameters -Steps @(
            New-PodeWebStep -Name 'Details' -Icon 'User-Plus' -Content @(
                New-PodeWebTextBox -Name 'Please provide ID' -Type Text -Value $WebEvent.Auth.User
                New-PodeWebTextbox -Name 'FirstName' -Type Text -Value (Get-PodeWebUsername)
            )
        ) -ScriptBlock {}
    }
}

^ That should do the trick. Also worth noting that Add-PodeAuthIIS doesn't return the user object inline. The return is handled within Pode's middleware, and it's the user object set as $WebEvent.Auth.User :)

(also, I removed -Sessionless as here you'll definitely want sessions. Sessionless is mostly just for REST APIs where authentication doesn't require a session)

PrzemyslawKlys commented 3 years ago

Doesn't seem to work. Had to remove argument list from Add-PodeWebpage, but still no user is shown in textbox.

Start-PodeServer -StatusPageExceptions Show {
    Add-PodeEndpoint -Address 127.0.0.1 -Protocol Http

    Enable-PodeSessionMiddleware -Duration 120 -Extend
    Add-PodeAuthIIS -Name 'IISAuth'

    New-PodeLoggingMethod -Terminal

    # set the use of templates, and set a login page
    Use-PodeWebTemplates -Title 'Administrative Requests' -Theme Dark -NoPageFilter

    Add-PodeWebPage -Name 'Admin 1' -Icon 'Settings' -ScriptBlock {
        param($EmailParameters)

        New-PodeWebSteps -Name 'AddUser' -ArgumentList $EmailParameters -Steps @(
            New-PodeWebStep -Name 'Details' -Icon 'User-Plus' -Content @(
                New-PodeWebTextBox -Name 'Please provide ID' -Type Text -Value $WebEvent.Auth.User
                New-PodeWebTextbox -Name 'FirstName' -Type Text -Value (Get-PodeWebUsername)
            )
        ) -ScriptBlock {}
    }
}
Badgerati commented 3 years ago

Oh wow, Add-PodeWebPage doesn't have an ArgumentList! 😱 Well, that... needs adding!

That will need to be added to make it function properly (or set the email parameters using the [shared state]()). For IIS, try doing Add-PodeAuthMiddleware -Name 'IISAuth' -Authentication 'IISAuth' just after Add-PodeAuthIIS - That could do it, as right now the auth method has been created, but hasn't been bound to anything as middleware for use. Usually Set-LoginPage would do this, expect in this case there's not really any need for the login page.

ittchmh commented 3 years ago

I just resolved Kerberos authentication/impersonation issues using Domain Account and Group Managed Service Account

Wrong settings in documentation!

Use case scenario:

Infrastructure setup

Start-PodeServer -StatusPageExceptions Show {

# add a simple endpoint
Add-PodeEndpoint -Address localhost -Port 443 -Protocol Https

# set the use of the pode.web templates
Use-PodeWebTemplates -Title 'Example' -Theme Dark

# add the page
Add-PodeWebPage -Name Processes -Icon Activity -Layouts @(
    New-PodeWebChart -Name 'Top Processes' -Type Bar -AutoRefresh -AsCard -ScriptBlock {
        Get-Process |
            Sort-Object -Property CPU -Descending |
            Select-Object -First 10 |
            ConvertTo-PodeWebChartData -LabelProperty ProcessName -DatasetProperty CPU
    }
)
Add-PodeAuthIIS -Name 'IISAuth' -Sessionless

Add-PodeRoute -Method Get -Path '/test-kerberos' -Authentication 'IISAuth' -ScriptBlock {
    Write-PodeJsonResponse -Value @{ User = $WebEvent.Auth.User }
}

Add-PodeRoute -Method Get -Path '/test-kerberos-impersonation' -Authentication 'IISAuth' -ScriptBlock {
    [System.Security.Principal.WindowsIdentity]::RunImpersonated($WebEvent.Auth.User.Identity.AccessToken, {
    $newIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() | Select-Object -ExpandProperty 'Name'
    Write-PodeTextResponse -Value "You are running this command as the server user $newIdentity"
    })
}

}

### Configuration steps for _Domain Account_:

1. Create Domain Users in AD for Pode AppPool - **Pode.Svc**
2. Create SPNs: 
``` cmd
setspn -d HTTP/PodeServer Contoso\pode.svc
setspn -d HTTP/PodeServer.Contoso.com Contoso\pode.svc
  1. Configure Pode.Svc user Delegation - Trust this user for delegation ...
  2. Configure Pode Website to use PodeServer.Contoso.com as Host Name
  3. Configure Pode Website AppPool to use Contoso\pode.svc as Identity
  4. !!! Important !!! Add PTR DNS record PodeServer.Contoso.com pointing to Load Balancer IP
  5. Open URLs:

Configuration steps for Group Managed Service Account

  1. Create Group Managed Service Account, configure SPNs and Delegation
    New-ADGroup -Name "Pode Authorized Hosts" -SamAccountName "pode.hosts" -GroupScope Global
    New-ADServiceAccount -Name "gmsaPodeSvc" -DnsHostName "PodeServer.Contoso.com" `
                     -ServicePrincipalNames "host/PodeServer", "host/PodeServer.Contoso.com","http/PodeServer", "http/PodeServer.Contoso.com" `
                     -PrincipalsAllowedToRetrieveManagedPassword "pode.hosts"
    Set-ADAccountControl -Identity gmsaPodeSvc$ -TrustedForDelegation $true -TrustedToAuthForDelegation $false
    Add-ADGroupMember -Identity "pode.hosts" -Members "iis1$","iis2$"
    Restart-Computer -ComputerName "iis1","iis2" -force
  2. !!! Important !!! Both IIS Servers must be rebooted to update Group Membership
  3. On both IIS Servers:
    Add-WindowsFeature RSAT-AD-PowerShell
    Install-ADServiceAccount gmsaPodeSvc
  4. Configure Pode Website to use PodeServer.Contoso.com as Host Name
  5. Configure Pode Website AppPool to use Contoso\gmsaPodeSvc$ as Identity
  6. !!! Important !!! Add PTR DNS record PodeServer.Contoso.com pointing to Load Balancer IP
  7. Open URLs:
Badgerati commented 3 years ago

Hey @ittchmh,

This write-up is amazing! The IIS settings in the documentation are more for simple IIS setups to get going. It would be great to have the above in the IIS hosting docs for more complex IIS/Kerberos setups, would you want to update the docs with the above under like an "Advanced Kerberos" heading? :)

ittchmh commented 3 years ago

Sure, will do

ittchmh commented 2 years ago

Oh wow, Add-PodeWebPage doesn't have an ArgumentList! 😱 Well, that... needs adding!

That will need to be added to make it function properly (or set the email parameters using the shared state). For IIS, try doing Add-PodeAuthMiddleware -Name 'IISAuth' -Authentication 'IISAuth' just after Add-PodeAuthIIS - That could do it, as right now the auth method has been created, but hasn't been bound to anything as middleware for use. Usually Set-LoginPage would do this, expect in this case there's not really any need for the login page.

Hello, by setting: Add-PodeAuthMiddleware -Name 'IISAuth' -Authentication 'IISAuth' Kerberos authentication doesn't work, it is switching to basic NTLM

How to enable Kerberos authentication with Pode.Web ? Please help!

ittchmh commented 2 years ago

I added Set-PodeWebHomePage and Kerberos auth now works, but $WebEvent.Auth is null WWW-Authenticate: Negotiate HTTP Header set, means $WebEvent.Auth must be populated with logged in user data, but it's not Get-PodeState -Name "pode.web.auth" -eq $null as well Here is the code:

Import-Module Pode.Web
Import-Module AWS.Tools.Route53 -UseWindowsPowerShell

# . .\Setup.ps1
# . ~\Documents\PowerShell\Modules\SR-Azure\AzureCredsFunctions.ps1
Start-PodeServer -StatusPageExceptions Show {

    Import-PodeModule -Name ActiveDirectory
    Import-PodeModule -Name Microsoft.PowerShell.SecretManagement
    Import-PodeModule -Name AzFilesHybrid
    Import-PodeModule -Name ExchangeOnlineManagement
    Import-PodeModule -Path 'C:\Users\gmsaAdmin$\Documents\PowerShell\Modules\SR-Azure\SR-Azure.psm1'

    Connect-AzAccount -ApplicationId $DefaultAzAccount.ApplicationId -TenantId $DefaultTenantId -CertificateThumbprint $DefaultAzAccount.CertificateThumbprint -SubscriptionId $DefaultSubscriptionId
    $Global:CacheExpired = $null
    New-PodeLoggingMethod -File -Name 'errors' | Enable-PodeErrorLogging -Levels Error, Warning, Informational, Verbose
    # add a simple endpoint
    Add-PodeEndpoint -Address localhost -Port 80 -Protocol Http -Name Admin

    Enable-PodeSessionMiddleware -Duration 120 -Extend
    Add-PodeAuthIIS -Name 'IISAuth' -Groups @("mgm") # -Sessionless 
    Add-PodeAuthMiddleware -Name 'IISAuth' -Authentication 'IISAuth'

    # set the use of the pode.web templates
    Use-PodeWebTemplates -Title 'SR Admin' -EndpointName Admin
    Import-PodeWebStylesheet -Url '/my-styles.css'

    New-PodeWebNavLink -Name "Projects" -Id "Project" -Url "/pages/projects"

    Set-PodeWebHomePage -Layouts @(
        New-PodeWebHero -Title 'Welcome!' -Message 'This is the home page' -Content @(
            New-PodeWebText -Value '$WebEvent.Auth value is:' -InParagraph #-Alignment Center
            New-PodeWebCodeBlock -Value $($WebEvent.Auth | ConvertTo-Json -Depth 10)

            New-PodeWebText -Value 'Get-PodeWebUsername output' -InParagraph
            New-PodeWebCodeBlock -Value $(Get-PodeWebUsername | ConvertTo-Json -Depth 10)
        )
    )
}

image

Badgerati commented 2 years ago

If the authentication is being done by IIS, then Pode shouldn't affect the authentication via IIS 🤔 a little strange if it is somehow, causing the NTLM check, not too sure how though.

For $WebEvent.Auth, at the point the elements are being created $WebEvent won't exist. -Layouts creates the elements there and then on server start, and not in the scope of a request; which is why they'll be null/empty. Add-PodeWebPage has a -ScriptBlock parameter which creates the elements dynamically, but within the scope of a request; so $WebEvent will exist:

Add-PodeWebPage -Name 'Example' -ScriptBlock {
    New-PodeWebHero -Title 'Welcome!' -Message 'This is the home page' -Content @(
        New-PodeWebText -Value '$WebEvent.Auth value is:' -InParagraph #-Alignment Center
        New-PodeWebCodeBlock -Value $($WebEvent.Auth | ConvertTo-Json -Depth 10)

        New-PodeWebText -Value 'Get-PodeWebUsername output' -InParagraph
        New-PodeWebCodeBlock -Value $(Get-PodeWebUsername | ConvertTo-Json -Depth 10)
    )
}

Set-PodeWebHomePage doesn't have the -ScriptBlock yet, something that needs to be added.

ittchmh commented 2 years ago

Add-PodeWebPage -Name 'Example' -ScriptBlock { Works! thanks I was testing using Add-PodeWebPage -Name 'Example' -Layouts @(, so that's why it was epmty

I was able to enable Kerberos Authentication by adding parameter -Authentication to Set-PodeWebHomePage function And setting $auth variable like this:

https://github.com/ittchmh/Pode.Web/commit/4a70ed5ef526372c53dd08ac552e8627ccad483c

I am adding code to set state, but those states are visible only on other Pages and not visible on HomePage I see good objects in exported Export-Clixml Files authdata.xml and authprops.xml after HomePage opened

So it is probably because Set-PodeWebHomePage need -ScriptBlock parameter And function like Initialize-PodeWebIISAuth to set states and authentication property to all Add-PodeRoute functions