Control notification of every GitHub event with OPA/Rego
Tags: opa, rego, github, go
Published: 2022-03-13 01:11:27

---
title: Control notification of every GitHub event with OPA/Rego
published: true
description: 
tags: opa, rego, github, go
//cover_image: https://direct_url_to_image.jpg
---

## TL;DR

- GitHub already provides official notifications and comment-based notification tools, but there are more notification use cases
- OPA/Rego allows for good separation of notification rules and implementation
- I implemented a tool as a PoC that can notify me of all GitHub events.

https://github.com/m-mizutani/ghnotify

## GitHub notification 
There are many ways to be notified of events on GitHub, not only official email notifications and [Slack integration], but various tools make this possible. In particular, [tokite](https://github.com/cookpad/tokite) inspects strings in Issues and Pull Requests (PRs) and notifies Slack if there are comments containing specific keywords. Specific use cases are following:

- Find comments that are not directly mentions by ID but have your name on them.
- Find internal Issues in which keywords related to the topic you are interested in (in your case, for example, "security," "personal information," "authentication," etc.) appear.

GitHub also provides [notifications of various events via webhook](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and- payloads), and in addition to comments, it may be possible to streamline development and increase transparency in GitHub operations by receiving notifications for the following events.

- Results of GitHub Actions (notifications when a particular repository or a particular step succeeds or fails)
- Issue new Deploy key
- Push to a specific branch
- Granting or deleting specific labels
- Creating and archiving repositories
- Team changes
- Installing or uninstalling GitHub App

## Problem about "notification rule"

It would be nice to leverage the notifications provided by GitHub, but there is a challenge in realizing this: "Different organizations and individuals want to receive different notifications. As mentioned above, there are also "specific" behaviors for "specific" resources. This is determined by organizational or individual policies, so creating a tool that provides useful notifications for one organization will be full of noise for another. It is logically possible to have different implementations for different organizations or individuals, but it doesn't seem to make much sense.

In order to do notifications to each organizations and individuals, tokite allows rules to be written that specify the body of comments and issue attributes. This can be done by

- Focusing to issues and PRs, and
- Focusing to the attributes (body, label, title, etc.) that determine whether you want to be notified and how to check them.

Creating a system of rules and implementing them requires a certain amount of effort, so by focusing on items that are likely to be commonly used, the return on investment is increased. But of course, it is very possible that the method of checking does not fit or that the attribute values you want are not included. If the cost of implementation is extremely low, it would be preferable to address all events.

## Separating of Policy and Implementation

Open Policy Agent (OPA) and Rego are available as one solution to this challenge. To give a very rough explanation, Rego is a language that provides only the function of "inputting structural data (such as JSON) and outputting new structural data," and OPA is the execution engine for this function. Usual use cases are to "input request contents and output the allow/deny" or "input cloud configuration information and output whether it complies with the organization's policy", but OPA/Rego is highly versatile and can be used not only for information security/compliance but also another systematic rule engine.

In this case, by entrusting OPA with the function of "inputting GitHub event data and outputting notification contents". It is possible to separate implementation and rules (policies). The figure below illustrates this workflow.

![Workflow of notification](https://storage.googleapis.com/zenn-user-upload/f1da6b4e278e-20220312.jpg)

OPA's policy determination process can be done by 1) processing imported Rego file with [runtime in Go](https://github.com/open-policy-agent/opa) or 2) inquiry to [OPA server](https://www.openpolicyagent.org/docs/latest/#running-opa). It can be chosen by organization or individual usage.

OPA can handle any form of structural data as input. Therefore, even if there are multiple schemas for each event like GitHub, there is no need to implement separate processing for each schema, and it is possible to "just throw it in as input". The implementation receiving the notifications can then give up concern about the schema.

The same is true for notifications: the only instruction returned from OPA is "send a notification with this text or item". Therefore, the notification part is also almost schema-neutral, just format the message and hit the API.

So, 

- No need to implement dependencies on GitHub notification event types, so all events can be handled without additional implementation costs
- Separation of implementation and policy ensures that changes to notification rules do not affect implementation

## ghnotify

So, I implemented a tool to filter notification of GitHub event by OPA/Rego.

{% embed https://github.com/m-mizutani/ghnotify %}.

The specific usage is detailed in the README, but for example, the following rules could be written.

```rego
notify[msg] {
    input.name == "issue_comment"
    contains(input.event.comment.body, "mizutani")
    msg := {
        "channel": "#notify-mizutani",
        "text": "Hello, mizutani",
        "body": input.event.comment.body,
    }
}
```

If the word `mizutani` is included in the comments of an issue in the monitored repository, the following notification will be sent via Slack.

![](https://user-images.githubusercontent.com/605953/155864886-c9c8ccbb-809c-44df-8925-fe69a0d820f4.png)

There are 3 usages for the tool.

- Case 1) Run ghnotify as a web service and receive webhooks
    - Case 1.1) Query an OPA server running separately
    - Case 1.2) Read the policy file included with the ghnotify web service
- Case 2) Catching events on GitHub Actions

Case 1 requires setting up a GitHub App or Webhook and deploying ghnotify as a web service, which is more time-consuming, but it is useful when you want to monitor many repositories because you can receive notifications for the entire Organization. Case 2, on the other hand, is easier to set up, but only allows you to monitor one repository. The author uses the following configuration.

![](https://storage.googleapis.com/zenn-user-upload/dc6764646b16-20220312.jpg)

# Policy examples

What other specific policies can we write? I would like to briefly introduce the following.

## Specific labels were assigned to the PR

```rego
notify[msg] {
    input.name == "pull_requests"
    input.event.action == "labeled"
    input.event.label.name == "breaking-change"

    msg := {
        "channel": "#alert",
        "text": "A new breaking change PR",
    }
}
```

## GitHub Actions for a specific repository failed

```rego
notify[msg] {
    input.name == "workflow_run"
    input.event.action == "completed"
    input.event.repository.full_name == "m-mizutani/nanika"
    input.event.workflow.name == "deploy"
    input.event.workflow_run.conclusion != "success"

    msg := {
        "text": "nanika deployment failed",
    }
}
```

## New Deploy Key issued

```rego
notify[msg] {
    input.name == "deploy_key"
    input.event.action == "created"
    msg := {
        "channel": "#alert",
        "text": "A new deploy key created",
        "fields": [
            {
                "name": "title",
                "value": input.event.key.title,
            },
            {
                "name": "read_only",
                "value": input.event.key.read_only,
            },
        ],
    }
}
```

## Create, delete, and publish repositories

```rego
notify[msg] {
    input.name == "repository"
    input.event.action == "created"

    msg := {
        "channel": "#alert",
        "text": "repository created",
    }
}

notify[msg] {
    input.name == "repository"
    input.event.action == "deleted"

    msg := {
        "channel": "#alert",
        "text": "repository created",
    }
}

notify[msg] {
    input.name == "repository"
    input.event.action == "publicized"

    msg := {
        "channel": "#alert",
        "text": "repository publicized",
    }
}
```

# Conclusion

OPA and Rego are mainly used in the field of information security and compliance because they are "policy languages," but the development philosophy of OPA and Rego is "to separate the logic of implementation and decision making" (Policy Decoupling).
Personally, I believe that there are many tools and systems that can take advantage of this idea of separation of implementation and policy, and I would like to continue to consider its application.