Secure Sentry webhook for Discord with user-level authorization running on top of US DoD maintained hardened container
Baby Yoda (Smokey) said, this is the way.
And more, much more than this, I did it my way.
-
3.1 Quick Start
3.3 Bot Commands
I was inspired to create this project after witnessing many unhandled exception occurrences on my colleague's web app for his Computer Science Bachelor thesis project. My colleague and I also experienced few crashes on our other web app project already in production environment, which I did not notice right away during the time of incident and took a considerable delay in time until I become aware of it.
I asked my other colleague if Sentry can post webhook alert to Discord but it turned out that there is no native Discord integration available yet. The existing method is using Slack integration modified to use Discord webhook endpoint, which is ugly, insecure, unformatted and requires paid Team billing plans.
Then, I decided that it is in the best interest of good software engineering principle to create and democratize the means to get notified of software errors securely in real-time and collaboratively in a more accessible way to enable quality Agile development and deliver fix faster than ever.
Moreover, I hope that this app would be adopted by US Air Force Gaming community and every other agencies across DoD informally in respect to #AccelerateChange directive through bolstering Cuture Change in adopting collaborative Agile approach to software engineering.
I believe it would be more effective if you also do it outside the professional domain in your spare time. For example, this is great for personal projects or hobbies which you can easily integrate to your informal Discord gaming channel community and collaborate with your peers without having to pay for Sentry Team billing plans or having to use your organization account.
Baby Yoda (Smokey) can only show you the Way. To follow the Way, you must become the Way.
This app also inherits the base image policy from P1 which is Free and Open Source Software.
Most of this app components were reused and improved from my other project: apx-bot.
This is a cloud-native, stateless python microservice web app using python38 DoD Hardened Container (DHC) as the base image and pulled from Platform One registry at build time with Docker. DHC is an OCI-compliant image that is secured and made compliant with the DoD Hardened Containers Cybersecurity Requirements. It is maintained by The DoD Container Hardening Team, which is composed of DevSecOps Engineers and other container experts that have knowledge of the product being hardened. They also have an understanding of DISA Security Requirements Guide (SRG) and Security Technical Implementation Guide (STIG) information.
This app is running a Discord bot client that forwards Sentry webhook requests to authorized Discord channels as a real-time alert with optional mention support to the authorized Discord user. Discord channel can be authorized through invoking bot command by user with a sufficient permission in the respective Discord server. To authorize and enable the optional mention support, Sentry project must be registered first by the respective user through invoking bot command.
It is using Sanic web framework to run asynchronous web server for better scalability and performance in forwarding Sentry webhook requests on top of asynchronous Discord.py client event loop. This app is using TLS connection but does not rely on Nginx reverse proxy that forwards all HTTPS requests to an HTTP Sanic backend in order to make this app as self-contained as possible and to ensure maximum portability in deployment without having to reconfigure existing Nginx configuration, if any. Instead, deliberate design decision was made to use self-signed TLS certificate generated at build time solely for the purpose of leveraging TLS traffic encryption. This certificate is not signed by CA and will not pass strict TLS validation from clients outside the scope of this app. Furthermore, this Sanic web server implementation is designed to invalidate existing TLS certificate on daily basis if app runtime is to be restarted.
IP validation is implemented on the endpoint utilizing decorator to whitelist Sentry.io outbound IP addresses on production runtime mode and prevent unauthorized request while it is possible to pass any IP address on development runtime mode for testing purpose. Input validation is implemented on the endpoint against both query string parameters and JSON schema with decorator utilizing Cerberus to ensure that only properly formed data is entering the workflow. Additionally, strict exception handling is implemented for every integer conversions on request data after passing endpoint input validation. Logging is used extensively on the endpoint with every invalid requests being contextually logged. Integration with Sentry enables meta capability of live endpoint cyber threat monitoring through Discord for proactive attack detection. Respectively, all unhandled exception occurrences on Discord commands are also being contextually logged. These security measures were made to be compliant to OWASP best practices.
Google Cloud Firestore is used as a serverless noSQL database with ACID transactions to resiliently store channels, project registrations and API keys configuration variables.
There are two available built-in runtime modes, each accomodates for development and production purpose. This is designed to keep environments across the application lifecycle as similar as possible and maximize dev/prod parity.
-
Authorize desired channel in your Discord server by invoking
!sentry authorize #channel
command (More Info) -
Copy ID of the authorized channel. Make sure you have Developer Mode enabled in Discord and then right click on desired channel -> Copy ID (More Info)
-
Go to your Sentry Dashboard and navigate to Settings -> Integrations page and search for webhook
- Select "WebHooks" integration and click on "Add to Project" button
- On the WebHooks configuration page, copy the following endpoint query to the Callback URLs form:
https://178.62.3.61:8080/sentry-bot/channel?id=your-discord-channel-id
replace your-discord-channel-id
with your previously authorized Discord channel ID, click on "Save Changes" button and "Enable Plugin"
- Click "Test Plugin" button on Sentry WebHooks configuration page and you will receive the test alert for your project on your authorized channel
- Go to your Sentry Dashboard and navigate to Alerts -> click on "Create Alert Rule" button
- Configure the new alert rule to your likings and click on "Add actions..." dropdown menu under "THEN" condition -> select "Send a notification via an integration" -> select "WebHooks" -> click on "Save Rule" button
Further error messages from this Sentry project will be posted to this channel following the same format
-
On your authorized Discord channel, invoke
!sentry project register project-name-slug
command to register your Sentry project into your Discord user ID (More Info) -
Copy ID of your Discord user. Make sure you have Developer Mode enabled in Discord and then right click on your user on the right tab -> Copy ID (More Info)
-
On the Sentry WebHooks configuration page, add the following argument to the endpoint query string in the Callback URLs form:
https://178.62.3.61:8080/sentry-bot/channel?id=your-discord-channel-id&mention=your-discord-user-id
replace both your-discord-channel-id
with your authorized Discord channel ID and your-discord-user-id
with your Discord user ID and click on "Save Changes" button
- Click "Test Plugin" button on Sentry WebHooks configuration page and you will receive the test alert with mention for your project on your authorized channel
Further error messages from this Sentry project will be posted to this channel following the same format
Display list of commands and usage example
Authorize or revoke bot access to channels
!sentry channel authorize #general
Authorize access to #general channel
!sentry channel revoke #general
Revoke access to #general channel
Register a Sentry project to your Discord user ID and enable mention alert
!sentry project register project-name-slug
Register a Sentry project with the respective project-name-slug to your Discord user ID
You can only register one Sentry project at a time. Invoking this command with different project will overwrite your existing registered project
!sentry project revoke
Revoke a registered Sentry project from your Discord user ID
- Install docker-compose on your machine
- Git clone this repository
- Acquire your Platform One credentials
- Follow this guide to create a Discord Bot account. You should create two instances of these bots for development and production runtime
- Follow this guide to create a Google Cloud Platform project
- Go to Google Cloud Platform Firestore Console -> Click on "Native Mode" button
- Select a regional database location that you desire from the dropdown menu
- After database is initialized, click on "Start Collection" and input the following on the forms:
Collection ID: keys
Document ID: api
Add 4 fields with the following name:
discordKey
discordKey_dev
sentryURL
sentryURL_dev
and with type of String
Fill the values with your own Discord Bot secret tokens and Sentry DSN. You should create two different Sentry DSNs for development and production runtime. Click "SAVE" once done
- Click on "Start Collection" again and fill Collection ID with :
channel-list
leaving the Document ID and fields blank. Repeat this step and createuser-projects
collection without Document ID and fields
You should end up with this configuration:
You can skip to step 14 if you're hosting this app on Google Cloud Platform (e.g. Compute Engine)
- Create a service account for your Google Cloud Platform Project following this guide and grant service account access to project with role:
Firebase Rules System
- Create a service account key for your Google Cloud Platform Project following this guide as JSON
- Copy the downloaded service account key into sentry-bot
main/
directory - On sentry-bot root directory where docker-compose.yml resides, create an
.env
file with the following content:
GOOGLE_APPLICATION_CREDENTIALS="Your-Private-Key-Filename.json"
- On sentry-bot
main/
directory, append Dockerfile on the following line:
CMD ["python", "main.py"]
to
CMD ["python", "main.py", "dev"]
and set development runtime mode
- Invoke
docker login
to https://registry1.dso.mil - Change directory to sentry-bot root directory where build script resides, make it executable with
chmod +x build
and execute build script with./build
- Invoke
docker ps
to see the active Docker processes, take note of the container ID where sentry-bot is running and invokedocker logs -f replace-with-your-container-id
to check sentry-bot standard output and make sure everything is running properly - Change directory to
tests/
from sentry-bot root directory and run tests locally withpython tests.py 0.0.0.0
to ensure that everything is working properly - If you want to run it in production runtime mode, remove
"dev"
argument from Dockerfile CMD - Update your Sentry webhook configuration to use your host IP address on the callback URLs form
- You need to rebuild the image daily because I designed Sanic web server to invalidate existing TLS certificate on daily basis if app runtime is to be restarted
- Use CI/CD tools like Jenkins or CircleCI to automate this orchestration. If you're doing so and you're hosting this app outside Google Cloud Platform, it's better to use HashiCorp's Vault service to manage your private key provisioning and rotate your Google Cloud Platform Service Account private key periodically
https://178.62.3.61:8080/sentry-bot/channel
Type: String
Required: Yes
Description: Authorized Discord channel ID
Example: https://178.62.3.61:8080/sentry-bot/channel?id=819187565546176513
Type: String
Required: Optional
Description: Discord user ID registered to this project
Example: https://178.62.3.61:8080/sentry-bot/channel?id=819187565546176513&mention=814237855571902525
Licensed under the MIT License