Fixed small issue is DoD URLs, updated GitHub Actions to align with new container design and added framework for proper Bearer Token handling within Engine

This commit is contained in:
Matthew Garrett 2024-02-13 09:43:44 -08:00
Родитель 15b8120952
Коммит f7956591fc
4 изменённых файлов: 144 добавлений и 30 удалений

3
.github/workflows/azure-ipam-build.yml поставляемый
Просмотреть файл

@ -48,6 +48,9 @@ jobs:
env:
IPAM_VERSION: ${{ steps.updateVersion.outputs.ipamVersion }}
run: |
az acr build -r $ACR_NAME -t ipam:$IPAM_VERSION -f ./Dockerfile.deb .
az acr build -r $ACR_NAME -t ipamfunc:$IPAM_VERSION -f ./Dockerfile.func .
az acr build -r $ACR_NAME -t ipam-engine:$IPAM_VERSION -f ./engine/Dockerfile.deb ./engine
az acr build -r $ACR_NAME -t ipam-func:$IPAM_VERSION -f ./engine/Dockerfile.func ./engine
az acr build -r $ACR_NAME -t ipam-ui:$IPAM_VERSION -f ./ui/Dockerfile.deb ./ui

43
.github/workflows/azure-ipam-testing.yml поставляемый
Просмотреть файл

@ -8,8 +8,8 @@ on:
env:
ACR_NAME: ${{ vars.IPAM_TEST_ACR }}
IPAM_UI_ID: ipam-ui-${{ github.run_id }}-${{ github.run_attempt }}
IPAM_ENGINE_ID: ipam-engine-${{ github.run_id }}-${{ github.run_attempt }}
IPAM_UI_NAME: ipam-ui-${{ github.run_id }}-${{ github.run_attempt }}
IPAM_ENGINE_NAME: ipam-engine-${{ github.run_id }}-${{ github.run_attempt }}
jobs:
deploy:
@ -26,7 +26,7 @@ jobs:
shell: pwsh
run: |
Set-PSRepository PSGallery -InstallationPolicy Trusted
Install-Module Az, Microsoft.Graph, powershell-yaml -AllowClobber -Force
Install-Module Az, Microsoft.Graph -AllowClobber -Force
- name: Azure Login
uses: azure/login@v1
@ -41,36 +41,30 @@ jobs:
deploy
engine
ui
lb
- name: Build Azure IPAM Containers
- name: Build Azure IPAM Container
run: |
az acr build -r $ACR_NAME -t ipam-engine:${{ github.run_id }}-${{ github.run_attempt }} -f ./engine/Dockerfile.deb ./engine
az acr build -r $ACR_NAME -t ipam-func:${{ github.run_id }}-${{ github.run_attempt }} -f ./engine/Dockerfile.func ./engine
az acr build -r $ACR_NAME -t ipam-ui:${{ github.run_id }}-${{ github.run_attempt }} -f ./ui/Dockerfile.deb ./ui
az acr build -r $ACR_NAME -t ipam-lb:${{ github.run_id }}-${{ github.run_attempt }} -f ./lb/Dockerfile ./lb
az acr build -r $ACR_NAME -t ipam:${{ github.run_id }}-${{ github.run_attempt }} -f ./Dockerfile.deb .
- name: Update Docker-Compose YAML
- name: Update Bicep File
working-directory: deploy
shell: pwsh
run: |
$uiContainer = "$env:ACR_NAME.azurecr.io/ipam-ui:${{ github.run_id }}-${{ github.run_attempt }}"
$engineContainer = "$env:ACR_NAME.azurecr.io/ipam-engine:${{ github.run_id }}-${{ github.run_attempt }}"
$lbContainer = "$env:ACR_NAME.azurecr.io/ipam-lb:${{ github.run_id }}-${{ github.run_attempt }}"
$acrName = "$env:ACR_NAME.azurecr.io"
$containerName = "ipam:${{ github.run_id }}-${{ github.run_attempt }}"
$composeFile = Get-Content -Path ./docker-compose.prod.yml
$composeYaml = $composeFile | ConvertFrom-Yaml
$bicepFile = Get-Content -Path ./appService.bicep
$composeYaml['services']['ipam-ui'].image = $uiContainer
$composeYaml['services']['ipam-engine'].image = $engineContainer
$composeYaml['services']['nginx-proxy'].image = $lbContainer
$bicepFile = $bicepFile -replace "azureipam.azurecr.io", $acrName
$bicepFile = $bicepFile -replace "ipam:latest", $containerName
$composeYaml | ConvertTo-Yaml | Out-File -FilePath ./docker-compose.prod.yml
$bicepFile | Out-File -FilePath ./appService.bicep -Force
- name: Deploy Azure IPAM
working-directory: deploy
id: deployScript
shell: pwsh
run: ./deploy.ps1 -Location "westus3" -UIAppName $env:IPAM_UI_ID -EngineAppName $env:IPAM_ENGINE_ID
run: ./deploy.ps1 -Location "westus3" -UIAppName $env:IPAM_UI_NAME -EngineAppName $env:IPAM_ENGINE_NAME
- name: "Upload Logs"
working-directory: logs
@ -174,7 +168,7 @@ jobs:
runs-on: ubuntu-latest
needs: [ deploy, test ]
steps:
- name: Install Deployment Prerequisites
- name: Install Cleanup Prerequisites
shell: pwsh
run: |
Set-PSRepository PSGallery -InstallationPolicy Trusted
@ -207,9 +201,6 @@ jobs:
$uiApp | Remove-AzADApplication
$engineApp | Remove-AzADApplication
- name: "Remove Azure IPAM Containers"
- name: "Remove Azure IPAM Container"
run: |
az acr repository delete --name $ACR_NAME --repository ipam-engine --yes
az acr repository delete --name $ACR_NAME --repository ipam-func --yes
az acr repository delete --name $ACR_NAME --repository ipam-ui --yes
az acr repository delete --name $ACR_NAME --repository ipam-lb --yes
az acr repository delete --name $ACR_NAME --repository ipam --yes

