зеркало из https://github.com/Azure/ipam.git
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:
Родитель
15b8120952
Коммит
f7956591fc
|
@ -48,6 +48,9 @@ jobs:
|
||||||
env:
|
env:
|
||||||
IPAM_VERSION: ${{ steps.updateVersion.outputs.ipamVersion }}
|
IPAM_VERSION: ${{ steps.updateVersion.outputs.ipamVersion }}
|
||||||
run: |
|
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-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-func:$IPAM_VERSION -f ./engine/Dockerfile.func ./engine
|
||||||
az acr build -r $ACR_NAME -t ipam-ui:$IPAM_VERSION -f ./ui/Dockerfile.deb ./ui
|
az acr build -r $ACR_NAME -t ipam-ui:$IPAM_VERSION -f ./ui/Dockerfile.deb ./ui
|
||||||
|
|
|
@ -8,8 +8,8 @@ on:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
ACR_NAME: ${{ vars.IPAM_TEST_ACR }}
|
ACR_NAME: ${{ vars.IPAM_TEST_ACR }}
|
||||||
IPAM_UI_ID: ipam-ui-${{ github.run_id }}-${{ github.run_attempt }}
|
IPAM_UI_NAME: ipam-ui-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
IPAM_ENGINE_ID: ipam-engine-${{ github.run_id }}-${{ github.run_attempt }}
|
IPAM_ENGINE_NAME: ipam-engine-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
|
@ -26,7 +26,7 @@ jobs:
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
||||||
Install-Module Az, Microsoft.Graph, powershell-yaml -AllowClobber -Force
|
Install-Module Az, Microsoft.Graph -AllowClobber -Force
|
||||||
|
|
||||||
- name: Azure Login
|
- name: Azure Login
|
||||||
uses: azure/login@v1
|
uses: azure/login@v1
|
||||||
|
@ -41,36 +41,30 @@ jobs:
|
||||||
deploy
|
deploy
|
||||||
engine
|
engine
|
||||||
ui
|
ui
|
||||||
lb
|
|
||||||
|
|
||||||
- name: Build Azure IPAM Containers
|
- name: Build Azure IPAM Container
|
||||||
run: |
|
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:${{ github.run_id }}-${{ github.run_attempt }} -f ./Dockerfile.deb .
|
||||||
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
|
|
||||||
|
|
||||||
- name: Update Docker-Compose YAML
|
- name: Update Bicep File
|
||||||
|
working-directory: deploy
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$uiContainer = "$env:ACR_NAME.azurecr.io/ipam-ui:${{ github.run_id }}-${{ github.run_attempt }}"
|
$acrName = "$env:ACR_NAME.azurecr.io"
|
||||||
$engineContainer = "$env:ACR_NAME.azurecr.io/ipam-engine:${{ github.run_id }}-${{ github.run_attempt }}"
|
$containerName = "ipam:${{ github.run_id }}-${{ github.run_attempt }}"
|
||||||
$lbContainer = "$env:ACR_NAME.azurecr.io/ipam-lb:${{ github.run_id }}-${{ github.run_attempt }}"
|
|
||||||
|
|
||||||
$composeFile = Get-Content -Path ./docker-compose.prod.yml
|
$bicepFile = Get-Content -Path ./appService.bicep
|
||||||
$composeYaml = $composeFile | ConvertFrom-Yaml
|
|
||||||
|
|
||||||
$composeYaml['services']['ipam-ui'].image = $uiContainer
|
$bicepFile = $bicepFile -replace "azureipam.azurecr.io", $acrName
|
||||||
$composeYaml['services']['ipam-engine'].image = $engineContainer
|
$bicepFile = $bicepFile -replace "ipam:latest", $containerName
|
||||||
$composeYaml['services']['nginx-proxy'].image = $lbContainer
|
|
||||||
|
|
||||||
$composeYaml | ConvertTo-Yaml | Out-File -FilePath ./docker-compose.prod.yml
|
$bicepFile | Out-File -FilePath ./appService.bicep -Force
|
||||||
|
|
||||||
- name: Deploy Azure IPAM
|
- name: Deploy Azure IPAM
|
||||||
working-directory: deploy
|
working-directory: deploy
|
||||||
id: deployScript
|
id: deployScript
|
||||||
shell: pwsh
|
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"
|
- name: "Upload Logs"
|
||||||
working-directory: logs
|
working-directory: logs
|
||||||
|
@ -174,7 +168,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ deploy, test ]
|
needs: [ deploy, test ]
|
||||||
steps:
|
steps:
|
||||||
- name: Install Deployment Prerequisites
|
- name: Install Cleanup Prerequisites
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
||||||
|
@ -207,9 +201,6 @@ jobs:
|
||||||
$uiApp | Remove-AzADApplication
|
$uiApp | Remove-AzADApplication
|
||||||
$engineApp | Remove-AzADApplication
|
$engineApp | Remove-AzADApplication
|
||||||
|
|
||||||
- name: "Remove Azure IPAM Containers"
|
- name: "Remove Azure IPAM Container"
|
||||||
run: |
|
run: |
|
||||||
az acr repository delete --name $ACR_NAME --repository ipam-engine --yes
|
az acr repository delete --name $ACR_NAME --repository ipam --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
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from fastapi import Request, HTTPException
|
from fastapi import Request, HTTPException
|
||||||
|
# from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||||
|
|
||||||
from requests import Session, adapters
|
from requests import Session, adapters
|
||||||
from urllib3.util.retry import Retry
|
from urllib3.util.retry import Retry
|
||||||
|
@ -16,6 +17,125 @@ from app.globals import globals
|
||||||
|
|
||||||
from app.logs.logs import ipam_logger as logger
|
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
|
_session = None
|
||||||
|
|
||||||
async def fetch_jwks_keys():
|
async def fetch_jwks_keys():
|
||||||
|
|
|
@ -14,10 +14,10 @@ const AZURE_ENV_MAP = {
|
||||||
MS_GRAPH: "graph.microsoft.us"
|
MS_GRAPH: "graph.microsoft.us"
|
||||||
},
|
},
|
||||||
AZURE_US_GOV_SECRET: {
|
AZURE_US_GOV_SECRET: {
|
||||||
AZURE_AD: "login.microsoftonline.microsoft.scloud/",
|
AZURE_AD: "login.microsoftonline.microsoft.scloud",
|
||||||
AZURE_ARM: "management.azure.microsoft.scloud/",
|
AZURE_ARM: "management.azure.microsoft.scloud",
|
||||||
AZURE_PORTAL: "portal.azure.microsoft.scloud/",
|
AZURE_PORTAL: "portal.azure.microsoft.scloud",
|
||||||
MS_GRAPH: "graph.cloudapi.microsoft.scloud/"
|
MS_GRAPH: "graph.cloudapi.microsoft.scloud"
|
||||||
},
|
},
|
||||||
AZURE_GERMANY: {
|
AZURE_GERMANY: {
|
||||||
AZURE_AD: "login.microsoftonline.de",
|
AZURE_AD: "login.microsoftonline.de",
|
||||||
|
|
Загрузка…
Ссылка в новой задаче