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 표현식에서 사용할 수 있는 변수와 함수의 모음입니다.

예를 들어 다음 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의 각 Expr 노드에 파싱 및 검사 중에 생성된 메타데이터를 색인화하는 데 사용되는 정수 ID가 포함된 프로토 기반 AST를 내보냅니다. 파싱 중에 생성되는 syntax.proto 파일은 표현식의 문자열 형식으로 입력된 항목의 추상적 표현을 나타냅니다.

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

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

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

  • 맞춤 확장 프로그램의 함수 바인딩
  • 변수 바인딩
  • 평가할 AST

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