-
-
Notifications
You must be signed in to change notification settings - Fork 0
365 lines (321 loc) · 14.4 KB
/
Copy pathlockdown.yml
File metadata and controls
365 lines (321 loc) · 14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# SPDX-License-Identifier: MPL-2.0
# SPDX-FileCopyrightText: 2025 Hyperpolymath
#
# CI/CD Lockdown Workflow - Maximum Protection
# Enforces no code changes unless explicitly forked
name: Lockdown Enforcement
on:
push:
branches: ['**']
pull_request:
branches: ['**']
fork:
create:
delete:
repository_dispatch:
permissions:
contents: read
security-events: write
jobs:
# ==========================================================================
# Verify Signed Commits
# ==========================================================================
verify-signatures:
name: Verify Commit Signatures
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
with:
fetch-depth: 0
- name: Verify All Commits Signed (via GitHub API)
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
# The previous implementation used `git verify-commit` locally
# on the runner. That ALWAYS fails for signed commits because
# CI runners have no GPG keyring with the signer's public
# key, so verify-commit can't validate the signature and
# exits 1 — flagging every signed human commit as "unsigned"
# (saw on PR #89, 2026-05-25 — commits with local `%G?: G`
# rejected in CI).
#
# GitHub already verifies signatures server-side and exposes
# the result via the REST API. We query each commit's
# verification status directly. This also implicitly handles
# the auto-merge-commit case: the API only knows about real
# commits, not the ephemeral `refs/pull/N/merge`.
echo "Checking commit signatures via GitHub API..."
# Trusted bot accounts that don't require signatures.
TRUSTED_BOTS="noreply@anthropic.com github-actions[bot] dependabot[bot]"
# Range: PR head vs base (PRs) or current branch tip vs main (pushes).
if [[ -n "${GITHUB_BASE_REF:-}" ]]; then
RANGE="origin/${GITHUB_BASE_REF}..HEAD"
git fetch --no-tags --depth=50 origin "${GITHUB_BASE_REF}" || true
else
RANGE="origin/main..HEAD"
fi
failed=0
for commit in $(git log --no-merges --format=%H "$RANGE" 2>/dev/null || git log --no-merges --format=%H -10); do
AUTHOR_EMAIL=$(git log -1 --format='%ae' "$commit")
AUTHOR_NAME=$(git log -1 --format='%an' "$commit")
# Trusted bot allowlist (still useful for dependabot etc.).
is_bot=false
for bot in $TRUSTED_BOTS; do
if [[ "$AUTHOR_EMAIL" == "$bot" ]] || [[ "$AUTHOR_NAME" == "$bot" ]]; then
is_bot=true
echo "✓ Trusted bot commit: $commit ($AUTHOR_EMAIL)"
break
fi
done
[[ "$is_bot" == "true" ]] && continue
verified=$(gh api "repos/${REPO}/commits/${commit}" --jq '.commit.verification.verified' 2>/dev/null || echo "")
reason=$(gh api "repos/${REPO}/commits/${commit}" --jq '.commit.verification.reason' 2>/dev/null || echo "unknown")
# `bad_email` means the SIGNATURE itself is valid (GPG checks
# out) but the signing key's UID set on the GitHub-registered
# key doesn't include this commit's author email. Soft-pass
# with a warning: the commit IS cryptographically signed by a
# key the user controls; only the GitHub-side UID binding is
# incomplete. Hard policy enforcement would require the user
# to add the noreply email as a UID on the key and re-upload.
if [[ "$verified" == "true" ]]; then
echo "✓ Signed and verified by GitHub: $commit"
elif [[ "$reason" == "bad_email" ]]; then
echo "⚠ Signed but GitHub UID mismatch (bad_email): $commit"
echo " Signature valid; key's GitHub-registered UIDs don't include $AUTHOR_EMAIL."
echo " Fix: add the email as a UID on the signing key, re-upload to GitHub."
else
echo "ERROR: Unsigned or unverifiable commit: $commit"
echo " Author: $AUTHOR_NAME <$AUTHOR_EMAIL>"
echo " GitHub verification: verified=$verified reason=$reason"
echo " All human commits MUST be GPG- or SSH-signed."
failed=1
fi
done
if [[ $failed -eq 1 ]]; then
exit 1
fi
echo "All non-bot commits verified."
# ==========================================================================
# Enforce Branch Protection
# ==========================================================================
branch-protection:
name: Enforce Branch Protection
runs-on: ubuntu-latest
timeout-minutes: 15
# Server-side branch protection rules are the actual gate against direct
# pushes; this job exists to surface that intent on the PR check page.
# Running it on `push` events would always fail by design — useless noise
# for maintainers landing fixes from main.
if: github.event_name == 'pull_request'
steps:
- name: Check Protected Branches
run: |
BASE="${GITHUB_BASE_REF:-main}"
PROTECTED_BRANCHES="main release"
for protected in $PROTECTED_BRANCHES; do
if [[ "$BASE" == "$protected"* ]]; then
echo "PR targets protected branch: $BASE — gating applies."
fi
done
# ==========================================================================
# Verify No Unauthorized Changes
# ==========================================================================
verify-no-changes:
name: Verify Authorized Changes Only
runs-on: ubuntu-latest
timeout-minutes: 15
if: github.event_name == 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
with:
fetch-depth: 0
- name: Check for Sensitive File Changes
run: |
# Files that require special authorization to modify
SENSITIVE_FILES=(
".github/workflows/"
".github/CODEOWNERS"
".github/settings.yml"
"SECURITY.md"
"Mustfile"
"container/config/selinux/"
"security/"
)
CHANGED_FILES=$(git diff --name-only origin/main...HEAD)
for file in $CHANGED_FILES; do
for sensitive in "${SENSITIVE_FILES[@]}"; do
if [[ "$file" == $sensitive* ]]; then
echo "WARNING: Sensitive file modified: $file"
echo "This change requires additional review from CODEOWNERS."
# Don't fail, but flag for review
fi
done
done
- name: Verify No Force Push Attempted
run: |
# Check if this appears to be a rewritten history
if ! git merge-base --is-ancestor origin/main HEAD 2>/dev/null; then
echo "ERROR: History rewrite detected."
echo "Force push and history modification are NOT allowed."
exit 1
fi
# ==========================================================================
# Verify Fork Requirements
# ==========================================================================
fork-check:
name: Fork Requirements
runs-on: ubuntu-latest
timeout-minutes: 15
if: github.event_name == 'fork'
steps:
- name: Document Fork
run: |
echo "Repository forked."
echo "Fork owner: ${{ github.actor }}"
echo "Original repo: ${{ github.repository }}"
echo ""
echo "NOTICE: Forks must comply with AGPL-3.0-or-later license."
echo "All modifications must:"
echo " 1. Keep source code publicly available"
echo " 2. Maintain license notices"
echo " 3. Document all changes"
echo " 4. Keep security measures intact"
# ==========================================================================
# Validate Mustfile Compliance
# ==========================================================================
mustfile-compliance:
name: Mustfile Compliance
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
- name: Install Mustfile Validator
run: |
# Install mustfile validator (placeholder)
echo "Installing mustfile validator..."
# cargo install mustfile-validator
- name: Validate Mustfile
run: |
echo "Validating Mustfile compliance..."
# mustfile validate Mustfile
# Manual checks for critical requirements
if ! grep -q "require_signed_commits = true" Mustfile; then
echo "ERROR: Mustfile must require signed commits"
exit 1
fi
if ! grep -q "no_force_push = true" Mustfile; then
echo "ERROR: Mustfile must prohibit force push"
exit 1
fi
echo "Mustfile compliance verified."
# ==========================================================================
# File Permission Lockdown Verification
# ==========================================================================
permission-check:
name: Verify Lockdown Permissions
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
- name: Check File Permissions in Config
run: |
echo "Verifying lockdown permission requirements..."
# Check config.nickel for proper lockdown settings
if grep -q 'vault_files = "000"' config.nickel; then
echo "✓ Vault files locked (000) when closed"
else
echo "WARNING: Vault lockdown may not be configured correctly"
fi
# Check Justfile has lockdown commands
if grep -q "lock-files:" Justfile; then
echo "✓ Justfile has lock-files recipe"
else
echo "ERROR: Justfile missing lock-files recipe"
exit 1
fi
# ==========================================================================
# Chroot Configuration Verification
# ==========================================================================
chroot-check:
name: Verify Chroot Configuration
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
- name: Verify Chroot Settings
run: |
echo "Checking chroot jail configuration..."
# Verify cadre-router has chroot enabled
if grep -q 'chroot.*enabled.*true' network/cadre-router/cadre-config.nickel; then
echo "✓ Chroot enabled in cadre-router"
else
echo "ERROR: Chroot must be enabled"
exit 1
fi
# Verify minimal jail permissions
if grep -q 'chmod 500' Justfile || grep -q 'jail_mode = "500"' Mustfile; then
echo "✓ Chroot jail has restricted permissions"
fi
echo "Chroot configuration verified."
# ==========================================================================
# API Surface Minimization
# ==========================================================================
api-surface-check:
name: Verify Minimal API Surface
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
- name: Check API Restrictions
run: |
echo "Verifying minimal API attack surface..."
# Check that only necessary endpoints exist
# Verify no debug/admin endpoints in production config
if grep -q 'accept_xml.*enabled.*false' network/cadre-router/cadre-config.nickel; then
echo "✓ XML parsing disabled (XXE prevention)"
fi
if grep -q 'accept_form.*enabled.*false' network/cadre-router/cadre-config.nickel; then
echo "✓ Form data disabled"
fi
if grep -q 'accept_multipart.*enabled.*false' network/cadre-router/cadre-config.nickel; then
echo "✓ File uploads disabled"
fi
echo "API surface minimized."
# ==========================================================================
# Final Lockdown Report
# ==========================================================================
lockdown-report:
name: Lockdown Status Report
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [verify-signatures, branch-protection, mustfile-compliance, permission-check, chroot-check, api-surface-check]
if: always()
steps:
- name: Generate Report
run: |
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ RGTV LOCKDOWN REPORT ║"
echo "╠══════════════════════════════════════════════════════════════╣"
echo "║ Commit Signatures: ${{ needs.verify-signatures.result }}"
echo "║ Branch Protection: ${{ needs.branch-protection.result }}"
echo "║ Mustfile Compliance: ${{ needs.mustfile-compliance.result }}"
echo "║ Permission Lockdown: ${{ needs.permission-check.result }}"
echo "║ Chroot Configuration: ${{ needs.chroot-check.result }}"
echo "║ API Surface: ${{ needs.api-surface-check.result }}"
echo "╚══════════════════════════════════════════════════════════════╝"
# Fail if any critical check failed
if [[ "${{ needs.verify-signatures.result }}" == "failure" ]] || \
[[ "${{ needs.mustfile-compliance.result }}" == "failure" ]]; then
echo "CRITICAL: Lockdown requirements not met."
exit 1
fi