Skip to content

secdev02/Canary-ActionRunner

Repository files navigation

Canary Action Runner — ProjFS Canarytoken Alerting via GitHub Actions

A Windows service creates a virtual directory tree of convincing-looking credential files (SSH keys, AWS creds, .env files, etc.) using the Windows Projected File System (ProjFS). When any file is opened, two alerts fire simultaneously:

  1. DNS canary — a lookup to your canarytokens.com token domain, recorded in the Thinkst dashboard.
  2. GitHub repo dispatch — a webhook POST to your repository that triggers file-access-alert.yml, which opens a GitHub Issue and (optionally) posts to Slack.

How it Works

Attacker opens decoy file
        │
        ▼
ProjFS-Service (Windows)
        │
        ├─► DNS lookup → canarytokens.com  (passive, no auth needed)
        │
        └─► POST /repos/{owner}/{repo}/dispatches
                    │
                    ▼
            file-access-alert.yml
                    │
                    ├─► Creates GitHub Issue  🚨
                    └─► Posts to Slack        (optional)

Repository Layout

Place these files at the root of your repository before activating the workflows:

your-repo/
├── ProjFS-Service.cs               # Windows service source
├── ProjFS-Service.exe.config       # Virtual filesystem layout + runtime config
└── .github/
    └── workflows/
        ├── deploy-honeypot-service.yml   # Builds & installs the service
        └── file-access-alert.yml         # Receives alerts, opens Issues

Prerequisites

Requirement Details
Windows runner A self-hosted Windows runner registered to this repo. For quick testing only, the ephemeral windows-latest GitHub-hosted runner also works.
.NET Framework 4.8 Pre-installed on Windows Server 2019+. Required to compile ProjFS-Service.cs.
Admin rights on runner Needed for ProjFS feature enablement and Windows Service installation.
Windows 10 1809 / Server 2019+ Minimum OS for the ProjFS kernel feature.

Step 1 — Create a Canarytoken (DNS)

  1. Go to canarytokens.com/generate.

  2. Select token type DNS.

  3. Enter a reminder (e.g. Windows honeypot — prod server).

  4. Click Create my Canarytoken.

  5. Copy the token hostname — it looks like:

    abc1def2ghi3.canarytokens.com
    

    This is the value you will store as ALERT_DOMAIN.

When the service runs on the target host, every file access fires a DNS lookup to a subdomain of this hostname. The Thinkst dashboard shows the timestamp, source IP, and (encoded in the subdomain) the filename and process name.


Step 2 — Create a GitHub Personal Access Token (PAT)

The PAT is used by the Windows service to POST the canary-file-access repository dispatch event. The file-access-alert.yml workflow then picks it up and opens an Issue.

Classic PAT (simpler)

  1. Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic).
  2. Click Generate new token (classic).
  3. Set an expiry appropriate for your deployment window.
  4. Under Select scopes, tick repo (the entire top-level scope). This grants the contents, issues, and metadata access needed for repository_dispatch and Issue creation.
  5. Click Generate token and copy it immediately — you will not see it again.

The value you store in ALERT_WEBHOOK_AUTH must include the Bearer prefix:

Bearer ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Fine-grained PAT (recommended for least privilege)

  1. Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens.
  2. Click Generate new token.
  3. Set Resource owner to the account or org that owns this repository.
  4. Under Repository access, select Only select repositories and choose this repo.
  5. Under Permissions → Repository permissions, grant:
    • Contents — Read-only
    • Issues — Read and write
    • Metadata — Read-only (mandatory, auto-selected)
  6. Click Generate token, copy the value.

Store as:

Bearer github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Security note: Store this PAT only in GitHub Secrets (see Step 3). Never commit it to the repository.


Step 3 — Configure Repository Secrets

Go to your repository → Settings → Secrets and variables → Actions → New repository secret and create each secret below.

Required secrets

Secret name Value Where it comes from
ALERT_DOMAIN abc1def2ghi3.canarytokens.com Canarytoken hostname from Step 1
ALERT_WEBHOOK_URL https://api.github.com/repos/{owner}/{repo}/dispatches Replace {owner} and {repo} with your GitHub username/org and repository name
ALERT_WEBHOOK_AUTH Bearer ghp_xxx… or Bearer github_pat_xxx… PAT created in Step 2, with the Bearer prefix

Optional secret

Secret name Value Purpose
SLACK_WEBHOOK_URL https://hooks.slack.com/services/… Slack incoming webhook URL. If absent the Slack step is silently skipped (continue-on-error: true).

What each secret does at runtime

deploy-honeypot-service.yml patches these three values directly into ProjFS-Service.exe.config on the runner before installing the service:

