Skip to content

Commit fd2c4ce

Browse files
committed
feat/policy-names: Add network policy names in hubble UI when they are known and correlated to flows.
Fixes: cilium/hubble#1100 Signed-off-by: Kris Gambirazzi <[email protected]>
1 parent 05eebce commit fd2c4ce

File tree

12 files changed

+223
-88
lines changed

12 files changed

+223
-88
lines changed

src/api/__mocks__/data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,5 +372,7 @@ export const flows: HubbleFlow[] = range(1000).map((): HubbleFlow => {
372372
trafficDirection:
373373
Math.random() <= 0.5 ? TrafficDirection.Egress : TrafficDirection.Ingress,
374374
authType: AuthType.Disbaled,
375+
egressAllowedBy: [],
376+
ingressAllowedBy: [],
375377
};
376378
});

src/api/grpc/common.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ export const filterEntryWhitelistFilters = (
181181
const pod = filter.podNamespace
182182
? `${filter.podNamespace}/${filter.query}`
183183
: filters.namespace
184-
? `${filters.namespace}/${filter.query}`
185-
: null;
184+
? `${filters.namespace}/${filter.query}`
185+
: null;
186186

187187
if (filter.fromRequired) {
188188
// NOTE: this makes possible to catch flows [outside of ns] -> [ns]

src/components/FlowsTable/Sidebar.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
DnsBodyItem,
2020
IdentityEntry,
2121
PodEntry,
22+
PolicyEntry,
2223
} from './SidebarComponents';
2324

2425
import css from './styles.scss';
@@ -460,6 +461,23 @@ export const FlowsTableSidebar = memo<Props>(function FlowsTableSidebar(props) {
460461
</div>
461462
</section>
462463
)}
464+
<hr />
465+
{flow.hasEgressAllowedBy && (
466+
<section className={css.block}>
467+
<span className={css.title}>Egress allowed by policies</span>
468+
<div className={css.body}>
469+
<PolicyEntry allowedBy={flow.egressAllowedBy} />
470+
</div>
471+
</section>
472+
)}
473+
{flow.hasIngressAllowedBy && (
474+
<section className={css.block}>
475+
<span className={css.title}>Ingress allowed by policies</span>
476+
<div className={css.body}>
477+
<PolicyEntry allowedBy={flow.ingressAllowedBy} />
478+
</div>
479+
</section>
480+
)}
463481
</div>
464482
);
465483
});

src/components/FlowsTable/SidebarComponents.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,29 @@ export const PodEntry = memo<PodItemProps>(
252252
);
253253
},
254254
);
255+
256+
export interface PolicyEntryProps {
257+
allowedBy: string[];
258+
}
259+
260+
export const PolicyEntry = memo<PolicyEntryProps>(
261+
function FlowsTableSidebarLabelsEntry(props) {
262+
return (
263+
<div className={css.policies}>
264+
{props.allowedBy.map(policyName => {
265+
return <PolicyEntryItem key={policyName} name={policyName} />;
266+
})}
267+
</div>
268+
);
269+
},
270+
);
271+
272+
export interface PolicyEntryItemProps {
273+
name: string;
274+
}
275+
276+
export const PolicyEntryItem = memo<PolicyEntryItemProps>(
277+
function FlowsTableSidebarPolicyEntry(props) {
278+
return <span className={css.policy}>{props.name}</span>;
279+
},
280+
);

src/components/FlowsTable/styles.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@
237237
}
238238
}
239239

