Skip to content

Commit 3510780

Browse files
Finally fix db encryption between restarts (#98)
This pull request introduces major improvements to SQLCipher integration, schema migration reliability, and cross-platform container builds. The changes ensure that encrypted databases are initialized securely and consistently, improve schema migration error handling, and transition the build/runtime containers from Alpine to Ubuntu for better compatibility. Additionally, the documentation and tests have been updated to reflect these changes and to ensure encrypted database headers. **SQLCipher Integration and Database Initialization:** * Refactored `internal/database/db.go` to construct SQLCipher DSNs with all required pragmas and encryption key, ensuring settings are applied before any page reads; removed post-open PRAGMA calls and inlined DSN generation for both file and in-memory databases. [[1]](diffhunk://#diff-d18caea19014a2e48b9dc31a7831f7a43b1c3068421642110862803da915e00dL30-R68) [[2]](diffhunk://#diff-d18caea19014a2e48b9dc31a7831f7a43b1c3068421642110862803da915e00dR111-R162) * Added a new `verifyKeyUsable` function to confirm the encryption key can decrypt the schema, improving error reporting and reliability. * Updated `AGENTS.md` to clarify that SQLCipher connection settings must be applied via DSN, not post-open PRAGMA calls. **Schema Migration and Reliability:** * Changed schema migration logic to execute each SQL statement individually, avoiding multi-statement driver errors and surfacing clear failure contexts; added the `execSQLStatements` helper and improved error handling for table info queries. [[1]](diffhunk://#diff-d18caea19014a2e48b9dc31a7831f7a43b1c3068421642110862803da915e00dL214-R247) [[2]](diffhunk://#diff-d18caea19014a2e48b9dc31a7831f7a43b1c3068421642110862803da915e00dL319-L323) [[3]](diffhunk://#diff-d18caea19014a2e48b9dc31a7831f7a43b1c3068421642110862803da915e00dR360-R367) [[4]](diffhunk://#diff-d18caea19014a2e48b9dc31a7831f7a43b1c3068421642110862803da915e00dR419-L401) [[5]](diffhunk://#diff-d18caea19014a2e48b9dc31a7831f7a43b1c3068421642110862803da915e00dR480-R487) * Updated documentation in `AGENTS.md` to require that schema migration SQL statements be terminated with semicolons and comments placed on their own lines. **Container Build and Runtime Platform:** * Migrated Docker build and runtime images from Alpine to Ubuntu 24.04, including installation of SQLCipher and Go toolchain, and adjusted build/run scripts for glibc compatibility and cross-arch support. [[1]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557L2-R2) [[2]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557L21-R50) [[3]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557L49-R76) [[4]](diffhunk://#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557L61-R97) **Testing and Validation:** * Added a new test to ensure databases are created with encrypted headers (not plaintext), verifying SQLCipher is applied from the start. **Documentation and Miscellaneous:** * Updated encryption key generation and documentation to use a consistent secret filename and clarified persistence requirements for production. [[1]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52L187-R189) [[2]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R222-R223) * Updated Go module to use a custom fork of `go-sqlite3` for compatibility with SQLCipher and libsqlite3 tag. Let me know if you want to dive deeper into any of these areas!
1 parent 24f098e commit 3510780

9 files changed

Lines changed: 283 additions & 126 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@
3434
- Never commit secrets. Copy `.env.example` to `.env` for local dev.
3535
- SQLCipher is mandatory; run `make dev-setup` and follow README notes. In prod, provide 32-byte key via Docker secret.
3636
- Required env: `SIGNAL_PHONE_NUMBER`, AI provider vars. Backend listens on `LISTEN_ADDR` (default `:8081`).
37-
37+
- SQLCipher connection pragmas are applied via DSN inside `internal/database`; avoid adding post-open `PRAGMA` calls (especially `cipher_migrate`).
38+
- Schema migrations execute statements individually—when adding SQL to `schema.sql`, ensure each statement is terminated with a semicolon and that comments stand on their own lines.

Dockerfile

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Stage 1: Build Next.js frontend
2-
FROM node:24-alpine AS frontend-builder
2+
FROM node:24-bookworm AS frontend-builder
33

44
WORKDIR /app/web
55

@@ -18,10 +18,36 @@ COPY web/ ./
1818
RUN npm run build
1919

2020
# Stage 2: Build Go backend
21-
FROM golang:1.25-alpine AS backend-builder
22-
23-
# Install SQLCipher and build dependencies, including static library
24-
RUN apk add --no-cache gcc musl-dev sqlite-dev sqlite-static sqlcipher-dev pkgconf openssl-dev openssl-libs-static
21+
FROM ubuntu:24.04 AS backend-builder
22+
23+
ARG GO_VERSION=1.25.1
24+
ENV DEBIAN_FRONTEND=noninteractive
25+
ENV PATH=/usr/local/go/bin:$PATH
26+
27+
# Install toolchain, SQLCipher dev headers, and download Go
28+
RUN apt-get update && \
29+
apt-get install -y --no-install-recommends \
30+
build-essential pkg-config curl ca-certificates libsqlcipher-dev libssl-dev && \
31+
rm -rf /var/lib/apt/lists/* && \
32+
arch=$(dpkg --print-architecture) && \
33+
case "$arch" in \
34+
amd64) GOARCH=amd64 ;; \
35+
arm64) GOARCH=arm64 ;; \
36+
*) echo "Unsupported arch: $arch" && exit 1 ;; \
37+
esac && \
38+
curl -fsSL https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz -o /tmp/go.tgz && \
39+
tar -C /usr/local -xzf /tmp/go.tgz && \
40+
rm /tmp/go.tgz && \
41+
# Ensure go is available
42+
go version && \
43+
# Align lib name expected by go-sqlite3 when using libsqlite3 tag
44+
bash -lc 'libdir=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null || echo aarch64-linux-gnu); \
45+
if [ -e /usr/lib/$libdir/libsqlcipher.so ] && [ ! -e /usr/lib/$libdir/libsqlite3.so ]; then \
46+
ln -s /usr/lib/$libdir/libsqlcipher.so /usr/lib/$libdir/libsqlite3.so; \
47+
fi'
48+
49+
# Enable CGO globally; actual flags are set at build invocation time below
50+
ENV CGO_ENABLED=1
2551

2652
WORKDIR /app
2753

@@ -46,9 +72,8 @@ ARG GIT_COMMIT=unknown
4672
ARG BUILD_TIME=unknown
4773
ARG GOARCH
4874

49-
RUN CGO_ENABLED=1 \
50-
CGO_CFLAGS="$(pkg-config --cflags sqlcipher) -DSQLITE_HAS_CODEC" \
51-
CGO_LDFLAGS="$(pkg-config --libs sqlcipher) $(pkg-config --libs libcrypto libssl)" \
75+
RUN CGO_CFLAGS="-I/usr/include/sqlcipher -DSQLITE_HAS_CODEC" \
76+
CGO_LDFLAGS="-lsqlcipher -lssl -lcrypto" \
5277
go build \
5378
-tags="sqlite_crypt libsqlite3" \
5479
-ldflags="-w -s \
@@ -58,14 +83,18 @@ RUN CGO_ENABLED=1 \
5883
-o summarizarr \
5984
./cmd/summarizarr
6085

61-
# Stage 3: Runtime
62-
FROM alpine:latest
86+
# Stage 3: Runtime (Debian/Ubuntu, glibc)
87+
FROM ubuntu:24.04
6388

64-
# Update package index and add ca certificates, utilities, and SQLCipher runtime libraries
65-
RUN apk update && \
66-
apk --no-cache add ca-certificates wget sqlcipher-libs openssl && \
67-
addgroup -g 1001 summarizarr && \
68-
adduser -D -s /bin/sh -u 1001 -G summarizarr summarizarr
89+
RUN apt-get update && \
90+
apt-get install -y --no-install-recommends ca-certificates wget sqlcipher && \
91+
rm -rf /var/lib/apt/lists/* && \
92+
bash -lc 'libdir=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null || echo aarch64-linux-gnu); \
93+
if [ -e /usr/lib/$libdir/libsqlcipher.so ] && [ ! -e /usr/lib/$libdir/libsqlite3.so ]; then \
94+
ln -s /usr/lib/$libdir/libsqlcipher.so /usr/lib/$libdir/libsqlite3.so; \
95+
fi' && \
96+
groupadd -g 1001 summarizarr && \
97+
useradd -u 1001 -g 1001 -M -s /bin/sh summarizarr
6998

7099
WORKDIR /app
71100

@@ -99,4 +128,4 @@ ENV DATABASE_PATH=/data/summarizarr.db
99128
ENV AI_PROVIDER=local
100129
ENV SUMMARIZATION_INTERVAL=1h
101130

102-
ENTRYPOINT ["./summarizarr"]
131+
ENTRYPOINT ["./summarizarr"]

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,9 @@ dev-setup-encrypted: dev-key ## Setup development environment with encryption en
184184

185185
prod-key: ## Generate production encryption key
186186
@mkdir -p docker/secrets
187-
@openssl rand -hex 32 > docker/secrets/sqlcipher_encryption_key
188-
@chmod 600 docker/secrets/sqlcipher_encryption_key
189-
@echo "$(GREEN)Generated production encryption key in docker/secrets/sqlcipher_encryption_key$(NC)"
187+
@openssl rand -hex 32 > docker/secrets/encryption_key
188+
@chmod 600 docker/secrets/encryption_key
189+
@echo "$(GREEN)Generated production encryption key in docker/secrets/encryption_key$(NC)"
190190
@echo "$(RED)IMPORTANT: Back up this key securely!$(NC)"
191191

192192
test-backend: ## Test Go backend with SQLCipher

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ Keys are 32-byte (64 hex characters) for AES-256. Never commit keys to version c
219219
220220
Note: Databases must be encrypted from the first run. There is no supported migration from unencrypted databases.
221221
222+
Tip: Ensure `./data` exists and is writable before starting with `make docker` or Compose. Both the database file and `encryption.key` persist in this directory.
223+
222224
## Production Deployment
223225
224226
### Container Registry
@@ -259,4 +261,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
259261
- [Signal CLI REST API](https://github.com/bbernhard/signal-cli-rest-api) - Signal integration
260262
- [Ollama](https://ollama.ai/) - Local AI capabilities
261263
- [Next.js](https://nextjs.org/) - Modern web framework
262-
- [Shadcn/ui](https://ui.shadcn.com/) - UI components
264+
- [Shadcn/ui](https://ui.shadcn.com/) - UI components

cmd/summarizarr/main.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"log/slog"
9+
"crypto/sha256"
910
"os"
1011
"os/signal"
1112
"path/filepath"
@@ -59,6 +60,14 @@ func main() {
5960
slog.Error("Failed to obtain encryption key", "error", err)
6061
os.Exit(1)
6162
}
63+
// Log masked key fingerprint and source for troubleshooting in dev
64+
{
65+
finger := sha256.Sum256([]byte(encKey))
66+
src := "generated"
67+
if _, err := os.Stat(encMgr.SecretPath); err == nil { src = encMgr.SecretPath }
68+
if _, err := os.Stat(encMgr.KeyFile); err == nil { src = encMgr.KeyFile }
69+
slog.Info("Using encryption key", "source", src, "sha256_prefix", fmt.Sprintf("%x", finger)[:8])
70+
}
6271

6372
db, err := database.NewDB(cfg.DatabasePath, encKey)
6473
if err != nil {

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ require (
66
github.com/alexedwards/scs/sqlite3store v0.0.0-20231113091146-cef4b05350c8
77
github.com/alexedwards/scs/v2 v2.9.0
88
github.com/coder/websocket v1.8.13
9-
github.com/mattn/go-sqlite3 v1.14.32
9+
github.com/mattn/go-sqlite3 v1.14.17-0.20240122133042-fb824c8e339e
1010
github.com/sashabaranov/go-openai v1.41.1
1111
golang.org/x/crypto v0.41.0
1212
)
1313

14-
replace github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.32
14+
replace github.com/mattn/go-sqlite3 => github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20240122133042-fb824c8e339e

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ github.com/alexedwards/scs/v2 v2.9.0 h1:xa05mVpwTBm1iLeTMNFfAWpKUm4fXAW7CeAViqBV
44
github.com/alexedwards/scs/v2 v2.9.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
55
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
66
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
7-
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
8-
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
7+
github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20240122133042-fb824c8e339e h1:0s+RSCZSEaZ3IM4fqMH1Nf5UDpttrn1R2QDutqiL2Zc=
8+
github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20240122133042-fb824c8e339e/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
99
github.com/sashabaranov/go-openai v1.41.1 h1:zf5tM+GuxpyiyD9XZg8nCqu52eYFQg9OOew0gnIuDy4=
1010
github.com/sashabaranov/go-openai v1.41.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
1111
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=

0 commit comments

Comments
 (0)