Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
37d2158
Add fail-closed check for public SSH and Kubernetes API ingress
EdwinJdevops Nov 7, 2026
9396b56
Remove unrestricted SSH and Kubernetes API ingress
EdwinJdevops Nov 7, 2026
2a2e6a2
Make YAML, directory and critical security checks fail closed
EdwinJdevops Nov 7, 2026
5aadb01
Document deployment stop conditions and low-cost architecture
EdwinJdevops Nov 7, 2026
a6c0e23
Default cloud instance to disabled and remove broken bootstrap reference
EdwinJdevops Nov 7, 2026
58fa0be
Add explicit fail-closed cloud deployment lock
EdwinJdevops Nov 7, 2026
bca85bc
Remove misleading SSH instructions and handle disabled instance
EdwinJdevops Nov 7, 2026
feb33c1
Disable automated GitOps synchronization and pruning
EdwinJdevops Nov 7, 2026
a5609c2
Replace unsupported production claims with verified experimental status
EdwinJdevops Nov 7, 2026
3fdc3be
Detect public ingress port ranges and unresolved literal rules
EdwinJdevops Nov 7, 2026
8a563a9
Test ingress guard against open ports and range bypasses
EdwinJdevops Nov 7, 2026
53ab8f1
Run ingress safety regression tests in CI
EdwinJdevops Nov 7, 2026
f2bfb4b
Add real Terraform init and validate gate to CI
EdwinJdevops Nov 7, 2026
6d60ba1
Require Terraform formatting before validation
EdwinJdevops Nov 7, 2026
5e44118
Add static regression guard for cloud provisioning safety lock
EdwinJdevops Nov 7, 2026
ae1f134
Enforce cloud deployment safety lock in CI
EdwinJdevops Nov 7, 2026
a97798b
Align Terraform EC2 arguments to satisfy fmt gate
EdwinJdevops Nov 7, 2026
b260163
Apply exact terraform fmt diff from failed CI job logs
EdwinJdevops Nov 7, 2026
3cf2a37
Enforce zero-change Terraform baseline and add disposable Kubernetes …
EdwinJdevops Nov 12, 2026
1f5cb64
Remove orphaned root-level AWS inputs flagged by CI
EdwinJdevops Nov 12, 2026
628a382
Benchmark the cloud-disabled Terraform policy with a repeatable regre…
EdwinJdevops Nov 12, 2026
726a281
Record public demand evidence and merge verification limits
EdwinJdevops Nov 12, 2026
209439a
Test Terraform resource regression inside an approved source file
EdwinJdevops Nov 12, 2026
093fd78
Test two upstream IAM policy renderers with network-isolated plans
EdwinJdevops Nov 13, 2026
0e81008
Exercise native offline IAM checks from pinned upstream module roots
EdwinJdevops Nov 13, 2026
02efecf
Use the upstream EKS release ref for acceptance checkout
EdwinJdevops Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 79 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,43 +1,105 @@
name: Archnet CI/CD
name: Archnet validation

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false

- name: Validate YAML files
- name: Validate YAML syntax
shell: bash
run: |
find . -name "*.yml" -o -name "*.yaml" | head -20
echo "YAML structure validated"
set -euo pipefail
python -m pip install --disable-pip-version-check PyYAML==6.0.2
python - <<'PY'
from pathlib import Path
import yaml
paths = sorted([*Path('.').rglob('*.yaml'), *Path('.').rglob('*.yml')])
if not paths:
raise SystemExit('No YAML files found')
for path in paths:
with path.open(encoding='utf-8') as stream:
list(yaml.safe_load_all(stream))
print('Parsed:', path)
PY

- name: Validate Shell Scripts
- name: Validate shell syntax
shell: bash
run: |
find . -name "*.sh" -exec bash -n {} \;
echo "Shell scripts OK"
set -euo pipefail
while IFS= read -r -d '' file; do
bash -n "$file"
done < <(find . -type f -name '*.sh' -print0)

- name: Check Project Structure
- name: Validate required directories
shell: bash
run: |
ls -la
echo "terraform/ argocd/ observability/ apps/ docs/ k3s/" | tr ' ' '\n' | while read dir; do
[ -d "$dir" ] && echo "OK: $dir exists" || echo "MISSING: $dir"
set -euo pipefail
for dir in terraform argocd observability apps docs k3s; do
test -d "$dir" || { echo "Missing: $dir" >&2; exit 1; }
done

security:
- name: Test infrastructure safety guard
run: python -m unittest discover -s tests -v

- name: Verify cloud deployment safety lock
run: python scripts/check_cloud_lock.py

terraform:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: 1.12.2
terraform_wrapper: false
- name: Benchmark source policy without Terraform execution
run: python scripts/benchmark_cloud_lock.py
- name: Verify empty Terraform plan and deny enablement
run: bash scripts/verify_cloud_plan.sh