Просмотреть файл

@ -1,4 +1,5 @@
from fastapi import Request, HTTPException
# from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from requests import Session, adapters
from urllib3.util.retry import Retry
@ -16,6 +17,125 @@ from app.globals import globals
from app.logs.logs import ipam_logger as logger
# class IPAMToken(HTTPBearer):
# _session = None
# def __init__(self, auto_error: bool = True):
# super(IPAMToken, self).__init__(
# auto_error=auto_error,
# scheme_name='IPAM Token',
# description="<font color='blue'><b>Please enter a valid IPAM token (Entra Access Token)</b></font><br><font color='blue'>Guide: </font><a href='https://azure.github.io/ipam/#/api/README?id=obtaining-an-azure-ad-token' target='_blank'>How to generate an Azure IPAM token</a>"
# )
# async def __call__(self, request: Request):
# credentials: HTTPAuthorizationCredentials = await super(IPAMToken, self).__call__(request)
# if credentials:
# if not credentials.scheme == "Bearer":
# raise HTTPException(status_code=403, detail="Invalid authentication scheme.")
# if not self.validate_token(request, credentials.credentials):
# raise HTTPException(status_code=403, detail="Invalid token or expired token.")
# return credentials.credentials
# else:
# raise HTTPException(status_code=403, detail="Invalid authorization code.")
# async def fetch_jwks_keys(self):
# if self._session is None:
# self._session = Session()
# retries = Retry(
# total=5,
# backoff_factor=0.1,
# status_forcelist=[ 500, 502, 503, 504 ]
# )
# self._session.mount('https://', adapters.HTTPAdapter(max_retries=retries))
# self._session.mount('http://', adapters.HTTPAdapter(max_retries=retries))
# key_url = "https://" + globals.AUTHORITY_HOST + "/" + globals.TENANT_ID + "/discovery/v2.0/keys"
# jwks = _session.get(key_url).json()
# return jwks
# async def check_admin(request: Request, user_oid: str, user_tid: str):
# admin_query = await cosmos_query("SELECT * FROM c WHERE c.type = 'admin'", user_tid)
# if admin_query:
# admin_data = copy.deepcopy(admin_query[0])
# if admin_data['admins']:
# is_admin = next((x for x in admin_data['admins'] if user_oid == x['id']), None)
# else:
# is_admin = True
# else:
# is_admin = True
# request.state.admin = True if is_admin else False
# async def validate_token(self, request: Request, token: str) -> bool:
# try:
# jwks = await self.fetch_jwks_keys()
# unverified_header = jwt.get_unverified_header(token)
# rsa_key = {}
# for key in jwks["keys"]:
# if key["kid"] == unverified_header["kid"]:
# rsa_key = {
# "kty": key["kty"],
# "kid": key["kid"],
# "use": key["use"],
# "n": key["n"],
# "e": key["e"]
# }
# except Exception:
# raise HTTPException(status_code=401, detail="Unable to parse authorization token.")
# try:
# token_version = int(jwt.decode(token, options={"verify_signature": False})["ver"].split(".")[0])
# except Exception:
# raise HTTPException(status_code=401, detail="Unable to decode token version.")
# if token_version == 1:
# logger.error("Microsoft Identity v1.0 access tokens are not supported!")
# logger.error("https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens#token-formats")
# raise HTTPException(status_code=401, detail="Microsoft Identity v1.0 access tokens are not supported.")
# if rsa_key:
# rsa_pem_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(rsa_key))
# rsa_pem_key_bytes = rsa_pem_key.public_bytes(
# encoding=serialization.Encoding.PEM,
# format=serialization.PublicFormat.SubjectPublicKeyInfo
# )
# try:
# payload = jwt.decode(
# token,
# key=rsa_pem_key_bytes,
# verify=True,
# algorithms=["RS256"],
# audience=globals.CLIENT_ID,
# issuer="https://" + globals.AUTHORITY_HOST + "/" + globals.TENANT_ID + "/v2.0"
# )
# except jwt.ExpiredSignatureError:
# raise HTTPException(status_code=401, detail="Token has expired.")
# except jwt.MissingRequiredClaimError:
# raise HTTPException(status_code=401, detail="Incorrect token claims, please check the audience and issuer.")
# except jwt.InvalidSignatureError:
# raise HTTPException(status_code=401, detail="Invalid token signature.")
# except Exception:
# raise HTTPException(status_code=401, detail="Unable to decode authorization token.")
# else:
# raise HTTPException(status_code=401, detail="Unable to find appropriate signing key.")
# request.state.tenant_id = payload['tid']
# await check_admin(request, payload['oid'], payload['tid'])
# return True
# ipam_security = IPAMToken(auto_error=False)
_session = None
async def fetch_jwks_keys():

Просмотреть файл

@ -14,10 +14,10 @@ const AZURE_ENV_MAP = {
MS_GRAPH: "graph.microsoft.us"
},
AZURE_US_GOV_SECRET: {
AZURE_AD: "login.microsoftonline.microsoft.scloud/",
AZURE_ARM: "management.azure.microsoft.scloud/",
AZURE_PORTAL: "portal.azure.microsoft.scloud/",
MS_GRAPH: "graph.cloudapi.microsoft.scloud/"
AZURE_AD: "login.microsoftonline.microsoft.scloud",
AZURE_ARM: "management.azure.microsoft.scloud",
AZURE_PORTAL: "portal.azure.microsoft.scloud",
MS_GRAPH: "graph.cloudapi.microsoft.scloud"
},
AZURE_GERMANY: {
AZURE_AD: "login.microsoftonline.de",