mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 08:29:55 +00:00
Merge pull request #3731 from Infisical/daniel/gateway-auth-methods
feat(identities/kubernetes-auth): gateway as token reviewer
This commit is contained in:
@@ -843,7 +843,8 @@ export const useAddIdentityKubernetesAuth = () => {
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
gatewayId
|
||||
gatewayId,
|
||||
tokenReviewMode
|
||||
}) => {
|
||||
const {
|
||||
data: { identityKubernetesAuth }
|
||||
@@ -860,7 +861,8 @@ export const useAddIdentityKubernetesAuth = () => {
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
gatewayId
|
||||
gatewayId,
|
||||
tokenReviewMode
|
||||
}
|
||||
);
|
||||
|
||||
@@ -950,7 +952,8 @@ export const useUpdateIdentityKubernetesAuth = () => {
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
gatewayId
|
||||
gatewayId,
|
||||
tokenReviewMode
|
||||
}) => {
|
||||
const {
|
||||
data: { identityKubernetesAuth }
|
||||
@@ -967,7 +970,8 @@ export const useUpdateIdentityKubernetesAuth = () => {
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
gatewayId
|
||||
gatewayId,
|
||||
tokenReviewMode
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -379,10 +379,16 @@ export type DeleteIdentityAzureAuthDTO = {
|
||||
identityId: string;
|
||||
};
|
||||
|
||||
export enum IdentityKubernetesAuthTokenReviewMode {
|
||||
Api = "api",
|
||||
Gateway = "gateway"
|
||||
}
|
||||
|
||||
export type IdentityKubernetesAuth = {
|
||||
identityId: string;
|
||||
kubernetesHost: string;
|
||||
tokenReviewerJwt: string;
|
||||
tokenReviewMode: IdentityKubernetesAuthTokenReviewMode;
|
||||
allowedNamespaces: string;
|
||||
allowedNames: string;
|
||||
allowedAudience: string;
|
||||
@@ -399,6 +405,7 @@ export type AddIdentityKubernetesAuthDTO = {
|
||||
identityId: string;
|
||||
kubernetesHost: string;
|
||||
tokenReviewerJwt?: string;
|
||||
tokenReviewMode: IdentityKubernetesAuthTokenReviewMode;
|
||||
allowedNamespaces: string;
|
||||
allowedNames: string;
|
||||
allowedAudience: string;
|
||||
@@ -417,6 +424,7 @@ export type UpdateIdentityKubernetesAuthDTO = {
|
||||
identityId: string;
|
||||
kubernetesHost?: string;
|
||||
tokenReviewerJwt?: string | null;
|
||||
tokenReviewMode?: IdentityKubernetesAuthTokenReviewMode;
|
||||
allowedNamespaces?: string;
|
||||
allowedNames?: string;
|
||||
allowedAudience?: string;
|
||||
|
||||
+133
-71
@@ -33,13 +33,19 @@ import {
|
||||
useGetIdentityKubernetesAuth,
|
||||
useUpdateIdentityKubernetesAuth
|
||||
} from "@app/hooks/api";
|
||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||
import {
|
||||
IdentityKubernetesAuthTokenReviewMode,
|
||||
IdentityTrustedIp
|
||||
} from "@app/hooks/api/identities/types";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
import { IdentityFormTab } from "./types";
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
tokenReviewMode: z
|
||||
.nativeEnum(IdentityKubernetesAuthTokenReviewMode)
|
||||
.default(IdentityKubernetesAuthTokenReviewMode.Api),
|
||||
kubernetesHost: z.string().min(1),
|
||||
tokenReviewerJwt: z.string().optional(),
|
||||
gatewayId: z.string().optional().nullable(),
|
||||
@@ -62,7 +68,15 @@ const schema = z
|
||||
)
|
||||
.min(1)
|
||||
})
|
||||
.required();
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Gateway && !data.gatewayId) {
|
||||
ctx.addIssue({
|
||||
path: ["gatewayId"],
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "When token review mode is set to Gateway, a gateway must be selected"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type FormData = z.infer<typeof schema>;
|
||||
|
||||
@@ -100,11 +114,14 @@ export const IdentityKubernetesAuthForm = ({
|
||||
control,
|
||||
handleSubmit,
|
||||
reset,
|
||||
watch,
|
||||
setValue,
|
||||
|
||||
formState: { isSubmitting }
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
tokenReviewMode: IdentityKubernetesAuthTokenReviewMode.Api,
|
||||
kubernetesHost: "",
|
||||
tokenReviewerJwt: "",
|
||||
allowedNames: "",
|
||||
@@ -128,6 +145,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
tokenReviewMode: data.tokenReviewMode,
|
||||
kubernetesHost: data.kubernetesHost,
|
||||
tokenReviewerJwt: data.tokenReviewerJwt,
|
||||
allowedNames: data.allowedNames,
|
||||
@@ -148,6 +166,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
reset({
|
||||
tokenReviewMode: IdentityKubernetesAuthTokenReviewMode.Api,
|
||||
kubernetesHost: "",
|
||||
tokenReviewerJwt: "",
|
||||
allowedNames: "",
|
||||
@@ -173,6 +192,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
gatewayId,
|
||||
tokenReviewMode,
|
||||
accessTokenTrustedIps
|
||||
}: FormData) => {
|
||||
try {
|
||||
@@ -189,6 +209,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
caCert,
|
||||
identityId,
|
||||
gatewayId: gatewayId || null,
|
||||
tokenReviewMode,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||
@@ -205,6 +226,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
allowedAudience: allowedAudience || "",
|
||||
gatewayId: gatewayId || null,
|
||||
caCert: caCert || "",
|
||||
tokenReviewMode,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||
@@ -228,6 +250,8 @@ export const IdentityKubernetesAuthForm = ({
|
||||
}
|
||||
};
|
||||
|
||||
const tokenReviewMode = watch("tokenReviewMode");
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||
@@ -235,6 +259,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
[
|
||||
"kubernetesHost",
|
||||
"tokenReviewerJwt",
|
||||
"tokenReviewMode",
|
||||
"gatewayId",
|
||||
"accessTokenTTL",
|
||||
"accessTokenMaxTTL",
|
||||
@@ -269,21 +294,113 @@ export const IdentityKubernetesAuthForm = ({
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="tokenReviewerJwt"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipClassName="max-w-md"
|
||||
label="Token Reviewer JWT"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
tooltipText="Optional JWT token for accessing Kubernetes TokenReview API. If provided, this long-lived token will be used to validate service account tokens during authentication. If omitted, the client's own JWT will be used instead, which requires the client to have the system:auth-delegator ClusterRole binding."
|
||||
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<div className="w-full flex-1">
|
||||
<OrgPermissionCan
|
||||
I={OrgGatewayPermissionActions.AttachGateways}
|
||||
a={OrgPermissionSubjects.Gateway}
|
||||
>
|
||||
<Input {...field} placeholder="" type="password" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{(isAllowed) => (
|
||||
<Controller
|
||||
control={control}
|
||||
name="gatewayId"
|
||||
defaultValue=""
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
label="Gateway"
|
||||
isOptional
|
||||
>
|
||||
<Tooltip
|
||||
isDisabled={isAllowed}
|
||||
content="Restricted access. You don't have permission to attach gateways to resources."
|
||||
>
|
||||
<div>
|
||||
<Select
|
||||
isDisabled={!isAllowed}
|
||||
value={value as string}
|
||||
onValueChange={(v) => {
|
||||
if (v !== "") {
|
||||
onChange(v);
|
||||
}
|
||||
|
||||
if (v === null) {
|
||||
setValue(
|
||||
"tokenReviewMode",
|
||||
IdentityKubernetesAuthTokenReviewMode.Api,
|
||||
{
|
||||
shouldDirty: true,
|
||||
shouldTouch: true
|
||||
}
|
||||
);
|
||||
}
|
||||
}}
|
||||
className="w-full border border-mineshaft-500"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
isLoading={isGatewayLoading}
|
||||
placeholder="Default: Internet Gateway"
|
||||
position="popper"
|
||||
>
|
||||
<SelectItem
|
||||
value={null as unknown as string}
|
||||
onClick={() => {
|
||||
onChange(null);
|
||||
}}
|
||||
>
|
||||
Internet Gateway
|
||||
</SelectItem>
|
||||
{gateways?.map((el) => (
|
||||
<SelectItem value={el.id} key={el.id}>
|
||||
{el.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="tokenReviewMode"
|
||||
render={({ field }) => (
|
||||
<FormControl
|
||||
tooltipClassName="max-w-md"
|
||||
tooltipText="The method of which tokens are reviewed. If you select Gateway as Reviewer, the selected gateway will be used to review tokens with. If this option is enabled, the gateway must be deployed in Kubernetes, and the gateway must have the system:auth-delegator ClusterRole binding."
|
||||
label="Review Mode"
|
||||
>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<SelectItem value="gateway">Gateway as Reviewer</SelectItem>
|
||||
<SelectItem value="api">Manual Token Reviewer JWT (API)</SelectItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{tokenReviewMode === "api" && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="tokenReviewerJwt"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipClassName="max-w-md"
|
||||
label="Token Reviewer JWT"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
tooltipText="Optional JWT token for accessing Kubernetes TokenReview API. If provided, this long-lived token will be used to validate service account tokens during authentication. If omitted, the client's own JWT will be used instead, which requires the client to have the system:auth-delegator ClusterRole binding."
|
||||
>
|
||||
<Input {...field} placeholder="" type="password" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
@@ -300,61 +417,6 @@ export const IdentityKubernetesAuthForm = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
<OrgPermissionCan
|
||||
I={OrgGatewayPermissionActions.AttachGateways}
|
||||
a={OrgPermissionSubjects.Gateway}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Controller
|
||||
control={control}
|
||||
name="gatewayId"
|
||||
defaultValue=""
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
label="Gateway"
|
||||
isOptional
|
||||
>
|
||||
<Tooltip
|
||||
isDisabled={isAllowed}
|
||||
content="Restricted access. You don't have permission to attach gateways to resources."
|
||||
>
|
||||
<div>
|
||||
<Select
|
||||
isDisabled={!isAllowed}
|
||||
value={value as string}
|
||||
onValueChange={(v) => {
|
||||
if (v !== "") {
|
||||
onChange(v);
|
||||
}
|
||||
}}
|
||||
className="w-full border border-mineshaft-500"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
isLoading={isGatewayLoading}
|
||||
placeholder="Default: Internet Gateway"
|
||||
position="popper"
|
||||
>
|
||||
<SelectItem
|
||||
value={null as unknown as string}
|
||||
onClick={() => onChange(null)}
|
||||
>
|
||||
Internet Gateway
|
||||
</SelectItem>
|
||||
{gateways?.map((el) => (
|
||||
<SelectItem value={el.id} key={el.id}>
|
||||
{el.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="allowedNames"
|
||||
|
||||
Reference in New Issue
Block a user