AppSettings key   ← GitHub Secret
─────────────────────────────────
RootPath          ← workflow input (not a secret)
AlertDomain       ← ALERT_DOMAIN
WebhookUrl        ← ALERT_WEBHOOK_URL
WebhookAuth       ← ALERT_WEBHOOK_AUTH

The service reads them from the config at startup. They are never written to disk in plaintext outside of that patched config file on the runner itself.


Step 4 — Copy the Workflow Files

Both .yml files must live in .github/workflows/ at the root of the repository. The directory must be created if it does not already exist.

# From the root of your local clone
mkdir -p .github/workflows

cp deploy-honeypot-service.yml .github/workflows/
cp file-access-alert.yml       .github/workflows/

Then commit and push:

git add .github/workflows/
git commit -m "Add honeypot deploy and alert workflows"
git push

GitHub Actions picks up any .yml file inside .github/workflows/ automatically — no further registration is needed.


Step 5 — Add Required Issue Labels

file-access-alert.yml applies three labels when it opens an Issue. Create them in your repository before the first alert fires so the Issue creation does not fail.

Go to Issues → Labels → New label and create:

Label Suggested colour
security #d93f0b (red-orange)
honeypot #e4e669 (yellow)
high-priority #b60205 (dark red)

Step 6 — Register a Self-Hosted Windows Runner

For a persistent honeypot (the whole point of this setup) you need a self-hosted runner on the target Windows machine. The GitHub-hosted windows-latest runner is ephemeral and resets after each job.

  1. Go to your repository → Settings → Actions → Runners → New self-hosted runner.
  2. Select Windows and follow the on-screen instructions to download and configure the runner agent.
  3. Run the agent as a service with Administrator privileges so it can install ProjFS and register the Windows service.
  4. Once registered, update the runs-on line in deploy-honeypot-service.yml:
# Before (ephemeral, testing only)
runs-on: windows-latest

# After (persistent, production)
runs-on: [self-hosted, windows, x64]

Step 7 — Deploy the Honeypot

  1. Go to Actions → Deploy ProjFS Fake FileSystem Service.

  2. Click Run workflow.

  3. Fill in the inputs:

    Input Description Default
    runner_type github-hosted for a quick test, self-hosted for production github-hosted
    action deploy installs and starts the service; remove uninstalls it deploy
    root_path Windows path where the decoy directory will appear C:\DevRepo
  4. Click the green Run workflow button.

The workflow will:

  • Enable the Client-ProjFS Windows feature if not already active.
  • Compile ProjFS-Service.cs with the .NET 4.8 csc.exe compiler.
  • Deploy and patch ProjFS-Service.exe.config with your secrets.
  • Register the WindowsFakeFileSystem Windows Event Log source.
  • Install and start the service via InstallUtil.exe.
  • Run a smoke test — opens the first projected file and waits 30 seconds for the webhook round-trip.

A successful run ends with:

Smoke test passed. Check canarytokens dashboard and GitHub Issues for the alert.

What Happens When an Alert Fires

  1. The WindowsFakeFileSystem service fires the webhook POST to the GitHub API.
  2. file-access-alert.yml wakes up on the canary-file-access repository dispatch event.
  3. A GitHub Issue is opened automatically titled:
    🚨 Honeypot Alert: \AWS\credentials accessed on DESKTOP-TARGET
    
    The issue body contains a table of the accessed file path, triggering process, hostname, and UTC timestamp, plus a checklist of immediate response actions.
  4. If SLACK_WEBHOOK_URL is configured, a formatted Slack message is also sent.
  5. The Canarytoken dashboard at canarytokens.com simultaneously records the DNS hit with the source IP.

Testing Without a Live Deployment

file-access-alert.yml supports workflow_dispatch so you can fire a test alert from the GitHub UI at any time without touching the Windows service.

  1. Go to Actions → File Access Alert → Run workflow.
  2. Fill in the test fields (file path, process name, hostname, timestamp).
  3. Click Run workflow.

A real Issue will be created and Slack will be notified (if configured) — identical to a live alert. Use this to verify your secrets and label setup before deploying to a production host.


Customising the Decoy File List

Edit the <fileList> section in ProjFS-Service.exe.config. Each line is:

\Path\To\Entry,isDirectory,fileSize,unixTimestamp
  • isDirectorytrue or false
  • fileSize — size in bytes (displayed to dir/Explorer; content is zero-filled)
  • unixTimestamp — file modification time as a Unix epoch integer

Directories must be declared before any files they contain. Commit the updated config and re-run the deploy workflow to push the new layout to the runner.


Removing the Service

  1. Go to Actions → Deploy ProjFS Fake FileSystem Service → Run workflow.
  2. Set action to remove.
  3. Run.

This stops and uninstalls the WindowsFakeFileSystem Windows service via InstallUtil /u.

About

Deploy ProjFS inside a Github Action Runner or Self-Hosted Runner

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages