From f3bb0212f90f3603cf8baa031e0032230e258632 Mon Sep 17 00:00:00 2001 From: Gal Szkolnik Date: Fri, 21 Jul 2023 04:37:56 +0000 Subject: [PATCH] Adding Utility scripts + Setup Documentation --- 00-Setup.md | 29 ++++++++++++++--- Connect-AppCert.ps1 | 5 +++ Create-Cert.ps1 | 78 +++++++++++++++++++++++++++++++++++++++++++++ Load-Cert.ps1 | 61 +++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 Connect-AppCert.ps1 create mode 100644 Create-Cert.ps1 create mode 100644 Load-Cert.ps1 diff --git a/00-Setup.md b/00-Setup.md index 1b96508..7dcfe4b 100644 --- a/00-Setup.md +++ b/00-Setup.md @@ -6,13 +6,34 @@ ~~3. Install AzureAD PowerShell Module ...~~ ~~3. Install Microsoft Graph Module ...~~ -2. Working with mcr.microsoft.com/microsoftgraph/powershell docker +2. Working with **mcr.microsoft.com/microsoftgraph/powershell** docker container. Chose this path after failing to setup my personal local Windows 11 machine with the above libraries, realizing this should have been my first choice. -3. Tested connection from within the docker conatiner: + NOTE: All console actions described below are from within the docker + container. + All Web UI action were done on my personal machine. I do not describe + the process of copying over the file from the container / remote + machine into my local-browser machine. + ```console - .../varonis $ - ``` \ No newline at end of file + # bash command line: + docker run --rm -it -v "$PWD/data:/data" \ + -v "$PWD/data/.dotnet:/root/.dotnet" \ + -v "$PWD/src:/data/src" \ + -w "/data" \ + mcr.microsoft.com/microsoftgraph/powershell + ``` + +3. Following [this tutorial page](https://learn.microsoft.com/en-us/graph/tutorials/powershell-app-only?tabs=linux-macos&tutorial-step=1#register-application-for-app-only-authentication) + I've created the following utility scripts to help with this process: + + 1. [`Create-Cert`](Create-Cert.ps1) - + Create a self-signed certificate and load it into the .NET + cert-store (see Load-Cert next). + 2. [`Load-Cert`](Load-Cert.ps1) - + Load certificate files into the local .NET cert-store. + 3. [`Connect-AppCert`](Connect-AppCert.ps1) - + This will be used to establish a new app connection. diff --git a/Connect-AppCert.ps1 b/Connect-AppCert.ps1 new file mode 100644 index 0000000..44a985c --- /dev/null +++ b/Connect-AppCert.ps1 @@ -0,0 +1,5 @@ +$clientId='56cb6a8c-92bc-484d-8919-b03af7cf4e80' +$tenantID='6b4b1b0d-23f1-4063-bbbd-b65e2984b893' +$Cert = ./src/Load-Cert.ps1 +$certThumb=$Cert.Thumbprint +Connect-MgGraph -ClientId $clientId -TenantId $tenantId -CertificateThumb $certThumb diff --git a/Create-Cert.ps1 b/Create-Cert.ps1 new file mode 100644 index 0000000..96d8cc8 --- /dev/null +++ b/Create-Cert.ps1 @@ -0,0 +1,78 @@ +# Following guide on: +# +# +# This script creates a new +[CmdletBinding()]param([switch]$Force) # This adds common parameters, like ErrorAction + +$local:Create = -not ( + (Test-Path 'powershell-app.pem') -and + (Test-Path 'powershell-app.crt') -and + (Test-Path 'powershell-app.key') +) +if( $Force -or $Create ) { + foreach( $local:ext in @('.key', '.pem', '.crt', '.pfx') ) { + if( Test-Path "powershell-app${ext}" ) { + Remove-Item -Force -LiteralPath "powershell-app${ext}" -Verbose + $Create = $true + } + } +} +if( $Create ) { + # Create private key + & openssl genrsa -des3 -out powershell-app.key ` + -passout "pass:keypassphrase" ` + 2048 + # -passout pass:... usage is ONLY acceptable in insecure testing env. + + # Create Signed Certificate with DES3 Encryption (with an INSECURE passkey) + & openssl req -x509 ` + -key 'powershell-app.key' -passin "pass:keypassphrase" ` + -sha256 -days 365 -subj '/CN=PowerShell App-Only' ` + -out 'powershell-app.crt' ` + -passout "pass:pempassphrase" + # -passin/passout pass:... usage is ONLY acceptable in insecure + # testing env. + + # Expose the public key for easy copying by non-root users + chmod 664 'powershell-app.crt' + + if( Test-Path 'powershell-app.pfx' ) { Remove-Item 'powershell-app.pfx' } + + Write-Host -ForegroundColor Green "`nKEY, PEM and CRT certificate files created`n" +} + +if( -not (Test-Path 'powershell-app.pfx') ) { + $Create = $true + # Self-Sign Certificate Request + & openssl pkcs12 -export -in 'powershell-app.crt' ` + -inkey 'powershell-app.key' -passin "pass:keypassphrase"` + -out 'powershell-app.pfx' -passout "pass:pfxpassphrase" + # -passin/passout pass:... usage is ONLY acceptable in insecure + # testing env. + + Write-Host -ForegroundColor Green "`nPFX certificate file created`n" + Write-Host "You can now load certificate by running ./src/Load-Cert.ps1" + Write-Host "" + Write-Host -ForegroundColor Red "NOTE: You will need to register this certifcate!" + Write-Host "Grab the powershell-app.crt public key file and Follow" + Write-Host "instructions in the tutorial link below: (very long link)" + Write-Host " https://learn.microsoft.com/en-us/graph/tutorials/powershell-app-only?tabs=linux-macos&tutorial-step=1#register-application-for-app-only-authentication" + Write-Host "" + } + +$Cert = ./src/Load-Cert.ps1 -Force:$Create +Write-Host -ForegroundColor Green "Certificate loaded with ./src/Load-Cert.ps1" +Write-Host "" +Write-Host -ForegroundColor Yellow "Recommendation:" +Write-Host "If you haven't already, it is recommended you create a script " +Write-Host "./src/Connect-AppCert.ps1 to quickly connect. It should have at" +Write-Host "least the following content:" +Write-Host "########################################################################################" +Write-Host "`$clientId='' # Get this from the Azure Portal" +Write-Host "`$tenantID='' # Get this from the Azure Portal" +Write-Host "`$certThumb='$($Cert.Thumbprint )' # grabbed from ./src/Load-Cert.ps1" +Write-Host "" +Write-Host 'Connect-MgGraph -ClientId $clientId -TenantId $tenantId -CertificateThumb $certThumb' +Write-Host "########################################################################################" +Write-Host "" +Write-Host "NOTE that Connect-AppCert.ps1 is listed in .gitignore and will not be saved to the git repo due to it's sensitive nature." \ No newline at end of file diff --git a/Load-Cert.ps1 b/Load-Cert.ps1 new file mode 100644 index 0000000..3871e7b --- /dev/null +++ b/Load-Cert.ps1 @@ -0,0 +1,61 @@ +using namespace System.Security.Cryptography.X509Certificates +[CmdletBinding()]param([switch]$Force) # This adds common parameters, like ErrorAction + +# Code from https://stackoverflow.com/a/42108420/799379 +# Since this is mimicing a C# clause, the verb isn't an approved one. +# If you see a Warning, it's OK. This was an intentional choice. +function Using-Object { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position=0)] + [AllowEmptyString()] + [AllowEmptyCollection()] + [AllowNull()] + [Object] + $InputObject, + + [Parameter(Mandatory = $true, Position=1)] + [scriptblock] + $ScriptBlock + ) + + try + { + Invoke-Command $ScriptBlock + } + finally + { + if ($InputObject -is [System.IDisposable]) + { + $InputObject.Dispose() + Write-Verbose "IDisposable Disposed" + } + } +} + +Using-Object ([X509Store]::new('My', 'CurrentUser', 'ReadWrite') | Tee-Object -Variable store) { + $local:Cert = $store.Certificates | + Where-Object Subject -eq "CN=PowerShell App-Only" + if( $tst -and $Force ) { + $store.Remove($Cert); + $Cert = $null + } + if( -not $Cert ) { + $store.Add([X509Certificate2]::new( + './powershell-app.pfx', + "pfxpassphrase", # BAD PRACTICE: Hard coded - BEWARE + [X509KeyStorageFlags]::PersistKeySet) + ) + + $Cert = $store.Certificates | + Where-Object Subject -eq "CN=PowerShell App-Only" + + if( -not $Cert ) { + throw "Failed to create certificate" + } + + Write-Host -ForegroundColor Green "Certificate created" + } + $Cert +} +$store = $null \ No newline at end of file