Add advanced attack scenarios #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================================= | |
| # SOC Lab Docker – CI: Stack Integration Test | |
| # ============================================================================= | |
| # | |
| # Runs on every push and PR to main. | |
| # Tests the full pipeline end-to-end on Linux (ubuntu-latest): | |
| # 1. Start the Docker Compose ELK stack | |
| # 2. Wait for Elasticsearch + Kibana to be healthy | |
| # 3. Confirm events flow from generator → Filebeat → Elasticsearch | |
| # 4. Run the brute force attack simulation | |
| # 5. Confirm simulation events are indexed | |
| # | |
| # Platform coverage: | |
| # Ubuntu: uses docker compose (V2 plugin, pre-installed on ubuntu-latest). | |
| # The standalone docker-compose V1 binary was removed from runners. | |
| # | |
| # macOS: Not tested in CI — QEMU/Colima setup on GitHub-hosted runners is | |
| # unreliable. Verified to work locally with Docker Desktop for Mac. | |
| # | |
| # Windows: Not tested in CI. Verified to work via Docker Desktop + WSL2. | |
| # ============================================================================= | |
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| integration-test: | |
| name: Integration test (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 35 | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest] | |
| steps: | |
| # ----------------------------------------------------------------------- | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| # ----------------------------------------------------------------------- | |
| - name: Prepare environment | |
| run: | | |
| cp .env.example .env | |
| mkdir -p data/logs | |
| # ----------------------------------------------------------------------- | |
| - name: Start lab stack | |
| run: docker compose up -d | |
| # ----------------------------------------------------------------------- | |
| - name: Wait for Elasticsearch to be healthy | |
| run: | | |
| echo "Polling Elasticsearch health (up to 5 min)..." | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:9200/_cluster/health 2>/dev/null \ | |
| | grep -qv '"status":"red"'; then | |
| echo "Elasticsearch is healthy" | |
| curl -s http://localhost:9200/_cluster/health | |
| break | |
| fi | |
| if [ "$i" -eq 30 ]; then | |
| echo "Elasticsearch failed to become healthy in time" | |
| docker compose logs --tail=40 elasticsearch | |
| exit 1 | |
| fi | |
| echo " ($i/30) not ready — retrying in 10s..." | |
| sleep 10 | |
| done | |
| # ----------------------------------------------------------------------- | |
| - name: Wait for Kibana to be available | |
| run: | | |
| echo "Polling Kibana status (up to 5 min)..." | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:5601/api/status 2>/dev/null \ | |
| | grep -q '"level":"available"'; then | |
| echo "Kibana is available" | |
| break | |
| fi | |
| if [ "$i" -eq 30 ]; then | |
| echo "Kibana failed to become available in time" | |
| docker compose logs --tail=40 kibana | |
| exit 1 | |
| fi | |
| echo " ($i/30) not ready — retrying in 10s..." | |
| sleep 10 | |
| done | |
| # ----------------------------------------------------------------------- | |
| - name: Wait for events to appear in Elasticsearch | |
| run: | | |
| echo "Waiting for generator → Filebeat → Elasticsearch pipeline..." | |
| for i in $(seq 1 18); do | |
| count=$(curl -sf "http://localhost:9200/soc-lab-*/_count" 2>/dev/null \ | |
| | python3 -c "import sys,json; print(json.load(sys.stdin).get('count',0))" \ | |
| 2>/dev/null || echo 0) | |
| if [ "$count" -gt 0 ]; then | |
| echo "Events confirmed in Elasticsearch: $count" | |
| break | |
| fi | |
| if [ "$i" -eq 18 ]; then | |
| echo "No events indexed after timeout" | |
| docker compose logs --tail=30 log-generator | |
| docker compose logs --tail=30 filebeat | |
| exit 1 | |
| fi | |
| echo " ($i/18) count=$count — retrying in 10s..." | |
| sleep 10 | |
| done | |
| # ----------------------------------------------------------------------- | |
| - name: Run brute force simulation | |
| run: bash scripts/brute_force_simulation.sh | |
| # ----------------------------------------------------------------------- | |
| - name: Verify simulation events are indexed | |
| run: | | |
| echo "Waiting 15s for simulation events to be indexed..." | |
| sleep 15 | |
| count=$(curl -sf "http://localhost:9200/soc-lab-*/_search" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"query":{"term":{"labels.simulation":"attack"}},"track_total_hits":true}' \ | |
| 2>/dev/null \ | |
| | python3 -c \ | |
| "import sys,json; d=json.load(sys.stdin); print(d['hits']['total']['value'])" \ | |
| 2>/dev/null || echo 0) | |
| echo "Simulation events found: $count" | |
| if [ "$count" -eq 0 ]; then | |
| echo "ERROR: No simulation events found in index" | |
| exit 1 | |
| fi | |
| echo "Simulation events confirmed" | |
| # ----------------------------------------------------------------------- | |
| - name: Dump all service logs on failure | |
| if: failure() | |
| run: | | |
| echo "===== docker compose ps =====" | |
| docker compose ps | |
| echo "===== elasticsearch (last 50 lines) =====" | |
| docker compose logs --tail=50 elasticsearch | |
| echo "===== log-generator (last 50 lines) =====" | |
| docker compose logs --tail=50 log-generator | |
| echo "===== filebeat (last 50 lines) =====" | |
| docker compose logs --tail=50 filebeat | |
| echo "===== kibana (last 50 lines) =====" | |
| docker compose logs --tail=50 kibana | |
| # ----------------------------------------------------------------------- | |
| - name: Teardown | |
| if: always() | |
| run: docker compose down -v |