콘텐츠로 이동

정책 서버 배포와 운영

aigo-policy-server는 조직의 서명된 엔터프라이즈 정책을 제공하는 중앙 서빙 스파인이다. 조직의 비공개 Ed25519 서명 키를 보관하고, 모든 데스크톱·헤드리스 클라이언트가 공개 신뢰 앵커만으로 검증하는 서명된 정책을 서빙하며, 기기 등록을 처리한다.

정책 서버는 서버 측 비공개 구성요소다. aigo-server(추론 서버) 및 데스크톱 앱과는 의도적으로 분리된 산출물이며, 클라이언트에 번들로 포함되지 않는다. 서명 키와 작성한 정책을 모든 사용자·추론 노드에 배포되는 산출물 밖에 두는 것은 최소 권한 원칙이다. 서명 키는 신뢰의 뿌리이므로, 운영자가 직접 운용하는 정책 서버에만 둔다.

정책 문서 스키마(정책이 무엇을 지정·잠금·게이팅할 수 있는지)는 정책 스키마 레퍼런스를 참고한다. 이 문서는 서버 운영을 다룬다.

정책 서버가 하는 일

서버의 역할은 네 개의 엔드포인트로 나뉜 하나의 임무다.

  • 일회용 등록 토큰을 검증하고 공개 신뢰 앵커와 폴링 메타데이터를 반환해, 새 기기가 이후 정책을 검증할 수 있게 한다.
  • 현재 서명된 정책을 서빙한다.
  • 폴링 시 전체 다운로드 없이 비교할 수 있도록 정책 id와 발급일만 서빙한다.
  • 서명된 클라이언트 감사 배치를 수신한다.

서버는 추론 런타임, continuum-router, 모델 로더를 실행하지 않는다. 정책 스키마와 Ed25519 서명에는 공유 aigo-policy 크레이트를, HTTP 표면에는 헤드리스 서버와 동일한 인증 REST 스택을 재사용한다.

사전 준비

서버를 세우기 전에 다음을 계획한다.

  • 관리 기기가 HTTPS로 도달할 수 있는 호스트(TLS는 서버 앞단의 리버스 프록시나 네트워크 경로에서 종단한다).
  • 비공개 서명 키를 보관할 안전한 위치(정책을 관리하는 운영자에게만 접근을 제한).
  • 작성한 EnterprisePolicy JSON 문서(정책 스키마 레퍼런스 참고).

서명 키 생성

서명 키는 PKCS#8 v2 Ed25519 키다. 하나 생성하고 안전하게 보관한다.

aigo-policy-server --generate-signing-key > policy-signing.pk8.b64

키는 base64로 stdout에 출력된다(사람이 읽는 안내는 stderr로 가므로 리디렉션은 키만 담는다). 이 키는 전체 플릿의 신뢰 뿌리다. 시크릿 매니저나 접근 통제된 파일에 저장하고, 백업하며, 절대 커밋하거나 클라이언트에 두지 않는다.

키를 원시 바이너리 PKCS#8 파일로 보관해도 된다. 서버는 --signing-key-file에서 원시 PKCS#8 문서나 그 base64 텍스트 인코딩을 모두 받는다.

신뢰 앵커 추출과 배포

클라이언트는 공개 신뢰 앵커만으로 서명된 정책을 검증한다. 서명 키에서 앵커를 출력한다.

aigo-policy-server \
    --signing-key-file policy-signing.pk8.b64 \
    --anchor-id org-2026 \
    --print-anchor

이 명령은 JSON 신뢰 앵커(id와 base64 공개 키)를 출력한다. 선택한 --anchor-id는 서빙되는 정책의 anchorId에 기록되며, 클라이언트에 프로비저닝하는 앵커 id와 일치해야 한다.

이 공개 앵커는 프로비저닝 프로필의 trustAnchors에 담아 기기에 배포한다. 기기가 이 서버로 등록하면 서버는 이 키에서 파생한 동일한 앵커를 반환하므로, 갓 등록한 기기가 곧이어 가져오는 정책을 검증할 수 있다. 기기 등록과 신뢰 앵커를 참고한다.

정책 작성과 서명

