Skip to content
Open
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
104 changes: 104 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ View Video: https://youtu.be/AWAlOQeNpgU?t=48
- ⌨️ Interactive Editor for `compose.yaml`
- 🦦 Interactive Web Terminal
- 🕷️ (1.4.0 🆕) Multiple agents support - You can manage multiple stacks from different Docker hosts in one single interface
- 🔐 **Proxy Authentication** - Integrate with identity providers like Authentik, Authelia, OAuth2 Proxy, and more
- 🏪 Convert `docker run ...` commands into `compose.yaml`
- 📙 File based structure - Dockge won't kidnap your compose files, they are stored on your drive as usual. You can interact with them using normal `docker compose` commands

Expand Down Expand Up @@ -126,6 +127,109 @@ If you want to translate Dockge into your language, please read [Translation Gui

Be sure to read the [guide](https://github.com/louislam/dockge/blob/master/CONTRIBUTING.md), as we don't accept all types of pull requests and don't want to waste your time.

## 🔐 Proxy Authentication

Dockge supports authentication via HTTP headers set by reverse proxies. This allows you to integrate with identity providers like **Authentik**, **Authelia**, **OAuth2 Proxy**, **Keycloak**, and others.

### Configuration

| Environment Variable | Description | Default |
|---------------------|-------------|---------|
| `DOCKGE_AUTH_PROXY_HEADER` | The HTTP header containing the authenticated username | *(disabled)* |
| `DOCKGE_AUTH_PROXY_AUTO_CREATE` | Automatically create users on first login | `false` |
| `DOCKGE_AUTH_PROXY_LOGOUT_URL` | URL to redirect to when user logs out | *(none)* |

### Supported Headers

The header name is configurable. Common headers used by identity providers:

| Provider | Header Name |
|----------|-------------|
| Authelia | `Remote-User` |
| Authentik | `X-authentik-username` |
| OAuth2 Proxy | `X-Auth-Request-User` |
| Traefik Forward Auth | `X-Forwarded-User` |

### Example: Authentik

```yaml
services:
dockge:
image: louislam/dockge:1
restart: unless-stopped
ports:
- 5001:5001
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
- /opt/stacks:/opt/stacks
environment:
- DOCKGE_STACKS_DIR=/opt/stacks
- DOCKGE_AUTH_PROXY_HEADER=X-authentik-username
- DOCKGE_AUTH_PROXY_AUTO_CREATE=true
- DOCKGE_AUTH_PROXY_LOGOUT_URL=https://auth.example.com/outpost.goauthentik.io/sign_out
```

### Example: Authelia

```yaml
services:
dockge:
image: louislam/dockge:1
restart: unless-stopped
ports:
- 5001:5001
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
- /opt/stacks:/opt/stacks
environment:
- DOCKGE_STACKS_DIR=/opt/stacks
- DOCKGE_AUTH_PROXY_HEADER=Remote-User
- DOCKGE_AUTH_PROXY_AUTO_CREATE=true
- DOCKGE_AUTH_PROXY_LOGOUT_URL=https://auth.example.com/logout
```

### Example: Traefik with Forward Auth

```yaml
services:
dockge:
image: louislam/dockge:1
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
- /opt/stacks:/opt/stacks
environment:
- DOCKGE_STACKS_DIR=/opt/stacks
- DOCKGE_AUTH_PROXY_HEADER=X-Forwarded-User
- DOCKGE_AUTH_PROXY_AUTO_CREATE=true
labels:
- "traefik.enable=true"
- "traefik.http.routers.dockge.rule=Host(`dockge.example.com`)"
- "traefik.http.routers.dockge.middlewares=authentik@docker"
```

### Security Considerations

⚠️ **Important**: When using proxy authentication, ensure that:

1. **Direct access is blocked** - Users should only access Dockge through your reverse proxy. The proxy header can be spoofed if users can connect directly.
2. **Your reverse proxy strips incoming auth headers** - Prevent users from injecting fake authentication headers.
3. **Use HTTPS** - Always use TLS between clients and your reverse proxy.

### First-Time Setup with Proxy Auth

When `DOCKGE_AUTH_PROXY_AUTO_CREATE=true`:
- The first user to authenticate via the proxy becomes an admin
- No manual setup is required
- Users are automatically created when they first log in

When `DOCKGE_AUTH_PROXY_AUTO_CREATE=false`:
- Users must be manually created in the database before they can log in
- Useful for restricting access to pre-approved users only

## FAQ

#### "Dockge"?
Expand Down
91 changes: 84 additions & 7 deletions backend/dockge-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { AgentSocketHandler } from "./agent-socket-handler";
import { AgentSocket } from "../common/agent-socket";
import { ManageAgentSocketHandler } from "./socket-handlers/manage-agent-socket-handler";
import { Terminal } from "./terminal";
import { ProxyAuth } from "./proxy-auth";

export class DockgeServer {
app : Express;
Expand Down Expand Up @@ -141,7 +142,20 @@ export class DockgeServer {
type: Boolean,
optional: true,
defaultValue: false,
}
},
authProxyHeader: {
type: String,
optional: true,
},
authProxyAutoCreate: {
type: Boolean,
optional: true,
defaultValue: false,
},
authProxyLogoutUrl: {
type: String,
optional: true,
},
});

