Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 9 additions & 3 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ jobs:
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
targets: x86_64-unknown-linux-musl

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2

- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross

- name: Generate Secret Key
run: head -c16 /dev/urandom > src/secret.key

Expand All @@ -36,8 +40,10 @@ jobs:
- name: Test
run: cargo test

- name: Build release
run: cargo build --release
- name: Build static release
env:
CARGO_TARGET_DIR: target-cross
run: cross build --release --target x86_64-unknown-linux-musl

- name: Build frontend
working-directory: ./web
Expand All @@ -52,7 +58,7 @@ jobs:
- name: Package app
run: |
mkdir -p app/stackdog/dist
cp target/release/stackdog app/stackdog/
cp target-cross/x86_64-unknown-linux-musl/release/stackdog app/stackdog/
cp -a web/dist/. app/stackdog/
cp docker/prod/Dockerfile app/Dockerfile
touch app/.env
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "stackdog"
version = "0.2.0"
version = "0.2.1"
authors = ["Vasili Pascal <info@try.direct>"]
edition = "2021"
description = "Security platform for Docker containers and Linux servers"
Expand Down Expand Up @@ -55,6 +55,7 @@ zstd = "0.13"

# Stream utilities
futures-util = "0.3"
lettre = { version = "0.11", default-features = false, features = ["tokio1", "tokio1-rustls-tls", "builder", "smtp-transport"] }

# eBPF (Linux only)
[target.'cfg(target_os = "linux")'.dependencies]
Expand All @@ -78,6 +79,8 @@ ebpf = []
# Testing
tokio-test = "0.4"
tempfile = "3"
actix-test = "0.1"
awc = "3"

# Benchmarking
criterion = { version = "0.5", features = ["html_reports"] }
Expand Down
103 changes: 98 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Stackdog Security

