mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-18 14:33:19 +00:00
fix(networking): treat host-network services as host-exposed in summaries (#1430)
The exposure summaries derived a stack's exposure solely from the declared published-port list, so a service running with network_mode: host (which publishes every container port on the host but declares no ports:) was under-reported as less exposed than it actually is. Capture network_mode in the lightweight dependency parser, add an isHostNetwork predicate, and treat a host-network service as exposed and publishing across the Fleet networking summary, the Stack Dossier export, and the Networking panel, matching how the Compose Doctor already flags host networking.
This commit is contained in:
@@ -10,7 +10,7 @@ import { FileSystemService } from '../FileSystemService';
|
||||
import { DatabaseService } from '../DatabaseService';
|
||||
import { parseComposeDependencies } from '../../helpers/composeDependencyParse';
|
||||
import { assembleStackDrift } from '../DriftDetectionService';
|
||||
import { isLoopback } from './normalize';
|
||||
import { isHostNetwork, isLoopback } from './normalize';
|
||||
import { getErrorMessage } from '../../utils/errors';
|
||||
import { sanitizeForLog } from '../../utils/safeLog';
|
||||
|
||||
@@ -60,8 +60,13 @@ export async function computeNodeNetworkingSummary(nodeId: number): Promise<Node
|
||||
const declared = parseComposeDependencies(content);
|
||||
if (declared.parseError) continue;
|
||||
|
||||
const publishesPort = declared.services.some(s => s.ports.length > 0);
|
||||
if (declared.services.some(s => s.ports.some(p => !isLoopback(p.hostIp)))) exposed.push(stack);
|
||||
// A host-network service publishes every container port directly on the host,
|
||||
// so it counts as exposed (beyond loopback) and as publishing even with no
|
||||
// declared `ports:`. This keeps the summary honest about host networking,
|
||||
// matching the Compose Doctor's host-network finding.
|
||||
const publishes = (s: typeof declared.services[number]): boolean => s.ports.length > 0 || isHostNetwork(s.networkMode);
|
||||
const publishesPort = declared.services.some(publishes);
|
||||
if (declared.services.some(s => isHostNetwork(s.networkMode) || s.ports.some(p => !isLoopback(p.hostIp)))) exposed.push(stack);
|
||||
|
||||
if (publishesPort) {
|
||||
// Unknown only when a publishing service is effectively unclassified: a
|
||||
@@ -70,7 +75,7 @@ export async function computeNodeNetworkingSummary(nodeId: number): Promise<Node
|
||||
const stackIntent = intents.find(i => i.service === '')?.intent ?? null;
|
||||
const byService = new Map(intents.filter(i => i.service !== '').map(i => [i.service, i.intent]));
|
||||
const anyUnclassified = declared.services
|
||||
.filter(s => s.ports.length > 0)
|
||||
.filter(publishes)
|
||||
.some(s => {
|
||||
const intent = byService.get(s.name) ?? stackIntent;
|
||||
return intent === null || intent === 'unknown';
|
||||
|
||||
@@ -23,6 +23,13 @@ export function isLoopback(ip: string): boolean {
|
||||
return ip === '127.0.0.1' || ip === '::1' || ip === '[::1]';
|
||||
}
|
||||
|
||||
/** True for `network_mode: host`, which publishes every container port directly
|
||||
* on the host regardless of any declared `ports:` (so it is always exposed
|
||||
* beyond loopback). Other modes (none, bridge, service:, container:) are not. */
|
||||
export function isHostNetwork(mode: string | undefined): boolean {
|
||||
return mode === 'host';
|
||||
}
|
||||
|
||||
/** Resolved runtime name of a top-level network/volume: a `name:` override wins,
|
||||
* otherwise compose prefixes the project (`<project>_<key>`). An external
|
||||
* resource is never project-prefixed: it references a pre-existing network/volume
|
||||
|
||||
Reference in New Issue
Block a user