this.config = args as Config;
Expand All @@ -155,6 +169,22 @@ export class DockgeServer {
this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
this.config.enableConsole = args.enableConsole || process.env.DOCKGE_ENABLE_CONSOLE === "true" || false;

// Proxy authentication configuration
this.config.authProxyHeader = args.authProxyHeader || process.env.DOCKGE_AUTH_PROXY_HEADER || undefined;
this.config.authProxyAutoCreate = args.authProxyAutoCreate || process.env.DOCKGE_AUTH_PROXY_AUTO_CREATE === "true" || false;
this.config.authProxyLogoutUrl = args.authProxyLogoutUrl || process.env.DOCKGE_AUTH_PROXY_LOGOUT_URL || undefined;

if (this.config.authProxyHeader) {
log.info("server", `Proxy authentication enabled using header: ${this.config.authProxyHeader}`);
if (this.config.authProxyAutoCreate) {
log.info("server", "Proxy authentication auto-create users: enabled");
}
if (this.config.authProxyLogoutUrl) {
log.info("server", `Proxy authentication logout URL: ${this.config.authProxyLogoutUrl}`);
}
}

this.stacksDir = this.config.stacksDir;

log.debug("server", this.config);
Expand Down Expand Up @@ -274,11 +304,6 @@ export class DockgeServer {

this.sendInfo(dockgeSocket, true);

if (this.needSetup) {
log.info("server", "Redirect to setup page");
dockgeSocket.emit("setup");
}

// Create socket handlers (original, no agent support)
for (const socketHandler of this.socketHandlerList) {
socketHandler.create(dockgeSocket, this);
Expand All @@ -300,7 +325,56 @@ export class DockgeServer {
// ***************************

log.debug("auth", "check auto login");
if (await Settings.get("disableAuth")) {

// Check for proxy authentication first (before setup page redirect)
if (this.config.authProxyHeader) {
const proxyUser = await ProxyAuth.authenticate(
socket.request.headers,
this.config.authProxyHeader,
this.config.authProxyAutoCreate
);

if (proxyUser) {
log.info("auth", `Proxy auth successful for user: ${proxyUser.username}`);

// If this was the first user created via proxy auth, clear the needSetup flag
if (this.needSetup) {
log.info("auth", "First user created via proxy auth, clearing setup flag");
this.needSetup = false;
}

await this.afterLogin(dockgeSocket, proxyUser);
dockgeSocket.emit("proxyAuthLogin", {
ok: true,
username: proxyUser.username,
logoutUrl: this.config.authProxyLogoutUrl,
});
} else {
// Proxy auth is enabled but no valid user found
// This could be: no header present, or user doesn't exist and auto-create is off
const headerValue = socket.request.headers[this.config.authProxyHeader.toLowerCase()];

if (headerValue) {
// Header is present but user doesn't exist and auto-create is disabled
log.warn("auth", `Proxy auth header present but user not found and auto-create is disabled. Header: ${this.config.authProxyHeader}, Value: ${headerValue}`);
dockgeSocket.emit("proxyAuthError", {
ok: false,
msg: "User not found. Please contact your administrator to create your account, or enable DOCKGE_AUTH_PROXY_AUTO_CREATE.",
});
} else {
// No proxy auth header present - user is accessing directly without going through proxy
log.warn("auth", `Proxy auth enabled but no header found. Expected header: ${this.config.authProxyHeader}`);
dockgeSocket.emit("proxyAuthError", {
ok: false,
msg: "Proxy authentication is required. Please access Dockge through your identity provider.",
});
}
}
} else if (this.needSetup) {
// Only redirect to setup if proxy auth is not enabled
log.info("server", "Redirect to setup page");
dockgeSocket.emit("setup");
} else if (await Settings.get("disableAuth")) {
log.info("auth", "Disabled Auth: auto login to admin");
this.afterLogin(dockgeSocket, await R.findOne("user") as User);
dockgeSocket.emit("autoLogin");
Expand Down Expand Up @@ -439,6 +513,9 @@ export class DockgeServer {
latestVersion: latestVersionProperty,
isContainer,
primaryHostname: await Settings.get("primaryHostname"),
// Proxy auth info for frontend
proxyAuthEnabled: !!this.config.authProxyHeader,
proxyAuthLogoutUrl: this.config.authProxyLogoutUrl,
//serverTimezone: await this.getTimezone(),
//serverTimezoneOffset: this.getTimezoneOffset(),
});
Expand Down
Loading
Loading