Posted on:September 17, 2026 at 12:00 AM

Infisical 처음부터 끝까지: 셀프 호스팅, CLI, Docker Compose, Kubernetes

Infisical 처음부터 끝까지: 셀프 호스팅, CLI, Docker Compose, Kubernetes

Infisical 처음부터 끝까지: 셀프 호스팅, CLI, Docker Compose, Kubernetes

대부분의 프로젝트는 .env 파일로 시작합니다. 그리고 대부분 거기서 끝납니다. 그 파일을 scp로 서버에 올리고, 새 팀원이 오면 Slack에 붙여넣고, 노트북 세 대에 그대로 남겨둡니다. 어느 값이 최신인지는 아무도 모릅니다. 키 하나가 새면 복사본을 전부 찾아다녀야 합니다.

이 글은 그걸 Infisical로 바꾸는 이야기입니다. 시크릿이 내 장비 밖으로 나가지 않게 직접 호스팅하고, 실제로 시크릿을 쓰는 두 곳 — Docker Compose 호스트와 Kubernetes 클러스터 — 에 연결하는 데까지 갑니다.

이 글에서 다루는 내용:

  1. Infisical이란?
  2. 설치 경로 선택하기
  3. Docker Compose로 셀프 호스팅하기
  4. CLI 설치하기
  5. 로컬 .env로 프로젝트 부트스트랩하기
  6. Compose 서버에서 시크릿 쓰기 — 서버의 .env 없애기
  7. Kubernetes에서 시크릿 쓰기

이 글의 기준 버전: 공개된 docker-compose.prod.yml의 infisical/infisical:latest 서버, 그리고 v1beta1 CRD를 쓰는 Kubernetes 오퍼레이터 Helm 차트 v0.11.9. Infisical은 변화가 빠르니 붙여넣기 전에 공식 문서에서 버전을 한 번 확인하세요.


1. Infisical이란?

Infisical은 오픈소스 시크릿 관리 플랫폼입니다. 구성은 단순합니다. 시크릿을 암호화해서 보관하는 서버 하나, 그리고 런타임에 그 시크릿을 받아가는 클라이언트들(CLI, SDK, Kubernetes 오퍼레이터, CI 연동). 시크릿이 코드 옆 파일에 있을 이유가 없어지는 게 핵심이죠.

개념은 3단계 트리입니다.

Organization (조직)
└── Project (프로젝트, 예: "orders-api")
    ├── Environment: dev
    ├── Environment: staging
    └── Environment: prod
        └── Folder path: /   /database   /stripe
            └── SECRET_KEY = value

시크릿 하나의 주소는 프로젝트 + 환경 슬러그 + 폴더 경로 + 키입니다. 아래 나오는 클라이언트는 전부 — infisical run이든 오퍼레이터 CRD든 SDK든 — 결국 이 네 가지를 받습니다.

.env 파일 대신 얻는 것

.env의 문제Infisical이 하는 일
누가 복사본을 갖고 있는지 모름프로젝트·환경 단위 권한, 조회 기록은 감사 로그에 남음
키 교체가 파일 찾기 숙제가 됨값을 한 번 바꾸면 클라이언트가 다음 조회 때 새 값을 씀
시크릿이 git에 올라감커밋할 게 없음. 커밋하는 설정 파일에는 값이 안 들어감
머신에 파일을 사람이 넣어줘야 함머신 아이덴티티가 ID/시크릿으로 알아서 인증
dev와 prod 값이 슬금슬금 어긋남같은 키, 다른 환경. 한 화면에서 비교됨

이 외에 인증서 관리, 다이나믹 시크릿(필요할 때 발급받고 금방 만료되는 DB 자격 증명), 시크릿 스캐닝도 있습니다. 이 글은 정적 시크릿만 다룹니다. 대부분 그것부터 필요하니까요.

다른 도구와 비교하면

  • HashiCorp Vault — 훨씬 강력하고, 운영 부담도 훨씬 큽니다. 이미 Vault를 굴리고 있는 게 아니고 “API 키나 좀 제대로 두자” 정도라면 과합니다.
  • Doppler / AWS Secrets Manager / GCP Secret Manager — 쓰는 느낌은 비슷한데 전부 호스팅 전용입니다. Infisical은 같은 제품을 그대로 셀프 호스팅할 수 있다는 게 다릅니다.
  • SOPS + age — 시크릿을 암호화해서 git에 두고 싶다면 좋은 선택입니다. 방향이 아예 다릅니다. 서버도, 감사 로그도, 런타임 API도 없습니다. GitOps에는 잘 맞고 “이 키 지금 서비스 12개에서 갈아끼워야 해”에는 안 맞습니다.

