mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 06:59:40 -05:00
image: implement idempotent upload of os images
This commit is contained in:
parent
17c45bc881
commit
ee91d8b1cc
@ -604,8 +604,8 @@ def go_dependencies():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable_global",
|
||||
importpath = "github.com/Azure/azure-sdk-for-go/sdk/azcore",
|
||||
sum = "h1:rTnT/Jrcm+figWlYz4Ixzt0SJVR2cMC8lvZcimipiEY=",
|
||||
version = "v1.4.0",
|
||||
sum = "h1:xGLAFFd9D3iLGxYiUGPdITSzsFmU1K8VtfuUHWAoN7M=",
|
||||
version = "v1.5.0",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_azure_azure_sdk_for_go_sdk_azidentity",
|
||||
@ -620,8 +620,8 @@ def go_dependencies():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable_global",
|
||||
importpath = "github.com/Azure/azure-sdk-for-go/sdk/internal",
|
||||
sum = "h1:leh5DwKv6Ihwi+h60uHtn6UWAxBbZ0q8DwQVMzf61zw=",
|
||||
version = "v1.2.0",
|
||||
sum = "h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=",
|
||||
version = "v1.3.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
@ -701,8 +701,8 @@ def go_dependencies():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable_global",
|
||||
importpath = "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob",
|
||||
sum = "h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk=",
|
||||
version = "v0.6.1",
|
||||
sum = "h1:u/LLAOFgsMv7HmNL4Qufg58y+qElGOt5qv0z1mURkRY=",
|
||||
version = "v1.0.0",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_azure_azure_service_bus_go",
|
||||
|
6
go.mod
6
go.mod
@ -45,13 +45,13 @@ require (
|
||||
cloud.google.com/go/secretmanager v1.10.0
|
||||
cloud.google.com/go/storage v1.30.1
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.7
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.19
|
||||
@ -129,7 +129,7 @@ require (
|
||||
cloud.google.com/go/iam v0.12.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.4.1 // indirect
|
||||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
|
12
go.sum
12
go.sum
@ -89,12 +89,12 @@ github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
|
||||
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 h1:rTnT/Jrcm+figWlYz4Ixzt0SJVR2cMC8lvZcimipiEY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 h1:xGLAFFd9D3iLGxYiUGPdITSzsFmU1K8VtfuUHWAoN7M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 h1:uqM+VoHjVH6zdlkLF2b6O0ZANcHoj3rO0PoQ3jglUJA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 h1:leh5DwKv6Ihwi+h60uHtn6UWAxBbZ0q8DwQVMzf61zw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0 h1:82w8tzLcOwDP/Q35j/wEBPt0n0kVC3cjtPdD62G8UAk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0/go.mod h1:S78i9yTr4o/nXlH76bKjGUye9Z2wSxO5Tz7GoDr4vfI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 h1:Lg6BW0VPmCwcMlvOviL3ruHFO+H9tZNqscK0AeuFjGM=
|
||||
@ -110,8 +110,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0 h1:mk57wRUA8fyjFxVcPPGv4shLcWDXPFYokTJL9zJxQtE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0/go.mod h1:mU96hbp8qJDA9OzTV1Ji7wCyPyaqC5kI6ZPsZfJ8sE4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 h1:u/LLAOFgsMv7HmNL4Qufg58y+qElGOt5qv0z1mURkRY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0/go.mod h1:2e8rMJtl2+2j+HXbTBwnyGpm5Nou7KhvSfxOq8JpTag=
|
||||
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
|
||||
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
|
@ -60,9 +60,9 @@ require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0 // indirect
|
||||
|
@ -81,12 +81,12 @@ github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
|
||||
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 h1:rTnT/Jrcm+figWlYz4Ixzt0SJVR2cMC8lvZcimipiEY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 h1:xGLAFFd9D3iLGxYiUGPdITSzsFmU1K8VtfuUHWAoN7M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 h1:uqM+VoHjVH6zdlkLF2b6O0ZANcHoj3rO0PoQ3jglUJA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 h1:leh5DwKv6Ihwi+h60uHtn6UWAxBbZ0q8DwQVMzf61zw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0 h1:BpGGvzarSyE7kQF1x1hptUcGmNzZEE3yYI+uqBSNRxk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0/go.mod h1:1ijUM40peD7YK5MFEJja2wjjp4eimFNWv0NXoY3nsZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.1 h1:H3g2mkmu105ON0c/Gqx3Bm+bzoIijLom8LmV9Gjn7X0=
|
||||
|
0
image/BUILD.bazel
Normal file
0
image/BUILD.bazel
Normal file
@ -57,7 +57,6 @@
|
||||
jq \
|
||||
mtools \
|
||||
ovmf \
|
||||
python3-crc32c \
|
||||
python3-pefile \
|
||||
python3-pyelftools \
|
||||
python3-setuptools \
|
||||
@ -177,6 +176,8 @@ secure-boot/azure/delete.sh --name "${AZURE_DISK_NAME}-setup-secure-boot"
|
||||
|
||||
## Upload to CSP
|
||||
|
||||
Warning! Never set `--version` to a value that is already used for a release image.
|
||||
|
||||
<details>
|
||||
<summary>AWS</summary>
|
||||
|
||||
@ -188,19 +189,9 @@ secure-boot/azure/delete.sh --name "${AZURE_DISK_NAME}-setup-secure-boot"
|
||||
- `pki_prod` is used for release images
|
||||
|
||||
```sh
|
||||
# set these variables
|
||||
export AWS_IMAGE_NAME= # e.g. "constellation-v1.0.0"
|
||||
export PKI=${PWD}/pki
|
||||
|
||||
export AWS_REGION=eu-central-1
|
||||
export AWS_REPLICATION_REGIONS="us-east-2"
|
||||
export AWS_BUCKET=constellation-images
|
||||
export AWS_EFIVARS_PATH=${PWD}/mkosi.output.aws/fedora~37/efivars.bin
|
||||
export AWS_IMAGE_PATH=${PWD}/mkosi.output.aws/fedora~37/image.raw
|
||||
export AWS_IMAGE_FILENAME=image-$(date +%s).raw
|
||||
export AWS_JSON_OUTPUT=${PWD}/mkosi.output.aws/fedora~37/image-upload.json
|
||||
secure-boot/aws/create_uefivars.sh "${AWS_EFIVARS_PATH}"
|
||||
upload/upload_aws.sh
|
||||
# Warning! Never set `--version` to a value that is already used for a release image.
|
||||
# Instead, use a `ref` that corresponds to your branch name.
|
||||
bazel run //image/upload -- aws --verbose --raw-image mkosi.output.aws/fedora~37/image.raw --variant "" --version ref/foo/stream/nightly/v2.7.0-pre-asdf
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -216,20 +207,12 @@ upload/upload_aws.sh
|
||||
- `pki_prod` is used for release images
|
||||
|
||||
```sh
|
||||
# set these variables
|
||||
export GCP_IMAGE_FAMILY= # e.g. "constellation"
|
||||
export GCP_IMAGE_NAME= # e.g. "constellation-v1.0.0"
|
||||
export PKI=${PWD}/pki
|
||||
|
||||
export GCP_PROJECT=constellation-images
|
||||
export GCP_REGION=europe-west3
|
||||
export GCP_BUCKET=constellation-images
|
||||
export GCP_RAW_IMAGE_PATH=${PWD}/mkosi.output.gcp/fedora~37/image.raw
|
||||
export GCP_IMAGE_FILENAME=$(date +%s).tar.gz
|
||||
export GCP_IMAGE_PATH=${PWD}/mkosi.output.gcp/fedora~37/image.tar.gz
|
||||
export GCP_JSON_OUTPUT=${PWD}/mkosi.output.gcp/fedora~37/image-upload.json
|
||||
upload/pack.sh gcp ${GCP_RAW_IMAGE_PATH} ${GCP_IMAGE_PATH}
|
||||
upload/upload_gcp.sh
|
||||
# Warning! Never set `--version` to a value that is already used for a release image.
|
||||
# Instead, use a `ref` that corresponds to your branch name.
|
||||
bazel run //image/upload -- gcp --verbose --raw-image "${GCP_IMAGE_PATH}" --variant "sev-es" --version ref/foo/stream/nightly/v2.7.0-pre-asdf
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -247,31 +230,12 @@ Note:
|
||||
- Optional (if Secure Boot should be enabled) [Prepare virtual machine guest state (VMGS) with customized NVRAM or use existing VMGS blob](#azure-secure-boot)
|
||||
|
||||
```sh
|
||||
# set these variables
|
||||
export AZURE_GALLERY_NAME= # e.g. "Constellation"
|
||||
export AZURE_IMAGE_DEFINITION= # e.g. "constellation"
|
||||
export AZURE_IMAGE_VERSION= # e.g. "1.0.0"
|
||||
# Set this variable to a path if you want to use Secure Boot.
|
||||
# Otherwise, set it to export AZURE_VMGS_PATH=
|
||||
export AZURE_VMGS_PATH= # e.g. nothing OR "path/to/ConfidentialVM.vmgs"
|
||||
# AZURE_SECURITY_TYPE can be one of
|
||||
# - "ConfidentialVMSupported" (ConfidentialVM with secure boot disabled),
|
||||
# - "ConfidentialVM" (ConfidentialVM with Secure Boot) or
|
||||
# - TrustedLaunch" (Trusted Launch with or without Secure Boot)
|
||||
export AZURE_SECURITY_TYPE=ConfidentialVMSupported
|
||||
|
||||
export AZURE_RESOURCE_GROUP_NAME=constellation-images
|
||||
export AZURE_REGION=northeurope
|
||||
export AZURE_REPLICATION_REGIONS="northeurope eastus westeurope westus"
|
||||
export AZURE_IMAGE_OFFER=constellation
|
||||
export AZURE_SKU=${AZURE_IMAGE_DEFINITION}
|
||||
export AZURE_PUBLISHER=edgelesssys
|
||||
export AZURE_DISK_NAME=constellation-$(date +%s)
|
||||
export AZURE_RAW_IMAGE_PATH=${PWD}/mkosi.output.azure/fedora~37/image.raw
|
||||
export AZURE_IMAGE_PATH=${PWD}/mkosi.output.azure/fedora~37/image.vhd
|
||||
export AZURE_JSON_OUTPUT=${PWD}/mkosi.output.azure/fedora~37/image-upload.json
|
||||
upload/pack.sh azure "${AZURE_RAW_IMAGE_PATH}" "${AZURE_IMAGE_PATH}"
|
||||
upload/upload_azure.sh -g --disk-name "${AZURE_DISK_NAME}" "${AZURE_VMGS_PATH}"
|
||||
# Warning! Never set `--version` to a value that is already used for a release image.
|
||||
# Instead, use a `ref` that corresponds to your branch name.
|
||||
bazel run //image/upload -- azure --verbose --raw-image "${AZURE_IMAGE_PATH}" --variant "cvm" --version ref/foo/stream/nightly/v2.7.0-pre-asdf
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -288,15 +252,9 @@ Note:
|
||||
- Login to AWS (see [here](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html))
|
||||
|
||||
```sh
|
||||
# set these variables
|
||||
export REF= # e.g. feat-xyz (branch name encoded with dashes)
|
||||
export STREAM= # e.g. "nightly", "debug", "stable" (depends on the type of image and if it is a release)
|
||||
export IMAGE_VERSION= # e.g. v2.1.0" or output of pseudo-version tool
|
||||
export OPENSTACK_BUCKET=cdn-constellation-backend
|
||||
export OPENSTACK_BASE_URL="https://cdn.confidential.cloud"
|
||||
export OPENSTACK_IMAGE_PATH=${PWD}/mkosi.output.qemu/fedora~37/image.raw
|
||||
export OPENSTACK_JSON_OUTPUT=${PWD}/mkosi.output.qemu/fedora~37/image-upload.json
|
||||
upload/upload_openstack.sh
|
||||
# Warning! Never set `--version` to a value that is already used for a release image.
|
||||
# Instead, use a `ref` that corresponds to your branch name.
|
||||
bazel run //image/upload -- openstack --verbose --raw-image mkosi.output.openstack/fedora~37/image.raw --variant "sev" --version ref/foo/stream/nightly/v2.7.0-pre-asdf
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -308,15 +266,9 @@ upload/upload_openstack.sh
|
||||
- Login to AWS (see [here](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html))
|
||||
|
||||
```sh
|
||||
# set these variables
|
||||
export REF= # e.g. feat-xyz (branch name encoded with dashes)
|
||||
export STREAM= # e.g. "nightly", "debug", "stable" (depends on the type of image and if it is a release)
|
||||
export IMAGE_VERSION= # e.g. v2.1.0" or output of pseudo-version tool
|
||||
export QEMU_BUCKET=cdn-constellation-backend
|
||||
export QEMU_BASE_URL="https://cdn.confidential.cloud"
|
||||
export QEMU_IMAGE_PATH=${PWD}/mkosi.output.qemu/fedora~37/image.raw
|
||||
export QEMU_JSON_OUTPUT=${PWD}/mkosi.output.qemu/fedora~37/image-upload.json
|
||||
upload/upload_qemu.sh
|
||||
# Warning! Never set `--version` to a value that is already used for a release image.
|
||||
# Instead, use a `ref` that corresponds to your branch name.
|
||||
bazel run //image/upload -- qemu --verbose --raw-image mkosi.output.qemu/fedora~37/image.raw --variant "default" --version ref/foo/stream/nightly/v2.7.0-pre-asdf
|
||||
```
|
||||
|
||||
</details>
|
||||
|
18
image/upload/BUILD.bazel
Normal file
18
image/upload/BUILD.bazel
Normal file
@ -0,0 +1,18 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "upload_lib",
|
||||
srcs = ["upload.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/image/upload",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//image/upload/internal/cmd",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "upload",
|
||||
embed = [":upload_lib"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
1
image/upload/delete_aws.sh
Normal file
1
image/upload/delete_aws.sh
Normal file
@ -0,0 +1 @@
|
||||
|
35
image/upload/internal/cmd/BUILD.bazel
Normal file
35
image/upload/internal/cmd/BUILD.bazel
Normal file
@ -0,0 +1,35 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "cmd",
|
||||
srcs = [
|
||||
"api.go",
|
||||
"aws.go",
|
||||
"azure.go",
|
||||
"flags.go",
|
||||
"gcp.go",
|
||||
"must.go",
|
||||
"nop.go",
|
||||
"openstack.go",
|
||||
"qemu.go",
|
||||
"secureboot.go",
|
||||
"upload.go",
|
||||
],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/image/upload/internal/cmd",
|
||||
visibility = ["//image/upload:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/logger",
|
||||
"//internal/osimage",
|
||||
"//internal/osimage/archive",
|
||||
"//internal/osimage/aws",
|
||||
"//internal/osimage/azure",
|
||||
"//internal/osimage/gcp",
|
||||
"//internal/osimage/nop",
|
||||
"//internal/osimage/secureboot",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_spf13_afero//:afero",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
"@org_uber_go_zap//zapcore",
|
||||
],
|
||||
)
|
25
image/upload/internal/cmd/api.go
Normal file
25
image/upload/internal/cmd/api.go
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
type archivist interface {
|
||||
Archive(ctx context.Context,
|
||||
version versionsapi.Version, csp, variant string, img io.Reader,
|
||||
) (string, error)
|
||||
}
|
||||
|
||||
type uploader interface {
|
||||
Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error)
|
||||
}
|
97
image/upload/internal/cmd/aws.go
Normal file
97
image/upload/internal/cmd/aws.go
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/archive"
|
||||
awsupload "github.com/edgelesssys/constellation/v2/internal/osimage/aws"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewAWSCmd returns the command that uploads an OS image to AWS.
|
||||
func NewAWSCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "aws",
|
||||
Short: "Upload OS image to AWS",
|
||||
Long: "Upload OS image to AWS.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: runAWS,
|
||||
}
|
||||
|
||||
cmd.Flags().String("aws-region", "eu-central-1", "AWS region used during AMI creation")
|
||||
cmd.Flags().String("aws-bucket", "constellation-images", "S3 bucket used during AMI creation")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runAWS(cmd *cobra.Command, _ []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseAWSFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
archiveC, err := archive.New(cmd.Context(), flags.region, flags.bucket, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadC, err := awsupload.New(flags.awsRegion, flags.awsBucket, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: %w", err)
|
||||
}
|
||||
|
||||
file, err := os.Open(flags.rawImage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening image file %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
size, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := file.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
out := cmd.OutOrStdout()
|
||||
if len(flags.out) > 0 {
|
||||
outF, err := os.Create(flags.out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening output file %w", err)
|
||||
}
|
||||
defer outF.Close()
|
||||
out = outF
|
||||
}
|
||||
|
||||
sbDatabase, uefiVarStore, err := loadSecureBootKeys(flags.pki)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadReq := &osimage.UploadRequest{
|
||||
Provider: flags.provider,
|
||||
Version: flags.version,
|
||||
Variant: flags.variant,
|
||||
SBDatabase: sbDatabase,
|
||||
UEFIVarStore: uefiVarStore,
|
||||
Size: size,
|
||||
Timestamp: flags.timestamp,
|
||||
Image: file,
|
||||
}
|
||||
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
|
||||
}
|
98
image/upload/internal/cmd/azure.go
Normal file
98
image/upload/internal/cmd/azure.go
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/archive"
|
||||
azureupload "github.com/edgelesssys/constellation/v2/internal/osimage/azure"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewAzureCmd returns the command that uploads an OS image to Azure.
|
||||
func NewAzureCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "azure",
|
||||
Short: "Upload OS image to Azure",
|
||||
Long: "Upload OS image to Azure.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: runAzure,
|
||||
}
|
||||
|
||||
cmd.Flags().String("az-subscription", "0d202bbb-4fa7-4af8-8125-58c269a05435", "Azure subscription to use")
|
||||
cmd.Flags().String("az-location", "northeurope", "Azure location to use")
|
||||
cmd.Flags().String("az-resource-group", "constellation-images", "Azure resource group to use")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runAzure(cmd *cobra.Command, _ []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseAzureFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
archiveC, err := archive.New(cmd.Context(), flags.region, flags.bucket, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadC, err := azureupload.New(flags.azSubscription, flags.azLocation, flags.azResourceGroup, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: %w", err)
|
||||
}
|
||||
|
||||
file, err := os.Open(flags.rawImage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening image file %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
size, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := file.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
out := cmd.OutOrStdout()
|
||||
if len(flags.out) > 0 {
|
||||
outF, err := os.Create(flags.out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening output file %w", err)
|
||||
}
|
||||
defer outF.Close()
|
||||
out = outF
|
||||
}
|
||||
|
||||
sbDatabase, uefiVarStore, err := loadSecureBootKeys(flags.pki)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadReq := &osimage.UploadRequest{
|
||||
Provider: flags.provider,
|
||||
Version: flags.version,
|
||||
Variant: flags.variant,
|
||||
SBDatabase: sbDatabase,
|
||||
UEFIVarStore: uefiVarStore,
|
||||
Size: size,
|
||||
Timestamp: flags.timestamp,
|
||||
Image: file,
|
||||
}
|
||||
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
|
||||
}
|
200
image/upload/internal/cmd/flags.go
Normal file
200
image/upload/internal/cmd/flags.go
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type commonFlags struct {
|
||||
rawImage string
|
||||
pki string
|
||||
provider cloudprovider.Provider
|
||||
variant string
|
||||
version versionsapi.Version
|
||||
timestamp time.Time
|
||||
region string
|
||||
bucket string
|
||||
out string
|
||||
logLevel zapcore.Level
|
||||
}
|
||||
|
||||
func parseCommonFlags(cmd *cobra.Command) (commonFlags, error) {
|
||||
workspaceDir := os.Getenv("BUILD_WORKSPACE_DIRECTORY")
|
||||
rawImage, err := cmd.Flags().GetString("raw-image")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
pki, err := cmd.Flags().GetString("pki")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
if pki == "" {
|
||||
pki = filepath.Join(workspaceDir, "image/pki")
|
||||
}
|
||||
variant, err := cmd.Flags().GetString("variant")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
version, err := cmd.Flags().GetString("version")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
ver, err := versionsapi.NewVersionFromShortPath(version, versionsapi.VersionKindImage)
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
timestamp, err := cmd.Flags().GetString("timestamp")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
if timestamp == "" {
|
||||
timestamp = time.Now().Format("2006-01-02T15:04:05Z07:00")
|
||||
}
|
||||
timestmp, err := time.Parse("2006-01-02T15:04:05Z07:00", timestamp)
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
region, err := cmd.Flags().GetString("region")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
bucket, err := cmd.Flags().GetString("bucket")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
out, err := cmd.Flags().GetString("out")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
verbose, err := cmd.Flags().GetBool("verbose")
|
||||
if err != nil {
|
||||
return commonFlags{}, err
|
||||
}
|
||||
logLevel := zapcore.InfoLevel
|
||||
if verbose {
|
||||
logLevel = zapcore.DebugLevel
|
||||
}
|
||||
|
||||
return commonFlags{
|
||||
rawImage: rawImage,
|
||||
pki: pki,
|
||||
variant: variant,
|
||||
version: ver,
|
||||
timestamp: timestmp,
|
||||
region: region,
|
||||
bucket: bucket,
|
||||
out: out,
|
||||
logLevel: logLevel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type awsFlags struct {
|
||||
commonFlags
|
||||
awsRegion string
|
||||
awsBucket string
|
||||
}
|
||||
|
||||
func parseAWSFlags(cmd *cobra.Command) (awsFlags, error) {
|
||||
common, err := parseCommonFlags(cmd)
|
||||
if err != nil {
|
||||
return awsFlags{}, err
|
||||
}
|
||||
|
||||
awsRegion, err := cmd.Flags().GetString("aws-region")
|
||||
if err != nil {
|
||||
return awsFlags{}, err
|
||||
}
|
||||
awsBucket, err := cmd.Flags().GetString("aws-bucket")
|
||||
if err != nil {
|
||||
return awsFlags{}, err
|
||||
}
|
||||
|
||||
common.provider = cloudprovider.AWS
|
||||
return awsFlags{
|
||||
commonFlags: common,
|
||||
awsRegion: awsRegion,
|
||||
awsBucket: awsBucket,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type azureFlags struct {
|
||||
commonFlags
|
||||
azSubscription string
|
||||
azLocation string
|
||||
azResourceGroup string
|
||||
}
|
||||
|
||||
func parseAzureFlags(cmd *cobra.Command) (azureFlags, error) {
|
||||
common, err := parseCommonFlags(cmd)
|
||||
if err != nil {
|
||||
return azureFlags{}, err
|
||||
}
|
||||
|
||||
azSubscription, err := cmd.Flags().GetString("az-subscription")
|
||||
if err != nil {
|
||||
return azureFlags{}, err
|
||||
}
|
||||
azLocation, err := cmd.Flags().GetString("az-location")
|
||||
if err != nil {
|
||||
return azureFlags{}, err
|
||||
}
|
||||
azResourceGroup, err := cmd.Flags().GetString("az-resource-group")
|
||||
if err != nil {
|
||||
return azureFlags{}, err
|
||||
}
|
||||
|
||||
common.provider = cloudprovider.Azure
|
||||
return azureFlags{
|
||||
commonFlags: common,
|
||||
azSubscription: azSubscription,
|
||||
azLocation: azLocation,
|
||||
azResourceGroup: azResourceGroup,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type gcpFlags struct {
|
||||
commonFlags
|
||||
gcpProject string
|
||||
gcpLocation string
|
||||
gcpBucket string
|
||||
}
|
||||
|
||||
func parseGCPFlags(cmd *cobra.Command) (gcpFlags, error) {
|
||||
common, err := parseCommonFlags(cmd)
|
||||
if err != nil {
|
||||
return gcpFlags{}, err
|
||||
}
|
||||
|
||||
gcpProject, err := cmd.Flags().GetString("gcp-project")
|
||||
if err != nil {
|
||||
return gcpFlags{}, err
|
||||
}
|
||||
gcpLocation, err := cmd.Flags().GetString("gcp-location")
|
||||
if err != nil {
|
||||
return gcpFlags{}, err
|
||||
}
|
||||
gcpBucket, err := cmd.Flags().GetString("gcp-bucket")
|
||||
if err != nil {
|
||||
return gcpFlags{}, err
|
||||
}
|
||||
|
||||
common.provider = cloudprovider.GCP
|
||||
return gcpFlags{
|
||||
commonFlags: common,
|
||||
gcpProject: gcpProject,
|
||||
gcpLocation: gcpLocation,
|
||||
gcpBucket: gcpBucket,
|
||||
}, nil
|
||||
}
|
98
image/upload/internal/cmd/gcp.go
Normal file
98
image/upload/internal/cmd/gcp.go
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/archive"
|
||||
gcpupload "github.com/edgelesssys/constellation/v2/internal/osimage/gcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewGCPCommand returns the command that uploads an OS image to GCP.
|
||||
func NewGCPCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "gcp",
|
||||
Short: "Upload OS image to GCP",
|
||||
Long: "Upload OS image to GCP.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: runGCP,
|
||||
}
|
||||
|
||||
cmd.Flags().String("gcp-project", "constellation-images", "GCP project to use")
|
||||
cmd.Flags().String("gcp-location", "europe-west3", "GCP location to use")
|
||||
cmd.Flags().String("gcp-bucket", "constellation-images", "GCP bucket to use")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runGCP(cmd *cobra.Command, _ []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseGCPFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
archiveC, err := archive.New(cmd.Context(), flags.region, flags.bucket, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadC, err := gcpupload.New(cmd.Context(), flags.gcpProject, flags.gcpLocation, flags.gcpBucket, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: %w", err)
|
||||
}
|
||||
|
||||
file, err := os.Open(flags.rawImage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening image file %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
size, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := file.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
out := cmd.OutOrStdout()
|
||||
if len(flags.out) > 0 {
|
||||
outF, err := os.Create(flags.out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening output file %w", err)
|
||||
}
|
||||
defer outF.Close()
|
||||
out = outF
|
||||
}
|
||||
|
||||
sbDatabase, uefiVarStore, err := loadSecureBootKeys(flags.pki)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadReq := &osimage.UploadRequest{
|
||||
Provider: flags.provider,
|
||||
Version: flags.version,
|
||||
Variant: flags.variant,
|
||||
SBDatabase: sbDatabase,
|
||||
UEFIVarStore: uefiVarStore,
|
||||
Size: size,
|
||||
Timestamp: flags.timestamp,
|
||||
Image: file,
|
||||
}
|
||||
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
|
||||
}
|
13
image/upload/internal/cmd/must.go
Normal file
13
image/upload/internal/cmd/must.go
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
81
image/upload/internal/cmd/nop.go
Normal file
81
image/upload/internal/cmd/nop.go
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/archive"
|
||||
nopupload "github.com/edgelesssys/constellation/v2/internal/osimage/nop"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func runNOP(cmd *cobra.Command, provider cloudprovider.Provider, _ []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseCommonFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flags.provider = provider
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
archiveC, err := archive.New(cmd.Context(), flags.region, flags.bucket, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadC := nopupload.New(log)
|
||||
|
||||
file, err := os.Open(flags.rawImage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening image file %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
size, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := file.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
out := cmd.OutOrStdout()
|
||||
if len(flags.out) > 0 {
|
||||
outF, err := os.Create(flags.out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image: opening output file %w", err)
|
||||
}
|
||||
defer outF.Close()
|
||||
out = outF
|
||||
}
|
||||
|
||||
sbDatabase, uefiVarStore, err := loadSecureBootKeys(flags.pki)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadReq := &osimage.UploadRequest{
|
||||
Provider: flags.provider,
|
||||
Version: flags.version,
|
||||
Variant: flags.variant,
|
||||
SBDatabase: sbDatabase,
|
||||
UEFIVarStore: uefiVarStore,
|
||||
Size: size,
|
||||
Timestamp: flags.timestamp,
|
||||
Image: file,
|
||||
}
|
||||
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
|
||||
}
|
29
image/upload/internal/cmd/openstack.go
Normal file
29
image/upload/internal/cmd/openstack.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewOpenStackCmd returns the command that uploads an OS image to OpenStack.
|
||||
func NewOpenStackCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "openstack",
|
||||
Short: "Upload OS image to OpenStack",
|
||||
Long: "Upload OS image to OpenStack.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: runOpenStack,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runOpenStack(cmd *cobra.Command, args []string) error {
|
||||
return runNOP(cmd, cloudprovider.OpenStack, args)
|
||||
}
|
29
image/upload/internal/cmd/qemu.go
Normal file
29
image/upload/internal/cmd/qemu.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewQEMUCmd returns the command that uploads an OS image to QEMU.
|
||||
func NewQEMUCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "qemu",
|
||||
Short: "Upload OS image to QEMU",
|
||||
Long: "Upload OS image to QEMU.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: runQEMU,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runQEMU(cmd *cobra.Command, args []string) error {
|
||||
return runNOP(cmd, cloudprovider.QEMU, args)
|
||||
}
|
44
image/upload/internal/cmd/secureboot.go
Normal file
44
image/upload/internal/cmd/secureboot.go
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/secureboot"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
func loadSecureBootKeys(basePath string) (secureboot.Database, secureboot.UEFIVarStore, error) {
|
||||
platformKeyCert := filepath.Join(basePath, "PK.cer")
|
||||
keyExchangeKeyCerts := []string{
|
||||
filepath.Join(basePath, "KEK.cer"),
|
||||
filepath.Join(basePath, "MicCorKEKCA2011_2011-06-24.crt"),
|
||||
}
|
||||
signatureDBCerts := []string{
|
||||
filepath.Join(basePath, "db.cer"),
|
||||
filepath.Join(basePath, "MicWinProPCA2011_2011-10-19.crt"),
|
||||
filepath.Join(basePath, "MicCorUEFCA2011_2011-06-27.crt"),
|
||||
}
|
||||
sbDatabase, err := secureboot.DatabaseFromFiles(afero.NewOsFs(), platformKeyCert, keyExchangeKeyCerts, signatureDBCerts)
|
||||
if err != nil {
|
||||
return secureboot.Database{},
|
||||
secureboot.UEFIVarStore{},
|
||||
fmt.Errorf("preparing secure boot database: %w", err)
|
||||
}
|
||||
platformKeyESL := filepath.Join(basePath, "PK.esl")
|
||||
keyExchangeKeyESL := filepath.Join(basePath, "KEK.esl")
|
||||
signatureDBESL := filepath.Join(basePath, "db.esl")
|
||||
uefiVarStore, err := secureboot.VarStoreFromFiles(afero.NewOsFs(), platformKeyESL, keyExchangeKeyESL, signatureDBESL, "")
|
||||
if err != nil {
|
||||
return secureboot.Database{},
|
||||
secureboot.UEFIVarStore{},
|
||||
fmt.Errorf("preparing secure boot variable store: %w", err)
|
||||
}
|
||||
return sbDatabase, uefiVarStore, nil
|
||||
}
|
66
image/upload/internal/cmd/upload.go
Normal file
66
image/upload/internal/cmd/upload.go
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
func uploadImage(ctx context.Context, archiveC archivist, uploadC uploader, req *osimage.UploadRequest, out io.Writer) error {
|
||||
// upload to S3 archive
|
||||
archiveURL, err := archiveC.Archive(ctx, req.Version, req.Provider.String(), req.Variant, req.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// rewind reader so we can read again
|
||||
if _, err := req.Image.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
// upload to CSP
|
||||
imageReferences, err := uploadC.Upload(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(imageReferences) == 0 {
|
||||
imageReferences = map[string]string{
|
||||
req.Variant: archiveURL,
|
||||
}
|
||||
}
|
||||
|
||||
imageInfo := versionsapi.ImageInfo{
|
||||
Ref: req.Version.Ref,
|
||||
Stream: req.Version.Stream,
|
||||
Version: req.Version.Version,
|
||||
}
|
||||
switch req.Provider {
|
||||
case cloudprovider.AWS:
|
||||
imageInfo.AWS = imageReferences
|
||||
case cloudprovider.Azure:
|
||||
imageInfo.Azure = imageReferences
|
||||
case cloudprovider.GCP:
|
||||
imageInfo.GCP = imageReferences
|
||||
case cloudprovider.OpenStack:
|
||||
imageInfo.OpenStack = imageReferences
|
||||
case cloudprovider.QEMU:
|
||||
imageInfo.QEMU = imageReferences
|
||||
default:
|
||||
return fmt.Errorf("uploading image: cloud provider %s is not yet supported", req.Provider.String())
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(out).Encode(imageInfo); err != nil {
|
||||
return fmt.Errorf("uploading image: marshaling output: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
100
image/upload/upload.go
Normal file
100
image/upload/upload.go
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// upload uploads os images.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/image/upload/internal/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func execute() error {
|
||||
rootCmd := newRootCmd()
|
||||
ctx, cancel := signalContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
return rootCmd.ExecuteContext(ctx)
|
||||
}
|
||||
|
||||
func newRootCmd() *cobra.Command {
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "upload",
|
||||
Short: "Uploads OS images to supported CSPs",
|
||||
Long: "Uploads OS images to supported CSPs.",
|
||||
PersistentPreRun: preRunRoot,
|
||||
}
|
||||
|
||||
rootCmd.SetOut(os.Stdout)
|
||||
|
||||
rootCmd.PersistentFlags().String("raw-image", "", "Path to os image in CSP specific format that should be uploaded.")
|
||||
rootCmd.PersistentFlags().String("pki", "", "Base path to the PKI (secure boot signing) files.")
|
||||
rootCmd.PersistentFlags().String("variant", "", "Variant of the image being uploaded.")
|
||||
rootCmd.PersistentFlags().String("version", "", "Shortname of the os image version.")
|
||||
rootCmd.PersistentFlags().String("timestamp", "", "Optional timestamp to use for resource names. Uses format 2006-01-02T15:04:05Z07:00.")
|
||||
rootCmd.PersistentFlags().String("region", "eu-central-1", "AWS region of the archive S3 bucket")
|
||||
rootCmd.PersistentFlags().String("bucket", "cdn-constellation-backend", "S3 bucket name of the archive")
|
||||
rootCmd.PersistentFlags().String("out", "", "Optional path to write the upload result to. If not set, the result is written to stdout.")
|
||||
rootCmd.PersistentFlags().Bool("verbose", false, "Enable verbose output")
|
||||
must(rootCmd.MarkPersistentFlagRequired("raw-image"))
|
||||
must(rootCmd.MarkPersistentFlagRequired("variant"))
|
||||
must(rootCmd.MarkPersistentFlagRequired("version"))
|
||||
|
||||
rootCmd.AddCommand(cmd.NewAWSCmd())
|
||||
rootCmd.AddCommand(cmd.NewAzureCmd())
|
||||
rootCmd.AddCommand(cmd.NewGCPCommand())
|
||||
rootCmd.AddCommand(cmd.NewOpenStackCmd())
|
||||
rootCmd.AddCommand(cmd.NewQEMUCmd())
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
// signalContext returns a context that is canceled on the handed signal.
|
||||
// The signal isn't watched after its first occurrence. Call the cancel
|
||||
// function to ensure the internal goroutine is stopped and the signal isn't
|
||||
// watched any longer.
|
||||
func signalContext(ctx context.Context, sig os.Signal) (context.Context, context.CancelFunc) {
|
||||
sigCtx, stop := signal.NotifyContext(ctx, sig)
|
||||
done := make(chan struct{}, 1)
|
||||
stopDone := make(chan struct{}, 1)
|
||||
|
||||
go func() {
|
||||
defer func() { stopDone <- struct{}{} }()
|
||||
defer stop()
|
||||
select {
|
||||
case <-sigCtx.Done():
|
||||
fmt.Println(" Signal caught. Press ctrl+c again to terminate the program immediately.")
|
||||
case <-done:
|
||||
}
|
||||
}()
|
||||
|
||||
cancelFunc := func() {
|
||||
done <- struct{}{}
|
||||
<-stopDone
|
||||
}
|
||||
|
||||
return sigCtx, cancelFunc
|
||||
}
|
||||
|
||||
func preRunRoot(cmd *cobra.Command, _ []string) {
|
||||
cmd.SilenceUsage = true
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -19,10 +19,14 @@ var (
|
||||
"component": "constellation-kek",
|
||||
}
|
||||
// StorageTags are the default tags for kms client created storage solutions.
|
||||
StorageTags = map[string]string{
|
||||
"createdBy": "constellation-kms-client",
|
||||
"component": "constellation-dek-store",
|
||||
StorageTags = map[string]*string{
|
||||
"createdBy": toPtr("constellation-kms-client"),
|
||||
"component": toPtr("constellation-dek-store"),
|
||||
}
|
||||
// AWSS3Tag is the default tag string for kms client created AWS S3 storage solutions.
|
||||
AWSS3Tag = "createdBy=constellation-kms-client&component=constellation-dek-store"
|
||||
)
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
13
internal/osimage/BUILD.bazel
Normal file
13
internal/osimage/BUILD.bazel
Normal file
@ -0,0 +1,13 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "osimage",
|
||||
srcs = ["osimage.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/osimage/secureboot",
|
||||
"//internal/versionsapi",
|
||||
],
|
||||
)
|
16
internal/osimage/archive/BUILD.bazel
Normal file
16
internal/osimage/archive/BUILD.bazel
Normal file
@ -0,0 +1,16 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "archive",
|
||||
srcs = ["archive.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/archive",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/logger",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_aws_aws_sdk_go_v2_config//:config",
|
||||
"@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//types",
|
||||
],
|
||||
)
|
68
internal/osimage/archive/archive.go
Normal file
68
internal/osimage/archive/archive.go
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package archive is used to archive OS images in S3.
|
||||
package archive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
// Archivist uploads OS images to S3.
|
||||
type Archivist struct {
|
||||
uploadClient uploadClient
|
||||
// bucket is the name of the S3 bucket to use.
|
||||
bucket string
|
||||
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
// New creates a new Archivist.
|
||||
func New(ctx context.Context, region, bucket string, log *logger.Logger) (*Archivist, error) {
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s3client := s3.NewFromConfig(cfg)
|
||||
uploadClient := s3manager.NewUploader(s3client)
|
||||
|
||||
return &Archivist{
|
||||
uploadClient: uploadClient,
|
||||
bucket: bucket,
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Archive reads the OS image in img and uploads it as key.
|
||||
func (a *Archivist) Archive(ctx context.Context, version versionsapi.Version, csp, variant string, img io.Reader) (string, error) {
|
||||
key, err := url.JoinPath(version.ArtifactPath(), version.Kind.String(), "csp", csp, variant, "image.raw")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
a.log.Debugf("Archiving OS image %s %s %v to s3://%v/%v", csp, variant, version.ShortPath(), a.bucket, key)
|
||||
_, err = a.uploadClient.Upload(ctx, &s3.PutObjectInput{
|
||||
Bucket: &a.bucket,
|
||||
Key: &key,
|
||||
Body: img,
|
||||
ChecksumAlgorithm: s3types.ChecksumAlgorithmSha256,
|
||||
})
|
||||
return baseURL + key, err
|
||||
}
|
||||
|
||||
type uploadClient interface {
|
||||
Upload(ctx context.Context, input *s3.PutObjectInput, opts ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
|
||||
}
|
||||
|
||||
const baseURL = "https://cdn.confidential.cloud/"
|
21
internal/osimage/aws/BUILD.bazel
Normal file
21
internal/osimage/aws/BUILD.bazel
Normal file
@ -0,0 +1,21 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "aws",
|
||||
srcs = ["awsupload.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/aws",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/logger",
|
||||
"//internal/osimage",
|
||||
"//internal/osimage/secureboot",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_aws_aws_sdk_go_v2_config//:config",
|
||||
"@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_ec2//:ec2",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_ec2//types",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//types",
|
||||
"@com_github_aws_smithy_go//:smithy-go",
|
||||
],
|
||||
)
|
590
internal/osimage/aws/awsupload.go
Normal file
590
internal/osimage/aws/awsupload.go
Normal file
@ -0,0 +1,590 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package aws implements uploading os images to aws.
|
||||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/aws/smithy-go"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/secureboot"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
// Uploader can upload and remove os images on GCP.
|
||||
type Uploader struct {
|
||||
region string
|
||||
bucketName string
|
||||
ec2 func(ctx context.Context, region string) (ec2API, error)
|
||||
s3 func(ctx context.Context, region string) (s3API, error)
|
||||
s3uploader func(ctx context.Context, region string) (s3UploaderAPI, error)
|
||||
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
// New creates a new Uploader.
|
||||
func New(region, bucketName string, log *logger.Logger) (*Uploader, error) {
|
||||
return &Uploader{
|
||||
region: region,
|
||||
bucketName: bucketName,
|
||||
ec2: func(ctx context.Context, region string) (ec2API, error) {
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ec2.NewFromConfig(cfg), nil
|
||||
},
|
||||
s3: func(ctx context.Context, region string) (s3API, error) {
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s3.NewFromConfig(cfg), nil
|
||||
},
|
||||
s3uploader: func(ctx context.Context, region string) (s3UploaderAPI, error) {
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s3manager.NewUploader(s3.NewFromConfig(cfg)), nil
|
||||
},
|
||||
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Upload uploads an OS image to AWS.
|
||||
func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error) {
|
||||
blobName := fmt.Sprintf("image-%s-%s-%d.raw", req.Version.Stream, req.Version.Version, req.Timestamp.Unix())
|
||||
imageName := imageName(req.Version, req.Timestamp)
|
||||
allRegions := []string{u.region}
|
||||
allRegions = append(allRegions, replicationRegions...)
|
||||
// TODO(malt3): make this configurable
|
||||
publish := true
|
||||
amiIDs := make(map[string]string, len(allRegions))
|
||||
if err := u.ensureBucket(ctx); err != nil {
|
||||
return nil, fmt.Errorf("ensuring bucket %s exists: %w", u.bucketName, err)
|
||||
}
|
||||
|
||||
// pre-cleaning
|
||||
for _, region := range allRegions {
|
||||
if err := u.ensureImageDeleted(ctx, imageName, region); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no image under the name %s in region %s: %w", imageName, region, err)
|
||||
}
|
||||
}
|
||||
if err := u.ensureSnapshotDeleted(ctx, imageName, u.region); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no snapshot using the same name exists: %w", err)
|
||||
}
|
||||
if err := u.ensureBlobDeleted(ctx, blobName); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no blob using the same name exists: %w", err)
|
||||
}
|
||||
|
||||
// create primary image
|
||||
if err := u.uploadBlob(ctx, blobName, req.Image); err != nil {
|
||||
return nil, fmt.Errorf("uploading image to s3: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := u.ensureBlobDeleted(ctx, blobName); err != nil {
|
||||
u.log.Errorf("post-cleaning: deleting temporary blob from s3", err)
|
||||
}
|
||||
}()
|
||||
snapshotID, err := u.importSnapshot(ctx, blobName, imageName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("importing snapshot: %w", err)
|
||||
}
|
||||
primaryAMIID, err := u.createImageFromSnapshot(ctx, req.Version, imageName, snapshotID, req.UEFIVarStore)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating image from snapshot: %w", err)
|
||||
}
|
||||
amiIDs[u.region] = primaryAMIID
|
||||
if err := u.waitForImage(ctx, primaryAMIID, u.region); err != nil {
|
||||
return nil, fmt.Errorf("waiting for primary image to become available: %w", err)
|
||||
}
|
||||
|
||||
// replicate image
|
||||
for _, region := range replicationRegions {
|
||||
amiID, err := u.replicateImage(ctx, imageName, primaryAMIID, region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("replicating image to region %s: %w", region, err)
|
||||
}
|
||||
amiIDs[region] = amiID
|
||||
}
|
||||
|
||||
// wait for replication, tag, publish
|
||||
for _, region := range allRegions {
|
||||
if err := u.waitForImage(ctx, amiIDs[region], region); err != nil {
|
||||
return nil, fmt.Errorf("waiting for image to become available in region %s: %w", region, err)
|
||||
}
|
||||
if err := u.tagImageAndSnapshot(ctx, imageName, amiIDs[region], region); err != nil {
|
||||
return nil, fmt.Errorf("tagging image in region %s: %w", region, err)
|
||||
}
|
||||
if !publish {
|
||||
continue
|
||||
}
|
||||
if err := u.publishImage(ctx, amiIDs[region], region); err != nil {
|
||||
return nil, fmt.Errorf("publishing image in region %s: %w", region, err)
|
||||
}
|
||||
}
|
||||
return amiIDs, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureBucket(ctx context.Context) error {
|
||||
s3C, err := u.s3(ctx, u.region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("determining if bucket %s exists: %w", u.bucketName, err)
|
||||
}
|
||||
_, err = s3C.HeadBucket(ctx, &s3.HeadBucketInput{
|
||||
Bucket: &u.bucketName,
|
||||
})
|
||||
if err == nil {
|
||||
u.log.Debugf("Bucket %s exists", u.bucketName)
|
||||
return nil
|
||||
}
|
||||
var noSuchBucketErr *types.NoSuchBucket
|
||||
if !errors.As(err, &noSuchBucketErr) {
|
||||
return fmt.Errorf("determining if bucket %s exists: %w", u.bucketName, err)
|
||||
}
|
||||
u.log.Debugf("Creating bucket %s", u.bucketName)
|
||||
_, err = s3C.CreateBucket(ctx, &s3.CreateBucketInput{
|
||||
Bucket: &u.bucketName,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating bucket %s: %w", u.bucketName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) uploadBlob(ctx context.Context, blobName string, img io.Reader) error {
|
||||
u.log.Debugf("Uploading os image as %s", blobName)
|
||||
uploadC, err := u.s3uploader(ctx, u.region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = uploadC.Upload(ctx, &s3.PutObjectInput{
|
||||
Bucket: &u.bucketName,
|
||||
Key: &blobName,
|
||||
Body: img,
|
||||
ChecksumAlgorithm: s3types.ChecksumAlgorithmSha256,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureBlobDeleted(ctx context.Context, blobName string) error {
|
||||
s3C, err := u.s3(ctx, u.region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s3C.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: &u.bucketName,
|
||||
Key: &blobName,
|
||||
})
|
||||
var apiError smithy.APIError
|
||||
if errors.As(err, &apiError) && apiError.ErrorCode() == "NotFound" {
|
||||
u.log.Debugf("Blob %s in %s doesn't exist. Nothing to clean up.", blobName, u.bucketName)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.log.Debugf("Deleting blob %s", blobName)
|
||||
_, err = s3C.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &u.bucketName,
|
||||
Key: &blobName,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *Uploader) findSnapshots(ctx context.Context, snapshotName, region string) ([]string, error) {
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
snapshots, err := ec2C.DescribeSnapshots(ctx, &ec2.DescribeSnapshotsInput{
|
||||
Filters: []ec2types.Filter{
|
||||
{
|
||||
Name: toPtr("tag:Name"),
|
||||
Values: []string{snapshotName},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("describing snapshots: %w", err)
|
||||
}
|
||||
var snapshotIDs []string
|
||||
for _, s := range snapshots.Snapshots {
|
||||
if s.SnapshotId == nil {
|
||||
continue
|
||||
}
|
||||
snapshotIDs = append(snapshotIDs, *s.SnapshotId)
|
||||
}
|
||||
return snapshotIDs, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) importSnapshot(ctx context.Context, blobName, snapshotName string) (string, error) {
|
||||
u.log.Debugf("Importing %s as snapshot %s", blobName, snapshotName)
|
||||
ec2C, err := u.ec2(ctx, u.region)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
importResp, err := ec2C.ImportSnapshot(ctx, &ec2.ImportSnapshotInput{
|
||||
ClientData: &ec2types.ClientData{
|
||||
Comment: &snapshotName,
|
||||
},
|
||||
Description: &snapshotName,
|
||||
DiskContainer: &ec2types.SnapshotDiskContainer{
|
||||
Description: &snapshotName,
|
||||
Format: toPtr(string(ec2types.DiskImageFormatRaw)),
|
||||
UserBucket: &ec2types.UserBucket{
|
||||
S3Bucket: &u.bucketName,
|
||||
S3Key: &blobName,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("importing snapshot: %w", err)
|
||||
}
|
||||
if importResp.ImportTaskId == nil {
|
||||
return "", fmt.Errorf("importing snapshot: no import task ID returned")
|
||||
}
|
||||
u.log.Debugf("Waiting for snapshot %s to be ready", snapshotName)
|
||||
return waitForSnapshotImport(ctx, ec2C, *importResp.ImportTaskId)
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureSnapshotDeleted(ctx context.Context, snapshotName, region string) error {
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
snapshots, err := u.findSnapshots(ctx, snapshotName, region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("finding snapshots: %w", err)
|
||||
}
|
||||
for _, snapshot := range snapshots {
|
||||
u.log.Debugf("Deleting snapshot %s in %s", snapshot, region)
|
||||
_, err = ec2C.DeleteSnapshot(ctx, &ec2.DeleteSnapshotInput{
|
||||
SnapshotId: toPtr(snapshot),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting snapshot %s: %w", snapshot, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) createImageFromSnapshot(ctx context.Context, version versionsapi.Version, imageName, snapshotID string, uefiVarStore secureboot.UEFIVarStore) (string, error) {
|
||||
u.log.Debugf("Creating image %s in %s", imageName, u.region)
|
||||
ec2C, err := u.ec2(ctx, u.region)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
uefiData, err := uefiVarStore.ToAWS()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating uefi data: %w", err)
|
||||
}
|
||||
createReq, err := ec2C.RegisterImage(ctx, &ec2.RegisterImageInput{
|
||||
Name: &imageName,
|
||||
Architecture: ec2types.ArchitectureValuesX8664,
|
||||
BlockDeviceMappings: []ec2types.BlockDeviceMapping{
|
||||
{
|
||||
DeviceName: toPtr("/dev/xvda"),
|
||||
Ebs: &ec2types.EbsBlockDevice{
|
||||
DeleteOnTermination: toPtr(true),
|
||||
SnapshotId: &snapshotID,
|
||||
},
|
||||
},
|
||||
},
|
||||
BootMode: ec2types.BootModeValuesUefi,
|
||||
Description: toPtr("Constellation " + version.ShortPath()),
|
||||
EnaSupport: toPtr(true),
|
||||
RootDeviceName: toPtr("/dev/xvda"),
|
||||
TpmSupport: ec2types.TpmSupportValuesV20,
|
||||
UefiData: &uefiData,
|
||||
VirtualizationType: toPtr("hvm"),
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating image: %w", err)
|
||||
}
|
||||
if createReq.ImageId == nil {
|
||||
return "", fmt.Errorf("creating image: no image ID returned")
|
||||
}
|
||||
return *createReq.ImageId, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) replicateImage(ctx context.Context, imageName, amiID string, region string) (string, error) {
|
||||
u.log.Debugf("Replicating image %s to %s", imageName, region)
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
replicateReq, err := ec2C.CopyImage(ctx, &ec2.CopyImageInput{
|
||||
Name: &imageName,
|
||||
SourceImageId: &amiID,
|
||||
SourceRegion: &u.region,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("replicating image: %w", err)
|
||||
}
|
||||
if replicateReq.ImageId == nil {
|
||||
return "", fmt.Errorf("replicating image: no image ID returned")
|
||||
}
|
||||
return *replicateReq.ImageId, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) findImage(ctx context.Context, imageName, region string) (string, error) {
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
snapshots, err := ec2C.DescribeImages(ctx, &ec2.DescribeImagesInput{
|
||||
Filters: []ec2types.Filter{
|
||||
{
|
||||
Name: toPtr("name"),
|
||||
Values: []string{imageName},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("describing images: %w", err)
|
||||
}
|
||||
if len(snapshots.Images) == 0 {
|
||||
return "", errAMIDoesNotExist
|
||||
}
|
||||
if len(snapshots.Images) != 1 {
|
||||
return "", fmt.Errorf("expected 1 image, got %d", len(snapshots.Images))
|
||||
}
|
||||
if snapshots.Images[0].ImageId == nil {
|
||||
return "", fmt.Errorf("image ID is nil")
|
||||
}
|
||||
return *snapshots.Images[0].ImageId, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) waitForImage(ctx context.Context, amiID, region string) error {
|
||||
u.log.Debugf("Waiting for image %s in %s to be created", amiID, region)
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
waiter := ec2.NewImageAvailableWaiter(ec2C)
|
||||
err = waiter.Wait(ctx, &ec2.DescribeImagesInput{
|
||||
ImageIds: []string{amiID},
|
||||
}, maxWait)
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for image: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) tagImageAndSnapshot(ctx context.Context, imageName, amiID, region string) error {
|
||||
u.log.Debugf("Tagging backing snapshot of image %s in %s", amiID, region)
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
snapshotID, err := getBackingSnapshotID(ctx, ec2C, amiID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting backing snapshot ID: %w", err)
|
||||
}
|
||||
_, err = ec2C.CreateTags(ctx, &ec2.CreateTagsInput{
|
||||
Resources: []string{amiID, snapshotID},
|
||||
Tags: []ec2types.Tag{
|
||||
{
|
||||
Key: toPtr("Name"),
|
||||
Value: toPtr(imageName),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("tagging ami and snapshot: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) publishImage(ctx context.Context, imageName, region string) error {
|
||||
u.log.Debugf("Publishing image %s in %s", imageName, region)
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
_, err = ec2C.ModifyImageAttribute(ctx, &ec2.ModifyImageAttributeInput{
|
||||
ImageId: &imageName,
|
||||
LaunchPermission: &ec2types.LaunchPermissionModifications{
|
||||
Add: []ec2types.LaunchPermission{
|
||||
{
|
||||
Group: ec2types.PermissionGroupAll,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("publishing image: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureImageDeleted(ctx context.Context, imageName, region string) error {
|
||||
ec2C, err := u.ec2(ctx, region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating ec2 client: %w", err)
|
||||
}
|
||||
amiID, err := u.findImage(ctx, imageName, region)
|
||||
if err == errAMIDoesNotExist {
|
||||
u.log.Debugf("Image %s in %s doesn't exist. Nothing to clean up.", imageName, region)
|
||||
return nil
|
||||
}
|
||||
snapshotID, err := getBackingSnapshotID(ctx, ec2C, amiID)
|
||||
if err == errAMIDoesNotExist {
|
||||
u.log.Debugf("Image %s doesn't exist. Nothing to clean up.", amiID)
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Deleting image %s in %s with backing snapshot", amiID, region)
|
||||
_, err = ec2C.DeregisterImage(ctx, &ec2.DeregisterImageInput{
|
||||
ImageId: &amiID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting image: %w", err)
|
||||
}
|
||||
_, err = ec2C.DeleteSnapshot(ctx, &ec2.DeleteSnapshotInput{
|
||||
SnapshotId: &snapshotID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting snapshot: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func imageName(version versionsapi.Version, timestamp time.Time) string {
|
||||
if version.Stream == "stable" {
|
||||
return fmt.Sprintf("constellation-%s", version.Version)
|
||||
}
|
||||
return fmt.Sprintf("constellation-%s-%s-%s", version.Stream, version.Version, timestamp.Format(timestampFormat))
|
||||
}
|
||||
|
||||
func waitForSnapshotImport(ctx context.Context, ec2C ec2API, importTaskID string) (string, error) {
|
||||
for {
|
||||
taskResp, err := ec2C.DescribeImportSnapshotTasks(ctx, &ec2.DescribeImportSnapshotTasksInput{
|
||||
ImportTaskIds: []string{importTaskID},
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("describing import snapshot task: %w", err)
|
||||
}
|
||||
if len(taskResp.ImportSnapshotTasks) == 0 {
|
||||
return "", fmt.Errorf("describing import snapshot task: no tasks returned")
|
||||
}
|
||||
if taskResp.ImportSnapshotTasks[0].SnapshotTaskDetail == nil {
|
||||
return "", fmt.Errorf("describing import snapshot task: no snapshot task detail returned")
|
||||
}
|
||||
if taskResp.ImportSnapshotTasks[0].SnapshotTaskDetail.Status == nil {
|
||||
return "", fmt.Errorf("describing import snapshot task: no status returned")
|
||||
}
|
||||
switch *taskResp.ImportSnapshotTasks[0].SnapshotTaskDetail.Status {
|
||||
case string(ec2types.SnapshotStateCompleted):
|
||||
return *taskResp.ImportSnapshotTasks[0].SnapshotTaskDetail.SnapshotId, nil
|
||||
case string(ec2types.SnapshotStateError):
|
||||
return "", fmt.Errorf("importing snapshot: task failed")
|
||||
}
|
||||
time.Sleep(waitInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func getBackingSnapshotID(ctx context.Context, ec2C ec2API, amiID string) (string, error) {
|
||||
describeResp, err := ec2C.DescribeImages(ctx, &ec2.DescribeImagesInput{
|
||||
ImageIds: []string{amiID},
|
||||
})
|
||||
if err != nil || len(describeResp.Images) == 0 {
|
||||
return "", errAMIDoesNotExist
|
||||
}
|
||||
if len(describeResp.Images) != 1 {
|
||||
return "", fmt.Errorf("describing image: expected 1 image, got %d", len(describeResp.Images))
|
||||
}
|
||||
image := describeResp.Images[0]
|
||||
if len(image.BlockDeviceMappings) != 1 {
|
||||
return "", fmt.Errorf("found %d block device mappings for image %s, expected 1", len(image.BlockDeviceMappings), amiID)
|
||||
}
|
||||
if image.BlockDeviceMappings[0].Ebs == nil {
|
||||
return "", fmt.Errorf("image %s does not have an EBS block device mapping", amiID)
|
||||
}
|
||||
ebs := image.BlockDeviceMappings[0].Ebs
|
||||
if ebs.SnapshotId == nil {
|
||||
return "", fmt.Errorf("image %s does not have an EBS snapshot", amiID)
|
||||
}
|
||||
return *ebs.SnapshotId, nil
|
||||
}
|
||||
|
||||
type ec2API interface {
|
||||
DescribeImages(ctx context.Context, params *ec2.DescribeImagesInput,
|
||||
optFns ...func(*ec2.Options),
|
||||
) (*ec2.DescribeImagesOutput, error)
|
||||
ModifyImageAttribute(ctx context.Context, params *ec2.ModifyImageAttributeInput,
|
||||
optFns ...func(*ec2.Options),
|
||||
) (*ec2.ModifyImageAttributeOutput, error)
|
||||
RegisterImage(ctx context.Context, params *ec2.RegisterImageInput,
|
||||
optFns ...func(*ec2.Options),
|
||||
) (*ec2.RegisterImageOutput, error)
|
||||
CopyImage(ctx context.Context, params *ec2.CopyImageInput, optFns ...func(*ec2.Options),
|
||||
) (*ec2.CopyImageOutput, error)
|
||||
DeregisterImage(ctx context.Context, params *ec2.DeregisterImageInput,
|
||||
optFns ...func(*ec2.Options),
|
||||
) (*ec2.DeregisterImageOutput, error)
|
||||
ImportSnapshot(ctx context.Context, params *ec2.ImportSnapshotInput,
|
||||
optFns ...func(*ec2.Options),
|
||||
) (*ec2.ImportSnapshotOutput, error)
|
||||
DescribeImportSnapshotTasks(ctx context.Context, params *ec2.DescribeImportSnapshotTasksInput,
|
||||
optFns ...func(*ec2.Options),
|
||||
) (*ec2.DescribeImportSnapshotTasksOutput, error)
|
||||
DescribeSnapshots(ctx context.Context, params *ec2.DescribeSnapshotsInput,
|
||||
optFns ...func(*ec2.Options),
|
||||
) (*ec2.DescribeSnapshotsOutput, error)
|
||||
DeleteSnapshot(ctx context.Context, params *ec2.DeleteSnapshotInput, optFns ...func(*ec2.Options),
|
||||
) (*ec2.DeleteSnapshotOutput, error)
|
||||
CreateTags(ctx context.Context, params *ec2.CreateTagsInput, optFns ...func(*ec2.Options),
|
||||
) (*ec2.CreateTagsOutput, error)
|
||||
}
|
||||
|
||||
type s3API interface {
|
||||
HeadBucket(ctx context.Context, params *s3.HeadBucketInput, optFns ...func(*s3.Options),
|
||||
) (*s3.HeadBucketOutput, error)
|
||||
CreateBucket(ctx context.Context, params *s3.CreateBucketInput, optFns ...func(*s3.Options),
|
||||
) (*s3.CreateBucketOutput, error)
|
||||
HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options),
|
||||
) (*s3.HeadObjectOutput, error)
|
||||
DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options),
|
||||
) (*s3.DeleteObjectOutput, error)
|
||||
}
|
||||
|
||||
type s3UploaderAPI interface {
|
||||
Upload(ctx context.Context, input *s3.PutObjectInput, opts ...func(*s3manager.Uploader),
|
||||
) (*s3manager.UploadOutput, error)
|
||||
}
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
const (
|
||||
waitInterval = 15 * time.Second
|
||||
maxWait = 15 * time.Minute
|
||||
timestampFormat = "20060102150405"
|
||||
)
|
||||
|
||||
var (
|
||||
errAMIDoesNotExist = errors.New("ami does not exist")
|
||||
replicationRegions = []string{"us-east-2", "ap-south-1"}
|
||||
)
|
21
internal/osimage/azure/BUILD.bazel
Normal file
21
internal/osimage/azure/BUILD.bazel
Normal file
@ -0,0 +1,21 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "azure",
|
||||
srcs = [
|
||||
"azureupload.go",
|
||||
"disktype_string.go",
|
||||
],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/azure",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/logger",
|
||||
"//internal/osimage",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_azure_azure_sdk_for_go_sdk_azcore//runtime",
|
||||
"@com_github_azure_azure_sdk_for_go_sdk_azidentity//:azidentity",
|
||||
"@com_github_azure_azure_sdk_for_go_sdk_resourcemanager_compute_armcompute_v4//:armcompute",
|
||||
"@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//blob",
|
||||
"@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//pageblob",
|
||||
],
|
||||
)
|
701
internal/osimage/azure/azureupload.go
Normal file
701
internal/osimage/azure/azureupload.go
Normal file
@ -0,0 +1,701 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package azure implements uploading os images to azure.
|
||||
package azure
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
armcomputev4 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/pageblob"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
// Uploader can upload and remove os images on Azure.
|
||||
type Uploader struct {
|
||||
subscription string
|
||||
location string
|
||||
resourceGroup string
|
||||
pollingFrequency time.Duration
|
||||
disks azureDiskAPI
|
||||
managedImages azureManagedImageAPI
|
||||
blob sasBlobUploader
|
||||
galleries azureGalleriesAPI
|
||||
image azureGalleriesImageAPI
|
||||
imageVersions azureGalleriesImageVersionAPI
|
||||
communityVersions azureCommunityGalleryImageVersionAPI
|
||||
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
// New creates a new Uploader.
|
||||
func New(subscription, location, resourceGroup string, log *logger.Logger) (*Uploader, error) {
|
||||
cred, err := azidentity.NewDefaultAzureCredential(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diskClient, err := armcomputev4.NewDisksClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
managedImagesClient, err := armcomputev4.NewImagesClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
galleriesClient, err := armcomputev4.NewGalleriesClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
galleriesImageClient, err := armcomputev4.NewGalleryImagesClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
galleriesImageVersionClient, err := armcomputev4.NewGalleryImageVersionsClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
communityImageVersionClient, err := armcomputev4.NewCommunityGalleryImageVersionsClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Uploader{
|
||||
subscription: subscription,
|
||||
location: location,
|
||||
resourceGroup: resourceGroup,
|
||||
pollingFrequency: pollingFrequency,
|
||||
disks: diskClient,
|
||||
managedImages: managedImagesClient,
|
||||
blob: func(sasBlobURL string) (azurePageblobAPI, error) {
|
||||
return pageblob.NewClientWithNoCredential(sasBlobURL, nil)
|
||||
},
|
||||
galleries: galleriesClient,
|
||||
image: galleriesImageClient,
|
||||
imageVersions: galleriesImageVersionClient,
|
||||
communityVersions: communityImageVersionClient,
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Upload uploads an OS image to Azure.
|
||||
func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error) {
|
||||
formattedTime := req.Timestamp.Format(timestampFormat)
|
||||
diskName := fmt.Sprintf("constellation-%s-%s-%s", req.Version.Stream, formattedTime, req.Variant)
|
||||
var sigName string
|
||||
switch req.Version.Stream {
|
||||
case "stable":
|
||||
sigName = sigNameStable
|
||||
case "debug":
|
||||
sigName = sigNameDebug
|
||||
default:
|
||||
sigName = sigNameDefault
|
||||
}
|
||||
definitionName := imageOffer(req.Version)
|
||||
versionName, err := imageVersion(req.Version, req.Timestamp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("determining image version name: %w", err)
|
||||
}
|
||||
|
||||
// ensure new image can be uploaded by deleting existing resources using the same name
|
||||
if err := u.ensureImageVersionDeleted(ctx, sigName, definitionName, versionName); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no image version using the same name exists: %w", err)
|
||||
}
|
||||
if err := u.ensureManagedImageDeleted(ctx, diskName); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no managed image using the same name exists: %w", err)
|
||||
}
|
||||
if err := u.ensureDiskDeleted(ctx, diskName); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no temporary disk using the same name exists: %w", err)
|
||||
}
|
||||
|
||||
diskID, err := u.createDisk(ctx, diskName, DiskTypeNormal, req.Image, nil, req.Size)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating disk: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
// cleanup temp disk
|
||||
err := u.ensureDiskDeleted(ctx, diskName)
|
||||
if err != nil {
|
||||
u.log.Errorf("post-cleaning: deleting disk image: %v", err)
|
||||
}
|
||||
}()
|
||||
managedImageID, err := u.createManagedImage(ctx, diskName, diskID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating managed image: %w", err)
|
||||
}
|
||||
if err := u.ensureSIG(ctx, sigName); err != nil {
|
||||
return nil, fmt.Errorf("ensuring sig exists: %w", err)
|
||||
}
|
||||
if err := u.ensureImageDefinition(ctx, sigName, definitionName, req.Version, req.Variant); err != nil {
|
||||
return nil, fmt.Errorf("ensuring image definition exists: %w", err)
|
||||
}
|
||||
|
||||
unsharedImageVersionID, err := u.createImageVersion(ctx, sigName, definitionName, versionName, managedImageID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating image version: %w", err)
|
||||
}
|
||||
|
||||
imageReference, err := u.getImageReference(ctx, sigName, definitionName, versionName, unsharedImageVersionID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting image reference: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
req.Variant: imageReference,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createDisk creates and initializes (uploads contents of) an azure disk.
|
||||
func (u *Uploader) createDisk(ctx context.Context, diskName string, diskType DiskType, img io.ReadSeeker, vmgs io.ReadSeeker, size int64) (string, error) {
|
||||
u.log.Debugf("Creating disk %s in %s", diskName, u.resourceGroup)
|
||||
if diskType == DiskTypeWithVMGS && vmgs == nil {
|
||||
return "", errors.New("cannot create disk with vmgs: vmgs reader is nil")
|
||||
}
|
||||
var createOption armcomputev4.DiskCreateOption
|
||||
var requestVMGSSAS bool
|
||||
switch diskType {
|
||||
case DiskTypeNormal:
|
||||
createOption = armcomputev4.DiskCreateOptionUpload
|
||||
case DiskTypeWithVMGS:
|
||||
createOption = armcomputev4.DiskCreateOptionUploadPreparedSecure
|
||||
requestVMGSSAS = true
|
||||
}
|
||||
disk := armcomputev4.Disk{
|
||||
Location: &u.location,
|
||||
Properties: &armcomputev4.DiskProperties{
|
||||
CreationData: &armcomputev4.CreationData{
|
||||
CreateOption: &createOption,
|
||||
UploadSizeBytes: toPtr(size),
|
||||
},
|
||||
HyperVGeneration: toPtr(armcomputev4.HyperVGenerationV2),
|
||||
OSType: toPtr(armcomputev4.OperatingSystemTypesLinux),
|
||||
},
|
||||
}
|
||||
createPoller, err := u.disks.BeginCreateOrUpdate(ctx, u.resourceGroup, diskName, disk, &armcomputev4.DisksClientBeginCreateOrUpdateOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating disk: %w", err)
|
||||
}
|
||||
createdDisk, err := createPoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("waiting for disk to be created: %w", err)
|
||||
}
|
||||
|
||||
u.log.Debugf("Granting temporary upload permissions via SAS token")
|
||||
accessGrant := armcomputev4.GrantAccessData{
|
||||
Access: toPtr(armcomputev4.AccessLevelWrite),
|
||||
DurationInSeconds: toPtr(int32(uploadAccessDuration)),
|
||||
GetSecureVMGuestStateSAS: &requestVMGSSAS,
|
||||
}
|
||||
accessPoller, err := u.disks.BeginGrantAccess(ctx, u.resourceGroup, diskName, accessGrant, &armcomputev4.DisksClientBeginGrantAccessOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generating disk sas token: %w", err)
|
||||
}
|
||||
accesPollerResp, err := accessPoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("waiting for sas token: %w", err)
|
||||
}
|
||||
|
||||
if requestVMGSSAS {
|
||||
u.log.Debugf("Uploading vmgs")
|
||||
vmgsSize, err := vmgs.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := vmgs.Seek(0, io.SeekStart); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if accesPollerResp.SecurityDataAccessSAS == nil {
|
||||
return "", errors.New("uploading vmgs: grant access returned no vmgs sas")
|
||||
}
|
||||
if err := uploadBlob(ctx, *accesPollerResp.SecurityDataAccessSAS, vmgs, vmgsSize, u.blob); err != nil {
|
||||
return "", fmt.Errorf("uploading vmgs: %w", err)
|
||||
}
|
||||
}
|
||||
u.log.Debugf("Uploading os image")
|
||||
if accesPollerResp.AccessSAS == nil {
|
||||
return "", errors.New("uploading disk: grant access returned no disk sas")
|
||||
}
|
||||
if err := uploadBlob(ctx, *accesPollerResp.AccessSAS, img, size, u.blob); err != nil {
|
||||
return "", fmt.Errorf("uploading image: %w", err)
|
||||
}
|
||||
revokePoller, err := u.disks.BeginRevokeAccess(ctx, u.resourceGroup, diskName, &armcomputev4.DisksClientBeginRevokeAccessOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("revoking disk sas token: %w", err)
|
||||
}
|
||||
if _, err := revokePoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency}); err != nil {
|
||||
return "", fmt.Errorf("waiting for sas token revocation: %w", err)
|
||||
}
|
||||
if createdDisk.ID == nil {
|
||||
return "", errors.New("created disk has no id")
|
||||
}
|
||||
return *createdDisk.ID, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureDiskDeleted(ctx context.Context, diskName string) error {
|
||||
_, err := u.disks.Get(ctx, u.resourceGroup, diskName, &armcomputev4.DisksClientGetOptions{})
|
||||
if err != nil {
|
||||
u.log.Debugf("Disk %s in %s doesn't exist. Nothing to clean up.", diskName, u.resourceGroup)
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Deleting disk %s in %s", diskName, u.resourceGroup)
|
||||
deletePoller, err := u.disks.BeginDelete(ctx, u.resourceGroup, diskName, &armcomputev4.DisksClientBeginDeleteOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting disk: %w", err)
|
||||
}
|
||||
if _, err = deletePoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency}); err != nil {
|
||||
return fmt.Errorf("waiting for disk to be deleted: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) createManagedImage(ctx context.Context, imageName string, diskID string) (string, error) {
|
||||
u.log.Debugf("Creating managed image %s in %s", imageName, u.resourceGroup)
|
||||
image := armcomputev4.Image{
|
||||
Location: &u.location,
|
||||
Properties: &armcomputev4.ImageProperties{
|
||||
HyperVGeneration: toPtr(armcomputev4.HyperVGenerationTypesV2),
|
||||
StorageProfile: &armcomputev4.ImageStorageProfile{
|
||||
OSDisk: &armcomputev4.ImageOSDisk{
|
||||
OSState: toPtr(armcomputev4.OperatingSystemStateTypesGeneralized),
|
||||
OSType: toPtr(armcomputev4.OperatingSystemTypesLinux),
|
||||
ManagedDisk: &armcomputev4.SubResource{
|
||||
ID: &diskID,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
createPoller, err := u.managedImages.BeginCreateOrUpdate(
|
||||
ctx, u.resourceGroup, imageName, image,
|
||||
&armcomputev4.ImagesClientBeginCreateOrUpdateOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating managed image: %w", err)
|
||||
}
|
||||
createdImage, err := createPoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("waiting for image to be created: %w", err)
|
||||
}
|
||||
if createdImage.ID == nil {
|
||||
return "", errors.New("created image has no id")
|
||||
}
|
||||
return *createdImage.ID, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureManagedImageDeleted(ctx context.Context, imageName string) error {
|
||||
_, err := u.managedImages.Get(ctx, u.resourceGroup, imageName, &armcomputev4.ImagesClientGetOptions{})
|
||||
if err != nil {
|
||||
u.log.Debugf("Managed image %s in %s doesn't exist. Nothing to clean up.", imageName, u.resourceGroup)
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Deleting managed image %s in %s", imageName, u.resourceGroup)
|
||||
deletePoller, err := u.managedImages.BeginDelete(ctx, u.resourceGroup, imageName, &armcomputev4.ImagesClientBeginDeleteOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting image: %w", err)
|
||||
}
|
||||
if _, err = deletePoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency}); err != nil {
|
||||
return fmt.Errorf("waiting for image to be deleted: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureSIG creates a SIG if it does not exist yet.
|
||||
func (u *Uploader) ensureSIG(ctx context.Context, sigName string) error {
|
||||
_, err := u.galleries.Get(ctx, u.resourceGroup, sigName, &armcomputev4.GalleriesClientGetOptions{})
|
||||
if err == nil {
|
||||
u.log.Debugf("Image gallery %s in %s exists", sigName, u.resourceGroup)
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Creating image gallery %s in %s", sigName, u.resourceGroup)
|
||||
gallery := armcomputev4.Gallery{
|
||||
Location: &u.location,
|
||||
}
|
||||
createPoller, err := u.galleries.BeginCreateOrUpdate(ctx, u.resourceGroup, sigName, gallery,
|
||||
&armcomputev4.GalleriesClientBeginCreateOrUpdateOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating image gallery: %w", err)
|
||||
}
|
||||
if _, err = createPoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency}); err != nil {
|
||||
return fmt.Errorf("waiting for image gallery to be created: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureImageDefinition creates an image definition (component of a SIG) if it does not exist yet.
|
||||
func (u *Uploader) ensureImageDefinition(ctx context.Context, sigName, definitionName string, version versionsapi.Version, variant string) error {
|
||||
_, err := u.image.Get(ctx, u.resourceGroup, sigName, definitionName, &armcomputev4.GalleryImagesClientGetOptions{})
|
||||
if err == nil {
|
||||
u.log.Debugf("Image definition %s/%s in %s exists", sigName, definitionName, u.resourceGroup)
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Creating image definition %s/%s in %s", sigName, definitionName, u.resourceGroup)
|
||||
var securityType string
|
||||
// TODO(malt3): This needs to allow the *Supported or the normal variant
|
||||
// based on wether a VMGS was provided or not.
|
||||
// VMGS provided: ConfidentialVM
|
||||
// No VMGS provided: ConfidentialVMSupported
|
||||
switch strings.ToLower(variant) {
|
||||
case "cvm":
|
||||
securityType = string("ConfidentialVMSupported")
|
||||
case "trustedlaunch":
|
||||
securityType = string(armcomputev4.SecurityTypesTrustedLaunch)
|
||||
}
|
||||
offer := imageOffer(version)
|
||||
|
||||
galleryImage := armcomputev4.GalleryImage{
|
||||
Location: &u.location,
|
||||
Properties: &armcomputev4.GalleryImageProperties{
|
||||
Identifier: &armcomputev4.GalleryImageIdentifier{
|
||||
Offer: &offer,
|
||||
Publisher: toPtr(imageDefinitionPublisher),
|
||||
SKU: toPtr(imageDefinitionSKU),
|
||||
},
|
||||
OSState: toPtr(armcomputev4.OperatingSystemStateTypesGeneralized),
|
||||
OSType: toPtr(armcomputev4.OperatingSystemTypesLinux),
|
||||
Architecture: toPtr(armcomputev4.ArchitectureX64),
|
||||
Features: []*armcomputev4.GalleryImageFeature{
|
||||
{
|
||||
Name: toPtr("SecurityType"),
|
||||
Value: &securityType,
|
||||
},
|
||||
},
|
||||
HyperVGeneration: toPtr(armcomputev4.HyperVGenerationV2),
|
||||
},
|
||||
}
|
||||
createPoller, err := u.image.BeginCreateOrUpdate(ctx, u.resourceGroup, sigName, definitionName, galleryImage,
|
||||
&armcomputev4.GalleryImagesClientBeginCreateOrUpdateOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating image definition: %w", err)
|
||||
}
|
||||
if _, err = createPoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency}); err != nil {
|
||||
return fmt.Errorf("waiting for image definition to be created: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Uploader) createImageVersion(ctx context.Context, sigName, definitionName, versionName, imageID string) (string, error) {
|
||||
u.log.Debugf("Creating image version %s/%s/%s in %s", sigName, definitionName, versionName, u.resourceGroup)
|
||||
imageVersion := armcomputev4.GalleryImageVersion{
|
||||
Location: &u.location,
|
||||
Properties: &armcomputev4.GalleryImageVersionProperties{
|
||||
StorageProfile: &armcomputev4.GalleryImageVersionStorageProfile{
|
||||
OSDiskImage: &armcomputev4.GalleryOSDiskImage{
|
||||
HostCaching: toPtr(armcomputev4.HostCachingReadOnly),
|
||||
},
|
||||
Source: &armcomputev4.GalleryArtifactVersionFullSource{
|
||||
ID: &imageID,
|
||||
},
|
||||
},
|
||||
PublishingProfile: &armcomputev4.GalleryImageVersionPublishingProfile{
|
||||
ReplicaCount: toPtr[int32](1),
|
||||
ReplicationMode: toPtr(armcomputev4.ReplicationModeFull),
|
||||
TargetRegions: targetRegions,
|
||||
},
|
||||
},
|
||||
}
|
||||
createPoller, err := u.imageVersions.BeginCreateOrUpdate(ctx, u.resourceGroup, sigName, definitionName, versionName, imageVersion,
|
||||
&armcomputev4.GalleryImageVersionsClientBeginCreateOrUpdateOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating image version: %w", err)
|
||||
}
|
||||
createdImage, err := createPoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("waiting for image version to be created: %w", err)
|
||||
}
|
||||
if createdImage.ID == nil {
|
||||
return "", errors.New("created image has no id")
|
||||
}
|
||||
return *createdImage.ID, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureImageVersionDeleted(ctx context.Context, sigName, definitionName, versionName string) error {
|
||||
_, err := u.imageVersions.Get(ctx, u.resourceGroup, sigName, definitionName, versionName, &armcomputev4.GalleryImageVersionsClientGetOptions{})
|
||||
if err != nil {
|
||||
u.log.Debugf("Image version %s in %s/%s/%s doesn't exist. Nothing to clean up.", versionName, u.resourceGroup, sigName, definitionName)
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Deleting image version %s in %s/%s/%s", versionName, u.resourceGroup, sigName, definitionName)
|
||||
deletePoller, err := u.imageVersions.BeginDelete(ctx, u.resourceGroup, sigName, definitionName, versionName, &armcomputev4.GalleryImageVersionsClientBeginDeleteOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting image version: %w", err)
|
||||
}
|
||||
if _, err = deletePoller.PollUntilDone(ctx, &runtime.PollUntilDoneOptions{Frequency: u.pollingFrequency}); err != nil {
|
||||
return fmt.Errorf("waiting for image version to be deleted: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getImageReference returns the image reference to use for the image version.
|
||||
// If the shared image gallery is a community gallery, the community identifier is returned.
|
||||
// Otherwise, the unshared identifier is returned.
|
||||
func (u *Uploader) getImageReference(ctx context.Context, sigName, definitionName, versionName, unsharedID string) (string, error) {
|
||||
galleryResp, err := u.galleries.Get(ctx, u.resourceGroup, sigName, &armcomputev4.GalleriesClientGetOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting image gallery %s: %w", sigName, err)
|
||||
}
|
||||
if galleryResp.Properties == nil ||
|
||||
galleryResp.Properties.SharingProfile == nil ||
|
||||
galleryResp.Properties.SharingProfile.CommunityGalleryInfo == nil ||
|
||||
galleryResp.Properties.SharingProfile.CommunityGalleryInfo.CommunityGalleryEnabled == nil ||
|
||||
!*galleryResp.Properties.SharingProfile.CommunityGalleryInfo.CommunityGalleryEnabled {
|
||||
u.log.Warnf("Image gallery %s in %s is not shared. Using private identifier", sigName, u.resourceGroup)
|
||||
return unsharedID, nil
|
||||
}
|
||||
if galleryResp.Properties == nil ||
|
||||
galleryResp.Properties.SharingProfile == nil ||
|
||||
galleryResp.Properties.SharingProfile.CommunityGalleryInfo == nil ||
|
||||
galleryResp.Properties.SharingProfile.CommunityGalleryInfo.PublicNames == nil ||
|
||||
len(galleryResp.Properties.SharingProfile.CommunityGalleryInfo.PublicNames) < 1 ||
|
||||
galleryResp.Properties.SharingProfile.CommunityGalleryInfo.PublicNames[0] == nil {
|
||||
return "", fmt.Errorf("image gallery %s in %s is a community gallery but has no public names", sigName, u.resourceGroup)
|
||||
}
|
||||
communityGalleryName := *galleryResp.Properties.SharingProfile.CommunityGalleryInfo.PublicNames[0]
|
||||
u.log.Debugf("Image gallery %s in %s is shared. Using community identifier in %s", sigName, u.resourceGroup, communityGalleryName)
|
||||
communityVersionResp, err := u.communityVersions.Get(ctx, u.location, communityGalleryName,
|
||||
definitionName, versionName,
|
||||
&armcomputev4.CommunityGalleryImageVersionsClientGetOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting community image version %s/%s/%s: %w", communityGalleryName, definitionName, versionName, err)
|
||||
}
|
||||
if communityVersionResp.Identifier == nil || communityVersionResp.Identifier.UniqueID == nil {
|
||||
return "", fmt.Errorf("community image version %s/%s/%s has no id", communityGalleryName, definitionName, versionName)
|
||||
}
|
||||
return *communityVersionResp.Identifier.UniqueID, nil
|
||||
}
|
||||
|
||||
func uploadBlob(ctx context.Context, sasURL string, disk io.ReadSeeker, size int64, uploader sasBlobUploader) error {
|
||||
uploadClient, err := uploader(sasURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading blob: %w", err)
|
||||
}
|
||||
var offset int64
|
||||
var chunksize int
|
||||
chunk := make([]byte, pageSizeMax)
|
||||
var readErr error
|
||||
for offset < size {
|
||||
chunksize, readErr = io.ReadAtLeast(disk, chunk, 1)
|
||||
if readErr != nil {
|
||||
return fmt.Errorf("reading from disk: %w", err)
|
||||
}
|
||||
if err := uploadChunk(ctx, uploadClient, bytes.NewReader(chunk[:chunksize]), offset, int64(chunksize)); err != nil {
|
||||
return fmt.Errorf("uploading chunk: %w", err)
|
||||
}
|
||||
offset += int64(chunksize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func uploadChunk(ctx context.Context, uploader azurePageblobAPI, chunk io.ReadSeeker, offset, chunksize int64) error {
|
||||
_, err := uploader.UploadPages(ctx, &readSeekNopCloser{chunk}, blob.HTTPRange{
|
||||
Offset: offset,
|
||||
Count: chunksize,
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func imageOffer(version versionsapi.Version) string {
|
||||
switch {
|
||||
case version.Stream == "stable":
|
||||
return "constellation"
|
||||
case version.Stream == "debug" && version.Ref == "-":
|
||||
return version.Version
|
||||
}
|
||||
return version.Ref + "-" + version.Stream
|
||||
}
|
||||
|
||||
// imageVersion determines the semantic version string used inside a sig image.
|
||||
// For releases, the actual semantic version of the image (without leading v) is used (major.minor.patch).
|
||||
// Otherwise, the version is derived from the commit timestamp.
|
||||
func imageVersion(version versionsapi.Version, timestamp time.Time) (string, error) {
|
||||
switch {
|
||||
case version.Stream == "stable":
|
||||
fallthrough
|
||||
case version.Stream == "debug" && version.Ref == "-":
|
||||
return strings.TrimLeft(version.Version, "v"), nil
|
||||
}
|
||||
|
||||
formattedTime := timestamp.Format(timestampFormat)
|
||||
if len(formattedTime) != len(timestampFormat) {
|
||||
return "", errors.New("invalid timestamp")
|
||||
}
|
||||
// <year>.<month><day>.<time>
|
||||
return formattedTime[:4] + "." + formattedTime[4:8] + "." + formattedTime[8:], nil
|
||||
}
|
||||
|
||||
type sasBlobUploader func(sasBlobURL string) (azurePageblobAPI, error)
|
||||
|
||||
type azureDiskAPI interface {
|
||||
Get(ctx context.Context, resourceGroupName string, diskName string,
|
||||
options *armcomputev4.DisksClientGetOptions,
|
||||
) (armcomputev4.DisksClientGetResponse, error)
|
||||
BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, diskName string, disk armcomputev4.Disk,
|
||||
options *armcomputev4.DisksClientBeginCreateOrUpdateOptions,
|
||||
) (*runtime.Poller[armcomputev4.DisksClientCreateOrUpdateResponse], error)
|
||||
BeginDelete(ctx context.Context, resourceGroupName string, diskName string,
|
||||
options *armcomputev4.DisksClientBeginDeleteOptions,
|
||||
) (*runtime.Poller[armcomputev4.DisksClientDeleteResponse], error)
|
||||
BeginGrantAccess(ctx context.Context, resourceGroupName string, diskName string, grantAccessData armcomputev4.GrantAccessData,
|
||||
options *armcomputev4.DisksClientBeginGrantAccessOptions,
|
||||
) (*runtime.Poller[armcomputev4.DisksClientGrantAccessResponse], error)
|
||||
BeginRevokeAccess(ctx context.Context, resourceGroupName string, diskName string,
|
||||
options *armcomputev4.DisksClientBeginRevokeAccessOptions,
|
||||
) (*runtime.Poller[armcomputev4.DisksClientRevokeAccessResponse], error)
|
||||
}
|
||||
|
||||
type azureManagedImageAPI interface {
|
||||
Get(ctx context.Context, resourceGroupName string, imageName string,
|
||||
options *armcomputev4.ImagesClientGetOptions,
|
||||
) (armcomputev4.ImagesClientGetResponse, error)
|
||||
BeginCreateOrUpdate(ctx context.Context, resourceGroupName string,
|
||||
imageName string, parameters armcomputev4.Image,
|
||||
options *armcomputev4.ImagesClientBeginCreateOrUpdateOptions,
|
||||
) (*runtime.Poller[armcomputev4.ImagesClientCreateOrUpdateResponse], error)
|
||||
BeginDelete(ctx context.Context, resourceGroupName string, imageName string,
|
||||
options *armcomputev4.ImagesClientBeginDeleteOptions,
|
||||
) (*runtime.Poller[armcomputev4.ImagesClientDeleteResponse], error)
|
||||
}
|
||||
|
||||
type azurePageblobAPI interface {
|
||||
UploadPages(ctx context.Context, body io.ReadSeekCloser, contentRange blob.HTTPRange,
|
||||
options *pageblob.UploadPagesOptions,
|
||||
) (pageblob.UploadPagesResponse, error)
|
||||
}
|
||||
|
||||
type azureGalleriesAPI interface {
|
||||
Get(ctx context.Context, resourceGroupName string, galleryName string,
|
||||
options *armcomputev4.GalleriesClientGetOptions,
|
||||
) (armcomputev4.GalleriesClientGetResponse, error)
|
||||
NewListPager(options *armcomputev4.GalleriesClientListOptions,
|
||||
) *runtime.Pager[armcomputev4.GalleriesClientListResponse]
|
||||
BeginCreateOrUpdate(ctx context.Context, resourceGroupName string,
|
||||
galleryName string, gallery armcomputev4.Gallery,
|
||||
options *armcomputev4.GalleriesClientBeginCreateOrUpdateOptions,
|
||||
) (*runtime.Poller[armcomputev4.GalleriesClientCreateOrUpdateResponse], error)
|
||||
}
|
||||
|
||||
type azureGalleriesImageAPI interface {
|
||||
Get(ctx context.Context, resourceGroupName string, galleryName string,
|
||||
galleryImageName string, options *armcomputev4.GalleryImagesClientGetOptions,
|
||||
) (armcomputev4.GalleryImagesClientGetResponse, error)
|
||||
BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, galleryName string,
|
||||
galleryImageName string, galleryImage armcomputev4.GalleryImage,
|
||||
options *armcomputev4.GalleryImagesClientBeginCreateOrUpdateOptions,
|
||||
) (*runtime.Poller[armcomputev4.GalleryImagesClientCreateOrUpdateResponse], error)
|
||||
BeginDelete(ctx context.Context, resourceGroupName string, galleryName string, galleryImageName string,
|
||||
options *armcomputev4.GalleryImagesClientBeginDeleteOptions,
|
||||
) (*runtime.Poller[armcomputev4.GalleryImagesClientDeleteResponse], error)
|
||||
}
|
||||
|
||||
type azureGalleriesImageVersionAPI interface {
|
||||
Get(ctx context.Context, resourceGroupName string, galleryName string, galleryImageName string, galleryImageVersionName string,
|
||||
options *armcomputev4.GalleryImageVersionsClientGetOptions,
|
||||
) (armcomputev4.GalleryImageVersionsClientGetResponse, error)
|
||||
NewListByGalleryImagePager(resourceGroupName string, galleryName string, galleryImageName string,
|
||||
options *armcomputev4.GalleryImageVersionsClientListByGalleryImageOptions,
|
||||
) *runtime.Pager[armcomputev4.GalleryImageVersionsClientListByGalleryImageResponse]
|
||||
BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, galleryName string, galleryImageName string,
|
||||
galleryImageVersionName string, galleryImageVersion armcomputev4.GalleryImageVersion,
|
||||
options *armcomputev4.GalleryImageVersionsClientBeginCreateOrUpdateOptions,
|
||||
) (*runtime.Poller[armcomputev4.GalleryImageVersionsClientCreateOrUpdateResponse], error)
|
||||
BeginDelete(ctx context.Context, resourceGroupName string, galleryName string, galleryImageName string,
|
||||
galleryImageVersionName string, options *armcomputev4.GalleryImageVersionsClientBeginDeleteOptions,
|
||||
) (*runtime.Poller[armcomputev4.GalleryImageVersionsClientDeleteResponse], error)
|
||||
}
|
||||
|
||||
type azureCommunityGalleryImageVersionAPI interface {
|
||||
Get(ctx context.Context, location string,
|
||||
publicGalleryName, galleryImageName, galleryImageVersionName string,
|
||||
options *armcomputev4.CommunityGalleryImageVersionsClientGetOptions,
|
||||
) (armcomputev4.CommunityGalleryImageVersionsClientGetResponse, error)
|
||||
}
|
||||
|
||||
const (
|
||||
pollingFrequency = 10 * time.Second
|
||||
// uploadAccessDuration is the time in seconds that
|
||||
// sas tokens should be valid for (24 hours).
|
||||
uploadAccessDuration = 86400 // 24 hours
|
||||
pageSizeMax = 4194304 // 4MiB
|
||||
pageSizeMin = 512 // 512 bytes
|
||||
sigNameStable = "Constellation_CVM"
|
||||
sigNameDebug = "Constellation_Debug_CVM"
|
||||
sigNameDefault = "Constellation_Testing_CVM"
|
||||
imageDefinitionPublisher = "edgelesssys"
|
||||
imageDefinitionSKU = "constellation"
|
||||
timestampFormat = "20060102150405"
|
||||
)
|
||||
|
||||
var targetRegions = []*armcomputev4.TargetRegion{
|
||||
{
|
||||
Name: toPtr("northeurope"),
|
||||
RegionalReplicaCount: toPtr[int32](1),
|
||||
},
|
||||
{
|
||||
Name: toPtr("eastus"),
|
||||
RegionalReplicaCount: toPtr[int32](1),
|
||||
},
|
||||
{
|
||||
Name: toPtr("westeurope"),
|
||||
RegionalReplicaCount: toPtr[int32](1),
|
||||
},
|
||||
{
|
||||
Name: toPtr("westus"),
|
||||
RegionalReplicaCount: toPtr[int32](1),
|
||||
},
|
||||
}
|
||||
|
||||
//go:generate stringer -type=DiskType -trimprefix=DiskType
|
||||
|
||||
// DiskType is the kind of disk created using the Azure API.
|
||||
type DiskType uint32
|
||||
|
||||
// FromString converts a string into an DiskType.
|
||||
func FromString(s string) DiskType {
|
||||
switch strings.ToLower(s) {
|
||||
case strings.ToLower(DiskTypeNormal.String()):
|
||||
return DiskTypeNormal
|
||||
case strings.ToLower(DiskTypeWithVMGS.String()):
|
||||
return DiskTypeWithVMGS
|
||||
default:
|
||||
return DiskTypeUnknown
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// DiskTypeUnknown is default value for DiskType.
|
||||
DiskTypeUnknown DiskType = iota
|
||||
// DiskTypeNormal creates a normal Azure disk (single block device).
|
||||
DiskTypeNormal
|
||||
// DiskTypeWithVMGS creates a disk with VMGS (also called secure disk)
|
||||
// that has an additional block device for the VMGS disk.
|
||||
DiskTypeWithVMGS
|
||||
)
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
type readSeekNopCloser struct {
|
||||
io.ReadSeeker
|
||||
}
|
||||
|
||||
func (n *readSeekNopCloser) Close() error {
|
||||
return nil
|
||||
}
|
25
internal/osimage/azure/disktype_string.go
Normal file
25
internal/osimage/azure/disktype_string.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Code generated by "stringer -type=DiskType -trimprefix=DiskType"; DO NOT EDIT.
|
||||
|
||||
package azure
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[DiskTypeUnknown-0]
|
||||
_ = x[DiskTypeNormal-1]
|
||||
_ = x[DiskTypeWithVMGS-2]
|
||||
}
|
||||
|
||||
const _DiskType_name = "UnknownNormalWithVMGS"
|
||||
|
||||
var _DiskType_index = [...]uint8{0, 7, 13, 21}
|
||||
|
||||
func (i DiskType) String() string {
|
||||
if i >= DiskType(len(_DiskType_index)-1) {
|
||||
return "DiskType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _DiskType_name[_DiskType_index[i]:_DiskType_index[i+1]]
|
||||
}
|
18
internal/osimage/gcp/BUILD.bazel
Normal file
18
internal/osimage/gcp/BUILD.bazel
Normal file
@ -0,0 +1,18 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "gcp",
|
||||
srcs = ["gcpupload.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/gcp",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/logger",
|
||||
"//internal/osimage",
|
||||
"//internal/osimage/secureboot",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_googleapis_gax_go_v2//:gax-go",
|
||||
"@com_google_cloud_go_compute//apiv1",
|
||||
"@com_google_cloud_go_compute//apiv1/computepb",
|
||||
"@com_google_cloud_go_storage//:storage",
|
||||
],
|
||||
)
|
289
internal/osimage/gcp/gcpupload.go
Normal file
289
internal/osimage/gcp/gcpupload.go
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package gcp implements uploading os images to gcp.
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
"cloud.google.com/go/compute/apiv1/computepb"
|
||||
"cloud.google.com/go/storage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/secureboot"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
gaxv2 "github.com/googleapis/gax-go/v2"
|
||||
)
|
||||
|
||||
// Uploader can upload and remove os images on GCP.
|
||||
type Uploader struct {
|
||||
project string
|
||||
location string
|
||||
bucketName string
|
||||
image imagesAPI
|
||||
bucket bucketAPI
|
||||
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
// New creates a new Uploader.
|
||||
func New(ctx context.Context, project, location, bucketName string, log *logger.Logger) (*Uploader, error) {
|
||||
image, err := compute.NewImagesRESTClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storage, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bucket := storage.Bucket(bucketName)
|
||||
|
||||
return &Uploader{
|
||||
project: project,
|
||||
location: location,
|
||||
bucketName: bucketName,
|
||||
image: image,
|
||||
bucket: bucket,
|
||||
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Upload uploads an OS image to GCP.
|
||||
func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error) {
|
||||
imageName := u.imageName(req.Version, req.Variant)
|
||||
blobName := imageName + ".tar.gz"
|
||||
if err := u.ensureBucket(ctx); err != nil {
|
||||
return nil, fmt.Errorf("setup: ensuring bucket exists: %w", err)
|
||||
}
|
||||
if err := u.ensureImageDeleted(ctx, imageName); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no image using the same name exists: %w", err)
|
||||
}
|
||||
if err := u.ensureBlobDeleted(ctx, blobName); err != nil {
|
||||
return nil, fmt.Errorf("pre-cleaning: ensuring no blob using the same name exists: %w", err)
|
||||
}
|
||||
if err := u.uploadBlob(ctx, blobName, req.Image); err != nil {
|
||||
return nil, fmt.Errorf("uploading blob: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
// cleanup temporary blob
|
||||
if err := u.ensureBlobDeleted(ctx, blobName); err != nil {
|
||||
u.log.Errorf("post-cleaning: deleting blob: %v", err)
|
||||
}
|
||||
}()
|
||||
imageRef, err := u.createImage(ctx, req.Version, imageName, blobName, req.SBDatabase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating image: %w", err)
|
||||
}
|
||||
return map[string]string{
|
||||
req.Variant: imageRef,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureBucket(ctx context.Context) error {
|
||||
_, err := u.bucket.Attrs(ctx)
|
||||
if err == nil {
|
||||
u.log.Debugf("Bucket %s exists", u.bucketName)
|
||||
return nil
|
||||
}
|
||||
if err != storage.ErrBucketNotExist {
|
||||
return err
|
||||
}
|
||||
u.log.Debugf("Creating bucket %s", u.bucketName)
|
||||
return u.bucket.Create(ctx, u.project, &storage.BucketAttrs{
|
||||
PublicAccessPrevention: storage.PublicAccessPreventionEnforced,
|
||||
Location: u.location,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *Uploader) uploadBlob(ctx context.Context, blobName string, img io.Reader) error {
|
||||
u.log.Debugf("Uploading os image as %s", blobName)
|
||||
writer := u.bucket.Object(blobName).NewWriter(ctx)
|
||||
_, err := io.Copy(writer, img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writer.Close()
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureBlobDeleted(ctx context.Context, blobName string) error {
|
||||
_, err := u.bucket.Object(blobName).Attrs(ctx)
|
||||
if err == storage.ErrObjectNotExist {
|
||||
u.log.Debugf("Blob %s in %s doesn't exist. Nothing to clean up.", blobName, u.bucketName)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.log.Debugf("Deleting blob %s", blobName)
|
||||
return u.bucket.Object(blobName).Delete(ctx)
|
||||
}
|
||||
|
||||
func (u *Uploader) createImage(ctx context.Context, version versionsapi.Version, imageName, blobName string, sbDatabase secureboot.Database) (string, error) {
|
||||
u.log.Debugf("Creating image %s", imageName)
|
||||
blobURL := u.blobURL(blobName)
|
||||
family := u.imageFamily(version)
|
||||
req := computepb.InsertImageRequest{
|
||||
ImageResource: &computepb.Image{
|
||||
Name: &imageName,
|
||||
RawDisk: &computepb.RawDisk{
|
||||
ContainerType: toPtr("TAR"),
|
||||
Source: &blobURL,
|
||||
},
|
||||
Family: &family,
|
||||
Architecture: toPtr("X86_64"),
|
||||
GuestOsFeatures: []*computepb.GuestOsFeature{
|
||||
{Type: toPtr("GVNIC")},
|
||||
{Type: toPtr("SEV_CAPABLE")},
|
||||
{Type: toPtr("VIRTIO_SCSI_MULTIQUEUE")},
|
||||
{Type: toPtr("UEFI_COMPATIBLE")},
|
||||
},
|
||||
ShieldedInstanceInitialState: &computepb.InitialStateConfig{
|
||||
Pk: pk(&sbDatabase),
|
||||
Keks: keks(&sbDatabase),
|
||||
Dbs: dbs(&sbDatabase),
|
||||
},
|
||||
},
|
||||
Project: u.project,
|
||||
}
|
||||
op, err := u.image.Insert(ctx, &req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating image: %w", err)
|
||||
}
|
||||
if err := op.Wait(ctx); err != nil {
|
||||
return "", fmt.Errorf("waiting for image to be created: %w", err)
|
||||
}
|
||||
policy := &computepb.Policy{
|
||||
Bindings: []*computepb.Binding{
|
||||
{
|
||||
Role: toPtr("roles/compute.imageUser"),
|
||||
Members: []string{"allAuthenticatedUsers"},
|
||||
},
|
||||
},
|
||||
}
|
||||
if _, err = u.image.SetIamPolicy(ctx, &computepb.SetIamPolicyImageRequest{
|
||||
Resource: imageName,
|
||||
Project: u.project,
|
||||
GlobalSetPolicyRequestResource: &computepb.GlobalSetPolicyRequest{
|
||||
Policy: policy,
|
||||
},
|
||||
}); err != nil {
|
||||
return "", fmt.Errorf("setting iam policy: %w", err)
|
||||
}
|
||||
image, err := u.image.Get(ctx, &computepb.GetImageRequest{
|
||||
Image: imageName,
|
||||
Project: u.project,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("created image doesn't exist: %w", err)
|
||||
}
|
||||
return strings.TrimPrefix(image.GetSelfLink(), "https://www.googleapis.com/compute/v1/"), nil
|
||||
}
|
||||
|
||||
func (u *Uploader) ensureImageDeleted(ctx context.Context, imageName string) error {
|
||||
_, err := u.image.Get(ctx, &computepb.GetImageRequest{
|
||||
Image: imageName,
|
||||
Project: u.project,
|
||||
})
|
||||
if err != nil {
|
||||
u.log.Debugf("Image %s doesn't exist. Nothing to clean up.", imageName)
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Deleting image %s", imageName)
|
||||
op, err := u.image.Delete(ctx, &computepb.DeleteImageRequest{
|
||||
Image: imageName,
|
||||
Project: u.project,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return op.Wait(ctx)
|
||||
}
|
||||
|
||||
func (u *Uploader) blobURL(blobName string) string {
|
||||
return (&url.URL{
|
||||
Scheme: "https",
|
||||
Host: "storage.googleapis.com",
|
||||
Path: path.Join(u.bucketName, blobName),
|
||||
}).String()
|
||||
}
|
||||
|
||||
func (u *Uploader) imageName(version versionsapi.Version, variant string) string {
|
||||
return strings.ReplaceAll(version.Version, ".", "-") + "-" + variant
|
||||
}
|
||||
|
||||
func (u *Uploader) imageFamily(version versionsapi.Version) string {
|
||||
if version.Stream == "stable" {
|
||||
return "constellation"
|
||||
}
|
||||
truncatedRef := version.Ref
|
||||
if len(version.Ref) > 45 {
|
||||
truncatedRef = version.Ref[:45]
|
||||
}
|
||||
return "constellation-" + truncatedRef
|
||||
}
|
||||
|
||||
func pk(sbDatabase *secureboot.Database) *computepb.FileContentBuffer {
|
||||
encoded := base64.StdEncoding.EncodeToString(sbDatabase.PK)
|
||||
return &computepb.FileContentBuffer{
|
||||
Content: toPtr(encoded),
|
||||
FileType: toPtr("X509"),
|
||||
}
|
||||
}
|
||||
|
||||
func keks(sbDatabase *secureboot.Database) []*computepb.FileContentBuffer {
|
||||
keks := make([]*computepb.FileContentBuffer, 0, len(sbDatabase.Keks))
|
||||
for _, kek := range sbDatabase.Keks {
|
||||
encoded := base64.StdEncoding.EncodeToString(kek)
|
||||
keks = append(keks, &computepb.FileContentBuffer{
|
||||
Content: toPtr(encoded),
|
||||
FileType: toPtr("X509"),
|
||||
})
|
||||
}
|
||||
return keks
|
||||
}
|
||||
|
||||
func dbs(sbDatabase *secureboot.Database) []*computepb.FileContentBuffer {
|
||||
dbs := make([]*computepb.FileContentBuffer, 0, len(sbDatabase.DBs))
|
||||
for _, db := range sbDatabase.DBs {
|
||||
encoded := base64.StdEncoding.EncodeToString(db)
|
||||
dbs = append(dbs, &computepb.FileContentBuffer{
|
||||
Content: toPtr(encoded),
|
||||
FileType: toPtr("X509"),
|
||||
})
|
||||
}
|
||||
return dbs
|
||||
}
|
||||
|
||||
type imagesAPI interface {
|
||||
Get(ctx context.Context, req *computepb.GetImageRequest, opts ...gaxv2.CallOption,
|
||||
) (*computepb.Image, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertImageRequest, opts ...gaxv2.CallOption,
|
||||
) (*compute.Operation, error)
|
||||
SetIamPolicy(ctx context.Context, req *computepb.SetIamPolicyImageRequest, opts ...gaxv2.CallOption,
|
||||
) (*computepb.Policy, error)
|
||||
Delete(ctx context.Context, req *computepb.DeleteImageRequest, opts ...gaxv2.CallOption,
|
||||
) (*compute.Operation, error)
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type bucketAPI interface {
|
||||
Attrs(ctx context.Context) (attrs *storage.BucketAttrs, err error)
|
||||
Create(ctx context.Context, projectID string, attrs *storage.BucketAttrs) (err error)
|
||||
Object(name string) *storage.ObjectHandle
|
||||
}
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
12
internal/osimage/nop/BUILD.bazel
Normal file
12
internal/osimage/nop/BUILD.bazel
Normal file
@ -0,0 +1,12 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "nop",
|
||||
srcs = ["nop.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/nop",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/logger",
|
||||
"//internal/osimage",
|
||||
],
|
||||
)
|
31
internal/osimage/nop/nop.go
Normal file
31
internal/osimage/nop/nop.go
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package nop implements a no-op for CSPs that don't require custom image upload functionality.
|
||||
package nop
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage"
|
||||
)
|
||||
|
||||
// Uploader is a no-op uploader.
|
||||
type Uploader struct {
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
// New creates a new Uploader.
|
||||
func New(log *logger.Logger) *Uploader {
|
||||
return &Uploader{log: log}
|
||||
}
|
||||
|
||||
// Upload pretends to upload images to a csp.
|
||||
func (u *Uploader) Upload(_ context.Context, req *osimage.UploadRequest) (map[string]string, error) {
|
||||
u.log.Debugf("Skipping image upload of %s since this CSP does not require images to be uploaded in advance.", req.Version.ShortPath())
|
||||
return nil, nil
|
||||
}
|
29
internal/osimage/osimage.go
Normal file
29
internal/osimage/osimage.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package osimage is used to handle osimages in the CI (uploading and maintenance).
|
||||
package osimage
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/secureboot"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
// UploadRequest is a request to upload an os image.
|
||||
type UploadRequest struct {
|
||||
Provider cloudprovider.Provider
|
||||
Version versionsapi.Version
|
||||
Variant string
|
||||
SBDatabase secureboot.Database
|
||||
UEFIVarStore secureboot.UEFIVarStore
|
||||
Size int64
|
||||
Timestamp time.Time
|
||||
Image io.ReadSeeker
|
||||
}
|
25
internal/osimage/secureboot/BUILD.bazel
Normal file
25
internal/osimage/secureboot/BUILD.bazel
Normal file
@ -0,0 +1,25 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("//bazel/go:go_test.bzl", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "secureboot",
|
||||
srcs = [
|
||||
"secureboot.go",
|
||||
"zlibdict.go",
|
||||
],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/secureboot",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = ["@com_github_spf13_afero//:afero"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "secureboot_test",
|
||||
srcs = ["secureboot_test.go"],
|
||||
embed = [":secureboot"],
|
||||
deps = [
|
||||
"@com_github_spf13_afero//:afero",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
"@com_github_stretchr_testify//require",
|
||||
"@org_uber_go_goleak//:goleak",
|
||||
],
|
||||
)
|
285
internal/osimage/secureboot/secureboot.go
Normal file
285
internal/osimage/secureboot/secureboot.go
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package secureboot holds secure boot configuration for image uploads.
|
||||
package secureboot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// Database holds the secure boot database that cloud providers should
|
||||
// use when enabling secure boot for a Constellation OS image.
|
||||
type Database struct {
|
||||
// PK is the platform key.
|
||||
PK []byte
|
||||
// Keks are trusted key-exchange-keys
|
||||
Keks [][]byte
|
||||
// DBs are entries of the signature database.
|
||||
DBs [][]byte
|
||||
}
|
||||
|
||||
// DatabaseFromFiles creates the secure boot database from individual files.
|
||||
func DatabaseFromFiles(fs afero.Fs, pk string, keks []string, dbs []string) (Database, error) {
|
||||
rawPK, err := afero.ReadFile(fs, pk)
|
||||
if err != nil {
|
||||
return Database{}, fmt.Errorf("loading PK %s: %w", pk, err)
|
||||
}
|
||||
rawKEKs := make([][]byte, len(keks))
|
||||
for i, kek := range keks {
|
||||
rawKEK, err := afero.ReadFile(fs, kek)
|
||||
if err != nil {
|
||||
return Database{}, fmt.Errorf("loading KEK %s: %w", kek, err)
|
||||
}
|
||||
rawKEKs[i] = rawKEK
|
||||
}
|
||||
rawDBs := make([][]byte, len(dbs))
|
||||
for i, db := range dbs {
|
||||
rawDB, err := afero.ReadFile(fs, db)
|
||||
if err != nil {
|
||||
return Database{}, fmt.Errorf("loading DB %s: %w", db, err)
|
||||
}
|
||||
rawDBs[i] = rawDB
|
||||
}
|
||||
return Database{
|
||||
PK: rawPK,
|
||||
Keks: rawKEKs,
|
||||
DBs: rawDBs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UEFIVarStore is a UEFI variable store.
|
||||
// It is a collection of UEFIVar structs.
|
||||
// This is an abstract var store that can convert to a concrete var store
|
||||
// for a specific CSP.
|
||||
type UEFIVarStore []UEFIVar
|
||||
|
||||
// VarStoreFromFiles creates the UEFI variable store
|
||||
// from "EFI Signature List" (esl) files.
|
||||
func VarStoreFromFiles(fs afero.Fs, pk, kek, db, dbx string) (UEFIVarStore, error) {
|
||||
vars := UEFIVarStore{}
|
||||
pkF, err := fs.OpenFile(pk, os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("opening PK ESL %s: %w", pk, err)
|
||||
}
|
||||
defer pkF.Close()
|
||||
pkVar, err := ReadVar(pkF, "PK", globalEFIGUID)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("reading PK ESL %s: %w", pk, err)
|
||||
}
|
||||
vars = append(vars, pkVar)
|
||||
kekF, err := fs.OpenFile(kek, os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("opening KEK ESL %s: %w", kek, err)
|
||||
}
|
||||
defer kekF.Close()
|
||||
kekVar, err := ReadVar(kekF, "KEK", globalEFIGUID)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("reading KEK ESL %s: %w", kek, err)
|
||||
}
|
||||
vars = append(vars, kekVar)
|
||||
dbF, err := fs.OpenFile(db, os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("opening DB ESL %s: %w", db, err)
|
||||
}
|
||||
defer dbF.Close()
|
||||
dbVar, err := ReadVar(dbF, "db", secureDatabaseGUID)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("reading DB ESL %s: %w", db, err)
|
||||
}
|
||||
vars = append(vars, dbVar)
|
||||
if len(dbx) == 0 {
|
||||
return vars, nil
|
||||
}
|
||||
dbxF, err := fs.OpenFile(dbx, os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("opening DBX ESL %s: %w", dbx, err)
|
||||
}
|
||||
defer dbxF.Close()
|
||||
dbxVar, err := ReadVar(dbxF, "dbx", secureDatabaseGUID)
|
||||
if err != nil {
|
||||
return UEFIVarStore{}, fmt.Errorf("reading DBX ESL %s: %w", dbx, err)
|
||||
}
|
||||
vars = append(vars, dbxVar)
|
||||
return vars, nil
|
||||
}
|
||||
|
||||
// ToAWS converts the UEFI variable store to the AWS UEFI vars v0 format.
|
||||
// The format is documented here:
|
||||
// https://github.com/awslabs/python-uefivars
|
||||
// It is structured as follows:
|
||||
// Header:
|
||||
// - 4 bytes: magic number
|
||||
// - 4 bytes: crc32 of the rest of the file
|
||||
// - 4 bytes: version number
|
||||
//
|
||||
// Body is zlib compressed stream of:
|
||||
// 8 bytes number of entries
|
||||
// for each entry:
|
||||
// - name (variable length field, utf8)
|
||||
// - data (variable length field)
|
||||
// - guid (16 bytes)
|
||||
// - attr (int32 in little endian)
|
||||
// OPTIONAL (if attr has EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set):
|
||||
// - timestamp (16 bytes)
|
||||
// - digest (variable length field).
|
||||
func (s UEFIVarStore) ToAWS() (string, error) {
|
||||
payload := bytes.Buffer{}
|
||||
// Write the number of entries.
|
||||
if err := binary.Write(&payload, binary.LittleEndian, uint64(len(s))); err != nil {
|
||||
return "", fmt.Errorf("writing number of entries: %w", err)
|
||||
}
|
||||
// Write the entries.
|
||||
for _, entry := range s {
|
||||
rawEntry, err := entry.AWSEntry()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("serializing entry: %w", err)
|
||||
}
|
||||
if _, err := payload.Write(rawEntry); err != nil {
|
||||
return "", fmt.Errorf("writing entry: %w", err)
|
||||
}
|
||||
}
|
||||
// Compress the payload.
|
||||
compressed := bytes.Buffer{}
|
||||
zlibW, err := zlib.NewWriterLevelDict(&compressed, zlib.BestCompression, zlibDict)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("creating compressor: %w", err)
|
||||
}
|
||||
if _, err := zlibW.Write(payload.Bytes()); err != nil {
|
||||
return "", fmt.Errorf("compressing payload: %w", err)
|
||||
}
|
||||
if err := zlibW.Close(); err != nil {
|
||||
return "", fmt.Errorf("closing compressor: %w", err)
|
||||
}
|
||||
compressedData := compressed.Bytes()
|
||||
// Calculate the CRC32 (Castagnoli) of the version + compressed payload.
|
||||
crcData := append(awsVersion, compressedData...)
|
||||
crc := crc32.Checksum(crcData, crc32.MakeTable(crc32.Castagnoli))
|
||||
out := bytes.Buffer{}
|
||||
// Write the header.
|
||||
if _, err := out.Write(awsMagic); err != nil {
|
||||
return "", fmt.Errorf("writing magic: %w", err)
|
||||
}
|
||||
if err := binary.Write(&out, binary.LittleEndian, crc); err != nil {
|
||||
return "", fmt.Errorf("writing crc: %w", err)
|
||||
}
|
||||
// Write the version + compressed payload.
|
||||
if _, err := out.Write(crcData); err != nil {
|
||||
return "", fmt.Errorf("writing compressed payload: %w", err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(out.Bytes()), nil
|
||||
}
|
||||
|
||||
// UEFIVar is a UEFI variable.
|
||||
type UEFIVar struct {
|
||||
Name string
|
||||
Data []byte
|
||||
GUID []byte
|
||||
Attr uint32
|
||||
Timestamp []byte
|
||||
Digest []byte
|
||||
}
|
||||
|
||||
// ReadVar reads a UEFI variable from an ESL file.
|
||||
func ReadVar(reader io.Reader, name string, guid []byte) (UEFIVar, error) {
|
||||
attr := uint32(
|
||||
EFIVariableNonVolatile |
|
||||
EFIVariableBootServiceAccess |
|
||||
EFIVariableRuntimeAccess |
|
||||
EFIVariableTimeBasedAuthenticatedWriteAccess,
|
||||
)
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return UEFIVar{}, err
|
||||
}
|
||||
return UEFIVar{
|
||||
Name: name,
|
||||
Data: data,
|
||||
GUID: guid,
|
||||
Attr: attr,
|
||||
Timestamp: []byte{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
},
|
||||
Digest: []byte{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AWSEntry returns the AWS format entry for the UEFI variable.
|
||||
func (v UEFIVar) AWSEntry() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := appendVariableLengthField(&buf, []byte(v.Name)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := appendVariableLengthField(&buf, v.Data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.Write(v.GUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := appendAttr(&buf, v.Attr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v.Attr&EFIVariableTimeBasedAuthenticatedWriteAccess == 0 {
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
if _, err := buf.Write(v.Timestamp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := appendVariableLengthField(&buf, v.Digest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func appendVariableLengthField(w io.Writer, data []byte) error {
|
||||
// variable length is encoded as unsigned, 64 bit little endian
|
||||
// followed by the data
|
||||
if err := binary.Write(w, binary.LittleEndian, uint64(len(data))); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func appendAttr(w io.Writer, attr uint32) error {
|
||||
return binary.Write(w, binary.LittleEndian, attr)
|
||||
}
|
||||
|
||||
// EFI constants.
|
||||
const (
|
||||
EFIVariableNonVolatile = 0x00000001
|
||||
EFIVariableBootServiceAccess = 0x00000002
|
||||
EFIVariableRuntimeAccess = 0x00000004
|
||||
EFIVariableTimeBasedAuthenticatedWriteAccess = 0x00000020
|
||||
)
|
||||
|
||||
var (
|
||||
awsMagic = []byte("AMZNUEFI")
|
||||
awsVersion = []byte{0, 0, 0, 0}
|
||||
globalEFIGUID = []byte{
|
||||
0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11,
|
||||
0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c,
|
||||
}
|
||||
secureDatabaseGUID = []byte{
|
||||
0xcb, 0xb2, 0x19, 0xd7, 0x3a, 0x3d, 0x96, 0x45,
|
||||
0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f,
|
||||
}
|
||||
)
|
182
internal/osimage/secureboot/secureboot_test.go
Normal file
182
internal/osimage/secureboot/secureboot_test.go
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package secureboot
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
|
||||
func TestDatabaseFromFile(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
pk string
|
||||
keks []string
|
||||
dbs []string
|
||||
wantErr bool
|
||||
}{
|
||||
"found": {
|
||||
pk: "pki/pk.cer",
|
||||
keks: []string{"pki/kek.cer"},
|
||||
dbs: []string{"pki/db.cer"},
|
||||
},
|
||||
"pk not found": {
|
||||
pk: "pki/missing",
|
||||
keks: []string{"pki/kek.cer"},
|
||||
dbs: []string{"pki/db.cer"},
|
||||
wantErr: true,
|
||||
},
|
||||
"kek not found": {
|
||||
pk: "pki/pk.cer",
|
||||
keks: []string{"pki/missing"},
|
||||
dbs: []string{"pki/db.cer"},
|
||||
wantErr: true,
|
||||
},
|
||||
"db not found": {
|
||||
pk: "pki/pk.cer",
|
||||
keks: []string{"pki/kek.cer"},
|
||||
dbs: []string{"pki/missing"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(afero.WriteFile(fs, "pki/pk.cer", []byte("pk"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/kek.cer", []byte("kek"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/db.cer", []byte("db"), 0o644))
|
||||
db, err := DatabaseFromFiles(fs, tc.pk, tc.keks, tc.dbs)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Len(db.Keks, 1)
|
||||
assert.Len(db.DBs, 1)
|
||||
assert.Equal("pk", string(db.PK))
|
||||
assert.Equal("kek", string(db.Keks[0]))
|
||||
assert.Equal("db", string(db.DBs[0]))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVarsStoreFromFiles(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
pk string
|
||||
kek string
|
||||
db string
|
||||
dbx string
|
||||
wantErr bool
|
||||
}{
|
||||
"found": {
|
||||
pk: "pki/pk.esl",
|
||||
kek: "pki/kek.esl",
|
||||
db: "pki/db.esl",
|
||||
dbx: "pki/dbx.esl",
|
||||
},
|
||||
"no dbx": {
|
||||
pk: "pki/pk.esl",
|
||||
kek: "pki/kek.esl",
|
||||
db: "pki/db.esl",
|
||||
},
|
||||
"pk not found": {
|
||||
pk: "pki/missing",
|
||||
kek: "pki/kek.esl",
|
||||
db: "pki/db.esl",
|
||||
wantErr: true,
|
||||
},
|
||||
"kek not found": {
|
||||
pk: "pki/pk.esl",
|
||||
kek: "pki/missing",
|
||||
db: "pki/db.esl",
|
||||
wantErr: true,
|
||||
},
|
||||
"db not found": {
|
||||
pk: "pki/pk.esl",
|
||||
kek: "pki/kek.esl",
|
||||
db: "pki/missing",
|
||||
wantErr: true,
|
||||
},
|
||||
"dbx not found": {
|
||||
pk: "pki/pk.esl",
|
||||
kek: "pki/kek.esl",
|
||||
db: "pki/db.esl",
|
||||
dbx: "pki/missing",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(afero.WriteFile(fs, "pki/pk.esl", []byte("pk"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/kek.esl", []byte("kek"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/db.esl", []byte("db"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/dbx.esl", []byte("dbx"), 0o644))
|
||||
store, err := VarStoreFromFiles(fs, tc.pk, tc.kek, tc.db, tc.dbx)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
if tc.dbx == "" {
|
||||
assert.Len(store, 3)
|
||||
} else {
|
||||
assert.Len(store, 4)
|
||||
assert.Equal("dbx", store[3].Name)
|
||||
assert.Equal("dbx", string(store[3].Data))
|
||||
assert.Equal(secureDatabaseGUID, store[3].GUID)
|
||||
}
|
||||
assert.Equal("PK", store[0].Name)
|
||||
assert.Equal("pk", string(store[0].Data))
|
||||
assert.Equal(globalEFIGUID, store[0].GUID)
|
||||
assert.Equal("KEK", store[1].Name)
|
||||
assert.Equal("kek", string(store[1].Data))
|
||||
assert.Equal(globalEFIGUID, store[1].GUID)
|
||||
assert.Equal("db", store[2].Name)
|
||||
assert.Equal("db", string(store[2].Data))
|
||||
assert.Equal(secureDatabaseGUID, store[2].GUID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToAWS(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(afero.WriteFile(fs, "pki/pk.esl", []byte("pk"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/kek.esl", []byte("kek"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/db.esl", []byte("db"), 0o644))
|
||||
require.NoError(afero.WriteFile(fs, "pki/dbx.esl", []byte("dbx"), 0o644))
|
||||
store, err := VarStoreFromFiles(fs, "pki/pk.esl", "pki/kek.esl", "pki/db.esl", "pki/dbx.esl")
|
||||
require.NoError(err)
|
||||
awsData, err := store.ToAWS()
|
||||
require.NoError(err)
|
||||
out, err := base64.StdEncoding.DecodeString(awsData)
|
||||
assert.NoError(err)
|
||||
assert.Equal([]byte{
|
||||
0x41, 0x4d, 0x5a, 0x4e, 0x55, 0x45, 0x46, 0x49, 0x5d, 0x52, 0x8c, 0xf0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x78, 0xf9, 0x6b, 0xb7, 0xd9, 0xf7, 0x62, 0x81, 0xda, 0xc4, 0x04, 0xa5, 0x03, 0xbc, 0x61, 0xac,
|
||||
0x82, 0x6c, 0x74, 0x5b, 0xd5, 0xb1, 0xb8, 0x50, 0x81, 0xc8, 0x25, 0xbe, 0xb0, 0x69, 0x3d, 0x6f,
|
||||
0x57, 0x6f, 0x18, 0x33, 0x3b, 0x95, 0xaa, 0x36, 0xc0, 0xdc, 0x9d, 0x92, 0x84, 0x60, 0xa1, 0xb7,
|
||||
0xcc, 0xa9, 0xe1, 0x83, 0x94, 0xa4, 0x0a, 0x24, 0x26, 0x35, 0x6d, 0x00, 0x04, 0x00, 0x00, 0xff,
|
||||
0xff, 0xd6, 0x50, 0x28, 0x9b,
|
||||
}, out)
|
||||
}
|
961
internal/osimage/secureboot/zlibdict.go
Normal file
961
internal/osimage/secureboot/zlibdict.go
Normal file
@ -0,0 +1,961 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package secureboot
|
||||
|
||||
// zlibDict is the zlib dictionary used for compressing the aws efivars.
|
||||
// See also https://github.com/awslabs/python-uefivars/blob/9679002a4392d8e7831d2dbda3fab41ccc5c6b8c/pyuefivars/aws_v0.py.
|
||||
var zlibDict = []byte{
|
||||
0x37, 0xa4, 0x30, 0xec, 0x12, 0x44, 0xfc, 0x0a, 0x1d, 0x10, 0x28, 0x9d,
|
||||
0x00, 0x00, 0x87, 0xae, 0xe3, 0x9e, 0xd0, 0x73, 0x2d, 0x2b, 0x4b, 0xca,
|
||||
0x1e, 0xac, 0x1c, 0xfb, 0x79, 0xc8, 0x7c, 0x05, 0xfb, 0xf4, 0xf4, 0xf5,
|
||||
0xf7, 0x01, 0x43, 0x05, 0x28, 0x00, 0x06, 0x03, 0x84, 0xe2, 0xd1, 0x60,
|
||||
0x28, 0x4b, 0x06, 0x77, 0x00, 0x04, 0x80, 0xc0, 0x04, 0x0a, 0x1c, 0x0e,
|
||||
0x06, 0x10, 0x08, 0x0a, 0x09, 0x05, 0x07, 0x34, 0x07, 0x04, 0x0a, 0x0f,
|
||||
0x03, 0x07, 0x09, 0x0c, 0x07, 0x04, 0x05, 0x06, 0x08, 0x85, 0xc1, 0x60,
|
||||
0x61, 0x68, 0x48, 0x20, 0x09, 0x07, 0x03, 0x71, 0xa1, 0x3c, 0xd5, 0x87,
|
||||
0x98, 0x01, 0xd8, 0x00, 0x24, 0x51, 0x0f, 0x22, 0x11, 0x4c, 0x51, 0x0c,
|
||||
0x02, 0x01, 0x00, 0x00, 0x82, 0x00, 0x21, 0xc8, 0xb8, 0x60, 0x68, 0x68,
|
||||
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00,
|
||||
0x00, 0x00, 0x02, 0x1f, 0x03, 0x12, 0x0a, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0x00, 0xb0, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f,
|
||||
0x00, 0x6e, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x02,
|
||||
0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x01,
|
||||
0x05, 0x00, 0x01, 0x03, 0x0d, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x87, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11,
|
||||
0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f, 0x00,
|
||||
0x6e, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x02, 0x01,
|
||||
0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
|
||||
0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x01, 0x05,
|
||||
0x00, 0x0f, 0x01, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f,
|
||||
0x00, 0x6e, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x02,
|
||||
0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x01,
|
||||
0x05, 0x00, 0xef, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93,
|
||||
0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00,
|
||||
0x6f, 0x00, 0x6e, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41,
|
||||
0x01, 0x05, 0x00, 0x2b, 0x29, 0x58, 0x9e, 0x68, 0x7c, 0x7d, 0x49, 0xa0,
|
||||
0xce, 0x65, 0x00, 0xfd, 0x9f, 0x1b, 0x95, 0x2c, 0xaf, 0x2c, 0x64, 0xfe,
|
||||
0xff, 0xff, 0xff, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x4d, 0x69,
|
||||
0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x57, 0x69, 0x6e, 0x64,
|
||||
0x6f, 0x77, 0x73, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x20, 0x50, 0x43, 0x41, 0x20, 0x32, 0x30, 0x31, 0x31, 0x30,
|
||||
0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
||||
0x0d, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x61, 0xdf,
|
||||
0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03,
|
||||
0x2b, 0x8c, 0x4c, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
|
||||
0x65, 0x6e, 0x67, 0x00, 0xff, 0xff, 0xaa, 0x55, 0x3f, 0x00, 0x07, 0x00,
|
||||
0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b,
|
||||
0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c,
|
||||
0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x74, 0x00, 0x66, 0x00, 0x6f, 0x00,
|
||||
0x72, 0x00, 0x6d, 0x00, 0x4c, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x67, 0x00,
|
||||
0x00, 0x00, 0x65, 0x6e, 0x00, 0xff, 0xff, 0xff, 0xaa, 0x55, 0x3f, 0x00,
|
||||
0x07, 0x00, 0x28, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0xd6,
|
||||
0x47, 0x4b, 0xd6, 0xa8, 0x52, 0x45, 0x9d, 0x44, 0xcc, 0xad, 0x2e, 0x0f,
|
||||
0x4c, 0xf9, 0x49, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00,
|
||||
0x61, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00,
|
||||
0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x4f, 0x00, 0x72, 0x00, 0x64, 0x00,
|
||||
0x65, 0x00, 0x72, 0x00, 0x28, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x16, 0xd6, 0x47, 0x4b, 0xd6, 0xa8, 0x52, 0x45, 0x9d, 0x44, 0xcc, 0xad,
|
||||
0x2e, 0x0f, 0x4c, 0xf9, 0x49, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00,
|
||||
0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x74, 0x00, 0x74, 0x00,
|
||||
0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x4f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x28, 0x00, 0x00, 0x00, 0x05, 0x00,
|
||||
0x00, 0x00, 0x16, 0xd6, 0x47, 0x4b, 0xd6, 0xa8, 0x52, 0x45, 0x9d, 0x44,
|
||||
0xcc, 0xad, 0x2e, 0x0f, 0x4c, 0xf9, 0x49, 0x00, 0x6e, 0x00, 0x69, 0x00,
|
||||
0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x74, 0x00,
|
||||
0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x4f, 0x00,
|
||||
0x72, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x28, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x16, 0xd6, 0x47, 0x4b, 0xd6, 0xa8, 0x52, 0x45,
|
||||
0x9d, 0x44, 0xcc, 0xad, 0x2e, 0x0f, 0x4c, 0xf9, 0x49, 0x00, 0x6e, 0x00,
|
||||
0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x41, 0x00,
|
||||
0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00,
|
||||
0x4f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
|
||||
0x00, 0x21, 0xe5, 0x7f, 0x93, 0xae, 0x95, 0x1a, 0x4d, 0x89, 0x29, 0x48,
|
||||
0xbc, 0xd9, 0x0a, 0xd3, 0x1a, 0x35, 0x00, 0x32, 0x00, 0x35, 0x00, 0x34,
|
||||
0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34,
|
||||
0x00, 0x35, 0x00, 0x36, 0x00, 0xe8, 0x7f, 0xb3, 0x04, 0xae, 0xf6, 0x0b,
|
||||
0x48, 0xbd, 0xd5, 0x37, 0xd9, 0x8c, 0x5e, 0x89, 0xaa, 0x56, 0x00, 0x61,
|
||||
0x00, 0x72, 0x00, 0x45, 0x00, 0x72, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x72,
|
||||
0x00, 0x46, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x67, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00, 0x88, 0x9c, 0xd0, 0xf7, 0xb6,
|
||||
0xc4, 0x7a, 0xd5, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x65, 0x00, 0x0e, 0x00,
|
||||
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0xe5, 0xbe, 0xd9, 0xdc, 0x75,
|
||||
0xd9, 0x49, 0xb4, 0xd7, 0xb5, 0x34, 0x21, 0x0f, 0x63, 0x7a, 0x63, 0x00,
|
||||
0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x64, 0x00, 0x62, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x55, 0x3c, 0x00, 0x23, 0x00,
|
||||
0x00, 0x04, 0x01, 0x2a, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xd8, 0xbc,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x6f, 0xbd, 0x26, 0x17, 0x0a, 0xae, 0xeb, 0x11, 0xaf, 0xcc, 0xb2,
|
||||
0x6f, 0x04, 0x02, 0x45, 0xc6, 0x02, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00,
|
||||
0x00, 0x00, 0x16, 0xd6, 0x47, 0x4b, 0xd6, 0xa8, 0x52, 0x45, 0x9d, 0x44,
|
||||
0xcc, 0xad, 0x2e, 0x0f, 0x4c, 0xf9, 0x49, 0x00, 0x6e, 0x00, 0x69, 0x00,
|
||||
0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x74, 0x00,
|
||||
0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x4f, 0x00,
|
||||
0x72, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x12, 0x34, 0x56, 0xff,
|
||||
0xff, 0xaa, 0x55, 0x50, 0x00, 0x58, 0x00, 0x45, 0x00, 0x76, 0x00, 0x43,
|
||||
0x00, 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74,
|
||||
0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
|
||||
0x77, 0x00, 0x73, 0x00, 0x11, 0xd4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f,
|
||||
0xc1, 0x4d, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4,
|
||||
0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b,
|
||||
0x8c, 0x45, 0x00, 0x72, 0x00, 0x72, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74,
|
||||
0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c,
|
||||
0x00, 0xd0, 0x41, 0x01, 0x05, 0x00, 0x02, 0xbd, 0x9a, 0xfa, 0x77, 0x59,
|
||||
0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x06,
|
||||
0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3d, 0x03, 0x0e, 0x00,
|
||||
0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93,
|
||||
0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00,
|
||||
0x6f, 0x00, 0x6e, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41,
|
||||
0x01, 0x05, 0x00, 0x11, 0xd4, 0x9a, 0x38, 0x00, 0x90, 0x27, 0x3f, 0xc1,
|
||||
0x4d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xbd, 0x9a, 0xfa, 0x77, 0x59,
|
||||
0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x00,
|
||||
0x50, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x63, 0x00, 0x79, 0x00,
|
||||
0x2d, 0x88, 0x11, 0xd3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d,
|
||||
0x00, 0xaf, 0xaf, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x6f, 0x22, 0xec,
|
||||
0xea, 0xa3, 0xc9, 0x7a, 0x47, 0xa8, 0x26, 0xdd, 0xc7, 0x16, 0xcd, 0xc0,
|
||||
0xe3, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x6f, 0x22, 0xec, 0xea,
|
||||
0xa3, 0xc9, 0x7a, 0x47, 0xa8, 0x26, 0xdd, 0xc7, 0x16, 0xcd, 0xc0, 0xe3,
|
||||
0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
|
||||
0x00, 0x05, 0x00, 0x06, 0x00, 0x11, 0xd2, 0x8e, 0x39, 0x00, 0xa0, 0xc9,
|
||||
0x69, 0x72, 0x3b, 0xbd, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d,
|
||||
0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x03, 0x18, 0x04, 0x00,
|
||||
0x7f, 0xff, 0x04, 0x00, 0x4e, 0xac, 0x08, 0x81, 0x11, 0x9f, 0x59, 0x4d,
|
||||
0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2, 0x00, 0x20, 0x00, 0x51,
|
||||
0x00, 0x4d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x0e,
|
||||
0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca,
|
||||
0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x45,
|
||||
0x00, 0x72, 0x00, 0x72, 0x00, 0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00,
|
||||
0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0,
|
||||
0x41, 0x01, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xbd, 0x9a, 0xfa, 0x77,
|
||||
0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b,
|
||||
0x80, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0x0d, 0x02, 0xfd, 0x91, 0xa6, 0x1e,
|
||||
0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8,
|
||||
0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0x0a, 0xbd, 0x9a, 0xfa, 0x77,
|
||||
0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0x07, 0x00, 0xaa, 0x55, 0x3d,
|
||||
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
|
||||
0x00, 0x58, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x42, 0x17, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00,
|
||||
0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0,
|
||||
0x98, 0x6b, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f,
|
||||
0x00, 0x6e, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c,
|
||||
0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06,
|
||||
0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7f, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03,
|
||||
0x13, 0x21, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20,
|
||||
0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
|
||||
0x4b, 0x45, 0x4b, 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, 0x31, 0x31, 0x30,
|
||||
0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
||||
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x61,
|
||||
0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98,
|
||||
0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x49, 0x00, 0x6e,
|
||||
0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c,
|
||||
0x00, 0xd0, 0x41, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d,
|
||||
0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x4b, 0x00, 0x65, 0x00,
|
||||
0x72, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x00, 0x37,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x45, 0x00, 0x46,
|
||||
0x00, 0x49, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65,
|
||||
0x00, 0x72, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x53,
|
||||
0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04,
|
||||
0x07, 0x14, 0x00, 0xc9, 0xbd, 0xb8, 0x7c, 0xeb, 0xf8, 0x34, 0x4f, 0xaa,
|
||||
0xea, 0x3e, 0x0e, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93,
|
||||
0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x4b, 0x00,
|
||||
0x65, 0x00, 0x79, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x51, 0xd7, 0x97, 0x9f, 0x00, 0x00,
|
||||
0x0c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x55, 0x3f, 0x00, 0x07, 0x00,
|
||||
0x00, 0x76, 0x00, 0x34, 0x00, 0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x41,
|
||||
0x00, 0x43, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x32, 0x00, 0x35, 0x00, 0x34,
|
||||
0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34,
|
||||
0x00, 0x35, 0x00, 0x36, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c,
|
||||
0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06,
|
||||
0x00, 0x00, 0x02, 0x03, 0x0b, 0x25, 0x00, 0x52, 0x54, 0x00, 0x12, 0x34,
|
||||
0x56, 0x00, 0x00, 0x53, 0x00, 0x69, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61,
|
||||
0x00, 0x74, 0x00, 0x75, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0xff, 0xff, 0xaa, 0x55, 0x3f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xbd,
|
||||
0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7,
|
||||
0x8f, 0x78, 0x4b, 0x4b, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x65,
|
||||
0x00, 0x6c, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x00, 0x00, 0xa1,
|
||||
0x59, 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, 0x5c,
|
||||
0x2b, 0xf0, 0x72, 0xd0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4,
|
||||
0x03, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa,
|
||||
0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x30, 0x82, 0x03, 0xa0, 0x30,
|
||||
0x82, 0x02, 0x88, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xfe,
|
||||
0xf5, 0x88, 0xe8, 0xf3, 0x96, 0xc0, 0xf1, 0x30, 0x0d, 0x06, 0xb9, 0x00,
|
||||
0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d,
|
||||
0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00,
|
||||
0x49, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41,
|
||||
0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f,
|
||||
0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||
0x7f, 0x7a, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f,
|
||||
0x00, 0x6e, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c,
|
||||
0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06,
|
||||
0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7f, 0x37, 0x32, 0x31, 0x32, 0x32, 0x34, 0x35, 0x5a,
|
||||
0x17, 0x0d, 0x32, 0x36, 0x30, 0x36, 0x32, 0x37, 0x32, 0x31, 0x33, 0x32,
|
||||
0x34, 0x35, 0x5a, 0x30, 0x81, 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
|
||||
0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06,
|
||||
0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e,
|
||||
0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04,
|
||||
0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e,
|
||||
0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x00, 0x76, 0x00, 0x36, 0x00,
|
||||
0x20, 0x00, 0x28, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x43, 0x00, 0x3a, 0x00,
|
||||
0x35, 0x00, 0x32, 0x00, 0x35, 0x00, 0x34, 0x00, 0x30, 0x00, 0x30, 0x00,
|
||||
0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00,
|
||||
0x29, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x02, 0x03, 0x0b,
|
||||
0x25, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, 0x4d,
|
||||
0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x52, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
||||
0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20,
|
||||
0x32, 0x30, 0x31, 0x30, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x31, 0x30,
|
||||
0x31, 0x39, 0x31, 0x38, 0x34, 0x31, 0x34, 0x32, 0x5a, 0x17, 0x0d, 0x32,
|
||||
0x36, 0x31, 0x30, 0x31, 0x39, 0x31, 0x38, 0x35, 0x31, 0x34, 0x32, 0x5a,
|
||||
0x30, 0x81, 0x84, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x74, 0x30,
|
||||
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
|
||||
0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x14, 0xfc, 0x7c, 0x71, 0x51,
|
||||
0xa5, 0x79, 0xc2, 0x6e, 0xb2, 0xef, 0x39, 0x3e, 0xbc, 0x3c, 0x52, 0x0f,
|
||||
0x6e, 0x2b, 0x3f, 0x10, 0x13, 0x73, 0xfe, 0xa8, 0x68, 0xd0, 0x48, 0xa6,
|
||||
0x34, 0x4d, 0x8a, 0x96, 0x05, 0x26, 0xee, 0x31, 0x46, 0x90, 0x61, 0x79,
|
||||
0xd6, 0x7f, 0xff, 0x04, 0x00, 0xaa, 0x55, 0x3f, 0x00, 0x07, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00,
|
||||
0xe0, 0x98, 0x03, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b,
|
||||
0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c,
|
||||
0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x00, 0x00,
|
||||
0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41,
|
||||
0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x14, 0x00, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa,
|
||||
0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x42, 0x00, 0x6f, 0x00, 0x6f,
|
||||
0x00, 0x74, 0x00, 0x4f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xaa, 0x55, 0x3f, 0x00, 0x07,
|
||||
0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0xec, 0x76,
|
||||
0xc0, 0x28, 0x70, 0x99, 0x43, 0xa0, 0x72, 0x71, 0xee, 0x5c, 0x44, 0x8b,
|
||||
0x9f, 0x43, 0x00, 0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x6d,
|
||||
0x00, 0x4d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xaa, 0x55, 0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xaa,
|
||||
0x55, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x9f, 0x04, 0x19, 0x4c, 0x37,
|
||||
0x41, 0xd3, 0x4d, 0x9c, 0x10, 0x8b, 0x97, 0xa8, 0x3f, 0xfd, 0xfa, 0x4d,
|
||||
0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x79, 0x00, 0x54,
|
||||
0x00, 0x79, 0x00, 0x70, 0x00, 0x65, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66,
|
||||
0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69,
|
||||
0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x80,
|
||||
0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x55, 0x3c, 0x00, 0x03,
|
||||
0x00, 0xd5, 0xf6, 0x56, 0xcb, 0x8f, 0xe8, 0xa2, 0x5c, 0x62, 0x68, 0xd1,
|
||||
0x3d, 0x94, 0x90, 0x5b, 0xd7, 0xce, 0x9a, 0x18, 0xc4, 0x30, 0x56, 0x06,
|
||||
0x03, 0x55, 0x1d, 0x1f, 0x04, 0x4f, 0x30, 0x4d, 0x30, 0x4b, 0xa0, 0x49,
|
||||
0xa0, 0x47, 0x86, 0x45, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
|
||||
0x72, 0x6c, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x63, 0x72, 0x6c,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x2f, 0x4d, 0x69,
|
||||
0x63, 0x52, 0x6f, 0x6f, 0x43, 0x65, 0x72, 0x41, 0x75, 0x74, 0x5f, 0x32,
|
||||
0x30, 0x31, 0x30, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x33, 0x2e, 0x63, 0x72,
|
||||
0x6c, 0x30, 0x5a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
|
||||
0x01, 0x04, 0x4e, 0x30, 0x4c, 0x30, 0x4a, 0x06, 0x08, 0x2b, 0x06, 0x01,
|
||||
0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, 0x74, 0x70, 0x3a,
|
||||
0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73,
|
||||
0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x0c, 0x00, 0x00, 0x00, 0x22, 0x00,
|
||||
0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d,
|
||||
0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00,
|
||||
0x49, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41,
|
||||
0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f,
|
||||
0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||
0x7f, 0xff, 0x04, 0x00, 0xff, 0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x49, 0x00,
|
||||
0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d,
|
||||
0x00, 0xe0, 0x98, 0x03, 0x03, 0x0f, 0x0b, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x03, 0x01, 0x01, 0x7f, 0x01, 0x04, 0x00, 0x01, 0x04, 0x14, 0x00, 0x9b,
|
||||
0x5a, 0x5a, 0x86, 0x5d, 0xb8, 0x4c, 0x47, 0x84, 0x55, 0x65, 0xd1, 0xbe,
|
||||
0x84, 0x4b, 0xe2, 0x03, 0x0e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0a,
|
||||
0x14, 0x00, 0x53, 0x47, 0xc1, 0xe0, 0xbe, 0x8d, 0x2b, 0xf1, 0xff, 0x96,
|
||||
0x76, 0x8b, 0x4c, 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x46, 0x56, 0x48, 0xff,
|
||||
0xfe, 0x04, 0x00, 0x48, 0x00, 0x19, 0xf9, 0x00, 0x00, 0x00, 0x02, 0x20,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x78, 0x2c, 0xf3, 0xaa, 0x7b, 0x94, 0x9a, 0x43, 0xa1,
|
||||
0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92, 0xb8, 0xdf, 0x00, 0x00, 0x5a,
|
||||
0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x55, 0x3c, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x0c, 0xec, 0x76, 0xc0, 0x28, 0x70, 0x99, 0x43, 0xa0,
|
||||
0x72, 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f, 0xf1, 0x00, 0x00, 0x00, 0x61,
|
||||
0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98,
|
||||
0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x4f, 0x00, 0x75,
|
||||
0x00, 0x74, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02,
|
||||
0x01, 0x0c, 0x00, 0xd0, 0x41, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x0e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0xff,
|
||||
0xff, 0xaa, 0x55, 0x3f, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x1a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xe4, 0x73,
|
||||
0x90, 0xec, 0x60, 0x6e, 0x4b, 0x99, 0x03, 0x4c, 0x22, 0x3c, 0x26, 0x0f,
|
||||
0x3c, 0x56, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x72,
|
||||
0x00, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x73, 0x00, 0x4e, 0x00, 0x76,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xaa, 0x55, 0x3f, 0x00, 0x03, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0xc7, 0x0b, 0xa3, 0xf0, 0x08, 0xaf, 0x56, 0x45, 0x99, 0xc4, 0x00,
|
||||
0x10, 0x09, 0xc9, 0x3a, 0x44, 0x53, 0x00, 0x65, 0x00, 0x63, 0x00, 0x75,
|
||||
0x00, 0x72, 0x00, 0x65, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74,
|
||||
0x00, 0x45, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65,
|
||||
0x00, 0x00, 0x00, 0x01, 0xff, 0xaa, 0x55, 0x3f, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0xff, 0xaa, 0x55, 0x3c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0xd6,
|
||||
0x47, 0x4b, 0xd6, 0xa8, 0x52, 0x45, 0x9d, 0x44, 0xcc, 0xad, 0x2e, 0x0f,
|
||||
0x4c, 0xf9, 0x49, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00,
|
||||
0x61, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00,
|
||||
0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x4f, 0x00, 0x72, 0x00, 0x64, 0x00,
|
||||
0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0xcb, 0xb2, 0x19, 0xd7, 0x3a, 0x3d,
|
||||
0x96, 0x45, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f, 0x64, 0x00,
|
||||
0x62, 0x00, 0x78, 0x00, 0x00, 0x00, 0x26, 0x16, 0xc4, 0xc1, 0x4c, 0x50,
|
||||
0x92, 0x40, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28, 0x4c, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xa3, 0xa8,
|
||||
0xba, 0xa0, 0x1d, 0x04, 0xa8, 0x48, 0xbc, 0x87, 0xc3, 0x6d, 0x12, 0x1b,
|
||||
0x5e, 0x3d, 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb,
|
||||
0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
||||
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, 0x26, 0x16,
|
||||
0xc4, 0xc1, 0x4c, 0x50, 0x92, 0x40, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93,
|
||||
0x43, 0x28, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x40,
|
||||
0x70, 0xeb, 0x02, 0x14, 0xd3, 0x11, 0x8e, 0x77, 0x00, 0xa0, 0xc9, 0x69,
|
||||
0x72, 0x3b, 0x4d, 0x00, 0x54, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0xaa, 0x55, 0x3c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0xd6,
|
||||
0x47, 0x4b, 0xd6, 0xa8, 0x52, 0x45, 0x9d, 0x44, 0xcc, 0xad, 0x2e, 0x0f,
|
||||
0x4c, 0xf9, 0x49, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00,
|
||||
0x61, 0x00, 0x6c, 0x00, 0x41, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00,
|
||||
0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x4f, 0x00, 0x72, 0x00, 0x64, 0x00,
|
||||
0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xaa, 0x55,
|
||||
0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00,
|
||||
0x00, 0x00, 0x19, 0x04, 0x00, 0x00, 0x45, 0x49, 0x32, 0x59, 0x44, 0xec,
|
||||
0x0d, 0x4c, 0xb1, 0xcd, 0x9d, 0xb1, 0x39, 0xdf, 0x07, 0x0c, 0x41, 0x00,
|
||||
0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00,
|
||||
0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x74, 0x74, 0x65,
|
||||
0x6d, 0x70, 0x74, 0x20, 0x31, 0x00, 0xf5, 0x2f, 0x83, 0xa3, 0xfa, 0x9c,
|
||||
0xfb, 0xd6, 0x92, 0x0f, 0x72, 0x28, 0x24, 0xdb, 0xe4, 0x03, 0x45, 0x34,
|
||||
0xd2, 0x5b, 0x85, 0x07, 0x24, 0x6b, 0x3b, 0x95, 0x7d, 0xac, 0x6e, 0x1b,
|
||||
0xce, 0x7a, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60,
|
||||
0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0xc5, 0xd9, 0xd8, 0xa1, 0x86, 0xe2,
|
||||
0xc8, 0x2d, 0x09, 0xaf, 0xaa, 0x2a, 0x6f, 0x7f, 0x2e, 0x73, 0x87, 0x0d,
|
||||
0x3e, 0x64, 0xf7, 0x2c, 0x4e, 0x08, 0xef, 0x67, 0x79, 0x6a, 0x84, 0x0f,
|
||||
0x0f, 0xbd, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60,
|
||||
0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x1a, 0xec, 0x84, 0xb8, 0x4b, 0x6c,
|
||||
0x65, 0xa5, 0x12, 0x20, 0xa9, 0xbe, 0x71, 0x81, 0x96, 0x52, 0x30, 0x21,
|
||||
0x0d, 0x62, 0xd6, 0xd3, 0x3c, 0x48, 0x99, 0x9c, 0x6b, 0x29, 0x5a, 0x2b,
|
||||
0x0a, 0x06, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60,
|
||||
0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0xc3, 0xa9, 0x9a, 0x46, 0x0d, 0xa4,
|
||||
0x64, 0xa0, 0x57, 0xc3, 0x58, 0x6d, 0x83, 0xce, 0xf5, 0xf4, 0xae, 0x08,
|
||||
0xb7, 0x10, 0x39, 0x79, 0xed, 0x89, 0x32, 0x74, 0x2d, 0xf0, 0xed, 0x53,
|
||||
0x0c, 0x66, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60,
|
||||
0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x58, 0xfb, 0x94, 0x1a, 0xef, 0x95,
|
||||
0xa2, 0x59, 0x43, 0xb3, 0xfb, 0x5f, 0x25, 0x10, 0xa0, 0xdf, 0x3f, 0xe4,
|
||||
0x4c, 0x58, 0xc9, 0x5e, 0x0a, 0xb8, 0x04, 0x87, 0x29, 0x75, 0x68, 0xab,
|
||||
0x97, 0x71, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60,
|
||||
0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x53, 0x91, 0xc3, 0xa2, 0xfb, 0x11,
|
||||
0x21, 0x02, 0xa6, 0xaa, 0x1e, 0xdc, 0x25, 0xae, 0x77, 0xe1, 0x9f, 0x5d,
|
||||
0x6f, 0x09, 0xcd, 0x09, 0xee, 0xb2, 0x50, 0x99, 0x22, 0xbf, 0xcd, 0x59,
|
||||
0x92, 0xea, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60,
|
||||
0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0xd6, 0x26, 0x15, 0x7e, 0x1d, 0x6a,
|
||||
0x71, 0x8b, 0xc1, 0x24, 0xab, 0x8d, 0xa2, 0x7c, 0xbb, 0x65, 0x07, 0x2c,
|
||||
0xa0, 0x3a, 0x7b, 0x6b, 0x25, 0x7d, 0xbd, 0xcb, 0xbd, 0x60, 0xf6, 0x5e,
|
||||
0xf3, 0xd1, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60,
|
||||
0x28, 0xf4, 0xe7, 0xff, 0x04, 0x00, 0xff, 0xff, 0xff, 0xaa, 0x55, 0x3c,
|
||||
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
|
||||
0x00, 0xc6, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0xff, 0x04, 0x00, 0xaa, 0x55, 0x3c,
|
||||
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
|
||||
0x00, 0xb4, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x28, 0x00, 0x00,
|
||||
0x00, 0x08, 0x00, 0x00, 0x00, 0x16, 0xd6, 0x47, 0x4b, 0xd6, 0xa8, 0x52,
|
||||
0x45, 0x9d, 0x44, 0xcc, 0xad, 0x2e, 0x0f, 0x4c, 0xf9, 0x49, 0x00, 0x6e,
|
||||
0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x41,
|
||||
0x00, 0x74, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74,
|
||||
0x00, 0x4f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xaa, 0x55, 0x3f,
|
||||
0x00, 0x03, 0x00, 0xdd, 0x0c, 0xbb, 0xa2, 0xe4, 0x2e, 0x09, 0xe3, 0xe7,
|
||||
0xc5, 0xf7, 0x96, 0x69, 0xbc, 0x00, 0x21, 0xbd, 0x69, 0x33, 0x33, 0xef,
|
||||
0xad, 0x04, 0xcb, 0x54, 0x80, 0xee, 0x06, 0x83, 0xbb, 0xc5, 0x20, 0x84,
|
||||
0xd9, 0xf7, 0xd2, 0x8b, 0xf3, 0x38, 0xb0, 0xab, 0xa4, 0xad, 0x2d, 0x7c,
|
||||
0x62, 0x79, 0x05, 0xff, 0xe3, 0x4a, 0x3f, 0x04, 0x35, 0x20, 0x70, 0xe3,
|
||||
0xc4, 0xe7, 0x6b, 0xe0, 0x9c, 0xc0, 0x36, 0x75, 0xe9, 0x8a, 0x31, 0xdd,
|
||||
0x8d, 0x70, 0xe5, 0xdc, 0x37, 0xb5, 0x74, 0x46, 0x96, 0x28, 0x5b, 0x87,
|
||||
0x60, 0x23, 0x2c, 0xbf, 0xdc, 0x47, 0xa5, 0x67, 0xf7, 0x51, 0x27, 0x9e,
|
||||
0x72, 0xeb, 0x07, 0xa6, 0xc9, 0xb9, 0x1e, 0x3b, 0x53, 0x35, 0x7c, 0xe5,
|
||||
0xd3, 0xec, 0x27, 0xb9, 0x87, 0x1c, 0xfe, 0xb9, 0xc9, 0x23, 0x09, 0x6f,
|
||||
0xa8, 0x46, 0x91, 0xc1, 0x6e, 0x96, 0x3c, 0x41, 0xd3, 0xcb, 0xa3, 0x3f,
|
||||
0x5d, 0x02, 0x6a, 0x4d, 0xec, 0x69, 0x1f, 0x25, 0x28, 0x5c, 0x36, 0xff,
|
||||
0xfd, 0x43, 0x15, 0x0a, 0x94, 0xe0, 0x19, 0xb4, 0xcf, 0xdf, 0xc2, 0x12,
|
||||
0xe2, 0xc2, 0x5b, 0x27, 0xee, 0x27, 0x78, 0x30, 0x8b, 0x5b, 0x2a, 0x09,
|
||||
0x6b, 0x22, 0x89, 0x53, 0x60, 0x16, 0x2c, 0xc0, 0x68, 0x1d, 0x53, 0xba,
|
||||
0xec, 0x49, 0xf3, 0x9d, 0x61, 0x8c, 0x85, 0x68, 0x09, 0x73, 0x44, 0x5d,
|
||||
0x7d, 0xa2, 0x54, 0x2b, 0xdd, 0x79, 0xf7, 0x15, 0xcf, 0x35, 0x5d, 0x6c,
|
||||
0x1c, 0x2b, 0x5c, 0xce, 0xbc, 0x9c, 0x23, 0x8b, 0x6f, 0x6e, 0xb5, 0x26,
|
||||
0xd9, 0x36, 0x13, 0xc3, 0x4f, 0xd6, 0x27, 0xae, 0xb9, 0x32, 0x3b, 0x41,
|
||||
0x92, 0x2c, 0xe1, 0xc7, 0xcd, 0x77, 0xe8, 0xaa, 0x54, 0x4e, 0xf7, 0x5c,
|
||||
0x0b, 0x04, 0x87, 0x65, 0xb4, 0x43, 0x18, 0xa8, 0xb2, 0xe0, 0x6d, 0x19,
|
||||
0x77, 0xec, 0x5a, 0x24, 0xfa, 0x48, 0x03, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||
0xa3, 0x82, 0x01, 0x43, 0x30, 0x82, 0x01, 0x3f, 0x30, 0x10, 0x06, 0x09,
|
||||
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01, 0x04, 0x03, 0x02,
|
||||
0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
|
||||
0x14, 0xa9, 0x29, 0x02, 0x39, 0x8e, 0x16, 0xc4, 0x97, 0x78, 0xcd, 0x90,
|
||||
0xf9, 0x9e, 0x4f, 0x9a, 0xe1, 0x7c, 0x55, 0xaf, 0x53, 0x30, 0x19, 0x06,
|
||||
0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04, 0x0c,
|
||||
0x1e, 0x0a, 0x00, 0x53, 0x00, 0x75, 0x00, 0x62, 0x00, 0x43, 0x00, 0x41,
|
||||
0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01,
|
||||
0x86, 0x30, 0x0f, 0xc4, 0xe8, 0xb5, 0x8a, 0xbf, 0xad, 0x57, 0x26, 0xb0,
|
||||
0x26, 0xc3, 0xea, 0xe7, 0xfb, 0x57, 0x7a, 0x44, 0x02, 0x5d, 0x07, 0x0d,
|
||||
0xda, 0x4a, 0xe5, 0x74, 0x2a, 0xe6, 0xb0, 0x0f, 0xec, 0x6d, 0xeb, 0xec,
|
||||
0x7f, 0xb9, 0xe3, 0x5a, 0x63, 0x32, 0x7c, 0x11, 0x17, 0x4f, 0x0e, 0xe3,
|
||||
0x0b, 0xa7, 0x38, 0x15, 0x93, 0x8e, 0xc6, 0xf5, 0xe0, 0x84, 0xb1, 0x9a,
|
||||
0x9b, 0x2c, 0xe7, 0xf5, 0xb7, 0x91, 0xd6, 0x09, 0xe1, 0xe2, 0xc0, 0x04,
|
||||
0xa8, 0xac, 0x30, 0x1c, 0xdf, 0x48, 0xf3, 0x06, 0x50, 0x9a, 0x64, 0xa7,
|
||||
0x51, 0x7f, 0xc8, 0x85, 0x4f, 0x8f, 0x20, 0x86, 0xce, 0xfe, 0x2f, 0xe1,
|
||||
0x9f, 0xff, 0x82, 0xc0, 0xed, 0xe9, 0xcd, 0xce, 0xf4, 0x53, 0x6a, 0x62,
|
||||
0x3a, 0x0b, 0x43, 0xb9, 0xe2, 0x25, 0xfd, 0xfe, 0x05, 0xf9, 0xd4, 0xc4,
|
||||
0x14, 0xab, 0x11, 0xe2, 0x23, 0x89, 0x8d, 0x70, 0xb7, 0xa4, 0x1d, 0x4d,
|
||||
0xec, 0xae, 0xe5, 0x9c, 0xfa, 0x16, 0xc2, 0xd7, 0xc1, 0xcb, 0xd4, 0xe8,
|
||||
0xc4, 0x2f, 0xe5, 0x99, 0xee, 0x24, 0x8b, 0x03, 0xec, 0x8d, 0xf2, 0x8b,
|
||||
0xea, 0xc3, 0x4a, 0xfb, 0x43, 0x11, 0x12, 0x0b, 0x7e, 0xb5, 0x47, 0x92,
|
||||
0x6c, 0xdc, 0xe6, 0x04, 0x89, 0xeb, 0xf5, 0x33, 0x04, 0xeb, 0x10, 0x01,
|
||||
0x2a, 0x71, 0xe5, 0xf9, 0x83, 0x13, 0x3c, 0xff, 0x25, 0x09, 0x2f, 0x68,
|
||||
0x76, 0x46, 0xff, 0xba, 0x4f, 0xbe, 0xdc, 0xad, 0x71, 0x2a, 0x58, 0xaa,
|
||||
0xfb, 0x0e, 0xd2, 0x79, 0x3d, 0xe4, 0x9b, 0x65, 0x3b, 0xcc, 0x29, 0x2a,
|
||||
0x9f, 0xfc, 0x72, 0x59, 0xa2, 0xeb, 0xae, 0x92, 0xef, 0xf6, 0x35, 0x13,
|
||||
0x80, 0xc6, 0x02, 0xec, 0xe4, 0x5f, 0xcc, 0x9d, 0x76, 0xcd, 0xef, 0x63,
|
||||
0x92, 0xc1, 0xaf, 0x79, 0x40, 0x84, 0x79, 0x87, 0x7f, 0xe3, 0x52, 0xa8,
|
||||
0xe8, 0x9d, 0x7b, 0x07, 0x69, 0x8f, 0x15, 0x02, 0x03, 0x01, 0x00, 0x01,
|
||||
0xa3, 0x82, 0x01, 0x4f, 0x30, 0x82, 0x01, 0x4b, 0x30, 0x10, 0x06, 0x09,
|
||||
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01, 0x04, 0x03, 0x02,
|
||||
0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
|
||||
0x14, 0x62, 0xfc, 0x43, 0xcd, 0xa0, 0x3e, 0xa4, 0xcb, 0x67, 0x12, 0xd2,
|
||||
0x5b, 0xd9, 0x55, 0xac, 0x7b, 0xcc, 0xb6, 0x8a, 0x5f, 0x30, 0x19, 0x06,
|
||||
0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04, 0x0c,
|
||||
0x1e, 0x0a, 0x00, 0x53, 0x00, 0x75, 0x00, 0x62, 0x00, 0x43, 0x00, 0x41,
|
||||
0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01,
|
||||
0x86, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
|
||||
0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1f, 0x06, 0xff, 0x04, 0x00,
|
||||
0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0e, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4,
|
||||
0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b,
|
||||
0x35, 0x08, 0x42, 0xff, 0x30, 0xcc, 0xce, 0xf7, 0x76, 0x0c, 0xad, 0x10,
|
||||
0x68, 0x58, 0x35, 0x29, 0x46, 0x32, 0x76, 0x27, 0x7c, 0xef, 0x12, 0x41,
|
||||
0x27, 0x42, 0x1b, 0x4a, 0xaa, 0x6d, 0x81, 0x38, 0x48, 0x59, 0x13, 0x55,
|
||||
0xf3, 0xe9, 0x58, 0x34, 0xa6, 0x16, 0x0b, 0x82, 0xaa, 0x5d, 0xad, 0x82,
|
||||
0xda, 0x80, 0x83, 0x41, 0x06, 0x8f, 0xb4, 0x1d, 0xf2, 0x03, 0xb9, 0xf3,
|
||||
0x1a, 0x5d, 0x1b, 0xf1, 0x50, 0x90, 0xf9, 0xb3, 0x55, 0x84, 0x42, 0x28,
|
||||
0x1c, 0x20, 0xbd, 0xb2, 0xae, 0x51, 0x14, 0xc5, 0xc0, 0xac, 0x97, 0x95,
|
||||
0x21, 0x1c, 0x90, 0xdb, 0x0f, 0xfc, 0x77, 0x9e, 0x95, 0x73, 0x91, 0x88,
|
||||
0xca, 0xbd, 0xbd, 0x52, 0xb9, 0x05, 0x50, 0x0d, 0xdf, 0x57, 0x9e, 0xa0,
|
||||
0x61, 0xed, 0x0d, 0xe5, 0x6d, 0x25, 0xd9, 0x40, 0x0f, 0x17, 0x40, 0xc8,
|
||||
0xce, 0xa3, 0x4a, 0xc2, 0x4d, 0xaf, 0x9a, 0x12, 0x1d, 0x08, 0x54, 0x8f,
|
||||
0xbd, 0xc7, 0xbc, 0xb9, 0x2b, 0x3d, 0x49, 0x2b, 0x1f, 0x32, 0xfc, 0x6a,
|
||||
0x21, 0x69, 0x4f, 0x9b, 0xc8, 0x7e, 0x42, 0x34, 0xfc, 0x36, 0x06, 0x17,
|
||||
0x8b, 0x8f, 0x20, 0x40, 0xc0, 0xb3, 0x9a, 0x25, 0x75, 0x27, 0xcd, 0xc9,
|
||||
0x03, 0xa3, 0xf6, 0x5d, 0xd1, 0xe7, 0x36, 0x54, 0x7a, 0xb9, 0x50, 0xb5,
|
||||
0xd3, 0x12, 0xd1, 0x07, 0xbf, 0xbb, 0x74, 0xdf, 0xdc, 0x1e, 0x8f, 0x80,
|
||||
0xd5, 0xed, 0x18, 0xf4, 0x2f, 0x14, 0x16, 0x6b, 0x2f, 0xde, 0x66, 0x8c,
|
||||
0xb0, 0x23, 0xe5, 0xc7, 0x84, 0xd8, 0xed, 0xea, 0xc1, 0x33, 0x82, 0xad,
|
||||
0x56, 0x4b, 0x18, 0x2d, 0xf1, 0x68, 0x95, 0x07, 0xcd, 0xcf, 0xf0, 0x72,
|
||||
0xf0, 0xae, 0xbb, 0xdd, 0x86, 0x85, 0x98, 0x2c, 0x21, 0x4c, 0x33, 0x2b,
|
||||
0xf0, 0x0f, 0x4a, 0xf0, 0x68, 0x87, 0xb5, 0x92, 0x55, 0x32, 0x75, 0xa1,
|
||||
0x6a, 0x82, 0x6a, 0x3c, 0xa3, 0x25, 0x11, 0xa4, 0xed, 0xad, 0xd7, 0x04,
|
||||
0xae, 0xcb, 0xd8, 0x40, 0x59, 0xa0, 0x84, 0xd1, 0x95, 0x4c, 0x62, 0x91,
|
||||
0x22, 0x1a, 0x74, 0x1d, 0x8c, 0x3d, 0x47, 0x0e, 0x44, 0xa6, 0xe4, 0xb0,
|
||||
0x9b, 0x34, 0x35, 0xb1, 0xfa, 0xb6, 0x53, 0xa8, 0x2c, 0x81, 0xec, 0xa4,
|
||||
0x05, 0x71, 0xc8, 0x9d, 0xb8, 0xba, 0xe8, 0x1b, 0x44, 0x66, 0xe4, 0x47,
|
||||
0x54, 0x0e, 0x8e, 0x56, 0x7f, 0xb3, 0x9f, 0x16, 0x98, 0xb2, 0x86, 0xd0,
|
||||
0x68, 0x3e, 0x90, 0x23, 0xb5, 0x2f, 0x5e, 0x8f, 0x50, 0x85, 0x8d, 0xc6,
|
||||
0x8d, 0x82, 0x5f, 0x41, 0xa1, 0xf4, 0x2e, 0x0d, 0xe0, 0x99, 0xd2, 0x6c,
|
||||
0x75, 0xe4, 0xb6, 0x69, 0xb5, 0x21, 0x86, 0xfa, 0x07, 0xd1, 0xf6, 0xe2,
|
||||
0x4d, 0xd1, 0xda, 0xad, 0x2c, 0x77, 0x53, 0x1e, 0x25, 0x32, 0x37, 0xc7,
|
||||
0x6c, 0x52, 0x72, 0x95, 0x86, 0xb0, 0xf1, 0x35, 0x61, 0x6a, 0x19, 0xf5,
|
||||
0xb2, 0x3b, 0x81, 0x50, 0x56, 0xa6, 0x32, 0x2d, 0xfe, 0xa2, 0x89, 0xf9,
|
||||
0x42, 0x86, 0x27, 0x18, 0x55, 0xa1, 0x82, 0xca, 0x5a, 0x9b, 0xf8, 0x30,
|
||||
0x98, 0x54, 0x14, 0xa6, 0x47, 0x96, 0x25, 0x2f, 0xc8, 0x26, 0xe4, 0x41,
|
||||
0x94, 0x1a, 0x5c, 0x02, 0x3f, 0xe5, 0x96, 0xe3, 0x85, 0x5b, 0x3c, 0x3e,
|
||||
0x3f, 0xbb, 0x47, 0x16, 0x72, 0x55, 0xe2, 0x25, 0x22, 0xb1, 0xd9, 0x7b,
|
||||
0xe7, 0x03, 0x06, 0x2a, 0xa3, 0xf7, 0x1e, 0x90, 0x46, 0xc3, 0x00, 0x0d,
|
||||
0xd6, 0x19, 0x89, 0xe3, 0x0e, 0x35, 0x27, 0x62, 0x03, 0x71, 0x15, 0xa6,
|
||||
0xef, 0xd0, 0x27, 0xa0, 0xa0, 0x59, 0x37, 0x60, 0xf8, 0x38, 0x94, 0xb8,
|
||||
0xe0, 0x78, 0x70, 0xf8, 0xba, 0x4c, 0x86, 0x87, 0x94, 0xf6, 0xe0, 0xae,
|
||||
0x02, 0x45, 0xee, 0x65, 0xc2, 0xb6, 0xa3, 0x7e, 0x69, 0x16, 0x75, 0x07,
|
||||
0x92, 0x9b, 0xf5, 0xa6, 0xbc, 0x59, 0x83, 0x58, 0xff, 0xff, 0xff, 0xaa,
|
||||
0x55, 0x3c, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe4, 0x07, 0x09, 0x10, 0x10, 0x36, 0x05, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
|
||||
0x00, 0x02, 0x02, 0x09, 0x00, 0xfe, 0xf5, 0x88, 0xe8, 0xf3, 0x96, 0xc0,
|
||||
0xf1, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
|
||||
0x01, 0x0b, 0x05, 0x00, 0x30, 0x51, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03,
|
||||
0x55, 0x04, 0x03, 0x13, 0x22, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74,
|
||||
0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74,
|
||||
0x20, 0x28, 0x50, 0x4b, 0x2f, 0x4b, 0x45, 0x4b, 0x20, 0x6b, 0x65, 0x79,
|
||||
0x20, 0x31, 0x29, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86, 0x48,
|
||||
0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63, 0x61,
|
||||
0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33,
|
||||
0x31, 0x31, 0x31, 0x31, 0x35, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x33, 0x37,
|
||||
0x31, 0x30, 0x32, 0x35, 0x31, 0x31, 0x31, 0x35, 0x33, 0x37, 0x5a, 0x30,
|
||||
0x51, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x22,
|
||||
0x52, 0xff, 0xff, 0xaa, 0x55, 0x3f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x61,
|
||||
0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98,
|
||||
0x03, 0x2b, 0x8c, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x30,
|
||||
0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00,
|
||||
0x00, 0x2c, 0x00, 0x55, 0x00, 0x69, 0x00, 0x41, 0x00, 0x70, 0x00, 0x70,
|
||||
0x00, 0x00, 0x00, 0x04, 0x07, 0x14, 0xd4, 0x84, 0x88, 0xf5, 0x14, 0x94,
|
||||
0x18, 0x02, 0xca, 0x2a, 0x3c, 0xfb, 0x2a, 0x92, 0x1c, 0x0c, 0xd7, 0xa0,
|
||||
0xd1, 0xf1, 0xe8, 0x52, 0x66, 0xa8, 0xee, 0xa2, 0xb5, 0x75, 0x7a, 0x90,
|
||||
0x00, 0xaa, 0x2d, 0xa4, 0x76, 0x5a, 0xea, 0x79, 0xb7, 0xb9, 0x37, 0x6a,
|
||||
0x51, 0x7b, 0x10, 0x64, 0xf6, 0xe1, 0x64, 0xf2, 0x02, 0x67, 0xbe, 0xf7,
|
||||
0xa8, 0x1b, 0x78, 0xbd, 0xba, 0xce, 0x88, 0x58, 0x64, 0x0c, 0xd6, 0x57,
|
||||
0xc8, 0x19, 0xa3, 0x5f, 0x05, 0xd6, 0xdb, 0xc6, 0xd0, 0x69, 0xce, 0x48,
|
||||
0x4b, 0x32, 0xb7, 0xeb, 0x5d, 0xd2, 0x30, 0xf5, 0xc0, 0xf5, 0xb8, 0xba,
|
||||
0x78, 0x07, 0xa3, 0x2b, 0xfe, 0x9b, 0xdb, 0x34, 0x56, 0x84, 0xec, 0x82,
|
||||
0xca, 0xae, 0x41, 0x25, 0x70, 0x9c, 0x6b, 0xe9, 0xfe, 0x90, 0x0f, 0xd7,
|
||||
0x96, 0x1f, 0xe5, 0xe7, 0x94, 0x1f, 0xb2, 0x2a, 0x0c, 0x8d, 0x4b, 0xff,
|
||||
0x28, 0x29, 0x10, 0x7b, 0xf7, 0xd7, 0x7c, 0xa5, 0xd1, 0x76, 0xb9, 0x05,
|
||||
0xc8, 0x79, 0xed, 0x0f, 0x90, 0x92, 0x9c, 0xc2, 0xfe, 0xdf, 0x6f, 0x7e,
|
||||
0x6c, 0x0f, 0x7b, 0xd4, 0xc1, 0x45, 0xdd, 0x34, 0x51, 0x96, 0x39, 0x0f,
|
||||
0xe5, 0x5e, 0x56, 0xd8, 0x18, 0x05, 0x96, 0xf4, 0x07, 0xa6, 0x42, 0xb3,
|
||||
0xa0, 0x77, 0xfd, 0x08, 0x19, 0xf2, 0x71, 0x56, 0xcc, 0x9f, 0x86, 0x23,
|
||||
0xa4, 0x87, 0xcb, 0xa6, 0xfd, 0x58, 0x7e, 0xd4, 0x69, 0x67, 0x15, 0x91,
|
||||
0x7e, 0x81, 0xf2, 0x7f, 0x13, 0xe5, 0x0d, 0x8b, 0x8a, 0x3c, 0x87, 0x84,
|
||||
0xeb, 0xe3, 0xce, 0xbd, 0x43, 0xe5, 0xad, 0x2d, 0x84, 0x93, 0x8e, 0x6a,
|
||||
0x2b, 0x5a, 0x7c, 0x44, 0xfa, 0x52, 0xaa, 0x81, 0xc8, 0x2d, 0x1c, 0xbb,
|
||||
0xe0, 0x52, 0xdf, 0x00, 0x11, 0xf8, 0x9a, 0x3d, 0xc1, 0x60, 0xb0, 0xe1,
|
||||
0x33, 0xb5, 0xa3, 0x88, 0xd1, 0x65, 0x19, 0x0a, 0x1a, 0xe7, 0xac, 0x7c,
|
||||
0xa4, 0xc1, 0x82, 0x87, 0x4e, 0x38, 0xb1, 0x2f, 0x0d, 0xc5, 0x14, 0x87,
|
||||
0x6f, 0xfd, 0x8d, 0x2e, 0xbc, 0x39, 0xb6, 0xe7, 0xe6, 0xc3, 0xe0, 0xe4,
|
||||
0xcd, 0x27, 0x84, 0xef, 0x94, 0x42, 0xef, 0x29, 0x8b, 0x90, 0x46, 0x41,
|
||||
0x3b, 0x81, 0x1b, 0x67, 0xd8, 0xf9, 0x43, 0x59, 0x65, 0xcb, 0x0d, 0xbc,
|
||||
0xfd, 0x00, 0x92, 0x4f, 0xf4, 0x75, 0x3b, 0xa7, 0xa9, 0x24, 0xfc, 0x50,
|
||||
0x41, 0x40, 0x79, 0xe0, 0x2d, 0x4f, 0x0a, 0x6a, 0x27, 0x76, 0x6e, 0x52,
|
||||
0xed, 0x96, 0x69, 0x7b, 0xaf, 0x0f, 0xf7, 0x87, 0x05, 0xd0, 0x45, 0xc2,
|
||||
0xad, 0x53, 0x14, 0x81, 0x1f, 0xfb, 0x30, 0x04, 0xaa, 0x37, 0x36, 0x61,
|
||||
0xda, 0x4a, 0x69, 0x1b, 0x34, 0xd8, 0x68, 0xed, 0xd6, 0x02, 0xcf, 0x6c,
|
||||
0x94, 0x0c, 0xd3, 0xcf, 0x6c, 0x22, 0x79, 0xad, 0xb1, 0xf0, 0xbc, 0x03,
|
||||
0xa2, 0x46, 0x60, 0xa9, 0xc4, 0x07, 0xc2, 0x21, 0x82, 0xf1, 0xfd, 0xf2,
|
||||
0xe8, 0x79, 0x32, 0x60, 0xbf, 0xd8, 0xac, 0xa5, 0x22, 0x14, 0x4b, 0xca,
|
||||
0xc1, 0xd8, 0x4b, 0xeb, 0x7d, 0x3f, 0x57, 0x35, 0xb2, 0xe6, 0x4f, 0x75,
|
||||
0xb4, 0xb0, 0x60, 0x03, 0x22, 0x53, 0xae, 0x91, 0x79, 0x1d, 0xd6, 0x9b,
|
||||
0x41, 0x1f, 0x15, 0x86, 0x54, 0x70, 0xb2, 0xde, 0x0d, 0x35, 0x0f, 0x7c,
|
||||
0xb0, 0x34, 0x72, 0xba, 0x97, 0x60, 0x3b, 0xf0, 0x79, 0xeb, 0xa2, 0xb2,
|
||||
0x1c, 0x5d, 0xa2, 0x16, 0xb8, 0x87, 0xc5, 0xe9, 0x1b, 0xf6, 0xb5, 0x97,
|
||||
0x25, 0x6f, 0x38, 0x9f, 0xe3, 0x91, 0xfa, 0x8a, 0x79, 0x98, 0xc3, 0x69,
|
||||
0x0e, 0xb7, 0xa3, 0x1c, 0x20, 0x05, 0x97, 0xf8, 0xca, 0x14, 0xae, 0x00,
|
||||
0xd7, 0xc4, 0xf3, 0xc0, 0x14, 0x10, 0x75, 0x6b, 0x34, 0xa0, 0x1b, 0xb5,
|
||||
0x99, 0x60, 0xf3, 0x5c, 0xb0, 0xc5, 0x57, 0x4e, 0x36, 0xd2, 0x32, 0x84,
|
||||
0xbf, 0x9e, 0xaa, 0x55, 0x3f, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x07, 0x09, 0x10, 0x10, 0x36,
|
||||
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x03, 0x00, 0x00, 0x61, 0xdf,
|
||||
0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xd0, 0x63, 0xec,
|
||||
0x28, 0xf6, 0x7e, 0xba, 0x53, 0xf1, 0x64, 0x2d, 0xbf, 0x7d, 0xff, 0x33,
|
||||
0xc6, 0xa3, 0x2a, 0xdd, 0x86, 0x9f, 0x60, 0x13, 0xfe, 0x16, 0x2e, 0x2c,
|
||||
0x32, 0xf1, 0xcb, 0xe5, 0x6d, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x29, 0xc6, 0xeb,
|
||||
0x52, 0xb4, 0x3c, 0x3a, 0xa1, 0x8b, 0x2c, 0xd8, 0xed, 0x6e, 0xa8, 0x60,
|
||||
0x7c, 0xef, 0x3c, 0xfa, 0xe1, 0xba, 0xfe, 0x11, 0x65, 0x75, 0x5c, 0xf2,
|
||||
0xe6, 0x14, 0x84, 0x4a, 0x44, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x90, 0xfb, 0xe7,
|
||||
0x0e, 0x69, 0xd6, 0x33, 0x40, 0x8d, 0x3e, 0x17, 0x0c, 0x68, 0x32, 0xdb,
|
||||
0xb2, 0xd2, 0x09, 0xe0, 0x27, 0x25, 0x27, 0xdf, 0xb6, 0x3d, 0x49, 0xd2,
|
||||
0x95, 0x72, 0xa6, 0xf4, 0x4c, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x10, 0x6f, 0xac,
|
||||
0xea, 0xcf, 0xec, 0xfd, 0x4e, 0x30, 0x3b, 0x74, 0xf4, 0x80, 0xa0, 0x80,
|
||||
0x98, 0xe2, 0xd0, 0x80, 0x2b, 0x93, 0x6f, 0x8e, 0xc7, 0x74, 0xce, 0x21,
|
||||
0xf3, 0x16, 0x86, 0x68, 0x9c, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x17, 0x4e, 0x3a,
|
||||
0x0b, 0x5b, 0x43, 0xc6, 0xa6, 0x07, 0xbb, 0xd3, 0x40, 0x4f, 0x05, 0x34,
|
||||
0x1e, 0x3d, 0xcf, 0x39, 0x62, 0x67, 0xce, 0x94, 0xf8, 0xb5, 0x0e, 0x2e,
|
||||
0x23, 0xa9, 0xda, 0x92, 0x0c, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x2b, 0x99, 0xcf,
|
||||
0x26, 0x42, 0x2e, 0x92, 0xfe, 0x36, 0x5f, 0xbf, 0x4b, 0xc3, 0x0d, 0x27,
|
||||
0x08, 0x6c, 0x9e, 0xe1, 0x4b, 0x7a, 0x6f, 0xff, 0x44, 0xfb, 0x2f, 0x6b,
|
||||
0x90, 0x01, 0x69, 0x99, 0x39, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x2e, 0x70, 0x91,
|
||||
0x67, 0x86, 0xa6, 0xf7, 0x73, 0x51, 0x1f, 0xa7, 0x18, 0x1f, 0xab, 0x0f,
|
||||
0x1d, 0x70, 0xb5, 0x57, 0xc6, 0x32, 0x2e, 0xa9, 0x23, 0xb2, 0xa8, 0xd3,
|
||||
0xb9, 0x2b, 0x51, 0xaf, 0x7d, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x3f, 0xce, 0x9b,
|
||||
0x9f, 0xdf, 0x3e, 0xf0, 0x9d, 0x54, 0x52, 0xb0, 0xf9, 0x5e, 0xe4, 0x81,
|
||||
0xc2, 0xb7, 0xf0, 0x6d, 0x74, 0x3a, 0x73, 0x79, 0x71, 0x55, 0x8e, 0x70,
|
||||
0x13, 0x6a, 0xce, 0x3e, 0x73, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x47, 0xcc, 0x08,
|
||||
0x61, 0x27, 0xe2, 0x06, 0x9a, 0x86, 0xe0, 0x3a, 0x6b, 0xef, 0x2c, 0xd4,
|
||||
0x10, 0xf8, 0xc5, 0x5a, 0x6d, 0x6b, 0xdb, 0x36, 0x21, 0x68, 0xc3, 0x1b,
|
||||
0x2c, 0xe3, 0x2a, 0x5a, 0xdf, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x71, 0xf2, 0x90,
|
||||
0x6f, 0xd2, 0x22, 0x49, 0x7e, 0x54, 0xa3, 0x46, 0x62, 0xab, 0x24, 0x97,
|
||||
0xfc, 0xc8, 0x10, 0x20, 0x77, 0x0f, 0xf5, 0x13, 0x68, 0xe9, 0xe3, 0xd9,
|
||||
0xbf, 0xcb, 0xfd, 0x63, 0x75, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x82, 0xdb, 0x3b,
|
||||
0xce, 0xb4, 0xf6, 0x08, 0x43, 0xce, 0x9d, 0x97, 0xc3, 0xd1, 0x87, 0xcd,
|
||||
0x9b, 0x59, 0x41, 0xcd, 0x3d, 0xe8, 0x10, 0x0e, 0x58, 0x6f, 0x2b, 0xda,
|
||||
0x56, 0x37, 0x57, 0x5f, 0x67, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x8a, 0xd6, 0x48,
|
||||
0x59, 0xf1, 0x95, 0xb5, 0xf5, 0x8d, 0xaf, 0xaa, 0x94, 0x0b, 0x6a, 0x61,
|
||||
0x67, 0xac, 0xd6, 0x7a, 0x88, 0x6e, 0x8f, 0x46, 0x93, 0x64, 0x17, 0x72,
|
||||
0x21, 0xc5, 0x59, 0x45, 0xb9, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x8d, 0x8e, 0xa2,
|
||||
0x89, 0xcf, 0xe7, 0x0a, 0x1c, 0x07, 0xab, 0x73, 0x65, 0xcb, 0x28, 0xee,
|
||||
0x51, 0xed, 0xd3, 0x3c, 0xf2, 0x50, 0x6d, 0xe8, 0x88, 0xfb, 0xad, 0xd6,
|
||||
0x0e, 0xbf, 0x80, 0x48, 0x1c, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0xae, 0xeb, 0xae,
|
||||
0x31, 0x51, 0x27, 0x12, 0x73, 0xed, 0x95, 0xaa, 0x2e, 0x67, 0x11, 0x39,
|
||||
0xed, 0x31, 0xa9, 0x85, 0x67, 0x30, 0x3a, 0x33, 0x22, 0x98, 0xf8, 0x37,
|
||||
0x09, 0xa9, 0xd5, 0x5a, 0xa1, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0xc4, 0x09, 0xbd,
|
||||
0xac, 0x47, 0x75, 0xad, 0xd8, 0xdb, 0x92, 0xaa, 0x22, 0xb5, 0xb7, 0x18,
|
||||
0xfb, 0x8c, 0x94, 0xa1, 0x46, 0x2c, 0x1f, 0xe9, 0xa4, 0x16, 0xb9, 0x5d,
|
||||
0x8a, 0x33, 0x88, 0xc2, 0xfc, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0xc6, 0x17, 0xc1,
|
||||
0xa8, 0xb1, 0xee, 0x2a, 0x81, 0x1c, 0x28, 0xb5, 0xa8, 0x1b, 0x4c, 0x83,
|
||||
0xd7, 0xc9, 0x8b, 0x5b, 0x0c, 0x27, 0x28, 0x1d, 0x61, 0x02, 0x07, 0xeb,
|
||||
0xe6, 0x92, 0xc2, 0x96, 0x7f, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0xc9, 0x0f, 0x33,
|
||||
0x66, 0x17, 0xb8, 0xe7, 0xf9, 0x83, 0x97, 0x54, 0x13, 0xc9, 0x97, 0xf1,
|
||||
0x0b, 0x73, 0xeb, 0x26, 0x7f, 0xd8, 0xa1, 0x0c, 0xb9, 0xe3, 0xbd, 0xbf,
|
||||
0xc6, 0x67, 0xab, 0xdb, 0x8b, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x64, 0x57, 0x5b,
|
||||
0xd9, 0x12, 0x78, 0x9a, 0x2e, 0x14, 0xad, 0x56, 0xf6, 0x34, 0x1f, 0x52,
|
||||
0xaf, 0x6b, 0xf8, 0x0c, 0xf9, 0x44, 0x00, 0x78, 0x59, 0x75, 0xe9, 0xf0,
|
||||
0x4e, 0x2d, 0x64, 0xd7, 0x45, 0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32,
|
||||
0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x45, 0xc7, 0xc8,
|
||||
0xae, 0x75, 0x0a, 0xcf, 0xbb, 0x48, 0xfc, 0x37, 0x52, 0x7d, 0x64, 0x12,
|
||||
0xdd, 0x64, 0x4d, 0xae, 0xd8, 0x91, 0x3c, 0xcd, 0x8a, 0x24, 0xc9, 0x4d,
|
||||
0x85, 0x69, 0x67, 0xdf, 0x8e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x02, 0x01, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11,
|
||||
0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x43, 0x00, 0x6f, 0x00,
|
||||
0x6e, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00,
|
||||
0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00,
|
||||
0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x03, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7f, 0x01, 0x04, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41,
|
||||
0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f,
|
||||
0x02, 0x01, 0x0c, 0x00, 0xd0, 0x6c, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4,
|
||||
0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b,
|
||||
0x8c, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x30, 0x00, 0x30,
|
||||
0x00, 0x30, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20,
|
||||
0x00, 0x55, 0x00, 0x45, 0x00, 0x46, 0x00, 0x49, 0x00, 0x20, 0x00, 0x51,
|
||||
0x00, 0x45, 0x00, 0x4d, 0x00, 0x55, 0x00, 0x20, 0x00, 0x44, 0x00, 0x56,
|
||||
0x00, 0x44, 0x00, 0x2d, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x20,
|
||||
0x00, 0x51, 0x00, 0x4d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31,
|
||||
0x00, 0x31, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0,
|
||||
0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x02,
|
||||
0x1f, 0x03, 0x12, 0x0a, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0x7f,
|
||||
0xff, 0x04, 0x00, 0x4e, 0xac, 0x08, 0x81, 0x11, 0x9f, 0x59, 0x4d, 0x85,
|
||||
0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2, 0xff, 0xff, 0xaa, 0x55, 0x3c,
|
||||
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
|
||||
0x00, 0x06, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2,
|
||||
0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x42, 0x00, 0x6f,
|
||||
0x00, 0x6f, 0x00, 0x74, 0x00, 0x4f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x65,
|
||||
0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff,
|
||||
0xff, 0xaa, 0x55, 0x3d, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x12, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4,
|
||||
0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b,
|
||||
0x8c, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x30, 0x00, 0x30,
|
||||
0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x45, 0x00, 0x46, 0x00, 0x49, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e,
|
||||
0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6c,
|
||||
0x00, 0x20, 0x00, 0x53, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c,
|
||||
0x00, 0x00, 0x00, 0x04, 0x07, 0x14, 0x00, 0xc9, 0xbd, 0xb8, 0x7c, 0xeb,
|
||||
0xf8, 0x34, 0x4f, 0xaa, 0xea, 0x3e, 0xe4, 0xaf, 0x65, 0x16, 0xa1, 0x04,
|
||||
0x06, 0x14, 0x00, 0x83, 0xa5, 0x04, 0x7c, 0x3e, 0x9e, 0x1c, 0x4f, 0xad,
|
||||
0x65, 0xe0, 0x52, 0x68, 0xd0, 0xb4, 0xd1, 0x7f, 0xff, 0x04, 0x00, 0xff,
|
||||
0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00, 0x01, 0xff, 0xaa, 0x55, 0x3f,
|
||||
0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xe4, 0x07, 0x09, 0x10, 0x10, 0x36, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
|
||||
0x00, 0x47, 0x0c, 0x00, 0x00, 0xcb, 0xb2, 0x19, 0xd7, 0x3a, 0x3d, 0x96,
|
||||
0x45, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f, 0x64, 0x00, 0x62,
|
||||
0x00, 0x00, 0x00, 0xa1, 0x59, 0xc0, 0xa5, 0xe4, 0x94, 0xa7, 0x4a, 0x87,
|
||||
0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72, 0x07, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xeb, 0x05, 0x00, 0x00, 0xbd, 0x9a, 0xfa, 0x77, 0x59,
|
||||
0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b, 0x30,
|
||||
0x82, 0x05, 0xd7, 0x30, 0x82, 0x03, 0xbf, 0xa0, 0x03, 0x02, 0x01, 0x02,
|
||||
0x02, 0x0a, 0x61, 0x07, 0x76, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
|
||||
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
|
||||
0x0b, 0x05, 0x00, 0x30, 0x81, 0x88, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
|
||||
0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06,
|
||||
0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e,
|
||||
0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04,
|
||||
0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e,
|
||||
0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x6c, 0x00, 0x00, 0x00, 0x61, 0xdf,
|
||||
0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03,
|
||||
0x2b, 0x8c, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x30, 0x00,
|
||||
0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x20, 0x00, 0x55, 0x00, 0x45, 0x00, 0x46, 0x00, 0x49, 0x00, 0x20, 0x00,
|
||||
0x51, 0x00, 0x45, 0x00, 0x4d, 0x00, 0x55, 0x00, 0x20, 0x00, 0x44, 0x00,
|
||||
0x56, 0x00, 0x44, 0x00, 0x2d, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x4d, 0x00,
|
||||
0x20, 0x00, 0x51, 0x00, 0x4d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00,
|
||||
0x31, 0x00, 0x31, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00,
|
||||
0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00,
|
||||
0x02, 0x1f, 0x03, 0x12, 0x0a, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00,
|
||||
0x7f, 0xff, 0x04, 0x00, 0x4e, 0xac, 0x08, 0x81, 0x11, 0x9f, 0x59, 0x4d,
|
||||
0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2, 0xff, 0xff, 0xaa, 0x55,
|
||||
0x3c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00,
|
||||
0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93,
|
||||
0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x42, 0x00,
|
||||
0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x4f, 0x00, 0x72, 0x00, 0x64, 0x00,
|
||||
0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00,
|
||||
0xff, 0xff, 0xaa, 0x55, 0x3d, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x61, 0xdf,
|
||||
0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03,
|
||||
0x2b, 0x8c, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x30, 0x00,
|
||||
0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x2c, 0x00, 0x45, 0x00, 0x46, 0x00, 0x49, 0x00, 0x20, 0x00, 0x49, 0x00,
|
||||
0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x61, 0x00,
|
||||
0x6c, 0x00, 0x20, 0x00, 0x53, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00,
|
||||
0x6c, 0x00, 0x00, 0x00, 0x04, 0x07, 0x14, 0x00, 0xc9, 0xbd, 0xb8, 0x7c,
|
||||
0xeb, 0xf8, 0x34, 0x4f, 0xaa, 0xea, 0x3e, 0xe4, 0xaf, 0x65, 0x16, 0xa1,
|
||||
0x04, 0x06, 0x14, 0x00, 0x83, 0xa5, 0x04, 0x7c, 0x3e, 0x9e, 0x1c, 0x4f,
|
||||
0xad, 0x65, 0xe0, 0x52, 0x68, 0xd0, 0xb4, 0xd1, 0x7f, 0xff, 0x04, 0x00,
|
||||
0xff, 0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00, 0x30, 0x81, 0x91, 0x31,
|
||||
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
|
||||
0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57,
|
||||
0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30,
|
||||
0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d,
|
||||
0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
|
||||
0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20,
|
||||
0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31,
|
||||
0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x4d, 0x69,
|
||||
0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70,
|
||||
0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, 0x68, 0x69, 0x72,
|
||||
0x64, 0x20, 0x50, 0x61, 0x72, 0x74, 0x79, 0x20, 0x4d, 0x61, 0x72, 0x6b,
|
||||
0x65, 0x74, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74,
|
||||
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x36, 0x32, 0x34, 0x32, 0x30,
|
||||
0x34, 0x31, 0x32, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x36, 0x32,
|
||||
0x34, 0x32, 0x30, 0x35, 0x31, 0x32, 0x39, 0x5a, 0x30, 0x81, 0x80, 0x31,
|
||||
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
|
||||
0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57,
|
||||
0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30,
|
||||
0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d,
|
||||
0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
|
||||
0x01, 0x04, 0x14, 0x00, 0x9b, 0x5a, 0x5a, 0x86, 0x5d, 0xb8, 0x4c, 0x47,
|
||||
0x84, 0x55, 0x65, 0xd1, 0xbe, 0x84, 0x4b, 0xe2, 0x03, 0x0e, 0x13, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x03, 0x0a, 0x14, 0x00, 0x53, 0x47, 0xc1, 0xe0, 0xbe,
|
||||
0xf9, 0xd2, 0x11, 0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d, 0x7f,
|
||||
0xff, 0x04, 0x00, 0xff, 0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00, 0x20,
|
||||
0x55, 0x45, 0x46, 0x49, 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, 0x31, 0x31,
|
||||
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
|
||||
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0x08, 0x6c,
|
||||
0x4c, 0xc7, 0x45, 0x09, 0x6a, 0x4b, 0x0c, 0xa4, 0xc0, 0x87, 0x7f, 0x06,
|
||||
0x75, 0x0c, 0x43, 0x01, 0x54, 0x64, 0xe0, 0x16, 0x7f, 0x07, 0xed, 0x92,
|
||||
0x7d, 0x0b, 0xb2, 0x73, 0xbf, 0x0c, 0x0a, 0xc6, 0x4a, 0x45, 0x61, 0xa0,
|
||||
0xc5, 0x16, 0x2d, 0x96, 0xd3, 0xf5, 0x2b, 0xa0, 0xfb, 0x4d, 0x49, 0x9b,
|
||||
0x41, 0x80, 0x90, 0x3c, 0xb9, 0x54, 0xfd, 0xe6, 0xbc, 0xd1, 0x9d, 0xc4,
|
||||
0xa4, 0x18, 0x8a, 0x7f, 0x41, 0x8a, 0x5c, 0x59, 0x83, 0x68, 0x32, 0xbb,
|
||||
0x8c, 0x47, 0xc9, 0xee, 0x71, 0xbc, 0x21, 0x4f, 0x9a, 0x8a, 0x7c, 0xff,
|
||||
0x44, 0x3f, 0x8d, 0x8f, 0x32, 0xb2, 0x26, 0x48, 0xae, 0x75, 0xb5, 0xee,
|
||||
0xc9, 0x4c, 0x1e, 0x4a, 0x19, 0x7e, 0xe4, 0x82, 0x9a, 0x1d, 0x78, 0x77,
|
||||
0x4d, 0x0c, 0xb0, 0xbd, 0xf6, 0x0f, 0xd3, 0x16, 0xd3, 0xbc, 0xfa, 0x2b,
|
||||
0xa5, 0x51, 0x38, 0x5d, 0xf5, 0xfb, 0xba, 0xdb, 0x78, 0x02, 0xdb, 0xff,
|
||||
0xec, 0x0a, 0x1b, 0x96, 0xd5, 0x83, 0xb8, 0x19, 0x13, 0xe9, 0xb6, 0xc0,
|
||||
0x7b, 0x40, 0x7b, 0xe1, 0x1f, 0x28, 0x27, 0xc9, 0xfa, 0xef, 0x56, 0x5e,
|
||||
0x1c, 0xe6, 0x7e, 0x94, 0x7e, 0xc0, 0xf0, 0x44, 0xb2, 0x79, 0x39, 0xe5,
|
||||
0xda, 0xb2, 0x62, 0x8b, 0x4d, 0xbf, 0x38, 0x70, 0xe2, 0x68, 0x24, 0x14,
|
||||
0xc9, 0x33, 0xa4, 0x08, 0x37, 0xd5, 0x58, 0x69, 0x5e, 0xd3, 0x7c, 0xed,
|
||||
0xc1, 0x04, 0x53, 0x08, 0xe7, 0x4e, 0xb0, 0x2a, 0x87, 0x63, 0x08, 0x61,
|
||||
0x6f, 0x63, 0x15, 0x59, 0xea, 0xb2, 0x2b, 0x79, 0xd7, 0x0c, 0x61, 0x67,
|
||||
0x8a, 0x5b, 0xfd, 0x5e, 0xad, 0x87, 0x7f, 0xba, 0x86, 0x67, 0x4f, 0x71,
|
||||
0x58, 0x12, 0x22, 0x04, 0x22, 0x22, 0xce, 0x8b, 0xef, 0x54, 0x71, 0x00,
|
||||
0xce, 0x50, 0x35, 0x58, 0x76, 0x95, 0x08, 0xee, 0x6a, 0xb1, 0xa2, 0x01,
|
||||
0xd5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x76, 0x30, 0x82,
|
||||
0x01, 0x72, 0x30, 0x12, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
|
||||
0x37, 0x15, 0x01, 0x04, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x23,
|
||||
0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x02, 0x04,
|
||||
0x16, 0x04, 0x14, 0xf8, 0xc1, 0x6b, 0xb7, 0x7f, 0x77, 0x53, 0x4a, 0xf3,
|
||||
0x25, 0x37, 0x1d, 0x4e, 0xa1, 0x26, 0x7b, 0x0f, 0x20, 0x70, 0x80, 0x30,
|
||||
0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x13, 0xad,
|
||||
0xbf, 0x43, 0x09, 0xbd, 0x82, 0x70, 0x9c, 0x8c, 0xd5, 0x4f, 0x31, 0x6e,
|
||||
0xd5, 0x22, 0x98, 0x8a, 0x1b, 0xd4, 0x30, 0x19, 0x06, 0x09, 0x2b, 0x06,
|
||||
0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00,
|
||||
0x53, 0x00, 0x75, 0x00, 0x62, 0x00, 0x43, 0x00, 0x41, 0x30, 0x0b, 0x06,
|
||||
0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x0f,
|
||||
0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
|
||||
0x01, 0x01, 0xff, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
|
||||
0x30, 0x16, 0x80, 0x14, 0x45, 0x66, 0x52, 0x43, 0xe1, 0x7e, 0x58, 0x11,
|
||||
0xbf, 0xd6, 0x4e, 0x9e, 0x23, 0x55, 0x08, 0x3b, 0x3a, 0x22, 0x6a, 0xa8,
|
||||
0x30, 0x5c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x55, 0x30, 0x53, 0x30,
|
||||
0x51, 0xa0, 0x4f, 0xa0, 0x4d, 0x86, 0x4b, 0x68, 0x74, 0x74, 0x70, 0x3a,
|
||||
0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73,
|
||||
0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f,
|
||||
0x63, 0x72, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73,
|
||||
0x2f, 0x4d, 0x69, 0x63, 0x43, 0x6f, 0x72, 0x54, 0x68, 0x69, 0x50, 0x61,
|
||||
0x72, 0x4d, 0x61, 0x72, 0x52, 0x6f, 0x6f, 0x5f, 0x32, 0x30, 0x31, 0x30,
|
||||
0x2d, 0x31, 0x30, 0x2d, 0x30, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x60,
|
||||
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x54,
|
||||
0x30, 0x52, 0x30, 0x50, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
|
||||
0x30, 0x02, 0x86, 0x44, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
|
||||
0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x63, 0x65, 0x72,
|
||||
0x74, 0x73, 0x2f, 0x4d, 0x69, 0x63, 0x43, 0x6f, 0x72, 0x54, 0x68, 0x69,
|
||||
0x50, 0x61, 0x72, 0x4d, 0x61, 0x72, 0x52, 0x6f, 0x6f, 0x5f, 0x32, 0x30,
|
||||
0x31, 0x30, 0x2d, 0x31, 0x30, 0x2d, 0x30, 0x35, 0x2e, 0x63, 0x72, 0x74,
|
||||
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
|
||||
0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x92, 0x00,
|
||||
0x00, 0x00, 0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d,
|
||||
0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c, 0x45, 0x00, 0x72, 0x00, 0x72, 0x00,
|
||||
0x4f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00,
|
||||
0xd0, 0x41, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00,
|
||||
0x00, 0x1f, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x01, 0x00, 0x00, 0x00,
|
||||
0x61, 0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0,
|
||||
0x98, 0x03, 0x2b, 0x8c, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00,
|
||||
0x4f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00,
|
||||
0x06, 0x00, 0xff, 0xff, 0xaa, 0x55, 0x3f, 0x00, 0x07, 0x00, 0x82, 0x01,
|
||||
0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
|
||||
0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
|
||||
0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x90, 0x1f, 0x84, 0x7b, 0x8d, 0xbc,
|
||||
0xeb, 0x97, 0x26, 0x82, 0x6d, 0x88, 0xab, 0x8a, 0xc9, 0x8c, 0x68, 0x70,
|
||||
0xf9, 0xdf, 0x4b, 0x07, 0xb2, 0x37, 0x83, 0x0b, 0x02, 0xc8, 0x67, 0x68,
|
||||
0x30, 0x9e, 0xe3, 0xf0, 0xf0, 0x99, 0x4a, 0xb8, 0x59, 0x57, 0xc6, 0x41,
|
||||
0xf6, 0x38, 0x8b, 0xfe, 0x66, 0x4c, 0x49, 0xe9, 0x37, 0x37, 0x92, 0x2e,
|
||||
0x98, 0x01, 0x1e, 0x5b, 0x14, 0x50, 0xe6, 0xa8, 0x8d, 0x25, 0x0d, 0xf5,
|
||||
0x86, 0xe6, 0xab, 0x30, 0xcb, 0x40, 0x16, 0xea, 0x8d, 0x8b, 0x16, 0x86,
|
||||
0x70, 0x43, 0x37, 0xf2, 0xce, 0xc0, 0x91, 0xdf, 0x71, 0x14, 0x8e, 0x99,
|
||||
0x0e, 0x89, 0xb6, 0x4c, 0x6d, 0x24, 0x1e, 0x8c, 0xe4, 0x2f, 0x4f, 0x25,
|
||||
0xd0, 0xba, 0x06, 0xf8, 0xc6, 0xe8, 0x19, 0x18, 0x76, 0x73, 0x1d, 0x81,
|
||||
0x6d, 0xa8, 0xd8, 0x05, 0xcf, 0x3a, 0xc8, 0x7b, 0x28, 0xc8, 0x36, 0xa3,
|
||||
0x16, 0x0d, 0x29, 0x8c, 0x99, 0x9a, 0x68, 0xdc, 0xab, 0xc0, 0x4d, 0x8d,
|
||||
0xbf, 0x5a, 0xbb, 0x2b, 0xa9, 0x39, 0x4b, 0x04, 0x97, 0x1c, 0xf9, 0x36,
|
||||
0xbb, 0xc5, 0x3a, 0x86, 0x04, 0xae, 0xaf, 0xd4, 0x82, 0x7b, 0xe0, 0xab,
|
||||
0xde, 0x49, 0x05, 0x68, 0xfc, 0xf6, 0xae, 0x68, 0x1a, 0x6c, 0x90, 0x4d,
|
||||
0x57, 0x19, 0x3c, 0x64, 0x66, 0x03, 0xf6, 0xc7, 0x52, 0x9b, 0xf7, 0x94,
|
||||
0xcf, 0x93, 0x6a, 0xa1, 0x68, 0xc9, 0xaa, 0xcf, 0x99, 0x6b, 0xbc, 0xaa,
|
||||
0x5e, 0x08, 0xe7, 0x39, 0x1c, 0xf7, 0xf8, 0x0f, 0xba, 0x06, 0x7e, 0xf1,
|
||||
0xcb, 0xe8, 0x76, 0xdd, 0xfe, 0x22, 0xda, 0xad, 0x3a, 0x5e, 0x5b, 0x34,
|
||||
0xea, 0xb3, 0xc9, 0xe0, 0x4d, 0x04, 0x29, 0x7e, 0xb8, 0x60, 0xb9, 0x05,
|
||||
0xef, 0xb5, 0xd9, 0x17, 0x58, 0x56, 0x16, 0x60, 0xb9, 0x30, 0x32, 0xf0,
|
||||
0x36, 0x4a, 0xc3, 0xf2, 0x79, 0x8d, 0x12, 0x40, 0x70, 0xf3, 0x02, 0x03,
|
||||
0x01, 0x00, 0x01, 0xa3, 0x7b, 0x30, 0x79, 0x30, 0x09, 0x06, 0x03, 0x55,
|
||||
0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x2c, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x1f, 0x16, 0x1d, 0x4f,
|
||||
0x70, 0x65, 0x6e, 0x53, 0x53, 0x4c, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72,
|
||||
0x61, 0x74, 0x65, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
||||
0x63, 0x61, 0x74, 0x65, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
|
||||
0x16, 0x04, 0x14, 0x3c, 0xe9, 0x60, 0xe3, 0xff, 0x19, 0xa1, 0x0a, 0x7b,
|
||||
0xa3, 0x42, 0xf4, 0x8d, 0x42, 0x2e, 0xb4, 0xd5, 0x9c, 0x72, 0xec, 0x30,
|
||||
0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
|
||||
0x3c, 0xe9, 0x60, 0xe3, 0xff, 0x19, 0xa1, 0x0a, 0x7b, 0xa3, 0x42, 0xf4,
|
||||
0x8d, 0x42, 0x2e, 0xb4, 0xd5, 0x9c, 0x72, 0xec, 0x30, 0x0d, 0x06, 0x09,
|
||||
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
|
||||
0x82, 0x01, 0x01, 0x00, 0x5c, 0x4d, 0x92, 0x88, 0xb4, 0x82, 0x5f, 0x1d,
|
||||
0xad, 0x8b, 0x11, 0xec, 0xdf, 0x06, 0xa6, 0x7a, 0xa5, 0x2b, 0x9f, 0x37,
|
||||
0x55, 0x0c, 0x8d, 0x6e, 0x05, 0x00, 0xad, 0xb7, 0x0c, 0x41, 0x89, 0x69,
|
||||
0xcf, 0xd6, 0x65, 0x06, 0x9b, 0x51, 0x78, 0xd2, 0xad, 0xc7, 0xbf, 0x9c,
|
||||
0xdc, 0x05, 0x73, 0x7f, 0xe7, 0x1e, 0x39, 0x13, 0xb4, 0xea, 0xb6, 0x30,
|
||||
0x7d, 0x40, 0x75, 0xab, 0x9c, 0x43, 0x0b, 0xdf, 0xb0, 0xc2, 0x1b, 0xbf,
|
||||
0x30, 0xe0, 0xf4, 0xfe, 0xc0, 0xdb, 0x62, 0x21, 0x98, 0xf6, 0xc5, 0xaf,
|
||||
0xde, 0x3b, 0x4f, 0x49, 0x0a, 0xe6, 0x1e, 0xf9, 0x86, 0xb0, 0x3f, 0x0d,
|
||||
0xd6, 0xd4, 0x46, 0x37, 0xdb, 0x54, 0x74, 0x5e, 0xff, 0x11, 0xc2, 0x60,
|
||||
0xc6, 0x70, 0x58, 0xc5, 0x1c, 0x6f, 0xec, 0xb2, 0xd8, 0x6e, 0x6f, 0xc3,
|
||||
0xbc, 0x33, 0x87, 0x38, 0xa4, 0xf3, 0x44, 0x64, 0x9c, 0x34, 0x3b, 0x28,
|
||||
0x94, 0x26, 0x78, 0x27, 0x9f, 0x16, 0x17, 0xe8, 0x3b, 0x69, 0x0a, 0x25,
|
||||
0xa9, 0x73, 0x36, 0x7e, 0x9e, 0x37, 0x5c, 0xec, 0xe8, 0x3f, 0xdb, 0x91,
|
||||
0xf9, 0x12, 0xb3, 0x3d, 0xce, 0xe7, 0xdd, 0x15, 0xc3, 0xae, 0x8c, 0x05,
|
||||
0x20, 0x61, 0x9b, 0x95, 0xde, 0x9b, 0xaf, 0xfa, 0xb1, 0x5c, 0x1c, 0xe5,
|
||||
0x97, 0xe7, 0xc3, 0x34, 0x11, 0x85, 0xf5, 0x8a, 0x27, 0x26, 0xa4, 0x70,
|
||||
0x36, 0xec, 0x0c, 0xf6, 0x83, 0x3d, 0x90, 0xf7, 0x36, 0xf3, 0xf9, 0xf3,
|
||||
0x15, 0xd4, 0x90, 0x62, 0xbe, 0x53, 0xb4, 0xaf, 0xd3, 0x49, 0xaf, 0xef,
|
||||
0xf4, 0x73, 0xe8, 0x7b, 0x76, 0xe4, 0x44, 0x2a, 0x37, 0xba, 0x81, 0xa4,
|
||||
0x99, 0x0c, 0x3a, 0x31, 0x24, 0x71, 0xa0, 0xe4, 0xe4, 0xb7, 0x1a, 0xcb,
|
||||
0x47, 0xe4, 0xaa, 0x22, 0xcf, 0xef, 0x75, 0x61, 0x80, 0xe3, 0x43, 0xb7,
|
||||
0x48, 0x57, 0x73, 0x11, 0x3d, 0x78, 0x9b, 0x69, 0xa1, 0x59, 0xc0, 0xa5,
|
||||
0xe4, 0x94, 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72,
|
||||
0x18, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x05, 0x00, 0x00,
|
||||
0xbd, 0x9a, 0xfa, 0x77, 0x59, 0x03, 0x32, 0x4d, 0xbd, 0x60, 0x28, 0xf4,
|
||||
0xe7, 0x8f, 0x78, 0x4b, 0x30, 0x82, 0x05, 0xe8, 0x30, 0x82, 0x03, 0xd0,
|
||||
0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x61, 0x0a, 0xd1, 0x88, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
|
||||
0x08, 0x01, 0x01, 0x03, 0x0a, 0x14, 0x00, 0x53, 0x47, 0xc1, 0xe0, 0xbe,
|
||||
0xf9, 0xd2, 0x11, 0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d, 0x7f,
|
||||
0x01, 0x04, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03, 0x0a, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x0c,
|
||||
0x00, 0xd0, 0x41, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x13,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x08, 0x01, 0x01, 0x03, 0x0a, 0x14, 0x00, 0x53, 0x47, 0xc1, 0xe0,
|
||||
0xbe, 0xf9, 0xff, 0xaa, 0x55, 0x3c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x61,
|
||||
0xdf, 0xe4, 0x8b, 0xca, 0x93, 0xd2, 0x11, 0xaa, 0x0d, 0x00, 0xe0, 0x98,
|
||||
0x03, 0x2b, 0x8c, 0x45, 0x00, 0x72, 0x00, 0x72, 0x00, 0x4f, 0x00, 0x75,
|
||||
0x00, 0x74, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0c, 0x00, 0xd0, 0x41, 0x03,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x00, 0x00, 0x1f, 0x02,
|
||||
0x01, 0x0c, 0x00, 0xd0, 0x41, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x0e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08, 0x01, 0x01, 0x03, 0x0a, 0x14, 0x00, 0x53, 0x47,
|
||||
0xc1, 0xe0, 0xbe, 0xf9, 0xd2,
|
||||
}
|
@ -10,7 +10,7 @@ replace (
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.18.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0
|
||||
github.com/edgelesssys/constellation/v2 v2.6.0
|
||||
@ -36,7 +36,7 @@ require (
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
|
@ -43,12 +43,12 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 h1:rTnT/Jrcm+figWlYz4Ixzt0SJVR2cMC8lvZcimipiEY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 h1:xGLAFFd9D3iLGxYiUGPdITSzsFmU1K8VtfuUHWAoN7M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 h1:uqM+VoHjVH6zdlkLF2b6O0ZANcHoj3rO0PoQ3jglUJA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 h1:leh5DwKv6Ihwi+h60uHtn6UWAxBbZ0q8DwQVMzf61zw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.1 h1:H3g2mkmu105ON0c/Gqx3Bm+bzoIijLom8LmV9Gjn7X0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0 h1:Vjq3Uy3JAU1DTxbA+uX6BegIhgO2pyFltbfbmDa9KdI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0/go.mod h1:Q3u+T/qw3Kb1Wf3DFKiFwEZlyaAyPb4yBgWm9wq7yh8=
|
||||
|
Loading…
Reference in New Issue
Block a user