정책 스키마 레퍼런스에 따라 EnterprisePolicy 문서(예: /etc/aigo/policy.json)를 작성한다. 서버는 시작 시 정책에 서명하거나, 이미 서명된 문서를 그대로 서빙할 수 있다.

서명 키로 작성한 정책을 시작 시 서명한다.

aigo-policy-server \
    --signing-key-file /etc/aigo/policy-signing.pk8 \
    --policy-file /etc/aigo/policy.json \
    --anchor-id org-2026

서명된 SignedPolicy 문서를 그대로 서빙한다(런타임에 서명 키가 필요 없으며, 더 격리된 별도 호스트에서 서명할 때 유용하다).

aigo-policy-server --signed-policy-file /etc/aigo/signed-policy.json

--policy-file--signed-policy-file은 상호 배타적이다. 서명 키는 AIGO_POLICY_SIGNING_KEY(base64)나 AIGO_POLICY_SIGNING_KEY_FILE로도 줄 수 있다. 키는 절대 네트워크로 읽지 않는다.

서버를 시작하지 않고 구성을 검증하려면 --dry-run을 쓴다. 정책이 파싱되는지, 스키마가 범위 안인지, 키가 서명하는지 확인하고 종료한다.

기기 등록 구성

등록은 일회용 토큰을 하나 이상 구성할 때만 제공된다. 각 토큰은 일회용이다. 첫 등록 성공 시 소비되며 재사용할 수 없다. 토큰은 TOKEN:ORG 형식으로 등록 기기를 조직 레이블에 선택적으로 묶을 수 있다.

명령줄로 토큰을 전달한다(여러 개면 반복).

aigo-policy-server \
    --signing-key-file /etc/aigo/policy-signing.pk8 \
    --policy-file /etc/aigo/policy.json \
    --anchor-id org-2026 \
    --enrollment-token tok-alice:acme \
    --enrollment-token tok-bob:acme \
    --poll-interval-secs 900

또는 토큰을 조직 레이블에 매핑하는 JSON 파일을 준다(null은 조직 없이 묶음). 파일은 운영자만 읽을 수 있게 두며, 토큰은 평문으로 로그에 남지 않는다.

{
  "tok-alice": "acme",
  "tok-bob": "acme",
  "tok-guest": null
}
aigo-policy-server \
    --signing-key-file /etc/aigo/policy-signing.pk8 \
    --policy-file /etc/aigo/policy.json \
    --anchor-id org-2026 \
    --enrollment-tokens-file /etc/aigo/enrollment-tokens.json

설정한 폴 간격(--poll-interval-secs, 기본 900)은 기기에 반환되어, 갱신된 정책을 얼마나 자주 폴링할지 정한다. 등록 토큰을 구성하지 않으면 등록이 제공되지 않고 POST /api/v1/enroll503을 반환한다.

HTTP 표면

서버는 --host(기본 localhost, 환경변수 AIGO_POLICY_HOST)와 --port(기본 8444, 환경변수 AIGO_POLICY_PORT)로 바인딩한다. 기본 포트는 헤드리스 aigo-server 기본값(8001)과 다르므로, 정책 서버와 추론 서버가 한 머신을 공유할 수 있다. 인증은 항상 필수다. 서버는 인증 비활성 구성을 덮어쓴다. 정책·감사 경로가 enterprise_read 스코프 검사 전에 기기 서명 검증에 의존하기 때문이다.

메서드 경로 인증 용도
POST /api/v1/enroll 일회용 등록 토큰 토큰을 검증하고 신뢰 앵커와 폴 간격을 반환
GET /api/v1/policy enterprise_read 현재 SignedPolicy를 반환
GET /api/v1/policy/version enterprise_read 저렴한 폴링용 정책 id와 발급일을 반환
POST /api/v1/audit 등록 기기 서명 서명된 클라이언트 감사 배치를 수신

기기 서명 인증은 등록 기기에 좁은 enterprise_read 스코프를 부여한다. 운영자·관리자 흐름에는 API 키와 세션 인증을 계속 쓸 수 있다. 클라이언트 감사 보고는 감사와 컴플라이언스에서 다룬다.

서비스로 실행하기(systemd)

장애 시 재시작하고 부팅 시 시작하도록 서비스 관리자 아래에서 서버를 실행한다. 아래 유닛은 번들 aigo-server 유닛의 강화 설정을 따른다. 경로, 사용자, 수신 주소는 환경에 맞춰 조정한다.

