first commit
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# SQLite database files
|
||||||
|
/data/*
|
||||||
|
# TLS certificates (private keys!)
|
||||||
|
/certs/*
|
||||||
|
|
||||||
|
# Docker volumes
|
||||||
|
caddy-data/
|
||||||
|
caddy-config/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
/build/
|
||||||
|
/dist/
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
.cache/
|
||||||
30
Caddyfile
Normal file
30
Caddyfile
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
auto_https off
|
||||||
|
}
|
||||||
|
|
||||||
|
:80 {
|
||||||
|
redir https://{host}{uri}
|
||||||
|
}
|
||||||
|
|
||||||
|
attestation.app:443 {
|
||||||
|
tls /etc/caddy/certs/attestation.app.crt /etc/caddy/certs/attestation.app.key
|
||||||
|
|
||||||
|
# Disable HSTS
|
||||||
|
header Strict-Transport-Security ""
|
||||||
|
|
||||||
|
# Serve static files (HTML, CSS, images, etc.)
|
||||||
|
root * /srv/static
|
||||||
|
file_server
|
||||||
|
|
||||||
|
# Proxy API endpoints to the attestation server
|
||||||
|
reverse_proxy /api/* attestation:8080
|
||||||
|
|
||||||
|
# Proxy Auditor app endpoints
|
||||||
|
reverse_proxy /auditor/* attestation:8080
|
||||||
|
|
||||||
|
# Enable logging
|
||||||
|
log {
|
||||||
|
output stdout
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Dockerfile
Normal file
63
Dockerfile
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
FROM eclipse-temurin:21-jdk-jammy AS builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN git clone --depth 1 --recurse-submodules https://github.com/GrapheneOS/AttestationServer.git .
|
||||||
|
|
||||||
|
# Patch the server to bind to 0.0.0.0 instead of localhost (::1)
|
||||||
|
# This is required for Docker networking to work
|
||||||
|
RUN sed -i 's/new InetSocketAddress("::1", 8080)/new InetSocketAddress("0.0.0.0", 8080)/' \
|
||||||
|
src/main/java/app/attestation/server/AttestationServer.java
|
||||||
|
|
||||||
|
# Optional: Patch the domain if you want to use a custom domain
|
||||||
|
# Uncomment and modify the following line for your domain:
|
||||||
|
# RUN sed -i 's/attestation.app/your-domain.com/g' \
|
||||||
|
# src/main/java/app/attestation/server/AttestationServer.java
|
||||||
|
|
||||||
|
RUN chmod +x gradlew && ./gradlew build -x test --no-daemon
|
||||||
|
|
||||||
|
# Process static files (replace {{css|...}} and {{js|...}} templates with SRI hashes)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
openssl sed \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY process-static-docker.sh /tmp/process-static.sh
|
||||||
|
RUN chmod +x /tmp/process-static.sh && /tmp/process-static.sh
|
||||||
|
|
||||||
|
# --- Runtime ---
|
||||||
|
FROM eclipse-temurin:21-jre-jammy
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN useradd -r -s /bin/false -u 1000 attestation
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the custom sqlite4java native library from the submodule
|
||||||
|
# This is built with newer SQLite that supports STRICT tables
|
||||||
|
RUN mkdir -p /app/libs
|
||||||
|
COPY --from=builder /build/libs/sqlite4java-prebuilt/libsqlite4java-linux-amd64-1.0.392.so /app/libs/
|
||||||
|
|
||||||
|
# Copy all JARs from builder
|
||||||
|
COPY --from=builder /build/build/libs/*.jar ./libs/
|
||||||
|
|
||||||
|
# Copy processed static files
|
||||||
|
COPY --from=builder /build/static ./static-orig/
|
||||||
|
|
||||||
|
COPY entrypoint.sh .
|
||||||
|
RUN chmod +x /app/entrypoint.sh
|
||||||
|
|
||||||
|
# Create directories and set permissions
|
||||||
|
# /data - for SQLite databases
|
||||||
|
# /srv/static - for sharing static files with caddy
|
||||||
|
RUN mkdir -p /data /srv/static && \
|
||||||
|
chown -R attestation:attestation /app /data /srv/static
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Run as root initially to fix permissions, entrypoint will drop privileges
|
||||||
|
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||||
148
README.md
Normal file
148
README.md
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# GrapheneOS AttestationServer Docker
|
||||||
|
|
||||||
|
Dockerized deployment of [GrapheneOS AttestationServer](https://github.com/GrapheneOS/AttestationServer) for local attestation.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This project provides a containerized setup for running your own GrapheneOS AttestationServer. It includes:
|
||||||
|
|
||||||
|
- **AttestationServer** - The main Java application handling attestations
|
||||||
|
- **Caddy** - Reverse proxy with HTTPS support
|
||||||
|
- **SQLite** - Local database storage for attestation data
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before running the server, ensure you have:
|
||||||
|
|
||||||
|
1. **Docker** and **Docker Compose** installed
|
||||||
|
2. **TLS certificates** for `attestation.app` domain in the `certs/` directory:
|
||||||
|
- `certs/attestation.app.crt` - Certificate file
|
||||||
|
- `certs/attestation.app.key` - Private key file
|
||||||
|
3. **DNS or hosts file configuration** to resolve `attestation.app` to your server
|
||||||
|
|
||||||
|
## Pre-Launch Setup
|
||||||
|
|
||||||
|
### 1. Prepare TLS Certificates
|
||||||
|
|
||||||
|
Place your TLS certificates for `attestation.app` in the `certs/` directory:
|
||||||
|
- `certs/attestation.app.crt` - Certificate
|
||||||
|
- `certs/attestation.app.key` - Private key
|
||||||
|
|
||||||
|
> **Note:** Certificate generation is up to you. You can use self-signed certificates for local testing or certificates from a trusted CA.
|
||||||
|
|
||||||
|
### 2. Configure DNS or Hosts File
|
||||||
|
|
||||||
|
The GrapheneOS Auditor app expects to connect to `attestation.app`. You must redirect this domain to your local server's IP address.
|
||||||
|
|
||||||
|
#### Option A: Local Machine (hosts file)
|
||||||
|
|
||||||
|
Edit your hosts file:
|
||||||
|
|
||||||
|
**Linux/macOS:**
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
```
|
||||||
|
C:\Windows\System32\drivers\etc\hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the following line (replace `192.168.1.100` with your server's IP):
|
||||||
|
```
|
||||||
|
192.168.1.100 attestation.app
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option B: Network-wide (DNS)
|
||||||
|
|
||||||
|
Configure your router or local DNS server (like Pi-hole or AdGuard) to resolve `attestation.app` to your server's IP address.
|
||||||
|
|
||||||
|
#### Option C: Android Device (root required)
|
||||||
|
|
||||||
|
If your Android device is rooted, edit `/system/etc/hosts`:
|
||||||
|
```bash
|
||||||
|
su
|
||||||
|
mount -o remount,rw /system
|
||||||
|
echo "192.168.1.100 attestation.app" >> /system/etc/hosts
|
||||||
|
mount -o remount,ro /system
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** You must configure this on the Android device running the Auditor app, not just the server.
|
||||||
|
|
||||||
|
### 3. Create Data Directory
|
||||||
|
|
||||||
|
Ensure the data directory exists for persistent SQLite storage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Server
|
||||||
|
|
||||||
|
### Build and Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. Build the AttestationServer from source
|
||||||
|
2. Start the attestation service on port 8080 (internal)
|
||||||
|
3. Start Caddy reverse proxy on ports 80 and 443
|
||||||
|
|
||||||
|
### Check Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose ps
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stop the Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stop and Remove All Data
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose down -v
|
||||||
|
rm -rf data/*.db data/*.db-*
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Ensure your Android device has `attestation.app` pointing to your server IP
|
||||||
|
2. Install [GrapheneOS Auditor](https://github.com/GrapheneOS/Auditor) app on your Android device
|
||||||
|
3. Open the Auditor app
|
||||||
|
4. The app will connect to your local attestation server instead of the official one
|
||||||
|
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── certs/ # TLS certificates
|
||||||
|
│ ├── attestation.app.crt
|
||||||
|
│ └── attestation.app.key
|
||||||
|
├── data/ # SQLite databases (persistent)
|
||||||
|
│ ├── attestation.db
|
||||||
|
│ └── samples.db
|
||||||
|
├── Caddyfile # Caddy reverse proxy config
|
||||||
|
├── Dockerfile # AttestationServer build
|
||||||
|
├── docker-compose.yml # Service orchestration
|
||||||
|
├── entrypoint.sh # Container entrypoint
|
||||||
|
└── process-static-docker.sh # Static file processor
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- Keep your private key (`certs/attestation.app.key`) secure and never commit it to version control
|
||||||
|
- The `.gitignore` file excludes sensitive files like certificates and databases
|
||||||
|
- This setup is intended for **local/private use only**
|
||||||
|
- For production deployment, use properly signed certificates from a trusted CA
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This Docker setup follows the same license as the upstream [GrapheneOS AttestationServer](https://github.com/GrapheneOS/AttestationServer).
|
||||||
44
docker-compose.yml
Normal file
44
docker-compose.yml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
attestation:
|
||||||
|
build: .
|
||||||
|
container_name: attestation-server
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
# Persist SQLite databases (attestation.db and samples.db)
|
||||||
|
- ./data:/data
|
||||||
|
# Share static files with caddy (attestation copies, caddy serves)
|
||||||
|
- static-files:/srv/static
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
expose:
|
||||||
|
- "8080"
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: caddy:2-alpine
|
||||||
|
container_name: attestation-caddy
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
- "443:443/udp"
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
|
- ./certs:/etc/caddy/certs:ro
|
||||||
|
- caddy-data:/data
|
||||||
|
- caddy-config:/config
|
||||||
|
# Mount static files from shared volume
|
||||||
|
- static-files:/srv/static:ro
|
||||||
|
depends_on:
|
||||||
|
- attestation
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy-data:
|
||||||
|
caddy-config:
|
||||||
|
static-files:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
internal:
|
||||||
45
entrypoint.sh
Normal file
45
entrypoint.sh
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Fix permissions on /data directory (runtime volume mount may have wrong ownership)
|
||||||
|
# This ensures the attestation user can write SQLite databases
|
||||||
|
if [ -d "/data" ]; then
|
||||||
|
chown -R attestation:attestation /data
|
||||||
|
chmod 755 /data
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy static files to shared volume (if mounted)
|
||||||
|
if [ -d "/srv/static" ]; then
|
||||||
|
echo "Copying static files to shared volume..."
|
||||||
|
cp -r /app/static-orig/* /srv/static/
|
||||||
|
chown -R attestation:attestation /srv/static
|
||||||
|
echo "Static files copied."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set working directory to /data where SQLite databases will be stored
|
||||||
|
# The application creates attestation.db and samples.db in the working directory
|
||||||
|
cd /data
|
||||||
|
|
||||||
|
JAR_FILE="/app/libs/attestation-server.jar"
|
||||||
|
|
||||||
|
if [ ! -f "$JAR_FILE" ]; then
|
||||||
|
echo "ERROR: Main JAR not found at $JAR_FILE!"
|
||||||
|
echo "Available JARs:"
|
||||||
|
ls -la /app/libs/
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting AttestationServer..."
|
||||||
|
echo "JAR: $JAR_FILE"
|
||||||
|
echo "Data directory: $(pwd)"
|
||||||
|
echo "Running as user: attestation (UID 1000)"
|
||||||
|
|
||||||
|
# Run with the sqlite4java library path, dropping privileges to attestation user
|
||||||
|
# Use setpriv to drop privileges while preserving environment
|
||||||
|
exec setpriv --reuid=attestation --regid=attestation --clear-groups \
|
||||||
|
java \
|
||||||
|
-Xmx512m \
|
||||||
|
-XX:+UseG1GC \
|
||||||
|
-XX:+ExitOnOutOfMemoryError \
|
||||||
|
-Djava.library.path=/app/libs \
|
||||||
|
-jar "$JAR_FILE"
|
||||||
35
process-static-docker.sh
Normal file
35
process-static-docker.sh
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
mkdir -p static-tmp
|
||||||
|
cp -a static/* static-tmp/
|
||||||
|
|
||||||
|
declare -a replacements
|
||||||
|
|
||||||
|
while IFS= read -r file; do
|
||||||
|
[ -f "$file" ] || continue
|
||||||
|
hash=$(sha256sum "$file" | head -c 8)
|
||||||
|
sri_hash=sha256-$(openssl dgst -sha256 -binary "$file" | openssl base64 -A)
|
||||||
|
dest="$(dirname "$file")/$hash.$(basename "$file")"
|
||||||
|
rel_path="${file#*/}"
|
||||||
|
dest_path="${dest#*/}"
|
||||||
|
|
||||||
|
if [[ "$file" == *.css ]]; then
|
||||||
|
replacements+=("s@{{css|/$rel_path}}@<link rel=\"stylesheet\" href=\"/$dest_path\" integrity=\"$sri_hash\"/>@g")
|
||||||
|
elif [[ "$file" == *.js ]]; then
|
||||||
|
replacements+=("s@{{js|/$rel_path}}@<script type=\"module\" src=\"/$dest_path\" integrity=\"$sri_hash\"></script>@g")
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "$file" "$dest"
|
||||||
|
replacements+=("s@{{integrity|/$rel_path}}@$sri_hash@g")
|
||||||
|
replacements+=("s@{{path|/$rel_path}}@/$dest_path@g")
|
||||||
|
done < <(find static-tmp -type f \( -name "*.css" -o -name "*.js" \))
|
||||||
|
|
||||||
|
while IFS= read -r htmlfile; do
|
||||||
|
for rep in "${replacements[@]}"; do
|
||||||
|
sed -i "$rep" "$htmlfile"
|
||||||
|
done
|
||||||
|
done < <(find static-tmp -type f -name "*.html")
|
||||||
|
|
||||||
|
rm -rf static
|
||||||
|
mv static-tmp static
|
||||||
Reference in New Issue
Block a user