{"id":28568679,"url":"https://github.com/iamthemitchell/oktalogger","last_synced_at":"2026-02-06T16:33:50.851Z","repository":{"id":293883988,"uuid":"982348059","full_name":"IAmTheMitchell/oktalogger","owner":"IAmTheMitchell","description":"Poll Okta for audit logs and index the logs in AWS OpenSearch via Lambda function","archived":false,"fork":false,"pushed_at":"2025-05-17T22:49:35.000Z","size":296,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-18T06:15:16.245Z","etag":null,"topics":["aws","aws-lambda","okta","opensearch","security"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IAmTheMitchell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-12T18:41:26.000Z","updated_at":"2025-05-17T22:50:59.000Z","dependencies_parsed_at":"2025-05-17T18:43:59.011Z","dependency_job_id":null,"html_url":"https://github.com/IAmTheMitchell/oktalogger","commit_stats":null,"previous_names":["iamthemitchell/oktalogger"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/IAmTheMitchell/oktalogger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IAmTheMitchell%2Foktalogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IAmTheMitchell%2Foktalogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IAmTheMitchell%2Foktalogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IAmTheMitchell%2Foktalogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IAmTheMitchell","download_url":"https://codeload.github.com/IAmTheMitchell/oktalogger/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IAmTheMitchell%2Foktalogger/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29168543,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T16:33:35.550Z","status":"ssl_error","status_checked_at":"2026-02-06T16:33:30.716Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","aws-lambda","okta","opensearch","security"],"created_at":"2025-06-10T16:41:25.475Z","updated_at":"2026-02-06T16:33:50.841Z","avatar_url":"https://github.com/IAmTheMitchell.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Okta Logger\n\nThe goal of this project is to poll Okta for audit logs and index the logs in AWS OpenSearch. The project contains Python code for a Lambda function to facilitate forwarding the logs and Terraform code to automate the infrastructure setup. \n\n## Set Up\n1. Follow [the Okta documentation](https://developer.okta.com/docs/guides/create-an-api-token/main/) to create an Okta API token.\n\n1. [Install uv](https://docs.astral.sh/uv/getting-started/installation/) to manage Python. \n\n1. Change to the `terraform` directory and run `terraform init` to download Terraform dependencies. (See [Hashicorp docs](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) for information on installing Terraform.)\n\n1. Run `terraform plan` to see the resources that will be created in AWS. Enter the variable values as prompted. (See [Hashicorp docs](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build) for connecting your local environment to AWS if necessary.)\n\n1. If everything checks out in the plan output, run `terraform apply`. Initial deployment may take 50+ minutes. (Note: If deployment fails with 403 HTTP forbidden error for OpenSearch resource creation, ensure that the OpenSearch cluster is fully deployed and healthy in the AWS console, then run Terraform again. The second run should go much faster.)\n\n1. Navigate to the Lambda service in the AWS console. A new Lambda function should have been created for ingesting Okta logs. The monitor page should display invocations on a set schedule. By default, the Lambda runs every 5 minutes. This can be modified via the `okta_polling_interval` Terraform variable. Optionally, the Lambda can be manually invoked from the Test tab.\n\n1. Navigate to the OpenSearch service in the AWS console. Clicking the link under `OpenSearch Dashboards URL (IPv4)` will take you to the OpenSearch console. Log in with the master credentials. (Default user is osadmin + the password you set.) If you have auth issues, make sure you are accessing the console from the IP you set in Terraform.\n\n1. Optional - Run `terraform destroy` to delete all resources created by this project. **IMPORTANT** - It is the responsibility of the user to ensure that all resources were deleted. Orphaned or improperly deleted resources may result in charges to the user's \nAWS account.\n\n## Security Detections\n\nTerraform will configure two example security detections. Security alerts will be available in the OpenSearch dashboard under the \"Alerts\" page.\n\n![An example of triggered alerts in the OpenSearch Dashboard](/docs/alerts.png)\n\n### Okta - Unauthorized Admin Console Access Attempt\n\nThis monitor searches for Okta logs indicating that an authenticated Okta user has attempted to access the Okta admin console and been denied. \n\nRecommendation: Investigate recent user activity. This may be indicative of an insider threat or compromised user account attempting to elevate privileges. \n\n### Okta - User Added to Admin Group\n\nThis monitor searches for Okta logs indicating that a user was added to the \"Admins\" group. This example can be easily tweaked to monitor any desired groups. Ideally, the monitored group should be highly sensitive, and memberships should rarely change to avoid generating excess false positives.\n\nRecommendation: Attempt to correlate the group modification with evidence of planned activity (such as a verified change request). If none found, investigate recent activity for both users. This may be indicative of insider threat, misconfiguration, and/or privilege escalation. \n\n### Adding Additional Security Detections\n\n`terraform/detections.tf` can be modified to include additional security detections. Detections are created using the `opensearch_monitor` resource and are built in JSON format. \n\n## Architecture\n\n![A Mermaid diagram of the project architecture.](/docs/architecture.png)\n\n### Overview\nThe Lambda function begins by retrieving the Okta API token from AWS Secrets Manager. An IAM role is utilized to allow for passwordless authentication to Secrets Manager. The function then queries OpenSearch for the last indexed Okta audit log. Next, the function polls logs from the Okta API, using the time of the last indexed log as the starting period for the poll. Finally, the new audit logs are sent to OpenSearch and indexed.\n\n### Components\n\n#### Terraform\nTerraform is used to manage all services and infrastructure for this project. This allows for automated deployment, drift correction, and tear-down.\n\n#### AWS Lambda\nLambda provides compute to run the Python script that polls and indexes the Okta logs. Thanks to the serverless architecture of Lambda, maintenance is minimal and no specific infrastucture has to be provisioned. AWS handles infrastructure scaling, maintenance, and updates. \n\n#### AWS Secrets Manager\nSecrets, such as the OpenSearch credentials and Okta API token, are stored in the AWS Secrets Manager. Credentials can be rotated and updated with Terraform, and the Lambda function will automatically retrieve the latest credentials.\n\n#### AWS Parameters and Secrets Lambda Extension\nThe AWS Parameters and Secrets Lambda Extension was used instead of the typical boto3 client method because it supports caching. This allows for faster secrets retrieval and lower costs by reducing the number of calls to AWS Secrets Manager.\n\n[Official AWS Documentation](https://docs.aws.amazon.com/secretsmanager/latest/userguide/retrieving-secrets_lambda.html)\n\n#### AWS OpenSearch\nOpenSearch is used as the SIEM platform to index, search, and alert on the Okta audit logs.\n\n## Troubleshooting\n\n### Lambda Logging and Errors\nTerraform will automatically configure a CloudWatch log group for the Lambda function. In the AWS console, navigate to the CloudWatch service, then Logs -\u003e Log groups. The corresponding log streams will contain logs from Lambda invocations in the past 14 days. This can be helpful for troubleshooting errors.\n\n### Running Code Locally\n`src/main.py` is designed to run both in AWS Lambda and locally on a dev machine. When running locally, install any missing Python dependencies and set the following environment variables in your terminal:\n```\nexport OKTA_HOST=your_value_here\nexport OKTA_API_TOKEN=your_value_here\nexport OPENSEARCH_HOST=your_value_here\nexport AWS_REGION=your_value_here\nexport INDEX_NAME=your_value_here\nexport OPENSEARCH_USERNAME=your_value_here\nexport OPENSEARCH_PASSWORD=your_value_here\n```\n\n### OpenSearch Failures\nRunning OpenSearch with minimal resources can lead to strange issues. Be sure to follow best practices and recommendations when provisioning an OpenSearch cluster for production. `opensearch.tf` can be modified to change cluster attributes.\n\nIf OpenSearch becomes unresponsive (unfortunately common when under-provisioned), Terraform may fail with errors due to being unable to update the state of internal OpenSearch resources. The solution is to either fix OpenSearch manually, remove the state of the broken Terraform resources manually (`terraform state rm resource_here`), or destroy all infrastructure with `terraform destroy -refresh=false`.\n\nAgain, if using OpenSearch for more than a proof of concept, be sure to follow best practices when provisioning. \n\n## Future Enhancements\n\n### Migrate from User/Pass Auth\nIt is highly recommended to disable user/pass authentication for OpenSearch if using this in production. User/pass is BASIC auth and does not implement MFA. This makes it susceptible to brute force or simple login if an attacker has obtained leaked credentials. (Limiting the console to an IP range is used as a mitigating control for this PoC.) SAML, AWS Cognito, or another more robust auth solution should be used in production.\n\n### Send Logs in Bulk\nIt would be better to send the logs to OpenSearch in bulk. Theoretically this is possible with the below code:\n\n```Python\n    # String to hold the formatted logs\n    bulk_string = \"\"\n\n    # Index the logs\n    for log in data:\n        # Add index wrapper to the log\n        wrapped_log = {\"index\": log}\n\n        # Convert the log to a JSON string\n        json_string = json.dumps(wrapped_log)\n\n        # Add log to string for bulk indexing\n        bulk_string += json_string\n\n    client.bulk(bulk_string)\n```\n\nHowever, OpenSearch returns a 400 error – Perhaps the bulk operation does not support nested objects. Further investigation is required. \n\n### Page Through Okta Logs\n\nBy default, Okta returns a maximum of 100 logs per API call. If there are more than 100 logs to be processed, it will take multiple function invocations to collect all the logs. A better solution would be to page through all the available logs and process in a single invocation. \n\n### Fix Unnecessary Terraform Refresh of Detections\n\nTerraform replaces the OpenSearch detections each run due to processing that happens around the detections/monitors after creation. Lifecycle rules could be instituted to correct this behavior. \n\n### Add CI/CD and Tests\nCI/CD to deploy the code and run automated tests would improve efficiency and reduce the risk of human error.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamthemitchell%2Foktalogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiamthemitchell%2Foktalogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamthemitchell%2Foktalogger/lists"}