-
Notifications
You must be signed in to change notification settings - Fork 45
Add example of AKS attestation and secret provisioning #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
30e27e9
fa27a8b
9e31a51
c93d61a
360d710
026d93a
1eae0ee
f6e00de
eb99532
26665c7
f8ecf6f
0194249
0596f83
11a24b6
b264d13
3dd139d
2c4905b
07fd80d
e2835ca
5442d9e
bfb5839
395059d
0ae230b
5c57c98
06a63d7
052f683
ad70844
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| # Gramine Attestation Inside AKS cluster | ||
|
|
||
| This guide demonstrates how Gramine DCAP attestation quote can be generated and verified from | ||
| within an AKS cluster. Here, we provide an end-to-end example to help Cloud Solution Providers | ||
| integrate Gramine’s RA-TLS attestation and secret provisioning feature with a confidential compute | ||
| cluster managed by Azure Kubernetes Service. The necessary reference wrappers that will enable | ||
| Gramine to use AKS components such as the AESMD and quote provider libraries are contributed. | ||
| A microservice deployment is also provided for the RA-TLS verifier module that can be readily | ||
| deployed to the AKS cluster. | ||
|
|
||
| ## Preparing client and server images | ||
|
|
||
| This demonstration is created for ``gramine/CI-Examples/ra-tls-secret-prov`` sample. The sample | ||
| contains client and server applications, where by-default server is running on localhost:4433. Here, | ||
| the client sends its SGX quote to the server for verification. After successful quote verification, | ||
| the server sends a secret to the client. To run these client and server applications inside AKS | ||
| cluster, user needs to prepare two docker images, each for client and server application. Since, now | ||
| the server will no longer run on localhost, instead it will run as part of a container inside AKS | ||
| cluster, the server container should be assigned a dns name (e.g., `<AKS-DNS-NAME>`) for outside | ||
| container visibility. The client will send requests to this dns name. Therefore, for demonstration | ||
| we updated ``gramine/CI-Examples/ra-tls-secret-prov/certs`` directory certificates by replacing | ||
| "Common Name" field in the server certificate (i.e., `server2-sha256.crt`) from `localhost` to | ||
| `<AKS-DNS-NAME.*.cloudapp.azure.com>`. | ||
|
|
||
| In order to create base client and server images for AKS environment, user can execute | ||
| base-image-generation-script.sh script. Since, both client and server applications will run | ||
| inside containers in AKS cluster, and the client wants to send its SGX quote to the server for | ||
| verification, therefore the user needs to graminize the client application, so that it can leverage | ||
| SGX capabilities from within a container. Hence, the following two steps create base server image | ||
| and gsc-client image for AKS cluster. | ||
|
|
||
| ### Creating server image | ||
|
|
||
| 1. The base-image-generation-script.sh script will create server image with the name | ||
| aks-secret-prov-server-img:latest. | ||
|
|
||
| 2. Push server image to Docker Hub or your preferred registry: | ||
|
|
||
| ```sh | ||
| $ docker tag <aks-secret-prov-server-img> \ | ||
| <dockerhubusername>/<aks-secret-prov-server-img> | ||
| $ docker push <dockerhubusername>/<aks-secret-prov-server-img> | ||
| ``` | ||
|
|
||
| 3. Deploy `<aks-secret-prov-server-img>` in AKS confidential compute cluster: | ||
| - Reference deployment file: | ||
| `gsc/examples/aks-attestation/aks-secret-prov-server-deployment.yaml` | ||
|
|
||
| ### Creating client image | ||
|
|
||
| 1. The base-image-generation-script.sh script will create client image with the name | ||
| aks-secret-prov-client-img:latest. | ||
|
|
||
| 2. Create GSC image for ra-tls-secret-prov min client: | ||
|
|
||
| ```sh | ||
| $ cd gsc | ||
| $ openssl genrsa -3 -out enclave-key.pem 3072 | ||
| $ ./gsc build <base-secret-prov-client-img> \ | ||
| examples/aks-attestation/aks-secret-prov-client.manifest | ||
| $ ./gsc sign-image <base-secret-prov-client-img> enclave-key.pem | ||
| ``` | ||
|
|
||
| 5. Push resulting image to Docker Hub or your preferred registry: | ||
|
|
||
| ```sh | ||
| $ docker tag <gsc-base-secret-prov-client-img> \ | ||
| <dockerhubusername>/<aks-gsc-secret-prov-client-img> | ||
| $ docker push <dockerhubusername>/<aks-gsc-secret-prov-client-img> | ||
| ``` | ||
|
|
||
| 6. Deploy `<aks-gsc-secret-prov-client-img>` in AKS confidential compute cluster: | ||
| - Reference deployment file: | ||
| `gsc/examples/aks-attestation/aks-secret-prov-client-deployment.yaml` | ||
|
|
||
| **NOTE**: We recommend deploying GSC images on Ubuntu with Linux kernel version 5.11 or higher. | ||
|
|
||
| ## Deploying client and server images inside AKS Confidential Compute cluster | ||
|
|
||
| AKS confidential compute cluster can be created using following | ||
| [link](https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-nodes-aks-get-started). | ||
|
|
||
| Gramine performs out-of-proc mode DCAP quote generation. Out-of-proc mode quote generation requires aesmd | ||
| service. To fulfill this requirement, AKS provides | ||
| [sgxquotehelper daemonset](https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-nodes-out-of-proc-attestation). | ||
| This feature exposes aesmd service for the container node. The service will internally connect with | ||
| az-dcap-client to fetch the platform collateral required for quote generation. In this demo, the | ||
| ``aks-secret-prov-client-deployment.yaml`` uses aesmd service exposed by AKS with the help of | ||
| sgxquotehelper plugin. | ||
|
|
||
| In the ra-tls-secret-prov example, the client will generate out-of-proc mode sgx quote that will be | ||
| embedded inside RA-TLS certificate. On receiving the quote, the server will internally verify it | ||
| using libsgx-dcap-quote-verify library via az-dcap-client library. Here, | ||
| ``aks-secret-prov-server-deployment.yaml`` will deploy a ra-tls-secret-prov server container inside | ||
| AKS cluster. | ||
|
|
||
| ### Deployment | ||
|
|
||
| ```sh | ||
| $ kubectl apply -f aks-secret-prov-server-deployment.yaml | ||
| ``` | ||
|
|
||
| Once the server container is in running state, start the client container as shown below: | ||
|
|
||
| ```sh | ||
| $ kubectl apply -f aks-secret-prov-client-deployment.yaml | ||
| ``` | ||
|
|
||
| At this stage, a successful RA-TLS verification would be completed, and the secrets have been | ||
| provisioned from the server to the client container. | ||
|
|
||
| ## Checking SGX quote generation and verification | ||
|
|
||
| Verify the client job is completed: | ||
|
|
||
| ```sh | ||
| $ kubectl get pods | ||
| ``` | ||
|
|
||
| Receive logs to verify the secret has been provisioned to the client: | ||
|
|
||
| ```sh | ||
| $ kubectl logs -l app=gsc-ra-tls-secret-prov-client --tail=50 | ||
| ``` | ||
|
|
||
| ### Expected Output | ||
|
|
||
| `--- Received secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'` | ||
|
|
||
| Delete both client and server containers | ||
|
|
||
| ```sh | ||
| $ kubectl delete -f aks-secret-prov-server-deployment.yaml | ||
| $ kubectl delete -f aks-secret-prov-client-deployment.yaml | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| apiVersion: batch/v1 | ||
| kind: Job | ||
| metadata: | ||
| name: secret-prov-client | ||
| labels: | ||
| app: secret-prov-client | ||
| spec: | ||
| template: | ||
| metadata: | ||
| labels: | ||
| app: gsc-ra-tls-secret-prov-client | ||
| spec: | ||
| volumes: | ||
| - name: var-run-aesmd | ||
| hostPath: | ||
| path: /var/run/aesmd | ||
| containers: | ||
| - name: gsc-ra-tls-secret-prov-client-container | ||
| image: <dockerhubusername>/<aks-gsc-secret-prov-client-img> | ||
| env: | ||
| - name: SECRET_PROVISION_SERVERS | ||
| value: "<AKS-DNS-NAME.*.cloudapp.azure.com>:4433" | ||
| resources: | ||
| limits: | ||
| kubernetes.azure.com/sgx_epc_mem_in_MiB: 10 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be |
||
| volumeMounts: | ||
| - name: var-run-aesmd | ||
| mountPath: /var/run/aesmd | ||
| restartPolicy: Never | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| FROM ubuntu:18.04 | ||
|
|
||
| RUN apt-get update \ | ||
| && env DEBIAN_FRONTEND=noninteractive apt-get install -y wget \ | ||
| build-essential \ | ||
| gnupg2 \ | ||
| libcurl3-gnutls \ | ||
| python3 | ||
|
|
||
| # Installing DCAP libraries | ||
|
|
||
| RUN echo 'deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu bionic main' \ | ||
| > /etc/apt/sources.list.d/intel-sgx.list \ | ||
| && wget https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key \ | ||
| && apt-key add intel-sgx-deb.key | ||
|
|
||
| RUN apt-get update \ | ||
| && apt-get install -y libsgx-urts \ | ||
| libsgx-dcap-ql \ | ||
| libsgx-quote-ex | ||
|
|
||
| RUN mkdir -p /ra-tls-secret-prov | ||
|
|
||
| COPY gramine/CI-Examples/ra-tls-secret-prov /ra-tls-secret-prov | ||
|
|
||
| COPY gramine/CI-Examples/ra-tls-secret-prov/secret_prov_min_client /usr/local/bin | ||
|
|
||
| WORKDIR /ra-tls-secret-prov | ||
|
|
||
| ENTRYPOINT ["secret_prov_min_client"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # Manifest file for ra-tls-secret-prov min client | ||
|
|
||
| # Secret Provisioning library (client-side) is preloaded | ||
| loader.env.LD_PRELOAD = "libsecret_prov_attest.so" | ||
|
|
||
| loader.env.SECRET_PROVISION_SERVERS = { passthrough = true } | ||
| loader.env.SECRET_PROVISION_CONSTRUCTOR = "1" | ||
| loader.env.SECRET_PROVISION_CA_CHAIN_PATH = "certs/test-ca-sha256.crt" | ||
|
|
||
| # Request remote attestation functionality from Gramine | ||
| sgx.remote_attestation = true | ||
|
|
||
| sgx.allowed_files = [ | ||
| "file:/etc/ethers", | ||
| "file:/etc/gai.conf", | ||
| "file:/etc/group", | ||
| "file:/etc/host.conf", | ||
| "file:/etc/hosts", | ||
| "file:/etc/nsswitch.conf", | ||
| "file:/etc/passwd", | ||
| "file:/etc/resolv.conf", | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: ra-tls-secret-prov-server | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: ra-tls-secret-prov-server | ||
| template: | ||
| metadata: | ||
| labels: | ||
| app: ra-tls-secret-prov-server | ||
| spec: | ||
| containers: | ||
| - name: ra-tls-secret-prov-server-container | ||
| image: <dockerhubusername>/<aks-secret-prov-server-img> | ||
| ports: | ||
| - containerPort: 4433 | ||
| resources: | ||
| limits: | ||
| kubernetes.azure.com/sgx_epc_mem_in_MiB: 10 | ||
| --- | ||
| apiVersion: v1 | ||
| kind: Service | ||
| metadata: | ||
| annotations: | ||
| service.beta.kubernetes.io/azure-dns-label-name: <AKS-DNS-NAME> | ||
| name: ra-tls-secret-prov-server | ||
| spec: | ||
| type: LoadBalancer | ||
| ports: | ||
| - port: 4433 | ||
| selector: | ||
| app: ra-tls-secret-prov-server |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FROM ubuntu:18.04 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN apt-get update \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && env DEBIAN_FRONTEND=noninteractive apt-get install -y \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| build-essential \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gnupg2 \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| libcurl3-gnutls \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| libcurl4-openssl-dev \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| python3 \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wget | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Installing Azure DCAP Quote Provider Library (az-dcap-client). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Here, the version of az-dcap-client should be in sync with the az-dcap-client | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # version used for quote generation. User can replace the below package with the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # latest package. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN wget https://github.com/microsoft/Azure-DCAP-Client/releases/download/1.8/az-dcap-client_1.8_amd64_18.04.deb \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && dpkg -i az-dcap-client_1.8_amd64_18.04.deb | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Installing DCAP Quote Verification Library | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN echo 'deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu bionic main' \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > /etc/apt/sources.list.d/intel-sgx.list \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && wget https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && apt-key add intel-sgx-deb.key | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN apt-get update && apt-get install -y libsgx-dcap-quote-verify | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Build environment of this Dockerfile should point to the root of Gramine directory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN mkdir -p /ra-tls-secret-prov | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY gramine/CI-Examples/ra-tls-secret-prov /ra-tls-secret-prov | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY gramine/CI-Examples/ra-tls-secret-prov/secret_prov_server_dcap /usr/local/bin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN mkdir -p /ra-tls-secret-prov/libs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY gramine/build/Pal/src/host/Linux-SGX/tools/ra-tls/libsecret_prov_verify_dcap.so /ra-tls-secret-prov/libs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY gramine/build/Pal/src/host/Linux-SGX/tools/common/libsgx_util.so /ra-tls-secret-prov/libs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedcrypto_gramine.so.6 /ra-tls-secret-prov/libs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedtls_gramine.so.13 /ra-tls-secret-prov/libs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedx509_gramine.so.1 /ra-tls-secret-prov/libs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WORKDIR /ra-tls-secret-prov | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN mkdir -p /ra-tls-secret-prov | |
| COPY gramine/CI-Examples/ra-tls-secret-prov /ra-tls-secret-prov | |
| COPY gramine/CI-Examples/ra-tls-secret-prov/secret_prov_server_dcap /usr/local/bin | |
| RUN mkdir -p /ra-tls-secret-prov/libs | |
| COPY gramine/build/Pal/src/host/Linux-SGX/tools/ra-tls/libsecret_prov_verify_dcap.so /ra-tls-secret-prov/libs | |
| COPY gramine/build/Pal/src/host/Linux-SGX/tools/common/libsgx_util.so /ra-tls-secret-prov/libs | |
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedcrypto_gramine.so.6 /ra-tls-secret-prov/libs | |
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedtls_gramine.so.13 /ra-tls-secret-prov/libs | |
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedx509_gramine.so.1 /ra-tls-secret-prov/libs | |
| WORKDIR /ra-tls-secret-prov | |
| WORKDIR /ra-tls-secret-prov | |
| COPY gramine/CI-Examples/ra-tls-secret-prov . | |
| COPY gramine/CI-Examples/ra-tls-secret-prov/secret_prov_server_dcap /usr/local/bin | |
| RUN mkdir libs | |
| COPY gramine/build/Pal/src/host/Linux-SGX/tools/ra-tls/libsecret_prov_verify_dcap.so libs | |
| COPY gramine/build/Pal/src/host/Linux-SGX/tools/common/libsgx_util.so libs | |
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedcrypto_gramine.so.6 libs | |
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedtls_gramine.so.13 libs | |
| COPY gramine/build/subprojects/mbedtls-mbedtls-2.26.0/libmbedx509_gramine.so.1 libs |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| # install Gramine dependencies | ||
|
|
||
| apt-get install -y \ | ||
| autoconf \ | ||
| bison \ | ||
| build-essential \ | ||
| coreutils \ | ||
| curl \ | ||
| gawk \ | ||
| git \ | ||
| libcurl4-openssl-dev \ | ||
| libprotobuf-c-dev \ | ||
| linux-headers-generic \ | ||
| ninja-build \ | ||
| pkg-config \ | ||
| protobuf-c-compiler \ | ||
| python3 \ | ||
| python3-pip \ | ||
| python3-protobuf \ | ||
| wget | ||
|
|
||
| python3 -B -m pip install 'toml>=0.10' 'meson>=0.55' | ||
|
|
||
| # Download Gramine | ||
|
|
||
| git clone https://github.com/gramineproject/gramine.git | ||
|
|
||
| # Generate Signing Key | ||
|
|
||
| cd gramine/Pal/src/host/Linux-SGX/signer/ | ||
| openssl genrsa -3 -out enclave-key.pem 3072 | ||
|
|
||
| # Build Gramine with DCAP enabled mode | ||
|
||
|
|
||
| cd ../../../../../ | ||
| meson setup build/ --buildtype=release -Ddirect=enabled -Dsgx=enabled -Ddcap=enabled | ||
| ninja -C build/ | ||
| sudo ninja -C build/ install | ||
|
|
||
| # Copy dummy server certificate with Common Name as "<AKS-DNS-NAME.*.cloudapp.azure.com> | ||
|
|
||
| cd CI-Examples/ra-tls-secret-prov | ||
| mv certs certs_orig | ||
| cp -r ../../../certs ./ | ||
|
|
||
| # Create Server image | ||
|
|
||
| make clean && make dcap | ||
| cd ../../../ | ||
| docker build -f aks-secret-prov-server.dockerfile -t aks-secret-prov-server-img . | ||
|
|
||
| # Create Client image | ||
|
|
||
| cd gramine/CI-Examples/ra-tls-secret-prov | ||
| make clean && make secret_prov_min_client | ||
| cd ../../../ | ||
| docker build -f aks-secret-prov-client.dockerfile -t aks-secret-prov-client-img . | ||
|
|
||
| # Remove Gramine directory | ||
|
|
||
| rm -r gramine/ | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| This directory contains pre-generated example certificates. In particular: | ||
|
|
||
| - `test-ca-sha256.crt` -- RSA SHA256 root CA certificate in PEM format. Loaded | ||
| in client (attester) so that client can verify the server's certificate. | ||
| - `server2-sha256.crt` -- RSA SHA256 leaf server certificate in PEM format. | ||
| Loaded in server (verifier), so it will send it to the client during TLS | ||
| handshake. Common Name `ra-tls-server-aks-dns.eastus.cloudapp.azure.com` | ||
| - `server2.key` -- RSA private key in PEM format. Loaded in server (verifier). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This demonstration is based on the ra-tls-secret-prov example from
path. Familiarity with this sample is highly recommended before proceeding.