Skip to content

Commit 182c38e

Browse files
authored
Merge pull request #108 from PerfectThymeTech/marvinbuss/health_endpoint_validation
Health Endpoint Validation
2 parents 02f3232 + d9dd808 commit 182c38e

File tree

6 files changed

+46
-3
lines changed

6 files changed

+46
-3
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ This sample uses FastAPI as a baseline which is a scalable, modern, fast and pro
5757

5858
This sample uses Open Telemetry and the FastAPI and Azure Application Insights integration for end to end tracking of API calls using logs and metrics and dependency calls. More information about Open Telemetry can be found [here](https://opentelemetry.io/).
5959

60+
### Health Endpoint
61+
62+
This sample exposes a health endpoint that includes header validation (Header `x-ms-auth-internal-token`) to ensure that only the health check feature of the Azure Function it self is allowed to call this endpoint. When trying to reach this endpoint from outside, the request will be blocked with a 400 response.
63+
6064
### Testing
6165

6266
Testing of the Azure Functon application code. The testing is done using `pytest`. Tests are stored in the [`/tests` folder](/tests/) and should be extended for new functionality that is being implemented over time. The `pytest.ini` is used to reference the Azure Functon project for imports. This file makes sure that the respective python objects from the Azrue Function application code can be imported into the tests and validated accordingly.
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any
22

3-
from fastapi import APIRouter
3+
from fastapi import APIRouter, Depends
4+
from fastapp.health.validate_request import verify_health_auth_header
45
from fastapp.models.heartbeat import HearbeatResult
56
from fastapp.utils import setup_logging
67

@@ -9,7 +10,12 @@
910
router = APIRouter()
1011

1112

12-
@router.get("/heartbeat", response_model=HearbeatResult, name="heartbeat")
13+
@router.get(
14+
"/heartbeat",
15+
response_model=HearbeatResult,
16+
name="heartbeat",
17+
dependencies=[Depends(verify_health_auth_header)],
18+
)
1319
async def get_hearbeat() -> Any:
1420
logger.info("Received Heartbeat Request")
1521
return HearbeatResult(isAlive=True)

code/function/fastapp/core/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class Settings(BaseSettings):
1818
)
1919
WEBSITE_NAME: str = Field(default="test", alias="WEBSITE_SITE_NAME")
2020
WEBSITE_INSTANCE_ID: str = Field(default="0", alias="WEBSITE_INSTANCE_ID")
21+
WEBSITE_AUTH_ENCRYPTION_KEY: str = Field(
22+
default="", alias="WEBSITE_AUTH_ENCRYPTION_KEY"
23+
)
2124
MY_SECRET_CONFIG: str = Field(default="", alias="MY_SECRET_CONFIG")
2225

2326

code/function/fastapp/health/__init__.py

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import base64
2+
from hashlib import sha256
3+
from typing import Annotated
4+
5+
from fastapi import Header, HTTPException
6+
from fastapp.core.config import settings
7+
8+
9+
async def verify_health_auth_header(
10+
x_ms_auth_internal_token: Annotated[str, Header()]
11+
) -> bool:
12+
"""Returns true if SHA256 of header_value matches WEBSITE_AUTH_ENCRYPTION_KEY.
13+
Documentation: https://learn.microsoft.com/en-us/azure/app-service/monitor-instances-health-check?tabs=python#authentication-and-security
14+
15+
x_ms_auth_internal_token: Value of the x-ms-auth-internal-token header.
16+
RETURNS (bool): Specifies whether the header matches.
17+
"""
18+
website_auth_encryption_key = settings.WEBSITE_AUTH_ENCRYPTION_KEY
19+
hash = base64.b64encode(
20+
sha256(website_auth_encryption_key.encode("utf-8")).digest()
21+
).decode("utf-8")
22+
if hash != x_ms_auth_internal_token:
23+
raise HTTPException(
24+
status_code=400, detail="x-ms-auth-internal-token is invalid"
25+
)
26+
else:
27+
return True

tests/test_main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ def client() -> TestClient:
1212
def test_get_heartbeat(client, version):
1313
# arrange
1414
path = f"/{version}/health/heartbeat"
15+
headers = {
16+
"x-ms-auth-internal-token": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
17+
}
1518

1619
# action
17-
response = client.get(path)
20+
response = client.get(path, headers=headers)
1821

1922
# assert
2023
assert response.status_code == 200

0 commit comments

Comments
 (0)