Common Expression Language (CEL)

Common Expression Language (CEL)는 빠르고 휴대 가능하며 안전하게 실행되도록 설계된 범용 표현식 언어입니다. CEL을 단독으로 사용하거나 더 큰 제품에 삽입할 수 있습니다. CEL은 원격 프로시져 콜 (RPC) 라우팅부터 보안 정책 정의에 이르기까지 다양한 애플리케이션에 적합합니다. CEL은 확장 가능하고 플랫폼 독립적이며 공식적으로 검증 가능하고 한 번 컴파일/여러 번 평가 워크플로에 최적화되어 있습니다.

CEL은 사용자 코드를 안전하게 실행하도록 특별히 설계되었습니다. 사용자의 Python 코드에서 eval()을 무작위로 호출하는 것은 위험하지만 사용자의 CEL 코드는 안전하게 실행할 수 있습니다. 또한 CEL은 성능을 저하시키는 동작을 방지하므로 나노초 또는 마이크로초 단위로 안전하게 평가됩니다. CEL의 속도와 안전성 덕분에 성능이 중요한 애플리케이션에 적합합니다.

CEL은 한 줄 함수 또는 람다 표현식과 유사한 표현식을 평가합니다. CEL은 일반적으로 불리언 결정에 사용되지만 JSON 또는 프로토콜 버퍼 메시지와 같은 더 복잡한 객체를 구성하는 데 사용할 수도 있습니다.

CEL을 사용하는 이유

많은 서비스와 애플리케이션이 선언적 구성을 평가합니다. 예를 들어 역할 기반 액세스 제어 (RBAC)는 사용자 역할과 사용자 집합이 주어지면 액세스 결정을 생성하는 선언적 구성입니다. 선언적 구성은 대부분의 경우에 충분하지만 때로는 더 표현력이 필요합니다. 이때 CEL이 사용됩니다.

CEL로 선언적 구성을 확장하는 예로 Google Cloud Identity and Access Management (IAM)의 기능을 고려해 보세요. RBAC는 일반적인 사례이지만 IAM은 사용자가 액세스하는 요청 또는 리소스의 프로토 메시지 속성에 따라 역할 기반 부여의 범위를 추가로 제한할 수 있도록 CEL 표현식을 제공합니다. 데이터 모델을 통해 이러한 조건을 설명하면 작업하기 어려운 복잡한 API 노출 영역이 발생합니다. 대신 속성 기반 액세스 제어 (ABAC)와 함께 CEL을 사용하면 RBAC를 표현력 있고 강력하게 확장할 수 있습니다.

CEL의 핵심 개념

CEL에서 표현식은 환경에 대해 컴파일됩니다. 컴파일 단계에서는 프로토콜 버퍼 형식으로 추상 구문 트리 (AST)를 생성합니다. 컴파일된 표현식은 평가를 최대한 빠르게 유지하기 위해 향후 사용을 위해 저장됩니다. 단일 컴파일된 표현식은 여러 가지 다른 입력으로 평가할 수 있습니다.

다음은 이러한 개념을 자세히 살펴본 것입니다.

표현식

표현식은 사용자가 작성합니다. 표현식은 한 줄 함수 본문 또는 람다 표현식과 유사합니다. 입력을 선언하는 함수 서명은 CEL 표현식 외부에 작성되고 CEL에서 사용할 수 있는 함수 라이브러리는 자동으로 가져옵니다.

예를 들어 다음 CEL 표현식은 요청 객체를 가져오고 요청에는 claims 토큰이 포함됩니다. 표현식은 claims 토큰이 아직 유효한지 여부를 나타내는 불리언 값을 반환합니다.

클레임 토큰을 인증하는 CEL 표현식 예시

// Check whether a JSON Web Token has expired by inspecting the 'exp' claim.
//
// Args:
//   claims - authentication claims.
//   now    - timestamp indicating the current system time.
// Returns: true if the token has expired.
//
timestamp(claims["exp"]) < now

사용자가 CEL 표현식을 정의하는 반면 서비스와 애플리케이션은 CEL 표현식이 실행되는 환경을 정의합니다.

환경

환경은 서비스에서 정의합니다. CEL을 삽입하는 서비스와 애플리케이션은 표현식 환경을 선언합니다. 환경은 CEL 표현식에서 사용할 수 있는 변수와 함수의 모음입니다.