2. 설치 경로 선택하기

정할 건 두 가지인데, 실제로 고민할 건 두 번째뿐입니다.

서버: Infisical Cloud(https://app.infisical.com, 무료 티어 있음)를 쓰거나 직접 호스팅합니다. 이 글 내용은 둘 다에서 돌아갑니다. 차이는 셀프 호스팅일 때 클라이언트한테 서버 주소를 알려줘야 한다는 것뿐입니다. 3장부터는 셀프 호스팅 기준입니다.

셀프 호스팅 방식:

방식이럴 때대신 감수할 것
Docker Compose호스트 하나, 홈랩, 소규모 팀단일 노드, HA 없음. 문서가 권하는 출발점
Kubernetes (Helm)이미 Kubernetes를 쓰고 있을 때손 댈 게 많고 Postgres·Redis는 직접 관리
스탠드얼론 Docker기존 오케스트레이터에 끼워 넣을 때Postgres와 Redis를 직접 붙여야 함

3장에서 다룰 건 Docker Compose입니다. 헷갈리기 쉬운 구분을 하나 짚고 갑니다. Compose로 Infisical을 띄우는 것(3장)과 Compose 앱에서 Infisical 시크릿을 꺼내 쓰는 것(6장)은 별개입니다. Infisical 서버가 앱과 같은 호스트에 있을 필요도 없습니다.

준비물

  • Docker와 Compose 플러그인이 있는 호스트 (docker compose version이 되면 됩니다)
  • 여유 메모리 2 GB 정도. 스택은 Node 백엔드, Postgres, Redis입니다
  • 내 노트북 말고 다른 데서도 접속할 거면 DNS 이름과 TLS

3. Docker Compose로 셀프 호스팅하기

3.1 파일 받기

mkdir -p ~/infisical && cd ~/infisical

curl -o docker-compose.prod.yml \
  https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.prod.yml

curl -o .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example

Compose 파일에는 서비스가 셋 들어 있습니다. backend(infisical/infisical:latest), db(postgres:14-alpine), redis. 네임드 볼륨 pg_data·redis_data를 쓰고, 백엔드를 80:8080으로 엽니다.

3.2 진짜 키 만들기

예제 .env에는 개발용이라고 대놓고 적혀 있는 샘플 값이 들어 있습니다. 다른 것보다 이걸 먼저 바꾸세요.

echo "ENCRYPTION_KEY=$(openssl rand -hex 16)"
echo "AUTH_SECRET=$(openssl rand -base64 32)"

두 키는 하는 일이 다릅니다.

변수하는 일형식
ENCRYPTION_KEYPostgres에 들어가는 모든 시크릿을 암호화16진수 32자 (rand -hex 16)
AUTH_SECRET세션과 JWT 토큰 서명base64, 32바이트

ENCRYPTION_KEY는 지금 바로, 이 호스트 밖에 백업하세요. 이 키 없는 Postgres 덤프는 못 읽습니다. 키를 새로 만들어서 DB만 복원하면 로그인 화면은 뜨는데 시크릿은 하나도 못 건집니다. 나중에 교체하는 절차가 문서에 있긴 하지만, 장애 났을 때 즉흥으로 할 일은 아닙니다.

3.3 나머지 .env 채우기

# --- 필수 ---
ENCRYPTION_KEY=<위에서 만든 16진수 32자>
AUTH_SECRET=<위에서 만든 base64 문자열>

POSTGRES_USER=infisical
POSTGRES_PASSWORD=<충분히 긴 랜덤 비밀번호>
POSTGRES_DB=infisical
DB_CONNECTION_URI=postgres://infisical:<위와 같은 비밀번호>@db:5432/infisical

REDIS_URL=redis://redis:6379

# 사용자와 클라이언트가 실제로 접속할 URL. 서버라면 "localhost"면 안 됩니다.
SITE_URL=https://secrets.example.com

# --- 선택이지만 권장: 초대와 비밀번호 재설정에 쓰는 SMTP ---
SMTP_HOST=
SMTP_PORT=
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=
SMTP_FROM_NAME=Infisical

처음부터 제대로 맞춰야 할 게 둘 있습니다.

  • DB_CONNECTION_URI는 POSTGRES_*와 일치해야 합니다. 호스트명은 Compose 서비스 이름인 db지 localhost가 아닙니다.
  • SITE_URL은 밖에서 닿는 URL이어야 합니다. 이 값이 초대 링크와 메일에 그대로 들어갑니다. 틀리면 아무도 못 누르는 링크가 나갑니다.

이제 이 파일이 전부의 열쇠니까 권한을 조입니다.

chmod 600 .env

3.4 실행

docker compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml ps

확인: 컨테이너 셋이 전부 running이고 db가 healthy로 나와야 합니다. backend가 재시작을 반복하면 거의 DB_CONNECTION_URI 문제입니다.

docker compose -f docker-compose.prod.yml logs -f backend

3.5 첫 로그인

http://<host>/를 엽니다. 백엔드가 80 포트로 열려 있습니다. 제일 먼저 가입한 계정이 인스턴스 관리자가 됩니다. 스택 뜨자마자, 그리고 이 호스트가 아무 데서나 닿기 전에 가입부터 하세요.

그다음 조직을 만들고, 첫 프로젝트를 만들고, 프로젝트 설정 페이지에서 프로젝트 ID를 적어둡니다. 아래 나오는 머신 클라이언트가 전부 이 값을 씁니다.

3.6 앞에 TLS 세우기

Compose 스택은 평문 HTTP입니다. 노트북 밖으로 나갈 거면 Caddy든 nginx든 Cloudflare Tunnel이든 앞에 세워서 TLS를 끊고 호스트 80 포트로 넘기세요. 그다음 SITE_URL을 https:// 주소로 바꾸고 백엔드를 재시작하면 됩니다.


4. CLI 설치하기

앞으로 CLI를 제일 많이 씁니다. 프로젝트를 잡고, 프로세스에 시크릿을 넣어주고, 머신을 인증합니다.

# macOS
brew install infisical/get-cli/infisical

# Debian / Ubuntu
curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | sudo -E bash
sudo apt-get update && sudo apt-get install -y infisical

# RHEL / CentOS / Amazon Linux
curl -1sLf 'https://artifacts-cli.infisical.com/setup.rpm.sh' | sudo -E bash
sudo yum install infisical

# Node가 있는 모든 플랫폼
npm install -g @infisical/cli
infisical --version

서버나 CI에서는 latest를 따라가지 말고 버전을 고정하세요. 재설치할 때 동작이 조용히 바뀌는 걸 막아줍니다.

CLI가 내 서버를 보게 하기

셀프 호스팅에서 제일 많이 걸려 넘어지는 지점입니다. CLI 기본값은 Infisical Cloud입니다. 이걸 안 해두면 infisical login이 아무 말 없이 app.infisical.com에 로그인한 다음, 내 프로젝트가 없다고 알려줍니다.

우선순위 높은 순서대로:

지정하는 곳예시
--domain 플래그infisical login --domain="https://secrets.example.com"
INFISICAL_DOMAIN 환경 변수export INFISICAL_DOMAIN=https://secrets.example.com
.infisical.json의 domain 필드프로젝트별로 인스턴스를 고정
기본값https://app.infisical.com

(옛날 변수인 INFISICAL_API_URL도 아직 먹지만, 둘 다 있으면 INFISICAL_DOMAIN이 이깁니다.)

/api는 붙이지 마세요. 셸 프로필에 한 줄 넣어두는 게 제일 편합니다.

export INFISICAL_DOMAIN="https://secrets.example.com"
infisical login

사람이 노트북에서 쓸 때는 infisical login이 브라우저를 열고 자격 증명을 OS 키체인에 넣습니다. 머신은 다릅니다. 6장에서 봅니다.


5. 로컬 .env로 프로젝트 부트스트랩하기

값이 잔뜩 든 .env가 이미 있다고 합시다. 하나씩 타이핑하지 않고 Infisical로 옮기는 게 목표입니다.

5.1 디렉터리를 프로젝트에 연결하기

프로젝트 루트에서:

cd ~/code/orders-api
infisical init

.infisical.json이 생깁니다.

{
  "workspaceId": "63ee5410a45f7a1ed39ba118",
  "defaultEnvironment": "dev",
  "domain": "https://secrets.example.com"
}

이 파일은 커밋하세요. 시크릿은 없고, 이 디렉터리가 어느 프로젝트·어느 인스턴스 소속인지만 들어 있습니다. domain까지 적어두면 팀원들이 각자 INFISICAL_DOMAIN을 export 안 해도 됩니다.

온 김에 진짜 시크릿은 git에 못 들어가게 막아둡니다.

grep -qxF '.env' .gitignore || echo '.env' >> .gitignore

5.2 기존 .env 올리기

infisical secrets set은 KEY=value를 몇 개든 받습니다. 파일 하나를 통째로 한 번에 올릴 수 있죠.

#!/usr/bin/env bash
# import-env.sh — 로컬 dotenv 파일을 Infisical 환경으로 올린다
set -euo pipefail

ENV_FILE="${1:-.env}"
ENV_SLUG="${2:-dev}"
SECRET_PATH="${3:-/}"

[[ -f "$ENV_FILE" ]] || { echo "파일이 없습니다: $ENV_FILE" >&2; exit 1; }

# KEY=value 줄만 남기고 주석, 빈 줄, `export ` 접두사를 걷어낸다.
mapfile -t PAIRS < <(
  sed -e 's/^[[:space:]]*export[[:space:]]\+//' "$ENV_FILE" \
    | grep -E '^[A-Za-z_][A-Za-z0-9_]*=' \
    | sed -e 's/=[[:space:]]*"\(.*\)"[[:space:]]*$/=\1/' \
          -e "s/=[[:space:]]*'\(.*\)'[[:space:]]*\$/=\1/"
)

(( ${#PAIRS[@]} )) || { echo "$ENV_FILE 에서 KEY=value 줄을 못 찾았습니다" >&2; exit 1; }

echo "시크릿 ${#PAIRS[@]}개를 env=$ENV_SLUG path=$SECRET_PATH 로 올립니다"
printf '  %s\n' "${PAIRS[@]%%=*}"

infisical secrets set "${PAIRS[@]}" --env="$ENV_SLUG" --path="$SECRET_PATH"
chmod +x import-env.sh
./import-env.sh .env dev /
./import-env.sh .env.production prod /

export 접두사랑 값 감싼 따옴표를 떼는 게 요점입니다. 대충 짠 한 줄짜리가 보통 여기서 깨집니다. 여러 줄짜리 값은 일부러 안 건드립니다. 바로 아래를 보세요.

확인:

infisical secrets --env=dev

5.3 올리기 전에 알아둘 것들

여러 줄 값이나 파일 내용(PEM 키, 서비스 계정 JSON)은 붙여넣지 말고 파일에서 읽히세요.

infisical secrets set PRIVATE_KEY=@./private.pem
infisical secrets set GCP_SA_JSON=@./service-account.json

값이 @로 시작하면 이스케이프해야 합니다: infisical secrets set EMAIL="\@example.com".

키가 스무 개쯤 넘어가면 폴더로 나누세요.

infisical secrets folders create --name=database --env=dev
infisical secrets set DB_PASSWORD=... --env=dev --path="/database"

--tag를 붙여두면 나중에 그 묶음만 따로 꺼낼 수 있습니다.

infisical secrets set STRIPE_KEY=... --tag=payments --env=prod

5.4 .env 없이 로컬에서 돌리기

개발자 입장에서 여기가 본론입니다.

infisical run --env=dev -- npm run dev

infisical run은 시크릿을 받아서 자식 프로세스에 환경 변수로 넣어줍니다. 디스크에는 안 씁니다. 쓸 만한 플래그:

플래그하는 일
--env환경 슬러그. 기본값은 dev
--path가져올 폴더. 여러 번 써도 됩니다 (--path=/common --path=/api)
--recursive--path 아래 하위 폴더까지
--watch위에서 시크릿이 바뀌면 명령을 재시작
--commandargv 대신 셸 문자열로 (--command="npm run build && npm start")
--projectId사용자 말고 머신 아이덴티티로 인증할 때는 필수

여기까지 되면 로컬 .env는 지우세요. 그러려고 한 겁니다.


6. Compose 서버에서 시크릿 사용하기

이제 서버 쪽입니다. 손으로 복사해둔 .env를 보고 docker compose up -d를 돌리는 호스트가 있고, 그 파일을 없애는 게 목표입니다.

6.1 먼저, 머신 아이덴티티

서버는 브라우저 로그인을 못 합니다. 대신 머신 아이덴티티로 Universal Auth — 클라이언트 ID와 시크릿 한 쌍 — 를 씁니다.

UI에서 Organization → Access Control → Machine Identities → Create로 갑니다. 이름(orders-api-prod)을 주고 Add Client Secret을 눌러 두 값을 복사하세요. 시크릿은 이때 한 번만 보여줍니다.

그다음 프로젝트 권한을 줍니다. Project → Access Control → Machine Identities → Add에서 읽기 전용이면 충분합니다. 배포 호스트가 시크릿을 쓸 일은 없습니다.

서비스마다, 환경마다 아이덴티티를 따로 두세요. 자격 증명을 돌려쓰면 스테이징 한 번 털렸을 때 프로덕션까지 같이 갑니다.

서버에서:

export INFISICAL_DOMAIN="https://secrets.example.com"

export INFISICAL_TOKEN=$(infisical login \
  --method=universal-auth \
  --client-id="<client-id>" \
  --client-secret="<client-secret>" \
  --plain --silent)

--plain --silent는 JWT만 뱉습니다. 그래서 위처럼 $(...) 안에 넣을 수 있죠. 이 토큰은 수명이 짧고(기본 30일), 필요하면 클라이언트 ID/시크릿으로 다시 받으면 됩니다.

이제 패턴 셋 중에 고릅니다.

6.2 패턴 A — 배포할 때 .env 만들기

기존 구성을 제일 적게 건드리는 방법입니다. .env 파일은 그대로 두되, 손으로 관리하는 것만 그만둡니다.

#!/usr/bin/env bash
# deploy.sh
set -euo pipefail
umask 077   # 만들어지는 .env가 644 말고 600이 되도록

export INFISICAL_DOMAIN="https://secrets.example.com"
PROJECT_ID="<your-project-id>"

INFISICAL_TOKEN=$(infisical login \
  --method=universal-auth \
  --client-id="$UA_CLIENT_ID" \
  --client-secret="$UA_CLIENT_SECRET" \
  --plain --silent)
export INFISICAL_TOKEN

infisical export \
  --projectId="$PROJECT_ID" \
  --env=prod \
  --path=/ \
  --format=dotenv > .env

docker compose up -d

--format은 dotenv, dotenv-export, json, yaml, csv를 받습니다.

얻는 것: 값이 한 곳에만 있고, 키 교체가 scp 대신 재배포가 됩니다.

못 얻는 것: 평문 파일은 여전히 디스크에 있습니다. 게다가 Compose는 .env를 Compose 파일 안의 ${VAR} 치환에도 씁니다. POSTGRES_PASSWORD 같은 이름이면 내 의도와 상관없이 Compose 파일에도 슬쩍 들어갑니다. 솔직히 말하면, 손으로 복사하던 것보다야 확실히 낫지만 셋 중에서는 제일 약합니다.

이 방법도 호스트에 클라이언트 ID와 시크릿은 있어야 합니다. root 소유 /etc/infisical.env에 600으로 두거나 systemd 유닛의 EnvironmentFile에 넣으세요. 부트스트랩 자격 증명은 어차피 하나 남습니다. 딱 하나만 남기고, UI에서 바로 폐기할 수 있게 만드는 게 목표입니다.

6.3 패턴 B — 파일을 아예 안 만들기

infisical run으로 Compose CLI 자체를 감싸면 됩니다.

infisical run \
  --projectId="<your-project-id>" \
  --env=prod \
  -- docker compose up -d

시크릿은 docker compose 프로세스의 환경으로 들어갑니다. Compose는 이걸 두 군데에 씁니다.

1. Compose 파일 안의 ${VAR} 치환:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

2. 값 없이 키만 적은 항목 — 셸에서 가져옵니다:

services:
  api:
    image: ghcr.io/example/orders-api:1.4.2
    environment:
      - DATABASE_URL
      - STRIPE_SECRET_KEY
      - JWT_SIGNING_KEY

두 번째를 쓰세요. 어떤 시크릿이 어느 컨테이너로 가는지 정확히 적히고, 그 자체가 문서가 됩니다.

함정 둘:

  • 치환에서는 셸 환경이 .env 파일을 이깁니다. 그래서 옮기는 중에 .env가 남아 있어도 infisical run이 넣은 값을 덮지는 않습니다. 다만 그게 안 쓰인다는 것도 눈에 안 띕니다. 다 옮겼으면 지우세요.
  • docker compose up -d는 바로 끝나고, 컨테이너는 시작할 때 받은 환경을 그대로 들고 삽니다. 시크릿이 바뀌면 명령을 다시 돌려야 합니다. 돌아가는 컨테이너에 저절로 반영되지 않습니다.

6.4 패턴 C — 컨테이너 안에서 CLI 돌리기

격리가 제일 셉니다. 컨테이너가 각자 자기 시크릿만 시작할 때 받아가니, 호스트에 전체 시크릿이 모인 적이 한 번도 없습니다.

FROM node:22-alpine

RUN apk add --no-cache curl \
 && curl -1sLf 'https://artifacts-cli.infisical.com/setup.apk.sh' | sh \
 && apk add --no-cache infisical

WORKDIR /app
COPY . .
RUN npm ci --omit=dev

ENTRYPOINT ["infisical", "run", "--projectId=<your-project-id>", "--env=prod", "--"]
CMD ["node", "server.js"]
services:
  orders-api:
    build: .
    environment:
      INFISICAL_TOKEN: ${INFISICAL_TOKEN}
      INFISICAL_DOMAIN: ${INFISICAL_DOMAIN}
export INFISICAL_DOMAIN="https://secrets.example.com"
export INFISICAL_TOKEN=$(infisical login --method=universal-auth \
  --client-id="$UA_CLIENT_ID" --client-secret="$UA_CLIENT_SECRET" --plain --silent)

docker compose up -d --build

여기서 INFISICAL_DOMAIN에 localhost 주소를 넣으면 안 됩니다. 컨테이너 안에서 그 주소는 자기 자신입니다. Infisical이 같은 호스트에 있으면 host.docker.internal을 쓰거나, 두 스택을 같은 Docker 네트워크에 올리고 서비스 이름으로 부르세요.

권한이 다른 서비스가 여럿이면 각각 아이덴티티랑 변수를 따로 주고(INFISICAL_TOKEN_WEB, INFISICAL_TOKEN_API) 서비스별로 매핑하면 됩니다.

6.5 뭘 고를까

A: .env로 뽑기B: infisical run --C: 컨테이너 안 CLI
호스트 디스크에 평문있음없음없음
앱 이미지 손댈 일없음없음Dockerfile + CLI
서비스별 권한 분리안 됨키 단위로 수동아이덴티티 단위
${VAR} 치환과 같이 쓰기됨됨안 됨
이럴 때기존 호스트 이사대부분의 Compose 구성멀티테넌트 호스트

B로 시작하세요. 서비스마다 권한이 달라지면 C로 갑니다. A는 스택 안의 뭔가가 진짜로 파일을 요구할 때만 쓰세요.


7. Kubernetes에서 시크릿 사용하기

Kubernetes에서는 프로세스에 시크릿을 밀어 넣지 않습니다. 오퍼레이터가 Infisical 시크릿을 네이티브 Kubernetes Secret으로 동기화하게 두고, 그 Secret은 원래 쓰던 대로 씁니다.

7.1 오퍼레이터 설치

helm repo add infisical-helm-charts \
  'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
helm repo update

helm install infisical-secrets-operator \
  infisical-helm-charts/secrets-operator \
  --namespace infisical-operator-system \
  --create-namespace
kubectl get pods -n infisical-operator-system
kubectl get crds | grep infisical

지금 API는 secrets.infisical.com/v1beta1이고, CRD 셋으로 역할이 깔끔하게 나뉩니다.

CRD담당
InfisicalConnection어느 Infisical 인스턴스인가, TLS는 어떻게 믿나
InfisicalAuth어느 머신 아이덴티티로, 어떤 방식으로 인증하나
InfisicalStaticSecret뭘 어디로, 얼마나 자주 동기화하나

연결·인증·동기화를 한 리소스에 다 때려넣던 구형 v1alpha1의 InfisicalSecret은 폐기 예정입니다. InfisicalPushSecret과 InfisicalDynamicSecret은 아직 v1alpha1에 있습니다.

7.2 자격 증명 Secret

닭이 먼저냐 달걀이 먼저냐 하는 문제가 하나 있습니다. 자격 증명을 가져오려면 자격 증명이 필요합니다. 6.1처럼 머신 아이덴티티를 만든 다음:

kubectl create secret generic universal-auth-credentials \
  --namespace=orders \
  --from-literal=clientId="<client-id>" \
  --from-literal=clientSecret="<client-secret>"

7.3 리소스 세 개

# 1. Infisical 인스턴스가 어디 있는지
apiVersion: secrets.infisical.com/v1beta1
kind: InfisicalConnection
metadata:
  name: self-hosted-infisical
  namespace: orders
spec:
  address: https://secrets.example.com
---
# 2. 거기에 어떻게 인증할지
apiVersion: secrets.infisical.com/v1beta1
kind: InfisicalAuth
metadata:
  name: orders-auth
  namespace: orders
spec:
  infisicalConnectionRef:
    name: self-hosted-infisical
    namespace: orders
  method: universal
  universal:
    clientIdRef:
      name: universal-auth-credentials
      namespace: orders
      key: clientId
    clientSecretRef:
      name: universal-auth-credentials
      namespace: orders
      key: clientSecret
---
# 3. 뭘 가져와서 어디에 넣을지
apiVersion: secrets.infisical.com/v1beta1
kind: InfisicalStaticSecret
metadata:
  name: orders-api-secrets
  namespace: orders
spec:
  infisicalAuthRef:
    name: orders-auth
    namespace: orders
  syncOptions:
    refreshInterval: 60s
  sources:
    - projectId: <your-project-id>
      environmentSlug: prod
      secretPath: /
  targets:
    - name: orders-api-secrets
      namespace: orders
      kind: Secret
      creationPolicy: Owner
kubectl apply -f infisical.yaml

확인:

kubectl get infisicalconnection,infisicalauth,infisicalstaticsecret -n orders
kubectl get secret orders-api-secrets -n orders -o jsonpath='{.data}' | jq 'keys'

출력 컬럼에 connection과 auth는 Ready, static secret은 Synced로 뜹니다. 뭔가 잘못되면 kubectl describe infisicalstaticsecret orders-api-secrets -n orders가 condition에 이유를 적어줍니다.

address에 /api는 붙이지 마세요. 오퍼레이터가 알아서 붙입니다. 사설 CA 뒤에 있으면 검증을 끄지 말고 connection의 spec.tls에 TLS 설정을 넣으세요.

7.4 스펙에서 알아둘 것들

sources는 리스트입니다. target 하나에 여러 출처를 합칠 수 있습니다. 공용 설정 더하기 서비스 전용 키, 이런 식으로요.

sources:
  - projectId: <project-id>
    environmentSlug: prod
    secretPath: /common
  - projectId: <project-id>
    environmentSlug: prod
    secretPath: /orders
    recursive: true
    tagSlugs: ["payments"]

각 source는 projectId나 projectSlug 중 하나에 environmentSlug와 secretPath를 같이 받습니다. recursive는 하위 폴더까지, tagSlugs는 태그로 거릅니다.

targets도 리스트고, Secret 대신 ConfigMap도 됩니다(kind: ConfigMap — 민감하지 않은 것만). creationPolicy가 중요합니다.

creationPolicy동작
Owner오퍼레이터가 Secret을 소유. CR을 지우면 같이 지워집니다. 기본으로 이거.
OrphanSecret이 CR보다 오래 남습니다. 이사 중이거나 다른 컨트롤러가 손댈 때.

syncOptions.refreshInterval 은 폴링 주기입니다(60s면 무난합니다). instantUpdates: true를 켜면 푸시로 받아서, 키를 바꿨을 때 주기를 안 기다립니다.

targets[].template 은 앱이 “시크릿 하나당 키 하나”가 아니라 특정 형식을 원할 때 씁니다. 렌더링된 설정 파일이나, 조각을 이어 붙인 DATABASE_URL 같은 거요. engineVersion: v1을 주고 data 아래에 Go 템플릿을 넣습니다.

7.5 가져다 쓰기, 그리고 바뀌면 다시 띄우기

apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders-api
  namespace: orders
  annotations:
    secrets.infisical.com/auto-reload: "true"
spec:
  replicas: 3
  selector:
    matchLabels:
      app: orders-api
  template:
    metadata:
      labels:
        app: orders-api
    spec:
      containers:
        - name: api
          image: ghcr.io/example/orders-api:1.4.2
          envFrom:
            - secretRef:
                name: orders-api-secrets

secrets.infisical.com/auto-reload: "true" 어노테이션이 다들 빼먹는 부분입니다. 환경 변수는 프로세스 시작할 때 한 번만 읽습니다. 이게 없으면 바뀐 시크릿이 Kubernetes Secret 안에만 들어앉아 있고, 돌아가는 파드들은 누가 재배포할 때까지 계속 옛날 값을 씁니다. 붙여두면 동기화된 Secret이 바뀔 때 오퍼레이터가 Deployment를 롤링합니다.

7.6 정적 자격 증명 없이 클러스터 안에서 인증하기

Universal Auth는 결국 클라이언트 ID와 시크릿을 Kubernetes Secret에 넣어두는 겁니다. .env보다야 낫지만, 여전히 오래 사는 자격 증명이고 직접 교체해야 합니다.

클러스터가 더 나은 걸 지원하면 InfisicalAuth에 다른 방식들도 있습니다. 전부 플랫폼이 이미 보증해주는 워크로드 아이덴티티를 씁니다.

method쓰는 것
kubernetes이 클러스터의 ServiceAccount 토큰
awsIam노드나 파드의 IAM 역할
gcpIdTokenWorkload Identity
azureAzure 관리형 아이덴티티
ldapLDAP 바인드

셀프 호스팅 인스턴스를 클러스터 옆에 두고 쓴다면 method: kubernetes가 제일 자연스럽습니다. 교체할 정적 시크릿이 없고, 권한이 ServiceAccount 단위로 잘립니다.

spec:
  method: kubernetes
  kubernetes:
    identityIdRef:
      name: infisical-identity-id
      namespace: orders
      key: identityId
    serviceAccountRef:
      name: orders-api
      namespace: orders

프로덕션이면 설정 몇 줄 더 쓸 값은 합니다. 일단 universal auth로 파이프라인이 도는지 확인하고 넘어가세요.

7.7 오퍼레이터가 해결 안 해주는 것 하나

동기화된 건 그냥 Kubernetes Secret입니다. 암호화가 아니라 base64입니다. 그 네임스페이스에 get secrets 권한 있는 사람은 다 읽습니다. Infisical이 푸는 건 배포 문제지 Kubernetes의 저장 방식이 아닙니다. 그래도 할 건 남습니다. etcd 저장 암호화를 켜고, 네임스페이스 전체 시크릿 읽기 권한을 아무한테나 주지 않는 RBAC를 짜세요.


정리하면

상황방법디스크에 남는 시크릿
로컬 개발infisical run -- npm run dev없음
Compose 서버infisical run -- docker compose up -d없음
Kubernetes오퍼레이터 → 네이티브 Secret → envFrom클러스터에만
CI머신 아이덴티티 + INFISICAL_TOKEN없음

결국 하나로 정리됩니다. 클라이언트가 각자 자기 이름으로 인증하고, 허용된 것만 가져갑니다. 키 교체는 UI 한 곳에서 한 번 고치면 끝이고, 자격 증명이 새도 아이덴티티 하나 폐기하면 끝납니다.

솔직한 단점도 있습니다. 이제 나머지 전부가 의존하는 상태 저장 서비스를 하나 굴리게 됩니다. 그 Postgres에는 백업이 필요하고, 그게 죽으면 같이 죽습니다. ENCRYPTION_KEY는 잃으면 전부 잃는 지점이라, 그 문자열 하나 백업을 DB만큼 진지하게 다뤄야 합니다. Kubernetes에서는 시크릿이 여전히 base64로 etcd에 들어갑니다. Infisical은 바닥을 많이 올려주지만, 생각까지 대신 해주지는 않습니다.

참고 자료

Related Posts