240+
.policies {
241+
.policy {
242+
display: block;
243+
}
244+
}
245+
240246
.tcpFlags {
241247
.tcpFlag {
242248
margin-right: 5px;

src/components/TopBar/NamespaceSelectorDropdown.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ export const NamespaceSelectorDropdown = memo<Props>(
5858
currentNamespace && namespaces.includes(currentNamespace)
5959
? currentNamespace
6060
: currentNamespace
61-
? `Waiting ${currentNamespace} namespace…`
62-
: 'Choose namespace';
61+
? `Waiting ${currentNamespace} namespace…`
62+
: 'Choose namespace';
6363

6464
return (
6565
<NamespaceSelect

src/domain/flows/index.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,8 @@ export class Flow {
333333
this.l7?.type === L7FlowType.Request
334334
? '->'
335335
: this.l7?.type === L7FlowType.Response
336-
? '<-'
337-
: '??';
336+
? '<-'
337+
: '??';
338338

339339
// TODO: check if this is a correct fingerprints for all but http
340340
return `${direction} ${l7helpers.getEndpointId(l7)}`;
@@ -517,4 +517,20 @@ export class Flow {
517517
public get authTypeLabel(): string {
518518
return authtypeHelpers.toString(this.ref.authType);
519519
}
520+
521+
public get hasEgressAllowedBy(): boolean {
522+
return this.ref.egressAllowedBy.length > 0;
523+
}
524+
525+
public get hasIngressAllowedBy(): boolean {
526+
return this.ref.ingressAllowedBy.length > 0;
527+
}
528+
529+
public get egressAllowedBy(): string[] {
530+
return this.ref.egressAllowedBy || [];
531+
}
532+
533+
public get ingressAllowedBy(): string[] {
534+
return this.ref.ingressAllowedBy || [];
535+
}
520536
}

src/domain/helpers/flows.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
TrafficDirection as PBTrafficDirection,
1717
CiliumEventType as PBCiliumEventType,
1818
Service as PBService,
19+
Policy as Policy,
1920
} from '~backend/proto/flow/flow_pb';
2021

2122
import {
@@ -47,6 +48,8 @@ import * as misc from '~/domain/misc';
4748
import * as verdictHelpers from './verdict';
4849
import { authTypeFromPb } from './auth-type';
4950

51+
const derivedFrom = 'reserved:io.cilium.policy.derived-from';
52+
5053
export const hubbleFlowFromObj = (obj: any): HubbleFlow | null => {
5154
obj = obj.flow != null ? obj.flow : obj;
5255

@@ -84,6 +87,9 @@ export const hubbleFlowFromObj = (obj: any): HubbleFlow | null => {
8487
const trafficDirection = trafficDirectionFromStr(obj.trafficDirection);
8588
const authType = authTypeFromStr(obj.authType);
8689

90+
const egressAllowedBy = getNameFromPolicy(obj.getEgressAllowedByList);
91+
const ingressAllowedBy = getNameFromPolicy(obj.getIngressAllowedByList);
92+
8793
return {
8894
time,
8995
verdict,
@@ -105,6 +111,8 @@ export const hubbleFlowFromObj = (obj: any): HubbleFlow | null => {
105111
summary: obj.summary,
106112
trafficDirection,
107113
authType,
114+
egressAllowedBy,
115+
ingressAllowedBy,
108116
};
109117
};
110118

@@ -148,6 +156,9 @@ export const hubbleFlowFromPb = (flow: PBFlow): HubbleFlow => {
148156
const trafficDirection = trafficDirectionFromPb(flow.getTrafficDirection());
149157
const authType = authTypeFromPb(flow.getAuthType());
150158

159+
const egressAllowedBy = getNameFromPolicy(flow.getEgressAllowedByList());
160+
const ingressAllowedBy = getNameFromPolicy(flow.getIngressAllowedByList());
161+
151162
return {
152163
time,
153164
verdict,
@@ -169,6 +180,8 @@ export const hubbleFlowFromPb = (flow: PBFlow): HubbleFlow => {
169180
summary: flow.getSummary(),
170181
trafficDirection,
171182
authType,
183+
egressAllowedBy: egressAllowedBy,
184+
ingressAllowedBy: ingressAllowedBy,
172185
};
173186
};
174187

@@ -512,3 +525,25 @@ export const icmpv4FromPb = (icmp: PBICMPv4): ICMPv4 => {
512525
export const icmpv6FromPb = (icmp: PBICMPv6): ICMPv6 => {
513526
return icmpv4FromPb(icmp);
514527
};
528+
529+
export const getNameFromPolicy = (policies: Array<Policy>): Array<string> => {
530+
return policies.map(policy => {
531+
if (policy.getName() === '') {
532+
const labelMap = new Map<string, string>();
533+
policy.getLabelsList().forEach(keyValueString => {
534+
const [key, value] = keyValueString.split('=');
535+
labelMap.set(key, value);
536+
});
537+
538+
// Note: We try to automatically derive the policy name if it is set.
539+
// This is set by the policy mapstate for certain known scenarios, like allowing localhost access.
540+
// See: https://github.com/cilium/cilium/blob/614f2ddcc8fe93aeaf463b4535dcc0f1dcc373a3/pkg/policy/mapstate.go#L42-L49
541+
if (labelMap.has(derivedFrom)) {
542+
return '<cilium-internal>/' + labelMap.get(derivedFrom);
543+
}
544+
return '<cilium-internal>/unknown';
545+
}
546+
547+
return policy.getNamespace() + '/' + policy.getName();
548+
});
549+
};

src/domain/hubble.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export interface HubbleFlow {
2121
readonly summary: string;
2222
readonly trafficDirection: TrafficDirection;
2323
readonly authType: AuthType;
24+
readonly egressAllowedBy: Array<string>;
25+
readonly ingressAllowedBy: Array<string>;
2426
}
2527

2628
export interface HubbleService {

src/testing/data/flows.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export const icmpv4Flow: HubbleFlow = {
4949
},
5050
trafficDirection: TrafficDirection.Ingress,
5151
authType: AuthType.Disbaled,
52+
egressAllowedBy: [],
53+
ingressAllowedBy: [],
5254
};
5355

5456
export const icmpv6Flow: HubbleFlow = {
@@ -96,6 +98,8 @@ export const hubbleOne: HubbleFlow = {
9698
},
9799
trafficDirection: TrafficDirection.Ingress,
98100
authType: AuthType.Disbaled,
101+
egressAllowedBy: [],
102+
ingressAllowedBy: [],
99103
};
100104

101105
export const hubbleNoSourceName: HubbleFlow = {

0 commit comments

Comments
 (0)