Compare commits

...

26 Commits

Author SHA1 Message Date
x032205
899b7fe024 Log available auth methods on password reset 2025-08-04 16:16:52 -04:00
Daniel Hougaard
e852cd8b4a Merge pull request #4287 from cyrgim/add-support-image-pull-secret
feat(helm): add support for imagePullSecrets
2025-08-04 23:36:23 +04:00
x032205
5a3325fc53 Merge pull request #4308 from Infisical/fix-github-hostname-check
fix github hostname check
2025-08-04 14:37:31 -04:00
Akhil Mohan
da6b233db1 Merge pull request #4307 from Infisical/helm-update-v0.9.5
Update Helm chart to version v0.9.5
2025-08-04 23:57:23 +05:30
x032205
6958f1cfbd fix github hostname check 2025-08-04 14:24:09 -04:00
akhilmhdh
adf7a88d67 Update Helm chart to version v0.9.5 2025-08-04 18:22:44 +00:00
Akhil Mohan
b8cd836225 Merge pull request #4296 from Infisical/feat/operator-ldap
feat: ldap auth for k8s operator
2025-08-04 23:46:19 +05:30
=
6826b1c242 feat: made review changed 2025-08-04 23:36:05 +05:30
Daniel Hougaard
35012fde03 fix: added ldap identity auth example 2025-08-04 21:57:07 +04:00
x032205
6e14b2f793 Merge pull request #4306 from Infisical/log-github-error
log github error
2025-08-04 13:48:38 -04:00
x032205
5a3aa3d608 log github error 2025-08-04 13:42:00 -04:00
Daniel Hougaard
95b327de50 Merge pull request #4299 from Infisical/daniel/injector-ldap-auth-docs
docs(agent-injector): ldap auth method
2025-08-04 21:26:27 +04:00
Scott Wilson
a3c36f82f3 Merge pull request #4305 from Infisical/add-react-import-to-email-components
fix: add react import to email button component
2025-08-04 10:22:10 -07:00
Scott Wilson
42612da57d Merge pull request #4293 from Infisical/minor-ui-feedback
improvements: adjust secret search padding when no clear icon and fix access approval reviewer tooltips display
2025-08-04 10:20:32 -07:00
x032205
98a08d136e Merge pull request #4302 from Infisical/fix-timeout-for-audit-prune
Add timeout to audit log
2025-08-04 12:28:48 -04:00
x032205
6c74b875f3 up to 10 mins 2025-08-04 10:46:10 -04:00
x032205
793cd4c144 Add timeout to audit log 2025-08-04 10:43:25 -04:00
Sid
ec0be1166f feat: Secret reminder from date filter (#4289)
* feat: add fromDate in reminders

* feat: update reminder form

* fix: lint

* chore: generate schema

* fix: reminder logic

* fix: update ui

* fix: pr change

---------

Co-authored-by: sidwebworks <xodeveloper@gmail.com>
2025-08-03 01:10:23 +05:30
Daniel Hougaard
899d01237c docs(agent-injector): ldap auth method 2025-08-02 19:43:27 +04:00
=
4b9e57ae61 feat: review changes for reptile 2025-08-01 21:10:26 +05:30
Akhil Mohan
eb27983990 Update k8-operator/packages/util/kubernetes.go
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-08-01 21:08:33 +05:30
=
fa311b032c feat: removed comments 2025-08-01 21:06:17 +05:30
=
71651f85fe docs: ldap auth in operator 2025-08-01 21:04:44 +05:30
=
d28d3449de feat: added ldap authentication to operator 2025-08-01 21:04:29 +05:30
Scott Wilson
4f26365c21 improvements: adjust secret search padding when no clear icon and fix access approval reviewer tooltips 2025-07-31 19:58:26 -07:00
cyrgim
4704774c63 feat(helm): add support for imagePullSecrets 2025-07-31 07:01:51 +02:00
40 changed files with 1118 additions and 496 deletions

View File

@@ -0,0 +1,19 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.Reminder, "fromDate"))) {
await knex.schema.alterTable(TableName.Reminder, (t) => {
t.timestamp("fromDate", { useTz: true }).nullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.Reminder, "fromDate")) {
await knex.schema.alterTable(TableName.Reminder, (t) => {
t.dropColumn("fromDate");
});
}
}

View File

@@ -14,7 +14,8 @@ export const RemindersSchema = z.object({
repeatDays: z.number().nullable().optional(),
nextReminderDate: z.date(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
fromDate: z.date().nullable().optional()
});
export type TReminders = z.infer<typeof RemindersSchema>;

View File

@@ -152,42 +152,54 @@ export const auditLogDALFactory = (db: TDbClient) => {
// delete all audit log that have expired
const pruneAuditLog: TAuditLogDALFactory["pruneAuditLog"] = async (tx) => {
const AUDIT_LOG_PRUNE_BATCH_SIZE = 10000;
const MAX_RETRY_ON_FAILURE = 3;
const runPrune = async (dbClient: knex.Knex) => {
const AUDIT_LOG_PRUNE_BATCH_SIZE = 10000;
const MAX_RETRY_ON_FAILURE = 3;
const today = new Date();
let deletedAuditLogIds: { id: string }[] = [];
let numberOfRetryOnFailure = 0;
let isRetrying = false;
const today = new Date();
let deletedAuditLogIds: { id: string }[] = [];
let numberOfRetryOnFailure = 0;
let isRetrying = false;
logger.info(`${QueueName.DailyResourceCleanUp}: audit log started`);
do {
try {
const findExpiredLogSubQuery = (tx || db)(TableName.AuditLog)
.where("expiresAt", "<", today)
.where("createdAt", "<", today) // to use audit log partition
.orderBy(`${TableName.AuditLog}.createdAt`, "desc")
.select("id")
.limit(AUDIT_LOG_PRUNE_BATCH_SIZE);
logger.info(`${QueueName.DailyResourceCleanUp}: audit log started`);
do {
try {
const findExpiredLogSubQuery = dbClient(TableName.AuditLog)
.where("expiresAt", "<", today)
.where("createdAt", "<", today) // to use audit log partition
.orderBy(`${TableName.AuditLog}.createdAt`, "desc")
.select("id")
.limit(AUDIT_LOG_PRUNE_BATCH_SIZE);
// eslint-disable-next-line no-await-in-loop
deletedAuditLogIds = await (tx || db)(TableName.AuditLog)
.whereIn("id", findExpiredLogSubQuery)
.del()
.returning("id");
numberOfRetryOnFailure = 0; // reset
} catch (error) {
numberOfRetryOnFailure += 1;
logger.error(error, "Failed to delete audit log on pruning");
} finally {
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 10); // time to breathe for db
});
}
isRetrying = numberOfRetryOnFailure > 0;
} while (deletedAuditLogIds.length > 0 || (isRetrying && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE));
logger.info(`${QueueName.DailyResourceCleanUp}: audit log completed`);
// eslint-disable-next-line no-await-in-loop
deletedAuditLogIds = await dbClient(TableName.AuditLog)
.whereIn("id", findExpiredLogSubQuery)
.del()
.returning("id");
numberOfRetryOnFailure = 0; // reset
} catch (error) {
numberOfRetryOnFailure += 1;
logger.error(error, "Failed to delete audit log on pruning");
} finally {
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 10); // time to breathe for db
});
}
isRetrying = numberOfRetryOnFailure > 0;
} while (deletedAuditLogIds.length > 0 || (isRetrying && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE));
logger.info(`${QueueName.DailyResourceCleanUp}: audit log completed`);
};
if (tx) {
await runPrune(tx);
} else {
const QUERY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
await db.transaction(async (trx) => {
await trx.raw(`SET statement_timeout = ${QUERY_TIMEOUT_MS}`);
await runPrune(trx);
});
}
};
const create: TAuditLogDALFactory["create"] = async (tx) => {

View File

@@ -22,6 +22,7 @@ export const registerSecretReminderRouter = async (server: FastifyZodProvider) =
message: z.string().trim().max(1024).optional(),
repeatDays: z.number().min(1).nullable().optional(),
nextReminderDate: z.string().datetime().nullable().optional(),
fromDate: z.string().datetime().nullable().optional(),
recipients: z.string().array().optional()
})
.refine((data) => {
@@ -45,6 +46,7 @@ export const registerSecretReminderRouter = async (server: FastifyZodProvider) =
message: req.body.message,
repeatDays: req.body.repeatDays,
nextReminderDate: req.body.nextReminderDate,
fromDate: req.body.fromDate,
recipients: req.body.recipients
}
});

View File

