Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Az.ConnectedKubernetes] support proxy environments and fix issues (from generation) #20955

Merged
merged 18 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 152 additions & 11 deletions src/ConnectedKubernetes/custom/New-AzConnectedKubernetes.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,55 @@ function New-AzConnectedKubernetes {
# The ID of the target subscription.
${SubscriptionId},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')]
[System.Uri]
# The http URI of the proxy server for the kubernetes cluster to use
${HttpProxy},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')]
[System.Uri]
# The https URI of the proxy server for the kubernetes cluster to use
${HttpsProxy},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')]
[System.String]
# The comma-separated list of hostnames that should be excluded from the proxy server for the kubernetes cluster to use
${NoProxy},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')]
[System.String]
# The path to the certificate file for proxy or custom Certificate Authority.
${ProxyCert},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')]
[ValidateRange(0,3600)]
[Int]
# The time required (in seconds) for the arc-agent pods to be installed on the kubernetes cluster.
${OnboardingTimeout} = 600,

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')]
[System.Management.Automation.SwitchParameter]
# Flag to disable auto upgrade of arc agents.
${DisableAutoUpgrade},

[Parameter()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Path')]
[System.String]
# Override the default container log path to enable fluent-bit logging.
${ContainerLogPath},

[Parameter(HelpMessage="Path to the kube config file")]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')]
[System.String]
# Path to the kube config file
${KubeConfig},

[Parameter(HelpMessage="Kubconfig context from current machine")]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Body')]
[System.String]
Expand Down Expand Up @@ -172,14 +215,14 @@ function New-AzConnectedKubernetes {
[Parameter(DontShow)]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')]
[System.Uri]
# The URI for the proxy server to use
# The URI of the proxy server for host os to use
${Proxy},

[Parameter(DontShow)]
[ValidateNotNull()]
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.Category('Runtime')]
[System.Management.Automation.PSCredential]
# Credentials for a proxy server to use for the remote call
# The credential of the proxy server for host os to use
${ProxyCredential},

[Parameter(DontShow)]
Expand Down Expand Up @@ -224,7 +267,7 @@ function New-AzConnectedKubernetes {
if ($PSBoundParameters.ContainsKey('KubeContext')) {
$Null = $PSBoundParameters.Remove('KubeContext')
}
if (($KubeContext -eq $null) -or ($KubeContext -eq '')) {
if (($null -eq $KubeContext) -or ($KubeContext -eq '')) {
$KubeContext = kubectl config current-context
}

Expand Down Expand Up @@ -260,9 +303,10 @@ function New-AzConnectedKubernetes {
#EndRegion

#Region get release namespace
Set-Variable ReleaseInstallNamespace -option Constant -value "azure-arc-release"
$ReleaseNamespace = $null
try {
$ReleaseNamespace = (helm status azure-arc -o json --kubeconfig $KubeConfig --kube-context $KubeContext | ConvertFrom-Json).namespace
$ReleaseNamespace = (helm status azure-arc -o json --kubeconfig $KubeConfig --kube-context $KubeContext -n $ReleaseInstallNamespace | ConvertFrom-Json).namespace
} catch {
Write-Error "Fail to find the namespace for azure-arc."
}
Expand Down Expand Up @@ -302,6 +346,15 @@ function New-AzConnectedKubernetes {
} else {
$ReleaseTrain = 'stable'
}
#Requires -Module Az.Resources
$AzLocation = Get-AzLocation | Where-Object { ($_.DisplayName -ieq $Location) -or ($_.Location -ieq $Location)}
$Region = $AzLocation.Location
if ($null -eq $Region) {
Write-Error "Invalid location: $Location"
return
} else {
$Location = $Region
}
$ChartLocationUrl = "https://${Location}.dp.kubernetesconfiguration.azure.com/azure-arc-k8sagents/GetLatestHelmPackagePath?api-version=2019-11-01-preview&releaseTrain=${ReleaseTrain}"

$Uri = [System.Uri]::New($ChartLocationUrl)
Expand All @@ -315,7 +368,7 @@ function New-AzConnectedKubernetes {
$HeaderParameter = @{
"Authorization" = "Bearer $AccessToken"
}
$Response = Invoke-WebRequest -Uri $Uri -Headers $HeaderParameter -Method Post
$Response = Invoke-WebRequest -Uri $Uri -Headers $HeaderParameter -Method Post -UseBasicParsing
if ($Response.StatusCode -eq 200) {
$RegisteryPath = ($Response.Content | ConvertFrom-Json).repositoryPath
} else {
Expand Down Expand Up @@ -349,21 +402,109 @@ function New-AzConnectedKubernetes {
#Endregion

$RSA = [System.Security.Cryptography.RSA]::Create(4096)
$AgentPublicKey = [System.Convert]::ToBase64String($RSA.ExportRSAPublicKey())
$AgentPrivateKey = "-----BEGIN RSA PRIVATE KEY-----`n" + [System.Convert]::ToBase64String($RSA.ExportRSAPrivateKey()) + "`n-----END RSA PRIVATE KEY-----"

if ($PSVersionTable.PSVersion.Major -eq 5) {
try {
. "$PSScriptRoot/RSAHelper.ps1"
$AgentPublicKey = ExportRSAPublicKeyBase64($RSA)
$AgentPrivateKey = ExportRSAPrivateKeyBase64($RSA)
$AgentPrivateKey = "-----BEGIN RSA PRIVATE KEY-----`n" + $AgentPrivateKey + "`n-----END RSA PRIVATE KEY-----"
} catch {
Write-Error "Unable to generate RSA keys"
throw
}
} else {
$AgentPublicKey = [System.Convert]::ToBase64String($RSA.ExportRSAPublicKey())
$AgentPrivateKey = "-----BEGIN RSA PRIVATE KEY-----`n" + [System.Convert]::ToBase64String($RSA.ExportRSAPrivateKey()) + "`n-----END RSA PRIVATE KEY-----"
}

$HelmChartPath = Join-Path -Path $ChartExportPath -ChildPath 'azure-arc-k8sagents'
if (Test-Path Env:HELMCHART) {
$ChartPath = Get-ChildItem -Path Env:HELMCHART
} else {
$ChartPath = $HelmChartPath
}

#Region helm options
$options = ""
$proxyEnableState = $false
if (-not ([string]::IsNullOrEmpty($HttpProxy))) {
$HttpProxyStr = $HttpProxy.ToString()
$HttpProxyStr = $HttpProxyStr -replace ',','\,'
$HttpProxyStr = $HttpProxyStr -replace '/','\/'
$options += " --set global.httpProxy=$HttpProxyStr"
$proxyEnableState = $true
$Null = $PSBoundParameters.Remove('HttpProxy')
}
if (-not ([string]::IsNullOrEmpty($HttpsProxy))) {
$HttpsProxyStr = $HttpsProxy.ToString()
$HttpsProxyStr = $HttpsProxyStr -replace ',','\,'
$HttpsProxyStr = $HttpsProxyStr -replace '/','\/'
$options += " --set global.httpsProxy=$HttpsProxyStr"
$proxyEnableState = $true
$Null = $PSBoundParameters.Remove('HttpsProxy')
}
if (-not ([string]::IsNullOrEmpty($NoProxy))) {
$NoProxy = $NoProxy -replace ',','\,'
$NoProxy = $NoProxy -replace '/','\/'
$options += " --set global.noProxy=$NoProxy"
$proxyEnableState = $true
$Null = $PSBoundParameters.Remove('NoProxy')
}
if ($proxyEnableState) {
$options += " --set global.isProxyEnabled=true"
}
try {
if ((-not ([string]::IsNullOrEmpty($ProxyCert))) -and (Test-Path $ProxyCert)) {
$options += " --set-file global.proxyCert=$ProxyCert"
$options += " --set global.isCustomCert=true"
}
} catch {
Write-Error "Unable to find ProxyCert from file path"
throw
yinghsugn marked this conversation as resolved.
Show resolved Hide resolved
}
if ($DisableAutoUpgrade) {
$options += " --set systemDefaultValues.azureArcAgents.autoUpdate=false"
$Null = $PSBoundParameters.Remove('DisableAutoUpgrade')
}
if (-not ([string]::IsNullOrEmpty($ContainerLogPath))) {
$options += " --set systemDefaultValues.fluent-bit.containerLogPath=$ContainerLogPath"
$Null = $PSBoundParameters.Remove('ContainerLogPath')
}
if (-not ([string]::IsNullOrEmpty($KubeConfig))) {
$options += " --kubeconfig $KubeConfig"
}
if (-not ([string]::IsNullOrEmpty($KubeContext))) {
$options += " --kube-context $KubeContext"
}
if (!$NoWait) {
$options += " --wait --timeout $OnboardingTimeout"
$options += "s"
}
#Endregion
if ($PSBoundParameters.ContainsKey('OnboardingTimeout')) {
$PSBoundParameters.Remove('OnboardingTimeout')
}
if ((-not ([string]::IsNullOrEmpty($Proxy))) -and (-not $PSBoundParameters.ContainsKey('ProxyCredential'))) {
if (-not ([string]::IsNullOrEmpty($Proxy.UserInfo))) {
try{
$userInfo = $Proxy.UserInfo -Split ':'
$pass = ConvertTo-SecureString $userInfo[1] -AsPlainText -Force
$ProxyCredential = New-Object System.Management.Automation.PSCredential ($userInfo[0] , $pass)
$PSBoundParameters.Add('ProxyCredential', $ProxyCredential)
} catch {
Write-Warning "Please set ProxyCredential or provide username and password in the Proxy parameter"
throw
}
} else {
Write-Warning "If the proxy is a private proxy, pass ProxyCredential parameter or provide username and password in the Proxy parameter"
}
}

$PSBoundParameters.Add('AgentPublicKeyCertificate', $AgentPublicKey)
$Response = Az.ConnectedKubernetes.internal\New-AzConnectedKubernetes @PSBoundParameters

$TenantId = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext.Tenant.Id
helm upgrade --install azure-arc $ChartPath --set global.subscriptionId=$SubscriptionId --set global.resourceGroupName=$ResourceGroupName --set global.resourceName=$ClusterName --set global.tenantId=$TenantId --set global.location=$Location --set global.onboardingPrivateKey=$AgentPrivateKey --set systemDefaultValues.spnOnboarding=false --set global.azureEnvironment=AZUREPUBLICCLOUD --set systemDefaultValues.clusterconnect-agent.enabled=true --set global.kubernetesDistro=$Distribution --set global.kubernetesInfra=$Infrastructure --kubeconfig $KubeConfig --kube-context $KubeContext --wait --timeout 600s
$TenantId = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext.Tenant.Id
helm upgrade --install azure-arc $ChartPath --namespace $ReleaseInstallNamespace --create-namespace --set global.subscriptionId=$SubscriptionId --set global.resourceGroupName=$ResourceGroupName --set global.resourceName=$ClusterName --set global.tenantId=$TenantId --set global.location=$Location --set global.onboardingPrivateKey=$AgentPrivateKey --set systemDefaultValues.spnOnboarding=false --set global.azureEnvironment=AZUREPUBLICCLOUD --set systemDefaultValues.clusterconnect-agent.enabled=true --set global.kubernetesDistro=$Distribution --set global.kubernetesInfra=$Infrastructure (-split $options)
Return $Response
}
}
134 changes: 134 additions & 0 deletions src/ConnectedKubernetes/custom/RSAHelper.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
function ExportRSAPrivateKeyBase64{
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.DoNotExportAttribute()]
param(
[Parameter(Mandatory)]
[System.Security.Cryptography.RSA]
$RSA
)
process{
$RSAParams = $RSA.ExportParameters(1)
[byte]$Sequence = 0x30
[byte[]]$Version = (0x00)
$stream = [System.IO.MemoryStream]::new()
$writer = [System.IO.BinaryWriter]::new($stream)
$writer.Write($Sequence); # SEQUENCE
$innerStream = [System.IO.MemoryStream]::new()
$innerWriter = [System.IO.BinaryWriter]::new($innerStream)

EncodeIntegerBigEndian $innerWriter $Version
EncodeIntegerBigEndian $innerWriter $RSAParams.Modulus
EncodeIntegerBigEndian $innerWriter $RSAParams.Exponent
EncodeIntegerBigEndian $innerWriter $RSAParams.D
EncodeIntegerBigEndian $innerWriter $RSAParams.P
EncodeIntegerBigEndian $innerWriter $RSAParams.Q
EncodeIntegerBigEndian $innerWriter $RSAParams.DP
EncodeIntegerBigEndian $innerWriter $RSAParams.DQ
EncodeIntegerBigEndian $innerWriter $RSAParams.InverseQ

$length = ([int]($innerStream.Length))
EncodeLength $writer $length
$writer.Write($innerStream.GetBuffer(), 0, $length)

$base64 = [Convert]::ToBase64String($stream.GetBuffer(), 0, ([int]($stream.Length)))

return $base64
}
}

function ExportRSAPublicKeyBase64{
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.DoNotExportAttribute()]
param(
[Parameter(Mandatory)]
[System.Security.Cryptography.RSA]
$RSA
)
process{
$RSAParams = $RSA.ExportParameters(0)
[byte]$Sequence = 0x30
$stream = [System.IO.MemoryStream]::new()
$writer = [System.IO.BinaryWriter]::new($stream)
$writer.Write($Sequence);
$innerStream = [System.IO.MemoryStream]::new()
$innerWriter = [System.IO.BinaryWriter]::new($innerStream)
EncodeIntegerBigEndian $innerWriter $RSAParams.Modulus
EncodeIntegerBigEndian $innerWriter $RSAParams.Exponent

$length = ([int]($innerStream.Length))
EncodeLength $writer $length
$writer.Write($innerStream.GetBuffer(), 0, $length)

$base64 = [Convert]::ToBase64String($stream.GetBuffer(), 0, ([int]($stream.Length)))

return $base64
}
}

function EncodeLength{
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.DoNotExportAttribute()]
param(
[System.IO.BinaryWriter]$stream,
[int]$length
)
process{
[byte]$bytex80 = 0x80
if($length -lt 0){
throw "Length must be non-negative"
}
if($length -lt $bytex80){
$stream.Write(([byte]$length))
}
else{
$temp = $length
$bytesRequired = 0;
while ($temp -gt 0) {
$temp = $temp -shr 8
$bytesRequired++
}

[byte]$byteToWrite = $bytesRequired -bor $bytex80
$stream.Write($byteToWrite)
$iValue = ($bytesRequired - 1)
[byte]$0ffByte = 0xff
for ($i = $iValue; $i -ge 0; $i--) {
[byte]$byteToWrite = ($length -shr (8 * $i) -band $0ffByte)
$stream.Write($byteToWrite )
}
}
}
}

function EncodeIntegerBigEndian{
[Microsoft.Azure.PowerShell.Cmdlets.ConnectedKubernetes.DoNotExportAttribute()]
param(
[System.IO.BinaryWriter]$stream,
[byte[]]$value,
[bool]$forceUnsigned = $true
)
process{
[byte]$Integer = 0x02

$stream.Write($Integer); # INTEGER
$prefixZeros = 0
for ($i = 0; $i -lt $value.Length; $i++) {
if ($value[$i] -ne 0){break}
$prefixZeros++
}
if(($value.Length - $prefixZeros) -eq 0){
EncodeLength $stream 1
$stream.Write(([byte]0))
}
else{
[byte]$newByte = 0x7f
if(($forceUnsigned) -AND ($value[$prefixZeros] -gt $newByte)){
EncodeLength $stream ($value.Length - $prefixZeros +1)
$stream.Write(([byte]0))
}
else{
EncodeLength $stream ($value.Length - $prefixZeros)
}
for ($i = $prefixZeros; $i -lt $value.Length; $i++) {
$stream.Write($value[$i])
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,10 @@ param(
#Endregion

#Region get release namespace
Set-Variable ReleaseInstallNamespace -option Constant -value "azure-arc-release"
$ReleaseNamespace = $null
try {
$ReleaseNamespace = (helm status azure-arc -o json --kubeconfig $KubeConfig --kube-context $KubeContext | ConvertFrom-Json).namespace
$ReleaseNamespace = (helm status azure-arc -o json --kubeconfig $KubeConfig --kube-context $KubeContext -n $ReleaseInstallNamespace | ConvertFrom-Json).namespace
} catch {
Write-Error "Fail to find the namespace for azure-arc."
}
Expand All @@ -211,7 +212,7 @@ param(
}
if (($ResourceGroupName -eq $ConfigmapRgName) -and ($ClusterName -eq $ConfigmapClusterName)) {
Az.ConnectedKubernetes.internal\Remove-AzConnectedKubernetes @PSBoundParameters
helm delete azure-arc --namespace $ReleaseNamespace --kubeconfig $KubeConfig --kube-context $KubeContext
helm delete azure-arc --namespace $ReleaseInstallNamespace --kubeconfig $KubeConfig --kube-context $KubeContext
} else {
Write-Error "The current context in the kubeconfig file does not correspond to the connected cluster resource specified. Agents installed on this cluster correspond to the resource group name '$ConfigmapRgName' and resource name '$ConfigmapClusterName'."
}
Expand Down
2 changes: 1 addition & 1 deletion src/ConnectedKubernetes/docs/Get-AzConnectedKubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ COMPLEX PARAMETER PROPERTIES
To create the parameters described below, construct a hash table containing the appropriate properties. For information on hash tables, run Get-Help about_Hash_Tables.


`INPUTOBJECT <IConnectedKubernetesIdentity>`: Identity Parameter
INPUTOBJECT <IConnectedKubernetesIdentity>: Identity Parameter
- `[ClusterName <String>]`: The name of the Kubernetes cluster on which get is called.
- `[Id <String>]`: Resource identity path
- `[ResourceGroupName <String>]`: The name of the resource group. The name is case insensitive.
Expand Down
Loading