-
Notifications
You must be signed in to change notification settings - Fork 31
Azure Function App which runs periodically to aggregate the bytes read per IP address over a window of time #215
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
Merged
elayrocks
merged 46 commits into
main
from
user/elay/add-function-app-to-generate-banned-ip
Jun 26, 2024
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
f382c5a
init
6470414
update
ad60299
update flake8 config
90a9724
code format changes
9a99dcc
fix long lines
55ad5d9
fix imports
3148e11
function app changes
bf2c7cc
code format changes
ea9bcf3
test
32f6458
remove readme
0f7f66a
Merge branch 'main' into user/elay/add-function-app-to-generate-banne…
9f9998e
Merge branch 'main' into user/elay/add-function-app-to-generate-banne…
3f1667b
remove redundant packages and revert python version
fe6bbcc
revert python version
f3d56e1
remove redundant type hints
f3cd2ba
use settings class inherited from baseSettings
4e9021a
change kql
1d9f5f6
use placeholder for timer schedule
5ade542
change test parameter
fa70e5e
update assertions and logger
b115435
remove import
e6cce58
remove dash in table name
21b4010
update test id
342a345
format
d2bdacc
add no-integration flag
1e6856e
Merge branch 'main' into user/elay/add-function-app-to-generate-banne…
d70c166
add dependencies
7334fea
test
bc69adb
change trigger to run every hour
20b7fa4
Merge branch 'main' into user/elay/add-function-app-to-generate-banne…
b6aa998
use azure clients as context manager
354352a
add context managers in test
29178a1
Merge branch 'main' into user/elay/add-function-app-to-generate-banne…
e95abd5
role assignment for function app
1753331
change LAW name
47e5790
change role
c6c012b
change provider's name
e4715fe
change name of LAW
c69460a
better readability
0c1bb14
better readability
c546d7f
format
d941b49
add logging
9b8282d
UPDATE function settings
58a6c19
suppress mypy warning
7c832c2
update env variables for function app
e0313eb
typo
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,4 +4,5 @@ extend-ignore = E203, W503 | |
| exclude = | ||
| .git | ||
| __pycache__ | ||
| setup.py | ||
| setup.py | ||
| .venv | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import datetime | ||
| import logging | ||
|
|
||
| import azure.functions as func | ||
| from azure.data.tables import TableServiceClient | ||
| from azure.identity import DefaultAzureCredential | ||
| from azure.monitor.query import LogsQueryClient | ||
|
|
||
| from .config import settings | ||
| from .models import UpdateBannedIPTask | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def main(mytimer: func.TimerRequest) -> None: | ||
| utc_timestamp: str = ( | ||
| datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() | ||
| ) | ||
| logger.info("Updating the ip ban list at %s", utc_timestamp) | ||
| credential: DefaultAzureCredential = DefaultAzureCredential() | ||
| with LogsQueryClient(credential) as logs_query_client: | ||
| with TableServiceClient( | ||
| endpoint=settings.storage_account_url, credential=credential | ||
| ) as table_service_client: | ||
| with table_service_client.create_table_if_not_exists( | ||
| settings.banned_ip_table | ||
| ) as table_client: | ||
| task = UpdateBannedIPTask(logs_query_client, table_client) | ||
| task.run() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # config.py | ||
| from pydantic import BaseSettings, Field | ||
|
|
||
|
|
||
| class Settings(BaseSettings): | ||
| storage_account_url: str = Field(env="STORAGE_ACCOUNT_URL") | ||
| banned_ip_table: str = Field(env="BANNED_IP_TABLE") | ||
| log_analytics_workspace_id: str = Field(env="LOG_ANALYTICS_WORKSPACE_ID") | ||
|
|
||
| # Time and threshold settings | ||
| time_window_in_hours: int = Field(default=24, env="TIME_WINDOW_IN_HOURS") | ||
| threshold_read_count_in_gb: int = Field( | ||
| default=5120, env="THRESHOLD_READ_COUNT_IN_GB" | ||
| ) | ||
|
|
||
|
|
||
| # Create a global settings instance | ||
| settings = Settings() # type: ignore |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "scriptFile": "__init__.py", | ||
| "bindings": [ | ||
| { | ||
| "name": "mytimer", | ||
| "type": "timerTrigger", | ||
| "direction": "in", | ||
| "schedule": "0 */1 * * *" | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| import logging | ||
| from typing import Any, List, Set | ||
|
|
||
| from azure.data.tables import TableClient, UpdateMode | ||
| from azure.monitor.query import LogsQueryClient | ||
| from azure.monitor.query._models import LogsTableRow | ||
|
|
||
| from .config import settings | ||
|
|
||
|
|
||
| class UpdateBannedIPTask: | ||
| def __init__( | ||
| self, | ||
| logs_query_client: LogsQueryClient, | ||
| table_client: TableClient, | ||
| ) -> None: | ||
| self.log_query_client = logs_query_client | ||
| self.table_client = table_client | ||
|
|
||
| def run(self) -> List[LogsTableRow]: | ||
| query_result: List[LogsTableRow] = self.get_blob_logs_query_result() | ||
| logging.info(f"Kusto query result: {query_result}") | ||
| self.update_banned_ips(query_result) | ||
| return query_result | ||
|
|
||
| def get_blob_logs_query_result(self) -> List[LogsTableRow]: | ||
| query: str = f""" | ||
| StorageBlobLogs | ||
| | where TimeGenerated > ago({settings.time_window_in_hours}h) | ||
| | extend IpAddress = tostring(split(CallerIpAddress, ":")[0]) | ||
| | where OperationName == 'GetBlob' | ||
| | where not(ipv4_is_private(IpAddress)) | ||
| | summarize readcount = sum(ResponseBodySize) / (1024 * 1024 * 1024) | ||
| by IpAddress | ||
| | where readcount > {settings.threshold_read_count_in_gb} | ||
| """ | ||
| response: Any = self.log_query_client.query_workspace( | ||
| settings.log_analytics_workspace_id, query, timespan=None | ||
| ) | ||
| return response.tables[0].rows | ||
|
|
||
| def update_banned_ips(self, query_result: List[LogsTableRow]) -> None: | ||
| existing_ips = { | ||
| entity["RowKey"] for entity in self.table_client.list_entities() | ||
| } | ||
| result_ips: Set[str] = set() | ||
| for result in query_result: | ||
| ip_address: str = result[0] | ||
| read_count: int = int(result[1]) | ||
| result_ips.add(ip_address) | ||
| entity = { | ||
| "PartitionKey": ip_address, | ||
| "RowKey": ip_address, | ||
| "ReadCount": read_count, | ||
| "Threshold": settings.threshold_read_count_in_gb, | ||
| "TimeWindow": settings.time_window_in_hours, | ||
| } | ||
|
|
||
| if ip_address in existing_ips: | ||
| self.table_client.update_entity(entity, mode=UpdateMode.REPLACE) | ||
| else: | ||
| self.table_client.create_entity(entity) | ||
|
|
||
| for ip_address in existing_ips: | ||
| if ip_address not in result_ips: | ||
| self.table_client.delete_entity( | ||
| partition_key=ip_address, row_key=ip_address | ||
| ) | ||
|
|
||
| logging.info("IP ban list has been updated successfully") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| from typing import List | ||
|
|
||
| import pytest | ||
|
|
||
|
|
||
| def pytest_addoption(parser: pytest.Parser) -> None: | ||
| parser.addoption( | ||
| "--no-integration", | ||
| action="store_true", | ||
| default=False, | ||
| help="don't run integration tests", | ||
| ) | ||
|
|
||
|
|
||
| def pytest_configure(config: pytest.Config) -> None: | ||
| config.addinivalue_line("markers", "integration: mark as an integration test") | ||
|
|
||
|
|
||
| def pytest_collection_modifyitems( | ||
| config: pytest.Config, items: List[pytest.Item] | ||
| ) -> None: | ||
| if config.getoption("--no-integration"): | ||
| # --no-integration given in cli: skip integration tests | ||
| skip_integration = pytest.mark.skip( | ||
| reason="needs --no-integration option to run" | ||
| ) | ||
| for item in items: | ||
| if "integration" in item.keywords: | ||
| item.add_marker(skip_integration) |
Empty file.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.