kubernetes:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Install pinned kind CLI
run: |
GOBIN="$RUNNER_TEMP/bin" go install sigs.k8s.io/kind@v0.30.0
echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH"
- name: Verify disposable Kubernetes control plane
run: bash scripts/smoke_kind.sh

- name: Run Trivy
uses: aquasecurity/trivy-action@master
security:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Scan filesystem with Trivy
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
cache: 'false'
version: v0.69.3
scan-type: fs
scan-ref: .
severity: CRITICAL
exit-code: 0
exit-code: '1'
98 changes: 98 additions & 0 deletions .github/workflows/full-module-iam.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Full module IAM acceptance
on: pull_request
permissions:
contents: read
jobs:
native:
runs-on: ubuntu-latest
timeout-minutes: 12
strategy:
fail-fast: false
matrix:
include:
- repository: terraform-aws-modules/terraform-aws-eks
ref: v20.37.2
policy: assume_role_policy
principal: Service
expected: eks.amazonaws.com
- repository: cloudposse/terraform-aws-eks-iam-role
ref: 0c2347ca06fb54579a0e57d03e20aed3c1045157
policy: service_account_assume_role
principal: Federated
expected: arn:aws:iam::111122223333:oidc-provider/example.invalid
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
repository: ${{ matrix.repository }}
ref: ${{ matrix.ref }}
persist-credentials: false
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd
with:
terraform_version: 1.12.2
terraform_wrapper: false
- name: Test actual policy from upstream root
shell: bash
env:
POLICY: ${{ matrix.policy }}
PRINCIPAL: ${{ matrix.principal }}
EXPECTED: ${{ matrix.expected }}
CHECKPOINT_DISABLE: '1'
run: |
set -euo pipefail
mkdir archnet-tests
cat > archnet-tests/policy.tftest.hcl <<EOF
provider "aws" {
region = "us-east-1"
access_key = "offline-fixture"
secret_key = "offline-fixture"
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_region_validation = true
}

run "policy" {
command = plan
plan_options {
refresh = false
target = [data.aws_iam_policy_document.$POLICY]
}
assert {
condition = jsondecode(data.aws_iam_policy_document.$POLICY[0].json).Statement[0].Principal.$PRINCIPAL == "$EXPECTED"
error_message = "ARCHNET_PRINCIPAL_MISMATCH"
}
}
EOF
if [[ "$PRINCIPAL" == Federated ]]; then
cat >> archnet-tests/policy.tftest.hcl <<'EOF'

variables {
eks_cluster_oidc_issuer_url = "https://example.invalid"
service_account_namespace = "default"
service_account_name = "example"
}

override_data {
target = data.aws_caller_identity.current[0]
override_during = plan
values = { account_id = "111122223333" }
}
EOF
fi
terraform init -backend=false -input=false
tf=$(command -v terraform)
run_offline() {
sudo unshare --net -- env -i PATH=/usr/bin:/bin CHECKPOINT_DISABLE=1 AWS_EC2_METADATA_DISABLED=true "$tf" test -test-directory=archnet-tests -no-color
}
run_offline
if [[ "$PRINCIPAL" == Service ]]; then
sed -i '0,/identifiers = \["eks.amazonaws.com"\]/{s/identifiers = \["eks.amazonaws.com"\]/identifiers = ["ec2.amazonaws.com"]/;}' main.tf
else
sed -i 's/account_id = "111122223333"/account_id = "444455556666"/' archnet-tests/policy.tftest.hcl
fi
if run_offline > "$RUNNER_TEMP/negative.log" 2>&1; then
echo "Changed principal passed unexpectedly" >&2
exit 1
fi
cat "$RUNNER_TEMP/negative.log"
grep -Fx 'ARCHNET_PRINCIPAL_MISMATCH' "$RUNNER_TEMP/negative.log"
20 changes: 20 additions & 0 deletions .github/workflows/iam-experiment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Offline IAM policy experiment
on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
offline-policy:
runs-on: ubuntu-latest
timeout-minutes: 8
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd
with:
terraform_version: 1.12.2
terraform_wrapper: false
- name: Run extracted renderers without network access
run: bash scripts/test_offline_iam.sh
90 changes: 6 additions & 84 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,9 @@
# Archnet: Zero-Trust Internal Developer Platform
# ARCHNET