예를 들어 다음 textproto 코드는 CEL 서비스의 CompileRequest 메시지를 사용하여 requestnow 변수가 포함된 환경을 선언합니다.

CEL 환경 선언 예시

# Format: $SOURCE_PATH/service.proto#CompileRequest
declarations {
  name: "request"
  ident {
    type { message_type: "google.rpc.context.AttributeContext.Request" }
  }
}
declarations {
  name: "now"
  ident {
    type { well_known: "TIMESTAMP" }
  }
}

프로토 기반 선언은 CEL 유형 검사기에서 표현식 내의 모든 식별자 및 함수 참조가 올바르게 선언되고 사용되도록 하는 데 사용됩니다.

표현식 처리 단계

CEL 표현식은 다음 세 단계로 처리됩니다.

  1. 파싱
  2. 확인
  3. 평가

CEL 사용의 가장 일반적인 패턴은 구성 시 표현식을 파싱하고 확인한 후 AST를 저장한 다음 런타임에 AST를 반복적으로 가져오고 평가하는 것입니다.

CEL 처리 단계 그림

표현식은 구성 경로에서 파싱되고 확인된 후 저장되며 읽기 경로에서 하나 이상의 컨텍스트에 대해 평가됩니다.

CEL은 사람이 읽을 수 있는 표현식에서 ANTLR 어휘 분석기 및 파서 문법을 사용하여 AST로 파싱됩니다. 파싱 단계에서는 프로토 기반 AST를 내보내는데, AST의 각 Expr 노드에는 파싱 및 확인 중에 생성된 메타데이터를 색인하는 데 사용되는 정수 ID가 포함됩니다. 파싱 중에 생성되는 syntax.proto 파일은 표현식의 문자열 형식으로 입력된 내용의 추상적 표현을 나타냅니다.

표현식이 파싱된 후에는 환경에 대해 유형 검사를 실행하여 표현식의 모든 변수 및 함수 식별자가 선언되고 올바르게 사용되고 있는지 확인합니다. 유형 검사기는 평가 효율성을 크게 개선할 수 있는 유형, 변수, 함수 확인 메타데이터가 포함된 checked.proto 파일을 생성합니다.

마지막으로 표현식이 파싱되고 확인된 후 저장된 AST가 평가됩니다.

CEL 평가기에는 다음 세 가지가 필요합니다.

  • 커스텀 확장 프로그램의 함수 결합
  • 변수 결합
  • 평가할 AST

함수 및 변수 결합은 AST를 컴파일하는 데 사용된 것과 일치해야 합니다. 이러한 입력은 여러 평가에서 재사용할 수 있습니다. 예를 들어 여러 변수 결합 집합에서 평가되는 AST, 여러 AST에 대해 사용되는 동일한 변수 또는 프로세스의 수명 동안 사용되는 함수 결합 (일반적인 사례) 등이 있습니다.

공식 검증

런타임 평가 외에도 CEL 표현식과 정책을 공식적으로 검증 하여 가능한 모든 입력에서 정확성을 수학적으로 증명할 수 있습니다.

Z3 정리 증명기를 기반으로 하는 CEL-Java의 CEL 공식 검증 프레임워크는 표현식과 CEL 정책을 SMT (Satisfiability Modulo Theories) 공식으로 변환하여 다음을 수행합니다.

  • 보안 불변성 증명: assumeassert 사양을 사용하여 입력 조합에서 중요한 정책을 우회할 수 없는지 확인합니다.
  • 논리적 동등성 검증: 리팩터링되거나 AI 생성 표현식이 원래 규칙과 동일하게 동작함을 수학적으로 증명합니다.
  • 포괄적인 유효성 적용: Kubernetes 유효성 검사 승인 정책과 같은 가드레일이 모든 입력에서 유지되도록 보장하거나 위반 시 구체적인 반례를 생성합니다.
  • 거짓양성 삭제: 3패스 테인트 추적을 사용하여 매핑되지 않은 커스텀 함수를 격리하고 보고된 위반이 항상 재현 가능한 버그인지 확인합니다.

소개 및 실제 예시는 Google 오픈소스 블로그 게시물인 에이전트 시대의 보안: CEL의 공식 검증 소개를 참고하세요.