[Unit]
Description=Backend.AI GO Policy Server
Documentation=https://github.com/lablup/backend.ai-go
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=aigo
Group=aigo
Environment=AIGO_POLICY_SIGNING_KEY_FILE=/etc/aigo/policy-signing.pk8
ExecStart=/usr/bin/aigo-policy-server \
    --host 0.0.0.0 \
    --port 8444 \
    --policy-file /etc/aigo/policy.json \
    --anchor-id org-2026 \
    --enrollment-tokens-file /etc/aigo/enrollment-tokens.json
ExecReload=/bin/kill -HUP $MAINPID

NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadOnlyPaths=/etc/aigo
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

서명 키와 등록 토큰 파일은 서비스 사용자만 읽을 수 있게 둔다(chmod 600, 소유자 root:aigo 또는 서비스 계정). ReadOnlyPaths=/etc/aigo 줄은 서비스가 키와 정책을 읽되 프로세스가 이를 수정하지 못하게 한다.

컨테이너로 실행하기

정책 서버는 컨테이너에서 깔끔하게 동작한다. 서명 키와 정책을 읽기 전용으로 마운트하고, 포트는 TLS 종단기 뒤에 공개한다. 저렴한 버전 경로로 헬스 체크를 추가한다.

HEALTHCHECK --interval=30s --timeout=5s \
  CMD curl -fsS http://localhost:8444/api/v1/policy/version \
      -H "Authorization: Bearer $AIGO_HEALTHCHECK_KEY" || exit 1

docker inspect로 확인된 헬스 상태를 조회한다.

docker inspect --format='{{.State.Health.Status}}' aigo-policy-server

배포 검증

서버를 시작한 뒤, 정책을 서빙하는지와 버전 경로가 응답하는지 확인한다. 버전 경로는 가장 저렴한 점검이며 클라이언트가 폴링하는 대상이다.

curl -fsS https://policy.acme.example/api/v1/policy/version \
    -H "Authorization: Bearer <operator-api-key>"

정상 응답은 정책 id(문서의 policyId)와 발급일을 담는다. 클라이언트는 이 id를 캐시된 id와 비교하고, 바뀐 경우에만 전체 정책을 내려받는다.

명령어 레퍼런스

플래그 환경변수 용도
--signing-key-file AIGO_POLICY_SIGNING_KEY_FILE PKCS#8 Ed25519 서명 키(원시 또는 base64).
--anchor-id AIGO_POLICY_ANCHOR_ID anchorId에 기록되고 클라이언트에 프로비저닝되는 신뢰 앵커 id.
--policy-file AIGO_POLICY_FILE 작성한 EnterprisePolicy JSON, 시작 시 서명.
--signed-policy-file AIGO_POLICY_SIGNED_FILE 서명된 SignedPolicy JSON, 그대로 서빙.
--enrollment-token 일회용 토큰 TOKEN[:ORG]. 반복 가능, 일회용.
--enrollment-tokens-file AIGO_POLICY_ENROLLMENT_TOKENS_FILE 토큰을 조직에 매핑한 JSON(null은 조직 없음).
--poll-interval-secs AIGO_POLICY_POLL_INTERVAL_SECS 등록 기기에 반환되는 폴 간격(기본 900).
--host / -H AIGO_POLICY_HOST 바인딩 호스트(기본 localhost).
--port / -p AIGO_POLICY_PORT 바인딩 포트(기본 8444).
--data-dir / -D AIGO_POLICY_DATA_DIR 애플리케이션 데이터 디렉터리(액세스 키, 설정).
--config / -c AIGO_POLICY_CONFIG TOML 설정 파일(aigo-server 설정 형태 재사용).
--print-anchor 공개 신뢰 앵커를 출력하고 종료.
--generate-signing-key 새 base64 PKCS#8 Ed25519 키를 출력하고 종료.
--no-socket Unix 도메인 소켓 리스너 비활성(TCP만).
--dry-run 설정·정책·키를 검증하고 종료.
--verbose / -v 로깅 상세도 증가(-v, -vv).
--quiet / -q 오류 외 출력 억제.

다음 단계