![CI/CD](https://github.com/EdwinJdevops/ARCHNET/actions/workflows/ci.yml/badge.svg)
![License](https://img.shields.io/badge/license-MIT-blue)
![Stack](https://img.shields.io/badge/stack-k3s%20%7C%20ArgoCD%20%7C%20Prometheus-brightgreen)
Experimental repository for reviewing a cloud-disabled infrastructure baseline in pull requests. CI checks whether the Terraform root proposes zero changes without cloud credentials and rejects an attempt to enable provisioning. A disposable kind cluster checks local Kubernetes API readiness and object reconciliation prerequisites.

> A production-grade Internal Developer Platform built by a self-taught DevOps Engineer from Nigeria.
- [Reproduce the cloud-disabled Terraform benchmark](docs/CLOUD_LOCK_BENCHMARK.md)
- [Architecture, demand evidence and alternatives](docs/ENGINEERING_AUDIT.md)
- [Cost boundary and local Kubernetes instructions](docs/DEPLOYMENT_SAFETY.md)

## Problem It Solves
Most teams deploy to Kubernetes manually — no automation, no observability, no drift detection. Archnet automates the entire platform lifecycle.

## Architecture
Developer → Git Push
→ GitHub Actions (CI)
→ Docker Registry
→ ArgoCD (GitOps)
→ k3s Cluster
→ Prometheus + Grafana
→ AlertManager → Slack

## Tech Stack
| Tool | Purpose |
|------|---------|
| k3s | Lightweight production Kubernetes |
| ArgoCD | GitOps deployments + drift detection |
| Sealed-Secrets | Zero-trust secret management |
| Prometheus | Metrics collection |
| Grafana | Dashboards + alerting |
| Loki | Log aggregation |
| GitHub Actions | CI/CD pipeline |
| Terraform | AWS infrastructure as code |

## Key Features
- Self-healing deployments (ArgoCD drift detection)
- Secrets encrypted at rest in Git
- Full observability stack (metrics + logs + alerts)
- RBAC audit logging
- Zero-trust network policies
- Single-command infrastructure provisioning

## Quick Start
```bash
# 1. Clone
git clone https://github.com/EdwinJdevops/ARCHNET
cd ARCHNET

# 2. Provision AWS infrastructure
cd terraform
terraform init
terraform plan
terraform apply

# 3. Access ArgoCD
kubectl port-forward svc/argocd-server -n argocd 8080:443

# 4. Access Grafana
kubectl port-forward svc/grafana -n monitoring 3000:3000
```

## Project Structure
archnet/
├── .github/workflows/ # CI/CD pipeline
├── terraform/ # AWS infrastructure
├── k3s/ # Cluster setup
├── argocd/ # GitOps manifests
├── observability/ # Prometheus, Grafana, Loki
├── apps/ # Application deployments
└── docs/ # Architecture + decisions

## Security Model
- Default deny network policies
- RBAC least-privilege access
- Sealed-Secrets (decrypt only inside cluster)
- Trivy image scanning pre-deploy
- No secrets in environment variables

## Documentation
- [Architecture](docs/ARCHITECTURE.md)
- [Tech Decisions](docs/TECH_DECISIONS.md)
- [Security](docs/SECURITY.md)

## Author
**Edwin** | DevOps & Cloud Engineer | Nigeria
[LinkedIn](https://linkedin.com/in/YOUR_LINKEDIN) · [Hashnode](https://YOUR_HASHNODE.hashnode.dev)

---
> Built to solve real infrastructure problems. Not a tutorial project.
The Argo CD Application is reference configuration. The Kubernetes smoke test does not deploy it, validate application workloads or demonstrate production readiness.
11 changes: 5 additions & 6 deletions argocd/application.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Reference only: not applied by default.
# Replace the repository and revision with a reviewed, immutable release before use.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
Expand All @@ -6,15 +8,12 @@ metadata:
spec:
project: default
source:
repoURL: https://github.com/YOUR_USERNAME/archnet.git
targetRevision: HEAD
repoURL: https://github.com/EdwinJdevops/ARCHNET.git
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- CreateNamespace=false
22 changes: 22 additions & 0 deletions docs/CLOUD_LOCK_BENCHMARK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Cloud-disabled Terraform benchmark

Run from the repository root on Linux or WSL with Python 3, Bash, Terraform 1.12.2 and jq.

```sh
python scripts/benchmark_cloud_lock.py
bash scripts/verify_cloud_plan.sh
```

Use a fresh checkout without existing Terraform state. These are the same commands CI runs. The source benchmark copies the checked-in root and policy into a temporary directory and uses the same provider-free `terraform_data` fixture in two isolated regression cases. It first adds an unexpected file, then removes that file and appends the declaration to the approved `main.tf`. It never invokes Terraform and leaves the checkout untouched. The real plan script checks the live source inventory before initializing Terraform.

| Case | Expected evidence |
| --- | --- |
| Clean source | Source guard exits 0. |
| Unexpected declaration | Source guard exits 1 and names the added file. |
| Declaration appended to approved file | Source guard exits 1 and identifies the modified `terraform/main.tf`. |
| Disabled Terraform root | Terraform exits 0 with zero changes, and the plan JSON policy passes. |
| Explicit enablement | Terraform rejects the input with the cloud-disabled diagnostic. |

The job fails if any decision differs. Logs report elapsed time for each source check and each Terraform plan command. Source timings include Python process startup. Timings describe that run and exclude queue time and setup. View the Terraform job in the [workflow history](https://github.com/EdwinJdevops/ARCHNET/actions/workflows/ci.yml) for the commit under review. Kubernetes smoke results appear in a separate job.

This benchmark verifies the disabled-root contract and two representative source regressions. It provides no evidence about adoption, production operation or the performance of a live AWS plan.
Loading
Loading