![Version](https://img.shields.io/badge/version-0.2.0-blue.svg)
![Version](https://img.shields.io/badge/version-0.2.1-blue.svg)
![License](https://img.shields.io/badge/license-MIT-green.svg)
![Rust](https://img.shields.io/badge/rust-1.75+-orange.svg)
![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey.svg)
Expand Down Expand Up @@ -71,6 +71,95 @@ cargo run
cargo run -- serve
```

### Run with Docker

Use the published container image for the quickest way to explore the API.
If you are validating a fresh branch or waiting for Docker Hub to pick up the latest CI build,
prefer the local-image flow below so you know you are running your current checkout:

```bash
docker volume create stackdog-data

docker run --rm -it \
--name stackdog \
-p 5000:5000 \
-e APP_HOST=0.0.0.0 \
-e APP_PORT=5000 \
-e DATABASE_URL=/data/stackdog.db \
-v stackdog-data:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
trydirect/stackdog:latest
```

Then open another shell and hit the API:

```bash
curl http://localhost:5000/api/security/status
curl http://localhost:5000/api/threats
curl http://localhost:5000/api/alerts
```

Mount the Docker socket when you want Docker-aware features such as container listing, live stats,
mail abuse guard polling, Docker log discovery, and Docker-backed quarantine/release flows.

If you do not want Stackdog to access the Docker daemon, disable the mail guard:

```bash
STACKDOG_MAIL_GUARD_ENABLED=false
```

To try log sniffing inside Docker against host log files, mount them read-only and run the
`sniff` subcommand instead of the default HTTP server:

```bash
docker run --rm -it \
-e DATABASE_URL=/tmp/stackdog.db \
-v /var/log:/host-logs:ro \
trydirect/stackdog:latest \
sniff --once --sources /host-logs/auth.log
```

If you want to test your current checkout instead of the latest published image:

```bash
docker build -f docker/backend/Dockerfile -t stackdog-local .

docker run --rm -it \
--name stackdog-local \
-p 5000:5000 \
-e APP_HOST=0.0.0.0 \
-e APP_PORT=5000 \
-e DATABASE_URL=/data/stackdog.db \
-v stackdog-data:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
stackdog-local
```

### Run backend + UI with Docker Compose

To run `stackdog serve` and the web UI as two separate services from your current checkout:

```bash
docker compose -f docker-compose.app.yml up --build
```

This starts:

- **API** at `http://localhost:5000`
- **UI** at `http://localhost:3000`

The compose stack uses:

- `stackdog` service — builds `docker/backend/Dockerfile`, runs `stackdog serve`, and mounts `/var/run/docker.sock`
- `stackdog-ui` service — builds the React app and serves it with Nginx
- `stackdog-data` volume — persists the SQLite database between restarts

To stop it:

```bash
docker compose -f docker-compose.app.yml down
```

### Log Sniffing

```bash
Expand Down Expand Up @@ -120,11 +209,15 @@ for event in events {
### Docker Development

```bash
# Start development environment
docker-compose up -d
# Run the published image
docker run --rm -it -p 5000:5000 trydirect/stackdog:latest

# Or, for the most reliable test of your current code, build and run your checkout
docker build -f docker/backend/Dockerfile -t stackdog-local .
docker run --rm -it -p 5000:5000 stackdog-local

# View logs
docker-compose logs -f stackdog
# Or run backend + UI together
docker compose -f docker-compose.app.yml up --build
```

---
Expand Down
2 changes: 1 addition & 1 deletion VERSION.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.0
0.2.1
32 changes: 32 additions & 0 deletions docker-compose.app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
services:
stackdog:
build:
context: .
dockerfile: docker/backend/Dockerfile
command: ["serve"]
container_name: stackdog
environment:
APP_HOST: 0.0.0.0
APP_PORT: 5000
DATABASE_URL: /data/stackdog.db
ports:
- "5000:5000"
volumes:
- stackdog-data:/data
- /var/run/docker.sock:/var/run/docker.sock

stackdog-ui:
build:
context: .
dockerfile: docker/ui/Dockerfile
args:
REACT_APP_API_URL: http://localhost:5000/api
REACT_APP_WS_URL: ws://localhost:5000/ws
container_name: stackdog-ui
depends_on:
- stackdog
ports:
- "3000:80"

volumes:
stackdog-data:
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ services:
echo "Starting Stackdog..."
cargo run --bin stackdog
ports:
- "${APP_PORT:-8080}:${APP_PORT:-8080}"
- "${APP_PORT:-5000}:${APP_PORT:-5000}"
env_file:
- .env
environment:
Expand Down
31 changes: 31 additions & 0 deletions docker/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM rust:slim-bookworm AS build

RUN apt-get update && \
apt-get install --no-install-recommends -y musl-tools pkg-config && \
rm -rf /var/lib/apt/lists/*

RUN rustup target add x86_64-unknown-linux-musl

WORKDIR /app

COPY Cargo.toml Cargo.lock ./
COPY migrations ./migrations
COPY src ./src
COPY .env.sample ./.env

RUN cargo build --release --target x86_64-unknown-linux-musl

FROM debian:bookworm-slim

WORKDIR /app

RUN apt-get update && \
apt-get install --no-install-recommends -y ca-certificates sqlite3 && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /data

COPY --from=build /app/target/x86_64-unknown-linux-musl/release/stackdog /app/stackdog

EXPOSE 5000

ENTRYPOINT ["/app/stackdog"]
27 changes: 27 additions & 0 deletions docker/ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
FROM node:20-alpine AS build

WORKDIR /web

COPY web/package*.json ./
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi

COPY web/ ./

ARG REACT_APP_API_URL=
ARG REACT_APP_WS_URL=
ARG APP_PORT=
ARG REACT_APP_API_PORT=

ENV REACT_APP_API_URL=${REACT_APP_API_URL}
ENV REACT_APP_WS_URL=${REACT_APP_WS_URL}
ENV APP_PORT=${APP_PORT}
ENV REACT_APP_API_PORT=${REACT_APP_API_PORT}

RUN npm run build

FROM nginx:1.27-alpine

COPY docker/ui/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /web/dist /usr/share/nginx/html

EXPOSE 80
11 changes: 11 additions & 0 deletions docker/ui/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
server {
listen 80;
server_name _;

root /usr/share/nginx/html;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}
}
4 changes: 3 additions & 1 deletion ebpf/.cargo/config → ebpf/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
target = ["bpfel-unknown-none"]

[target.bpfel-unknown-none]
rustflags = ["-C", "link-arg=--Bstatic"]

[unstable]
build-std = ["core"]
3 changes: 3 additions & 0 deletions ebpf/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "nightly"
components = ["rust-src"]
9 changes: 7 additions & 2 deletions ebpf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@
#![no_main]
#![no_std]

#[no_mangle]
pub fn main() {}
mod maps;
mod syscalls;

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo<'_>) -> ! {
loop {}
}
Loading
Loading