fix(security): restrict deployment file creation
This commit is contained in:
@@ -88,7 +88,7 @@ Internal container paths, allowed roots and service authentication are intention
|
||||
|
||||
- The main application never mounts the Docker socket.
|
||||
- Only the private, non-published agent can access Docker, through typed and deny-by-default operations.
|
||||
- Both images use a root identity inside their container namespaces so fresh bind mounts work without a host-specific image UID. Their root filesystems remain read-only, all Linux capabilities are dropped, and no recursive ownership change is performed on application, server or backup data.
|
||||
- Both images use a root identity inside their container namespaces so fresh bind mounts work without a host-specific image UID. Their root filesystems remain read-only, all Linux capabilities are dropped, Linux file creation uses a private `0077` umask, and no recursive ownership change is performed on application, server or backup data.
|
||||
- Internal secrets are generated from the operating system cryptographic random source, stored with restrictive permissions and never logged or exposed in the UI.
|
||||
- The master key is mounted only through the application data path; the agent has no access to it.
|
||||
- Agent path checks remain fixed to `/srv/game-servers` and `/srv/game-backups` inside the containers.
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
catalogdata "git.zaynet.fr/DoGaMa/DoGaMa-serv/catalog"
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/agent"
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/catalog"
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/processsecurity"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -22,6 +23,7 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
processsecurity.RestrictFileCreation()
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
||||
if err := run(logger); err != nil {
|
||||
logger.Error("agent stopped", "event", "agent.failed", "error", err)
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/internalsecrets"
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/notification"
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/persistence/sqlite"
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/processsecurity"
|
||||
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/web"
|
||||
)
|
||||
|
||||
@@ -33,6 +34,7 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
processsecurity.RestrictFileCreation()
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
||||
if err := run(logger); err != nil {
|
||||
logger.Error("application stopped", "event", "application.failed", "error", err)
|
||||
|
||||
@@ -40,7 +40,7 @@ Read this compact operational baseline before starting a milestone. Open detaile
|
||||
- Minimal production Compose containing only `dogama` and `agent`; `docker compose up -d` is the complete first-start workflow with no initializer, setup command, host UID/GID preparation or user-managed internal secrets.
|
||||
- Service-owned persistent secrets: the agent atomically creates and validates its mode-`0640` shared token in `agent_state`; the application independently creates and validates its mode-`0600` master key below the application data path. The application tolerates concurrent first start by waiting up to 60 seconds for the token and authenticated agent health.
|
||||
- Two service networks: an administrator-named application/reverse-proxy network plus a private Compose control network. The agent safely ensures the fixed `DOGAMA_GAMES_NETWORK` exists and applies it to every game-container create or replacement; it is not caller-selectable through the lifecycle API.
|
||||
- Portable fresh bind-mount startup uses root identities inside the read-only, capability-free container namespaces. No recursive ownership change is performed; game-container UID/GID remains per-instance configuration.
|
||||
- Portable fresh bind-mount startup uses root identities inside the read-only, capability-free container namespaces and a private process umask. No recursive ownership change is performed; game-container UID/GID remains per-instance configuration.
|
||||
- Gitea CI for pull requests and `main`, plus tag-only multi-architecture image publication and Gitea Release creation.
|
||||
|
||||
## Durable decisions
|
||||
|
||||
@@ -19,7 +19,7 @@ The application data path contains SQLite, import staging and the application-on
|
||||
|
||||
Only host-side storage locations, image version, web port, timezone and the two Docker network names are public Compose settings. `DOGAMA_NETWORK` names the application-facing network used by a reverse proxy. `DOGAMA_GAMES_NETWORK` names the sole network that the restricted agent attaches to created and recreated game containers. API plans contain no caller-selectable network. Container paths and allowed agent roots remain fixed internal contracts. Back up the application data, game servers, backups and `agent_state` volume together.
|
||||
|
||||
Both services use root inside their container namespaces so Docker-created bind directories and ordinary administrator-selected paths work without knowledge of an image-specific UID/GID. They keep read-only root filesystems, `no-new-privileges` and an empty Linux capability set. The main application never receives the Docker socket. DoGaMa creates only directories it needs below the configured roots and never performs an automatic recursive `chown` of application, server or backup data. Game-container UID/GID selection remains a separate per-instance setting.
|
||||
Both services use root inside their container namespaces so Docker-created bind directories and ordinary administrator-selected paths work without knowledge of an image-specific UID/GID. They keep read-only root filesystems, `no-new-privileges`, an empty Linux capability set and a `0077` process umask for new files. The main application never receives the Docker socket. DoGaMa creates only directories it needs below the configured roots and never performs an automatic recursive `chown` of application, server or backup data. Game-container UID/GID selection remains a separate per-instance setting.
|
||||
|
||||
For NAS or server-style paths, set ordinary writable locations in `.env`, for example `/srv/apps/dogama/data`, `/srv/games` and `/srv/backups`, then run `docker compose up -d`. No `/etc` or host `/var/lib` setup, system user, systemd unit or bootstrap script is required.
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build linux
|
||||
|
||||
// Package processsecurity applies process-local operating-system hardening.
|
||||
package processsecurity
|
||||
|
||||
import "syscall"
|
||||
|
||||
// RestrictFileCreation prevents newly created files from granting group or
|
||||
// other permissions. Explicitly less-permissive modes remain unchanged.
|
||||
func RestrictFileCreation() {
|
||||
syscall.Umask(0o077)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//go:build !linux
|
||||
|
||||
// Package processsecurity applies process-local operating-system hardening.
|
||||
package processsecurity
|
||||
|
||||
// RestrictFileCreation is a no-op outside the Linux production target.
|
||||
func RestrictFileCreation() {}
|
||||
Reference in New Issue
Block a user