From 2b8220a71b93730719d553b0e5be1d8394d4d1ae Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 01:43:47 +0800 Subject: [PATCH 01/13] feat: added support for outputting bootstrap credentials to k8 secret --- cli/packages/api/api.go | 8 +- cli/packages/api/model.go | 32 +++++ cli/packages/cmd/bootstrap.go | 210 ++++++++++++++++++++++++++++++-- cli/packages/util/constants.go | 9 ++ docs/cli/commands/bootstrap.mdx | 127 ++++++++++++++++++- 5 files changed, 371 insertions(+), 15 deletions(-) diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index 83732b64a..15f75a57d 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -631,8 +631,8 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error { return nil } -func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (map[string]interface{}, error) { - var resBody map[string]interface{} +func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (BootstrapInstanceResponse, error) { + var resBody BootstrapInstanceResponse response, err := httpClient. R(). SetResult(&resBody). @@ -641,11 +641,11 @@ func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRe Post(fmt.Sprintf("%v/v1/admin/bootstrap", request.Domain)) if err != nil { - return nil, NewGenericRequestError(operationCallBootstrapInstance, err) + return BootstrapInstanceResponse{}, NewGenericRequestError(operationCallBootstrapInstance, err) } if response.IsError() { - return nil, NewAPIErrorWithResponse(operationCallBootstrapInstance, response, nil) + return BootstrapInstanceResponse{}, NewAPIErrorWithResponse(operationCallBootstrapInstance, response, nil) } return resBody, nil diff --git a/cli/packages/api/model.go b/cli/packages/api/model.go index a7a797a0b..9bf666e44 100644 --- a/cli/packages/api/model.go +++ b/cli/packages/api/model.go @@ -655,3 +655,35 @@ type BootstrapInstanceRequest struct { Organization string `json:"organization"` Domain string `json:"domain"` } + +type BootstrapInstanceResponse struct { + Message string `json:"message"` + Identity BootstrapIdentity `json:"identity"` + Organization BootstrapOrganization `json:"organization"` + User BootstrapUser `json:"user"` +} + +type BootstrapIdentity struct { + ID string `json:"id"` + Name string `json:"name"` + Credentials BootstrapIdentityCredentials `json:"credentials"` +} + +type BootstrapIdentityCredentials struct { + Token string `json:"token"` +} + +type BootstrapOrganization struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` +} + +type BootstrapUser struct { + ID string `json:"id"` + Email string `json:"email"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + Username string `json:"username"` + SuperAdmin bool `json:"superAdmin"` +} diff --git a/cli/packages/cmd/bootstrap.go b/cli/packages/cmd/bootstrap.go index 4582cb001..51e8b076e 100644 --- a/cli/packages/cmd/bootstrap.go +++ b/cli/packages/cmd/bootstrap.go @@ -4,9 +4,14 @@ Copyright (c) 2023 Infisical Inc. package cmd import ( + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/base64" "encoding/json" "fmt" "os" + "text/template" "github.com/Infisical/infisical-merge/packages/api" "github.com/Infisical/infisical-merge/packages/util" @@ -14,6 +19,133 @@ import ( "github.com/spf13/cobra" ) +// handleK8SecretOutput processes the k8-secret output type by creating a Kubernetes secret +func handleK8SecretOutput(bootstrapResponse api.BootstrapInstanceResponse, k8SecretTemplate, k8SecretName, k8SecretNamespace string) error { + // Read Kubernetes service account credentials from the pod + k8sToken, err := os.ReadFile(util.KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH) + if err != nil { + return fmt.Errorf("failed to read Kubernetes service account token: %v", err) + } + + k8sCaCert, err := os.ReadFile(util.KUBERNETES_SERVICE_ACCOUNT_CA_CERT_PATH) + if err != nil { + return fmt.Errorf("failed to read Kubernetes CA certificate: %v", err) + } + + // Get Kubernetes API server URL from environment variables + k8sHost := os.Getenv(util.KUBERNETES_SERVICE_HOST_ENV_NAME) + k8sPort := os.Getenv(util.KUBERNETES_SERVICE_PORT_HTTPS_ENV_NAME) + if k8sHost == "" || k8sPort == "" { + return fmt.Errorf("failed to get Kubernetes API server address from environment variables") + } + + k8sApiUrl := fmt.Sprintf("https://%s:%s", k8sHost, k8sPort) + + // Parse and execute the template to render only the data/stringData section + tmpl, err := template.New("k8-secret-template").Funcs(template.FuncMap{ + "b64enc": func(s string) string { + return base64.StdEncoding.EncodeToString([]byte(s)) + }, + }).Parse(k8SecretTemplate) + + if err != nil { + return fmt.Errorf("failed to parse output template: %v", err) + } + + var renderedDataSection bytes.Buffer + err = tmpl.Execute(&renderedDataSection, bootstrapResponse) + if err != nil { + return fmt.Errorf("failed to execute output template: %v", err) + } + + // Parse the rendered template as JSON to validate it's valid + var dataSection map[string]interface{} + if err := json.Unmarshal(renderedDataSection.Bytes(), &dataSection); err != nil { + return fmt.Errorf("template output is not valid JSON: %v", err) + } + + // Construct the complete Kubernetes secret object + k8sSecret := map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": k8SecretName, + "namespace": k8SecretNamespace, + }, + "type": "Opaque", + } + + // Merge the rendered data section into the secret + for key, value := range dataSection { + k8sSecret[key] = value + } + + // Prepare the HTTP client with TLS configuration + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(k8sCaCert) { + return fmt.Errorf("failed to parse Kubernetes CA certificate") + } + + tlsConfig := &tls.Config{ + RootCAs: caCertPool, + } + + // Create a new HTTP client for Kubernetes API + k8sHttpClient, err := util.GetRestyClientWithCustomHeaders() + if err != nil { + return fmt.Errorf("failed to create Kubernetes HTTP client: %v", err) + } + + k8sHttpClient.SetTLSClientConfig(tlsConfig) + k8sHttpClient.SetHeader("Authorization", fmt.Sprintf("Bearer %s", string(k8sToken))) + k8sHttpClient.SetHeader("Content-Type", "application/json") + + // Check if secret already exists first + checkUrl := fmt.Sprintf("%s/api/v1/namespaces/%s/secrets/%s", k8sApiUrl, k8SecretNamespace, k8SecretName) + checkResponse, err := k8sHttpClient.R().Get(checkUrl) + + if err != nil { + return fmt.Errorf("failed to check if Kubernetes secret exists: %v", err) + } + + secretUrl := fmt.Sprintf("%s/api/v1/namespaces/%s/secrets", k8sApiUrl, k8SecretNamespace) + + if checkResponse.StatusCode() == 200 { + // Secret exists, update it + secretUrl = fmt.Sprintf("%s/%s", secretUrl, k8SecretName) + response, err := k8sHttpClient.R(). + SetBody(k8sSecret). + Put(secretUrl) + + if err != nil { + return fmt.Errorf("failed to update Kubernetes secret: %v", err) + } + + if response.IsError() { + return fmt.Errorf("kubernetes API returned error when updating secret: %s", response.String()) + } + + log.Info().Msgf("Successfully updated Kubernetes secret '%s' in namespace '%s'", k8SecretName, k8SecretNamespace) + } else { + // Secret doesn't exist, create it + response, err := k8sHttpClient.R(). + SetBody(k8sSecret). + Post(secretUrl) + + if err != nil { + return fmt.Errorf("failed to create Kubernetes secret: %v", err) + } + + if response.IsError() { + return fmt.Errorf("kubernetes API returned error when creating secret: %s", response.String()) + } + + log.Info().Msgf("Successfully created Kubernetes secret '%s' in namespace '%s'", k8SecretName, k8SecretNamespace) + } + + return nil +} + var bootstrapCmd = &cobra.Command{ Use: "bootstrap", Short: "Used to bootstrap your Infisical instance", @@ -23,7 +155,7 @@ var bootstrapCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { email, _ := cmd.Flags().GetString("email") if email == "" { - if envEmail, ok := os.LookupEnv("INFISICAL_ADMIN_EMAIL"); ok { + if envEmail, ok := os.LookupEnv(util.INFISICAL_BOOTSTRAP_EMAIL_NAME); ok { email = envEmail } } @@ -35,7 +167,7 @@ var bootstrapCmd = &cobra.Command{ password, _ := cmd.Flags().GetString("password") if password == "" { - if envPassword, ok := os.LookupEnv("INFISICAL_ADMIN_PASSWORD"); ok { + if envPassword, ok := os.LookupEnv(util.INFISICAL_BOOTSTRAP_PASSWORD_NAME); ok { password = envPassword } } @@ -47,7 +179,7 @@ var bootstrapCmd = &cobra.Command{ organization, _ := cmd.Flags().GetString("organization") if organization == "" { - if envOrganization, ok := os.LookupEnv("INFISICAL_ADMIN_ORGANIZATION"); ok { + if envOrganization, ok := os.LookupEnv(util.INFISICAL_BOOTSTRAP_ORGANIZATION_NAME); ok { organization = envOrganization } } @@ -69,11 +201,55 @@ var bootstrapCmd = &cobra.Command{ return } + outputType, err := cmd.Flags().GetString("output") + if err != nil { + log.Error().Msgf("Failed to get output type: %v", err) + return + } + + k8SecretTemplate, err := cmd.Flags().GetString("k8-secret-template") + if err != nil { + log.Error().Msgf("Failed to get output template: %v", err) + } + + k8SecretName, err := cmd.Flags().GetString("k8-secret-name") + if err != nil { + log.Error().Msgf("Failed to get k8-secret-name: %v", err) + } + + k8SecretNamespace, err := cmd.Flags().GetString("k8-secret-namespace") + if err != nil { + log.Error().Msgf("Failed to get k8-secret-namespace: %v", err) + } + + if outputType == "k8-secret" { + if k8SecretTemplate == "" { + log.Error().Msg("k8-secret-template is required when using k8-secret output type") + return + } + + if k8SecretName == "" { + log.Error().Msg("k8-secret-name is required when using k8-secret output type") + return + } + + if k8SecretNamespace == "" { + log.Error().Msg("k8-secret-namespace is required when using k8-secret output type") + return + } + } + httpClient, err := util.GetRestyClientWithCustomHeaders() if err != nil { log.Error().Msgf("Failed to get resty client with custom headers: %v", err) return } + + ignoreIfBootstrapped, err := cmd.Flags().GetBool("ignore-if-bootstrapped") + if err != nil { + log.Error().Msgf("Failed to get ignore-if-bootstrapped flag: %v", err) + } + httpClient.SetHeader("Accept", "application/json") bootstrapResponse, err := api.CallBootstrapInstance(httpClient, api.BootstrapInstanceRequest{ @@ -84,16 +260,26 @@ var bootstrapCmd = &cobra.Command{ }) if err != nil { - log.Error().Msgf("Failed to bootstrap instance: %v", err) + if !ignoreIfBootstrapped { + log.Error().Msgf("Failed to bootstrap instance: %v", err) + } return } - responseJSON, err := json.MarshalIndent(bootstrapResponse, "", " ") - if err != nil { - log.Fatal().Msgf("Failed to convert response to JSON: %v", err) - return + if outputType == "k8-secret" { + if err := handleK8SecretOutput(bootstrapResponse, k8SecretTemplate, k8SecretName, k8SecretNamespace); err != nil { + log.Error().Msgf("Failed to handle k8-secret output: %v", err) + return + } + } else { + responseJSON, err := json.MarshalIndent(bootstrapResponse, "", " ") + if err != nil { + log.Fatal().Msgf("Failed to convert response to JSON: %v", err) + return + } + + fmt.Println(string(responseJSON)) } - fmt.Println(string(responseJSON)) }, } @@ -102,6 +288,10 @@ func init() { bootstrapCmd.Flags().String("email", "", "The desired email address of the instance admin") bootstrapCmd.Flags().String("password", "", "The desired password of the instance admin") bootstrapCmd.Flags().String("organization", "", "The name of the organization to create for the instance") - + bootstrapCmd.Flags().String("output", "", "The type of output to use for the bootstrap command (json or k8-secret)") + bootstrapCmd.Flags().Bool("ignore-if-bootstrapped", false, "Whether to continue on error if the instance has already been bootstrapped") + bootstrapCmd.Flags().String("k8-secret-template", "", "The template to use for rendering the Kubernetes secret (entire secret YAML)") + bootstrapCmd.Flags().String("k8-secret-namespace", "", "The namespace to use for the Kubernetes secret") + bootstrapCmd.Flags().String("k8-secret-name", "", "The name of the Kubernetes secret to create") rootCmd.AddCommand(bootstrapCmd) } diff --git a/cli/packages/util/constants.go b/cli/packages/util/constants.go index 126e5a5d0..383c7fc4c 100644 --- a/cli/packages/util/constants.go +++ b/cli/packages/util/constants.go @@ -10,6 +10,10 @@ const ( INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME = "INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN" INFISICAL_VAULT_FILE_PASSPHRASE_ENV_NAME = "INFISICAL_VAULT_FILE_PASSPHRASE" // This works because we've forked the keyring package and added support for this env variable. This explains why you won't find any occurrences of it in the CLI codebase. + INFISICAL_BOOTSTRAP_EMAIL_NAME = "INFISICAL_ADMIN_EMAIL" + INFISICAL_BOOTSTRAP_PASSWORD_NAME = "INFISICAL_ADMIN_PASSWORD" + INFISICAL_BOOTSTRAP_ORGANIZATION_NAME = "INFISICAL_ADMIN_ORGANIZATION" + VAULT_BACKEND_AUTO_MODE = "auto" VAULT_BACKEND_FILE_MODE = "file" @@ -47,6 +51,11 @@ const ( INFISICAL_BACKUP_SECRET = "infisical-backup-secrets" // akhilmhdh: @depreciated remove in version v0.30 INFISICAL_BACKUP_SECRET_ENCRYPTION_KEY = "infisical-backup-secret-encryption-key" + + KUBERNETES_SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST" + KUBERNETES_SERVICE_PORT_HTTPS_ENV_NAME = "KUBERNETES_SERVICE_PORT_HTTPS" + KUBERNETES_SERVICE_ACCOUNT_CA_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token" ) var ( diff --git a/docs/cli/commands/bootstrap.mdx b/docs/cli/commands/bootstrap.mdx index 77f8b38f1..f85c4167b 100644 --- a/docs/cli/commands/bootstrap.mdx +++ b/docs/cli/commands/bootstrap.mdx @@ -75,8 +75,90 @@ This flag is required. + + Whether to continue without error if the instance has already been bootstrapped. Useful for idempotent automation scripts. + +```bash +# Example +infisical bootstrap --ignore-if-bootstrapped +``` + +This flag is optional and defaults to `false`. + + + + + The type of output format for the bootstrap command. Supports `k8-secret` for Kubernetes secret integration. This flag is optional and defaults to "". + +```bash +# Kubernetes secret output +infisical bootstrap --output=k8-secret --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token}}"}}' --k8-secret-name=infisical-bootstrap --k8-secret-namespace=default +``` + +When using `k8-secret`, the command will create or update a Kubernetes secret directly in your cluster. + + + + + The template to use for rendering the Kubernetes secret data/stringData section. Required when using `--output=k8-secret`. The template uses Go template syntax and has access to the bootstrap response data. + +```bash +# Example template that stores the token +infisical bootstrap --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token | b64enc}}"}}' + +# Example template with multiple fields +infisical bootstrap --k8-secret-template='{"stringData":{"token":"{{.Identity.Credentials.Token}}","org-id":"{{.Organization.Id}}","user-email":"{{.User.Email}}"}}' +``` + +Available template functions: + +- `b64enc`: Base64 encode a string + +Available data fields: + +- `.Identity.Credentials.Token`: The machine identity token +- `.Identity.Id`: The identity ID +- `.Identity.Name`: The identity name +- `.Organization.Id`: The organization ID +- `.Organization.Name`: The organization name +- `.Organization.Slug`: The organization slug +- `.User.Email`: The admin user email +- `.User.Id`: The admin user ID +- `.User.FirstName`: The admin user first name +- `.User.LastName`: The admin user last name + +This flag is required when using `k8-secret` output. + + + + + The name of the Kubernetes secret to create or update. Required when using `--output=k8-secret`. + +```bash +# Example +infisical bootstrap --k8-secret-name=infisical-bootstrap-credentials +``` + +This flag is required when using `k8-secret` output. + + + + + The namespace where the Kubernetes secret should be created or updated. Required when using `--output=k8-secret`. + +```bash +# Example +infisical bootstrap --k8-secret-namespace=infisical-system +``` + +This flag is required when using `k8-secret` output. + + + ## Response +### JSON Output (Default) + The command returns a JSON response with details about the created user, organization, and machine identity: ```json @@ -105,6 +187,47 @@ The command returns a JSON response with details about the created user, organiz } ``` +### Kubernetes Secret Output + +When using `--output=k8-secret`, the command creates or updates a Kubernetes secret in your cluster and logs the operation result. + +## Kubernetes Integration + +### Prerequisites for k8-secret Output + +When running with `--output=k8-secret`, the command must be executed from within a Kubernetes pod with proper service account permissions. The command automatically: + +1. Reads the service account token from `/var/run/secrets/kubernetes.io/serviceaccount/token` +2. Reads the CA certificate from `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` +3. Gets the Kubernetes API server URL from environment variables (`KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT_HTTPS`) + +### Required RBAC Permissions + +Your service account needs the following permissions: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: infisical-bootstrap +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "create", "update"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: infisical-bootstrap +subjects: + - kind: ServiceAccount + name: your-service-account +roleRef: + kind: Role + name: infisical-bootstrap + apiGroup: rbac.authorization.k8s.io +``` + ## Usage with Automation For automation purposes, you can extract just the machine identity token from the response: @@ -127,6 +250,8 @@ echo "Token has been captured and can be used for authentication" ## Notes - The bootstrap process can only be performed once on a fresh Infisical instance -- All flags are required for the bootstrap process to complete successfully +- All core flags (domain, email, password, organization) are required for the bootstrap process to complete successfully - Security controls prevent privilege escalation: instance admin identities cannot be managed by non-instance admin users and identities - The generated admin user account can be used to log in via the UI if needed +- When using `k8-secret` output, the command must run within a Kubernetes pod with proper service account permissions +- The `--ignore-if-bootstrapped` flag is useful for making bootstrap scripts idempotent From 3276853427c071698eabb2bddc1aefb2e685fdf6 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 02:12:08 +0800 Subject: [PATCH 02/13] misc: added helm support for auto bootstrap --- ...rt-tests-infisical-standalone-postgres.yml | 7 ++ .../guides/automated-bootstrapping.mdx | 81 +++++++++++++++++++ .../templates/bootstrap-job.yaml | 57 +++++++++++++ .../templates/jobs-rbac.yaml | 32 +++++++- .../infisical-standalone-postgres/values.yaml | 25 ++++++ 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml diff --git a/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml b/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml index fcf519720..d8f6466c6 100644 --- a/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml +++ b/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml @@ -51,6 +51,13 @@ jobs: --from-literal=ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218 \ --from-literal=SITE_URL=http://localhost:8080 + - name: Create bootstrap secret + run: | + kubectl create secret generic infisical-bootstrap-credentials \ + --namespace infisical-standalone-postgres \ + --from-literal=INFISICAL_ADMIN_EMAIL=admin@example.com \ + --from-literal=INFISICAL_ADMIN_PASSWORD=admin-password + - name: Run chart-testing (install) run: | ct install \ diff --git a/docs/self-hosting/guides/automated-bootstrapping.mdx b/docs/self-hosting/guides/automated-bootstrapping.mdx index ebc9c3c80..5266c933b 100644 --- a/docs/self-hosting/guides/automated-bootstrapping.mdx +++ b/docs/self-hosting/guides/automated-bootstrapping.mdx @@ -8,6 +8,7 @@ Infisical's Automated Bootstrapping feature enables you to provision and configu ## Overview The Automated Bootstrapping workflow automates the following processes: + - Creating an admin user account - Initializing an organization for the entire instance - Establishing an **instance admin machine identity** with full administrative permissions @@ -51,6 +52,7 @@ You can bootstrap an Infisical instance using either the API or the CLI. -d '{"email":"admin@example.com","password":"your-secure-password","organization":"your-org-name"}' \ http://your-infisical-instance.com/api/v1/admin/bootstrap ``` + Use the [Infisical CLI](/cli/commands/bootstrap) to bootstrap the instance and extract the token for immediate use in automation: @@ -60,9 +62,88 @@ You can bootstrap an Infisical instance using either the API or the CLI. ``` This example command pipes the output through `jq` to extract only the machine identity token, making it easy to capture and use directly in automation scripts or export as an environment variable for tools like Terraform. + +## Helm Chart Auto Bootstrap + +When deploying Infisical using the official Helm chart, you can enable automatic bootstrapping that runs as part of the deployment process. This eliminates the need to manually bootstrap the instance after deployment. + +### Configuration + +Enable auto bootstrapping in your Helm values by setting `autoBootstrap.enabled: true` and providing the necessary configuration: + +```yaml +autoBootstrap: + enabled: true + organization: "My Organization" + secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | b64enc}}"}}' + + secretDestination: + name: "infisical-bootstrap-secret" + namespace: "default" # defaults to release namespace if not specified + + credentialSecret: + name: "infisical-bootstrap-credentials" +``` + +You'll also need to create a secret containing the bootstrap credentials before deployment. The secret must contain `INFISICAL_ADMIN_EMAIL` and `INFISICAL_ADMIN_PASSWORD` keys: + +```bash +kubectl create secret generic infisical-bootstrap-credentials \ + --from-literal=INFISICAL_ADMIN_EMAIL="admin@example.com" \ + --from-literal=INFISICAL_ADMIN_PASSWORD="your-secure-password" \ + --namespace=release-namespace +``` + +### How It Works + +The Helm chart auto bootstrap feature: + +1. **Post-Install Hook**: Runs automatically after the main Infisical deployment is complete +2. **Readiness Check**: Uses an init container with curl to wait for Infisical to be ready by polling the `/api/status` endpoint +3. **Bootstrap Execution**: Uses the Infisical CLI to bootstrap the instance +4. **Kubernetes Secret Creation**: Creates a Kubernetes secret directly via the Kubernetes API using the rendered template +5. **RBAC**: Automatically configures the necessary permissions (`get`, `create`, `update` on secrets) for the bootstrap job + +### Template System + +The `secretTemplate` field allows you to customize the data section of the created Kubernetes secret. The template has access to the full bootstrap response and includes helper functions: + +- `{{ .Identity.Credentials.Token }}` - The admin machine identity token +- `{{ .Organization.Id }}` - The created organization ID +- `{{ .Organization.Slug }}` - The organization slug +- `{{ .User.Email }}` - The admin user email +- `b64enc` function for base64 encoding values + +Example template for storing multiple values: + +```yaml +secretTemplate: | + { + "data": { + "infisical_token": "{{ .Identity.Credentials.Token | b64enc }}", + "admin_email": "{{ .User.Email | b64enc }}", + "organization": "{{ .Organization.Name | b64enc }}" + } + } +``` + +### Benefits + +- **Zero-Touch Deployment**: Complete Infisical setup without manual intervention +- **Infrastructure as Code**: Bootstrap configuration is versioned with your Helm values +- **Secure Token Storage**: Admin identity credentials are immediately stored in Kubernetes secrets +- **Integration Ready**: The created secret can be referenced by other applications or automation tools + +### Security Considerations + +- The bootstrap job requires permissions to create secrets in the specified namespace +- Bootstrap credentials should be stored securely and rotated regularly +- The generated admin token has full instance privileges and should be protected accordingly +- Consider using Kubernetes RBAC to restrict access to the generated secret + ## API Response Structure The bootstrap process returns a JSON response with details about the created user, organization, and machine identity: diff --git a/helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml b/helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml new file mode 100644 index 000000000..b8631a681 --- /dev/null +++ b/helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml @@ -0,0 +1,57 @@ +{{- $infisicalValues := .Values.infisical }} +{{- if $infisicalValues.autoBootstrap.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: "{{ .Release.Name }}-bootstrap-{{ .Release.Revision }}" + annotations: + "helm.sh/hook": post-install + "helm.sh/hook-weight": "10" + "helm.sh/hook-delete-policy": before-hook-creation + labels: + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" +spec: + backoffLimit: 3 + template: + metadata: + name: "{{ .Release.Name }}-bootstrap" + labels: + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + spec: + serviceAccountName: {{ include "infisical.serviceAccountName" . }} + {{- if $infisicalValues.image.imagePullSecrets }} + imagePullSecrets: + {{- toYaml $infisicalValues.image.imagePullSecrets | nindent 6 }} + {{- end }} + restartPolicy: OnFailure + initContainers: + - name: wait-for-infisical + image: curlimages/curl:8.14.1 + command: ['sh', '-c'] + args: + - | + echo "Waiting for Infisical to be ready..." + until curl -f http://{{ include "infisical.fullname" . }}:8080/api/status; do + echo "Infisical not ready yet, retrying in 10 seconds..." + sleep 10 + done + echo "Infisical is ready! Proceeding with bootstrap..." + containers: + - name: infisical-bootstrap + image: "infisical/cli:{{ $infisicalValues.image.tag }}" + imagePullPolicy: {{ $infisicalValues.image.pullPolicy | default "IfNotPresent" }} + args: + - bootstrap + - --domain=http://{{ include "infisical.fullname" . }}:8080 + - --output=k8-secret + - --k8-secret-name={{ $infisicalValues.autoBootstrap.secretDestination.name }} + - --k8-secret-namespace={{ $infisicalValues.autoBootstrap.secretDestination.namespace | default .Release.Namespace }} + - --organization={{ $infisicalValues.autoBootstrap.organization }} + - --k8-secret-template={{ $infisicalValues.autoBootstrap.secretTemplate }} + - --ignore-if-bootstrapped=true + envFrom: + - secretRef: + name: {{ $infisicalValues.autoBootstrap.credentialSecret.name }} +{{- end }} diff --git a/helm-charts/infisical-standalone-postgres/templates/jobs-rbac.yaml b/helm-charts/infisical-standalone-postgres/templates/jobs-rbac.yaml index 38230d9a1..3a229c7cf 100644 --- a/helm-charts/infisical-standalone-postgres/templates/jobs-rbac.yaml +++ b/helm-charts/infisical-standalone-postgres/templates/jobs-rbac.yaml @@ -39,4 +39,34 @@ subjects: roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: {{ include "infisical.roleName" . }} \ No newline at end of file + name: {{ include "infisical.roleName" . }} +--- +{{- if .Values.infisical.autoBootstrap.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "infisical.roleName" . }}-bootstrap + namespace: {{ .Values.infisical.autoBootstrap.secretDestination.namespace | default .Release.Namespace }} + labels: + {{- include "infisical.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "create", "update"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "infisical.roleBindingName" . }}-bootstrap + namespace: {{ .Values.infisical.autoBootstrap.secretDestination.namespace | default .Release.Namespace }} + labels: + {{- include "infisical.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + name: {{ include "infisical.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "infisical.roleName" . }}-bootstrap +{{- end }} diff --git a/helm-charts/infisical-standalone-postgres/values.yaml b/helm-charts/infisical-standalone-postgres/values.yaml index d617a1389..2d5f12abc 100644 --- a/helm-charts/infisical-standalone-postgres/values.yaml +++ b/helm-charts/infisical-standalone-postgres/values.yaml @@ -13,6 +13,31 @@ infisical: # -- Automatically migrates new database schema when deploying autoDatabaseSchemaMigration: true + autoBootstrap: + # -- Enable auto-bootstrap of the Infisical instance + enabled: false + + image: + # -- Infisical Infisical CLI image tag version + tag: "0.41.86" + + # -- Template for the data/stringData section of the Kubernetes secret. Available functions: b64enc + secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | b64enc}}"}}' + + secretDestination: + # -- Name of the bootstrap secret to create in the Kubernetes cluster which will store the formatted root identity credentials + name: "infisical-bootstrap-secret" + + # -- Namespace to create the bootstrap secret in. If not provided, the secret will be created in the same namespace as the release. + namespace: "default" + + # -- Infisical organization to create in the Infisical instance during auto-bootstrap + organization: "default-org" + + credentialSecret: + # -- Name of the Kubernetes secret containing the credentials for the auto-bootstrap workflow + name: "infisical-bootstrap-credentials" + databaseSchemaMigrationJob: image: # -- Image repository for migration wait job From 07d491acd14cc1bea8495766c95df27c688fe24d Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 02:26:13 +0800 Subject: [PATCH 03/13] misc: corrected template doc --- docs/cli/commands/bootstrap.mdx | 8 ++++---- docs/self-hosting/guides/automated-bootstrapping.mdx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/cli/commands/bootstrap.mdx b/docs/cli/commands/bootstrap.mdx index f85c4167b..156cfabb8 100644 --- a/docs/cli/commands/bootstrap.mdx +++ b/docs/cli/commands/bootstrap.mdx @@ -107,7 +107,7 @@ When using `k8-secret`, the command will create or update a Kubernetes secret di infisical bootstrap --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token | b64enc}}"}}' # Example template with multiple fields -infisical bootstrap --k8-secret-template='{"stringData":{"token":"{{.Identity.Credentials.Token}}","org-id":"{{.Organization.Id}}","user-email":"{{.User.Email}}"}}' +infisical bootstrap --k8-secret-template='{"stringData":{"token":"{{.Identity.Credentials.Token}}","org-id":"{{.Organization.ID}}","user-email":"{{.User.Email}}"}}' ``` Available template functions: @@ -117,13 +117,13 @@ Available template functions: Available data fields: - `.Identity.Credentials.Token`: The machine identity token -- `.Identity.Id`: The identity ID +- `.Identity.ID`: The identity ID - `.Identity.Name`: The identity name -- `.Organization.Id`: The organization ID +- `.Organization.ID`: The organization ID - `.Organization.Name`: The organization name - `.Organization.Slug`: The organization slug - `.User.Email`: The admin user email -- `.User.Id`: The admin user ID +- `.User.ID`: The admin user ID - `.User.FirstName`: The admin user first name - `.User.LastName`: The admin user last name diff --git a/docs/self-hosting/guides/automated-bootstrapping.mdx b/docs/self-hosting/guides/automated-bootstrapping.mdx index 5266c933b..7a34fc9f8 100644 --- a/docs/self-hosting/guides/automated-bootstrapping.mdx +++ b/docs/self-hosting/guides/automated-bootstrapping.mdx @@ -112,7 +112,7 @@ The Helm chart auto bootstrap feature: The `secretTemplate` field allows you to customize the data section of the created Kubernetes secret. The template has access to the full bootstrap response and includes helper functions: - `{{ .Identity.Credentials.Token }}` - The admin machine identity token -- `{{ .Organization.Id }}` - The created organization ID +- `{{ .Organization.ID }}` - The created organization ID - `{{ .Organization.Slug }}` - The organization slug - `{{ .User.Email }}` - The admin user email - `b64enc` function for base64 encoding values From 9cdd7380df1d5ec2a35b3a1859f92de7d274dda3 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 02:30:26 +0800 Subject: [PATCH 04/13] misc: greptie --- cli/packages/cmd/bootstrap.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/packages/cmd/bootstrap.go b/cli/packages/cmd/bootstrap.go index 51e8b076e..5f6e6d047 100644 --- a/cli/packages/cmd/bootstrap.go +++ b/cli/packages/cmd/bootstrap.go @@ -209,7 +209,7 @@ var bootstrapCmd = &cobra.Command{ k8SecretTemplate, err := cmd.Flags().GetString("k8-secret-template") if err != nil { - log.Error().Msgf("Failed to get output template: %v", err) + log.Error().Msgf("Failed to get k8-secret-template: %v", err) } k8SecretName, err := cmd.Flags().GetString("k8-secret-name") @@ -248,6 +248,7 @@ var bootstrapCmd = &cobra.Command{ ignoreIfBootstrapped, err := cmd.Flags().GetBool("ignore-if-bootstrapped") if err != nil { log.Error().Msgf("Failed to get ignore-if-bootstrapped flag: %v", err) + return } httpClient.SetHeader("Accept", "application/json") @@ -290,7 +291,7 @@ func init() { bootstrapCmd.Flags().String("organization", "", "The name of the organization to create for the instance") bootstrapCmd.Flags().String("output", "", "The type of output to use for the bootstrap command (json or k8-secret)") bootstrapCmd.Flags().Bool("ignore-if-bootstrapped", false, "Whether to continue on error if the instance has already been bootstrapped") - bootstrapCmd.Flags().String("k8-secret-template", "", "The template to use for rendering the Kubernetes secret (entire secret YAML)") + bootstrapCmd.Flags().String("k8-secret-template", "", "The template to use for rendering the Kubernetes secret (entire secret JSON)") bootstrapCmd.Flags().String("k8-secret-namespace", "", "The namespace to use for the Kubernetes secret") bootstrapCmd.Flags().String("k8-secret-name", "", "The name of the Kubernetes secret to create") rootCmd.AddCommand(bootstrapCmd) From cfe51d4a52f9d4a6c207fa85ee8041cb36bc963f Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 03:50:56 +0800 Subject: [PATCH 05/13] misc: improved template dcs --- .../guides/automated-bootstrapping.mdx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/self-hosting/guides/automated-bootstrapping.mdx b/docs/self-hosting/guides/automated-bootstrapping.mdx index 7a34fc9f8..b4b77d5c1 100644 --- a/docs/self-hosting/guides/automated-bootstrapping.mdx +++ b/docs/self-hosting/guides/automated-bootstrapping.mdx @@ -109,13 +109,20 @@ The Helm chart auto bootstrap feature: ### Template System -The `secretTemplate` field allows you to customize the data section of the created Kubernetes secret. The template has access to the full bootstrap response and includes helper functions: +The `secretTemplate` field allows you to customize the data section of the created Kubernetes secret. The template has access to the full bootstrap response with the following available data fields: -- `{{ .Identity.Credentials.Token }}` - The admin machine identity token -- `{{ .Organization.ID }}` - The created organization ID -- `{{ .Organization.Slug }}` - The organization slug -- `{{ .User.Email }}` - The admin user email -- `b64enc` function for base64 encoding values +- `{{ .Identity.Credentials.Token }}`: The admin machine identity token +- `{{ .Identity.ID }}`: The identity ID +- `{{ .Identity.Name }}`: The identity name +- `{{ .Organization.ID }}`: The organization ID +- `{{ .Organization.Name }}`: The organization name +- `{{ .Organization.Slug }}`: The organization slug +- `{{ .User.Email }}`: The admin user email +- `{{ .User.ID }}`: The admin user ID +- `{{ .User.FirstName }}`: The admin user first name +- `{{ .User.LastName }}`: The admin user last name + +The template also supports the `b64enc` function for base64 encoding values. Example template for storing multiple values: From bd54054bc37f7d4faff07ef1cf02be87b8caf020 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 03:53:57 +0800 Subject: [PATCH 06/13] misc: enabled auto bootstrap for check --- .../run-helm-chart-tests-infisical-standalone-postgres.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml b/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml index d8f6466c6..50cb9ecb0 100644 --- a/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml +++ b/.github/workflows/run-helm-chart-tests-infisical-standalone-postgres.yml @@ -64,5 +64,5 @@ jobs: --config ct.yaml \ --charts helm-charts/infisical-standalone-postgres \ --helm-extra-args="--timeout=300s" \ - --helm-extra-set-args="--set ingress.nginx.enabled=false --set infisical.autoDatabaseSchemaMigration=false --set infisical.replicaCount=1 --set infisical.image.tag=v0.132.2-postgres" \ + --helm-extra-set-args="--set ingress.nginx.enabled=false --set infisical.autoDatabaseSchemaMigration=false --set infisical.replicaCount=1 --set infisical.image.tag=v0.132.2-postgres --set infisical.autoBootstrap.enabled=true" \ --namespace infisical-standalone-postgres From 8e3918ada3802edccdef1e423997265c1a04ecd7 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 20:20:53 +0800 Subject: [PATCH 07/13] misc: addressed tag issue for CLI --- .../infisical-standalone-postgres/templates/bootstrap-job.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml b/helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml index b8631a681..23cac2bb8 100644 --- a/helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml +++ b/helm-charts/infisical-standalone-postgres/templates/bootstrap-job.yaml @@ -40,7 +40,7 @@ spec: echo "Infisical is ready! Proceeding with bootstrap..." containers: - name: infisical-bootstrap - image: "infisical/cli:{{ $infisicalValues.image.tag }}" + image: "infisical/cli:{{ $infisicalValues.autoBootstrap.image.tag }}" imagePullPolicy: {{ $infisicalValues.image.pullPolicy | default "IfNotPresent" }} args: - bootstrap From 470d7cca6a7721d2e76207b65a349f72fc7ec2ea Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 19 Jun 2025 20:57:42 +0800 Subject: [PATCH 08/13] misc: updated chart version --- helm-charts/infisical-standalone-postgres/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-charts/infisical-standalone-postgres/Chart.yaml b/helm-charts/infisical-standalone-postgres/Chart.yaml index 1f7efa8da..2c55b16f5 100644 --- a/helm-charts/infisical-standalone-postgres/Chart.yaml +++ b/helm-charts/infisical-standalone-postgres/Chart.yaml @@ -7,7 +7,7 @@ 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: 1.5.0 +version: 1.6.0 # 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 From f1587d83752b3b43d50541bf847c9d76fdaeffad Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Tue, 24 Jun 2025 18:18:07 +0800 Subject: [PATCH 09/13] misc: addressed comments --- cli/packages/cmd/bootstrap.go | 6 +++--- docs/cli/commands/bootstrap.mdx | 6 +++--- docs/self-hosting/guides/automated-bootstrapping.mdx | 12 +++++++----- .../infisical-standalone-postgres/values.yaml | 4 ++-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/cli/packages/cmd/bootstrap.go b/cli/packages/cmd/bootstrap.go index 5f6e6d047..e6c46a2a2 100644 --- a/cli/packages/cmd/bootstrap.go +++ b/cli/packages/cmd/bootstrap.go @@ -43,7 +43,7 @@ func handleK8SecretOutput(bootstrapResponse api.BootstrapInstanceResponse, k8Sec // Parse and execute the template to render only the data/stringData section tmpl, err := template.New("k8-secret-template").Funcs(template.FuncMap{ - "b64enc": func(s string) string { + "encodeBase64": func(s string) string { return base64.StdEncoding.EncodeToString([]byte(s)) }, }).Parse(k8SecretTemplate) @@ -291,8 +291,8 @@ func init() { bootstrapCmd.Flags().String("organization", "", "The name of the organization to create for the instance") bootstrapCmd.Flags().String("output", "", "The type of output to use for the bootstrap command (json or k8-secret)") bootstrapCmd.Flags().Bool("ignore-if-bootstrapped", false, "Whether to continue on error if the instance has already been bootstrapped") - bootstrapCmd.Flags().String("k8-secret-template", "", "The template to use for rendering the Kubernetes secret (entire secret JSON)") - bootstrapCmd.Flags().String("k8-secret-namespace", "", "The namespace to use for the Kubernetes secret") + bootstrapCmd.Flags().String("k8-secret-template", "{\"data\":{\"token\":\"{{.Identity.Credentials.Token | encodeBase64}}\"}}", "The template to use for rendering the Kubernetes secret (entire secret JSON)") + bootstrapCmd.Flags().String("k8-secret-namespace", "", "The namespace to create the Kubernetes secret in") bootstrapCmd.Flags().String("k8-secret-name", "", "The name of the Kubernetes secret to create") rootCmd.AddCommand(bootstrapCmd) } diff --git a/docs/cli/commands/bootstrap.mdx b/docs/cli/commands/bootstrap.mdx index 156cfabb8..0a7a2791c 100644 --- a/docs/cli/commands/bootstrap.mdx +++ b/docs/cli/commands/bootstrap.mdx @@ -104,7 +104,7 @@ When using `k8-secret`, the command will create or update a Kubernetes secret di ```bash # Example template that stores the token -infisical bootstrap --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token | b64enc}}"}}' +infisical bootstrap --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token | encodeBase64}}"}}' # Example template with multiple fields infisical bootstrap --k8-secret-template='{"stringData":{"token":"{{.Identity.Credentials.Token}}","org-id":"{{.Organization.ID}}","user-email":"{{.User.Email}}"}}' @@ -112,7 +112,7 @@ infisical bootstrap --k8-secret-template='{"stringData":{"token":"{{.Identity.Cr Available template functions: -- `b64enc`: Base64 encode a string +- `encodeBase64`: Base64 encode a string Available data fields: @@ -189,7 +189,7 @@ The command returns a JSON response with details about the created user, organiz ### Kubernetes Secret Output -When using `--output=k8-secret`, the command creates or updates a Kubernetes secret in your cluster and logs the operation result. +When using `--output=k8-secret`, the command creates or updates a Kubernetes secret in your cluster and logs the operation result. This is particularly useful for automated bootstrapping scenarios such as Kubernetes Jobs, GitOps workflows, or when you need to immediately store the admin credentials for use by other applications in your cluster. ## Kubernetes Integration diff --git a/docs/self-hosting/guides/automated-bootstrapping.mdx b/docs/self-hosting/guides/automated-bootstrapping.mdx index b4b77d5c1..05b8989ed 100644 --- a/docs/self-hosting/guides/automated-bootstrapping.mdx +++ b/docs/self-hosting/guides/automated-bootstrapping.mdx @@ -70,6 +70,8 @@ You can bootstrap an Infisical instance using either the API or the CLI. When deploying Infisical using the official Helm chart, you can enable automatic bootstrapping that runs as part of the deployment process. This eliminates the need to manually bootstrap the instance after deployment. +The bootstrapping process automatically generates a Kubernetes secret containing the instance admin token, which can then be referenced by Crossplane providers, Terraform operators, or other automation systems for further infrastructure provisioning and configuration. + ### Configuration Enable auto bootstrapping in your Helm values by setting `autoBootstrap.enabled: true` and providing the necessary configuration: @@ -78,7 +80,7 @@ Enable auto bootstrapping in your Helm values by setting `autoBootstrap.enabled: autoBootstrap: enabled: true organization: "My Organization" - secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | b64enc}}"}}' + secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | encodeBase64}}"}}' secretDestination: name: "infisical-bootstrap-secret" @@ -122,7 +124,7 @@ The `secretTemplate` field allows you to customize the data section of the creat - `{{ .User.FirstName }}`: The admin user first name - `{{ .User.LastName }}`: The admin user last name -The template also supports the `b64enc` function for base64 encoding values. +The template also supports the `encodeBase64` function for base64 encoding values. Example template for storing multiple values: @@ -130,9 +132,9 @@ Example template for storing multiple values: secretTemplate: | { "data": { - "infisical_token": "{{ .Identity.Credentials.Token | b64enc }}", - "admin_email": "{{ .User.Email | b64enc }}", - "organization": "{{ .Organization.Name | b64enc }}" + "infisical_token": "{{ .Identity.Credentials.Token | encodeBase64 }}", + "admin_email": "{{ .User.Email | encodeBase64 }}", + "organization": "{{ .Organization.Name | encodeBase64 }}" } } ``` diff --git a/helm-charts/infisical-standalone-postgres/values.yaml b/helm-charts/infisical-standalone-postgres/values.yaml index 2d5f12abc..9a4d69163 100644 --- a/helm-charts/infisical-standalone-postgres/values.yaml +++ b/helm-charts/infisical-standalone-postgres/values.yaml @@ -21,8 +21,8 @@ infisical: # -- Infisical Infisical CLI image tag version tag: "0.41.86" - # -- Template for the data/stringData section of the Kubernetes secret. Available functions: b64enc - secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | b64enc}}"}}' + # -- Template for the data/stringData section of the Kubernetes secret. Available functions: encodeBase64 + secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | encodeBase64}}"}}' secretDestination: # -- Name of the bootstrap secret to create in the Kubernetes cluster which will store the formatted root identity credentials From 02a23099539f57ebb7bb35550386882fb26c53a7 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Tue, 24 Jun 2025 18:26:17 +0800 Subject: [PATCH 10/13] misc: added note for bootstrap output flag --- docs/cli/commands/bootstrap.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli/commands/bootstrap.mdx b/docs/cli/commands/bootstrap.mdx index 0a7a2791c..d9dc5c592 100644 --- a/docs/cli/commands/bootstrap.mdx +++ b/docs/cli/commands/bootstrap.mdx @@ -95,7 +95,7 @@ This flag is optional and defaults to `false`. infisical bootstrap --output=k8-secret --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token}}"}}' --k8-secret-name=infisical-bootstrap --k8-secret-namespace=default ``` -When using `k8-secret`, the command will create or update a Kubernetes secret directly in your cluster. +When using `k8-secret`, the command will create or update a Kubernetes secret directly in your cluster. Note that this option requires the command to be executed from within a Kubernetes pod with appropriate service account permissions. From 1d7da56b401a599a6a3a990480101b81f15926b3 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Wed, 25 Jun 2025 02:38:51 +0800 Subject: [PATCH 11/13] misc: used kubernetes client --- cli/go.mod | 35 +++- cli/go.sum | 84 ++++++++-- cli/packages/cmd/bootstrap.go | 149 ++++++++---------- docs/cli/commands/bootstrap.mdx | 2 +- .../guides/automated-bootstrapping.mdx | 8 +- .../infisical-standalone-postgres/values.yaml | 2 +- 6 files changed, 171 insertions(+), 109 deletions(-) diff --git a/cli/go.mod b/cli/go.mod index e6d55eb49..3afc8d3be 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -40,6 +40,9 @@ require ( golang.org/x/term v0.30.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.31.4 + k8s.io/apimachinery v0.31.4 + k8s.io/client-go v0.31.4 ) require ( @@ -70,16 +73,25 @@ require ( github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/errors v0.20.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/strfmt v0.21.3 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/uuid v1.6.0 // indirect @@ -90,17 +102,23 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.5 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/muesli/mango v0.1.0 // indirect github.com/muesli/mango-pflag v0.1.0 // indirect github.com/muesli/termenv v0.15.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/ginkgo/v2 v2.22.2 // indirect github.com/pelletier/go-toml v1.9.3 // indirect @@ -117,6 +135,7 @@ require ( github.com/tetratelabs/wazero v1.9.0 // indirect github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect github.com/wlynxg/anet v0.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect go.mongodb.org/mongo-driver v1.10.0 // indirect go.opencensus.io v0.24.0 // indirect @@ -127,18 +146,26 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/mod v0.23.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.6.0 // indirect + golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.30.0 // indirect google.golang.org/api v0.188.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect google.golang.org/grpc v1.64.1 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.62.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) require ( diff --git a/cli/go.sum b/cli/go.sum index 2e41c756b..066f736a2 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -134,6 +134,8 @@ github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMS github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -152,6 +154,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gitleaks/go-gitdiff v0.9.1 h1:ni6z6/3i9ODT685OLCTf+s/ERlWUNWQF4x1pvoNICw0= github.com/gitleaks/go-gitdiff v0.9.1/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA= @@ -165,8 +169,16 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -174,6 +186,7 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -211,6 +224,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -222,9 +237,12 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -298,7 +316,11 @@ github.com/infisical/infisical-kmip v0.3.5 h1:QM3s0e18B+mYv3a9HQNjNAlbwZJBzXq5BA github.com/infisical/infisical-kmip v0.3.5/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs= github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= +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= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -308,6 +330,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -318,6 +341,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -346,8 +371,12 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= @@ -365,7 +394,8 @@ github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -406,8 +436,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -467,6 +497,8 @@ github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8S github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= @@ -596,8 +628,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -610,8 +642,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -693,8 +725,8 @@ golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -863,14 +895,17 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -890,6 +925,27 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM= +k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw= +k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM= +k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.4 h1:t4QEXt4jgHIkKKlx06+W3+1JOwAFU/2OPiOo7H92eRQ= +k8s.io/client-go v0.31.4/go.mod h1:kvuMro4sFYIa8sulL5Gi5GFqUPvfH2O/dXuKstbaaeg= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/cli/packages/cmd/bootstrap.go b/cli/packages/cmd/bootstrap.go index e6c46a2a2..7132b634d 100644 --- a/cli/packages/cmd/bootstrap.go +++ b/cli/packages/cmd/bootstrap.go @@ -5,8 +5,7 @@ package cmd import ( "bytes" - "crypto/tls" - "crypto/x509" + "context" "encoding/base64" "encoding/json" "fmt" @@ -17,31 +16,28 @@ import ( "github.com/Infisical/infisical-merge/packages/util" "github.com/rs/zerolog/log" "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" ) // handleK8SecretOutput processes the k8-secret output type by creating a Kubernetes secret func handleK8SecretOutput(bootstrapResponse api.BootstrapInstanceResponse, k8SecretTemplate, k8SecretName, k8SecretNamespace string) error { - // Read Kubernetes service account credentials from the pod - k8sToken, err := os.ReadFile(util.KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH) + // Create in-cluster config + config, err := rest.InClusterConfig() if err != nil { - return fmt.Errorf("failed to read Kubernetes service account token: %v", err) + return fmt.Errorf("failed to create in-cluster config: %v", err) } - k8sCaCert, err := os.ReadFile(util.KUBERNETES_SERVICE_ACCOUNT_CA_CERT_PATH) + // Create Kubernetes client + clientset, err := kubernetes.NewForConfig(config) if err != nil { - return fmt.Errorf("failed to read Kubernetes CA certificate: %v", err) + return fmt.Errorf("failed to create Kubernetes client: %v", err) } - // Get Kubernetes API server URL from environment variables - k8sHost := os.Getenv(util.KUBERNETES_SERVICE_HOST_ENV_NAME) - k8sPort := os.Getenv(util.KUBERNETES_SERVICE_PORT_HTTPS_ENV_NAME) - if k8sHost == "" || k8sPort == "" { - return fmt.Errorf("failed to get Kubernetes API server address from environment variables") - } - - k8sApiUrl := fmt.Sprintf("https://%s:%s", k8sHost, k8sPort) - - // Parse and execute the template to render only the data/stringData section + // Parse and execute the template to render the data/stringData section tmpl, err := template.New("k8-secret-template").Funcs(template.FuncMap{ "encodeBase64": func(s string) string { return base64.StdEncoding.EncodeToString([]byte(s)) @@ -64,83 +60,66 @@ func handleK8SecretOutput(bootstrapResponse api.BootstrapInstanceResponse, k8Sec return fmt.Errorf("template output is not valid JSON: %v", err) } - // Construct the complete Kubernetes secret object - k8sSecret := map[string]interface{}{ - "apiVersion": "v1", - "kind": "Secret", - "metadata": map[string]interface{}{ - "name": k8SecretName, - "namespace": k8SecretNamespace, + // Prepare the secret data and stringData maps + secretData := make(map[string][]byte) + secretStringData := make(map[string]string) + + // Process the dataSection to separate data and stringData + if data, exists := dataSection["data"]; exists { + if dataMap, ok := data.(map[string]interface{}); ok { + for key, value := range dataMap { + if strValue, ok := value.(string); ok { + secretData[key] = []byte(strValue) + } + } + } + } + + if stringData, exists := dataSection["stringData"]; exists { + if stringDataMap, ok := stringData.(map[string]interface{}); ok { + for key, value := range stringDataMap { + if strValue, ok := value.(string); ok { + secretStringData[key] = strValue + } + } + } + } + + // Create the Kubernetes secret object + k8sSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: k8SecretName, + Namespace: k8SecretNamespace, }, - "type": "Opaque", + Type: corev1.SecretTypeOpaque, + Data: secretData, + StringData: secretStringData, } - // Merge the rendered data section into the secret - for key, value := range dataSection { - k8sSecret[key] = value - } + ctx := context.Background() + secretsClient := clientset.CoreV1().Secrets(k8SecretNamespace) - // Prepare the HTTP client with TLS configuration - caCertPool := x509.NewCertPool() - if !caCertPool.AppendCertsFromPEM(k8sCaCert) { - return fmt.Errorf("failed to parse Kubernetes CA certificate") - } - - tlsConfig := &tls.Config{ - RootCAs: caCertPool, - } - - // Create a new HTTP client for Kubernetes API - k8sHttpClient, err := util.GetRestyClientWithCustomHeaders() + // Check if secret already exists + existingSecret, err := secretsClient.Get(ctx, k8SecretName, metav1.GetOptions{}) if err != nil { - return fmt.Errorf("failed to create Kubernetes HTTP client: %v", err) - } - - k8sHttpClient.SetTLSClientConfig(tlsConfig) - k8sHttpClient.SetHeader("Authorization", fmt.Sprintf("Bearer %s", string(k8sToken))) - k8sHttpClient.SetHeader("Content-Type", "application/json") - - // Check if secret already exists first - checkUrl := fmt.Sprintf("%s/api/v1/namespaces/%s/secrets/%s", k8sApiUrl, k8SecretNamespace, k8SecretName) - checkResponse, err := k8sHttpClient.R().Get(checkUrl) - - if err != nil { - return fmt.Errorf("failed to check if Kubernetes secret exists: %v", err) - } - - secretUrl := fmt.Sprintf("%s/api/v1/namespaces/%s/secrets", k8sApiUrl, k8SecretNamespace) - - if checkResponse.StatusCode() == 200 { + if errors.IsNotFound(err) { + // Secret doesn't exist, create it + _, err = secretsClient.Create(ctx, k8sSecret, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("failed to create Kubernetes secret: %v", err) + } + log.Info().Msgf("Successfully created Kubernetes secret '%s' in namespace '%s'", k8SecretName, k8SecretNamespace) + } else { + return fmt.Errorf("failed to check if Kubernetes secret exists: %v", err) + } + } else { // Secret exists, update it - secretUrl = fmt.Sprintf("%s/%s", secretUrl, k8SecretName) - response, err := k8sHttpClient.R(). - SetBody(k8sSecret). - Put(secretUrl) - + k8sSecret.ObjectMeta.ResourceVersion = existingSecret.ObjectMeta.ResourceVersion + _, err = secretsClient.Update(ctx, k8sSecret, metav1.UpdateOptions{}) if err != nil { return fmt.Errorf("failed to update Kubernetes secret: %v", err) } - - if response.IsError() { - return fmt.Errorf("kubernetes API returned error when updating secret: %s", response.String()) - } - log.Info().Msgf("Successfully updated Kubernetes secret '%s' in namespace '%s'", k8SecretName, k8SecretNamespace) - } else { - // Secret doesn't exist, create it - response, err := k8sHttpClient.R(). - SetBody(k8sSecret). - Post(secretUrl) - - if err != nil { - return fmt.Errorf("failed to create Kubernetes secret: %v", err) - } - - if response.IsError() { - return fmt.Errorf("kubernetes API returned error when creating secret: %s", response.String()) - } - - log.Info().Msgf("Successfully created Kubernetes secret '%s' in namespace '%s'", k8SecretName, k8SecretNamespace) } return nil @@ -291,7 +270,7 @@ func init() { bootstrapCmd.Flags().String("organization", "", "The name of the organization to create for the instance") bootstrapCmd.Flags().String("output", "", "The type of output to use for the bootstrap command (json or k8-secret)") bootstrapCmd.Flags().Bool("ignore-if-bootstrapped", false, "Whether to continue on error if the instance has already been bootstrapped") - bootstrapCmd.Flags().String("k8-secret-template", "{\"data\":{\"token\":\"{{.Identity.Credentials.Token | encodeBase64}}\"}}", "The template to use for rendering the Kubernetes secret (entire secret JSON)") + bootstrapCmd.Flags().String("k8-secret-template", "{\"data\":{\"token\":\"{{.Identity.Credentials.Token}}\"}}", "The template to use for rendering the Kubernetes secret (entire secret JSON)") bootstrapCmd.Flags().String("k8-secret-namespace", "", "The namespace to create the Kubernetes secret in") bootstrapCmd.Flags().String("k8-secret-name", "", "The name of the Kubernetes secret to create") rootCmd.AddCommand(bootstrapCmd) diff --git a/docs/cli/commands/bootstrap.mdx b/docs/cli/commands/bootstrap.mdx index d9dc5c592..ae31e470f 100644 --- a/docs/cli/commands/bootstrap.mdx +++ b/docs/cli/commands/bootstrap.mdx @@ -104,7 +104,7 @@ When using `k8-secret`, the command will create or update a Kubernetes secret di ```bash # Example template that stores the token -infisical bootstrap --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token | encodeBase64}}"}}' +infisical bootstrap --k8-secret-template='{"data":{"token":"{{.Identity.Credentials.Token}}"}}' # Example template with multiple fields infisical bootstrap --k8-secret-template='{"stringData":{"token":"{{.Identity.Credentials.Token}}","org-id":"{{.Organization.ID}}","user-email":"{{.User.Email}}"}}' diff --git a/docs/self-hosting/guides/automated-bootstrapping.mdx b/docs/self-hosting/guides/automated-bootstrapping.mdx index 05b8989ed..18bf90da6 100644 --- a/docs/self-hosting/guides/automated-bootstrapping.mdx +++ b/docs/self-hosting/guides/automated-bootstrapping.mdx @@ -80,7 +80,7 @@ Enable auto bootstrapping in your Helm values by setting `autoBootstrap.enabled: autoBootstrap: enabled: true organization: "My Organization" - secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | encodeBase64}}"}}' + secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token}}"}}' secretDestination: name: "infisical-bootstrap-secret" @@ -132,9 +132,9 @@ Example template for storing multiple values: secretTemplate: | { "data": { - "infisical_token": "{{ .Identity.Credentials.Token | encodeBase64 }}", - "admin_email": "{{ .User.Email | encodeBase64 }}", - "organization": "{{ .Organization.Name | encodeBase64 }}" + "infisical_token": "{{ .Identity.Credentials.Token }}", + "admin_email": "{{ .User.Email }}", + "organization": "{{ .Organization.Name }}" } } ``` diff --git a/helm-charts/infisical-standalone-postgres/values.yaml b/helm-charts/infisical-standalone-postgres/values.yaml index 9a4d69163..d2e3297d5 100644 --- a/helm-charts/infisical-standalone-postgres/values.yaml +++ b/helm-charts/infisical-standalone-postgres/values.yaml @@ -22,7 +22,7 @@ infisical: tag: "0.41.86" # -- Template for the data/stringData section of the Kubernetes secret. Available functions: encodeBase64 - secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token | encodeBase64}}"}}' + secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token}}"}}' secretDestination: # -- Name of the bootstrap secret to create in the Kubernetes cluster which will store the formatted root identity credentials From ae2da0066ac7ba1224b111bf1f2a0fe4852fc71b Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Wed, 25 Jun 2025 04:40:07 +0800 Subject: [PATCH 12/13] misc: add helm chart auto bootstrap to methods --- .../guides/automated-bootstrapping.mdx | 176 +++++++++--------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/docs/self-hosting/guides/automated-bootstrapping.mdx b/docs/self-hosting/guides/automated-bootstrapping.mdx index 18bf90da6..a2be0dc07 100644 --- a/docs/self-hosting/guides/automated-bootstrapping.mdx +++ b/docs/self-hosting/guides/automated-bootstrapping.mdx @@ -29,7 +29,7 @@ The Automated Bootstrapping workflow automates the following processes: ## Bootstrap Methods -You can bootstrap an Infisical instance using either the API or the CLI. +You can bootstrap an Infisical instance using the API, CLI, or Helm chart. @@ -64,95 +64,95 @@ You can bootstrap an Infisical instance using either the API or the CLI. This example command pipes the output through `jq` to extract only the machine identity token, making it easy to capture and use directly in automation scripts or export as an environment variable for tools like Terraform. + + When deploying Infisical using the official Helm chart, you can enable automatic bootstrapping that runs as part of the deployment process. This eliminates the need to manually bootstrap the instance after deployment. + + The bootstrapping process automatically generates a Kubernetes secret containing the instance admin token, which can then be referenced by Crossplane providers, Terraform operators, or other automation systems for further infrastructure provisioning and configuration. + + ### Configuration + + Enable auto bootstrapping in your Helm values by setting `autoBootstrap.enabled: true` and providing the necessary configuration: + + ```yaml + autoBootstrap: + enabled: true + organization: "My Organization" + secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token}}"}}' + + secretDestination: + name: "infisical-bootstrap-secret" + namespace: "default" # defaults to release namespace if not specified + + credentialSecret: + name: "infisical-bootstrap-credentials" + ``` + + You'll also need to create a secret containing the bootstrap credentials before deployment. The secret must contain `INFISICAL_ADMIN_EMAIL` and `INFISICAL_ADMIN_PASSWORD` keys: + + ```bash + kubectl create secret generic infisical-bootstrap-credentials \ + --from-literal=INFISICAL_ADMIN_EMAIL="admin@example.com" \ + --from-literal=INFISICAL_ADMIN_PASSWORD="your-secure-password" \ + --namespace=release-namespace + ``` + + ### How It Works + + The Helm chart auto bootstrap feature: + + 1. **Post-Install Hook**: Runs automatically after the main Infisical deployment is complete + 2. **Readiness Check**: Uses an init container with curl to wait for Infisical to be ready by polling the `/api/status` endpoint + 3. **Bootstrap Execution**: Uses the Infisical CLI to bootstrap the instance + 4. **Kubernetes Secret Creation**: Creates a Kubernetes secret directly via the Kubernetes API using the rendered template + 5. **RBAC**: Automatically configures the necessary permissions (`get`, `create`, `update` on secrets) for the bootstrap job + + ### Template System + + The `secretTemplate` field allows you to customize the data section of the created Kubernetes secret. The template has access to the full bootstrap response with the following available data fields: + + - `{{ .Identity.Credentials.Token }}`: The admin machine identity token + - `{{ .Identity.ID }}`: The identity ID + - `{{ .Identity.Name }}`: The identity name + - `{{ .Organization.ID }}`: The organization ID + - `{{ .Organization.Name }}`: The organization name + - `{{ .Organization.Slug }}`: The organization slug + - `{{ .User.Email }}`: The admin user email + - `{{ .User.ID }}`: The admin user ID + - `{{ .User.FirstName }}`: The admin user first name + - `{{ .User.LastName }}`: The admin user last name + + The template also supports the `encodeBase64` function for base64 encoding values. + + Example template for storing multiple values: + + ```yaml + secretTemplate: | + { + "data": { + "infisical_token": "{{ .Identity.Credentials.Token }}", + "admin_email": "{{ .User.Email }}", + "organization": "{{ .Organization.Name }}" + } + } + ``` + + ### Benefits + + - **Zero-Touch Deployment**: Complete Infisical setup without manual intervention + - **Infrastructure as Code**: Bootstrap configuration is versioned with your Helm values + - **Secure Token Storage**: Admin identity credentials are immediately stored in Kubernetes secrets + - **Integration Ready**: The created secret can be referenced by other applications or automation tools + + ### Security Considerations + + - The bootstrap job requires permissions to create secrets in the specified namespace + - Bootstrap credentials should be stored securely and rotated regularly + - The generated admin token has full instance privileges and should be protected accordingly + - Consider using Kubernetes RBAC to restrict access to the generated secret + + -## Helm Chart Auto Bootstrap - -When deploying Infisical using the official Helm chart, you can enable automatic bootstrapping that runs as part of the deployment process. This eliminates the need to manually bootstrap the instance after deployment. - -The bootstrapping process automatically generates a Kubernetes secret containing the instance admin token, which can then be referenced by Crossplane providers, Terraform operators, or other automation systems for further infrastructure provisioning and configuration. - -### Configuration - -Enable auto bootstrapping in your Helm values by setting `autoBootstrap.enabled: true` and providing the necessary configuration: - -```yaml -autoBootstrap: - enabled: true - organization: "My Organization" - secretTemplate: '{"data":{"token":"{{.Identity.Credentials.Token}}"}}' - - secretDestination: - name: "infisical-bootstrap-secret" - namespace: "default" # defaults to release namespace if not specified - - credentialSecret: - name: "infisical-bootstrap-credentials" -``` - -You'll also need to create a secret containing the bootstrap credentials before deployment. The secret must contain `INFISICAL_ADMIN_EMAIL` and `INFISICAL_ADMIN_PASSWORD` keys: - -```bash -kubectl create secret generic infisical-bootstrap-credentials \ - --from-literal=INFISICAL_ADMIN_EMAIL="admin@example.com" \ - --from-literal=INFISICAL_ADMIN_PASSWORD="your-secure-password" \ - --namespace=release-namespace -``` - -### How It Works - -The Helm chart auto bootstrap feature: - -1. **Post-Install Hook**: Runs automatically after the main Infisical deployment is complete -2. **Readiness Check**: Uses an init container with curl to wait for Infisical to be ready by polling the `/api/status` endpoint -3. **Bootstrap Execution**: Uses the Infisical CLI to bootstrap the instance -4. **Kubernetes Secret Creation**: Creates a Kubernetes secret directly via the Kubernetes API using the rendered template -5. **RBAC**: Automatically configures the necessary permissions (`get`, `create`, `update` on secrets) for the bootstrap job - -### Template System - -The `secretTemplate` field allows you to customize the data section of the created Kubernetes secret. The template has access to the full bootstrap response with the following available data fields: - -- `{{ .Identity.Credentials.Token }}`: The admin machine identity token -- `{{ .Identity.ID }}`: The identity ID -- `{{ .Identity.Name }}`: The identity name -- `{{ .Organization.ID }}`: The organization ID -- `{{ .Organization.Name }}`: The organization name -- `{{ .Organization.Slug }}`: The organization slug -- `{{ .User.Email }}`: The admin user email -- `{{ .User.ID }}`: The admin user ID -- `{{ .User.FirstName }}`: The admin user first name -- `{{ .User.LastName }}`: The admin user last name - -The template also supports the `encodeBase64` function for base64 encoding values. - -Example template for storing multiple values: - -```yaml -secretTemplate: | - { - "data": { - "infisical_token": "{{ .Identity.Credentials.Token }}", - "admin_email": "{{ .User.Email }}", - "organization": "{{ .Organization.Name }}" - } - } -``` - -### Benefits - -- **Zero-Touch Deployment**: Complete Infisical setup without manual intervention -- **Infrastructure as Code**: Bootstrap configuration is versioned with your Helm values -- **Secure Token Storage**: Admin identity credentials are immediately stored in Kubernetes secrets -- **Integration Ready**: The created secret can be referenced by other applications or automation tools - -### Security Considerations - -- The bootstrap job requires permissions to create secrets in the specified namespace -- Bootstrap credentials should be stored securely and rotated regularly -- The generated admin token has full instance privileges and should be protected accordingly -- Consider using Kubernetes RBAC to restrict access to the generated secret - ## API Response Structure The bootstrap process returns a JSON response with details about the created user, organization, and machine identity: From 0eb36d7e35ded29a233046b21902bce39af09f04 Mon Sep 17 00:00:00 2001 From: Sheen <65645666+sheensantoscapadngan@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:56:06 +0000 Subject: [PATCH 13/13] misc: final doc changes --- .../guides/automated-bootstrapping.mdx | 110 +++++++++++------- 1 file changed, 66 insertions(+), 44 deletions(-) diff --git a/docs/self-hosting/guides/automated-bootstrapping.mdx b/docs/self-hosting/guides/automated-bootstrapping.mdx index a2be0dc07..3c2186eb9 100644 --- a/docs/self-hosting/guides/automated-bootstrapping.mdx +++ b/docs/self-hosting/guides/automated-bootstrapping.mdx @@ -5,14 +5,13 @@ description: "Learn how to provision and configure Infisical instances programma Infisical's Automated Bootstrapping feature enables you to provision and configure an Infisical instance without using the UI, allowing for complete automation through static configuration files, API calls, or CLI commands. This is especially valuable for enterprise environments where automated deployment and infrastructure-as-code practices are essential. -## Overview +The bootstrapping workflow automates creating an admin user account, initializing an organization for the entire instance, establishing an **instance admin machine identity** with full administrative permissions, and returning the machine identity credentials for further automation. -The Automated Bootstrapping workflow automates the following processes: +## Prerequisites -- Creating an admin user account -- Initializing an organization for the entire instance -- Establishing an **instance admin machine identity** with full administrative permissions -- Returning the machine identity credentials for further automation +- An Infisical instance launched with all required configuration variables +- Access to the Infisical CLI or the ability to make API calls to the instance +- Network connectivity to the Infisical instance ## Key Concepts @@ -21,12 +20,6 @@ The Automated Bootstrapping workflow automates the following processes: ![Instance Admin Identity](/images/self-hosting/guides/automated-bootstrapping/identity-instance-admin.png) - **Token Auth**: The instance admin machine identity uses [Token Auth](/documentation/platform/identities/token-auth), providing a JWT token that can be used directly to make authenticated requests to the Infisical API. -## Prerequisites - -- An Infisical instance launched with all required configuration variables -- Access to the Infisical CLI or the ability to make API calls to the instance -- Network connectivity to the Infisical instance - ## Bootstrap Methods You can bootstrap an Infisical instance using the API, CLI, or Helm chart. @@ -53,6 +46,36 @@ You can bootstrap an Infisical instance using the API, CLI, or Helm chart. http://your-infisical-instance.com/api/v1/admin/bootstrap ``` + ### API Response Structure + + The bootstrap process returns a JSON response with details about the created user, organization, and machine identity: + + ```json + { + "identity": { + "credentials": { + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eUlkIjoiZGIyMjQ3OTItZWQxOC00Mjc3LTlkYWUtNTdlNzUyMzE1ODU0IiwiaWRlbnRpdHlBY2Nlc3NUb2tlbklkIjoiZmVkZmZmMGEtYmU3Yy00NjViLWEwZWEtZjM5OTNjMTg4OGRlIiwiYXV0aFRva2VuVHlwZSI6ImlkZW50aXR5QWNjZXNzVG9rZW4iLCJpYXQiOjE3NDIzMjI0ODl9.mqcZZqIFqER1e9ubrQXp8FbzGYi8nqqZwfMvz09g-8Y" + }, + "id": "db224792-ed18-4277-9dae-57e752315854", + "name": "Instance Admin Identity" + }, + "message": "Successfully bootstrapped instance", + "organization": { + "id": "b56bece0-42f5-4262-b25e-be7bf5f84957", + "name": "dog", + "slug": "dog-v-e5l" + }, + "user": { + "email": "admin@example.com", + "firstName": "Admin", + "id": "a418f355-c8da-453c-bbc8-6c07208eeb3c", + "lastName": "User", + "superAdmin": true, + "username": "admin@example.com" + } + } + ``` + Use the [Infisical CLI](/cli/commands/bootstrap) to bootstrap the instance and extract the token for immediate use in automation: @@ -62,10 +85,39 @@ You can bootstrap an Infisical instance using the API, CLI, or Helm chart. ``` This example command pipes the output through `jq` to extract only the machine identity token, making it easy to capture and use directly in automation scripts or export as an environment variable for tools like Terraform. + + ### API Response Structure + The bootstrap process returns a JSON response with details about the created user, organization, and machine identity: + + ```json + { + "identity": { + "credentials": { + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eUlkIjoiZGIyMjQ3OTItZWQxOC00Mjc3LTlkYWUtNTdlNzUyMzE1ODU0IiwiaWRlbnRpdHlBY2Nlc3NUb2tlbklkIjoiZmVkZmZmMGEtYmU3Yy00NjViLWEwZWEtZjM5OTNjMTg4OGRlIiwiYXV0aFRva2VuVHlwZSI6ImlkZW50aXR5QWNjZXNzVG9rZW4iLCJpYXQiOjE3NDIzMjI0ODl9.mqcZZqIFqER1e9ubrQXp8FbzGYi8nqqZwfMvz09g-8Y" + }, + "id": "db224792-ed18-4277-9dae-57e752315854", + "name": "Instance Admin Identity" + }, + "message": "Successfully bootstrapped instance", + "organization": { + "id": "b56bece0-42f5-4262-b25e-be7bf5f84957", + "name": "dog", + "slug": "dog-v-e5l" + }, + "user": { + "email": "admin@example.com", + "firstName": "Admin", + "id": "a418f355-c8da-453c-bbc8-6c07208eeb3c", + "lastName": "User", + "superAdmin": true, + "username": "admin@example.com" + } + } + ``` - - When deploying Infisical using the official Helm chart, you can enable automatic bootstrapping that runs as part of the deployment process. This eliminates the need to manually bootstrap the instance after deployment. + + When deploying Infisical using the official [Helm chart](/self-hosting/deployment-options/kubernetes-helm#kubernetes-via-helm-chart), you can enable automatic bootstrapping that runs as part of the deployment process. This eliminates the need to manually bootstrap the instance after deployment. The bootstrapping process automatically generates a Kubernetes secret containing the instance admin token, which can then be referenced by Crossplane providers, Terraform operators, or other automation systems for further infrastructure provisioning and configuration. @@ -153,36 +205,6 @@ You can bootstrap an Infisical instance using the API, CLI, or Helm chart. -## API Response Structure - -The bootstrap process returns a JSON response with details about the created user, organization, and machine identity: - -```json -{ - "identity": { - "credentials": { - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eUlkIjoiZGIyMjQ3OTItZWQxOC00Mjc3LTlkYWUtNTdlNzUyMzE1ODU0IiwiaWRlbnRpdHlBY2Nlc3NUb2tlbklkIjoiZmVkZmZmMGEtYmU3Yy00NjViLWEwZWEtZjM5OTNjMTg4OGRlIiwiYXV0aFRva2VuVHlwZSI6ImlkZW50aXR5QWNjZXNzVG9rZW4iLCJpYXQiOjE3NDIzMjI0ODl9.mqcZZqIFqER1e9ubrQXp8FbzGYi8nqqZwfMvz09g-8Y" - }, - "id": "db224792-ed18-4277-9dae-57e752315854", - "name": "Instance Admin Identity" - }, - "message": "Successfully bootstrapped instance", - "organization": { - "id": "b56bece0-42f5-4262-b25e-be7bf5f84957", - "name": "dog", - "slug": "dog-v-e5l" - }, - "user": { - "email": "admin@example.com", - "firstName": "Admin", - "id": "a418f355-c8da-453c-bbc8-6c07208eeb3c", - "lastName": "User", - "superAdmin": true, - "username": "admin@example.com" - } -} -``` - ## Using the Instance Admin Machine Identity Token The bootstrap process automatically creates a machine identity with Token Auth configured. The returned token has instance-level admin privileges (the highest level of access) and should be treated with the same security considerations as a root credential.