@@ -35,7 +35,7 @@ export const getGitHubInstanceApiUrl = async (config: {
}) => {
const host = config.credentials.host || "github.com";
await blockLocalAndPrivateIpAddresses(host);
await blockLocalAndPrivateIpAddresses(`https://${host}`);
let apiBase: string;
if (config.credentials.instanceType === "server") {
@@ -348,6 +348,8 @@ export const validateGitHubConnectionCredentials = async (
});
}
} catch (e: unknown) {
logger.error(e, "Unable to verify GitHub connection");
if (e instanceof BadRequestError) {
throw e;
}

View File

@@ -193,6 +193,10 @@ export const authPaswordServiceFactory = ({
}
if (!user.authMethods?.includes(AuthMethod.EMAIL)) {
logger.error(
{ authMethods: user.authMethods },
"Unable to reset password, no email authentication method is configured"
);
throw new BadRequestError({ message: "Unable to reset password, no email authentication method is configured" });
}

View File

@@ -79,25 +79,33 @@ export const reminderServiceFactory = ({
repeatDays,
nextReminderDate: nextReminderDateInput,
recipients,
projectId
projectId,
fromDate: fromDateInput
}: {
secretId?: string;
message?: string | null;
repeatDays?: number | null;
nextReminderDate?: string | null;
recipients?: string[] | null;
fromDate?: string | null;
projectId: string;
}) => {
if (!secretId) {
throw new BadRequestError({ message: "secretId is required" });
}
let nextReminderDate;
let fromDate;
if (nextReminderDateInput) {
nextReminderDate = new Date(nextReminderDateInput);
}
if (repeatDays && repeatDays > 0) {
nextReminderDate = $addDays(repeatDays);
if (repeatDays) {
if (fromDateInput) {
fromDate = new Date(fromDateInput);
nextReminderDate = fromDate;
} else {
nextReminderDate = $addDays(repeatDays);
}
}
if (!nextReminderDate) {
@@ -112,7 +120,8 @@ export const reminderServiceFactory = ({
await reminderDAL.updateById(existingReminder.id, {
message,
repeatDays,
nextReminderDate
nextReminderDate,
fromDate
});
reminderId = existingReminder.id;
} else {
@@ -121,7 +130,8 @@ export const reminderServiceFactory = ({
secretId,
message,
repeatDays,
nextReminderDate
nextReminderDate,
fromDate
});
reminderId = newReminder.id;
}
@@ -280,14 +290,28 @@ export const reminderServiceFactory = ({
}
const processedReminders = remindersData.map(
({ secretId, message, repeatDays, nextReminderDate: nextReminderDateInput, recipients, projectId }) => {
({
secretId,
message,
repeatDays,
nextReminderDate: nextReminderDateInput,
recipients,
projectId,
fromDate: fromDateInput
}) => {
let nextReminderDate;
let fromDate;
if (nextReminderDateInput) {
nextReminderDate = new Date(nextReminderDateInput);
}
if (repeatDays && repeatDays > 0 && !nextReminderDate) {
nextReminderDate = $addDays(repeatDays);
if (repeatDays && !nextReminderDate) {
if (fromDateInput) {
fromDate = new Date(fromDateInput);
nextReminderDate = fromDate;
} else {
nextReminderDate = $addDays(repeatDays);
}
}
if (!nextReminderDate) {
@@ -302,17 +326,19 @@ export const reminderServiceFactory = ({
repeatDays,
nextReminderDate,
recipients: recipients ? [...new Set(recipients)] : [],
projectId
projectId,
fromDate
};
}
);
const newReminders = await reminderDAL.insertMany(
processedReminders.map(({ secretId, message, repeatDays, nextReminderDate }) => ({
processedReminders.map(({ secretId, message, repeatDays, nextReminderDate, fromDate }) => ({
secretId,
message,
repeatDays,
nextReminderDate
nextReminderDate,
fromDate
})),
tx
);

View File

@@ -8,6 +8,7 @@ export type TReminder = {
message?: string | null;
repeatDays?: number | null;
nextReminderDate: Date;
fromDate?: Date | null;
createdAt: Date;
updatedAt: Date;
};
@@ -21,6 +22,7 @@ export type TCreateReminderDTO = {
secretId?: string;
message?: string | null;
repeatDays?: number | null;
fromDate?: string | null;
nextReminderDate?: string | null;
recipients?: string[] | null;
};
@@ -31,6 +33,7 @@ export type TBatchCreateReminderDTO = {
message?: string | null;
repeatDays?: number | null;
nextReminderDate?: string | Date | null;
fromDate?: Date | null;
recipients?: string[] | null;
projectId?: string;
}[];
@@ -95,6 +98,7 @@ export interface TReminderServiceFactory {
nextReminderDate?: string | null;
recipients?: string[] | null;
projectId: string;
fromDate?: string | null;
}) => Promise<{
id: string;
created: boolean;

View File

@@ -105,44 +105,93 @@ The templates hold an array of templates that will be rendered and injected into
### Authentication
The Infisical Agent Injector only supports Machine Identity [Kubernetes Auth](/documentation/platform/identities/kubernetes-auth) authentication at the moment.
The Infisical Agent Injector supports Machine Identity [Kubernetes Auth](/documentation/platform/identities/kubernetes-auth) and [LDAP Auth](/documentation/platform/identities/ldap-auth) authentication.
To configure Kubernetes Auth, you need to set the `auth.type` field to `kubernetes` and set the `auth.config.identity-id` to the ID of the machine identity you wish to use for authentication.
```yaml
auth:
type: "kubernetes"
config:
identity-id: "<your-infisical-machine-identity-id>"
```
<AccordionGroup>
<Accordion title="Kubernetes Auth">
### Example ConfigMap
```yaml config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-config-map
data:
config.yaml: |
infisical:
address: "https://app.infisical.com"
auth:
type: "kubernetes"
config:
identity-id: "<your-infisical-machine-identity-id>"
templates:
- destination-path: "/path/to/save/secrets/file.txt"
template-content: |
{{- with secret "<your-project-id>" "dev" "/" }}
{{- range . }}
{{ .Key }}={{ .Value }}
{{- end }}
{{- end }}
```
To configure Kubernetes Auth, you need to set the `auth.type` field to `kubernetes` and set the `auth.config.identity-id` to the ID of the machine identity you wish to use for authentication.
```yaml
auth:
type: "kubernetes"
config:
identity-id: "<your-infisical-machine-identity-id>"
```
```bash
kubectl apply -f config-map.yaml
```
### Example ConfigMap
```yaml config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-config-map
data:
config.yaml: |
infisical:
address: "https://app.infisical.com"
auth:
type: "kubernetes"
config:
identity-id: "<your-infisical-machine-identity-id>"
templates:
- destination-path: "/path/to/save/secrets/file.txt"
template-content: |
{{- with secret "<your-project-id>" "dev" "/" }}
{{- range . }}
{{ .Key }}={{ .Value }}
{{- end }}
{{- end }}
```
```bash
kubectl apply -f config-map.yaml
```
</Accordion>
<Accordion title="LDAP Auth">
To configure LDAP Auth, you need to set the `auth.type` field to `ldap-auth` and set the `auth.config.identity-id` to the ID of the machine identity you wish to use for authentication. Configure the `auth.config.username` and `auth.config.password` to the username and password of the LDAP user to authenticate with.
```yaml
auth:
type: "ldap-auth"
config:
identity-id: "<your-infisical-machine-identity-id>"
username: "<your-ldap-username>"
password: "<your-ldap-password>"
```
### Example ConfigMap
```yaml config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-config-map
data:
config.yaml: |
infisical:
address: "https://app.infisical.com"
auth:
type: "ldap-auth"
config:
identity-id: "<your-infisical-machine-identity-id>"
username: "<your-ldap-username>"
password: "<your-ldap-password>"
templates:
- destination-path: "/path/to/save/secrets/file.txt"
template-content: |
{{- with secret "<your-project-id>" "dev" "/" }}
{{- range . }}
{{ .Key }}={{ .Value }}
{{- end }}
{{- end }}
```
```bash
kubectl apply -f config-map.yaml
```
</Accordion>
</AccordionGroup>
To use the config map in your pod, you will need to add the `org.infisical.com/agent-config-map` annotation to your pod's deployment. The value of the annotation is the name of the config map you created above.
```yaml

View File

@@ -68,6 +68,11 @@ spec:
serviceAccountKeyFilePath: </path-to-service-account-key-file.json>
gcpIdTokenAuth:
identityId: <machine-identity-id>
ldapAuth:
identityId: <machine-identity-id>
credentialsRef:
secretName: <secret-name> # ldap-auth-credentials
secretNamespace: <secret-namespace> # default
kubernetesAuth:
identityId: <machine-identity-id>
serviceAccountRef:
@@ -105,15 +110,15 @@ kind: Secret
### InfisicalDynamicSecret CRD properties
<Accordion title="hostAPI">
If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to
` https://your-self-hosted-instace.com/api`
If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to
`https://your-self-hosted-instace.com/api`
When `hostAPI` is not defined the operator fetches secrets from Infisical Cloud.
<Accordion title="Advanced use case">
If you have installed your Infisical instance within the same cluster as the Infisical operator, you can optionally access the Infisical backend's service directly without having to route through the public internet.
If you have installed your Infisical instance within the same cluster as the Infisical operator, you can optionally access the Infisical backend's service directly without having to route through the public internet.
To achieve this, use the following address for the hostAPI field:
``` bash
http://<backend-svc-name>.<namespace>.svc.cluster.local:4000/api
```
@@ -126,18 +131,19 @@ When `hostAPI` is not defined the operator fetches secrets from Infisical Cloud.
<Accordion title="leaseTTL">
The `leaseTTL` is a string-formatted duration that defines the time the lease should last for the dynamic secret.
The format of the field is `[duration][unit]` where `duration` is a number and `unit` is a string representing the unit of time.
The format of the field is `[duration][unit]` where `duration` is a number and `unit` is a string representing the unit of time.
The following units are supported:
The following units are supported:
- `s` for seconds (must be at least 5 seconds)
- `m` for minutes
- `h` for hours
- `d` for days
- `s` for seconds (must be at least 5 seconds)
- `m` for minutes
- `h` for hours
- `d` for days
<Note>
The lease duration at most be 1 day (24 hours). And the TTL must be less than the max TTL defined on the dynamic secret.
</Note>
<Note>
The lease duration at most be 1 day (24 hours). And the TTL must be less than the max TTL defined on the dynamic secret.
</Note>
</Accordion>
<Accordion title="managedSecretReference">
@@ -212,7 +218,7 @@ spec:
<Accordion title="dynamicSecret">
The `dynamicSecret` field is used to specify which dynamic secret to create leases for. The required fields are `secretName`, `projectId`, `secretsPath`, and `environmentSlug`.
```yaml
spec:
dynamicSecret:
@@ -300,7 +306,6 @@ The available authentication methods are `universalAuth`, `kubernetesAuth`, `aws
- `autoCreateServiceAccountToken`: If set to `true`, the operator will automatically create a short-lived service account token on-demand for the service account. Defaults to `false`.
- `serviceAccountTokenAudiences`: Optionally specify audience for the service account token. This field is only relevant if you have set `autoCreateServiceAccountToken` to `true`. No audience is specified by default.
Example:
```yaml
@@ -316,7 +321,40 @@ The available authentication methods are `universalAuth`, `kubernetesAuth`, `aws
```
</Accordion>
<Accordion title="ldapAuth">
The LDAP machine identity authentication method is used to authenticate with a configured LDAP directory. [Read more about LDAP Auth](/documentation/platform/identities/ldap-auth).
Valid fields:
- `identityId`: The identity ID of the machine identity you created.
- `credentialsRef`: The name and namespace of the Kubernetes secret that stores the LDAP credentials.
- `credentialsRef.secretName`: The name of the Kubernetes secret.
- `credentialsRef.secretNamespace`: The namespace of the Kubernetes secret.
Example:
```yaml
# infisical-push-secret.yaml
spec:
ldapAuth:
identityId: <machine-identity-id>
credentialsRef:
secretName: <secret-name>
secretNamespace: <secret-namespace>
```
```yaml
# machine-identity-credentials.yaml
apiVersion: v1
kind: Secret
metadata:
name: ldap-auth-credentials
type: Opaque
stringData:
username: <ldap-username>
password: <ldap-password>
```
</Accordion>
<Accordion title="awsIamAuth">
The AWS IAM machine identity authentication method is used to authenticate with Infisical.
[Read more about AWS IAM Auth](/documentation/platform/identities/aws-auth).
@@ -391,7 +429,7 @@ The available authentication methods are `universalAuth`, `kubernetesAuth`, `aws
<Accordion title="tls">
This block defines the TLS settings to use for connecting to the Infisical
instance.
Fields:
<Accordion title="caRef">
This block defines the reference to the CA certificate to use for connecting to the Infisical instance with SSL/TLS.
@@ -444,7 +482,7 @@ metadata:
name: nginx-deployment
labels:
app: nginx
annotations:
annotations:
secrets.infisical.com/auto-reload: "true" # <- redeployment annotation
spec:
replicas: 1
@@ -467,7 +505,7 @@ spec:
```
</Accordion>
<Info>
#### How it works
When the lease changes, the operator will check to see which deployments are using the operator-managed Kubernetes secret that received the update.
#### How it works
When the lease changes, the operator will check to see which deployments are using the operator-managed Kubernetes secret that received the update.
Then, for each deployment that has this annotation present, a rolling update will be triggered. A redeployment won't happen if the lease is renewed, only if it's recreated.
</Info>

View File

@@ -5,9 +5,9 @@ description: "Learn how to use the InfisicalPushSecret CRD to push and manage se
---
## Overview
## Overview
The **InfisicalPushSecret** CRD allows you to create secrets in your Kubernetes cluster and push them to Infisical.
The **InfisicalPushSecret** CRD allows you to create secrets in your Kubernetes cluster and push them to Infisical.
This CRD offers the following features:
@@ -70,6 +70,11 @@ Before applying the InfisicalPushSecret CRD, you need to create a Kubernetes sec
serviceAccountRef:
name: <secret-name>
namespace: <secret-namespace>
ldapAuth:
identityId: <machine-identity-id>
credentialsRef:
secretName: <secret-name> # ldap-auth-credentials
secretNamespace: <secret-namespace> # default
universalAuth:
credentialsRef:
secretName: <secret-name> # universal-auth-credentials
@@ -104,15 +109,15 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
## InfisicalPushSecret CRD properties
<Accordion title="hostAPI">
If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to
` https://your-self-hosted-instace.com/api`
If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to
`https://your-self-hosted-instace.com/api`
When `hostAPI` is not defined the operator fetches secrets from Infisical Cloud.
<Accordion title="Advanced use case">
If you have installed your Infisical instance within the same cluster as the Infisical operator, you can optionally access the Infisical backend's service directly without having to route through the public internet.
If you have installed your Infisical instance within the same cluster as the Infisical operator, you can optionally access the Infisical backend's service directly without having to route through the public internet.
To achieve this, use the following address for the hostAPI field:
``` bash
http://<backend-svc-name>.<namespace>.svc.cluster.local:4000/api
```
@@ -187,7 +192,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
<Accordion title="destination">
The `destination` field is used to specify where you want to create the secrets in Infisical. The required fields are `projectId`, `environmentSlug`, and `secretsPath`.
```yaml
spec:
destination:
@@ -212,7 +217,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
<Accordion title="push">
The `push` field is used to define what you want to push to Infisical. Currently the operator only supports pushing Kubernetes secrets to Infisical. An example of the `push` field is shown below.
<Accordion title="secret">
@@ -220,7 +225,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
Example usage of the `push.secret` field:
Example usage of the `push.secret` field:
```yaml infisical-push-secret.yaml
push:
@@ -282,7 +287,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
spec:
universalAuth:
credentialsRef:
secretName: <secret-name>
secretName: <secret-name>
secretNamespace: <secret-namespace>
```
@@ -324,7 +329,39 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
namespace: <secret-namespace>
```
</Accordion>
<Accordion title="ldapAuth">
The LDAP machine identity authentication method is used to authenticate with a configured LDAP directory. [Read more about LDAP Auth](/documentation/platform/identities/ldap-auth).
Valid fields:
- `identityId`: The identity ID of the machine identity you created.
- `credentialsRef`: The name and namespace of the Kubernetes secret that stores the LDAP credentials.
- `credentialsRef.secretName`: The name of the Kubernetes secret.
- `credentialsRef.secretNamespace`: The namespace of the Kubernetes secret.
Example:
```yaml
# infisical-push-secret.yaml
spec:
ldapAuth:
identityId: <machine-identity-id>
credentialsRef:
secretName: <secret-name>
secretNamespace: <secret-namespace>
```
```yaml
# machine-identity-credentials.yaml
apiVersion: v1
kind: Secret
metadata:
name: ldap-auth-credentials
type: Opaque
stringData:
username: <ldap-username>
password: <ldap-password>
```
</Accordion>
<Accordion title="awsIamAuth">
The AWS IAM machine identity authentication method is used to authenticate with Infisical.
[Read more about AWS IAM Auth](/documentation/platform/identities/aws-auth).
@@ -398,7 +435,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
<Accordion title="tls">
This block defines the TLS settings to use for connecting to the Infisical
instance.
Fields:
<Accordion title="caRef">
This block defines the reference to the CA certificate to use for connecting to the Infisical instance with SSL/TLS.
@@ -438,7 +475,7 @@ Using Go templates, you can format, combine, and create new key-value pairs of s
Use this option when you would like to push **only** a subset of secrets from the Kubernetes secret to Infisical.
</Accordion>
<Accordion title="push.secret.template.data">
Define secret keys and their corresponding templates.
Define secret keys and their corresponding templates.
Each data value uses a Golang template with access to all secrets defined in the `push.secret.secretName` Kubernetes secret.
Secrets are structured as follows:
@@ -483,7 +520,7 @@ A generator is defined as a custom resource (`ClusterGenerator`) within the clus
Because of this behavior, you may want to disable automatic syncing for the `InfisicalPushSecret` resource to avoid continuous regeneration of secrets. This can be done by omitting the `resyncInterval` field from the InfisicalPushSecret CRD.
### Example usage
### Example usage
```yaml
push:
secret:
@@ -625,4 +662,4 @@ After applying, you should notice that the secrets have been pushed to Infisical
```bash
kubectl apply -f source-push-secret.yaml # The secret that you're referencing in the InfisicalPushSecret CRD push.secret field
kubectl apply -f example-infisical-push-secret-crd.yaml # The InfisicalPushSecret CRD itself
```
```

View File

@@ -44,15 +44,15 @@ spec:
The following properties help define what instance of Infisical the operator will interact with, the interval it will sync secrets and any CA certificates that may be required to connect.
<Accordion title="hostAPI">
If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to
If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to
` https://your-self-hosted-instace.com/api`
When `hostAPI` is not defined the operator fetches secrets from Infisical Cloud.
<Accordion title="Advanced use case">
If you have installed your Infisical instance within the same cluster as the Infisical operator, you can optionally access the Infisical backend's service directly without having to route through the public internet.
If you have installed your Infisical instance within the same cluster as the Infisical operator, you can optionally access the Infisical backend's service directly without having to route through the public internet.
To achieve this, use the following address for the hostAPI field:
``` bash
http://<backend-svc-name>.<namespace>.svc.cluster.local:4000/api
```
@@ -110,7 +110,7 @@ The list of available authentication methods are shown below.
<Step title="Create Kubernetes secret containing machine identity credentials">
Once you have created your machine identity and added it to your project(s), you will need to create a Kubernetes secret containing the identity credentials.
To quickly create a Kubernetes secret containing the identity credentials, you can run the command below.
Make sure you replace `<your-identity-client-id>` with the identity client ID and `<your-identity-client-secret>` with the identity client secret.
``` bash
@@ -525,49 +525,6 @@ spec:
...
```
</Tab>
</Tabs>
@@ -747,6 +704,59 @@ spec:
</Accordion>
<Accordion title="authentication.ldapAuth">
The LDAP machine identity authentication method is used to authenticate with Infisical using the configured LDAP directory. The username and password needs to be stored in a Kubernetes secret. This block defines the reference to the name and namespace of secret that stores these credentials.
<Steps>
<Step title="Create a machine identity">
You need to create a machine identity, and give it access to the project(s) you want to interact with. You can [read more about machine identities here](/documentation/platform/identities/universal-auth).
</Step>
<Step title="Create Kubernetes secret containing machine identity credentials">
Once you have created your machine identity and added it to your project(s), you will need to create a Kubernetes secret containing the identity credentials.
To quickly create a Kubernetes secret containing the identity credentials, you can run the command below.
Make sure you replace `<your-identity-ldap-username>` with the identity LDAP username and `<your-identity-ldap-password>` with the identity LDAP password.
``` bash
kubectl create secret generic ldap-auth-credentials --from-literal=username="<your-identity-ldap-username>" --from-literal=password="<your-identity-ldap-password>"
```
</Step>
<Step title="Add reference for the Kubernetes secret containing the identity credentials">
Once the secret is created, add the `secretName` and `secretNamespace` of the secret that was just created under `authentication.ldapAuth.credentialsRef` field in the InfisicalSecret resource.
</Step>
</Steps>
<Info>
Make sure to also populate the `secretsScope` field with the project slug
_`projectSlug`_, environment slug _`envSlug`_, and secrets path
_`secretsPath`_ that you want to fetch secrets from. Please see the example
below.
</Info>
## Example
```yaml
apiVersion: secrets.infisical.com/v1alpha1
kind: InfisicalSecret
metadata:
name: infisicalsecret-sample-crd
spec:
authentication:
ldapAuth:
secretsScope:
projectSlug: <project-slug> # <-- project slug
envSlug: <env-slug> # "dev", "staging", "prod", etc..
secretsPath: "<secrets-path>" # Root is "/"
identityId: <machine-identity-id>
credentialsRef:
secretName: ldap-auth-credentials # <-- name of the Kubernetes secret that stores our machine identity credentials
secretNamespace: default # <-- namespace of the Kubernetes secret that stores our machine identity credentials
```
</Accordion>
<Accordion title="authentication.serviceToken">
The service token required to authenticate with Infisical needs to be stored in a Kubernetes secret. This block defines the reference to the name and namespace of secret that stores this service token.
@@ -826,7 +836,7 @@ managedKubeSecretReferences:
The name of the managed Kubernetes secret to be created
</Accordion>
<Accordion title="managedKubeSecretReferences[].secretNamespace">
The namespace of the managed Kubernetes secret to be created.
The namespace of the managed Kubernetes secret to be created.
</Accordion>
<Accordion title="managedKubeSecretReferences[].secretType">
Override the default Opaque type for managed secrets with this field. Useful for creating kubernetes.io/dockerconfigjson secrets.
@@ -855,8 +865,8 @@ Using Go templates, you can format, combine, and create new key-value pairs from
<Accordion title="managedKubeSecretReferences[].template">
</Accordion>
<Accordion title="managedKubeSecretReferences[].template.includeAllSecrets">
This property controls what secrets are included in your managed secret when using templates.
When set to `true`, all secrets fetched from your Infisical project will be added into your managed Kubernetes secret resource.
This property controls what secrets are included in your managed secret when using templates.
When set to `true`, all secrets fetched from your Infisical project will be added into your managed Kubernetes secret resource.
**Use this option when you would like to sync all secrets from Infisical to Kubernetes but want to template a subset of them.**
When set to `false`, only secrets defined in the `managedKubeSecretReferences[].template.data` field of the template will be included in the managed secret.
@@ -864,7 +874,7 @@ Use this option when you would like to sync **only** a subset of secrets from In
</Accordion>
<Accordion title="managedKubeSecretReferences[].template.data">
Define secret keys and their corresponding templates.
Define secret keys and their corresponding templates.
Each data value uses a Golang template with access to all secrets retrieved from the specified scope.
Secrets are structured as follows:
@@ -928,7 +938,9 @@ The properties includes defining the name and namespace of the Kubernetes config
The Infisical operator will automatically create the Kubernetes config map in the specified name/namespace and ensure it stays up-to-date. If a config map already exists in the specified namespace, the operator will update the existing config map with the new data.
<Warning>
The usage of config maps is only intended for storing non-sensitive data. If you are looking to store sensitive data, please use the [managed secret](#operator-managed-secrets) property instead.
The usage of config maps is only intended for storing non-sensitive data. If
you are looking to store sensitive data, please use the [managed
secret](#operator-managed-secrets) property instead.
</Warning>
<Accordion title="managedKubeConfigMapReferences">
@@ -937,25 +949,24 @@ The Infisical operator will automatically create the Kubernetes config map in th
The name of the managed Kubernetes config map that your Infisical data will be stored in.
</Accordion>
<Accordion title="managedKubeConfigMapReferences[].configMapNamespace">
The namespace of the managed Kubernetes config map that your Infisical data will be stored in.
The namespace of the managed Kubernetes config map that your Infisical data will be stored in.
</Accordion>
<Accordion title="managedKubeConfigMapReferences[].creationPolicy">
Creation policies allow you to control whether or not owner references should be added to the managed Kubernetes config map that is generated by the Infisical operator.
This is useful for tools such as ArgoCD, where every resource requires an owner reference; otherwise, it will be pruned automatically.
#### Available options
#### Available options
- `Orphan` (default)
- `Owner`
- `Orphan` (default)
- `Owner`
<Tip>
When creation policy is set to `Owner`, the `InfisicalSecret` CRD must be in
the same namespace as where the managed kubernetes config map.
</Tip>
<Tip>
When creation policy is set to `Owner`, the `InfisicalSecret` CRD must be in
the same namespace as where the managed kubernetes config map.
</Tip>
</Accordion>
#### Managed ConfigMap Templating
Fetching secrets from Infisical as is via the operator may not be enough. This is where templating functionality may be helpful.
@@ -964,67 +975,68 @@ Using Go templates, you can format, combine, and create new key-value pairs from
<Accordion title="managedKubeConfigMapReferences[].template">
</Accordion>
<Accordion title="managedKubeConfigMapReferences[].template.includeAllSecrets">
This property controls what secrets are included in your managed config map when using templates.
When set to `true`, all secrets fetched from your Infisical project will be added into your managed Kubernetes config map resource.
This property controls what secrets are included in your managed config map when using templates.
When set to `true`, all secrets fetched from your Infisical project will be added into your managed Kubernetes config map resource.
**Use this option when you would like to sync all secrets from Infisical to Kubernetes but want to template a subset of them.**
When set to `false`, only secrets defined in the `managedKubeConfigMapReferences[].template.data` field of the template will be included in the managed config map.
Use this option when you would like to sync **only** a subset of secrets from Infisical to Kubernetes.
When set to `false`, only secrets defined in the `managedKubeConfigMapReferences[].template.data` field of the template will be included in the managed config map.
Use this option when you would like to sync **only** a subset of secrets from Infisical to Kubernetes.
</Accordion>
<Accordion title="managedKubeConfigMapReferences[].template.data">
Define secret keys and their corresponding templates.
Define secret keys and their corresponding templates.
Each data value uses a Golang template with access to all secrets retrieved from the specified scope.
Secrets are structured as follows:
Secrets are structured as follows:
```golang
type TemplateSecret struct {
Value string `json:"value"`
SecretPath string `json:"secretPath"`
}
```
```golang
type TemplateSecret struct {
Value string `json:"value"`
SecretPath string `json:"secretPath"`
}
```
#### Example template configuration:
#### Example template configuration:
```yaml
managedKubeConfigMapReferences:
- configMapName: managed-configmap
configMapNamespace: default
template:
includeAllSecrets: true
data:
# Create new key that doesn't exist in your Infisical project using values of other secrets
SITE_URL: "{{ .SITE_URL.Value }}"
# Override an existing key in Infisical project with a new value using values of other secrets
API_URL: "https://api.{{.SITE_URL.Value}}.{{.REGION.Value}}.com"
```
```yaml
managedKubeConfigMapReferences:
- configMapName: managed-configmap
configMapNamespace: default
template:
includeAllSecrets: true
data:
# Create new key that doesn't exist in your Infisical project using values of other secrets
SITE_URL: "{{ .SITE_URL.Value }}"
# Override an existing key in Infisical project with a new value using values of other secrets
API_URL: "https://api.{{.SITE_URL.Value}}.{{.REGION.Value}}.com"
```
For this example, let's assume the following secrets exist in your Infisical project:
For this example, let's assume the following secrets exist in your Infisical project:
```
SITE_URL = "https://example.com"
REGION = "us-east-1"
API_URL = "old-url" # This will be overridden
```
```
SITE_URL = "https://example.com"
REGION = "us-east-1"
API_URL = "old-url" # This will be overridden
```
The resulting managed Kubernetes config map will then contain:
The resulting managed Kubernetes config map will then contain:
```
# Original config map data (from includeAllSecrets: true)
SITE_URL = "https://example.com"
REGION = "us-east-1"
```
# Original config map data (from includeAllSecrets: true)
SITE_URL = "https://example.com"
REGION = "us-east-1"
# New and overridden config map data
SITE_URL = "https://example.com"
API_URL = "https://api.example.com.us-east-1.com" # Existing secret overridden by template
```
# New and overridden config map data
SITE_URL = "https://example.com"
API_URL = "https://api.example.com.us-east-1.com" # Existing secret overridden by template
```
To help transform your config map data further, the operator provides a set of built-in functions that you can use in your templates.
To help transform your config map data further, the operator provides a set of built-in functions that you can use in your templates.
### Available templating functions
Please refer to the [templating functions documentation](/integrations/platforms/kubernetes/overview#available-helper-functions) for more information.
### Available templating functions
Please refer to the [templating functions documentation](/integrations/platforms/kubernetes/overview#available-helper-functions) for more information.
</Accordion>
## Applying CRD
@@ -1061,8 +1073,6 @@ To verify that the operator has successfully created the managed secret, you can
</Tab>
</Tabs>
## Using Managed Secret In Your Deployment
To make use of the managed secret created by the operator into your deployment can be achieved through several methods.
@@ -1071,45 +1081,45 @@ Here, we will highlight three of the most common ways to utilize it. Learn more
<Accordion title="envFrom">
This will take all the secrets from your managed secret and expose them to your container
````yaml
envFrom:
- secretRef:
name: managed-secret # managed secret name
```
````yaml
envFrom:
- secretRef:
name: managed-secret # managed secret name
```
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
template:
metadata:
labels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
envFrom:
- secretRef:
name: managed-secret # <- name of managed secret
ports:
- containerPort: 80
````
spec:
containers:
- name: nginx
image: nginx:1.14.2
envFrom:
- secretRef:
name: managed-secret # <- name of managed secret
ports:
- containerPort: 80
````
</Accordion>
<Accordion title="env">
This will allow you to select individual secrets by key name from your managed secret and expose them to your container
<Accordion title="env">
This will allow you to select individual secrets by key name from your managed secret and expose them to your container
```yaml
env:
- name: SECRET_NAME # The environment variable's name which is made available in the container
@@ -1119,37 +1129,38 @@ Here, we will highlight three of the most common ways to utilize it. Learn more
key: SOME_SECRET_KEY # The name of the key which exists in the managed secret
```
Example usage in a deployment
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
template:
metadata:
labels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
env:
- name: STRIPE_API_SECRET
valueFrom:
secretKeyRef:
name: managed-secret # <- name of managed secret
key: STRIPE_API_SECRET
ports:
- containerPort: 80
```
spec:
containers:
- name: nginx
image: nginx:1.14.2
env:
- name: STRIPE_API_SECRET
valueFrom:
secretKeyRef:
name: managed-secret # <- name of managed secret
key: STRIPE_API_SECRET
ports:
- containerPort: 80
```
</Accordion>
<Accordion title="volumes">
@@ -1161,48 +1172,48 @@ Here, we will highlight three of the most common ways to utilize it. Learn more
secretName: managed-secret # managed secret name
````
You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
```yaml
volumeMounts:
- name: secrets-volume-name
mountPath: /etc/secrets
readOnly: true
```
```yaml
volumeMounts:
- name: secrets-volume-name
mountPath: /etc/secrets
readOnly: true
```
Example usage in a deployment
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
template:
metadata:
labels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
volumeMounts:
- name: secrets-volume-name
mountPath: /etc/secrets
readOnly: true
ports:
- containerPort: 80
volumes:
- name: secrets-volume-name
secret:
secretName: managed-secret # <- managed secrets
```
spec:
containers:
- name: nginx
image: nginx:1.14.2
volumeMounts:
- name: secrets-volume-name
mountPath: /etc/secrets
readOnly: true
ports:
- containerPort: 80
volumes:
- name: secrets-volume-name
secret:
secretName: managed-secret # <- managed secrets
```
</Accordion>
@@ -1244,7 +1255,7 @@ secrets.infisical.com/auto-reload: "true"
name: nginx-deployment
labels:
app: nginx
annotations:
annotations:
secrets.infisical.com/auto-reload: "true" # <- redeployment annotation
spec:
replicas: 1
@@ -1339,9 +1350,11 @@ secrets.infisical.com/auto-reload: "true"
</Accordion>
<Info>
#### How it works
When a managed secret is updated, the operator checks for any Deployments, DaemonSets, or StatefulSets that consume the updated secret and have the annotation
`secrets.infisical.com/auto-reload: "true"`. For each matching workload, the operator triggers a rolling restart to ensure it picks up the latest secret values.
#### How it works When a managed secret is updated, the operator checks for
any Deployments, DaemonSets, or StatefulSets that consume the updated secret
and have the annotation `secrets.infisical.com/auto-reload: "true"`. For each
matching workload, the operator triggers a rolling restart to ensure it picks
up the latest secret values.
</Info>
## Using Managed ConfigMap In Your Deployment
@@ -1350,52 +1363,52 @@ To make use of the managed ConfigMap created by the operator into your deploymen
Here, we will highlight three of the most common ways to utilize it. Learn more about Kubernetes ConfigMaps [here](https://kubernetes.io/docs/concepts/configuration/configmap/)
<Tip>
Automatic redeployment of deployments using managed ConfigMaps is not yet supported.
Automatic redeployment of deployments using managed ConfigMaps is not yet
supported.
</Tip>
<Accordion title="envFrom">
This will take all the secrets from your managed ConfigMap and expose them to your container
````yaml
envFrom:
- configMapRef:
name: managed-configmap # managed configmap name
```
````yaml
envFrom:
- configMapRef:
name: managed-configmap # managed configmap name
```
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
template:
metadata:
labels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
envFrom:
- configMapRef:
name: managed-configmap # <- name of managed configmap
ports:
- containerPort: 80
````
spec:
containers:
- name: nginx
image: nginx:1.14.2
envFrom:
- configMapRef:
name: managed-configmap # <- name of managed configmap
ports:
- containerPort: 80
````
</Accordion>
<Accordion title="env">
This will allow you to select individual secrets by key name from your managed ConfigMap and expose them to your container
<Accordion title="env">
This will allow you to select individual secrets by key name from your managed ConfigMap and expose them to your container
```yaml
env:
- name: CONFIG_NAME # The environment variable's name which is made available in the container
@@ -1405,37 +1418,37 @@ Here, we will highlight three of the most common ways to utilize it. Learn more
key: SOME_CONFIG_KEY # The name of the key which exists in the managed configmap
```
Example usage in a deployment
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
template:
metadata:
labels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
env:
- name: STRIPE_API_SECRET
valueFrom:
configMapKeyRef:
name: managed-configmap # <- name of managed configmap
key: STRIPE_API_SECRET
ports:
- containerPort: 80
```
spec:
containers:
- name: nginx
image: nginx:1.14.2
env:
- name: STRIPE_API_SECRET
valueFrom:
configMapKeyRef:
name: managed-configmap # <- name of managed configmap
key: STRIPE_API_SECRET
ports:
- containerPort: 80
```
</Accordion>
@@ -1448,48 +1461,49 @@ Here, we will highlight three of the most common ways to utilize it. Learn more
name: managed-configmap # managed configmap name
````
You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
```yaml
volumeMounts:
- name: configmaps-volume-name
mountPath: /etc/config
readOnly: true
```
```yaml
volumeMounts:
- name: configmaps-volume-name
mountPath: /etc/config
readOnly: true
```
Example usage in a deployment
Example usage in a deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
template:
metadata:
labels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
volumeMounts:
- name: configmaps-volume-name
mountPath: /etc/config
readOnly: true
ports:
- containerPort: 80
volumes:
- name: configmaps-volume-name
configMap:
name: managed-configmap # <- managed configmap
```
spec:
containers:
- name: nginx
image: nginx:1.14.2
volumeMounts:
- name: configmaps-volume-name
mountPath: /etc/config
readOnly: true
ports:
- containerPort: 80
volumes:
- name: configmaps-volume-name
configMap:
name: managed-configmap # <- managed configmap
```
</Accordion>
The definition file of the Kubernetes secret for the CA certificate can be structured like the following:
@@ -1532,20 +1546,21 @@ Thus, if a specific label is required on the resulting secret, it can be applied
...
```
This would result in the following managed secret to be created:
This would result in the following managed secret to be created:
```yaml
apiVersion: v1
data: ...
kind: Secret
metadata:
annotations:
example.com/annotation-to-be-passed-to-managed-secret: sample-value
secrets.infisical.com/version: W/"3f1-ZyOSsrCLGSkAhhCkY2USPu2ivRw"
labels:
label-to-be-passed-to-managed-secret: sample-value
name: managed-token
namespace: default
type: Opaque
```
```yaml
apiVersion: v1
data: ...
kind: Secret
metadata:
annotations:
example.com/annotation-to-be-passed-to-managed-secret: sample-value
secrets.infisical.com/version: W/"3f1-ZyOSsrCLGSkAhhCkY2USPu2ivRw"
labels:
label-to-be-passed-to-managed-secret: sample-value
name: managed-token
namespace: default
type: Opaque
```
</Accordion>

View File

@@ -12,14 +12,15 @@ export const useCreateReminder = (secretId: string) => {
const queryClient = useQueryClient();
return useMutation<Reminder, object, CreateReminderDTO>({
mutationFn: async ({ message, repeatDays, nextReminderDate, recipients }) => {
mutationFn: async ({ message, repeatDays, nextReminderDate, recipients, fromDate }) => {
const { data } = await apiRequest.post<{ reminder: Reminder }>(
`/api/v1/reminders/secrets/${secretId}`,
{
message,
repeatDays,
nextReminderDate,
recipients
recipients,
fromDate
}
);
return data.reminder;

View File

@@ -2,6 +2,7 @@ export type CreateReminderDTO = {
message?: string | null;
repeatDays?: number | null;
nextReminderDate?: Date | null;
fromDate?: Date | null;
secretId: string;
recipients?: string[];
};

View File

@@ -52,8 +52,15 @@ export const SecretSearchInput = ({
if (activeIndex === 0 && e.key === "Enter") setIsOpen(true);
}}
autoComplete="off"
className="input text-md h-[2.3rem] w-full rounded-md rounded-l-none bg-mineshaft-800 py-[0.375rem] pl-2.5 pr-8 text-gray-400 placeholder-mineshaft-50 placeholder-opacity-50 outline-none duration-200 placeholder:text-sm hover:ring-bunker-400/60 focus:bg-mineshaft-700/80 focus:ring-1 focus:ring-primary-400/50"
placeholder="Search by secret, folder, tag or metadata..."
className={twMerge(
"input text-md h-[2.3rem] w-full rounded-md rounded-l-none bg-mineshaft-800 py-[0.375rem] pl-2.5 text-gray-400 placeholder-mineshaft-50 placeholder-opacity-50 outline-none duration-200 placeholder:text-sm hover:ring-bunker-400/60 focus:bg-mineshaft-700/80 focus:ring-1 focus:ring-primary-400/50",
hasSearch ? "pr-8" : "pr-2.5"
)}
placeholder={
isSingleEnv
? "Search by secret, folder, tag or metadata..."
: "Search by secret or folder name..."
}
value={value}
onChange={(e) => onChange(e.target.value)}
/>

View File

@@ -538,16 +538,20 @@ export const SecretApprovalRequestChanges = ({
className="flex flex-nowrap items-center justify-between space-x-2 rounded border border-mineshaft-600 bg-mineshaft-800 px-2 py-1"
key={`required-approver-${requiredApprover.userId}`}
>
<div className="flex text-sm">
<Tooltip
content={`${requiredApprover.firstName || ""} ${
requiredApprover.lastName || ""
}`}
>
<span>{requiredApprover?.email}</span>
</Tooltip>
<span className="text-red">*</span>
</div>
<Tooltip
content={
requiredApprover.firstName
? `${requiredApprover.firstName || ""} ${requiredApprover.lastName || ""}`
: undefined
}
position="left"
sideOffset={10}
>
<div className="flex text-sm">
<div>{requiredApprover?.email}</div>
<span className="text-red">*</span>
</div>
</Tooltip>
<div>
{reviewer?.comment && (
<Tooltip className="max-w-lg break-words" content={reviewer.comment}>

View File

@@ -4,6 +4,7 @@ import { faClock, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import { useQueryClient } from "@tanstack/react-query";
import { format } from "date-fns";
import { twMerge } from "tailwind-merge";
import { z } from "zod";
@@ -33,6 +34,7 @@ const MIN_REPEAT_DAYS = 1;
const MAX_REPEAT_DAYS = 365;
const DEFAULT_REPEAT_DAYS = 30;
const DEFAULT_TEXTAREA_ROWS = 8;
const ONE_DAY_IN_MILLIS = 86400000;
// Enums
enum ReminderType {
@@ -79,6 +81,11 @@ const ReminderFormSchema = z.object({
.refine((data) => data > new Date(), { message: "Reminder date must be in the future" })
.nullable()
.optional(),
fromDate: z.coerce
.date()
.refine((data) => data > new Date(), { message: "From date must be in the future" })
.nullable()
.optional(),
reminderType: z.enum(["Recurring", "One Time"])
});
@@ -86,7 +93,7 @@ export type TReminderFormSchema = z.infer<typeof ReminderFormSchema>;
// Custom hook for form state management
const useReminderForm = (reminderData?: Reminder) => {
const { repeatDays, message, nextReminderDate } = reminderData || {};
const { repeatDays, message, nextReminderDate, fromDate } = reminderData || {};
const isEditMode = Boolean(reminderData);
@@ -96,9 +103,10 @@ const useReminderForm = (reminderData?: Reminder) => {
message: message || "",
nextReminderDate: nextReminderDate || null,
reminderType: repeatDays ? ReminderType.Recurring : ReminderType.OneTime,
recipients: []
recipients: [],
fromDate
}),
[repeatDays, message, nextReminderDate]
[repeatDays, message, nextReminderDate, fromDate]
);
return {
@@ -153,7 +161,8 @@ export const CreateReminderForm = ({
message: reminderData?.message || "",
nextReminderDate: reminderData?.nextReminderDate || null,
reminderType: reminderData?.repeatDays ? ReminderType.Recurring : ReminderType.OneTime,
recipients: []
recipients: [],
fromDate: reminderData?.fromDate
},
resolver: zodResolver(ReminderFormSchema)
});
@@ -170,6 +179,7 @@ export const CreateReminderForm = ({
// Watch form values
const reminderType = watch("reminderType");
const fromDate = watch("fromDate");
// Invalidate queries helper
const invalidateQueries = () => {
@@ -195,7 +205,8 @@ export const CreateReminderForm = ({
message: data.message,
recipients: data.recipients?.map((r) => r.value) || [],
secretId,
nextReminderDate: data.nextReminderDate
nextReminderDate: data.nextReminderDate,
fromDate: data.fromDate
});
invalidateQueries();
@@ -243,6 +254,7 @@ export const CreateReminderForm = ({
if (newType === ReminderType.Recurring) {
setValue("repeatDays", DEFAULT_REPEAT_DAYS);
setValue("nextReminderDate", null);
setValue("fromDate", null);
} else if (newType === ReminderType.OneTime) {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
@@ -259,6 +271,7 @@ export const CreateReminderForm = ({
const {
repeatDays: repeatDaysInitial,
fromDate: fromDateInitial,
message,
recipients,
nextReminderDate: nextReminderDateInitial
@@ -266,6 +279,7 @@ export const CreateReminderForm = ({
if (repeatDaysInitial) {
setValue("repeatDays", repeatDaysInitial);
setValue("fromDate", fromDateInitial);
setValue("reminderType", ReminderType.Recurring);
} else {
setValue("reminderType", ReminderType.OneTime);
@@ -323,44 +337,73 @@ export const CreateReminderForm = ({
{/* Conditional Fields Based on Reminder Type */}
{reminderType === ReminderType.Recurring ? (
<Controller
control={control}
name="repeatDays"
render={({ field, fieldState }) => (
<div>
<div className="grid grid-cols-[1fr,auto] gap-x-2">
<Controller
control={control}
name="repeatDays"
render={({ field, fieldState }) => (
<div>
<FormControl
isRequired
className="mb-0"
label="Reminder Interval (in days)"
isError={Boolean(fieldState.error)}
errorText={fieldState.error?.message || ""}
>
<Input
onChange={(el) => {
const value = parseInt(el.target.value, 10);
setValue("repeatDays", Number.isNaN(value) ? null : value);
}}
type="number"
placeholder={DEFAULT_REPEAT_DAYS.toString()}
value={field.value || ""}
min={MIN_REPEAT_DAYS}
max={MAX_REPEAT_DAYS}
/>
</FormControl>
{/* Interval description */}
<div
className={twMerge(
"ml-1 mt-2 text-xs",
field.value ? "opacity-60" : "opacity-0"
)}
>
A reminder will be sent every{" "}
{field.value && field.value > 1 ? `${field.value} days` : "day"}
{fromDate ? ` starting from ${format(fromDate, "MM/dd/yy")}` : ""}
</div>
</div>
)}
/>
<Controller
control={control}
name="fromDate"
render={({ field, fieldState }) => (
<FormControl
isRequired
className="mb-0"
label="Reminder Interval (in days)"
label="Start Date"
tooltipText="When enabled, this date will be used as the start date for the first reminder"
isError={Boolean(fieldState.error)}
errorText={fieldState.error?.message || ""}
>
<Input
onChange={(el) => {
const value = parseInt(el.target.value, 10);
setValue("repeatDays", Number.isNaN(value) ? null : value);
<DatePicker
value={field.value || undefined}
className="w-full"
onChange={field.onChange}
dateFormat="P"
popUpProps={{
open: isDatePickerOpen,
onOpenChange: setIsDatePickerOpen
}}
type="number"
placeholder={DEFAULT_REPEAT_DAYS.toString()}
value={field.value || ""}
min={MIN_REPEAT_DAYS}
max={MAX_REPEAT_DAYS}
popUpContentProps={{}}
hideTime
hidden={{ before: new Date(Date.now() + ONE_DAY_IN_MILLIS) }}
/>
</FormControl>
{/* Interval description */}
<div
className={twMerge(
"ml-1 mt-2 text-xs",
field.value ? "opacity-60" : "opacity-0"
)}
>
A reminder will be sent every{" "}
{field.value && field.value > 1 ? `${field.value} days` : "day"}
</div>
</div>
)}
/>
)}
/>
</div>
) : (
<Controller
control={control}
@@ -385,7 +428,7 @@ export const CreateReminderForm = ({
}}
popUpContentProps={{}}
hideTime
hidden={{ before: new Date(Date.now() + 86400000) }}
hidden={{ before: new Date(Date.now() + ONE_DAY_IN_MILLIS) }}
/>
</FormControl>
</div>

View File

@@ -13,9 +13,9 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: v0.9.4
version: v0.9.5
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "v0.9.4"
appVersion: "v0.9.5"

View File

@@ -86,6 +86,10 @@ spec:
securityContext:
runAsNonRoot: true
serviceAccountName: {{ include "secrets-operator.fullname" . }}-controller-manager
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
terminationGracePeriodSeconds: 10
nodeSelector: {{ toYaml .Values.controllerManager.nodeSelector | nindent 8 }}
tolerations: {{ toYaml .Values.controllerManager.tolerations | nindent 8 }}

View File

@@ -104,6 +104,27 @@ spec:
- identityId
- serviceAccountRef
type: object
ldapAuth:
properties:
credentialsRef:
properties:
secretName:
description: The name of the Kubernetes Secret
type: string
secretNamespace:
description: The name space where the Kubernetes Secret
is located
type: string
required:
- secretName
- secretNamespace
type: object
identityId:
type: string
required:
- credentialsRef
- identityId
type: object
universalAuth:
properties:
credentialsRef:

View File

@@ -104,6 +104,27 @@ spec:
- identityId
- serviceAccountRef
type: object
ldapAuth:
properties:
credentialsRef:
properties:
secretName:
description: The name of the Kubernetes Secret
type: string
secretNamespace:
description: The name space where the Kubernetes Secret
is located
type: string
required:
- secretName
- secretNamespace
type: object
identityId:
type: string
required:
- credentialsRef
- identityId
type: object
universalAuth:
properties:
credentialsRef:

View File

@@ -182,6 +182,43 @@ spec:
- secretsScope
- serviceAccountRef
type: object
ldapAuth:
properties:
credentialsRef:
properties:
secretName:
description: The name of the Kubernetes Secret
type: string
secretNamespace:
description: The name space where the Kubernetes Secret
is located
type: string
required:
- secretName
- secretNamespace
type: object
identityId:
type: string
secretsScope:
properties:
envSlug:
type: string
projectSlug:
type: string
recursive:
type: boolean
secretsPath:
type: string
required:
- envSlug
- projectSlug
- secretsPath
type: object
required:
- credentialsRef
- identityId
- secretsScope
type: object
serviceAccount:
properties:
environmentName:

View File

@@ -32,7 +32,7 @@ controllerManager:
- ALL
image:
repository: infisical/kubernetes-operator
tag: v0.9.4
tag: v0.9.5
resources:
limits:
cpu: 500m
@@ -56,3 +56,4 @@ kubernetesClusterDomain: cluster.local
scopedNamespace: ""
scopedRBAC: false
installCRDs: true
imagePullSecrets: []

View File

@@ -13,6 +13,8 @@ type GenericInfisicalAuthentication struct {
GcpIdTokenAuth GenericGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"`
// +kubebuilder:validation:Optional
GcpIamAuth GenericGcpIamAuth `json:"gcpIamAuth,omitempty"`
// +kubebuilder:validation:Optional
LdapAuth GenericLdapAuth `json:"ldapAuth,omitempty"`
}
type GenericUniversalAuth struct {
@@ -20,6 +22,13 @@ type GenericUniversalAuth struct {
CredentialsRef KubeSecretReference `json:"credentialsRef"`
}
type GenericLdapAuth struct {
// +kubebuilder:validation:Required
IdentityID string `json:"identityId"`
// +kubebuilder:validation:Required
CredentialsRef KubeSecretReference `json:"credentialsRef"`
}
type GenericAwsIamAuth struct {
// +kubebuilder:validation:Required
IdentityID string `json:"identityId"`

View File

@@ -21,6 +21,8 @@ type Authentication struct {
GcpIdTokenAuth GCPIdTokenAuthDetails `json:"gcpIdTokenAuth"`
// +kubebuilder:validation:Optional
GcpIamAuth GcpIamAuthDetails `json:"gcpIamAuth"`
// +kubebuilder:validation:Optional
LdapAuth LdapAuthDetails `json:"ldapAuth"`
}
type UniversalAuthDetails struct {
@@ -30,6 +32,15 @@ type UniversalAuthDetails struct {
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
}
type LdapAuthDetails struct {
// +kubebuilder:validation:Required
IdentityID string `json:"identityId"`
// +kubebuilder:validation:Required
CredentialsRef KubeSecretReference `json:"credentialsRef"`
// +kubebuilder:validation:Required
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
}
type KubernetesAuthDetails struct {
// +kubebuilder:validation:Required
IdentityID string `json:"identityId"`

View File

@@ -53,6 +53,7 @@ func (in *Authentication) DeepCopyInto(out *Authentication) {
out.AzureAuth = in.AzureAuth
out.GcpIdTokenAuth = in.GcpIdTokenAuth
out.GcpIamAuth = in.GcpIamAuth
out.LdapAuth = in.LdapAuth
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authentication.
@@ -326,6 +327,7 @@ func (in *GenericInfisicalAuthentication) DeepCopyInto(out *GenericInfisicalAuth
out.AzureAuth = in.AzureAuth
out.GcpIdTokenAuth = in.GcpIdTokenAuth
out.GcpIamAuth = in.GcpIamAuth
out.LdapAuth = in.LdapAuth
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericInfisicalAuthentication.
@@ -359,6 +361,22 @@ func (in *GenericKubernetesAuth) DeepCopy() *GenericKubernetesAuth {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GenericLdapAuth) DeepCopyInto(out *GenericLdapAuth) {
*out = *in
out.CredentialsRef = in.CredentialsRef
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericLdapAuth.
func (in *GenericLdapAuth) DeepCopy() *GenericLdapAuth {
if in == nil {
return nil
}
out := new(GenericLdapAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GenericUniversalAuth) DeepCopyInto(out *GenericUniversalAuth) {
*out = *in
@@ -810,6 +828,23 @@ func (in *KubernetesServiceAccountRef) DeepCopy() *KubernetesServiceAccountRef {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LdapAuthDetails) DeepCopyInto(out *LdapAuthDetails) {
*out = *in
out.CredentialsRef = in.CredentialsRef
out.SecretsScope = in.SecretsScope
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LdapAuthDetails.
func (in *LdapAuthDetails) DeepCopy() *LdapAuthDetails {
if in == nil {
return nil
}
out := new(LdapAuthDetails)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MachineIdentityScopeInWorkspace) DeepCopyInto(out *MachineIdentityScopeInWorkspace) {
*out = *in

View File

@@ -103,6 +103,27 @@ spec:
- identityId
- serviceAccountRef
type: object
ldapAuth:
properties:
credentialsRef:
properties:
secretName:
description: The name of the Kubernetes Secret
type: string
secretNamespace:
description: The name space where the Kubernetes Secret
is located
type: string
required:
- secretName
- secretNamespace
type: object
identityId:
type: string
required:
- credentialsRef
- identityId
type: object
universalAuth:
properties:
credentialsRef:

View File

@@ -103,6 +103,27 @@ spec:
- identityId
- serviceAccountRef
type: object
ldapAuth:
properties:
credentialsRef:
properties:
secretName:
description: The name of the Kubernetes Secret
type: string
secretNamespace:
description: The name space where the Kubernetes Secret
is located
type: string
required:
- secretName
- secretNamespace
type: object
identityId:
type: string
required:
- credentialsRef
- identityId
type: object
universalAuth:
properties:
credentialsRef:

View File

@@ -181,6 +181,43 @@ spec:
- secretsScope
- serviceAccountRef
type: object
ldapAuth:
properties:
credentialsRef:
properties:
secretName:
description: The name of the Kubernetes Secret
type: string
secretNamespace:
description: The name space where the Kubernetes Secret
is located
type: string
required:
- secretName
- secretNamespace
type: object
identityId:
type: string
secretsScope:
properties:
envSlug:
type: string
projectSlug:
type: string
recursive:
type: boolean
secretsPath:
type: string
required:
- envSlug
- projectSlug
- secretsPath
type: object
required:
- credentialsRef
- identityId
- secretsScope
type: object
serviceAccount:
properties:
environmentName:

View File

@@ -65,6 +65,19 @@ spec:
secretsPath: "/path"
recursive: true
ldapAuth:
identityId: <machine-identity-id>
credentialsRef:
secretName: <secret-name> # ldap-auth-credentials
secretNamespace: <secret-namespace> # default
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
secretsScope:
projectSlug: your-project-slug
envSlug: prod
secretsPath: "/path"
recursive: true
# Azure Auth
azureAuth:
identityId: <your-machine-identity-id>

View File

@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: ldap-auth-credentials
type: Opaque
stringData:
username: <ldap-username>
password: <ldap-password>

View File

@@ -85,6 +85,7 @@ func (r *InfisicalDynamicSecretReconciler) handleAuthentication(ctx context.Cont
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth,
}
for authStrategy, authHandler := range authStrategies {

View File

@@ -32,6 +32,7 @@ func (r *InfisicalPushSecretReconciler) handleAuthentication(ctx context.Context
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth,
}
for authStrategy, authHandler := range authStrategies {

View File

@@ -57,6 +57,7 @@ func (r *InfisicalSecretReconciler) handleAuthentication(ctx context.Context, in
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth,
}
for authStrategy, authHandler := range authStrategies {

View File

@@ -5,7 +5,7 @@ go 1.21
require (
github.com/Masterminds/sprig/v3 v3.3.0
github.com/aws/smithy-go v1.20.3
github.com/infisical/go-sdk v0.5.97
github.com/infisical/go-sdk v0.5.99
github.com/lestrrat-go/jwx/v2 v2.1.4
github.com/onsi/ginkgo/v2 v2.6.0
github.com/onsi/gomega v1.24.1
@@ -40,6 +40,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
@@ -52,9 +53,12 @@ require (
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oracle/oci-go-sdk/v65 v65.95.2 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect

View File

@@ -152,6 +152,8 @@ github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -237,6 +239,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/infisical/go-sdk v0.5.97 h1:veOi6Hduda6emtwjdUI5SBg2qd2iDQc5xLKqZ15KSoM=
github.com/infisical/go-sdk v0.5.97/go.mod h1:ExjqFLRz7LSpZpGluqDLvFl6dFBLq5LKyLW7GBaMAIs=
github.com/infisical/go-sdk v0.5.99 h1:trvn7JhKYuSzDkc44h+yqToVjclkrRyP42t315k5kEE=
github.com/infisical/go-sdk v0.5.99/go.mod h1:j2D2a5WPNdKXDfHO+3y/TNyLWh5Aq9QYS7EcGI96LZI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
@@ -303,6 +307,8 @@ github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc=
github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
github.com/oracle/oci-go-sdk/v65 v65.95.2 h1:0HJ0AgpLydp/DtvYrF2d4str2BjXOVAeNbuW7E07g94=
github.com/oracle/oci-go-sdk/v65 v65.95.2/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -347,6 +353,8 @@ github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+D
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -365,8 +373,11 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -407,6 +418,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
@@ -551,6 +563,7 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -559,6 +572,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=

View File

@@ -6,11 +6,16 @@ type ServiceAccountDetails struct {
PrivateKey string
}
type MachineIdentityDetails struct {
type UniversalAuthIdentityDetails struct {
ClientId string
ClientSecret string
}
type LdapIdentityDetails struct {
Username string
Password string
}
type SingleEnvironmentVariable struct {
Key string `json:"key"`
Value string `json:"value"`

View File

@@ -88,6 +88,7 @@ var AuthStrategy = struct {
AZURE_MACHINE_IDENTITY AuthStrategyType
GCP_ID_TOKEN_MACHINE_IDENTITY AuthStrategyType
GCP_IAM_MACHINE_IDENTITY AuthStrategyType
LDAP_MACHINE_IDENTITY AuthStrategyType
}{
SERVICE_TOKEN: "SERVICE_TOKEN",
SERVICE_ACCOUNT: "SERVICE_ACCOUNT",
@@ -97,6 +98,7 @@ var AuthStrategy = struct {
AZURE_MACHINE_IDENTITY: "AZURE_MACHINE_IDENTITY",
GCP_ID_TOKEN_MACHINE_IDENTITY: "GCP_ID_TOKEN_MACHINE_IDENTITY",
GCP_IAM_MACHINE_IDENTITY: "GCP_IAM_MACHINE_IDENTITY",
LDAP_MACHINE_IDENTITY: "LDAP_MACHINE_IDENTITY",
}
type SecretCrdType string
@@ -188,6 +190,71 @@ func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, se
}, nil
}
func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
var ldapAuthSpec v1alpha1.LdapAuthDetails
switch secretCrd.Type {
case SecretCrd.INFISICAL_SECRET:
infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret)
if !ok {
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
}
ldapAuthSpec = infisicalSecret.Spec.Authentication.LdapAuth
case SecretCrd.INFISICAL_PUSH_SECRET:
infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret)
if !ok {
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret")
}
ldapAuthSpec = v1alpha1.LdapAuthDetails{
CredentialsRef: infisicalPushSecret.Spec.Authentication.LdapAuth.CredentialsRef,
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
IdentityID: infisicalPushSecret.Spec.Authentication.LdapAuth.IdentityID,
}
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
if !ok {
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
}
ldapAuthSpec = v1alpha1.LdapAuthDetails{
CredentialsRef: infisicalDynamicSecret.Spec.Authentication.LdapAuth.CredentialsRef,
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
IdentityID: infisicalDynamicSecret.Spec.Authentication.LdapAuth.IdentityID,
}
}
ldapAuthKubeSecret, err := GetInfisicalLdapAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
SecretNamespace: ldapAuthSpec.CredentialsRef.SecretNamespace,
SecretName: ldapAuthSpec.CredentialsRef.SecretName,
})
if err != nil {
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err)
}
if ldapAuthKubeSecret.Username == "" && ldapAuthKubeSecret.Password == "" {
return AuthenticationDetails{}, ErrAuthNotApplicable
}
_, err = infisicalClient.Auth().LdapAuthLogin(ldapAuthSpec.IdentityID, ldapAuthKubeSecret.Username, ldapAuthKubeSecret.Password)
if err != nil {
return AuthenticationDetails{}, fmt.Errorf("unable to login with machine identity credentials [err=%s]", err)
}
return AuthenticationDetails{
AuthStrategy: AuthStrategy.LDAP_MACHINE_IDENTITY,
MachineIdentityScope: ldapAuthSpec.SecretsScope,
IsMachineIdentityAuth: true,
SecretType: secretCrd.Type,
}, nil
}
func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
var kubernetesAuthSpec v1alpha1.KubernetesAuthDetails

View File

@@ -18,6 +18,9 @@ import (
const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId"
const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret"
const INFISICAL_MACHINE_IDENTITY_LDAP_USERNAME = "username"
const INFISICAL_MACHINE_IDENTITY_LDAP_PASSWORD = "password"
func GetKubeSecretByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.Secret, error) {
kubeSecret := &corev1.Secret{}
err := reconcilerClient.Get(ctx, namespacedName, kubeSecret)
@@ -38,7 +41,7 @@ func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient clie
return kubeConfigMap, err
}
func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.MachineIdentityDetails, err error) {
func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.UniversalAuthIdentityDetails, err error) {
universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: universalAuthRef.SecretNamespace,
@@ -48,17 +51,39 @@ func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClie
})
if k8Errors.IsNotFound(err) {
return model.MachineIdentityDetails{}, nil
return model.UniversalAuthIdentityDetails{}, nil
}
if err != nil {
return model.MachineIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err)
return model.UniversalAuthIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err)
}
clientIdFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_ID]
clientSecretFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET]
return model.MachineIdentityDetails{ClientId: string(clientIdFromSecret), ClientSecret: string(clientSecretFromSecret)}, nil
return model.UniversalAuthIdentityDetails{ClientId: string(clientIdFromSecret), ClientSecret: string(clientSecretFromSecret)}, nil
}
func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, ldapAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.LdapIdentityDetails, err error) {
ldapAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: ldapAuthRef.SecretNamespace,
Name: ldapAuthRef.SecretName,
})
if k8Errors.IsNotFound(err) {
return model.LdapIdentityDetails{}, nil
}
if err != nil {
return model.LdapIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err)
}
usernameFromSecret := ldapAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_LDAP_USERNAME]
passwordFromSecret := ldapAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_LDAP_PASSWORD]
return model.LdapIdentityDetails{Username: string(usernameFromSecret), Password: string(passwordFromSecret)}, nil
}