sync: auto-sync from GURU-BEAST-ROG at 2026-06-02 10:44:23
Author: Mike Swanson Machine: GURU-BEAST-ROG Timestamp: 2026-06-02 10:44:23
This commit is contained in:
@@ -48,6 +48,7 @@
|
||||
- [Paste-safe command formatting (Howard)](feedback_command_formatting.md) — Two clauses, one root cause: (a) multi-line scripts not semicolon one-liners (wrap breaks paste), (b) all code at column 0 inside fences (indentation breaks PowerShell paste).
|
||||
- [Autonomous infra/build setup](feedback_autonomous_infra_setup.md) — During infra/build/CI/dev setup, just install prerequisites and push through routine steps; reserve check-ins for genuine decisions (forks, destructive/outward, client/prod).
|
||||
- [Check patterns before asking](feedback_check_patterns_before_asking.md) — Before asking how to do something repeat-style (sync, save, sweep, billing), study existing artifacts and workflow docs first; reach for similar past artifacts as the template.
|
||||
- [Pricing verification — no guessing](policy_pricing_verification.md) — ANY cost presented to the team or a client MUST be verified via live web lookup (WebFetch/WebSearch, fallback to headless Chrome). Never estimate from training data. Cite source + date inline. If unreachable, say so — do NOT substitute a guess.
|
||||
- [Client communication tone](feedback_client_tone.md) — How to write client-facing Syncro comments — expert partner, not intake questionnaire.
|
||||
- [Add Mike as owner on all Entra apps](feedback_entra_app_owner.md) — Apps created via management SP have no user owner — must add Mike manually or publisher verification fails.
|
||||
- [No TOML/config file approach for endpoints](feedback_no_toml_config_endpoints.md) — User explicitly prohibits TOML or config-file-based endpoint configuration — this will never be approved.
|
||||
|
||||
30
.claude/memory/policy_pricing_verification.md
Normal file
30
.claude/memory/policy_pricing_verification.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Policy: Pricing Verification — No Guessing
|
||||
|
||||
**Set by:** Mike Swanson, 2026-06-02
|
||||
|
||||
Any time a cost or price is presented to the team or a client, it MUST be verified via a
|
||||
live web lookup before being stated. Never use training-data recollections or estimates.
|
||||
|
||||
## Rules
|
||||
|
||||
- Hardware, parts, software licensing, SaaS pricing, labor rates — all require real-time verification.
|
||||
- Use WebFetch / WebSearch first. Fall back to headless Chrome (`web-fetch-chrome.py`) if bot-blocked.
|
||||
- Always cite source and date inline: `[$X.XX — Amazon, 2026-06-02]`
|
||||
- If no pricing source is reachable, state that explicitly. Do NOT substitute a guess or a range
|
||||
"from memory."
|
||||
- Applies in all contexts: Discord bot, main session, any Claude session in this repo.
|
||||
|
||||
## Scope
|
||||
|
||||
Applies to ALL estimate types:
|
||||
- Computer hardware (drives, RAM, CPUs, etc.)
|
||||
- Replacement parts and peripherals
|
||||
- Software licensing and subscriptions
|
||||
- Third-party service quotes
|
||||
- Repair labor estimates (when citing market rates)
|
||||
|
||||
## Why
|
||||
|
||||
Hardware prices fluctuate significantly. A "budget" vs "mid-range" vs "quality" drive can
|
||||
vary by 2-3x depending on current market. Presenting stale or fabricated numbers to clients
|
||||
damages trust and can result in inaccurate quotes.
|
||||
117
clients/glaztech/session-logs/2026-06-02-session.md
Normal file
117
clients/glaztech/session-logs/2026-06-02-session.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Session Log — 2026-06-02 — Glaz-Tech Industries
|
||||
|
||||
## User
|
||||
- **User:** Mike Swanson (mike)
|
||||
- **Machine:** GURU-BEAST-ROG
|
||||
- **Role:** admin
|
||||
|
||||
---
|
||||
|
||||
## Session Summary
|
||||
|
||||
Mike requested a transport rule in the Glaztech Exchange Online tenant to allow messages from MailProtector as `noreply@azcomputerguru.com` through spam filtering. These are MailProtector quarantine digest notifications sent to Glaztech users on behalf of ACG's no-reply address.
|
||||
|
||||
Before creating the rule, a message trace was pulled (via `Get-MessageTraceV2`) for `noreply@azcomputerguru.com` over the past 10 days to verify that messages were in fact being filtered by Microsoft. The trace confirmed the issue: the vast majority of digest messages delivered successfully, but some recipients were hitting `FilteredAsSpam` status (e.g., `tshaw@glaztech.com` on 2026-06-02 at 3:07 PM). The `gtimail@glaztech.com` address showed `Failed` status on every daily send — this is caused by the existing "GTIMail No-Reply - Reject Inbound" transport rule (Priority 1, `SentToPredicate` → `RejectMessageAction`) and is a separate, pre-existing issue noted for follow-up.
|
||||
|
||||
Authentication to Exchange Online used the ComputerGuru Exchange Operator multi-tenant app (`b43e7342`) with certificate-based credentials from the vault. The token was acquired via `get-token.sh` for the `exchange-op` tier against the Glaztech tenant (`82931e3c-de7a-4f74-87f7-fe714be1f160`) and passed to `Connect-ExchangeOnline -AccessToken` with EXO PowerShell V3 (3.9.2).
|
||||
|
||||
A new transport rule was created: **"SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)"** at Priority 4, condition `From: noreply@azcomputerguru.com`, action `SetSCL -1`. This bypasses all spam and junk folder filtering for these digests. The rule was verified active immediately after creation.
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- **SCL = -1 rather than domain-level bypass:** The sender address `noreply@azcomputerguru.com` is specific enough that setting SCL=-1 on it carries minimal risk. A domain-level bypass (`azcomputerguru.com`) was considered but rejected — too broad, would cover all ACG-origin mail.
|
||||
- **Priority 4:** Placed below the existing SCL bypass rules (Priority 2–3) since no conflict exists; priority ordering doesn't matter for non-overlapping senders. Placed above any catch-all rules that might exist in the future.
|
||||
- **Did not restrict by connector:** The "Inbound Spam Filter" connector has no SenderIPAddresses restriction (per prior decision — avoids blocking calendar invites from external M365 tenants). Adding a connector-based condition to the rule was avoided for the same reason.
|
||||
- **gtimail@glaztech.com not addressed:** The daily `Failed` delivery to `gtimail@glaztech.com` is caused by the pre-existing "GTIMail No-Reply - Reject Inbound" rule. Mike did not request any change to that rule; flagged for separate review.
|
||||
|
||||
---
|
||||
|
||||
## Problems Encountered
|
||||
|
||||
- **`Get-MessageTrace` deprecated:** Initial call to `Get-MessageTrace` returned a deprecation warning and failed. Switched to `Get-MessageTraceV2`. Note: `Get-MessageTraceV2` does not accept `-PageSize` — that parameter does not exist on the V2 cmdlet.
|
||||
- **`New-TransportRule -SenderAddresses` not valid:** First attempt used `-SenderAddresses` which is not a valid parameter. Correct parameter is `-From` for explicit sender address matching.
|
||||
- **Cert not in Windows cert store:** Exchange Operator cert (`A615823DE1CAF15229027DEC075AFE32B900D82C`) is not installed in LocalMachine\My or CurrentUser\My on BEAST. Used `get-token.sh` cert-based JWT flow instead, passing the resulting bearer token to `Connect-ExchangeOnline -AccessToken`.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
- **Exchange Online transport rule created** in `glaztechindustries.onmicrosoft.com`:
|
||||
- Name: `SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)`
|
||||
- Condition: `From = noreply@azcomputerguru.com`
|
||||
- Action: `SetSCL -1`
|
||||
- Priority: 4
|
||||
- State: Enabled
|
||||
- Comments: "Bypass spam filtering for MailProtector quarantine digest emails sent as noreply@azcomputerguru.com. Created 2026-06-02 by ACG."
|
||||
|
||||
---
|
||||
|
||||
## Credentials & Secrets
|
||||
|
||||
- **Vault path used:** `msp-tools/computerguru-exchange-operator.sops.yaml`
|
||||
- App: ComputerGuru - Exchange Operator
|
||||
- Client ID: `b43e7342-5b4b-492f-890f-bb5a4f7f40e9`
|
||||
- Cert thumbprint: `A615823DE1CAF15229027DEC075AFE32B900D82C`
|
||||
- Token acquired via: `bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> exchange-op`
|
||||
|
||||
---
|
||||
|
||||
## Infrastructure & Servers
|
||||
|
||||
- **Glaztech tenant:** `glaztechindustries.onmicrosoft.com`
|
||||
- **Tenant ID:** `82931e3c-de7a-4f74-87f7-fe714be1f160`
|
||||
- **Inbound mail filter:** MailProtector — `glaztech-com.inbound.emailservice.io`
|
||||
- **Inbound connector:** "Inbound Spam Filter" — Partner type, RequireTls=True, no IP restriction (intentional — preserves calendar invite delivery)
|
||||
- **EXO PowerShell module:** ExchangeOnlineManagement 3.9.2
|
||||
|
||||
---
|
||||
|
||||
## Commands & Outputs
|
||||
|
||||
```powershell
|
||||
# Connect to Glaztech EXO with app-only token
|
||||
$token = bash .claude/skills/remediation-tool/scripts/get-token.sh 82931e3c-de7a-4f74-87f7-fe714be1f160 exchange-op
|
||||
Connect-ExchangeOnline -AccessToken $token -Organization 'glaztechindustries.onmicrosoft.com' -ShowBanner:$false
|
||||
|
||||
# Message trace (last 10 days) — confirmed FilteredAsSpam occurrences
|
||||
Get-MessageTraceV2 -SenderAddress 'noreply@azcomputerguru.com' -StartDate (Get-Date).AddDays(-10) -EndDate (Get-Date)
|
||||
# Key finding: tshaw@glaztech.com → FilteredAsSpam (2026-06-02 3:07 PM)
|
||||
# Key finding: gtimail@glaztech.com → Failed daily (pre-existing rule, separate issue)
|
||||
|
||||
# Create rule
|
||||
New-TransportRule `
|
||||
-Name 'SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)' `
|
||||
-From 'noreply@azcomputerguru.com' `
|
||||
-SetSCL -1 `
|
||||
-Priority 4 `
|
||||
-Comments 'Bypass spam filtering for MailProtector quarantine digest emails sent as noreply@azcomputerguru.com. Created 2026-06-02 by ACG.' `
|
||||
-Enabled $true
|
||||
```
|
||||
|
||||
**Final transport rule list (Glaztech):**
|
||||
```
|
||||
Priority 0 Pensky Allow Enabled
|
||||
Priority 1 GTIMail No-Reply - Reject Inbound Enabled
|
||||
Priority 2 SCL Bypass - hartsglass + olemons (SHVSALES) Enabled
|
||||
Priority 3 SCL Bypass - aaaglassinc.com (SHVSALES) Enabled
|
||||
Priority 4 SCL Bypass - noreply@azcomputerguru.com (MailProtector digests) Enabled
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pending / Incomplete Tasks
|
||||
|
||||
- **gtimail@glaztech.com failing daily:** The "GTIMail No-Reply - Reject Inbound" rule (Priority 1) rejects all inbound mail to `gtimail@glaztech.com`. This causes the daily MailProtector digest to fail for that address. Confirm with Steve Eastman whether `gtimail@glaztech.com` should receive digests (i.e., whether the reject rule should have an exception or be modified).
|
||||
- **Exchange Operator cert not in BEAST cert store:** If cert-based PowerShell connections are needed without `get-token.sh` (e.g., for interactive EXO sessions), the cert will need to be imported to the machine store. Not urgent — token flow works fine for bot-driven operations.
|
||||
|
||||
---
|
||||
|
||||
## Reference Information
|
||||
|
||||
- **Syncro customer ID:** 143932
|
||||
- **EXO rule created:** `SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)` — Priority 4
|
||||
- **EXO PowerShell V2 deprecation note:** `Get-MessageTrace` deprecated Sept 1 2025; use `Get-MessageTraceV2` (no `-PageSize` parameter)
|
||||
- **Vault:** `msp-tools/computerguru-exchange-operator.sops.yaml`
|
||||
- **Token cache:** `/tmp/remediation-tool/82931e3c-de7a-4f74-87f7-fe714be1f160/exchange-op.jwt`
|
||||
@@ -73,6 +73,23 @@ drive the human's interactive session.
|
||||
|
||||
---
|
||||
|
||||
## Pricing — Always Verify, Never Guess
|
||||
|
||||
**MANDATORY:** Before presenting any cost to Mike, Howard, or a client, verify current pricing
|
||||
via live web lookup. Never estimate or recall costs from training data — hardware prices,
|
||||
software licensing, and labor rates change constantly.
|
||||
|
||||
Rules:
|
||||
- Any dollar amount for hardware, parts, software, or services requires a real-time lookup
|
||||
before being stated.
|
||||
- Use WebFetch / WebSearch first. Fall back to headless Chrome if bot-blocked.
|
||||
- Cite the source and date: `[$X.XX — Amazon, 2026-06-02]`
|
||||
- If you cannot reach a pricing source, say so explicitly — do NOT substitute a guess.
|
||||
- Applies to all estimate types: parts, repair quotes, hardware refreshes, software licensing,
|
||||
labor, and third-party services.
|
||||
|
||||
---
|
||||
|
||||
## Task Loop
|
||||
|
||||
For every request, work this loop:
|
||||
@@ -86,13 +103,41 @@ For every request, work this loop:
|
||||
4. **Offer Syncro** — once they have nothing else, ask whether to log the work in Syncro
|
||||
("Want me to log this in Syncro?"). If yes, invoke `/syncro` to create or update the ticket.
|
||||
5. **Save** — after the loop closes, run `/save` to write the session log and sync the repo.
|
||||
6. **Kill the thread** — after `/save` completes successfully, delete the thread:
|
||||
```bash
|
||||
bash C:/Users/guru/ClaudeTools/projects/discord-bot/scripts/delete-thread.sh <Thread ID>
|
||||
```
|
||||
The Thread ID is in every `[DISCORD_CONTEXT]` block as `Thread ID: <id>`. Do not delete
|
||||
if `/save` failed or errored. Do not post a closing message — the deletion is immediate.
|
||||
|
||||
---
|
||||
|
||||
## Thread Lifecycle — Auto-Delete on Completion
|
||||
|
||||
Every `[DISCORD_CONTEXT]` block now includes `Thread ID: <id>` (injected by the bot after
|
||||
the thread is resolved or created). This ID is what you pass to the delete script.
|
||||
|
||||
**When to delete:** Only after step 6 of the Task Loop — `/save` succeeded, session log
|
||||
committed and pushed. The thread is the conversation record; don't kill it before the log lands.
|
||||
|
||||
**When NOT to delete:**
|
||||
- `/save` failed or sync errored
|
||||
- The user said "yes" to continuing (open follow-up items remain)
|
||||
- The thread is an ongoing informational channel, not a single-task session
|
||||
|
||||
**Script:**
|
||||
```bash
|
||||
bash C:/Users/guru/ClaudeTools/projects/discord-bot/scripts/delete-thread.sh <Thread ID>
|
||||
```
|
||||
Source: `projects/discord-bot/scripts/delete-thread.sh` — reads bot token from `.env`, calls
|
||||
`DELETE /channels/{id}` on the Discord API. Exits 0 on HTTP 200/204, 1 on error.
|
||||
|
||||
---
|
||||
|
||||
## Who Is Asking: Discord User Identity
|
||||
|
||||
Every message is prefixed with a `[DISCORD_CONTEXT]` block containing the sender's Discord
|
||||
username, display name, and user ID. Always read this block to determine who is asking.
|
||||
username, display name, user ID, and Thread ID. Always read this block to determine who is asking.
|
||||
|
||||
### Known Team Members — Full Access
|
||||
|
||||
|
||||
@@ -41,7 +41,18 @@ class MessageHandler:
|
||||
await message.reply("Hey! How can I help?")
|
||||
return
|
||||
|
||||
# Resolve or create the thread first so we can include its ID in context.
|
||||
if isinstance(message.channel, discord.Thread):
|
||||
thread = message.channel
|
||||
else:
|
||||
name = self._thread_name(user_text) if user_text else "Attachment"
|
||||
thread = await message.create_thread(
|
||||
name=name,
|
||||
auto_archive_duration=1440,
|
||||
)
|
||||
|
||||
# Build caller-identity header so the agent always knows who is asking.
|
||||
# Thread ID is included so the agent can delete the thread on completion.
|
||||
author = message.author
|
||||
display = getattr(author, "display_name", author.name)
|
||||
guild_name = message.guild.name if message.guild else "DM"
|
||||
@@ -51,20 +62,11 @@ class MessageHandler:
|
||||
f"User: @{author.name}"
|
||||
+ (f" (display: {display})" if display != author.name else "")
|
||||
+ f" | ID: {author.id}\n"
|
||||
f"Channel: #{channel_name} | Guild: {guild_name}\n"
|
||||
f"Channel: #{channel_name} | Thread ID: {thread.id} | Guild: {guild_name}\n"
|
||||
"[/DISCORD_CONTEXT]\n\n"
|
||||
)
|
||||
content = (discord_ctx + user_text).strip()
|
||||
|
||||
if isinstance(message.channel, discord.Thread):
|
||||
thread = message.channel
|
||||
else:
|
||||
name = self._thread_name(user_text) if user_text else "Attachment"
|
||||
thread = await message.create_thread(
|
||||
name=name,
|
||||
auto_archive_duration=1440,
|
||||
)
|
||||
|
||||
attachment_paths = await self._download_attachments(message, thread.id)
|
||||
if attachment_paths:
|
||||
lines = "\n".join(f"- {p}" for p in attachment_paths)
|
||||
|
||||
56
projects/discord-bot/scripts/delete-thread.sh
Normal file
56
projects/discord-bot/scripts/delete-thread.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# delete-thread.sh <thread_id>
|
||||
#
|
||||
# Deletes a Discord thread (channel) via the Discord REST API.
|
||||
# Reads the bot token from projects/discord-bot/.env (DISCORD_TOKEN=...).
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — deleted (HTTP 200 or 204)
|
||||
# 1 — bad args, token not found, or API error
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
THREAD_ID="${1:-}"
|
||||
if [ -z "$THREAD_ID" ]; then
|
||||
echo "[ERROR] Usage: delete-thread.sh <thread_id>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ENV_FILE="$SCRIPT_DIR/../.env"
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo "[ERROR] .env not found at $ENV_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract token — handles quoted and unquoted values, ignores inline comments
|
||||
DISCORD_TOKEN=$(grep '^DISCORD_TOKEN=' "$ENV_FILE" \
|
||||
| head -1 \
|
||||
| sed 's/^DISCORD_TOKEN=//' \
|
||||
| sed "s/[\"']//g" \
|
||||
| sed 's/#.*//' \
|
||||
| tr -d '[:space:]')
|
||||
|
||||
if [ -z "$DISCORD_TOKEN" ]; then
|
||||
echo "[ERROR] DISCORD_TOKEN not found or empty in $ENV_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RESP=$(curl -s -o /tmp/discord_delete_resp.txt -w "%{http_code}" \
|
||||
-X DELETE \
|
||||
"https://discord.com/api/v10/channels/${THREAD_ID}" \
|
||||
-H "Authorization: Bot ${DISCORD_TOKEN}" \
|
||||
-H "Content-Type: application/json")
|
||||
|
||||
HTTP_CODE="$RESP"
|
||||
BODY=$(cat /tmp/discord_delete_resp.txt 2>/dev/null || echo "")
|
||||
rm -f /tmp/discord_delete_resp.txt
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "204" ]; then
|
||||
echo "[OK] Thread ${THREAD_ID} deleted (HTTP ${HTTP_CODE})"
|
||||
exit 0
|
||||
else
|
||||
echo "[ERROR] Delete failed: HTTP ${HTTP_CODE} — ${BODY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -2,11 +2,13 @@
|
||||
type: client
|
||||
name: glaztech
|
||||
display_name: Glaz-Tech Industries
|
||||
last_compiled: 2026-05-24
|
||||
last_compiled: 2026-06-02
|
||||
compiled_by: DESKTOP-0O8A1RL/claude-main
|
||||
sources:
|
||||
- clients/glaztech/session-logs/2026-04-20-session.md
|
||||
- clients/glaztech/session-logs/2026-04-21-session.md
|
||||
- clients/glaztech/session-logs/2026-05-28-session.md
|
||||
- clients/glaztech/session-logs/2026-06-02-session.md
|
||||
- clients/glaztech/reports/2026-04-17-phishing-incident-report.md
|
||||
- clients/glaztech/PROJECT_STATE.md
|
||||
- clients/glaztech/README.md
|
||||
@@ -43,12 +45,39 @@ No dedicated on-premises server infrastructure documented. Multi-site Windows en
|
||||
- **Tenant ID:** 82931e3c-de7a-4f74-87f7-fe714be1f160
|
||||
- **Primary domain:** glaztech.com
|
||||
- **Inbound mail filter:** MailProtector — `glaztech-com.inbound.emailservice.io` (MX 5, sole MX as of 2026-04-17)
|
||||
- **MailProtector IPs (EFSkipIPs on inbound connector):** 162.248.93.233, 162.248.93.81, 65.113.52.82
|
||||
- **DMARC:** p=reject; sp=reject (hardened 2026-04-17, was p=none)
|
||||
- **DKIM:** CNAME records exist for selector1/selector2 — active status unverified [WARNING: confirm DKIM is active in M365]
|
||||
- **MFA status:** [WARNING] DISABLED as of 2026-04-21. Security Defaults off. No Conditional Access (requires Entra P1, not licensed). ~160 users with password-only sign-in. MFA rollout is open work item — do not enable Security Defaults until service account audit is complete (see Active Work).
|
||||
- **Licensing:** Basic M365 (no Entra P1 / Business Premium). Per-user MFA or Security Defaults are the available free options.
|
||||
- **Mailbox forwarding (internal, low risk):** Payroll@glaztech.com → carmen@glaztech.com; TUCCSR@glaztech.com → bryce@glaztech.com
|
||||
- **OAuth consent grants:** 38 grants — not audited as of last session
|
||||
- **EXO PowerShell:** ExchangeOnlineManagement 3.9.2. `Get-MessageTrace` deprecated Sept 2025 — use `Get-MessageTraceV2` (no `-PageSize` parameter).
|
||||
|
||||
### Exchange Online Transport Rules
|
||||
|
||||
Full transport rule list as of 2026-06-02:
|
||||
|
||||
| Priority | Name | Condition | Action | State |
|
||||
|---|---|---|---|---|
|
||||
| 0 | Pensky Allow | [unknown] | [unknown] | Enabled |
|
||||
| 1 | GTIMail No-Reply - Reject Inbound | SentTo: gtimail@glaztech.com | RejectMessageAction | Enabled |
|
||||
| 2 | SCL Bypass - hartsglass + olemons (SHVSALES) | From: hartsglass@centurytel.net, olemons@eastexglass.com, SSales@arkglass.com, bossier@glassservices.com | SetSCL -1 | Enabled |
|
||||
| 3 | SCL Bypass - aaaglassinc.com (SHVSALES) | SenderDomainIs: aaaglassinc.com | SetSCL -1 | Enabled |
|
||||
| 4 | SCL Bypass - noreply@azcomputerguru.com (MailProtector digests) | From: noreply@azcomputerguru.com | SetSCL -1 | Enabled |
|
||||
|
||||
Rule GUIDs: Priority 2 = 482c714a-8780-4c62-ae0a-0b6da9ca9d52; Priority 3 = 7e0c01a8-ec22-43fe-b600-796c0f295aa5. GUIDs for Priority 0, 1, 4 not recorded.
|
||||
|
||||
Note on Priority 1: The "GTIMail No-Reply - Reject Inbound" rule rejects ALL inbound mail to gtimail@glaztech.com, which causes the daily MailProtector digest for that address to fail. This is a pre-existing rule — review with Steve is pending (see Active Work).
|
||||
|
||||
### Inbound Connector
|
||||
|
||||
- **Name:** "Inbound Spam Filter"
|
||||
- **Type:** Partner
|
||||
- **RequireTls:** True
|
||||
- **EFSkipIPs:** 162.248.93.233, 162.248.93.81, 65.113.52.82 (MailProtector IPs)
|
||||
- **SCLMinusOne:** null (EOP re-evaluates all mail; do NOT change to true — too broad)
|
||||
- **SenderIPAddresses restriction:** None (intentional — avoids blocking calendar invites from external M365 tenants)
|
||||
|
||||
### Network
|
||||
|
||||
@@ -61,11 +90,13 @@ No dedicated on-premises server infrastructure documented. Multi-site Windows en
|
||||
|
||||
- **Remediation tool:** ComputerGuru apps consented in tenant (Exchange Operator, Security Investigator, Tenant Admin, Defender Add-on)
|
||||
- **Exchange Operator App ID:** b43e7342-5b4b-492f-890f-bb5a4f7f40e9
|
||||
- **Exchange Operator cert thumbprint:** A615823DE1CAF15229027DEC075AFE32B900D82C (not in Windows cert store on BEAST — use `get-token.sh` bearer token flow)
|
||||
- **Remediation tool app (AI):** fabb3421-8b34-484b-bc17-e46de9703418
|
||||
- **Exchange Admin role:** Assigned to ACG service principal in Entra
|
||||
- **Global Admin account:** admin@glaztechindustries.onmicrosoft.com (ACG admin only — external GA from tomakkglass.com removed 2026-04-21)
|
||||
- **Vault path:** `clients/glaztech/` [no SOPS credential file documented — remediation tool uses MSP-wide app credentials]
|
||||
- **Exchange Operator vault:** `msp-tools/computerguru-exchange-operator.sops.yaml`
|
||||
- **Token acquisition:** `bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> exchange-op` → `Connect-ExchangeOnline -AccessToken $token -Organization 'glaztechindustries.onmicrosoft.com'`
|
||||
- **DNS access:** `root@172.16.3.10` (IX server)
|
||||
- **Deploy (endpoints):** ScreenConnect or GuruRMM
|
||||
|
||||
@@ -73,9 +104,14 @@ No dedicated on-premises server infrastructure documented. Multi-site Windows en
|
||||
|
||||
- **Phishing via direct-to-M365 MX bypass:** Two phishing campaigns in April 2026 succeeded because DNS had a secondary MX record (`glaztech-com.mail.protection.outlook.com` at priority 10) that bypassed MailProtector. Hardened: MX 10 removed, DMARC to p=reject, Enhanced Filtering for Connectors enabled. Do not re-add a secondary MX record.
|
||||
- **Inbound connector IP restriction:** Do NOT restrict `SenderIPAddresses` on the "Inbound Spam Filter" connector — blocks legitimate calendar invites from external M365 tenants (learned from Dataforth incident). EFSkipIPs are set to MailProtector IPs instead.
|
||||
- **Do NOT set SCLMinusOne=true on connector:** This would trust MailProtector's verdict for all inbound mail — too broad. Use targeted transport rules for specific senders instead.
|
||||
- **DMARC-rejecting vendor senders:** With Enhanced Filtering enabled, EOP looks past MailProtector to the original sender's SPF/DKIM/DMARC. Vendors with `p=reject` domains (e.g., centurytel.net, eastexglass.com) get hard 550 5.7.509 NDR rejections. Fix: SCL=-1 transport rule scoped to the specific sender address or domain. Transport rules evaluate before DMARC enforcement in EOP.
|
||||
- **EXO transport rule name limit:** 64-character maximum. Plan names accordingly.
|
||||
- **EXO REST API:** Direct `/TransportRule` REST endpoints 404 in this tenant. Use `InvokeCommand` pattern: `POST /adminapi/beta/{tenant}/InvokeCommand` with `{"CmdletInput": {"CmdletName": "New-TransportRule", "Parameters": {...}}}`.
|
||||
- **Service accounts need audit before MFA rollout:** Shoretel, mitel, Gti-FaxFinder, GTIMail, GTIQUOTE, CAS1944, clerk — all need SMTP/auth method confirmation before Security Defaults can be enabled.
|
||||
- **PDF preview broken (MOTW):** Windows KB5066791/KB5066835 broke PDF preview on network shares via Mark of the Web. Fix scripts are ready in `clients/glaztech/` — deployment is pending (as of 2026-03-30).
|
||||
- **clearcutglass.com DMARC history:** Corena Spottsville (clearcutglass.com) emails to seastman and zulema were rejected. Temporary transport rule (SCL=-1) was set and removed on 2026-04-21. SPF ~all weakness noted to Team Logic IT (Jordan Fox, jfox@tlit60302.com); recommend they harden to -all and confirm DKIM.
|
||||
- **glassservices.com SPF broken:** `bossier@glassservices.com` publishes `v=spf1 -all` — rejected by all mail providers. SCL=-1 rule covers this as a workaround. Steve should notify vendor to fix SPF.
|
||||
- **Client tone:** ACG has managed GlazTech ~15 years. Steve Eastman is a trusted internal IT partner. Comments and communication should lead with what we know, state findings and actions taken, ask only one targeted question if needed — not open-ended discovery.
|
||||
- **Unlicensed accounts (pending Steve confirmation):** Chauntelle@glaztech.com, Denouser1@glaztech.com, Gti-FaxFinder@glaztech.com.
|
||||
|
||||
@@ -103,6 +139,10 @@ Waiting on Steve's reply to:
|
||||
|
||||
MFA rollout plan: Phase 1 — user communication (install Authenticator); Phase 2 — enable enforcement; Phase 3 — follow-up stragglers; Phase 4 (future/P1) — Conditional Access with trusted IPs for office locations.
|
||||
|
||||
### gtimail@glaztech.com Daily Digest Failure (Pending — review with Steve)
|
||||
|
||||
The "GTIMail No-Reply - Reject Inbound" transport rule (Priority 1) rejects all inbound mail to `gtimail@glaztech.com`, causing the daily MailProtector digest for that address to fail every day. This is a pre-existing rule and was not modified during the 2026-06-02 session. Confirm with Steve Eastman whether `gtimail@glaztech.com` should receive MailProtector digests — if so, the rule needs an exception or the recipient needs to be removed from the MailProtector digest list.
|
||||
|
||||
### Pending follow-ups
|
||||
|
||||
- Audit 38 OAuth consent grants (not done as of 2026-04-21)
|
||||
@@ -110,7 +150,9 @@ MFA rollout plan: Phase 1 — user communication (install Authenticator); Phase
|
||||
- Monitor DMARC aggregate reports (rua=noreply@glaztech.com — should be a monitored mailbox or reporting service)
|
||||
- Security awareness training for staff (multiple employees forwarded and replied to obvious phishing in April 2026)
|
||||
- Review whether any user clicked phishing links (check sign-in logs for suspicious auth attempts post-April 17)
|
||||
- Confirm test email clean delivery from clearcutglass.com after DMARC fix
|
||||
- Notify Steve: glassservices.com vendor needs to fix their SPF record (`v=spf1 -all`)
|
||||
- Harts Glass original rejected emails need to be resent by sender — our SCL bypass is live but NDR'd messages do not auto-retry
|
||||
- Consider creating retroactive Syncro ticket for 2026-05-28 SHVSALES email delivery work
|
||||
|
||||
## History Highlights
|
||||
|
||||
@@ -119,6 +161,8 @@ MFA rollout plan: Phase 1 — user communication (install Authenticator); Phase
|
||||
- **2026-04-17** — Two phishing campaigns bypassed MailProtector via direct-to-M365 MX bypass. 32 messages purged across 8 users. Hardened: MX 10 removed, DMARC p=reject, Enhanced Filtering Connectors enabled. Remediation tool onboarded (admin consent, Exchange Admin role). Forensic evidence preserved in `clients/glaztech/reports/`.
|
||||
- **2026-04-20** — Exchange transport rule created to allow clearcutglass.com mail (DMARC bypass, SCL=-1) while Team Logic IT fixed their DNS. Ticket #32176 created.
|
||||
- **2026-04-21** — clearcutglass.com DNS fixed by Team Logic IT (Jordan Fox). Transport rule removed. External Global Admin (glaztechadmin from tomakkglass.com / Team Logic IT) removed from tenant. M365 security review surfaced: no MFA, 38 OAuth grants, unlicensed accounts, service account audit needed. Ticket #32186 opened for MFA implementation. Feedback: use expert-partner tone with Steve, not open-ended discovery questions.
|
||||
- **2026-05-28** — SHVSALES@glaztech.com vendor email delivery failure. Root cause: vendors (centurytel.net, eastexglass.com) publish DMARC p=reject; Enhanced Filtering re-evaluates past MailProtector relay, producing 550 5.7.509 NDR. Fix: two SCL=-1 transport rules created (Priority 2: specific addresses for hartsglass, olemons, SSales, bossier; Priority 3: aaaglassinc.com domain). glassservices.com SPF broken (`-all`) — workaround only, vendor must fix.
|
||||
- **2026-06-02** — MailProtector quarantine digest messages from `noreply@azcomputerguru.com` confirmed hitting `FilteredAsSpam` for some recipients (e.g., tshaw@glaztech.com). Transport rule created: "SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)" at Priority 4 (From=noreply@azcomputerguru.com, SetSCL=-1). Message trace via `Get-MessageTraceV2` also revealed `gtimail@glaztech.com` failing daily due to pre-existing Priority 1 reject rule — flagged for Steve review.
|
||||
|
||||
## Backlinks
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Run `/wiki-lint` to check for stale entries and broken backlinks.
|
||||
| [ACG Internal Infrastructure](clients/internal-infrastructure.md) | ACG's own hosting infra — Neptune Exchange (cert expires 2026-05-31, DkimSigner disabled), IX server, Cloudflare tunnel workaround, ACG M365 tenant gaps | 2026-05-24 |
|
||||
| [BirthBiologic](clients/birth-biologic.md) | Bio/healthcare; BB-SERVER (WS2016) GuruRMM enrolled; Datto→SharePoint migration incomplete; M365 apps partially consented | 2026-05-24 |
|
||||
| [CryoWeave](clients/cryoweave.md) | Custom cryogenic cable assemblies; cPanel on IX; website redesign + SEO project in progress; Syncro ID not documented | 2026-05-24 |
|
||||
| [Glaz-Tech Industries](clients/glaztech.md) | ~200 users, 9 locations; M365; two phishing campaigns bypassed MailProtector via secondary MX (removed); no MFA enforcement yet | 2026-05-24 |
|
||||
| [Glaz-Tech Industries](clients/glaztech.md) | ~200 users, 9 locations; M365; two phishing campaigns bypassed MailProtector via secondary MX (removed); no MFA enforcement yet; SCL bypass rules for vendor DMARC failures + MailProtector digests | 2026-06-02 |
|
||||
| [Grabb & Durando Law Office](clients/grabb-durando.md) | Personal injury law firm; GND-SERVER GuruRMM enrolled; AI demand review app scoped ($4K–$7K); website migration pending; plaintext DB password in README needs vaulting | 2026-05-24 |
|
||||
| [Pavon](clients/pavon.md) | Former/archive client; GeoVision NVR surveillance; OwnCloud at 172.16.3.22 backed by Uranus; cron stacking fixed; Nextcloud migration deferred 3–6 months | 2026-05-24 |
|
||||
| [Rednour Law Offices](clients/rednour.md) | Law firm; M365 rednourlaw.com (tenant 4a4ca18a) fully onboarded 2026-05-31; all 5 ComputerGuru SPs consented; no MDE license; 3 workstations GuruRMM enrolled (FRONTDESKRECEPT/LEGALASST/REDNOURCARRIEVI); Carla Skinner renamed from Emma; prior MSP agents (ScreenConnect/Splashtop/Datto) still present; shared-drive access for Nick Pafford deferred | 2026-06-02 |
|
||||
|
||||
Reference in New Issue
Block a user