diff --git a/.github/ISSUE_TEMPLATE/test-plan-docs.md b/.github/ISSUE_TEMPLATE/test-plan-docs.md index e31d0b73cfd51..1f62947d3239b 100644 --- a/.github/ISSUE_TEMPLATE/test-plan-docs.md +++ b/.github/ISSUE_TEMPLATE/test-plan-docs.md @@ -1,49 +1,52 @@ --- -name: Documentation Test Plan -about: Manual test plan for Teleport major releases +name: Documentation Release Plan +about: Documentation checks and changes to perform for major Teleport releases title: "Teleport X Docs Test Plan" labels: testplan --- -Perform the following checks on the Teleport documentation whenever we roll out -a new major version of Teleport on Teleport Cloud. Use `/docs/upcoming-releases` -to determine the rollout date. +Perform the following tasks whenever we roll out a new major version of +Teleport. -## Is the internal documentation coverage record up to date? +We need to make sure that the documentation site presents accurate information +to Teleport Enterprise (Cloud) users by default. Since we roll out a new major +Teleport version to Teleport Enterprise (Cloud) users several weeks after we +release the version, documentation release steps take place in two +phases: -- [ ] Identify features within the new release that we want to include as topics - in our measurement of documentation coverage. Update our internal - documentation coverage record to include the new topics. See our internal - knowledge base for the location of the coverage record. +- **Phase One:** We have released a new major version of Teleport but have not + rolled it out to Teleport Enterprise (Cloud) customers. +- **Phase Two:** We have rolled out the new major version of Teleport to + Teleport Enterprise (Cloud) customers. -## Is the docs site configuration accurate? +Use `/docs/upcoming-releases` to determine the Teleport Enterprise (Cloud) +rollout date. -> [!IMPORTANT] -> **Do not merge the new docs site configuration** before we roll out a new -> major version to Teleport Enterprise (Cloud). +## Phase One tasks -- [ ] Verify the latest version in `gravitational/docs/config.json` +Make sure these tasks are complete by the time we have released a new major +version of Teleport. -- [ ] Verify that `gravitational/docs/.gitmodules` contains the latest release - -- [ ] Ensure that submodule directories in `gravitational/docs` correspond to - those in `.gitmodules`. +- [ ] Identify features within the new release that we want to include as topics + in our measurement of documentation coverage. Update our internal + documentation coverage record to include the new topics. See our internal + knowledge base for the location of the coverage record. - Remove the directory of the EOL release and create one for the next release - using a command similar to the following: +- [ ] Update the submodule configuration in `gravitational/docs-website`. - ```bash - git submodule add https://github.com/gravitational/teleport content/.x - ``` + Remove the directory of the EOL release. Create a directory for the next + release using a command similar to the following: + + ```bash + git submodule add https://github.com/gravitational/teleport content/.x + ``` -## Is the docs site content up to date with the new release? + Verify that `gravitational/docs-website/.gitmodules` contains the latest + release and not the EOL release. - [ ] Verify that Teleport version variables are correct and reflect the upcoming - release. Check `docs/config.json` for this. - -- [ ] Ensure that redirects (as configured in `docs/config.json`) only exist for - the default version of the docs site, and have been removed from other - versions. + release. Check `docs/config.json` for this in all supported branches of + `gravitational/teleport`. - [ ] Remove version warnings in the docs that mention a version we no longer support _except_ for the last EOL version. E.g., if we no longer support @@ -52,11 +55,11 @@ to determine the rollout date. - [ ] Verify that all necessary documentation for the release was backported to the release branch: - - [ ] Diff between master and release branch and make sure there are no missed - PRs + - [ ] Diff between `master` and the new release branch and make sure there are + no missed PRs. - [ ] Ensure that the release branch's documentation content reflects all changes introduced by the release. If not, plan to update the docs ASAP and - notify all relevant teams of the delay (e.g., Developer Relations). + notify all relevant teams of the delay. - [ ] Verify that the [changelog](../../CHANGELOG.md) is up to date and complete for the default docs version. If one release branch has a more complete @@ -67,38 +70,47 @@ to determine the rollout date. $ git checkout origin/branch/v -- CHANGELOG.md ``` -- [ ] Verify the supported versions table in the FAQ - (https://goteleport.com/docs/faq/#supported-versions) +- [ ] Update the supported versions table in the FAQ + (https://goteleport.com/docs/faq/#supported-versions). -- [ ] Verify that the [Upcoming Releases - Page](../../docs/pages/upcoming-releases.mdx) only exists for the major - version of Teleport we are releasing. Ensure that this page contains the - latest information: +- [ ] Verify the accuracy of critical docs pages. Follow the docs guides below + and verify their accuracy while using the newly released major version of + Teleport. - ```bash - $ git checkout origin/branch/v -- docs/pages/upcoming-releases.mdx - ``` + - [ ] General [installation page](../../docs/pages/installation.mdx): ensure + that installation methods support the new release candidate. + - [ ] [Teleport Community + Edition](../../docs/pages/admin-guides/deploy-a-cluster/linux-demo.mdx) demo + guide. + - [ ] [Teleport Enterprise (Cloud)](../../docs/pages/get-started.mdx) getting + started guide. + - [ ] [Teleport Enterprise (Self-Hosted) with + Helm](../../docs/pages/admin-guides/deploy-a-cluster/helm-deployments/kubernetes-cluster.mdx) + - [ ] [Teleport Enterprise (Self-Hosted) with + Terraform](../../docs/pages/admin-guides/deploy-a-cluster/deployments/aws-ha-autoscale-cluster-terraform.mdx) -## Verify the accuracy of critical docs pages +## Phase Two changes -Follow the docs guides below and verify their accuracy. To do so, open the -version of the docs site that corresponds to the major release we're testing -for. For example, for Teleport 12 release use `branch/v12` branch and make sure -to select "Version 12.0" in the documentation version switcher. +Make sure these tasks are complete by the time we have rolled out a new major +version of Teleport to Teleport Enterprise (Cloud) customers. -### Installation +- [ ] Update the docs site configuration in + `gravitational/docs-website/config.json`: ensure that the EOL version has + `"deprecated": true` assigned and the newly rolled out version has + `"isDefault" true`. Remove the `"isDefault": true` assignment from the + previous version. -- [ ] General [installation page](../../docs/pages/installation.mdx): ensure that - installation methods support the new release candidate. -- [ ] Enterprise Cloud [downloads - page](../../docs/pages/choose-an-edition/teleport-cloud/downloads.mdx): ensure that - the release cnadidate is available at the repositories we link to. +- [ ] Copy the changelog from the previous default branch to the new one: + + ```bash + $ git checkout origin/branch/v -- CHANGELOG.md + ``` -### Getting started +- [ ] Verify that the [Upcoming Releases + Page](../../docs/pages/upcoming-releases.mdx) only exists for the major + version of Teleport we have rolled out. Ensure that this page contains the + latest information: -- [ ] [Teleport Community Edition](../../docs/pages/index.mdx) -- [ ] [Teleport Enterprise (Cloud)](../../docs/pages/choose-an-edition/teleport-cloud/get-started.mdx). -- [ ] [Teleport Enterprise (Self-Hosted) with - Helm](../../docs/pages/deploy-a-cluster/helm-deployments/kubernetes-cluster.mdx) -- [ ] [Teleport Enterprise (Self-Hosted) with - Terraform](../../docs/pages/deploy-a-cluster/deployments/aws-ha-autoscale-cluster-terraform.mdx) + ```bash + $ git checkout origin/branch/v -- docs/pages/upcoming-releases.mdx + ``` diff --git a/.github/ISSUE_TEMPLATE/webtestplan.md b/.github/ISSUE_TEMPLATE/webtestplan.md index c9d7987f48b27..5ef007db6325b 100644 --- a/.github/ISSUE_TEMPLATE/webtestplan.md +++ b/.github/ISSUE_TEMPLATE/webtestplan.md @@ -123,10 +123,9 @@ All actions should require re-authn with a webauthn device. For each, test the invite, reset, and login flows - [ ] Verify that input fields validates -- [ ] Verify with `second_factor` type to `off` -- [ ] Verify with `second_factor` type to `otp`, requires otp -- [ ] Verify with `second_factor` type to `webauthn`, requires hardware key -- [ ] Verify with `second_factor` type to `on`, requires a MFA device +- [ ] Verify with `second_factors` set to `["otp"]`, requires otp +- [ ] Verify with `second_factors` set to `["webauthn"]`, requires hardware key +- [ ] Verify with `second_factors` set to `["webauthn", "otp"]`, requires a MFA device - [ ] Verify that error message is shown if an invite/reset is expired/invalid - [ ] Verify that account is locked after several unsuccessful login attempts @@ -808,16 +807,16 @@ Add the following to enable read access to trusted clusters - Auth methods - Verify that the app supports clusters using different auth settings (`auth_service.authentication` in the cluster config): - - [ ] `type: local`, `second_factor: "otp"` + - [ ] `type: local`, `second_factors: ["otp"]` - [ ] Test per-session MFA items listed later in the test plan. - - [ ] `type: local`, `second_factor: "webauthn"`, + - [ ] `type: local`, `second_factors: ["webauthn"]`, - [ ] Test per-session MFA items listed later in the test plan. - - [ ] `type: local`, `second_factor: "webauthn"`, log in passwordlessly with hardware key - - [ ] `type: local`, `second_factor: "webauthn"`, log in passwordlessly with touch ID - - [ ] `type: local`, `second_factor: "on"`, log in with OTP + - [ ] `type: local`, `second_factors: ["webauthn"]`, log in passwordlessly with hardware key + - [ ] `type: local`, `second_factors: ["webauthn"]`, log in passwordlessly with touch ID + - [ ] `type: local`, `second_factors: ["webauthn", "otp"]`, log in with OTP - [ ] Test per-session MFA items listed later in the test plan. - - [ ] `type: local`, `second_factor: "on"`, log in with hardware key - - [ ] `type: local`, `second_factor: "on"`, log in with passwordless auth + - [ ] `type: local`, `second_factors: ["webauthn", "otp"]`, log in with hardware key + - [ ] `type: local`, `second_factors: ["webauthn", "otp"]`, log in with passwordless auth - [ ] Verify that the passwordless credential picker works. - To make the picker show up, you need to add the same MFA device with passwordless capabilities to multiple users. diff --git a/.github/workflows/doc-tests.yaml b/.github/workflows/doc-tests.yaml index e4463ddba2530..54df4b0743ef2 100644 --- a/.github/workflows/doc-tests.yaml +++ b/.github/workflows/doc-tests.yaml @@ -50,7 +50,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - repository: 'gravitational/docs' + repository: 'gravitational/docs-website' path: 'docs' # Cache node_modules. Unlike the example in the actions/cache repo, this @@ -80,30 +80,35 @@ jobs: # use for the live docs site in that we only test a single version of # the content. # - # To do this, we replace the three submodules we use for building the - # live docs site with a single submodule, pointing to the - # gravitational/teleport branch we are linting. - # + # To do this, we delete the three submodules we use for building the + # live docs site and copy a gravitational/teleport clone into the + # content directory. + # # The docs engine expects a config.json file at the root of the # gravitational/docs clone that associates directories with git # submodules. By default, these directories represent versioned branches # of gravitational/teleport. We override this in order to build only a # single version of the docs. + # + # We also replace data fetched from Sanity CMS with hardcoded JSON + # objects to remove the need to authenticate with Sanity. Each includes + # the minimal set of data required for docs builds to succeed. run: | echo "" > .gitmodules rm -rf content/* - cd content # Rather than using a submodule, copy the teleport source into the # content directory. - cp -r $GITHUB_WORKSPACE/teleport $GITHUB_WORKSPACE/docs/content - cd $GITHUB_WORKSPACE/docs - echo "{\"versions\": [{\"name\": \"teleport\", \"branch\": \"teleport\", \"deprecated\": false}]}" > $GITHUB_WORKSPACE/docs/config.json - cat <<< "$(jq '.scripts."git-update" = "echo Skipping submodule update"' package.json)" > package.json - yarn build-node + cp -r "$GITHUB_WORKSPACE/teleport" "$GITHUB_WORKSPACE/docs/content/current" + jq -nr --arg version "current" '{"versions": [{"name": $version,"branch": $version,"deprecated": false,"isDefault": true}]}' > config.json + NEW_PACKAGE_JSON=$(jq '.scripts."git-update" = "echo Skipping submodule update"' package.json); + NEW_PACKAGE_JSON=$(jq '.scripts."prepare-sanity-data" = "echo Using pre-populated Sanity data"' <<< "$NEW_PACKAGE_JSON"); + echo "$NEW_PACKAGE_JSON" > package.json; + echo "{}" > data/events.json + echo '{"bannerButtons":{"second":{"title":"LOG IN","url":"https://teleport.sh"},"first":{"title":"Support","url":"https://goteleport.com/support/"}},"navbarData":{"rightSide":{},"logo":"/favicon.svg","menu":[]}}' > data/navbar.json - name: Check spelling working-directory: 'docs' - run: yarn spellcheck content/teleport + run: yarn spellcheck content/current - name: Lint docs formatting working-directory: 'docs' diff --git a/.github/workflows/docs-amplify.yaml b/.github/workflows/docs-amplify.yaml new file mode 100644 index 0000000000000..3ca12933b50c1 --- /dev/null +++ b/.github/workflows/docs-amplify.yaml @@ -0,0 +1,42 @@ +name: Docs Preview +on: + pull_request: + paths: + - 'docs/**' + - .github/workflows/docs-amplify.yaml + workflow_dispatch: + +permissions: + pull-requests: write + id-token: write + +jobs: + amplify-preview: + name: Prepare Amplify preview URL + runs-on: ubuntu-22.04-2core-arm64 + environment: docs-amplify + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4 + with: + aws-region: us-west-2 + role-to-assume: ${{ vars.IAM_ROLE }} + + - name: Create Amplify preview environment + uses: gravitational/shared-workflows/tools/amplify-preview@tools/amplify-preview/v0.0.1 + continue-on-error: true + with: + app_ids: ${{ vars.AMPLIFY_APP_IDS }} + create_branches: "true" + github_token: ${{ secrets.GITHUB_TOKEN }} + wait: "true" + + - name: Print failure message + if: failure() + env: + ERR_TITLE: Teleport Docs preview build failed + ERR_MESSAGE: >- + Please refer to the following documentation for help: https://www.notion.so/goteleport/How-to-Amplify-deployments-162fdd3830be8096ba72efa1a49ee7bc?pvs=4 + run: | + echo ::error title=$ERR_TITLE::$ERR_MESSAGE + exit 1 diff --git a/.github/workflows/update-docs-webhook.yaml b/.github/workflows/update-docs-webhook.yaml index 7f46fb51996ac..4c59c66039cbd 100644 --- a/.github/workflows/update-docs-webhook.yaml +++ b/.github/workflows/update-docs-webhook.yaml @@ -15,18 +15,11 @@ jobs: environment: update-docs strategy: fail-fast: false - matrix: - webhooks: - - url_secret_name: DOCS_DEPLOY_HOOK - http_method: GET - - url_secret_name: AMPLIFY_DOCS_DEPLOY_HOOK - http_method: POST steps: - name: Call deployment webhook env: - WEBHOOK_URL: ${{ secrets[matrix.webhooks.url_secret_name] }} - HTTP_METHOD: ${{ matrix.webhooks.http_method }} + WEBHOOK_URL: ${{ secrets[AMPLIFY_DOCS_DEPLOY_HOOK] }} run: | - if curl -X "$HTTP_METHOD" --silent --fail --show-error "$WEBHOOK_URL" > /dev/null; then + if curl -X POST --silent --fail --show-error "$WEBHOOK_URL" > /dev/null; then echo "Triggered successfully" fi diff --git a/.github/workflows/vercel-preview.yaml b/.github/workflows/vercel-preview.yaml deleted file mode 100644 index f74b3086138c0..0000000000000 --- a/.github/workflows/vercel-preview.yaml +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: Deploy Vercel Preview -permissions: - # Required in order to comment on PRs with a deployment link - pull-requests: write -env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} - -on: - pull_request: - paths: - - 'docs/**' - - .github/workflows/vercel-preview.yaml - types: [opened, reopened, synchronize] - workflow_dispatch: - -jobs: - deploy-vercel-preview: - name: Deploy Vercel preview - runs-on: ubuntu-latest - environment: vercel - - steps: - - name: Extract branch name - run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT - id: extract_branch - - uses: actions/checkout@v4 - with: - repository: 'gravitational/docs' - - name: Configure Submodules - env: - BRANCH: ${{ steps.extract_branch.outputs.branch }} - # Edit config.json and .gitmodules so there is a single submodule - # pointing to the version of the docs to deploy to the preview site. - run: | - git rm content/* - git submodule add --force -b "${BRANCH}" https://github.com/gravitational/teleport content/preview - echo "{\"versions\": [{\"name\": \"preview\", \"branch\": \"preview\", \"deprecated\": false, \"latest\": true}]}" > config.json - git submodule update --init --remote --progress - - name: Install Vercel CLI - run: yarn global add vercel@latest - - name: Pull Vercel Environment Information - run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} - - name: Build Project Artifacts - run: vercel build --token=${{ secrets.VERCEL_TOKEN }} - - name: Deploy Project Artifacts to Vercel - id: deploy - run: | - vercel deploy --archive=tgz --prebuilt --token=${{ secrets.VERCEL_TOKEN }} > deployment-url.txt - preview_url="$(cat deployment-url.txt)" - echo "PREVIEW_URL=$preview_url" >> $GITHUB_OUTPUT - - uses: actions/github-script@v7 - env: - PREVIEW_URL: ${{ steps.deploy.outputs.PREVIEW_URL }} - with: - script: | - const previewUrl = process.env.PREVIEW_URL - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `🤖 Vercel preview here: ${previewUrl}/docs` - }) diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 6589f7c77bbdb..0000000000000 --- a/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "arrowParens": "avoid", - "printWidth": 80, - "bracketSpacing": true, - "semi": true, - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "es5" -} diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000000000..cc4e29cb2a273 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,39 @@ +const tsConfigBase = require('./tsconfig.json'); + +const ourPackages = new Set( + Object.keys(tsConfigBase.compilerOptions.paths).map( + // Remove extra '/*' if present in the package name. + packageName => packageName.split('/')[0] + ) +); +const appPackages = ['teleport', 'e-teleport', 'teleterm'].flatMap(pkg => [ + pkg, + `@gravitational/${pkg}`, +]); +const libraryPackages = [...ourPackages] + .filter(pkg => !appPackages.includes(pkg)) + .flatMap(pkg => [pkg, `@gravitational/${pkg}`]); + +module.exports = { + arrowParens: 'avoid', + printWidth: 80, + bracketSpacing: true, + plugins: ['@ianvs/prettier-plugin-sort-imports'], + importOrder: [ + '', + '', + '', + '', + `^(${libraryPackages.join('|')})`, + '', + `^(${appPackages.join('|')})`, + '', + '^[./]', + ], + importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'], + importOrderTypeScriptVersion: '5.0.0', + semi: true, + singleQuote: true, + tabWidth: 2, + trailingComma: 'es5', +}; diff --git a/Cargo.lock b/Cargo.lock index d9fa0c291965b..0520da46407a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,28 +204,26 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-lc-rs" -version = "1.9.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" +checksum = "f409eb70b561706bf8abba8ca9c112729c481595893fd06a2dd9af8ed8441148" dependencies = [ "aws-lc-sys", - "mirai-annotations", "paste", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.21.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ddc4a5b231dd6958b140ff3151b6412b3f4321fab354f399eec8f14b06df62" +checksum = "923ded50f602b3007e5e63e3f094c479d9c8a9b42d7f4034e4afe456aa48bfd2" dependencies = [ - "bindgen 0.69.4", + "bindgen 0.69.5", "cc", "cmake", "dunce", "fs_extra", - "libc", "paste", ] @@ -284,9 +282,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags 2.6.0", "cexpr", @@ -807,9 +805,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -1229,9 +1227,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1242,7 +1240,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -1674,10 +1671,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1813,12 +1811,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - [[package]] name = "nom" version = "7.1.3" @@ -2160,26 +2152,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2418,9 +2390,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64", "bytes", @@ -2449,6 +2421,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls", + "tower", "tower-service", "url", "wasm-bindgen", @@ -2548,9 +2521,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "aws-lc-rs", "log", @@ -2984,9 +2957,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -3006,9 +2979,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -3132,14 +3105,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -3153,15 +3126,15 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -3171,9 +3144,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3182,9 +3155,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3203,9 +3176,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -3340,9 +3313,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -3351,13 +3324,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -3378,9 +3350,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3388,9 +3360,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -3401,15 +3373,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Makefile b/Makefile index 0baf6ffbbf81f..ede5433c3a53b 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ # Stable releases: "1.0.0" # Pre-releases: "1.0.0-alpha.1", "1.0.0-beta.2", "1.0.0-rc.3" # Master/dev branch: "1.0.0-dev" -VERSION=17.0.0-dev +VERSION=18.0.0-dev DOCKER_IMAGE ?= teleport diff --git a/api/client/client.go b/api/client/client.go index 6ef91851aa256..edffe12d00ff2 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -2286,12 +2286,56 @@ func (c *Client) GetTrustedClusters(ctx context.Context) ([]types.TrustedCluster } // UpsertTrustedCluster creates or updates a Trusted Cluster. -func (c *Client) UpsertTrustedCluster(ctx context.Context, trusedCluster types.TrustedCluster) (types.TrustedCluster, error) { - trustedCluster, ok := trusedCluster.(*types.TrustedClusterV2) +// +// Deprecated: Use [Client.UpsertTrustedClusterV2] instead. +func (c *Client) UpsertTrustedCluster(ctx context.Context, trustedCluster types.TrustedCluster) (types.TrustedCluster, error) { + trustedClusterV2, ok := trustedCluster.(*types.TrustedClusterV2) + if !ok { + return nil, trace.BadParameter("invalid type %T", trustedCluster) + } + resp, err := c.grpc.UpsertTrustedCluster(ctx, trustedClusterV2) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// UpsertTrustedClusterV2 creates or updates a Trusted Cluster. +func (c *Client) UpsertTrustedClusterV2(ctx context.Context, trustedCluster types.TrustedCluster) (types.TrustedCluster, error) { + trustedClusterV2, ok := trustedCluster.(*types.TrustedClusterV2) + if !ok { + return nil, trace.BadParameter("invalid type %T", trustedCluster) + } + req := &trustpb.UpsertTrustedClusterRequest{TrustedCluster: trustedClusterV2} + resp, err := c.TrustClient().UpsertTrustedCluster(ctx, req) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// CreateTrustedCluster creates a Trusted Cluster. +func (c *Client) CreateTrustedCluster(ctx context.Context, trustedCluster types.TrustedCluster) (types.TrustedCluster, error) { + trustedClusterV2, ok := trustedCluster.(*types.TrustedClusterV2) + if !ok { + return nil, trace.BadParameter("invalid type %T", trustedCluster) + } + req := &trustpb.CreateTrustedClusterRequest{TrustedCluster: trustedClusterV2} + resp, err := c.TrustClient().CreateTrustedCluster(ctx, req) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// UpdateTrustedCluster updates a Trusted Cluster. +func (c *Client) UpdateTrustedCluster(ctx context.Context, trustedCluster types.TrustedCluster) (types.TrustedCluster, error) { + trustedClusterV2, ok := trustedCluster.(*types.TrustedClusterV2) if !ok { - return nil, trace.BadParameter("invalid type %T", trusedCluster) + return nil, trace.BadParameter("invalid type %T", trustedCluster) } - resp, err := c.grpc.UpsertTrustedCluster(ctx, trustedCluster) + req := &trustpb.UpdateTrustedClusterRequest{TrustedCluster: trustedClusterV2} + resp, err := c.TrustClient().UpdateTrustedCluster(ctx, req) if err != nil { return nil, trace.Wrap(err) } @@ -5097,6 +5141,52 @@ func (c *Client) UpsertUserLastSeenNotification(ctx context.Context, req *notifi return rsp, trace.Wrap(err) } +// GetWorkloadIdentity returns a workload identity by name. +func (c *Client) GetWorkloadIdentity(ctx context.Context, name string) (*workloadidentityv1pb.WorkloadIdentity, error) { + resp, err := c.WorkloadIdentityResourceServiceClient().GetWorkloadIdentity(ctx, &workloadidentityv1pb.GetWorkloadIdentityRequest{ + Name: name, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// DeleteWorkloadIdentity deletes a workload identity by name. It will throw an +// error if the workload identity does not exist. +func (c *Client) DeleteWorkloadIdentity(ctx context.Context, name string) error { + _, err := c.WorkloadIdentityResourceServiceClient().DeleteWorkloadIdentity(ctx, &workloadidentityv1pb.DeleteWorkloadIdentityRequest{ + Name: name, + }) + if err != nil { + return trace.Wrap(err) + } + return nil +} + +// CreateWorkloadIdentity creates a new workload identity, it will not overwrite +// an existing workload identity with the same name. +func (c *Client) CreateWorkloadIdentity(ctx context.Context, r *workloadidentityv1pb.WorkloadIdentity) (*workloadidentityv1pb.WorkloadIdentity, error) { + resp, err := c.WorkloadIdentityResourceServiceClient().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.CreateWorkloadIdentityRequest{ + WorkloadIdentity: r, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// UpsertWorkloadIdentity creates or updates a workload identity. +func (c *Client) UpsertWorkloadIdentity(ctx context.Context, r *workloadidentityv1pb.WorkloadIdentity) (*workloadidentityv1pb.WorkloadIdentity, error) { + resp, err := c.WorkloadIdentityResourceServiceClient().UpsertWorkloadIdentity(ctx, &workloadidentityv1pb.UpsertWorkloadIdentityRequest{ + WorkloadIdentity: r, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + // ResourceUsageClient returns an unadorned Resource Usage service client, // using the underlying Auth gRPC connection. // Clients connecting to non-Enterprise clusters, or older Teleport versions, diff --git a/api/client/proto/authservice.pb.go b/api/client/proto/authservice.pb.go index 2deb3d5ef03e1..f9099550a91f1 100644 --- a/api/client/proto/authservice.pb.go +++ b/api/client/proto/authservice.pb.go @@ -16255,12 +16255,12 @@ func init() { } var fileDescriptor_0ffcffcda38ae159 = []byte{ - // 15541 bytes of a gzipped FileDescriptorProto + // 15544 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0xbd, 0x5b, 0x6c, 0x5c, 0x49, 0x7a, 0x18, 0xac, 0x6e, 0xde, 0x3f, 0xde, 0x5a, 0x45, 0x52, 0x6c, 0x51, 0x97, 0x96, 0x8e, 0x46, 0x33, 0x1a, 0xed, 0xac, 0x2e, 0x9c, 0xcb, 0xce, 0x7d, 0xa6, 0x9b, 0xa4, 0x48, 0x4a, 0xbc, 0xcd, 0x69, 0x92, 0x9a, 0x9b, 0xb7, 0xf7, 0xb0, 0xbb, 0x44, 0x1e, 0xab, 0xd9, 0xa7, 0xf7, 0x9c, 0xd3, - 0xd2, 0x68, 0xfd, 0xaf, 0x7f, 0xd8, 0xfe, 0xff, 0xc4, 0x41, 0x90, 0xc4, 0x06, 0xec, 0xc0, 0x81, + 0xd2, 0x68, 0xfd, 0xaf, 0x7f, 0xd8, 0xfe, 0xff, 0xc4, 0x41, 0x90, 0xc4, 0x06, 0xe2, 0xc0, 0x86, 0x03, 0x38, 0x01, 0x1c, 0x20, 0x08, 0x10, 0xc0, 0x2f, 0x81, 0x9f, 0xfc, 0x90, 0xa7, 0x6c, 0x0c, 0x04, 0x89, 0x61, 0xfb, 0x25, 0x40, 0xe8, 0x64, 0x01, 0xbf, 0x10, 0xc9, 0x83, 0x11, 0x24, 0x40, 0x36, 0x30, 0x10, 0xd4, 0x57, 0x97, 0x53, 0x75, 0x2e, 0xdd, 0xa4, 0xa4, 0x59, 0xe7, 0x45, 0x62, @@ -16410,7 +16410,7 @@ var fileDescriptor_0ffcffcda38ae159 = []byte{ 0xfe, 0x27, 0x54, 0x79, 0xba, 0x04, 0x3e, 0xf9, 0xe0, 0xea, 0x6b, 0x73, 0x62, 0xf2, 0x36, 0x8c, 0x2c, 0xd2, 0x26, 0xdd, 0x77, 0x42, 0x4f, 0x4e, 0x27, 0xdc, 0x01, 0x26, 0x81, 0xda, 0x98, 0x8b, 0x28, 0x99, 0x39, 0x6c, 0x53, 0x27, 0xf0, 0x5a, 0xba, 0x39, 0xec, 0x23, 0x44, 0x37, 0x87, 0x39, - 0x0d, 0xf9, 0xed, 0x1c, 0x8c, 0x96, 0x5b, 0x2d, 0xe1, 0x58, 0x0a, 0x84, 0xd6, 0x67, 0x6e, 0x29, + 0x0d, 0xf9, 0xad, 0x1c, 0x8c, 0x96, 0x5b, 0x2d, 0xe1, 0x58, 0x0a, 0x84, 0xd6, 0x67, 0x6e, 0x29, 0x4f, 0xec, 0x9a, 0xb3, 0x47, 0x9b, 0xbb, 0x4e, 0xb3, 0x43, 0x83, 0xca, 0xd7, 0xcc, 0x42, 0xf9, 0x8f, 0x47, 0xa5, 0x0f, 0x4e, 0xe1, 0x2a, 0x8a, 0x7c, 0xba, 0xdb, 0xbe, 0xe3, 0x86, 0x01, 0xfb, 0x6a, 0x9d, 0xa8, 0x40, 0xfd, 0xbb, 0xd1, 0xea, 0x11, 0xad, 0x0d, 0x83, 0xbd, 0xd6, 0x06, 0x72, @@ -16495,7 +16495,7 @@ var fileDescriptor_0ffcffcda38ae159 = []byte{ 0xef, 0xe7, 0xdf, 0xcd, 0xdd, 0xef, 0x1f, 0x1e, 0x2d, 0x8c, 0xf1, 0x63, 0xf5, 0xfb, 0xfd, 0xc3, 0xe3, 0x85, 0x09, 0xab, 0x0c, 0x93, 0x31, 0x7a, 0x52, 0x84, 0x21, 0xda, 0x62, 0x9b, 0xff, 0x06, 0xdf, 0x10, 0xd9, 0xf2, 0x27, 0x99, 0x86, 0x81, 0xa6, 0x7b, 0xe8, 0x86, 0x58, 0xe0, 0x80, 0xcd, - 0x7f, 0x58, 0xbf, 0x93, 0x03, 0x92, 0x5c, 0x8f, 0xc8, 0xed, 0x98, 0x18, 0xbe, 0xf5, 0x15, 0x20, + 0x7f, 0x58, 0xbf, 0x9d, 0x03, 0x92, 0x5c, 0x8f, 0xc8, 0xed, 0x98, 0x18, 0xbe, 0xf5, 0x15, 0x20, 0xfd, 0xe0, 0x40, 0x4a, 0xff, 0x0c, 0xa6, 0xf8, 0x80, 0x90, 0x2b, 0xa7, 0x56, 0x16, 0x9f, 0xb1, 0x53, 0xd0, 0xba, 0xb3, 0x49, 0xa0, 0x71, 0x9d, 0x5d, 0xc3, 0xaa, 0x75, 0x60, 0x26, 0x75, 0x25, 0x22, 0xeb, 0x30, 0x73, 0xe8, 0xb5, 0xc2, 0x83, 0xe6, 0x33, 0xb9, 0x10, 0x89, 0xd2, 0x72, 0x58, @@ -16619,7 +16619,7 @@ var fileDescriptor_0ffcffcda38ae159 = []byte{ 0xaa, 0x57, 0xe9, 0xd3, 0x30, 0xa0, 0x7b, 0x29, 0xf9, 0x0f, 0xeb, 0x37, 0x73, 0x30, 0x8d, 0xc3, 0x60, 0xdf, 0x65, 0xcb, 0x43, 0xd4, 0x96, 0x79, 0xa3, 0xd3, 0x2e, 0x1a, 0x9d, 0x16, 0xa3, 0x55, 0xbd, 0xf7, 0x7e, 0xa2, 0xf7, 0x2e, 0xa6, 0xf5, 0x1e, 0x4e, 0x01, 0xae, 0xd7, 0xd2, 0x3b, 0x4d, - 0x3f, 0xae, 0xfb, 0x9d, 0x1c, 0x4c, 0x69, 0x75, 0x52, 0x0d, 0xbc, 0x6b, 0x54, 0xe9, 0x42, 0x4a, + 0x3f, 0xae, 0xfb, 0xed, 0x1c, 0x4c, 0x69, 0x75, 0x52, 0x0d, 0xbc, 0x6b, 0x54, 0xe9, 0x42, 0x4a, 0x95, 0x12, 0xe3, 0xa9, 0x92, 0xa8, 0xd1, 0x2b, 0xdd, 0x6a, 0x94, 0x36, 0x9c, 0x8c, 0x61, 0xf2, 0x97, 0x39, 0x98, 0x49, 0xd5, 0x01, 0x39, 0xc7, 0xf6, 0xff, 0x75, 0x9f, 0x86, 0x42, 0xf3, 0xe2, 0x17, 0x83, 0xaf, 0x06, 0x41, 0x87, 0xfa, 0x42, 0xef, 0xe2, 0x17, 0x79, 0x05, 0xc6, 0xb7, 0xa8, @@ -16705,7 +16705,7 @@ var fileDescriptor_0ffcffcda38ae159 = []byte{ 0x83, 0x21, 0x96, 0x93, 0x24, 0x7f, 0x92, 0x9c, 0x24, 0xbf, 0x04, 0x19, 0x2e, 0xab, 0x13, 0x78, 0x01, 0x5e, 0x3b, 0x3e, 0x2a, 0x5d, 0x8b, 0xbc, 0x00, 0x1c, 0x9b, 0xe6, 0x0e, 0xc8, 0x28, 0x22, 0xe9, 0xbf, 0xe8, 0x7f, 0x0e, 0xff, 0xc5, 0x1d, 0x18, 0x42, 0x63, 0x66, 0x75, 0x4b, 0x44, 0x7e, - 0xe2, 0xf0, 0xc4, 0x0c, 0x45, 0x35, 0x57, 0x4f, 0x07, 0x28, 0xc9, 0xac, 0x7f, 0x98, 0x87, 0x2b, + 0xe2, 0xf0, 0xc4, 0x0c, 0x45, 0x35, 0x57, 0x4f, 0x07, 0x28, 0xc9, 0xac, 0x7f, 0x94, 0x87, 0x2b, 0xd9, 0x3a, 0x17, 0x75, 0x5b, 0x04, 0x88, 0x22, 0x5c, 0xba, 0x45, 0xd4, 0xe0, 0xb7, 0xf3, 0x94, 0xee, 0xa9, 0x88, 0x36, 0x8d, 0x8f, 0xed, 0x7d, 0xe4, 0x4d, 0xeb, 0xd8, 0x71, 0x8a, 0x71, 0x01, 0x5b, 0x64, 0xb3, 0x15, 0x20, 0x23, 0x9b, 0xad, 0x80, 0x91, 0x3d, 0x98, 0xdd, 0xf2, 0xdd, 0x27, @@ -16743,127 +16743,127 @@ var fileDescriptor_0ffcffcda38ae159 = []byte{ 0x94, 0xca, 0xf4, 0xf0, 0x91, 0x73, 0xeb, 0xc9, 0xdd, 0x5b, 0x29, 0xb4, 0x3c, 0x11, 0x41, 0x5d, 0x22, 0xb4, 0xf8, 0x2c, 0xfd, 0xa0, 0x2b, 0x85, 0x91, 0x7c, 0x01, 0xd3, 0xd5, 0xea, 0x26, 0xbf, 0xcc, 0x61, 0xcb, 0x03, 0x7e, 0x7b, 0x4d, 0x04, 0x6c, 0x61, 0x77, 0x07, 0x81, 0x57, 0x13, 0x97, - 0x40, 0xf4, 0xb0, 0x00, 0x3d, 0x15, 0x43, 0x9a, 0x08, 0xfd, 0xa4, 0xfc, 0xb7, 0xd4, 0x5d, 0x05, - 0xb6, 0x15, 0x71, 0x9b, 0x54, 0x5c, 0x3a, 0x92, 0x03, 0x3b, 0xe3, 0x94, 0x2f, 0xf7, 0x2d, 0x9f, - 0xf2, 0xfd, 0x41, 0x5e, 0xde, 0xd0, 0x48, 0x1e, 0xb4, 0x9e, 0xfa, 0xb0, 0x2f, 0xb5, 0x05, 0x27, - 0x5a, 0xa7, 0x53, 0x2b, 0x47, 0x2a, 0xf2, 0xa8, 0x54, 0x25, 0x2b, 0x9b, 0x50, 0xc7, 0x0e, 0x11, - 0xc2, 0x38, 0x3d, 0xc5, 0x5d, 0x91, 0xc6, 0x15, 0x3f, 0x87, 0xeb, 0x7b, 0xf1, 0x73, 0xb8, 0x1f, - 0xc3, 0x8c, 0xbc, 0x1a, 0xb5, 0x40, 0x5b, 0x21, 0xf5, 0xe5, 0x89, 0xfd, 0x44, 0x94, 0xf4, 0x0d, - 0xd3, 0xfb, 0x15, 0xa0, 0xaf, 0x6c, 0x6f, 0x08, 0x8f, 0x0e, 0xfb, 0x93, 0x5c, 0x31, 0x03, 0xe2, - 0xf8, 0x9d, 0x37, 0x23, 0xfc, 0xed, 0x0a, 0xab, 0x2e, 0xf7, 0xb3, 0xb8, 0x32, 0x55, 0x9f, 0xad, - 0x83, 0xac, 0x05, 0xb8, 0x60, 0x16, 0xbf, 0x45, 0xfd, 0x43, 0x17, 0xf7, 0xde, 0x55, 0x1a, 0xca, - 0x42, 0x73, 0x51, 0xa1, 0x44, 0x0f, 0xa8, 0x16, 0x66, 0xe0, 0xff, 0xce, 0x43, 0x29, 0xb5, 0x11, - 0xe5, 0x20, 0x70, 0xf7, 0x5b, 0x98, 0x41, 0xe3, 0x22, 0xf4, 0x3f, 0x70, 0x5b, 0x0d, 0xdd, 0x90, - 0x7c, 0xec, 0xb6, 0x1a, 0x36, 0x42, 0x99, 0x0d, 0x52, 0xed, 0xec, 0x21, 0x81, 0x66, 0x22, 0x07, - 0x9d, 0xbd, 0x1a, 0x23, 0xd2, 0x6d, 0x10, 0x41, 0x46, 0xae, 0xc3, 0x90, 0xcc, 0xb6, 0xd6, 0x17, - 0x79, 0xcf, 0x64, 0x9a, 0x35, 0x89, 0x23, 0x1f, 0xc1, 0xf0, 0x3a, 0x0d, 0x9d, 0x86, 0x13, 0x3a, - 0x62, 0xec, 0xc8, 0x87, 0x30, 0x24, 0xb8, 0x52, 0x10, 0x2b, 0xf4, 0xf0, 0xa1, 0x80, 0xd8, 0x8a, - 0x05, 0x15, 0xe8, 0x06, 0xed, 0xa6, 0xf3, 0x4c, 0x05, 0x93, 0x32, 0x05, 0x46, 0x20, 0xf2, 0x8e, - 0x19, 0x72, 0x11, 0x1d, 0x9f, 0xa5, 0x2a, 0x24, 0x0a, 0xc8, 0x58, 0xc1, 0x30, 0x90, 0x48, 0xd5, - 0x22, 0x9b, 0xa0, 0x95, 0xca, 0x6d, 0x50, 0xda, 0x26, 0xa3, 0xf5, 0xdb, 0xa3, 0x70, 0x76, 0xcb, - 0xd9, 0x77, 0x5b, 0x6c, 0x47, 0x61, 0xd3, 0xc0, 0xeb, 0xf8, 0x75, 0x4a, 0xca, 0x30, 0x61, 0x06, - 0x70, 0xf7, 0x08, 0x4f, 0x67, 0x9b, 0x26, 0x13, 0x46, 0xe6, 0x61, 0x44, 0x5d, 0x1a, 0x17, 0x3b, - 0x9d, 0x94, 0xcb, 0xe4, 0x2b, 0x67, 0xec, 0x88, 0x8c, 0xbc, 0x67, 0x1c, 0x3e, 0x4e, 0xaa, 0xfc, - 0x07, 0x48, 0x3b, 0xcf, 0x23, 0x6c, 0x5b, 0x5e, 0xc3, 0xdc, 0xad, 0xf1, 0x13, 0xb6, 0x1f, 0x24, - 0xce, 0x23, 0x07, 0x8c, 0x1a, 0x27, 0x9c, 0xb2, 0xb8, 0x51, 0xcd, 0xcc, 0x5e, 0x9f, 0x72, 0x52, - 0xf9, 0x25, 0x8c, 0x3e, 0xe8, 0xec, 0x51, 0x79, 0xf2, 0x3a, 0x28, 0x36, 0x6f, 0xf1, 0x6b, 0x09, - 0x02, 0xbf, 0xfb, 0x26, 0xff, 0x8a, 0x1f, 0x77, 0xf6, 0x68, 0xf2, 0x59, 0x04, 0xb6, 0x6a, 0x6a, - 0xc2, 0xc8, 0x01, 0x14, 0xe2, 0x37, 0x08, 0x44, 0x97, 0x76, 0xb9, 0xf7, 0x80, 0x89, 0x7e, 0xb4, - 0xc7, 0x17, 0x78, 0x5c, 0xb3, 0x51, 0x48, 0x42, 0x2a, 0xf9, 0x31, 0xcc, 0xa4, 0xba, 0xc4, 0xd5, - 0x1d, 0xc8, 0xee, 0xde, 0x76, 0x5c, 0x82, 0x62, 0x5a, 0x93, 0x17, 0x2e, 0x8d, 0x92, 0xd3, 0x4b, - 0x21, 0x0d, 0x98, 0x8c, 0x45, 0xc6, 0x8b, 0x17, 0x66, 0xb2, 0x63, 0xed, 0x71, 0xd7, 0x24, 0x93, - 0x34, 0xa7, 0x96, 0x15, 0x17, 0x49, 0xd6, 0x60, 0x44, 0xf9, 0xa2, 0x44, 0x6e, 0xbe, 0x34, 0xbf, - 0x5b, 0xf1, 0xf8, 0xa8, 0x34, 0x1d, 0xf9, 0xdd, 0x0c, 0x99, 0x91, 0x00, 0xf2, 0xcb, 0x70, 0x55, - 0x0d, 0xd1, 0x4d, 0x3f, 0xdd, 0x43, 0x29, 0x1e, 0x77, 0xb8, 0x19, 0x1f, 0xe1, 0x59, 0xf4, 0xbb, - 0x77, 0x2b, 0xf9, 0x62, 0x6e, 0xe5, 0x8c, 0xdd, 0x5b, 0x34, 0xf9, 0xd5, 0x1c, 0x9c, 0xcb, 0x28, - 0x75, 0x0c, 0x4b, 0xed, 0xe9, 0x36, 0x46, 0xcb, 0x13, 0xef, 0xfd, 0xb9, 0x8d, 0xe8, 0x7e, 0xac, - 0xf4, 0x1f, 0x1b, 0xed, 0xce, 0x28, 0x89, 0xdc, 0x01, 0xd8, 0x77, 0x43, 0x31, 0xc6, 0x30, 0x4d, - 0x5d, 0xf2, 0x03, 0x65, 0x6a, 0xdb, 0x77, 0x43, 0x31, 0xd2, 0x7e, 0x3f, 0xd7, 0x73, 0x5e, 0xc7, - 0xec, 0x75, 0xa3, 0xf3, 0xaf, 0x76, 0x9b, 0xf4, 0x22, 0xea, 0xca, 0x9d, 0xe3, 0xa3, 0xd2, 0x1b, - 0x2a, 0x05, 0x5a, 0x1d, 0xa9, 0xe4, 0x2d, 0xdf, 0x9a, 0xa3, 0xe8, 0x8c, 0xf6, 0xf4, 0x5c, 0x5a, - 0xde, 0x80, 0x41, 0xf4, 0x4c, 0x05, 0xc5, 0x71, 0xb4, 0xdd, 0x30, 0x71, 0x17, 0xfa, 0xaf, 0xf4, - 0xdd, 0x9a, 0xa0, 0x21, 0x2b, 0xcc, 0x06, 0xc2, 0xdd, 0xa2, 0xb4, 0x59, 0x44, 0x9a, 0x3f, 0x61, - 0x47, 0x73, 0x94, 0xcc, 0xbf, 0x63, 0x3c, 0x4f, 0x62, 0xb2, 0x55, 0x00, 0x86, 0x7d, 0x31, 0xdd, - 0xde, 0xef, 0x1f, 0xee, 0x2f, 0x0c, 0xf0, 0x19, 0x41, 0xde, 0x25, 0xf9, 0xf5, 0x61, 0x7e, 0xf1, - 0x7c, 0xa7, 0xe5, 0x3e, 0x72, 0xa3, 0x99, 0x59, 0xf7, 0x69, 0x47, 0xef, 0x84, 0x09, 0x8b, 0x33, - 0xe3, 0x45, 0x30, 0xe5, 0xfe, 0xce, 0xf7, 0x74, 0x7f, 0xbf, 0xa9, 0x1d, 0x14, 0x6b, 0xe9, 0x7c, - 0xb9, 0x65, 0x61, 0xba, 0x9b, 0xa3, 0x13, 0xe4, 0xaf, 0x61, 0x10, 0x33, 0xf0, 0xf2, 0x53, 0xf8, - 0xd1, 0xf9, 0x5b, 0xa2, 0x3b, 0xbb, 0x54, 0x9f, 0xa7, 0xec, 0x15, 0xc9, 0x24, 0xb8, 0xc6, 0x11, - 0x60, 0x68, 0x1c, 0x21, 0x64, 0x1b, 0xa6, 0xb6, 0xd8, 0x46, 0x97, 0xdf, 0x68, 0x68, 0xfb, 0xc2, - 0x25, 0xc8, 0x9d, 0x8d, 0xb8, 0xd1, 0x6e, 0x4b, 0x74, 0x8d, 0x2a, 0xbc, 0xbe, 0xd7, 0x4c, 0x61, - 0x27, 0x4b, 0x30, 0x51, 0xa5, 0x8e, 0x5f, 0x3f, 0x78, 0x40, 0x9f, 0x31, 0x23, 0xc3, 0x78, 0x1a, - 0x27, 0x40, 0x0c, 0x6b, 0x2f, 0xa2, 0xf4, 0xc8, 0x2a, 0x93, 0x89, 0x7c, 0x0a, 0x83, 0x55, 0xcf, - 0x0f, 0x2b, 0xcf, 0xc4, 0x6c, 0x2d, 0xcf, 0x69, 0x39, 0xb0, 0x72, 0x5e, 0x3e, 0x0f, 0x14, 0x78, - 0x7e, 0x58, 0xdb, 0x33, 0x32, 0xc1, 0x71, 0x12, 0xf2, 0x0c, 0xa6, 0xcd, 0x99, 0x52, 0x04, 0xda, - 0x0f, 0x0b, 0xe3, 0x26, 0x6d, 0x3a, 0xe6, 0x24, 0x95, 0x1b, 0x42, 0xfa, 0x95, 0xf8, 0x7c, 0xfc, - 0x08, 0xf1, 0xba, 0x45, 0x90, 0xc6, 0x4f, 0xd6, 0xf1, 0x5d, 0x25, 0xde, 0xa2, 0x72, 0xc0, 0x03, - 0xf4, 0x47, 0xa2, 0x5c, 0x83, 0x1d, 0x9c, 0x6d, 0x51, 0x13, 0x4e, 0x10, 0x7f, 0x8c, 0xcb, 0x4e, - 0xb0, 0x92, 0x2d, 0x38, 0xbb, 0x13, 0xd0, 0x2d, 0x9f, 0x3e, 0x71, 0xe9, 0x53, 0x29, 0x0f, 0xa2, - 0xc4, 0x6c, 0x4c, 0x5e, 0x9b, 0x63, 0xd3, 0x04, 0x26, 0x99, 0xc9, 0x7b, 0x00, 0x5b, 0x6e, 0xab, - 0x45, 0x1b, 0x78, 0xd8, 0x3f, 0x8a, 0xa2, 0xf0, 0x20, 0xa3, 0x8d, 0xd0, 0x9a, 0xd7, 0x6a, 0xea, - 0x2a, 0xd5, 0x88, 0x49, 0x05, 0xc6, 0x57, 0x5b, 0xf5, 0x66, 0x47, 0x04, 0xe5, 0x04, 0x38, 0x53, - 0x8a, 0x84, 0x91, 0x2e, 0x47, 0xd4, 0x12, 0x1f, 0xb9, 0xc9, 0x42, 0x1e, 0x00, 0x11, 0x00, 0x31, - 0x6a, 0x9d, 0xbd, 0x26, 0x15, 0x9f, 0x3b, 0x3a, 0x28, 0xa5, 0x20, 0x1c, 0xee, 0x46, 0x1e, 0xc6, - 0x04, 0xdb, 0xdc, 0x7b, 0x30, 0xaa, 0x8d, 0xf9, 0x94, 0xec, 0x28, 0xd3, 0x7a, 0x76, 0x94, 0x11, - 0x3d, 0x0b, 0xca, 0x3f, 0xcd, 0xc1, 0xc5, 0xf4, 0x6f, 0x49, 0xd8, 0x26, 0x9b, 0x30, 0xa2, 0x80, - 0xea, 0x3e, 0x9c, 0x34, 0xb8, 0x63, 0x5b, 0x3b, 0xfe, 0x41, 0xcb, 0x99, 0x47, 0x6f, 0x7d, 0x24, - 0xe3, 0x39, 0x4e, 0xc1, 0xfe, 0xf6, 0x30, 0x4c, 0xe3, 0xbd, 0x8f, 0xf8, 0x3c, 0xf5, 0x09, 0x66, - 0x39, 0x42, 0x98, 0x76, 0xa8, 0x23, 0xfc, 0xbb, 0x1c, 0x1e, 0xcf, 0xf7, 0x67, 0x30, 0x90, 0xb7, - 0xf5, 0x48, 0xa4, 0xbc, 0xf6, 0x8e, 0x93, 0x04, 0xea, 0x4d, 0x88, 0x42, 0x94, 0x5e, 0x37, 0x02, - 0x61, 0x4e, 0x3c, 0xe9, 0xf5, 0x9f, 0x74, 0xd2, 0xdb, 0x51, 0x93, 0x1e, 0xcf, 0x9e, 0xf3, 0x9a, - 0x36, 0xe9, 0xbd, 0xfc, 0xd9, 0x6e, 0xf0, 0x65, 0xcf, 0x76, 0x43, 0x2f, 0x36, 0xdb, 0x0d, 0x3f, - 0xe7, 0x6c, 0x77, 0x0f, 0x26, 0x36, 0x28, 0x6d, 0x68, 0xc7, 0x93, 0x23, 0xd1, 0xea, 0xd9, 0xa2, - 0xe8, 0x78, 0x4e, 0x3b, 0xa3, 0x8c, 0x71, 0x65, 0xce, 0x9a, 0xf0, 0x37, 0x33, 0x6b, 0x8e, 0xbe, - 0xe4, 0x59, 0x73, 0xec, 0x45, 0x66, 0xcd, 0xc4, 0xd4, 0x37, 0x7e, 0xea, 0xa9, 0xef, 0x45, 0x66, - 0xab, 0x7f, 0x9c, 0x87, 0x59, 0xf6, 0x01, 0x34, 0x9f, 0xd0, 0x6a, 0x75, 0x45, 0x04, 0x70, 0x45, - 0x81, 0x52, 0x07, 0x5e, 0x20, 0xef, 0x3a, 0xe0, 0xdf, 0x0c, 0xd6, 0xf6, 0x7c, 0x19, 0x6c, 0x82, - 0x7f, 0x93, 0x0a, 0x0c, 0xf2, 0x2f, 0xa4, 0xd8, 0x67, 0xa4, 0xa6, 0xca, 0x90, 0xab, 0x7f, 0x5f, - 0xb6, 0xe0, 0x24, 0x77, 0x61, 0x3a, 0xed, 0x53, 0x11, 0x6e, 0x8c, 0xa9, 0x76, 0xca, 0x67, 0xf2, - 0x1a, 0x4c, 0xc6, 0x3e, 0x06, 0xfe, 0xec, 0x8b, 0x3d, 0x11, 0x18, 0x1f, 0xc2, 0x8b, 0xa8, 0x67, - 0x01, 0x8a, 0xc9, 0x56, 0x88, 0x79, 0xfc, 0x35, 0x10, 0x37, 0xbc, 0x85, 0xb5, 0x1d, 0xdf, 0x5f, - 0xdb, 0x02, 0x6d, 0x7d, 0x8c, 0xc1, 0xd2, 0x4a, 0x40, 0xa0, 0xe9, 0x77, 0x45, 0xd3, 0xef, 0x8a, - 0xd0, 0xef, 0x96, 0xa6, 0x5f, 0xf6, 0xb7, 0x55, 0xc1, 0x10, 0x69, 0x9d, 0x5f, 0xdd, 0xb9, 0x1a, - 0x12, 0x17, 0xb6, 0xc5, 0x3a, 0x92, 0xa8, 0x82, 0xc4, 0x5b, 0x7f, 0x9e, 0xe3, 0xe1, 0x16, 0xff, - 0x37, 0x2e, 0x47, 0x2f, 0x12, 0x02, 0xf1, 0x6b, 0x51, 0x22, 0x17, 0x91, 0x74, 0xc6, 0x77, 0xea, - 0x8f, 0xa3, 0x18, 0x94, 0xef, 0xb3, 0xb9, 0x54, 0x47, 0x08, 0x63, 0x68, 0x56, 0x69, 0x4a, 0x47, - 0xee, 0xde, 0x95, 0x93, 0xac, 0xc8, 0x67, 0xc3, 0xc1, 0xe6, 0x24, 0xab, 0x33, 0x60, 0x14, 0xf0, - 0xa4, 0x65, 0xf3, 0x3c, 0x24, 0xa9, 0x35, 0x78, 0x27, 0x99, 0x49, 0x03, 0x2d, 0xd9, 0x28, 0x93, - 0x86, 0xae, 0xc6, 0x28, 0xa7, 0xc6, 0x0e, 0x5c, 0xb0, 0xe9, 0xa1, 0xf7, 0x84, 0xbe, 0x5c, 0xb1, - 0x5f, 0xc1, 0x79, 0x53, 0x20, 0xbf, 0x73, 0xc9, 0x1f, 0x08, 0xf9, 0x38, 0xfd, 0x59, 0x11, 0xc1, - 0xc0, 0x9f, 0x15, 0xe1, 0xaf, 0x13, 0xb0, 0x3f, 0xf5, 0xb5, 0x19, 0x71, 0x96, 0x07, 0x17, 0x4d, - 0xe1, 0xe5, 0x46, 0x03, 0x5f, 0x26, 0xae, 0xbb, 0x6d, 0xa7, 0x15, 0x92, 0x4d, 0x18, 0xd5, 0x7e, - 0xc6, 0xfc, 0x4c, 0x1a, 0x46, 0xec, 0x1b, 0x23, 0x80, 0x91, 0xdd, 0x39, 0x02, 0x5b, 0x14, 0x4a, - 0x71, 0xf5, 0x30, 0x95, 0xe9, 0x65, 0x56, 0x60, 0x5c, 0xfb, 0xa9, 0x0e, 0x63, 0x70, 0x82, 0xd5, - 0x4a, 0x30, 0x15, 0x66, 0xb2, 0x58, 0x75, 0x98, 0x4b, 0x53, 0x1a, 0x7f, 0x06, 0x80, 0x2c, 0x45, - 0x59, 0xfe, 0x7a, 0xc7, 0x11, 0x4f, 0x66, 0x65, 0xf8, 0xb3, 0xfe, 0x41, 0x3f, 0x5c, 0x10, 0x9d, - 0xf1, 0x32, 0x7b, 0x9c, 0xfc, 0x00, 0x46, 0xb5, 0x3e, 0x16, 0x4a, 0xbf, 0x22, 0x6f, 0x4c, 0x66, - 0x8d, 0x05, 0xee, 0x0f, 0xeb, 0x20, 0xa0, 0x16, 0xeb, 0xee, 0x95, 0x33, 0xb6, 0x2e, 0x92, 0x34, - 0x61, 0xc2, 0xec, 0x68, 0xe1, 0x12, 0xbc, 0x96, 0x5a, 0x88, 0x49, 0x2a, 0xdf, 0x08, 0x68, 0xd4, - 0x52, 0xbb, 0x7b, 0xe5, 0x8c, 0x1d, 0x93, 0x4d, 0xbe, 0x81, 0xb3, 0x89, 0x5e, 0x16, 0xfe, 0xde, - 0x57, 0x53, 0x0b, 0x4c, 0x50, 0xf3, 0x83, 0x26, 0x1f, 0xc1, 0x99, 0xc5, 0x26, 0x0b, 0x21, 0x0d, - 0x18, 0xd3, 0x3b, 0x5e, 0xf8, 0x2c, 0xaf, 0x76, 0x51, 0x25, 0x27, 0xe4, 0x1b, 0x68, 0xa1, 0x4b, - 0xec, 0xfb, 0x67, 0xe6, 0xe1, 0x99, 0x41, 0x3c, 0x0c, 0x83, 0xfc, 0xb7, 0xf5, 0x07, 0x39, 0xb8, - 0xb0, 0xe5, 0xd3, 0x80, 0xb6, 0xea, 0xd4, 0xb8, 0x7b, 0xf2, 0x82, 0x23, 0x22, 0xeb, 0xdc, 0x2a, - 0xff, 0xc2, 0xe7, 0x56, 0xd6, 0xbf, 0xc9, 0x41, 0x31, 0xad, 0xca, 0x55, 0xda, 0x6a, 0x90, 0x2d, - 0x28, 0xc4, 0xdb, 0x20, 0xbe, 0x18, 0x4b, 0xa5, 0x78, 0xcf, 0x6c, 0xed, 0xca, 0x19, 0x3b, 0xc1, - 0x4d, 0x36, 0xe0, 0xac, 0x06, 0x13, 0xe7, 0x46, 0xf9, 0x93, 0x9c, 0x1b, 0xb1, 0x1e, 0x4e, 0xb0, - 0xea, 0xc7, 0x6e, 0x2b, 0xb8, 0xea, 0x2e, 0x7a, 0x87, 0x8e, 0xdb, 0x62, 0x86, 0x8a, 0x96, 0x44, - 0x10, 0x22, 0xa8, 0x50, 0x3b, 0x3f, 0x48, 0x42, 0xa8, 0xbc, 0x86, 0xa7, 0x48, 0xac, 0x0f, 0x71, - 0x75, 0x10, 0xce, 0x63, 0x9e, 0xf8, 0x40, 0x09, 0xbb, 0x02, 0x03, 0xdb, 0x6b, 0xd5, 0x85, 0xb2, - 0x48, 0xa3, 0xc0, 0x93, 0xef, 0x34, 0x83, 0x5a, 0xdd, 0xb1, 0x39, 0xc2, 0xfa, 0x00, 0xc8, 0x32, - 0x0d, 0xc5, 0x1b, 0x23, 0x8a, 0xef, 0x3a, 0x0c, 0x09, 0x90, 0xe0, 0xc4, 0x23, 0x11, 0xf1, 0x62, - 0x89, 0x2d, 0x71, 0xd6, 0x96, 0xb4, 0xf3, 0x9a, 0xd4, 0x09, 0xb4, 0x45, 0xff, 0x5d, 0x18, 0xf6, - 0x05, 0x4c, 0xac, 0xf9, 0x13, 0xea, 0x09, 0x29, 0x04, 0xf3, 0xa3, 0x3a, 0x49, 0x63, 0xab, 0xbf, - 0xac, 0x35, 0x4c, 0x94, 0xb5, 0xb9, 0xba, 0xb8, 0xc0, 0xb4, 0x2a, 0x94, 0x25, 0xbb, 0xe3, 0x36, - 0xde, 0xbc, 0x09, 0xa9, 0x9e, 0x44, 0x01, 0x55, 0x83, 0x13, 0x88, 0x48, 0x0f, 0xa7, 0x91, 0x58, - 0x6f, 0xaa, 0xb4, 0x5b, 0x29, 0xd2, 0xb2, 0x9e, 0x42, 0xda, 0xc0, 0x84, 0x62, 0xcb, 0x18, 0x64, - 0xf8, 0x32, 0x2a, 0xe1, 0xc0, 0x1c, 0xdf, 0x42, 0xb0, 0x56, 0x89, 0x87, 0x60, 0x3d, 0x35, 0xed, - 0x2e, 0xc0, 0x88, 0x82, 0xa9, 0x88, 0x01, 0xae, 0x2b, 0x83, 0x7e, 0xf7, 0x4d, 0x9e, 0x6f, 0xa2, - 0xae, 0x04, 0x44, 0x7c, 0xac, 0x08, 0xfe, 0x4d, 0x7f, 0xcb, 0x45, 0x04, 0xd4, 0x0f, 0xbf, 0xd5, - 0x22, 0xa2, 0x8c, 0x73, 0xa7, 0x29, 0xc2, 0xa0, 0xdf, 0x9d, 0x3f, 0x89, 0xa2, 0xbe, 0xe5, 0x22, - 0x98, 0xa2, 0xbe, 0xbd, 0x22, 0xa8, 0x4c, 0xcd, 0xc7, 0x07, 0x69, 0xa2, 0x90, 0xa5, 0x64, 0x21, - 0xf2, 0x44, 0x25, 0xc6, 0xd1, 0xb5, 0x3f, 0x28, 0x5c, 0xe4, 0xca, 0xfa, 0x39, 0x14, 0xc3, 0x14, - 0xf6, 0xed, 0x16, 0xf3, 0x8f, 0x72, 0x3c, 0x51, 0x60, 0x75, 0x53, 0x7b, 0x82, 0xb9, 0xf5, 0xc8, + 0x40, 0xf4, 0xb0, 0x00, 0x3d, 0x15, 0x43, 0x9a, 0x08, 0xfd, 0xa4, 0xfc, 0x1f, 0xaa, 0xbb, 0x0a, + 0x6c, 0x2b, 0xe2, 0x36, 0xa9, 0xb8, 0x74, 0x24, 0x07, 0x76, 0xc6, 0x29, 0x5f, 0xee, 0x5b, 0x3e, + 0xe5, 0xfb, 0x83, 0xbc, 0xbc, 0xa1, 0x91, 0x3c, 0x68, 0x3d, 0xf5, 0x61, 0x5f, 0x6a, 0x0b, 0x4e, + 0xb4, 0x4e, 0xa7, 0x56, 0x8e, 0x54, 0xe4, 0x51, 0xa9, 0x4a, 0x56, 0x36, 0xa1, 0x8e, 0x1d, 0x22, + 0x84, 0x71, 0x7a, 0x8a, 0xbb, 0x22, 0x8d, 0x2b, 0x7e, 0x0e, 0xd7, 0xf7, 0xe2, 0xe7, 0x70, 0x3f, + 0x86, 0x19, 0x79, 0x35, 0x6a, 0x81, 0xb6, 0x42, 0xea, 0xcb, 0x13, 0xfb, 0x89, 0x28, 0xe9, 0x1b, + 0xa6, 0xf7, 0x2b, 0x40, 0x5f, 0xd9, 0xde, 0x10, 0x1e, 0x1d, 0xf6, 0x27, 0xb9, 0x62, 0x06, 0xc4, + 0xf1, 0x3b, 0x6f, 0x46, 0xf8, 0xdb, 0x15, 0x56, 0x5d, 0xee, 0x67, 0x71, 0x65, 0xaa, 0x3e, 0x5b, + 0x07, 0x59, 0x0b, 0x70, 0xc1, 0x2c, 0x7e, 0x8b, 0xfa, 0x87, 0x2e, 0xee, 0xbd, 0xab, 0x34, 0x94, + 0x85, 0xe6, 0xa2, 0x42, 0x89, 0x1e, 0x50, 0x2d, 0xcc, 0xc0, 0xff, 0x9d, 0x87, 0x52, 0x6a, 0x23, + 0xca, 0x41, 0xe0, 0xee, 0xb7, 0x30, 0x83, 0xc6, 0x45, 0xe8, 0x7f, 0xe0, 0xb6, 0x1a, 0xba, 0x21, + 0xf9, 0xd8, 0x6d, 0x35, 0x6c, 0x84, 0x32, 0x1b, 0xa4, 0xda, 0xd9, 0x43, 0x02, 0xcd, 0x44, 0x0e, + 0x3a, 0x7b, 0x35, 0x46, 0xa4, 0xdb, 0x20, 0x82, 0x8c, 0x5c, 0x87, 0x21, 0x99, 0x6d, 0xad, 0x2f, + 0xf2, 0x9e, 0xc9, 0x34, 0x6b, 0x12, 0x47, 0x3e, 0x82, 0xe1, 0x75, 0x1a, 0x3a, 0x0d, 0x27, 0x74, + 0xc4, 0xd8, 0x91, 0x0f, 0x61, 0x48, 0x70, 0xa5, 0x20, 0x56, 0xe8, 0xe1, 0x43, 0x01, 0xb1, 0x15, + 0x0b, 0x2a, 0xd0, 0x0d, 0xda, 0x4d, 0xe7, 0x99, 0x0a, 0x26, 0x65, 0x0a, 0x8c, 0x40, 0xe4, 0x1d, + 0x33, 0xe4, 0x22, 0x3a, 0x3e, 0x4b, 0x55, 0x48, 0x14, 0x90, 0xb1, 0x82, 0x61, 0x20, 0x91, 0xaa, + 0x45, 0x36, 0x41, 0x2b, 0x95, 0xdb, 0xa0, 0xb4, 0x4d, 0x46, 0xeb, 0xb7, 0x46, 0xe1, 0xec, 0x96, + 0xb3, 0xef, 0xb6, 0xd8, 0x8e, 0xc2, 0xa6, 0x81, 0xd7, 0xf1, 0xeb, 0x94, 0x94, 0x61, 0xc2, 0x0c, + 0xe0, 0xee, 0x11, 0x9e, 0xce, 0x36, 0x4d, 0x26, 0x8c, 0xcc, 0xc3, 0x88, 0xba, 0x34, 0x2e, 0x76, + 0x3a, 0x29, 0x97, 0xc9, 0x57, 0xce, 0xd8, 0x11, 0x19, 0x79, 0xcf, 0x38, 0x7c, 0x9c, 0x54, 0xf9, + 0x0f, 0x90, 0x76, 0x9e, 0x47, 0xd8, 0xb6, 0xbc, 0x86, 0xb9, 0x5b, 0xe3, 0x27, 0x6c, 0x3f, 0x48, + 0x9c, 0x47, 0x0e, 0x18, 0x35, 0x4e, 0x38, 0x65, 0x71, 0xa3, 0x9a, 0x99, 0xbd, 0x3e, 0xe5, 0xa4, + 0xf2, 0x4b, 0x18, 0x7d, 0xd0, 0xd9, 0xa3, 0xf2, 0xe4, 0x75, 0x50, 0x6c, 0xde, 0xe2, 0xd7, 0x12, + 0x04, 0x7e, 0xf7, 0x4d, 0xfe, 0x15, 0x3f, 0xee, 0xec, 0xd1, 0xe4, 0xb3, 0x08, 0x6c, 0xd5, 0xd4, + 0x84, 0x91, 0x03, 0x28, 0xc4, 0x6f, 0x10, 0x88, 0x2e, 0xed, 0x72, 0xef, 0x01, 0x13, 0xfd, 0x68, + 0x8f, 0x2f, 0xf0, 0xb8, 0x66, 0xa3, 0x90, 0x84, 0x54, 0xf2, 0x63, 0x98, 0x49, 0x75, 0x89, 0xab, + 0x3b, 0x90, 0xdd, 0xbd, 0xed, 0xb8, 0x04, 0xc5, 0xb4, 0x26, 0x2f, 0x5c, 0x1a, 0x25, 0xa7, 0x97, + 0x42, 0x1a, 0x30, 0x19, 0x8b, 0x8c, 0x17, 0x2f, 0xcc, 0x64, 0xc7, 0xda, 0xe3, 0xae, 0x49, 0x26, + 0x69, 0x4e, 0x2d, 0x2b, 0x2e, 0x92, 0xac, 0xc1, 0x88, 0xf2, 0x45, 0x89, 0xdc, 0x7c, 0x69, 0x7e, + 0xb7, 0xe2, 0xf1, 0x51, 0x69, 0x3a, 0xf2, 0xbb, 0x19, 0x32, 0x23, 0x01, 0xe4, 0x97, 0xe1, 0xaa, + 0x1a, 0xa2, 0x9b, 0x7e, 0xba, 0x87, 0x52, 0x3c, 0xee, 0x70, 0x33, 0x3e, 0xc2, 0xb3, 0xe8, 0x77, + 0xef, 0x56, 0xf2, 0xc5, 0xdc, 0xca, 0x19, 0xbb, 0xb7, 0x68, 0xf2, 0xab, 0x39, 0x38, 0x97, 0x51, + 0xea, 0x18, 0x96, 0xda, 0xd3, 0x6d, 0x8c, 0x96, 0x27, 0xde, 0xfb, 0x73, 0x1b, 0xd1, 0xfd, 0x58, + 0xe9, 0x3f, 0x36, 0xda, 0x9d, 0x51, 0x12, 0xb9, 0x03, 0xb0, 0xef, 0x86, 0x62, 0x8c, 0x61, 0x9a, + 0xba, 0xe4, 0x07, 0xca, 0xd4, 0xb6, 0xef, 0x86, 0x62, 0xa4, 0xfd, 0x7e, 0xae, 0xe7, 0xbc, 0x8e, + 0xd9, 0xeb, 0x46, 0xe7, 0x5f, 0xed, 0x36, 0xe9, 0x45, 0xd4, 0x95, 0x3b, 0xc7, 0x47, 0xa5, 0x37, + 0x54, 0x0a, 0xb4, 0x3a, 0x52, 0xc9, 0x5b, 0xbe, 0x35, 0x47, 0xd1, 0x19, 0xed, 0xe9, 0xb9, 0xb4, + 0xbc, 0x01, 0x83, 0xe8, 0x99, 0x0a, 0x8a, 0xe3, 0x68, 0xbb, 0x61, 0xe2, 0x2e, 0xf4, 0x5f, 0xe9, + 0xbb, 0x35, 0x41, 0x43, 0x56, 0x98, 0x0d, 0x84, 0xbb, 0x45, 0x69, 0xb3, 0x88, 0x34, 0x7f, 0xc2, + 0x8e, 0xe6, 0x28, 0x99, 0x7f, 0xc7, 0x78, 0x9e, 0xc4, 0x64, 0xab, 0x00, 0x0c, 0xfb, 0x62, 0xba, + 0xbd, 0xdf, 0x3f, 0xdc, 0x5f, 0x18, 0xe0, 0x33, 0x82, 0xbc, 0x4b, 0xf2, 0xeb, 0xc3, 0xfc, 0xe2, + 0xf9, 0x4e, 0xcb, 0x7d, 0xe4, 0x46, 0x33, 0xb3, 0xee, 0xd3, 0x8e, 0xde, 0x09, 0x13, 0x16, 0x67, + 0xc6, 0x8b, 0x60, 0xca, 0xfd, 0x9d, 0xef, 0xe9, 0xfe, 0x7e, 0x53, 0x3b, 0x28, 0xd6, 0xd2, 0xf9, + 0x72, 0xcb, 0xc2, 0x74, 0x37, 0x47, 0x27, 0xc8, 0x5f, 0xc3, 0x20, 0x66, 0xe0, 0xe5, 0xa7, 0xf0, + 0xa3, 0xf3, 0xb7, 0x44, 0x77, 0x76, 0xa9, 0x3e, 0x4f, 0xd9, 0x2b, 0x92, 0x49, 0x70, 0x8d, 0x23, + 0xc0, 0xd0, 0x38, 0x42, 0xc8, 0x36, 0x4c, 0x6d, 0xb1, 0x8d, 0x2e, 0xbf, 0xd1, 0xd0, 0xf6, 0x85, + 0x4b, 0x90, 0x3b, 0x1b, 0x71, 0xa3, 0xdd, 0x96, 0xe8, 0x1a, 0x55, 0x78, 0x7d, 0xaf, 0x99, 0xc2, + 0x4e, 0x96, 0x60, 0xa2, 0x4a, 0x1d, 0xbf, 0x7e, 0xf0, 0x80, 0x3e, 0x63, 0x46, 0x86, 0xf1, 0x34, + 0x4e, 0x80, 0x18, 0xd6, 0x5e, 0x44, 0xe9, 0x91, 0x55, 0x26, 0x13, 0xf9, 0x14, 0x06, 0xab, 0x9e, + 0x1f, 0x56, 0x9e, 0x89, 0xd9, 0x5a, 0x9e, 0xd3, 0x72, 0x60, 0xe5, 0xbc, 0x7c, 0x1e, 0x28, 0xf0, + 0xfc, 0xb0, 0xb6, 0x67, 0x64, 0x82, 0xe3, 0x24, 0xe4, 0x19, 0x4c, 0x9b, 0x33, 0xa5, 0x08, 0xb4, + 0x1f, 0x16, 0xc6, 0x4d, 0xda, 0x74, 0xcc, 0x49, 0x2a, 0x37, 0x84, 0xf4, 0x2b, 0xf1, 0xf9, 0xf8, + 0x11, 0xe2, 0x75, 0x8b, 0x20, 0x8d, 0x9f, 0xac, 0xe3, 0xbb, 0x4a, 0xbc, 0x45, 0xe5, 0x80, 0x07, + 0xe8, 0x8f, 0x44, 0xb9, 0x06, 0x3b, 0x38, 0xdb, 0xa2, 0x26, 0x9c, 0x20, 0xfe, 0x18, 0x97, 0x9d, + 0x60, 0x25, 0x5b, 0x70, 0x76, 0x27, 0xa0, 0x5b, 0x3e, 0x7d, 0xe2, 0xd2, 0xa7, 0x52, 0x1e, 0x44, + 0x89, 0xd9, 0x98, 0xbc, 0x36, 0xc7, 0xa6, 0x09, 0x4c, 0x32, 0x93, 0xf7, 0x00, 0xb6, 0xdc, 0x56, + 0x8b, 0x36, 0xf0, 0xb0, 0x7f, 0x14, 0x45, 0xe1, 0x41, 0x46, 0x1b, 0xa1, 0x35, 0xaf, 0xd5, 0xd4, + 0x55, 0xaa, 0x11, 0x93, 0x0a, 0x8c, 0xaf, 0xb6, 0xea, 0xcd, 0x8e, 0x08, 0xca, 0x09, 0x70, 0xa6, + 0x14, 0x09, 0x23, 0x5d, 0x8e, 0xa8, 0x25, 0x3e, 0x72, 0x93, 0x85, 0x3c, 0x00, 0x22, 0x00, 0x62, + 0xd4, 0x3a, 0x7b, 0x4d, 0x2a, 0x3e, 0x77, 0x74, 0x50, 0x4a, 0x41, 0x38, 0xdc, 0x8d, 0x3c, 0x8c, + 0x09, 0xb6, 0xb9, 0xf7, 0x60, 0x54, 0x1b, 0xf3, 0x29, 0xd9, 0x51, 0xa6, 0xf5, 0xec, 0x28, 0x23, + 0x7a, 0x16, 0x94, 0x7f, 0x9a, 0x83, 0x8b, 0xe9, 0xdf, 0x92, 0xb0, 0x4d, 0x36, 0x61, 0x44, 0x01, + 0xd5, 0x7d, 0x38, 0x69, 0x70, 0xc7, 0xb6, 0x76, 0xfc, 0x83, 0x96, 0x33, 0x8f, 0xde, 0xfa, 0x48, + 0xc6, 0x73, 0x9c, 0x82, 0xfd, 0xed, 0x61, 0x98, 0xc6, 0x7b, 0x1f, 0xf1, 0x79, 0xea, 0x13, 0xcc, + 0x72, 0x84, 0x30, 0xed, 0x50, 0x47, 0xf8, 0x77, 0x39, 0x3c, 0x9e, 0xef, 0xcf, 0x60, 0x20, 0x6f, + 0xeb, 0x91, 0x48, 0x79, 0xed, 0x1d, 0x27, 0x09, 0xd4, 0x9b, 0x10, 0x85, 0x28, 0xbd, 0x6e, 0x04, + 0xc2, 0x9c, 0x78, 0xd2, 0xeb, 0x3f, 0xe9, 0xa4, 0xb7, 0xa3, 0x26, 0x3d, 0x9e, 0x3d, 0xe7, 0x35, + 0x6d, 0xd2, 0x7b, 0xf9, 0xb3, 0xdd, 0xe0, 0xcb, 0x9e, 0xed, 0x86, 0x5e, 0x6c, 0xb6, 0x1b, 0x7e, + 0xce, 0xd9, 0xee, 0x1e, 0x4c, 0x6c, 0x50, 0xda, 0xd0, 0x8e, 0x27, 0x47, 0xa2, 0xd5, 0xb3, 0x45, + 0xd1, 0xf1, 0x9c, 0x76, 0x46, 0x19, 0xe3, 0xca, 0x9c, 0x35, 0xe1, 0x6f, 0x66, 0xd6, 0x1c, 0x7d, + 0xc9, 0xb3, 0xe6, 0xd8, 0x8b, 0xcc, 0x9a, 0x89, 0xa9, 0x6f, 0xfc, 0xd4, 0x53, 0xdf, 0x8b, 0xcc, + 0x56, 0xff, 0x38, 0x0f, 0xb3, 0xec, 0x03, 0x68, 0x3e, 0xa1, 0xd5, 0xea, 0x8a, 0x08, 0xe0, 0x8a, + 0x02, 0xa5, 0x0e, 0xbc, 0x40, 0xde, 0x75, 0xc0, 0xbf, 0x19, 0xac, 0xed, 0xf9, 0x32, 0xd8, 0x04, + 0xff, 0x26, 0x15, 0x18, 0xe4, 0x5f, 0x48, 0xb1, 0xcf, 0x48, 0x4d, 0x95, 0x21, 0x57, 0xff, 0xbe, + 0x6c, 0xc1, 0x49, 0xee, 0xc2, 0x74, 0xda, 0xa7, 0x22, 0xdc, 0x18, 0x53, 0xed, 0x94, 0xcf, 0xe4, + 0x35, 0x98, 0x8c, 0x7d, 0x0c, 0xfc, 0xd9, 0x17, 0x7b, 0x22, 0x30, 0x3e, 0x84, 0x17, 0x51, 0xcf, + 0x02, 0x14, 0x93, 0xad, 0x10, 0xf3, 0xf8, 0x6b, 0x20, 0x6e, 0x78, 0x0b, 0x6b, 0x3b, 0xbe, 0xbf, + 0xb6, 0x05, 0xda, 0xfa, 0x18, 0x83, 0xa5, 0x95, 0x80, 0x40, 0xd3, 0xef, 0x8a, 0xa6, 0xdf, 0x15, + 0xa1, 0xdf, 0x2d, 0x4d, 0xbf, 0xec, 0x6f, 0xab, 0x82, 0x21, 0xd2, 0x3a, 0xbf, 0xba, 0x73, 0x35, + 0x24, 0x2e, 0x6c, 0x8b, 0x75, 0x24, 0x51, 0x05, 0x89, 0xb7, 0xfe, 0x3c, 0xc7, 0xc3, 0x2d, 0xfe, + 0x6f, 0x5c, 0x8e, 0x5e, 0x24, 0x04, 0xe2, 0xd7, 0xa2, 0x44, 0x2e, 0x22, 0xe9, 0x8c, 0xef, 0xd4, + 0x1f, 0x47, 0x31, 0x28, 0xdf, 0x67, 0x73, 0xa9, 0x8e, 0x10, 0xc6, 0xd0, 0xac, 0xd2, 0x94, 0x8e, + 0xdc, 0xbd, 0x2b, 0x27, 0x59, 0x91, 0xcf, 0x86, 0x83, 0xcd, 0x49, 0x56, 0x67, 0xc0, 0x28, 0xe0, + 0x49, 0xcb, 0xe6, 0x79, 0x48, 0x52, 0x6b, 0xf0, 0x4e, 0x32, 0x93, 0x06, 0x5a, 0xb2, 0x51, 0x26, + 0x0d, 0x5d, 0x8d, 0x51, 0x4e, 0x8d, 0x1d, 0xb8, 0x60, 0xd3, 0x43, 0xef, 0x09, 0x7d, 0xb9, 0x62, + 0xbf, 0x82, 0xf3, 0xa6, 0x40, 0x7e, 0xe7, 0x92, 0x3f, 0x10, 0xf2, 0x71, 0xfa, 0xb3, 0x22, 0x82, + 0x81, 0x3f, 0x2b, 0xc2, 0x5f, 0x27, 0x60, 0x7f, 0xea, 0x6b, 0x33, 0xe2, 0x2c, 0x0f, 0x2e, 0x9a, + 0xc2, 0xcb, 0x8d, 0x06, 0xbe, 0x4c, 0x5c, 0x77, 0xdb, 0x4e, 0x2b, 0x24, 0x9b, 0x30, 0xaa, 0xfd, + 0x8c, 0xf9, 0x99, 0x34, 0x8c, 0xd8, 0x37, 0x46, 0x00, 0x23, 0xbb, 0x73, 0x04, 0xb6, 0x28, 0x94, + 0xe2, 0xea, 0x61, 0x2a, 0xd3, 0xcb, 0xac, 0xc0, 0xb8, 0xf6, 0x53, 0x1d, 0xc6, 0xe0, 0x04, 0xab, + 0x95, 0x60, 0x2a, 0xcc, 0x64, 0xb1, 0xea, 0x30, 0x97, 0xa6, 0x34, 0xfe, 0x0c, 0x00, 0x59, 0x8a, + 0xb2, 0xfc, 0xf5, 0x8e, 0x23, 0x9e, 0xcc, 0xca, 0xf0, 0x67, 0xfd, 0x83, 0x7e, 0xb8, 0x20, 0x3a, + 0xe3, 0x65, 0xf6, 0x38, 0xf9, 0x01, 0x8c, 0x6a, 0x7d, 0x2c, 0x94, 0x7e, 0x45, 0xde, 0x98, 0xcc, + 0x1a, 0x0b, 0xdc, 0x1f, 0xd6, 0x41, 0x40, 0x2d, 0xd6, 0xdd, 0x2b, 0x67, 0x6c, 0x5d, 0x24, 0x69, + 0xc2, 0x84, 0xd9, 0xd1, 0xc2, 0x25, 0x78, 0x2d, 0xb5, 0x10, 0x93, 0x54, 0xbe, 0x11, 0xd0, 0xa8, + 0xa5, 0x76, 0xf7, 0xca, 0x19, 0x3b, 0x26, 0x9b, 0x7c, 0x03, 0x67, 0x13, 0xbd, 0x2c, 0xfc, 0xbd, + 0xaf, 0xa6, 0x16, 0x98, 0xa0, 0xe6, 0x07, 0x4d, 0x3e, 0x82, 0x33, 0x8b, 0x4d, 0x16, 0x42, 0x1a, + 0x30, 0xa6, 0x77, 0xbc, 0xf0, 0x59, 0x5e, 0xed, 0xa2, 0x4a, 0x4e, 0xc8, 0x37, 0xd0, 0x42, 0x97, + 0xd8, 0xf7, 0xcf, 0xcc, 0xc3, 0x33, 0x83, 0x78, 0x18, 0x06, 0xf9, 0x6f, 0xeb, 0x0f, 0x72, 0x70, + 0x61, 0xcb, 0xa7, 0x01, 0x6d, 0xd5, 0xa9, 0x71, 0xf7, 0xe4, 0x05, 0x47, 0x44, 0xd6, 0xb9, 0x55, + 0xfe, 0x85, 0xcf, 0xad, 0xac, 0x7f, 0x93, 0x83, 0x62, 0x5a, 0x95, 0xab, 0xb4, 0xd5, 0x20, 0x5b, + 0x50, 0x88, 0xb7, 0x41, 0x7c, 0x31, 0x96, 0x4a, 0xf1, 0x9e, 0xd9, 0xda, 0x95, 0x33, 0x76, 0x82, + 0x9b, 0x6c, 0xc0, 0x59, 0x0d, 0x26, 0xce, 0x8d, 0xf2, 0x27, 0x39, 0x37, 0x62, 0x3d, 0x9c, 0x60, + 0xd5, 0x8f, 0xdd, 0x56, 0x70, 0xd5, 0x5d, 0xf4, 0x0e, 0x1d, 0xb7, 0xc5, 0x0c, 0x15, 0x2d, 0x89, + 0x20, 0x44, 0x50, 0xa1, 0x76, 0x7e, 0x90, 0x84, 0x50, 0x79, 0x0d, 0x4f, 0x91, 0x58, 0x1f, 0xe2, + 0xea, 0x20, 0x9c, 0xc7, 0x3c, 0xf1, 0x81, 0x12, 0x76, 0x05, 0x06, 0xb6, 0xd7, 0xaa, 0x0b, 0x65, + 0x91, 0x46, 0x81, 0x27, 0xdf, 0x69, 0x06, 0xb5, 0xba, 0x63, 0x73, 0x84, 0xf5, 0x01, 0x90, 0x65, + 0x1a, 0x8a, 0x37, 0x46, 0x14, 0xdf, 0x75, 0x18, 0x12, 0x20, 0xc1, 0x89, 0x47, 0x22, 0xe2, 0xc5, + 0x12, 0x5b, 0xe2, 0xac, 0x2d, 0x69, 0xe7, 0x35, 0xa9, 0x13, 0x68, 0x8b, 0xfe, 0xbb, 0x30, 0xec, + 0x0b, 0x98, 0x58, 0xf3, 0x27, 0xd4, 0x13, 0x52, 0x08, 0xe6, 0x47, 0x75, 0x92, 0xc6, 0x56, 0x7f, + 0x59, 0x6b, 0x98, 0x28, 0x6b, 0x73, 0x75, 0x71, 0x81, 0x69, 0x55, 0x28, 0x4b, 0x76, 0xc7, 0x6d, + 0xbc, 0x79, 0x13, 0x52, 0x3d, 0x89, 0x02, 0xaa, 0x06, 0x27, 0x10, 0x91, 0x1e, 0x4e, 0x23, 0xb1, + 0xde, 0x54, 0x69, 0xb7, 0x52, 0xa4, 0x65, 0x3d, 0x85, 0xb4, 0x81, 0x09, 0xc5, 0x96, 0x31, 0xc8, + 0xf0, 0x65, 0x54, 0xc2, 0x81, 0x39, 0xbe, 0x85, 0x60, 0xad, 0x12, 0x0f, 0xc1, 0x7a, 0x6a, 0xda, + 0x5d, 0x80, 0x11, 0x05, 0x53, 0x11, 0x03, 0x5c, 0x57, 0x06, 0xfd, 0xee, 0x9b, 0x3c, 0xdf, 0x44, + 0x5d, 0x09, 0x88, 0xf8, 0x58, 0x11, 0xfc, 0x9b, 0xfe, 0x96, 0x8b, 0x08, 0xa8, 0x1f, 0x7e, 0xab, + 0x45, 0x44, 0x19, 0xe7, 0x4e, 0x53, 0x84, 0x41, 0xbf, 0x3b, 0x7f, 0x12, 0x45, 0x7d, 0xcb, 0x45, + 0x30, 0x45, 0x7d, 0x7b, 0x45, 0x50, 0x99, 0x9a, 0x8f, 0x0f, 0xd2, 0x44, 0x21, 0x4b, 0xc9, 0x42, + 0xe4, 0x89, 0x4a, 0x8c, 0xa3, 0x6b, 0x7f, 0x50, 0xb8, 0xc8, 0x95, 0xf5, 0x73, 0x28, 0x86, 0x29, + 0xec, 0xdb, 0x2d, 0xe6, 0x77, 0x72, 0x3c, 0x51, 0x60, 0x75, 0x53, 0x7b, 0x82, 0xb9, 0xf5, 0xc8, 0xd3, 0x02, 0x9a, 0xb4, 0xaf, 0x5d, 0x3b, 0x60, 0xc6, 0x80, 0x26, 0xa7, 0x13, 0x1e, 0xa8, 0x44, 0xfa, 0x78, 0xda, 0x1c, 0xa7, 0x26, 0xef, 0xc1, 0xb8, 0x06, 0x52, 0x3b, 0x41, 0xfe, 0xd4, 0x91, 0xce, 0xee, 0x36, 0x6c, 0x93, 0xd2, 0xfa, 0xeb, 0x1c, 0x4c, 0x55, 0x9f, 0x05, 0x21, 0x3d, 0xc4, @@ -16996,7 +16996,7 @@ var fileDescriptor_0ffcffcda38ae159 = []byte{ 0xe5, 0x96, 0x7a, 0x21, 0x77, 0xb3, 0x22, 0x1f, 0x11, 0xd7, 0xbe, 0x73, 0x32, 0x0a, 0x43, 0x8b, 0x4b, 0xf7, 0xca, 0x3b, 0x6b, 0xdb, 0x85, 0x33, 0xec, 0xc7, 0x82, 0xbd, 0x54, 0xde, 0x5e, 0x5a, 0x2c, 0xe4, 0xc8, 0x08, 0x0c, 0x54, 0xb7, 0xcb, 0xdb, 0x4b, 0x85, 0x3c, 0x19, 0x86, 0xfe, 0x9d, - 0xea, 0x92, 0x5d, 0xe8, 0x9b, 0xff, 0xad, 0xdf, 0xcd, 0x71, 0x47, 0xa3, 0xbc, 0xcc, 0xf7, 0xb5, + 0xea, 0x92, 0x5d, 0xe8, 0x9b, 0xff, 0x9d, 0xdf, 0xcd, 0x71, 0x47, 0xa3, 0xbc, 0xcc, 0xf7, 0xb5, 0x66, 0x50, 0x8a, 0x29, 0x4f, 0xbc, 0x98, 0x9c, 0x69, 0x3d, 0xe2, 0x2e, 0x60, 0xae, 0xcb, 0xe1, 0x0e, 0x12, 0xdc, 0xc8, 0xdd, 0xc9, 0x11, 0x1b, 0x83, 0x61, 0x62, 0xf6, 0x95, 0x92, 0x9c, 0x6e, 0x02, 0xcf, 0x5d, 0xea, 0x6a, 0x96, 0x91, 0x5f, 0x02, 0x4b, 0x97, 0x99, 0x61, 0x85, 0x7c, 0xf7, @@ -17134,100 +17134,100 @@ var fileDescriptor_0ffcffcda38ae159 = []byte{ 0x66, 0x76, 0x5d, 0x45, 0x3a, 0xdd, 0x4e, 0x50, 0x83, 0x93, 0x38, 0xdb, 0x4e, 0xa2, 0x89, 0x6c, 0xcf, 0x14, 0x5b, 0x0e, 0xb7, 0xfd, 0x4e, 0x10, 0xd2, 0x46, 0xd2, 0xa3, 0x64, 0x56, 0x46, 0x46, 0x46, 0x98, 0xe4, 0xbb, 0xf3, 0x64, 0x15, 0xe7, 0x36, 0x13, 0xdc, 0xcd, 0xe5, 0x96, 0x2e, 0x06, - 0xa7, 0x9e, 0x15, 0x75, 0xad, 0xc7, 0xac, 0x53, 0x56, 0xd9, 0xd9, 0x95, 0x52, 0x2a, 0x3a, 0x61, - 0xeb, 0xb2, 0x54, 0xf4, 0x01, 0x86, 0x01, 0x70, 0xf7, 0x5f, 0x2f, 0xcd, 0xc4, 0x2f, 0x1a, 0x91, - 0x4f, 0x61, 0x44, 0x32, 0xf7, 0x56, 0x48, 0x9c, 0x1b, 0x15, 0xb2, 0x08, 0xe3, 0xc6, 0x2d, 0x2a, - 0x65, 0xdd, 0xa4, 0xdd, 0xad, 0xea, 0xd2, 0xcf, 0xe3, 0xc6, 0x6d, 0x29, 0x25, 0x25, 0xed, 0x0e, - 0x55, 0xa6, 0x94, 0x8f, 0x60, 0x54, 0xa8, 0xb4, 0xab, 0x36, 0xb2, 0xfd, 0x6d, 0x33, 0x5a, 0x44, - 0x72, 0xa7, 0xe1, 0x86, 0x0b, 0x5e, 0xeb, 0x91, 0xbb, 0xdf, 0x53, 0x31, 0x49, 0x96, 0xdd, 0x79, - 0xf2, 0x15, 0x3e, 0x2b, 0x2f, 0x1f, 0xfb, 0xa7, 0xe1, 0x53, 0xcf, 0x7f, 0xec, 0xb6, 0xf6, 0x7b, - 0x88, 0xbc, 0x62, 0x8a, 0x8c, 0xf3, 0xc9, 0xd8, 0x91, 0xaf, 0x60, 0xae, 0x9a, 0x2d, 0xbc, 0xa7, - 0x90, 0xee, 0xcb, 0x4b, 0x15, 0x2e, 0x62, 0xf4, 0xcc, 0x69, 0xeb, 0xde, 0x55, 0xe8, 0x17, 0x3c, - 0x61, 0xa3, 0xf4, 0xd5, 0xd7, 0x3d, 0xbf, 0xd1, 0x5b, 0x62, 0xc9, 0x0c, 0xa4, 0x8d, 0xb1, 0x49, - 0x65, 0x7c, 0x01, 0xe7, 0xab, 0x99, 0xa2, 0x7b, 0x89, 0xe8, 0xb5, 0x93, 0xbc, 0x80, 0xaa, 0x38, - 0x65, 0xbd, 0xbb, 0xca, 0x5c, 0xc5, 0x39, 0x8d, 0xad, 0x43, 0x5b, 0x3e, 0x7d, 0x44, 0x7d, 0x0c, - 0xd7, 0xee, 0x15, 0xa8, 0x6c, 0x92, 0xcb, 0x96, 0xaf, 0xc2, 0xd9, 0x6a, 0x42, 0x54, 0x16, 0x4b, - 0xaf, 0xf3, 0x9f, 0x29, 0x6c, 0xe9, 0x09, 0xeb, 0xd5, 0x23, 0x4a, 0x68, 0x74, 0x99, 0x86, 0x3b, - 0xab, 0x3d, 0xb4, 0x24, 0xef, 0x13, 0x48, 0xc2, 0xdd, 0xbb, 0x8c, 0xb3, 0xaa, 0x71, 0x26, 0x29, - 0x32, 0x3f, 0xde, 0x4f, 0xe5, 0x59, 0x48, 0xcf, 0x62, 0xb3, 0x24, 0xbc, 0x89, 0x73, 0xa1, 0x08, - 0x59, 0x9e, 0x8d, 0xb6, 0x00, 0x1c, 0x12, 0xb9, 0xea, 0xb4, 0xe8, 0xe5, 0x80, 0x94, 0xb9, 0xf9, - 0xc7, 0x87, 0x87, 0x80, 0x5d, 0x4e, 0x84, 0xb2, 0x77, 0x15, 0xc1, 0xbd, 0xa0, 0x6b, 0x5e, 0xfd, - 0xb1, 0xee, 0x05, 0x65, 0xbf, 0xe3, 0xee, 0x41, 0x06, 0xdb, 0x9d, 0x17, 0x33, 0x3e, 0xfb, 0x61, - 0x04, 0x7e, 0x21, 0x20, 0x9a, 0xf1, 0xe3, 0x70, 0xe1, 0x41, 0x7a, 0x53, 0xfa, 0x16, 0xb1, 0x40, - 0x53, 0x72, 0xa6, 0x6a, 0x94, 0x5b, 0x11, 0x99, 0x4c, 0xb7, 0xa2, 0x5e, 0xd1, 0x6c, 0x5f, 0x3e, - 0xb1, 0x69, 0xbb, 0x89, 0x51, 0xd0, 0x87, 0x1e, 0xe7, 0x89, 0x02, 0x63, 0x93, 0xa8, 0xde, 0xf1, - 0x5b, 0x53, 0x22, 0xea, 0xc7, 0x50, 0xbc, 0x4a, 0xa8, 0x9c, 0xc4, 0x45, 0xaa, 0xd4, 0x83, 0x91, - 0xee, 0xe4, 0xc8, 0x06, 0x9c, 0x5b, 0xa6, 0xa1, 0x98, 0xe3, 0x6c, 0x1a, 0x84, 0xbe, 0x5b, 0x0f, - 0xbb, 0x1e, 0x0c, 0x4a, 0xdb, 0x24, 0x85, 0x67, 0xf7, 0x2d, 0x26, 0xaf, 0x9a, 0x2e, 0xaf, 0x2b, - 0x5f, 0x97, 0x10, 0x59, 0x71, 0xda, 0x70, 0x9a, 0x2a, 0x66, 0x0f, 0xf1, 0x21, 0x1e, 0x81, 0x93, - 0xcd, 0x5a, 0x88, 0xb2, 0x9f, 0x08, 0x6b, 0xeb, 0x16, 0x0c, 0x72, 0xa6, 0xcc, 0x05, 0x75, 0x4c, - 0xe7, 0x21, 0x77, 0x61, 0x44, 0x85, 0xd0, 0x10, 0x03, 0x95, 0x59, 0xaf, 0xbb, 0x30, 0xc2, 0x4d, - 0xab, 0x93, 0xb3, 0x7c, 0x00, 0x23, 0x2a, 0xe6, 0xe6, 0xd4, 0x2b, 0xfd, 0x27, 0x30, 0xae, 0x47, - 0xdf, 0x9c, 0x5e, 0x91, 0x1f, 0xe1, 0xf1, 0xad, 0x3c, 0x25, 0xc9, 0xe6, 0x9f, 0x89, 0x25, 0x85, - 0x11, 0x2a, 0xe5, 0x13, 0xa4, 0x04, 0x66, 0x56, 0xff, 0x6c, 0x82, 0x9b, 0x7c, 0x20, 0x6f, 0x32, - 0x29, 0xe6, 0x24, 0x51, 0x17, 0x9d, 0x4d, 0x70, 0x35, 0x3f, 0x0f, 0xb3, 0x9a, 0x60, 0x7b, 0x56, - 0xfb, 0x24, 0xc7, 0xcc, 0xbd, 0x55, 0x97, 0x25, 0x65, 0x13, 0x77, 0x69, 0x89, 0x87, 0x18, 0xb3, - 0x05, 0x5d, 0xce, 0x7e, 0xbb, 0x11, 0x3b, 0xe3, 0x3e, 0x5a, 0x81, 0x09, 0x6c, 0x66, 0xf3, 0xba, - 0xbc, 0x05, 0x19, 0x99, 0xbd, 0x49, 0x71, 0x5d, 0xd8, 0xba, 0x59, 0xd1, 0xe2, 0x7e, 0xe6, 0x4b, - 0x11, 0xb7, 0x2a, 0x83, 0x18, 0x4f, 0xde, 0xd8, 0xec, 0x9a, 0x5d, 0x48, 0x39, 0xd8, 0xee, 0xd9, - 0x17, 0x59, 0xe2, 0x7e, 0x01, 0x77, 0x87, 0xa9, 0x59, 0xc1, 0xb2, 0x85, 0xdd, 0xd0, 0x62, 0x23, - 0x52, 0x39, 0xd5, 0xa2, 0xf7, 0x18, 0xaf, 0x88, 0xa5, 0x3f, 0x55, 0xf9, 0x6a, 0x0f, 0x29, 0x52, - 0x13, 0xaf, 0xf5, 0xa4, 0x53, 0xc7, 0xa4, 0x17, 0xf8, 0x0a, 0x9b, 0x5e, 0x5e, 0x8f, 0xa7, 0x37, - 0x53, 0x4e, 0xae, 0x55, 0x84, 0x68, 0xba, 0x40, 0x33, 0x42, 0xb4, 0x6b, 0x1b, 0xb2, 0xd4, 0xff, - 0x19, 0x94, 0xa2, 0x00, 0x90, 0xd3, 0x75, 0x42, 0x76, 0x60, 0x22, 0x49, 0x68, 0x2a, 0x20, 0xdd, - 0x9e, 0x6c, 0x9a, 0xbb, 0x9a, 0xa5, 0x61, 0xfd, 0x1a, 0x8c, 0x08, 0x6c, 0x8b, 0x3d, 0xda, 0x9a, - 0xf5, 0xfc, 0x6b, 0x17, 0x3f, 0xac, 0xb8, 0x33, 0xf7, 0x52, 0x04, 0x25, 0x7b, 0xfb, 0xf4, 0x82, - 0x54, 0x7c, 0x46, 0x4c, 0x90, 0xd5, 0xa5, 0x7b, 0x7b, 0x1f, 0x3d, 0x16, 0x33, 0xfa, 0xf5, 0xf4, - 0x1d, 0xea, 0x44, 0xf7, 0xc4, 0x62, 0x49, 0x04, 0xf5, 0xbb, 0xb9, 0x49, 0x54, 0xfc, 0x92, 0x53, - 0x1a, 0x85, 0x0a, 0x8a, 0x2a, 0xca, 0x22, 0x18, 0x9c, 0x99, 0x22, 0x9e, 0xef, 0x86, 0xcf, 0x16, - 0xec, 0xb5, 0xc8, 0xad, 0xa0, 0x23, 0xa4, 0x6c, 0x90, 0x48, 0x7b, 0x8d, 0x7c, 0x89, 0x53, 0x89, - 0x10, 0x5f, 0xf1, 0xbc, 0x30, 0x08, 0x7d, 0xa7, 0x5d, 0xc5, 0xc7, 0xac, 0x33, 0x1b, 0x1d, 0xc5, - 0x70, 0xa7, 0xb1, 0x69, 0x21, 0xa5, 0x22, 0x8f, 0x7d, 0x5a, 0xe6, 0x1b, 0x75, 0xad, 0x26, 0x0d, - 0xd9, 0xc5, 0x72, 0xa9, 0xca, 0xcc, 0xf5, 0x2f, 0x53, 0x68, 0x0d, 0x66, 0x33, 0xf2, 0x05, 0xa9, - 0xd3, 0xdb, 0xee, 0xf9, 0x84, 0xe6, 0xba, 0x17, 0x4c, 0xbe, 0x82, 0x99, 0xd4, 0x84, 0x42, 0xca, - 0x03, 0xdd, 0x2d, 0xdd, 0x50, 0x2f, 0xe1, 0x8f, 0xa1, 0xc8, 0x2f, 0x74, 0x60, 0xdc, 0xb2, 0x91, - 0x5b, 0x26, 0xba, 0xe6, 0x93, 0x41, 0x10, 0x9f, 0xaf, 0xb3, 0xe9, 0xd4, 0x65, 0xf3, 0x69, 0x4c, - 0x2a, 0x22, 0x5f, 0xdf, 0xa6, 0x75, 0xef, 0x09, 0xf5, 0x9f, 0xa9, 0x0f, 0x2f, 0x0d, 0xd9, 0xed, - 0x2e, 0xd1, 0x16, 0xcc, 0xec, 0x52, 0xdf, 0x7d, 0xf4, 0x2c, 0x2e, 0x50, 0x6a, 0x26, 0x15, 0xdb, - 0x4d, 0xe2, 0xe7, 0x30, 0xbb, 0xe0, 0x1d, 0xb6, 0xc5, 0xad, 0x3d, 0x43, 0xa6, 0x3a, 0x8a, 0x4f, - 0xc7, 0xf7, 0x8e, 0x65, 0x9a, 0x53, 0xd7, 0x0a, 0x75, 0xbe, 0x05, 0xbc, 0xce, 0x7a, 0xc3, 0x0c, - 0x27, 0x48, 0x21, 0x89, 0x2e, 0x63, 0x48, 0x53, 0x4e, 0xe7, 0xdf, 0xc6, 0x41, 0x18, 0xe3, 0xe3, - 0xbe, 0x39, 0x6d, 0x10, 0xa6, 0xe1, 0xbb, 0xdf, 0x01, 0x4b, 0x91, 0xca, 0x0b, 0xcc, 0x96, 0x7a, - 0x82, 0xda, 0x6e, 0xc8, 0xb5, 0x65, 0xcb, 0x77, 0x9f, 0xb8, 0x4d, 0xba, 0x2f, 0xdc, 0x88, 0x66, - 0xd0, 0xb4, 0x89, 0xec, 0x56, 0x4f, 0x2d, 0xab, 0x42, 0xb3, 0xd9, 0x65, 0x8b, 0x45, 0xf4, 0xb4, - 0x0a, 0x8c, 0x12, 0x9d, 0xf8, 0xe3, 0x3a, 0x6f, 0xb7, 0xd9, 0x3a, 0xc1, 0x8c, 0x9b, 0xda, 0xf7, - 0x61, 0xac, 0xaa, 0x17, 0x9e, 0x52, 0x48, 0xe6, 0xa0, 0x50, 0xb7, 0x80, 0x7a, 0xd7, 0xbd, 0x4b, - 0x2c, 0xa8, 0x5a, 0x78, 0x4e, 0xd4, 0x8a, 0xcc, 0xd0, 0x19, 0xe3, 0xed, 0x39, 0xb5, 0x0a, 0xa4, - 0x3d, 0xbf, 0xa9, 0x42, 0x67, 0xd2, 0x9f, 0xab, 0xab, 0xf1, 0x17, 0x6d, 0xe2, 0xaf, 0xab, 0x12, - 0xab, 0xf7, 0x33, 0xc6, 0x2a, 0x26, 0xbe, 0xeb, 0xf3, 0xac, 0x3c, 0xce, 0x27, 0x7a, 0x6d, 0x4f, - 0x8f, 0xf3, 0x49, 0xbc, 0xe1, 0xa7, 0xc7, 0xf9, 0xa4, 0x3c, 0xd0, 0x57, 0x85, 0x42, 0xfc, 0xf9, - 0x40, 0xe5, 0x56, 0xca, 0x78, 0x1d, 0x51, 0x45, 0xf4, 0x64, 0xbe, 0x3b, 0xb8, 0x84, 0x15, 0x8c, - 0xde, 0x17, 0xea, 0xe2, 0xe1, 0x50, 0x75, 0x4b, 0x79, 0xc6, 0xe8, 0x81, 0x9e, 0xf1, 0x83, 0xbf, - 0x4a, 0xd4, 0xc5, 0x81, 0x1b, 0xcf, 0xf4, 0x11, 0x7b, 0xc6, 0xe8, 0x1e, 0x14, 0xf8, 0x03, 0x0d, - 0x51, 0xa2, 0xc4, 0x28, 0x9e, 0x30, 0xf9, 0x6e, 0x44, 0x97, 0x91, 0x52, 0x88, 0xa7, 0x97, 0x53, - 0x0a, 0xcb, 0xc8, 0x3b, 0xd7, 0x65, 0xfc, 0x43, 0x94, 0x44, 0x4e, 0x79, 0xbb, 0x12, 0x79, 0xe5, - 0xe6, 0xce, 0xa7, 0x60, 0xd4, 0x3e, 0x75, 0x4c, 0x4f, 0x39, 0xa7, 0x9a, 0x94, 0x92, 0x87, 0x6e, - 0xee, 0x42, 0x2a, 0x4e, 0x08, 0x0a, 0xf9, 0xf3, 0xe0, 0xe9, 0xaf, 0xb5, 0x47, 0xb7, 0xc3, 0xba, - 0xd0, 0xc8, 0x62, 0x6e, 0x9e, 0x84, 0x54, 0x94, 0x4a, 0xd5, 0xeb, 0x4a, 0x29, 0x4f, 0xc4, 0xbf, - 0x96, 0x72, 0x81, 0xc3, 0xa0, 0x88, 0x06, 0x64, 0xf7, 0xf7, 0xea, 0xc9, 0x43, 0xf9, 0xda, 0x4d, - 0x46, 0x49, 0xbd, 0x04, 0x64, 0xf6, 0xe0, 0x43, 0xf9, 0xbe, 0xcd, 0xcb, 0x16, 0xbc, 0x07, 0x17, - 0x63, 0xb7, 0x42, 0x4c, 0xc1, 0x37, 0xd3, 0xaf, 0x8e, 0xa4, 0xaa, 0x27, 0xdb, 0x10, 0xb8, 0x92, - 0xbc, 0x3d, 0x12, 0xeb, 0xf7, 0xd3, 0x4e, 0xa4, 0xeb, 0x30, 0x81, 0x73, 0x57, 0x40, 0xfd, 0x65, - 0xdf, 0xeb, 0xb4, 0xa3, 0x84, 0x33, 0x26, 0x38, 0x9e, 0xf9, 0x28, 0x8e, 0x55, 0x97, 0xd2, 0xc7, - 0xc4, 0x4d, 0x63, 0x44, 0xe8, 0x89, 0x70, 0x14, 0x30, 0x6d, 0x69, 0x44, 0xc4, 0xee, 0x5d, 0xf2, - 0x11, 0x4c, 0x46, 0x17, 0x90, 0xb9, 0x88, 0x14, 0xb2, 0x2e, 0xde, 0xb7, 0xc9, 0xe8, 0x16, 0xf2, - 0xe9, 0xd9, 0x57, 0xe4, 0xfa, 0x16, 0xb1, 0x5f, 0x4a, 0xdc, 0xa1, 0x31, 0xda, 0x70, 0x92, 0x65, - 0x4e, 0xd3, 0xed, 0x69, 0x7b, 0xa7, 0x8e, 0x9f, 0x5b, 0x7a, 0x2e, 0x45, 0xfd, 0x73, 0xeb, 0x9a, - 0xef, 0x51, 0xed, 0xa9, 0x33, 0xe4, 0xac, 0xc3, 0x35, 0xcc, 0xbf, 0xb2, 0xc5, 0x33, 0xee, 0xa5, - 0x53, 0x65, 0xd7, 0x3d, 0x9e, 0xb5, 0xa5, 0x09, 0x57, 0x7b, 0x26, 0x93, 0x24, 0xb7, 0x8d, 0xb8, - 0x99, 0xde, 0x69, 0x27, 0xbb, 0x98, 0x33, 0xd3, 0x69, 0x39, 0x19, 0xd5, 0xe2, 0xdd, 0x25, 0x3d, - 0xa4, 0x5a, 0xbc, 0xbb, 0x26, 0x75, 0xfc, 0x1c, 0x9f, 0x90, 0x12, 0x6b, 0x14, 0xe6, 0x54, 0xa2, - 0x2d, 0x9e, 0x69, 0xba, 0xeb, 0x59, 0xd2, 0x55, 0xf3, 0xa4, 0x35, 0xc1, 0x88, 0x86, 0xd2, 0x65, - 0x61, 0xde, 0x65, 0x09, 0xef, 0x2d, 0xa4, 0x4b, 0xbc, 0xf6, 0x65, 0x3e, 0x00, 0x4f, 0x5d, 0xf3, - 0x0c, 0x78, 0x65, 0xf1, 0x27, 0xff, 0xe5, 0x72, 0xee, 0x27, 0x3f, 0xbd, 0x9c, 0xfb, 0x0f, 0x3f, - 0xbd, 0x9c, 0xfb, 0xcf, 0x3f, 0xbd, 0x9c, 0xfb, 0x72, 0xfe, 0x64, 0xf9, 0x8e, 0xf9, 0xa3, 0x8f, - 0xb7, 0xb9, 0xb8, 0x41, 0xfc, 0xef, 0xcd, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xa5, 0xf6, 0x2e, - 0x3b, 0xed, 0xf0, 0x00, 0x00, + 0xa7, 0x9e, 0x75, 0x75, 0xad, 0xc7, 0xac, 0x53, 0x56, 0xd9, 0x99, 0x95, 0x92, 0x07, 0xda, 0x42, + 0x4f, 0x27, 0x6c, 0x62, 0x96, 0x9e, 0x3e, 0xc0, 0x58, 0x00, 0xee, 0x03, 0xec, 0xa5, 0x9e, 0xf8, + 0x6d, 0x23, 0xf2, 0x29, 0x8c, 0x48, 0xe6, 0xde, 0x5a, 0x89, 0x73, 0xa3, 0x56, 0x16, 0x61, 0xdc, + 0xb8, 0x4a, 0xa5, 0x4c, 0x9c, 0xb4, 0x0b, 0x56, 0x5d, 0x3a, 0x7b, 0xdc, 0xb8, 0x32, 0xa5, 0xa4, + 0xa4, 0x5d, 0xa4, 0xca, 0x94, 0xf2, 0x11, 0x8c, 0x0a, 0x95, 0x76, 0xd5, 0x46, 0xb6, 0xd3, 0x6d, + 0x46, 0x0b, 0x4b, 0xee, 0x34, 0xdc, 0x70, 0xc1, 0x6b, 0x3d, 0x72, 0xf7, 0x7b, 0x2a, 0x26, 0xc9, + 0xb2, 0x3b, 0x4f, 0xbe, 0xc2, 0xb7, 0xe5, 0xe5, 0x8b, 0xff, 0x34, 0x7c, 0xea, 0xf9, 0x8f, 0xdd, + 0xd6, 0x7e, 0x0f, 0x91, 0x57, 0x4c, 0x91, 0x71, 0x3e, 0x39, 0x78, 0xbe, 0x82, 0xb9, 0x6a, 0xb6, + 0xf0, 0x9e, 0x42, 0xba, 0xaf, 0x31, 0x55, 0xb8, 0x88, 0x21, 0x34, 0xa7, 0xad, 0x7b, 0x57, 0xa1, + 0x5f, 0xf0, 0xac, 0x8d, 0xd2, 0x61, 0x5f, 0xf7, 0xfc, 0x46, 0x6f, 0x89, 0x25, 0x33, 0x9a, 0x36, + 0xc6, 0x26, 0x95, 0xf1, 0x05, 0x9c, 0xaf, 0x66, 0x8a, 0xee, 0x25, 0xa2, 0xd7, 0x76, 0xf2, 0x02, + 0xaa, 0xe2, 0x94, 0xf5, 0xee, 0x2a, 0x73, 0x15, 0x27, 0x36, 0xb6, 0x18, 0x6d, 0xf9, 0xf4, 0x11, + 0xf5, 0x31, 0x66, 0xbb, 0x57, 0xb4, 0xb2, 0x49, 0x2e, 0x5b, 0xbe, 0x0a, 0x67, 0xab, 0x09, 0x51, + 0x59, 0x2c, 0xbd, 0x0e, 0x81, 0xa6, 0xb0, 0xa5, 0x27, 0xac, 0x57, 0x8f, 0x50, 0xa1, 0xd1, 0x65, + 0x1a, 0xee, 0xac, 0xf6, 0xd0, 0x92, 0xbc, 0x54, 0x20, 0x09, 0x77, 0xef, 0x32, 0xce, 0xaa, 0xc6, + 0x99, 0xa4, 0xc8, 0xfc, 0x78, 0x3f, 0x95, 0x07, 0x22, 0x3d, 0x8b, 0xcd, 0x92, 0xf0, 0x26, 0xce, + 0x85, 0x22, 0x6e, 0x79, 0x36, 0xda, 0x07, 0x70, 0x48, 0xe4, 0xaf, 0xd3, 0x42, 0x98, 0x03, 0x52, + 0xe6, 0x36, 0x20, 0x1f, 0x1e, 0x02, 0x76, 0x39, 0x11, 0xcf, 0xde, 0x55, 0x04, 0x77, 0x85, 0xae, + 0x79, 0xf5, 0xc7, 0xba, 0x2b, 0x94, 0xfd, 0x8e, 0xfb, 0x08, 0x19, 0x6c, 0x77, 0x5e, 0xcc, 0xf8, + 0xec, 0x87, 0x11, 0xfd, 0x85, 0x80, 0x68, 0xc6, 0x8f, 0xc3, 0x85, 0x1b, 0xe9, 0x4d, 0xe9, 0x60, + 0xc4, 0x02, 0x4d, 0xc9, 0x99, 0xaa, 0x51, 0xbe, 0x45, 0x64, 0x32, 0x7d, 0x8b, 0x7a, 0x45, 0xb3, + 0x1d, 0xfa, 0xc4, 0xa6, 0xed, 0x26, 0x86, 0x42, 0x1f, 0x7a, 0x9c, 0x27, 0x8a, 0x8e, 0x4d, 0xa2, + 0x7a, 0x07, 0x71, 0x4d, 0x89, 0xd0, 0x1f, 0x43, 0xf1, 0x2a, 0xab, 0x72, 0x12, 0x17, 0xa9, 0x52, + 0x8f, 0x48, 0xba, 0x93, 0x23, 0x1b, 0x70, 0x6e, 0x99, 0x86, 0x62, 0x8e, 0xb3, 0x69, 0x10, 0xfa, + 0x6e, 0x3d, 0xec, 0x7a, 0x3a, 0x28, 0x0d, 0x94, 0x14, 0x9e, 0xdd, 0xb7, 0x98, 0xbc, 0x6a, 0xba, + 0xbc, 0xae, 0x7c, 0x5d, 0xe2, 0x64, 0xc5, 0x91, 0xc3, 0x69, 0xaa, 0x98, 0x3d, 0xc4, 0x87, 0x78, + 0x18, 0x4e, 0x36, 0x6b, 0x21, 0x4a, 0x81, 0x22, 0x4c, 0xae, 0x5b, 0x30, 0xc8, 0x99, 0x32, 0x17, + 0xd4, 0x31, 0x9d, 0x87, 0xdc, 0x85, 0x11, 0x15, 0x47, 0x43, 0x0c, 0x54, 0x66, 0xbd, 0xee, 0xc2, + 0x08, 0xb7, 0xaf, 0x4e, 0xce, 0xf2, 0x01, 0x8c, 0xa8, 0xc0, 0x9b, 0x53, 0xaf, 0xf4, 0x9f, 0xc0, + 0xb8, 0x1e, 0x82, 0x73, 0x7a, 0x45, 0x7e, 0x84, 0x67, 0xb8, 0xf2, 0xa8, 0x24, 0x9b, 0x7f, 0x26, + 0x96, 0x19, 0x46, 0xa8, 0x94, 0x4f, 0x90, 0x12, 0x98, 0x59, 0xfd, 0xb3, 0x09, 0x6e, 0xf2, 0x81, + 0xbc, 0xce, 0xa4, 0x98, 0x93, 0x44, 0x5d, 0x74, 0x36, 0xc1, 0xd5, 0xfc, 0x3c, 0xcc, 0x6a, 0x82, + 0xed, 0x59, 0xed, 0x93, 0x9c, 0x35, 0xf7, 0x56, 0x5d, 0x96, 0x94, 0x4d, 0xdc, 0xa5, 0x25, 0x5e, + 0x63, 0xcc, 0x16, 0x74, 0x39, 0xfb, 0x01, 0x47, 0xec, 0x8c, 0xfb, 0x68, 0x0a, 0x26, 0xb0, 0x99, + 0xcd, 0xeb, 0xf2, 0x20, 0x64, 0x64, 0xfb, 0x26, 0xc5, 0x75, 0x61, 0xeb, 0x66, 0x4a, 0x8b, 0x4b, + 0x9a, 0x2f, 0x45, 0xdc, 0xaa, 0x8c, 0x64, 0x3c, 0x79, 0x63, 0xb3, 0x6b, 0x76, 0x21, 0xe5, 0x74, + 0xbb, 0x67, 0x5f, 0x64, 0x89, 0xfb, 0x05, 0xdc, 0x1d, 0xa6, 0xa6, 0x06, 0xcb, 0x16, 0x76, 0x43, + 0x0b, 0x90, 0x48, 0xe5, 0x54, 0x8b, 0xde, 0x63, 0xbc, 0x27, 0x96, 0xfe, 0x5e, 0xe5, 0xab, 0x3d, + 0xa4, 0x48, 0x4d, 0xbc, 0xd6, 0x93, 0x4e, 0x9d, 0x95, 0x5e, 0xe0, 0x2b, 0x6c, 0x7a, 0x79, 0x3d, + 0xde, 0xdf, 0x4c, 0x39, 0xbe, 0x56, 0x61, 0xa2, 0xe9, 0x02, 0xcd, 0x30, 0xd1, 0xae, 0x6d, 0xc8, + 0x52, 0xff, 0x67, 0x50, 0x8a, 0xa2, 0x40, 0x4e, 0xd7, 0x09, 0xd9, 0xd1, 0x89, 0x24, 0xa1, 0xa9, + 0x80, 0x74, 0x7b, 0xb7, 0x69, 0xee, 0x6a, 0x96, 0x86, 0xf5, 0xbb, 0x30, 0x22, 0xba, 0x2d, 0xf6, + 0x72, 0x6b, 0xd6, 0x1b, 0xb0, 0x5d, 0x9c, 0xb1, 0xe2, 0xe2, 0xdc, 0x4b, 0x11, 0x94, 0xec, 0xed, + 0xd3, 0x0b, 0x52, 0x41, 0x1a, 0x31, 0x41, 0x56, 0x97, 0xee, 0xed, 0x7d, 0xfe, 0x58, 0xcc, 0xe8, + 0xd7, 0xd3, 0x77, 0xa8, 0x13, 0x5d, 0x16, 0x8b, 0x65, 0x12, 0xd4, 0x2f, 0xe8, 0x26, 0x51, 0xf1, + 0x9b, 0x4e, 0x69, 0x14, 0x2a, 0x32, 0xaa, 0x28, 0x8b, 0x60, 0x70, 0x66, 0x8a, 0x78, 0xbe, 0x1b, + 0x3e, 0x5b, 0xb0, 0xd7, 0x22, 0xb7, 0x82, 0x8e, 0x90, 0xb2, 0x41, 0x22, 0xed, 0x35, 0xf2, 0x25, + 0x4e, 0x25, 0x42, 0x7c, 0xc5, 0xf3, 0xc2, 0x20, 0xf4, 0x9d, 0x76, 0x15, 0x5f, 0xb4, 0xce, 0x6c, + 0x74, 0x14, 0xc8, 0x9d, 0xc6, 0xa6, 0xc5, 0x95, 0x8a, 0x64, 0xf6, 0x69, 0xe9, 0x6f, 0xd4, 0xdd, + 0x9a, 0x34, 0x64, 0x17, 0xcb, 0xa5, 0x2a, 0xd3, 0xd7, 0xbf, 0x4c, 0xa1, 0x35, 0x98, 0xcd, 0x48, + 0x1a, 0xa4, 0x8e, 0x70, 0xbb, 0x27, 0x15, 0x9a, 0xeb, 0x5e, 0x30, 0xf9, 0x0a, 0x66, 0x52, 0xb3, + 0x0a, 0x29, 0x37, 0x74, 0xb7, 0x9c, 0x43, 0xbd, 0x84, 0x3f, 0x86, 0x22, 0xbf, 0xd5, 0x81, 0xc1, + 0xcb, 0x46, 0x82, 0x99, 0xe8, 0xae, 0x4f, 0x06, 0x41, 0x7c, 0xbe, 0xce, 0xa6, 0x53, 0x37, 0xce, + 0xa7, 0x31, 0xb3, 0x88, 0x7c, 0x82, 0x9b, 0xd6, 0xbd, 0x27, 0xd4, 0x7f, 0xa6, 0x3e, 0xbc, 0x34, + 0x64, 0xb7, 0x0b, 0x45, 0x5b, 0x30, 0xb3, 0x4b, 0x7d, 0xf7, 0xd1, 0xb3, 0xb8, 0x40, 0xa9, 0x99, + 0x54, 0x6c, 0x37, 0x89, 0x9f, 0xc3, 0xec, 0x82, 0x77, 0xd8, 0x16, 0x57, 0xf7, 0x0c, 0x99, 0xea, + 0x3c, 0x3e, 0x1d, 0xdf, 0x3b, 0xa0, 0x69, 0x4e, 0xdd, 0x2d, 0xd4, 0xf9, 0x16, 0xf0, 0x4e, 0xeb, + 0x0d, 0x33, 0xa6, 0x20, 0x85, 0x24, 0xba, 0x91, 0x21, 0x4d, 0x39, 0x9d, 0x7f, 0x1b, 0x07, 0x61, + 0x8c, 0x8f, 0xfb, 0xe6, 0xb4, 0x41, 0x98, 0x86, 0xef, 0x7e, 0x11, 0x2c, 0x45, 0x2a, 0x2f, 0x30, + 0x5b, 0xea, 0x09, 0x6a, 0xbb, 0x21, 0xd7, 0x96, 0x2d, 0xdf, 0x7d, 0xe2, 0x36, 0xe9, 0xbe, 0x70, + 0x23, 0x9a, 0x91, 0xd3, 0x26, 0xb2, 0x5b, 0x3d, 0xb5, 0xd4, 0x0a, 0xcd, 0x66, 0x97, 0x2d, 0x16, + 0xd1, 0x73, 0x2b, 0x30, 0x4a, 0xf4, 0xe4, 0x8f, 0xeb, 0xbc, 0xdd, 0x66, 0xeb, 0x04, 0x33, 0x6e, + 0x6a, 0xdf, 0x87, 0xb1, 0xaa, 0x5e, 0x78, 0x4a, 0x21, 0x99, 0x83, 0x42, 0x5d, 0x05, 0xea, 0x5d, + 0xf7, 0x2e, 0x01, 0xa1, 0x6a, 0xe1, 0x39, 0x51, 0x2b, 0x32, 0xe3, 0x67, 0x8c, 0x07, 0xe8, 0xd4, + 0x2a, 0x90, 0xf6, 0x06, 0xa7, 0x8a, 0x9f, 0x49, 0x7f, 0xb3, 0xae, 0xc6, 0x9f, 0xb5, 0x89, 0x3f, + 0xb1, 0x4a, 0xac, 0xde, 0x6f, 0x19, 0xab, 0xc0, 0xf8, 0xae, 0x6f, 0xb4, 0xf2, 0x60, 0x9f, 0xe8, + 0xc9, 0x3d, 0x3d, 0xd8, 0x27, 0xf1, 0x90, 0x9f, 0x1e, 0xec, 0x93, 0xf2, 0x4a, 0x5f, 0x15, 0x0a, + 0xf1, 0x37, 0x04, 0x95, 0x5b, 0x29, 0xe3, 0x89, 0x44, 0x15, 0xd6, 0x93, 0xf9, 0xf8, 0xe0, 0x12, + 0x56, 0x30, 0x7a, 0x64, 0xa8, 0x8b, 0x87, 0x43, 0xd5, 0x2d, 0xe5, 0x2d, 0xa3, 0x07, 0x7a, 0xda, + 0x0f, 0xfe, 0x34, 0x51, 0x17, 0x07, 0x6e, 0x3c, 0xdd, 0x47, 0xec, 0x2d, 0xa3, 0x7b, 0x50, 0xe0, + 0xaf, 0x34, 0x44, 0xd9, 0x12, 0xa3, 0xa0, 0xc2, 0xe4, 0xe3, 0x11, 0x5d, 0x46, 0x4a, 0x21, 0x9e, + 0x63, 0x4e, 0x29, 0x2c, 0x23, 0xf9, 0x5c, 0x97, 0xf1, 0x0f, 0x51, 0x26, 0x39, 0xe5, 0xed, 0x4a, + 0x24, 0x97, 0x9b, 0x3b, 0x9f, 0x82, 0x51, 0xfb, 0xd4, 0x31, 0x3d, 0xef, 0x9c, 0x6a, 0x52, 0x4a, + 0x32, 0xba, 0xb9, 0x0b, 0xa9, 0x38, 0x21, 0x28, 0xe4, 0x6f, 0x84, 0xa7, 0x3f, 0xd9, 0x1e, 0x5d, + 0x11, 0xeb, 0x42, 0x23, 0x8b, 0xb9, 0x79, 0x12, 0x52, 0x51, 0x2a, 0x55, 0x4f, 0x2c, 0xa5, 0xbc, + 0x13, 0xff, 0x5a, 0xca, 0x2d, 0x0e, 0x83, 0x22, 0x1a, 0x90, 0xdd, 0x1f, 0xad, 0x27, 0x0f, 0xe5, + 0x93, 0x37, 0x19, 0x25, 0xf5, 0x12, 0x90, 0xd9, 0x83, 0x0f, 0xe5, 0x23, 0x37, 0x2f, 0x5b, 0xf0, + 0x1e, 0x5c, 0x8c, 0x5d, 0x0d, 0x31, 0x05, 0xdf, 0x4c, 0xbf, 0x3f, 0x92, 0xaa, 0x9e, 0x6c, 0x43, + 0xe0, 0x4a, 0xf2, 0x0a, 0x49, 0xac, 0xdf, 0x4f, 0x3b, 0x91, 0xae, 0xc3, 0x04, 0xce, 0x5d, 0x01, + 0xf5, 0x97, 0x7d, 0xaf, 0xd3, 0x8e, 0xb2, 0xce, 0x98, 0xe0, 0x78, 0xfa, 0xa3, 0x38, 0x56, 0xdd, + 0x4c, 0x1f, 0x13, 0xd7, 0x8d, 0x11, 0xa1, 0x67, 0xc3, 0x51, 0xc0, 0xb4, 0xa5, 0x11, 0x11, 0xbb, + 0x77, 0xc9, 0x47, 0x30, 0x19, 0xdd, 0x42, 0xe6, 0x22, 0x52, 0xc8, 0xba, 0x78, 0xdf, 0x26, 0xa3, + 0xab, 0xc8, 0xa7, 0x67, 0x5f, 0x91, 0xeb, 0x5b, 0xc4, 0x7e, 0x29, 0x71, 0x91, 0xc6, 0x68, 0xc3, + 0x49, 0x96, 0x39, 0x4d, 0xb7, 0xa7, 0xed, 0x9d, 0x3a, 0x7e, 0x6e, 0xe9, 0x09, 0x15, 0xf5, 0xcf, + 0xad, 0x6b, 0xd2, 0x47, 0xb5, 0xa7, 0xce, 0x90, 0xb3, 0x0e, 0xd7, 0x30, 0x09, 0xcb, 0x16, 0x4f, + 0xbb, 0x97, 0x4e, 0x95, 0x5d, 0xf7, 0x78, 0xea, 0x96, 0x26, 0x5c, 0xed, 0x99, 0x51, 0x92, 0xdc, + 0x36, 0x82, 0x67, 0x7a, 0xe7, 0x9e, 0xec, 0x62, 0xce, 0x4c, 0xa7, 0x25, 0x66, 0x54, 0x8b, 0x77, + 0x97, 0x1c, 0x91, 0x6a, 0xf1, 0xee, 0x9a, 0xd9, 0xf1, 0x73, 0x7c, 0x47, 0x4a, 0xac, 0x51, 0x98, + 0x58, 0x89, 0xb6, 0x78, 0xba, 0xe9, 0xae, 0x67, 0x49, 0x57, 0xcd, 0x93, 0xd6, 0x04, 0x23, 0x1a, + 0x4a, 0x97, 0x85, 0x79, 0x97, 0x25, 0xbc, 0xb7, 0x90, 0x2e, 0x41, 0xdb, 0x97, 0xf9, 0x00, 0x3c, + 0x75, 0xcd, 0x33, 0xe0, 0x95, 0xc5, 0x9f, 0xfc, 0x97, 0xcb, 0xb9, 0x9f, 0xfc, 0xf4, 0x72, 0xee, + 0x3f, 0xfc, 0xf4, 0x72, 0xee, 0x3f, 0xff, 0xf4, 0x72, 0xee, 0xcb, 0xf9, 0x93, 0x25, 0x3d, 0xe6, + 0x2f, 0x3f, 0xde, 0xe6, 0xe2, 0x06, 0xf1, 0xbf, 0x37, 0xff, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xf4, 0x4b, 0x4f, 0x24, 0xf2, 0xf0, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -17612,6 +17612,8 @@ type AuthServiceClient interface { // GetTrustedClusters gets all current Trusted Cluster resources. GetTrustedClusters(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*types.TrustedClusterV2List, error) // UpsertTrustedCluster upserts a Trusted Cluster in a backend. + // + // Deprecated: Use [teleport.trust.v1.UpsertTrustedCluster] instead. UpsertTrustedCluster(ctx context.Context, in *types.TrustedClusterV2, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) // DeleteTrustedCluster deletes an existing Trusted Cluster in a backend by name. DeleteTrustedCluster(ctx context.Context, in *types.ResourceRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) @@ -19593,6 +19595,7 @@ func (c *authServiceClient) GetTrustedClusters(ctx context.Context, in *emptypb. return out, nil } +// Deprecated: Do not use. func (c *authServiceClient) UpsertTrustedCluster(ctx context.Context, in *types.TrustedClusterV2, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) { out := new(types.TrustedClusterV2) err := c.cc.Invoke(ctx, "/proto.AuthService/UpsertTrustedCluster", in, out, opts...) @@ -21001,6 +21004,8 @@ type AuthServiceServer interface { // GetTrustedClusters gets all current Trusted Cluster resources. GetTrustedClusters(context.Context, *emptypb.Empty) (*types.TrustedClusterV2List, error) // UpsertTrustedCluster upserts a Trusted Cluster in a backend. + // + // Deprecated: Use [teleport.trust.v1.UpsertTrustedCluster] instead. UpsertTrustedCluster(context.Context, *types.TrustedClusterV2) (*types.TrustedClusterV2, error) // DeleteTrustedCluster deletes an existing Trusted Cluster in a backend by name. DeleteTrustedCluster(context.Context, *types.ResourceRequest) (*emptypb.Empty, error) diff --git a/api/client/proto/event.pb.go b/api/client/proto/event.pb.go index 826cb7de67995..0cd4d8e9a4eb5 100644 --- a/api/client/proto/event.pb.go +++ b/api/client/proto/event.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/legacy/client/proto/event.proto diff --git a/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go b/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go index 012d25f7f71e1..58afbe766d1bb 100644 --- a/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go +++ b/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/access_graph/v1/authorized_key.proto diff --git a/api/gen/proto/go/teleport/accessgraph/v1/private_key.pb.go b/api/gen/proto/go/teleport/accessgraph/v1/private_key.pb.go index 201326357ea5d..4b93f506a0a29 100644 --- a/api/gen/proto/go/teleport/accessgraph/v1/private_key.pb.go +++ b/api/gen/proto/go/teleport/accessgraph/v1/private_key.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/access_graph/v1/private_key.proto diff --git a/api/gen/proto/go/teleport/accessgraph/v1/secrets_service.pb.go b/api/gen/proto/go/teleport/accessgraph/v1/secrets_service.pb.go index 6925d822a55fe..0159b47a0950d 100644 --- a/api/gen/proto/go/teleport/accessgraph/v1/secrets_service.pb.go +++ b/api/gen/proto/go/teleport/accessgraph/v1/secrets_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/access_graph/v1/secrets_service.proto diff --git a/api/gen/proto/go/teleport/accesslist/v1/accesslist.pb.go b/api/gen/proto/go/teleport/accesslist/v1/accesslist.pb.go index ef450e28039cc..1eefd6d3e6c28 100644 --- a/api/gen/proto/go/teleport/accesslist/v1/accesslist.pb.go +++ b/api/gen/proto/go/teleport/accesslist/v1/accesslist.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/accesslist/v1/accesslist.proto diff --git a/api/gen/proto/go/teleport/accesslist/v1/accesslist_service.pb.go b/api/gen/proto/go/teleport/accesslist/v1/accesslist_service.pb.go index 507d3551927f9..d79fa7dd8b5ce 100644 --- a/api/gen/proto/go/teleport/accesslist/v1/accesslist_service.pb.go +++ b/api/gen/proto/go/teleport/accesslist/v1/accesslist_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/accesslist/v1/accesslist_service.proto diff --git a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules.pb.go b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules.pb.go index fb61929055642..e5f23f5ae32ab 100644 --- a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules.pb.go +++ b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/accessmonitoringrules/v1/access_monitoring_rules.proto diff --git a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service.pb.go b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service.pb.go index 72b194b85bbb7..624bdd8c8c339 100644 --- a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service.pb.go +++ b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/accessmonitoringrules/v1/access_monitoring_rules_service.proto diff --git a/api/gen/proto/go/teleport/auditlog/v1/auditlog.pb.go b/api/gen/proto/go/teleport/auditlog/v1/auditlog.pb.go index 3b89ae810bf19..6915adf45f0b7 100644 --- a/api/gen/proto/go/teleport/auditlog/v1/auditlog.pb.go +++ b/api/gen/proto/go/teleport/auditlog/v1/auditlog.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/auditlog/v1/auditlog.proto diff --git a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go index 8d7e52517814b..ebf89e4f8e971 100644 --- a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go +++ b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/autoupdate/v1/autoupdate.proto @@ -986,6 +986,7 @@ func (x *AutoUpdateAgentRolloutSpec) GetMaintenanceWindowDuration() *durationpb. } // AutoUpdateAgentRolloutStatus tracks the current agent rollout status. +// The status is reset if any spec field changes except the mode. type AutoUpdateAgentRolloutStatus struct { state protoimpl.MessageState `protogen:"open.v1"` Groups []*AutoUpdateAgentRolloutStatusGroup `protobuf:"bytes,1,rep,name=groups,proto3" json:"groups,omitempty"` @@ -998,7 +999,11 @@ type AutoUpdateAgentRolloutStatus struct { // will not start updating to the new version directly. The controller sees that the group theoretical start time is // before the rollout start time and the maintenance window belongs to the previous rollout. // When the timestamp is nil, the controller will ignore the start time and check and allow groups to activate. - StartTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + // Time override is an optional timestamp making the autoupdate_agent_rollout controller use a specific time instead + // of the system clock when evaluating time-based criteria. This field is used for testing and troubleshooting + // purposes. + TimeOverride *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=time_override,json=timeOverride,proto3" json:"time_override,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1054,6 +1059,13 @@ func (x *AutoUpdateAgentRolloutStatus) GetStartTime() *timestamppb.Timestamp { return nil } +func (x *AutoUpdateAgentRolloutStatus) GetTimeOverride() *timestamppb.Timestamp { + if x != nil { + return x.TimeOverride + } + return nil +} + // AutoUpdateAgentRolloutStatusGroup tracks the current agent rollout status of a specific group. type AutoUpdateAgentRolloutStatusGroup struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1316,7 +1328,7 @@ var file_teleport_autoupdate_v1_autoupdate_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x19, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, - 0x77, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf7, 0x01, 0x0a, 0x1c, 0x41, 0x75, + 0x77, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb8, 0x02, 0x0a, 0x1c, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x51, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x74, 0x65, 0x6c, @@ -1332,73 +1344,77 @@ var file_teleport_autoupdate_v1_autoupdate_proto_rawDesc = []byte{ 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x22, 0xc0, 0x03, 0x0a, 0x21, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, - 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x47, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, - 0x64, 0x61, 0x79, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x44, 0x61, 0x79, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x6f, - 0x75, 0x72, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x77, 0x61, 0x69, - 0x74, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x57, 0x61, 0x69, 0x74, 0x48, 0x6f, 0x75, 0x72, 0x73, 0x4a, 0x04, - 0x08, 0x08, 0x10, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x77, 0x61, 0x69, - 0x74, 0x5f, 0x64, 0x61, 0x79, 0x73, 0x2a, 0xf7, 0x01, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x6f, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x29, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, + 0x69, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, + 0x72, 0x69, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, + 0x72, 0x69, 0x64, 0x65, 0x22, 0xc0, 0x03, 0x0a, 0x21, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, + 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x47, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, + 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x5f, 0x64, 0x61, 0x79, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x44, 0x61, 0x79, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, + 0x6f, 0x75, 0x72, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x77, 0x61, + 0x69, 0x74, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x57, 0x61, 0x69, 0x74, 0x48, 0x6f, 0x75, 0x72, 0x73, 0x4a, + 0x04, 0x08, 0x08, 0x10, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x77, 0x61, + 0x69, 0x74, 0x5f, 0x64, 0x61, 0x79, 0x73, 0x2a, 0xf7, 0x01, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x6f, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x29, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x2b, 0x0a, 0x27, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x2b, 0x0a, 0x27, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, + 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, + 0x01, 0x12, 0x28, 0x0a, 0x24, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, + 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x41, + 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, + 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x4e, + 0x45, 0x10, 0x03, 0x12, 0x2c, 0x0a, 0x28, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, - 0x12, 0x28, 0x0a, 0x24, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, - 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x41, 0x55, - 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, - 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x4e, 0x45, - 0x10, 0x03, 0x12, 0x2c, 0x0a, 0x28, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, - 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x04, - 0x2a, 0x83, 0x02, 0x0a, 0x1b, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x2f, 0x0a, 0x2b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, - 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x2d, 0x0a, 0x29, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x44, 0x42, 0x41, 0x43, 0x4b, 0x10, + 0x04, 0x2a, 0x83, 0x02, 0x0a, 0x1b, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x2f, 0x0a, 0x2b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, + 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x2d, 0x0a, 0x29, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, + 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, + 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, - 0x12, 0x2a, 0x0a, 0x26, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, - 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x28, 0x0a, 0x24, - 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, - 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, - 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x03, 0x12, 0x2e, 0x0a, 0x2a, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, - 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, - 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x44, - 0x42, 0x41, 0x43, 0x4b, 0x10, 0x04, 0x42, 0x56, 0x5a, 0x54, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x28, 0x0a, + 0x24, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, + 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, + 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x03, 0x12, 0x2e, 0x0a, 0x2a, 0x41, 0x55, 0x54, 0x4f, 0x5f, + 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, + 0x4c, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x4c, 0x45, + 0x44, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x04, 0x42, 0x56, 0x5a, 0x54, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1455,14 +1471,15 @@ var file_teleport_autoupdate_v1_autoupdate_proto_depIdxs = []int32{ 15, // 15: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatus.groups:type_name -> teleport.autoupdate.v1.AutoUpdateAgentRolloutStatusGroup 1, // 16: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatus.state:type_name -> teleport.autoupdate.v1.AutoUpdateAgentRolloutState 18, // 17: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatus.start_time:type_name -> google.protobuf.Timestamp - 18, // 18: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatusGroup.start_time:type_name -> google.protobuf.Timestamp - 0, // 19: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatusGroup.state:type_name -> teleport.autoupdate.v1.AutoUpdateAgentGroupState - 18, // 20: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatusGroup.last_update_time:type_name -> google.protobuf.Timestamp - 21, // [21:21] is the sub-list for method output_type - 21, // [21:21] is the sub-list for method input_type - 21, // [21:21] is the sub-list for extension type_name - 21, // [21:21] is the sub-list for extension extendee - 0, // [0:21] is the sub-list for field type_name + 18, // 18: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatus.time_override:type_name -> google.protobuf.Timestamp + 18, // 19: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatusGroup.start_time:type_name -> google.protobuf.Timestamp + 0, // 20: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatusGroup.state:type_name -> teleport.autoupdate.v1.AutoUpdateAgentGroupState + 18, // 21: teleport.autoupdate.v1.AutoUpdateAgentRolloutStatusGroup.last_update_time:type_name -> google.protobuf.Timestamp + 22, // [22:22] is the sub-list for method output_type + 22, // [22:22] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_teleport_autoupdate_v1_autoupdate_proto_init() } diff --git a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate_service.pb.go b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate_service.pb.go index d8fcfb70bd150..46fd39e2c983d 100644 --- a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate_service.pb.go +++ b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/autoupdate/v1/autoupdate_service.proto diff --git a/api/gen/proto/go/teleport/clusterconfig/v1/access_graph.pb.go b/api/gen/proto/go/teleport/clusterconfig/v1/access_graph.pb.go index e24b8daa15281..0bbe9d081d7e1 100644 --- a/api/gen/proto/go/teleport/clusterconfig/v1/access_graph.pb.go +++ b/api/gen/proto/go/teleport/clusterconfig/v1/access_graph.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/clusterconfig/v1/access_graph.proto diff --git a/api/gen/proto/go/teleport/clusterconfig/v1/access_graph_settings.pb.go b/api/gen/proto/go/teleport/clusterconfig/v1/access_graph_settings.pb.go index 82e6501c0e96d..78ae3205b43dd 100644 --- a/api/gen/proto/go/teleport/clusterconfig/v1/access_graph_settings.pb.go +++ b/api/gen/proto/go/teleport/clusterconfig/v1/access_graph_settings.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/clusterconfig/v1/access_graph_settings.proto diff --git a/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service.pb.go b/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service.pb.go index 986a699fd3a74..41e324787bdeb 100644 --- a/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service.pb.go +++ b/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/clusterconfig/v1/clusterconfig_service.proto diff --git a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel.pb.go b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel.pb.go index 22de09bae306e..701b289b159ac 100644 --- a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel.pb.go +++ b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/crownjewel/v1/crownjewel.proto diff --git a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go index 8f77287d54363..8db11e390b127 100644 --- a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go +++ b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/crownjewel/v1/crownjewel_service.proto diff --git a/api/gen/proto/go/teleport/dbobject/v1/dbobject.pb.go b/api/gen/proto/go/teleport/dbobject/v1/dbobject.pb.go index d6b586ff33c53..3c4ac98b7619e 100644 --- a/api/gen/proto/go/teleport/dbobject/v1/dbobject.pb.go +++ b/api/gen/proto/go/teleport/dbobject/v1/dbobject.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/dbobject/v1/dbobject.proto diff --git a/api/gen/proto/go/teleport/dbobject/v1/dbobject_service.pb.go b/api/gen/proto/go/teleport/dbobject/v1/dbobject_service.pb.go index 187fa7c19624f..a0ce791c81ad2 100644 --- a/api/gen/proto/go/teleport/dbobject/v1/dbobject_service.pb.go +++ b/api/gen/proto/go/teleport/dbobject/v1/dbobject_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/dbobject/v1/dbobject_service.proto diff --git a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule.pb.go b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule.pb.go index d4fb2a479a8a2..0549857f14423 100644 --- a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule.pb.go +++ b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/dbobjectimportrule/v1/dbobjectimportrule.proto diff --git a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service.pb.go b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service.pb.go index f2b80b39c7310..480b571dec331 100644 --- a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service.pb.go +++ b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/dbobjectimportrule/v1/dbobjectimportrule_service.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/database_access.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/database_access.pb.go index 6fd8cdf14f083..f60a120e794e5 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/database_access.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/database_access.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/database_access.proto @@ -40,6 +40,7 @@ type EvaluateDatabaseAccessRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *RequestMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` TlsIdentity *TLSIdentity `protobuf:"bytes,2,opt,name=tls_identity,json=tlsIdentity,proto3" json:"tls_identity,omitempty"` + Database *Resource `protobuf:"bytes,3,opt,name=database,proto3" json:"database,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -88,6 +89,13 @@ func (x *EvaluateDatabaseAccessRequest) GetTlsIdentity() *TLSIdentity { return nil } +func (x *EvaluateDatabaseAccessRequest) GetDatabase() *Resource { + if x != nil { + return x.Database + } + return nil +} + // EvaluateDatabaseAccessResponse describes the result of a database access // evaluation. type EvaluateDatabaseAccessResponse struct { @@ -280,52 +288,59 @@ var file_teleport_decision_v1alpha1_database_access_proto_rawDesc = []byte{ 0x74, 0x6f, 0x1a, 0x31, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x29, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2f, 0x74, 0x6c, 0x73, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb4, 0x01, 0x0a, 0x1d, 0x45, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x63, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x74, 0x6c, 0x73, + 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0xf6, 0x01, 0x0a, 0x1d, 0x45, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x47, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, + 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4a, 0x0a, 0x0c, 0x74, 0x6c, + 0x73, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x4c, + 0x53, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0b, 0x74, 0x6c, 0x73, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x40, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x4a, 0x0a, 0x0c, 0x74, 0x6c, 0x73, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x54, 0x4c, 0x53, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0b, - 0x74, 0x6c, 0x73, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0xc2, 0x01, 0x0a, 0x1e, - 0x45, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, - 0x0a, 0x06, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, - 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x74, - 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x12, 0x4a, 0x0a, 0x06, 0x64, 0x65, - 0x6e, 0x69, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x6e, 0x69, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x06, - 0x64, 0x65, 0x6e, 0x69, 0x61, 0x6c, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x22, 0x5e, 0x0a, 0x14, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x12, 0x46, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x22, 0x5e, 0x0a, 0x14, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x44, 0x65, 0x6e, 0x69, 0x61, 0x6c, 0x12, 0x46, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6e, 0x69, 0x61, 0x6c, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, - 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, - 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x3b, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, + 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x22, 0xc2, 0x01, 0x0a, 0x1e, 0x45, 0x76, 0x61, + 0x6c, 0x75, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x06, 0x70, + 0x65, 0x72, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x48, 0x00, 0x52, + 0x06, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x12, 0x4a, 0x0a, 0x06, 0x64, 0x65, 0x6e, 0x69, 0x61, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x44, 0x65, 0x6e, 0x69, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6e, + 0x69, 0x61, 0x6c, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x5e, 0x0a, + 0x14, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x74, 0x12, 0x46, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5e, 0x0a, + 0x14, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x44, + 0x65, 0x6e, 0x69, 0x61, 0x6c, 0x12, 0x46, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6e, 0x69, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x5a, 0x5a, + 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x64, + 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -348,21 +363,23 @@ var file_teleport_decision_v1alpha1_database_access_proto_goTypes = []any{ (*DatabaseAccessDenial)(nil), // 3: teleport.decision.v1alpha1.DatabaseAccessDenial (*RequestMetadata)(nil), // 4: teleport.decision.v1alpha1.RequestMetadata (*TLSIdentity)(nil), // 5: teleport.decision.v1alpha1.TLSIdentity - (*PermitMetadata)(nil), // 6: teleport.decision.v1alpha1.PermitMetadata - (*DenialMetadata)(nil), // 7: teleport.decision.v1alpha1.DenialMetadata + (*Resource)(nil), // 6: teleport.decision.v1alpha1.Resource + (*PermitMetadata)(nil), // 7: teleport.decision.v1alpha1.PermitMetadata + (*DenialMetadata)(nil), // 8: teleport.decision.v1alpha1.DenialMetadata } var file_teleport_decision_v1alpha1_database_access_proto_depIdxs = []int32{ 4, // 0: teleport.decision.v1alpha1.EvaluateDatabaseAccessRequest.metadata:type_name -> teleport.decision.v1alpha1.RequestMetadata 5, // 1: teleport.decision.v1alpha1.EvaluateDatabaseAccessRequest.tls_identity:type_name -> teleport.decision.v1alpha1.TLSIdentity - 2, // 2: teleport.decision.v1alpha1.EvaluateDatabaseAccessResponse.permit:type_name -> teleport.decision.v1alpha1.DatabaseAccessPermit - 3, // 3: teleport.decision.v1alpha1.EvaluateDatabaseAccessResponse.denial:type_name -> teleport.decision.v1alpha1.DatabaseAccessDenial - 6, // 4: teleport.decision.v1alpha1.DatabaseAccessPermit.metadata:type_name -> teleport.decision.v1alpha1.PermitMetadata - 7, // 5: teleport.decision.v1alpha1.DatabaseAccessDenial.metadata:type_name -> teleport.decision.v1alpha1.DenialMetadata - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 6, // 2: teleport.decision.v1alpha1.EvaluateDatabaseAccessRequest.database:type_name -> teleport.decision.v1alpha1.Resource + 2, // 3: teleport.decision.v1alpha1.EvaluateDatabaseAccessResponse.permit:type_name -> teleport.decision.v1alpha1.DatabaseAccessPermit + 3, // 4: teleport.decision.v1alpha1.EvaluateDatabaseAccessResponse.denial:type_name -> teleport.decision.v1alpha1.DatabaseAccessDenial + 7, // 5: teleport.decision.v1alpha1.DatabaseAccessPermit.metadata:type_name -> teleport.decision.v1alpha1.PermitMetadata + 8, // 6: teleport.decision.v1alpha1.DatabaseAccessDenial.metadata:type_name -> teleport.decision.v1alpha1.DenialMetadata + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_teleport_decision_v1alpha1_database_access_proto_init() } @@ -373,6 +390,7 @@ func file_teleport_decision_v1alpha1_database_access_proto_init() { file_teleport_decision_v1alpha1_denial_metadata_proto_init() file_teleport_decision_v1alpha1_permit_metadata_proto_init() file_teleport_decision_v1alpha1_request_metadata_proto_init() + file_teleport_decision_v1alpha1_resource_proto_init() file_teleport_decision_v1alpha1_tls_identity_proto_init() file_teleport_decision_v1alpha1_database_access_proto_msgTypes[1].OneofWrappers = []any{ (*EvaluateDatabaseAccessResponse_Permit)(nil), diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/decision_service.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/decision_service.pb.go index d69083027a3e4..d50f86f8a1a7f 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/decision_service.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/decision_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/decision_service.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/denial_metadata.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/denial_metadata.pb.go index f0652e9f0d836..d915867e0186d 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/denial_metadata.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/denial_metadata.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/denial_metadata.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/enforcement_feature.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/enforcement_feature.pb.go index 1305cecf1767f..4f62700671f18 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/enforcement_feature.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/enforcement_feature.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/enforcement_feature.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/permit_metadata.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/permit_metadata.pb.go index b317a9f4ee7c5..619193d751c90 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/permit_metadata.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/permit_metadata.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/permit_metadata.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/request_metadata.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/request_metadata.pb.go index 07f8daaa7b59a..4ad047ca54f6f 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/request_metadata.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/request_metadata.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/request_metadata.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/resource.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/resource.pb.go index 67717c7513743..a7d7f19e53d09 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/resource.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/resource.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/resource.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/ssh_access.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/ssh_access.pb.go index 832975efc9392..2c88519fe0668 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/ssh_access.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/ssh_access.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/ssh_access.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/ssh_identity.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/ssh_identity.pb.go index 714e0f84f6307..34833d21cb83b 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/ssh_identity.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/ssh_identity.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/ssh_identity.proto diff --git a/api/gen/proto/go/teleport/decision/v1alpha1/tls_identity.pb.go b/api/gen/proto/go/teleport/decision/v1alpha1/tls_identity.pb.go index 2ca338f5b2310..08547f70b19e3 100644 --- a/api/gen/proto/go/teleport/decision/v1alpha1/tls_identity.pb.go +++ b/api/gen/proto/go/teleport/decision/v1alpha1/tls_identity.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/decision/v1alpha1/tls_identity.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/assert.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/assert.pb.go index 56364d3a69173..5a3baf76bed4d 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/assert.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/assert.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/assert.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/authenticate_challenge.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/authenticate_challenge.pb.go index 4169f9b6c9108..b77c2bb6e60f7 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/authenticate_challenge.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/authenticate_challenge.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/authenticate_challenge.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go index 06b7efe8cf492..28982e37a9833 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/device.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go index 6fddf6604b72a..9a4c6852c7c98 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_collected_data.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/device_collected_data.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_confirmation_token.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_confirmation_token.pb.go index 79e086c32bfe2..437859da2de81 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device_confirmation_token.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_confirmation_token.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/device_confirmation_token.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_enroll_token.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_enroll_token.pb.go index cd2cc9679c4bb..870d58b0c8191 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device_enroll_token.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_enroll_token.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/device_enroll_token.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.go index 3146c89f2dd95..25d07d180601d 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_profile.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/device_profile.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go index 5949addc0b84f..05e94ea93924e 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_source.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/device_source.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/device_web_token.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/device_web_token.pb.go index 3b81002fb9c9c..6ed47b8c22ef1 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/device_web_token.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/device_web_token.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/device_web_token.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go index 792c213d5fe81..fa9bcb045acd0 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/devicetrust_service.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/os_type.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/os_type.pb.go index 284e4ffc0e2a9..86f87c2696f2c 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/os_type.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/os_type.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/os_type.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/tpm.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/tpm.pb.go index dad838ee35095..8f096a0b12fab 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/tpm.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/tpm.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/tpm.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/usage.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/usage.pb.go index ed4d2bf1f3e51..7017dc863c8e7 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/usage.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/usage.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/usage.proto diff --git a/api/gen/proto/go/teleport/devicetrust/v1/user_certificates.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/user_certificates.pb.go index 417b8ea699ee8..2e1777356b79e 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/user_certificates.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/user_certificates.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/devicetrust/v1/user_certificates.proto diff --git a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig.pb.go b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig.pb.go index 2297bbd87c396..45246e9784a65 100644 --- a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig.pb.go +++ b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/discoveryconfig/v1/discoveryconfig.proto diff --git a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go index 2edf5bf751a67..ed9ba6ca3192f 100644 --- a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go +++ b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/discoveryconfig/v1/discoveryconfig_service.proto diff --git a/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go b/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go index d50508e8f15be..2775a75226d38 100644 --- a/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go +++ b/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/dynamicwindows/v1/dynamicwindows_service.proto diff --git a/api/gen/proto/go/teleport/embedding/v1/embedding.pb.go b/api/gen/proto/go/teleport/embedding/v1/embedding.pb.go index 72d02a36c9c54..e8e6468e43d50 100644 --- a/api/gen/proto/go/teleport/embedding/v1/embedding.pb.go +++ b/api/gen/proto/go/teleport/embedding/v1/embedding.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/embedding/v1/embedding.proto diff --git a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage.pb.go b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage.pb.go index a24fab7a7b678..8c5496ac23f7e 100644 --- a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage.pb.go +++ b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/externalauditstorage/v1/externalauditstorage.proto diff --git a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service.pb.go b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service.pb.go index 8edabb94ee258..a92b3db942e2e 100644 --- a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service.pb.go +++ b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/externalauditstorage/v1/externalauditstorage_service.proto diff --git a/api/gen/proto/go/teleport/gitserver/v1/git_server_service.pb.go b/api/gen/proto/go/teleport/gitserver/v1/git_server_service.pb.go index f07d22a859201..f27cca6f71657 100644 --- a/api/gen/proto/go/teleport/gitserver/v1/git_server_service.pb.go +++ b/api/gen/proto/go/teleport/gitserver/v1/git_server_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/gitserver/v1/git_server_service.proto diff --git a/api/gen/proto/go/teleport/header/v1/metadata.pb.go b/api/gen/proto/go/teleport/header/v1/metadata.pb.go index 2cd0f77409150..08cb5db8f338e 100644 --- a/api/gen/proto/go/teleport/header/v1/metadata.pb.go +++ b/api/gen/proto/go/teleport/header/v1/metadata.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/header/v1/metadata.proto diff --git a/api/gen/proto/go/teleport/header/v1/resourceheader.pb.go b/api/gen/proto/go/teleport/header/v1/resourceheader.pb.go index 37cda50a08225..432d595d89825 100644 --- a/api/gen/proto/go/teleport/header/v1/resourceheader.pb.go +++ b/api/gen/proto/go/teleport/header/v1/resourceheader.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/header/v1/resourceheader.proto diff --git a/api/gen/proto/go/teleport/identitycenter/v1/identitycenter.pb.go b/api/gen/proto/go/teleport/identitycenter/v1/identitycenter.pb.go index 00189e9add99a..f2c2277101eae 100644 --- a/api/gen/proto/go/teleport/identitycenter/v1/identitycenter.pb.go +++ b/api/gen/proto/go/teleport/identitycenter/v1/identitycenter.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/identitycenter/v1/identitycenter.proto diff --git a/api/gen/proto/go/teleport/identitycenter/v1/identitycenter_service.pb.go b/api/gen/proto/go/teleport/identitycenter/v1/identitycenter_service.pb.go index c8c09f385c4e9..d35581691f971 100644 --- a/api/gen/proto/go/teleport/identitycenter/v1/identitycenter_service.pb.go +++ b/api/gen/proto/go/teleport/identitycenter/v1/identitycenter_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/identitycenter/v1/identitycenter_service.proto diff --git a/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go b/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go index 6c21f0556d576..31b04b0e44c15 100644 --- a/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go +++ b/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/integration/v1/awsoidc_service.proto diff --git a/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go b/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go index f2d42625cef51..e2585cd02c4c3 100644 --- a/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go +++ b/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/integration/v1/integration_service.proto diff --git a/api/gen/proto/go/teleport/kube/v1/kube_service.pb.go b/api/gen/proto/go/teleport/kube/v1/kube_service.pb.go index b674dd5c68d83..883afa302bc90 100644 --- a/api/gen/proto/go/teleport/kube/v1/kube_service.pb.go +++ b/api/gen/proto/go/teleport/kube/v1/kube_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/kube/v1/kube_service.proto diff --git a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer.pb.go b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer.pb.go index 1a49e853154e6..a2f027e4d512f 100644 --- a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer.pb.go +++ b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/kubewaitingcontainer/v1/kubewaitingcontainer.proto diff --git a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service.pb.go b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service.pb.go index 87484d4c22375..10a50822351e5 100644 --- a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service.pb.go +++ b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service.proto diff --git a/api/gen/proto/go/teleport/label/v1/label.pb.go b/api/gen/proto/go/teleport/label/v1/label.pb.go index 94821f0483abd..25b3eb2f0f2e0 100644 --- a/api/gen/proto/go/teleport/label/v1/label.pb.go +++ b/api/gen/proto/go/teleport/label/v1/label.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/label/v1/label.proto diff --git a/api/gen/proto/go/teleport/loginrule/v1/loginrule.pb.go b/api/gen/proto/go/teleport/loginrule/v1/loginrule.pb.go index 5a61ef4bd8f71..43c89fb3572c9 100644 --- a/api/gen/proto/go/teleport/loginrule/v1/loginrule.pb.go +++ b/api/gen/proto/go/teleport/loginrule/v1/loginrule.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/loginrule/v1/loginrule.proto diff --git a/api/gen/proto/go/teleport/loginrule/v1/loginrule_service.pb.go b/api/gen/proto/go/teleport/loginrule/v1/loginrule_service.pb.go index 3b350879b446f..cb1b8d15ab424 100644 --- a/api/gen/proto/go/teleport/loginrule/v1/loginrule_service.pb.go +++ b/api/gen/proto/go/teleport/loginrule/v1/loginrule_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/loginrule/v1/loginrule_service.proto diff --git a/api/gen/proto/go/teleport/machineid/v1/bot.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot.pb.go index 6b056460688f4..49ae2c9dacf97 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/machineid/v1/bot.proto diff --git a/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go index b794040d05ba0..757c72160aa17 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/machineid/v1/bot_instance.proto @@ -22,6 +22,7 @@ package machineidv1 import ( v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + v11 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" @@ -332,7 +333,11 @@ type BotInstanceStatusAuthentication struct { // The public key of the Bot instance. This must be a PEM wrapped, PKIX DER // encoded public key. This provides consistency and supports multiple types // of public key algorithm. - PublicKey []byte `protobuf:"bytes,6,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + PublicKey []byte `protobuf:"bytes,6,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + // The attributes generated during the join process. Typically, this is + // information from the join attestation process itself. This field will + // eventually replace the `metadata` field, which is structureless. + JoinAttrs *v11.JoinAttrs `protobuf:"bytes,8,opt,name=join_attrs,json=joinAttrs,proto3" json:"join_attrs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -409,6 +414,13 @@ func (x *BotInstanceStatusAuthentication) GetPublicKey() []byte { return nil } +func (x *BotInstanceStatusAuthentication) GetJoinAttrs() *v11.JoinAttrs { + if x != nil { + return x.JoinAttrs + } + return nil +} + // BotInstanceStatus holds the status of a BotInstance. type BotInstanceStatus struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -497,103 +509,110 @@ var file_teleport_machineid_v1_bot_instance_proto_rawDesc = []byte{ 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x02, 0x0a, 0x0b, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, - 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x12, 0x40, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, 0x61, - 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x58, 0x0a, 0x0f, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6f, 0x74, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x6f, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x49, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0xd1, - 0x02, 0x0a, 0x1a, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x3b, 0x0a, - 0x0b, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, - 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, - 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x69, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x31, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, - 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6e, 0x65, 0x5f, 0x73, 0x68, 0x6f, 0x74, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6f, 0x6e, 0x65, 0x53, 0x68, 0x6f, 0x74, 0x12, 0x22, - 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, - 0x72, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x6f, 0x73, 0x22, 0xaf, 0x02, 0x0a, 0x1f, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x61, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1d, - 0x0a, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x33, 0x0a, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, - 0x72, 0x69, 0x6e, 0x74, 0x22, 0xb1, 0x03, 0x0a, 0x11, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6d, 0x0a, 0x16, 0x69, 0x6e, - 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x74, 0x65, 0x6c, + 0x6f, 0x74, 0x6f, 0x1a, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x77, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, + 0x31, 0x2f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x8e, 0x02, 0x0a, 0x0b, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, + 0x63, 0x12, 0x40, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x58, 0x0a, 0x0f, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6f, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x6f, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x49, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0xd1, 0x02, + 0x0a, 0x1a, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x3b, 0x0a, 0x0b, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x72, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, + 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x31, + 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6e, 0x65, 0x5f, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6f, 0x6e, 0x65, 0x53, 0x68, 0x6f, 0x74, 0x12, 0x22, 0x0a, + 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, + 0x73, 0x22, 0xf7, 0x02, 0x0a, 0x1f, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x61, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x33, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x12, 0x46, 0x0a, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x52, 0x09, 0x6a, + 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x52, 0x0b, + 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x22, 0xb1, 0x03, 0x0a, 0x11, + 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x6d, 0x0a, 0x16, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x6d, 0x0a, 0x16, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x15, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, + 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x5e, 0x0a, 0x11, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, + 0x62, 0x65, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x65, - 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6d, 0x0a, 0x16, 0x6c, 0x61, 0x74, - 0x65, 0x73, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x15, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5e, 0x0a, 0x11, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x6c, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x48, - 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x5e, 0x0a, 0x11, 0x6c, 0x61, 0x74, 0x65, - 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x10, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x48, 0x65, - 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x42, 0x56, 0x5a, 0x54, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, - 0x64, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x74, 0x75, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x10, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, + 0x5e, 0x0a, 0x11, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, + 0x65, 0x61, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x10, 0x6c, + 0x61, 0x74, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x42, + 0x56, 0x5a, 0x54, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, + 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x69, 0x64, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x69, 0x64, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -619,6 +638,7 @@ var file_teleport_machineid_v1_bot_instance_proto_goTypes = []any{ (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp (*durationpb.Duration)(nil), // 7: google.protobuf.Duration (*structpb.Struct)(nil), // 8: google.protobuf.Struct + (*v11.JoinAttrs)(nil), // 9: teleport.workloadidentity.v1.JoinAttrs } var file_teleport_machineid_v1_bot_instance_proto_depIdxs = []int32{ 5, // 0: teleport.machineid.v1.BotInstance.metadata:type_name -> teleport.header.v1.Metadata @@ -628,15 +648,16 @@ var file_teleport_machineid_v1_bot_instance_proto_depIdxs = []int32{ 7, // 4: teleport.machineid.v1.BotInstanceStatusHeartbeat.uptime:type_name -> google.protobuf.Duration 6, // 5: teleport.machineid.v1.BotInstanceStatusAuthentication.authenticated_at:type_name -> google.protobuf.Timestamp 8, // 6: teleport.machineid.v1.BotInstanceStatusAuthentication.metadata:type_name -> google.protobuf.Struct - 3, // 7: teleport.machineid.v1.BotInstanceStatus.initial_authentication:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication - 3, // 8: teleport.machineid.v1.BotInstanceStatus.latest_authentications:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication - 2, // 9: teleport.machineid.v1.BotInstanceStatus.initial_heartbeat:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat - 2, // 10: teleport.machineid.v1.BotInstanceStatus.latest_heartbeats:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 9, // 7: teleport.machineid.v1.BotInstanceStatusAuthentication.join_attrs:type_name -> teleport.workloadidentity.v1.JoinAttrs + 3, // 8: teleport.machineid.v1.BotInstanceStatus.initial_authentication:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication + 3, // 9: teleport.machineid.v1.BotInstanceStatus.latest_authentications:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication + 2, // 10: teleport.machineid.v1.BotInstanceStatus.initial_heartbeat:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat + 2, // 11: teleport.machineid.v1.BotInstanceStatus.latest_heartbeats:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_teleport_machineid_v1_bot_instance_proto_init() } diff --git a/api/gen/proto/go/teleport/machineid/v1/bot_instance_service.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot_instance_service.pb.go index f665ce529cafc..c7035de1776ae 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot_instance_service.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot_instance_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/machineid/v1/bot_instance_service.proto diff --git a/api/gen/proto/go/teleport/machineid/v1/bot_service.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot_service.pb.go index 4d3beeb3a29f1..1f475a16c9ea9 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot_service.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/machineid/v1/bot_service.proto diff --git a/api/gen/proto/go/teleport/machineid/v1/federation.pb.go b/api/gen/proto/go/teleport/machineid/v1/federation.pb.go index 8e279f5d5adc4..7dfe88cd93e28 100644 --- a/api/gen/proto/go/teleport/machineid/v1/federation.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/federation.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/machineid/v1/federation.proto diff --git a/api/gen/proto/go/teleport/machineid/v1/federation_service.pb.go b/api/gen/proto/go/teleport/machineid/v1/federation_service.pb.go index e9f0fcaf60c53..bfa2ee7f2546a 100644 --- a/api/gen/proto/go/teleport/machineid/v1/federation_service.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/federation_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/machineid/v1/federation_service.proto diff --git a/api/gen/proto/go/teleport/machineid/v1/workload_identity_service.pb.go b/api/gen/proto/go/teleport/machineid/v1/workload_identity_service.pb.go index c4bb080435a56..21a488a6c2c98 100644 --- a/api/gen/proto/go/teleport/machineid/v1/workload_identity_service.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/workload_identity_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/machineid/v1/workload_identity_service.proto diff --git a/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go b/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go index 560f2a75be616..996bd7492ecb9 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/notifications/v1/notifications.proto diff --git a/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go b/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go index 655da01ddc32f..3a5e485bdf899 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications_service.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/notifications/v1/notifications_service.proto diff --git a/api/gen/proto/go/teleport/okta/v1/okta_service.pb.go b/api/gen/proto/go/teleport/okta/v1/okta_service.pb.go index 70d79a741e7f6..a093e7aa22673 100644 --- a/api/gen/proto/go/teleport/okta/v1/okta_service.pb.go +++ b/api/gen/proto/go/teleport/okta/v1/okta_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/okta/v1/okta_service.proto diff --git a/api/gen/proto/go/teleport/plugins/v1/plugin_service.pb.go b/api/gen/proto/go/teleport/plugins/v1/plugin_service.pb.go index 44a02cb8588e5..17992a7f0ec61 100644 --- a/api/gen/proto/go/teleport/plugins/v1/plugin_service.pb.go +++ b/api/gen/proto/go/teleport/plugins/v1/plugin_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/plugins/v1/plugin_service.proto diff --git a/api/gen/proto/go/teleport/presence/v1/service.pb.go b/api/gen/proto/go/teleport/presence/v1/service.pb.go index dd0f2a5cdbb96..7e730569d135f 100644 --- a/api/gen/proto/go/teleport/presence/v1/service.pb.go +++ b/api/gen/proto/go/teleport/presence/v1/service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/presence/v1/service.proto diff --git a/api/gen/proto/go/teleport/provisioning/v1/provisioning.pb.go b/api/gen/proto/go/teleport/provisioning/v1/provisioning.pb.go index 61160a9f7df37..89f958940e704 100644 --- a/api/gen/proto/go/teleport/provisioning/v1/provisioning.pb.go +++ b/api/gen/proto/go/teleport/provisioning/v1/provisioning.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/provisioning/v1/provisioning.proto diff --git a/api/gen/proto/go/teleport/provisioning/v1/provisioning_service.pb.go b/api/gen/proto/go/teleport/provisioning/v1/provisioning_service.pb.go index d800f6d268d64..9afe3669e6b5c 100644 --- a/api/gen/proto/go/teleport/provisioning/v1/provisioning_service.pb.go +++ b/api/gen/proto/go/teleport/provisioning/v1/provisioning_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/provisioning/v1/provisioning_service.proto diff --git a/api/gen/proto/go/teleport/resourceusage/v1/access_requests.pb.go b/api/gen/proto/go/teleport/resourceusage/v1/access_requests.pb.go index f62bcf10293d4..b0f800e4cc531 100644 --- a/api/gen/proto/go/teleport/resourceusage/v1/access_requests.pb.go +++ b/api/gen/proto/go/teleport/resourceusage/v1/access_requests.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/resourceusage/v1/access_requests.proto diff --git a/api/gen/proto/go/teleport/resourceusage/v1/account_usage_type.pb.go b/api/gen/proto/go/teleport/resourceusage/v1/account_usage_type.pb.go index 7db2483018589..774a416ac63be 100644 --- a/api/gen/proto/go/teleport/resourceusage/v1/account_usage_type.pb.go +++ b/api/gen/proto/go/teleport/resourceusage/v1/account_usage_type.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/resourceusage/v1/account_usage_type.proto diff --git a/api/gen/proto/go/teleport/resourceusage/v1/device_trust.pb.go b/api/gen/proto/go/teleport/resourceusage/v1/device_trust.pb.go index 8b7d128131517..2f1edefaa1d97 100644 --- a/api/gen/proto/go/teleport/resourceusage/v1/device_trust.pb.go +++ b/api/gen/proto/go/teleport/resourceusage/v1/device_trust.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/resourceusage/v1/device_trust.proto diff --git a/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service.pb.go b/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service.pb.go index 10ce0bb44dcbf..56434b3e61391 100644 --- a/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service.pb.go +++ b/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/resourceusage/v1/resourceusage_service.proto diff --git a/api/gen/proto/go/teleport/samlidp/v1/samlidp.pb.go b/api/gen/proto/go/teleport/samlidp/v1/samlidp.pb.go index f7d7df1e650c7..3907eb1233c15 100644 --- a/api/gen/proto/go/teleport/samlidp/v1/samlidp.pb.go +++ b/api/gen/proto/go/teleport/samlidp/v1/samlidp.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/samlidp/v1/samlidp.proto diff --git a/api/gen/proto/go/teleport/scim/v1/scim_service.pb.go b/api/gen/proto/go/teleport/scim/v1/scim_service.pb.go index 85fe68affaca4..6011ca60f276f 100644 --- a/api/gen/proto/go/teleport/scim/v1/scim_service.pb.go +++ b/api/gen/proto/go/teleport/scim/v1/scim_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/scim/v1/scim_service.proto diff --git a/api/gen/proto/go/teleport/secreports/v1/secreports.pb.go b/api/gen/proto/go/teleport/secreports/v1/secreports.pb.go index 881dbaf878341..09ac982395bf1 100644 --- a/api/gen/proto/go/teleport/secreports/v1/secreports.pb.go +++ b/api/gen/proto/go/teleport/secreports/v1/secreports.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/secreports/v1/secreports.proto diff --git a/api/gen/proto/go/teleport/secreports/v1/secreports_service.pb.go b/api/gen/proto/go/teleport/secreports/v1/secreports_service.pb.go index 1b0375cd03e29..8a73244cc5a72 100644 --- a/api/gen/proto/go/teleport/secreports/v1/secreports_service.pb.go +++ b/api/gen/proto/go/teleport/secreports/v1/secreports_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/secreports/v1/secreports_service.proto diff --git a/api/gen/proto/go/teleport/trait/v1/trait.pb.go b/api/gen/proto/go/teleport/trait/v1/trait.pb.go index 89031f6f5aa19..019987e24e0fb 100644 --- a/api/gen/proto/go/teleport/trait/v1/trait.pb.go +++ b/api/gen/proto/go/teleport/trait/v1/trait.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/trait/v1/trait.proto diff --git a/api/gen/proto/go/teleport/transport/v1/transport_service.pb.go b/api/gen/proto/go/teleport/transport/v1/transport_service.pb.go index f20c2cff21066..38d8b33eb7511 100644 --- a/api/gen/proto/go/teleport/transport/v1/transport_service.pb.go +++ b/api/gen/proto/go/teleport/transport/v1/transport_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/transport/v1/transport_service.proto diff --git a/api/gen/proto/go/teleport/trust/v1/trust_service.pb.go b/api/gen/proto/go/teleport/trust/v1/trust_service.pb.go index 349eb18c6ae9e..ca26508234431 100644 --- a/api/gen/proto/go/teleport/trust/v1/trust_service.pb.go +++ b/api/gen/proto/go/teleport/trust/v1/trust_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/trust/v1/trust_service.proto @@ -38,6 +38,144 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Request for UpsertTrustedCluster. +type UpsertTrustedClusterRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // TrustedCluster specifies a Trusted Cluster resource. + TrustedCluster *types.TrustedClusterV2 `protobuf:"bytes,1,opt,name=trusted_cluster,json=trustedCluster,proto3" json:"trusted_cluster,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpsertTrustedClusterRequest) Reset() { + *x = UpsertTrustedClusterRequest{} + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpsertTrustedClusterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpsertTrustedClusterRequest) ProtoMessage() {} + +func (x *UpsertTrustedClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpsertTrustedClusterRequest.ProtoReflect.Descriptor instead. +func (*UpsertTrustedClusterRequest) Descriptor() ([]byte, []int) { + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{0} +} + +func (x *UpsertTrustedClusterRequest) GetTrustedCluster() *types.TrustedClusterV2 { + if x != nil { + return x.TrustedCluster + } + return nil +} + +// Request for CreateTrustedCluster. +type CreateTrustedClusterRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // TrustedCluster specifies a Trusted Cluster resource. + TrustedCluster *types.TrustedClusterV2 `protobuf:"bytes,1,opt,name=trusted_cluster,json=trustedCluster,proto3" json:"trusted_cluster,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateTrustedClusterRequest) Reset() { + *x = CreateTrustedClusterRequest{} + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateTrustedClusterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTrustedClusterRequest) ProtoMessage() {} + +func (x *CreateTrustedClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTrustedClusterRequest.ProtoReflect.Descriptor instead. +func (*CreateTrustedClusterRequest) Descriptor() ([]byte, []int) { + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateTrustedClusterRequest) GetTrustedCluster() *types.TrustedClusterV2 { + if x != nil { + return x.TrustedCluster + } + return nil +} + +// Request for UpdateTrustedCluster. +type UpdateTrustedClusterRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // TrustedCluster specifies a Trusted Cluster resource. + TrustedCluster *types.TrustedClusterV2 `protobuf:"bytes,1,opt,name=trusted_cluster,json=trustedCluster,proto3" json:"trusted_cluster,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateTrustedClusterRequest) Reset() { + *x = UpdateTrustedClusterRequest{} + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateTrustedClusterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateTrustedClusterRequest) ProtoMessage() {} + +func (x *UpdateTrustedClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateTrustedClusterRequest.ProtoReflect.Descriptor instead. +func (*UpdateTrustedClusterRequest) Descriptor() ([]byte, []int) { + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{2} +} + +func (x *UpdateTrustedClusterRequest) GetTrustedCluster() *types.TrustedClusterV2 { + if x != nil { + return x.TrustedCluster + } + return nil +} + // Request for GetCertAuthority type GetCertAuthorityRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -53,7 +191,7 @@ type GetCertAuthorityRequest struct { func (x *GetCertAuthorityRequest) Reset() { *x = GetCertAuthorityRequest{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[0] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -65,7 +203,7 @@ func (x *GetCertAuthorityRequest) String() string { func (*GetCertAuthorityRequest) ProtoMessage() {} func (x *GetCertAuthorityRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[0] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -78,7 +216,7 @@ func (x *GetCertAuthorityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCertAuthorityRequest.ProtoReflect.Descriptor instead. func (*GetCertAuthorityRequest) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{0} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{3} } func (x *GetCertAuthorityRequest) GetType() string { @@ -115,7 +253,7 @@ type GetCertAuthoritiesRequest struct { func (x *GetCertAuthoritiesRequest) Reset() { *x = GetCertAuthoritiesRequest{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[1] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -127,7 +265,7 @@ func (x *GetCertAuthoritiesRequest) String() string { func (*GetCertAuthoritiesRequest) ProtoMessage() {} func (x *GetCertAuthoritiesRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[1] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -140,7 +278,7 @@ func (x *GetCertAuthoritiesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCertAuthoritiesRequest.ProtoReflect.Descriptor instead. func (*GetCertAuthoritiesRequest) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{1} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{4} } func (x *GetCertAuthoritiesRequest) GetType() string { @@ -168,7 +306,7 @@ type GetCertAuthoritiesResponse struct { func (x *GetCertAuthoritiesResponse) Reset() { *x = GetCertAuthoritiesResponse{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[2] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -180,7 +318,7 @@ func (x *GetCertAuthoritiesResponse) String() string { func (*GetCertAuthoritiesResponse) ProtoMessage() {} func (x *GetCertAuthoritiesResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[2] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -193,7 +331,7 @@ func (x *GetCertAuthoritiesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCertAuthoritiesResponse.ProtoReflect.Descriptor instead. func (*GetCertAuthoritiesResponse) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{2} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{5} } func (x *GetCertAuthoritiesResponse) GetCertAuthoritiesV2() []*types.CertAuthorityV2 { @@ -216,7 +354,7 @@ type DeleteCertAuthorityRequest struct { func (x *DeleteCertAuthorityRequest) Reset() { *x = DeleteCertAuthorityRequest{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[3] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -228,7 +366,7 @@ func (x *DeleteCertAuthorityRequest) String() string { func (*DeleteCertAuthorityRequest) ProtoMessage() {} func (x *DeleteCertAuthorityRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[3] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -241,7 +379,7 @@ func (x *DeleteCertAuthorityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteCertAuthorityRequest.ProtoReflect.Descriptor instead. func (*DeleteCertAuthorityRequest) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{3} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{6} } func (x *DeleteCertAuthorityRequest) GetType() string { @@ -269,7 +407,7 @@ type UpsertCertAuthorityRequest struct { func (x *UpsertCertAuthorityRequest) Reset() { *x = UpsertCertAuthorityRequest{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[4] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -281,7 +419,7 @@ func (x *UpsertCertAuthorityRequest) String() string { func (*UpsertCertAuthorityRequest) ProtoMessage() {} func (x *UpsertCertAuthorityRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[4] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -294,7 +432,7 @@ func (x *UpsertCertAuthorityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertCertAuthorityRequest.ProtoReflect.Descriptor instead. func (*UpsertCertAuthorityRequest) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{4} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{7} } func (x *UpsertCertAuthorityRequest) GetCertAuthority() *types.CertAuthorityV2 { @@ -333,7 +471,7 @@ type RotateCertAuthorityRequest struct { func (x *RotateCertAuthorityRequest) Reset() { *x = RotateCertAuthorityRequest{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[5] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -345,7 +483,7 @@ func (x *RotateCertAuthorityRequest) String() string { func (*RotateCertAuthorityRequest) ProtoMessage() {} func (x *RotateCertAuthorityRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[5] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -358,7 +496,7 @@ func (x *RotateCertAuthorityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RotateCertAuthorityRequest.ProtoReflect.Descriptor instead. func (*RotateCertAuthorityRequest) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{5} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{8} } func (x *RotateCertAuthorityRequest) GetType() string { @@ -411,7 +549,7 @@ type RotationSchedule struct { func (x *RotationSchedule) Reset() { *x = RotationSchedule{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[6] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -423,7 +561,7 @@ func (x *RotationSchedule) String() string { func (*RotationSchedule) ProtoMessage() {} func (x *RotationSchedule) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[6] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -436,7 +574,7 @@ func (x *RotationSchedule) ProtoReflect() protoreflect.Message { // Deprecated: Use RotationSchedule.ProtoReflect.Descriptor instead. func (*RotationSchedule) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{6} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{9} } func (x *RotationSchedule) GetUpdateClients() *timestamppb.Timestamp { @@ -469,7 +607,7 @@ type RotateCertAuthorityResponse struct { func (x *RotateCertAuthorityResponse) Reset() { *x = RotateCertAuthorityResponse{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[7] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -481,7 +619,7 @@ func (x *RotateCertAuthorityResponse) String() string { func (*RotateCertAuthorityResponse) ProtoMessage() {} func (x *RotateCertAuthorityResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[7] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -494,7 +632,7 @@ func (x *RotateCertAuthorityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RotateCertAuthorityResponse.ProtoReflect.Descriptor instead. func (*RotateCertAuthorityResponse) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{7} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{10} } // Request for RotateExternalCertAuthority. @@ -508,7 +646,7 @@ type RotateExternalCertAuthorityRequest struct { func (x *RotateExternalCertAuthorityRequest) Reset() { *x = RotateExternalCertAuthorityRequest{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[8] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -520,7 +658,7 @@ func (x *RotateExternalCertAuthorityRequest) String() string { func (*RotateExternalCertAuthorityRequest) ProtoMessage() {} func (x *RotateExternalCertAuthorityRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[8] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -533,7 +671,7 @@ func (x *RotateExternalCertAuthorityRequest) ProtoReflect() protoreflect.Message // Deprecated: Use RotateExternalCertAuthorityRequest.ProtoReflect.Descriptor instead. func (*RotateExternalCertAuthorityRequest) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{8} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{11} } func (x *RotateExternalCertAuthorityRequest) GetCertAuthority() *types.CertAuthorityV2 { @@ -552,7 +690,7 @@ type RotateExternalCertAuthorityResponse struct { func (x *RotateExternalCertAuthorityResponse) Reset() { *x = RotateExternalCertAuthorityResponse{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[9] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -564,7 +702,7 @@ func (x *RotateExternalCertAuthorityResponse) String() string { func (*RotateExternalCertAuthorityResponse) ProtoMessage() {} func (x *RotateExternalCertAuthorityResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[9] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -577,7 +715,7 @@ func (x *RotateExternalCertAuthorityResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use RotateExternalCertAuthorityResponse.ProtoReflect.Descriptor instead. func (*RotateExternalCertAuthorityResponse) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{9} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{12} } // GenerateHostCertRequest is the request for GenerateHostCert. @@ -603,7 +741,7 @@ type GenerateHostCertRequest struct { func (x *GenerateHostCertRequest) Reset() { *x = GenerateHostCertRequest{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[10] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -615,7 +753,7 @@ func (x *GenerateHostCertRequest) String() string { func (*GenerateHostCertRequest) ProtoMessage() {} func (x *GenerateHostCertRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[10] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -628,7 +766,7 @@ func (x *GenerateHostCertRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateHostCertRequest.ProtoReflect.Descriptor instead. func (*GenerateHostCertRequest) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{10} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{13} } func (x *GenerateHostCertRequest) GetKey() []byte { @@ -691,7 +829,7 @@ type GenerateHostCertResponse struct { func (x *GenerateHostCertResponse) Reset() { *x = GenerateHostCertResponse{} - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[11] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -703,7 +841,7 @@ func (x *GenerateHostCertResponse) String() string { func (*GenerateHostCertResponse) ProtoMessage() {} func (x *GenerateHostCertResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[11] + mi := &file_teleport_trust_v1_trust_service_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -716,7 +854,7 @@ func (x *GenerateHostCertResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateHostCertResponse.ProtoReflect.Descriptor instead. func (*GenerateHostCertResponse) Descriptor() ([]byte, []int) { - return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{11} + return file_teleport_trust_v1_trust_service_proto_rawDescGZIP(), []int{14} } func (x *GenerateHostCertResponse) GetSshCertificate() []byte { @@ -740,147 +878,183 @@ var file_teleport_trust_v1_trust_service_proto_rawDesc = []byte{ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x66, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x4b, 0x65, 0x79, 0x22, 0x50, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x64, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x13, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x5f, 0x76, 0x32, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x56, 0x32, 0x52, 0x11, 0x63, 0x65, 0x72, 0x74, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x56, 0x32, 0x22, 0x48, 0x0a, 0x1a, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5f, 0x0a, 0x1b, 0x55, + 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x56, 0x32, 0x52, 0x0e, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x5f, 0x0a, 0x1b, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x54, 0x72, 0x75, + 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x56, 0x32, 0x52, 0x0e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x5f, 0x0a, + 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x0f, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x56, 0x32, 0x52, 0x0e, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x66, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x5b, 0x0a, 0x1a, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x43, - 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0e, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, - 0x79, 0x56, 0x32, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x22, 0xe6, 0x01, 0x0a, 0x1a, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, - 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x67, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x70, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x67, 0x72, 0x61, 0x63, 0x65, 0x50, 0x65, 0x72, - 0x69, 0x6f, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x68, - 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x50, 0x68, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x73, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, - 0x65, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0xce, 0x01, 0x0a, 0x10, - 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x12, 0x41, 0x0a, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x50, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, + 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x64, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, + 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x13, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x5f, 0x76, 0x32, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x56, 0x32, 0x52, 0x11, 0x63, 0x65, 0x72, + 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x56, 0x32, 0x22, 0x48, + 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x5b, 0x0a, 0x1a, 0x55, 0x70, 0x73, 0x65, + 0x72, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0e, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x56, 0x32, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0xe6, 0x01, 0x0a, 0x1a, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, + 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x67, 0x72, 0x61, 0x63, + 0x65, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x67, 0x72, 0x61, 0x63, 0x65, + 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x50, 0x68, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x3f, 0x0a, + 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x23, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0xce, + 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x07, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79, 0x22, 0x1d, 0x0a, 0x1b, - 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x63, 0x0a, 0x22, 0x52, - 0x6f, 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x65, 0x72, - 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x3d, 0x0a, 0x0e, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x56, - 0x32, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, - 0x22, 0x25, 0x0a, 0x23, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1b, - 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, - 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, - 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, - 0x43, 0x0a, 0x18, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, - 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, - 0x73, 0x68, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x73, 0x73, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x32, 0x87, 0x06, 0x0a, 0x0c, 0x54, 0x72, 0x75, 0x73, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x56, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x65, - 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x56, 0x32, 0x12, 0x71, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5c, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x5c, - 0x0a, 0x13, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x74, 0x61, + 0x6e, 0x64, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79, 0x22, + 0x1d, 0x0a, 0x1b, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x63, + 0x0a, 0x22, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x65, 0x72, - 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x56, 0x32, 0x12, 0x74, 0x0a, 0x13, - 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x43, 0x65, - 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, - 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x8c, 0x01, 0x0a, 0x1b, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0e, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x56, 0x32, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x22, 0x25, 0x0a, 0x23, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x12, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, - 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, - 0x74, 0x43, 0x65, 0x72, 0x74, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, - 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, - 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x72, 0x75, 0x73, 0x74, 0x76, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe5, 0x01, 0x0a, 0x17, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x21, + 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x74, + 0x74, 0x6c, 0x22, 0x43, 0x0a, 0x18, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, + 0x0a, 0x0f, 0x73, 0x73, 0x68, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x73, 0x73, 0x68, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x32, 0xaa, 0x08, 0x0a, 0x0c, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x56, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, + 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x56, 0x32, + 0x12, 0x71, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, + 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x72, + 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x5c, 0x0a, 0x13, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, + 0x65, 0x72, 0x74, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x56, 0x32, 0x12, + 0x74, 0x0a, 0x13, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, + 0x65, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, + 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8c, 0x01, 0x0a, 0x1b, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, + 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x48, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x56, 0x32, 0x12, 0x5f, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x56, 0x32, 0x12, 0x5f, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x75, + 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2e, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x56, 0x32, 0x42, 0x4e, 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, + 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -895,54 +1069,67 @@ func file_teleport_trust_v1_trust_service_proto_rawDescGZIP() []byte { return file_teleport_trust_v1_trust_service_proto_rawDescData } -var file_teleport_trust_v1_trust_service_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_teleport_trust_v1_trust_service_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_teleport_trust_v1_trust_service_proto_goTypes = []any{ - (*GetCertAuthorityRequest)(nil), // 0: teleport.trust.v1.GetCertAuthorityRequest - (*GetCertAuthoritiesRequest)(nil), // 1: teleport.trust.v1.GetCertAuthoritiesRequest - (*GetCertAuthoritiesResponse)(nil), // 2: teleport.trust.v1.GetCertAuthoritiesResponse - (*DeleteCertAuthorityRequest)(nil), // 3: teleport.trust.v1.DeleteCertAuthorityRequest - (*UpsertCertAuthorityRequest)(nil), // 4: teleport.trust.v1.UpsertCertAuthorityRequest - (*RotateCertAuthorityRequest)(nil), // 5: teleport.trust.v1.RotateCertAuthorityRequest - (*RotationSchedule)(nil), // 6: teleport.trust.v1.RotationSchedule - (*RotateCertAuthorityResponse)(nil), // 7: teleport.trust.v1.RotateCertAuthorityResponse - (*RotateExternalCertAuthorityRequest)(nil), // 8: teleport.trust.v1.RotateExternalCertAuthorityRequest - (*RotateExternalCertAuthorityResponse)(nil), // 9: teleport.trust.v1.RotateExternalCertAuthorityResponse - (*GenerateHostCertRequest)(nil), // 10: teleport.trust.v1.GenerateHostCertRequest - (*GenerateHostCertResponse)(nil), // 11: teleport.trust.v1.GenerateHostCertResponse - (*types.CertAuthorityV2)(nil), // 12: types.CertAuthorityV2 - (*durationpb.Duration)(nil), // 13: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp - (*emptypb.Empty)(nil), // 15: google.protobuf.Empty + (*UpsertTrustedClusterRequest)(nil), // 0: teleport.trust.v1.UpsertTrustedClusterRequest + (*CreateTrustedClusterRequest)(nil), // 1: teleport.trust.v1.CreateTrustedClusterRequest + (*UpdateTrustedClusterRequest)(nil), // 2: teleport.trust.v1.UpdateTrustedClusterRequest + (*GetCertAuthorityRequest)(nil), // 3: teleport.trust.v1.GetCertAuthorityRequest + (*GetCertAuthoritiesRequest)(nil), // 4: teleport.trust.v1.GetCertAuthoritiesRequest + (*GetCertAuthoritiesResponse)(nil), // 5: teleport.trust.v1.GetCertAuthoritiesResponse + (*DeleteCertAuthorityRequest)(nil), // 6: teleport.trust.v1.DeleteCertAuthorityRequest + (*UpsertCertAuthorityRequest)(nil), // 7: teleport.trust.v1.UpsertCertAuthorityRequest + (*RotateCertAuthorityRequest)(nil), // 8: teleport.trust.v1.RotateCertAuthorityRequest + (*RotationSchedule)(nil), // 9: teleport.trust.v1.RotationSchedule + (*RotateCertAuthorityResponse)(nil), // 10: teleport.trust.v1.RotateCertAuthorityResponse + (*RotateExternalCertAuthorityRequest)(nil), // 11: teleport.trust.v1.RotateExternalCertAuthorityRequest + (*RotateExternalCertAuthorityResponse)(nil), // 12: teleport.trust.v1.RotateExternalCertAuthorityResponse + (*GenerateHostCertRequest)(nil), // 13: teleport.trust.v1.GenerateHostCertRequest + (*GenerateHostCertResponse)(nil), // 14: teleport.trust.v1.GenerateHostCertResponse + (*types.TrustedClusterV2)(nil), // 15: types.TrustedClusterV2 + (*types.CertAuthorityV2)(nil), // 16: types.CertAuthorityV2 + (*durationpb.Duration)(nil), // 17: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp + (*emptypb.Empty)(nil), // 19: google.protobuf.Empty } var file_teleport_trust_v1_trust_service_proto_depIdxs = []int32{ - 12, // 0: teleport.trust.v1.GetCertAuthoritiesResponse.cert_authorities_v2:type_name -> types.CertAuthorityV2 - 12, // 1: teleport.trust.v1.UpsertCertAuthorityRequest.cert_authority:type_name -> types.CertAuthorityV2 - 13, // 2: teleport.trust.v1.RotateCertAuthorityRequest.grace_period:type_name -> google.protobuf.Duration - 6, // 3: teleport.trust.v1.RotateCertAuthorityRequest.schedule:type_name -> teleport.trust.v1.RotationSchedule - 14, // 4: teleport.trust.v1.RotationSchedule.update_clients:type_name -> google.protobuf.Timestamp - 14, // 5: teleport.trust.v1.RotationSchedule.update_servers:type_name -> google.protobuf.Timestamp - 14, // 6: teleport.trust.v1.RotationSchedule.standby:type_name -> google.protobuf.Timestamp - 12, // 7: teleport.trust.v1.RotateExternalCertAuthorityRequest.cert_authority:type_name -> types.CertAuthorityV2 - 13, // 8: teleport.trust.v1.GenerateHostCertRequest.ttl:type_name -> google.protobuf.Duration - 0, // 9: teleport.trust.v1.TrustService.GetCertAuthority:input_type -> teleport.trust.v1.GetCertAuthorityRequest - 1, // 10: teleport.trust.v1.TrustService.GetCertAuthorities:input_type -> teleport.trust.v1.GetCertAuthoritiesRequest - 3, // 11: teleport.trust.v1.TrustService.DeleteCertAuthority:input_type -> teleport.trust.v1.DeleteCertAuthorityRequest - 4, // 12: teleport.trust.v1.TrustService.UpsertCertAuthority:input_type -> teleport.trust.v1.UpsertCertAuthorityRequest - 5, // 13: teleport.trust.v1.TrustService.RotateCertAuthority:input_type -> teleport.trust.v1.RotateCertAuthorityRequest - 8, // 14: teleport.trust.v1.TrustService.RotateExternalCertAuthority:input_type -> teleport.trust.v1.RotateExternalCertAuthorityRequest - 10, // 15: teleport.trust.v1.TrustService.GenerateHostCert:input_type -> teleport.trust.v1.GenerateHostCertRequest - 12, // 16: teleport.trust.v1.TrustService.GetCertAuthority:output_type -> types.CertAuthorityV2 - 2, // 17: teleport.trust.v1.TrustService.GetCertAuthorities:output_type -> teleport.trust.v1.GetCertAuthoritiesResponse - 15, // 18: teleport.trust.v1.TrustService.DeleteCertAuthority:output_type -> google.protobuf.Empty - 12, // 19: teleport.trust.v1.TrustService.UpsertCertAuthority:output_type -> types.CertAuthorityV2 - 7, // 20: teleport.trust.v1.TrustService.RotateCertAuthority:output_type -> teleport.trust.v1.RotateCertAuthorityResponse - 9, // 21: teleport.trust.v1.TrustService.RotateExternalCertAuthority:output_type -> teleport.trust.v1.RotateExternalCertAuthorityResponse - 11, // 22: teleport.trust.v1.TrustService.GenerateHostCert:output_type -> teleport.trust.v1.GenerateHostCertResponse - 16, // [16:23] is the sub-list for method output_type - 9, // [9:16] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 15, // 0: teleport.trust.v1.UpsertTrustedClusterRequest.trusted_cluster:type_name -> types.TrustedClusterV2 + 15, // 1: teleport.trust.v1.CreateTrustedClusterRequest.trusted_cluster:type_name -> types.TrustedClusterV2 + 15, // 2: teleport.trust.v1.UpdateTrustedClusterRequest.trusted_cluster:type_name -> types.TrustedClusterV2 + 16, // 3: teleport.trust.v1.GetCertAuthoritiesResponse.cert_authorities_v2:type_name -> types.CertAuthorityV2 + 16, // 4: teleport.trust.v1.UpsertCertAuthorityRequest.cert_authority:type_name -> types.CertAuthorityV2 + 17, // 5: teleport.trust.v1.RotateCertAuthorityRequest.grace_period:type_name -> google.protobuf.Duration + 9, // 6: teleport.trust.v1.RotateCertAuthorityRequest.schedule:type_name -> teleport.trust.v1.RotationSchedule + 18, // 7: teleport.trust.v1.RotationSchedule.update_clients:type_name -> google.protobuf.Timestamp + 18, // 8: teleport.trust.v1.RotationSchedule.update_servers:type_name -> google.protobuf.Timestamp + 18, // 9: teleport.trust.v1.RotationSchedule.standby:type_name -> google.protobuf.Timestamp + 16, // 10: teleport.trust.v1.RotateExternalCertAuthorityRequest.cert_authority:type_name -> types.CertAuthorityV2 + 17, // 11: teleport.trust.v1.GenerateHostCertRequest.ttl:type_name -> google.protobuf.Duration + 3, // 12: teleport.trust.v1.TrustService.GetCertAuthority:input_type -> teleport.trust.v1.GetCertAuthorityRequest + 4, // 13: teleport.trust.v1.TrustService.GetCertAuthorities:input_type -> teleport.trust.v1.GetCertAuthoritiesRequest + 6, // 14: teleport.trust.v1.TrustService.DeleteCertAuthority:input_type -> teleport.trust.v1.DeleteCertAuthorityRequest + 7, // 15: teleport.trust.v1.TrustService.UpsertCertAuthority:input_type -> teleport.trust.v1.UpsertCertAuthorityRequest + 8, // 16: teleport.trust.v1.TrustService.RotateCertAuthority:input_type -> teleport.trust.v1.RotateCertAuthorityRequest + 11, // 17: teleport.trust.v1.TrustService.RotateExternalCertAuthority:input_type -> teleport.trust.v1.RotateExternalCertAuthorityRequest + 13, // 18: teleport.trust.v1.TrustService.GenerateHostCert:input_type -> teleport.trust.v1.GenerateHostCertRequest + 0, // 19: teleport.trust.v1.TrustService.UpsertTrustedCluster:input_type -> teleport.trust.v1.UpsertTrustedClusterRequest + 1, // 20: teleport.trust.v1.TrustService.CreateTrustedCluster:input_type -> teleport.trust.v1.CreateTrustedClusterRequest + 2, // 21: teleport.trust.v1.TrustService.UpdateTrustedCluster:input_type -> teleport.trust.v1.UpdateTrustedClusterRequest + 16, // 22: teleport.trust.v1.TrustService.GetCertAuthority:output_type -> types.CertAuthorityV2 + 5, // 23: teleport.trust.v1.TrustService.GetCertAuthorities:output_type -> teleport.trust.v1.GetCertAuthoritiesResponse + 19, // 24: teleport.trust.v1.TrustService.DeleteCertAuthority:output_type -> google.protobuf.Empty + 16, // 25: teleport.trust.v1.TrustService.UpsertCertAuthority:output_type -> types.CertAuthorityV2 + 10, // 26: teleport.trust.v1.TrustService.RotateCertAuthority:output_type -> teleport.trust.v1.RotateCertAuthorityResponse + 12, // 27: teleport.trust.v1.TrustService.RotateExternalCertAuthority:output_type -> teleport.trust.v1.RotateExternalCertAuthorityResponse + 14, // 28: teleport.trust.v1.TrustService.GenerateHostCert:output_type -> teleport.trust.v1.GenerateHostCertResponse + 15, // 29: teleport.trust.v1.TrustService.UpsertTrustedCluster:output_type -> types.TrustedClusterV2 + 15, // 30: teleport.trust.v1.TrustService.CreateTrustedCluster:output_type -> types.TrustedClusterV2 + 15, // 31: teleport.trust.v1.TrustService.UpdateTrustedCluster:output_type -> types.TrustedClusterV2 + 22, // [22:32] is the sub-list for method output_type + 12, // [12:22] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_teleport_trust_v1_trust_service_proto_init() } @@ -956,7 +1143,7 @@ func file_teleport_trust_v1_trust_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_trust_v1_trust_service_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go b/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go index 5a91787708411..4cdc57fe369e1 100644 --- a/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go @@ -42,6 +42,9 @@ const ( TrustService_RotateCertAuthority_FullMethodName = "/teleport.trust.v1.TrustService/RotateCertAuthority" TrustService_RotateExternalCertAuthority_FullMethodName = "/teleport.trust.v1.TrustService/RotateExternalCertAuthority" TrustService_GenerateHostCert_FullMethodName = "/teleport.trust.v1.TrustService/GenerateHostCert" + TrustService_UpsertTrustedCluster_FullMethodName = "/teleport.trust.v1.TrustService/UpsertTrustedCluster" + TrustService_CreateTrustedCluster_FullMethodName = "/teleport.trust.v1.TrustService/CreateTrustedCluster" + TrustService_UpdateTrustedCluster_FullMethodName = "/teleport.trust.v1.TrustService/UpdateTrustedCluster" ) // TrustServiceClient is the client API for TrustService service. @@ -65,6 +68,12 @@ type TrustServiceClient interface { // GenerateHostCert takes a public key in the OpenSSH `authorized_keys` format and returns // a SSH certificate signed by the Host CA. GenerateHostCert(ctx context.Context, in *GenerateHostCertRequest, opts ...grpc.CallOption) (*GenerateHostCertResponse, error) + // UpsertTrustedCluster upserts a Trusted Cluster in a backend. + UpsertTrustedCluster(ctx context.Context, in *UpsertTrustedClusterRequest, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) + // CreateTrustedCluster creates a Trusted Cluster in a backend. + CreateTrustedCluster(ctx context.Context, in *CreateTrustedClusterRequest, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) + // UpdateTrustedCluster updates a Trusted Cluster in a backend. + UpdateTrustedCluster(ctx context.Context, in *UpdateTrustedClusterRequest, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) } type trustServiceClient struct { @@ -145,6 +154,36 @@ func (c *trustServiceClient) GenerateHostCert(ctx context.Context, in *GenerateH return out, nil } +func (c *trustServiceClient) UpsertTrustedCluster(ctx context.Context, in *UpsertTrustedClusterRequest, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(types.TrustedClusterV2) + err := c.cc.Invoke(ctx, TrustService_UpsertTrustedCluster_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *trustServiceClient) CreateTrustedCluster(ctx context.Context, in *CreateTrustedClusterRequest, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(types.TrustedClusterV2) + err := c.cc.Invoke(ctx, TrustService_CreateTrustedCluster_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *trustServiceClient) UpdateTrustedCluster(ctx context.Context, in *UpdateTrustedClusterRequest, opts ...grpc.CallOption) (*types.TrustedClusterV2, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(types.TrustedClusterV2) + err := c.cc.Invoke(ctx, TrustService_UpdateTrustedCluster_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // TrustServiceServer is the server API for TrustService service. // All implementations must embed UnimplementedTrustServiceServer // for forward compatibility. @@ -166,6 +205,12 @@ type TrustServiceServer interface { // GenerateHostCert takes a public key in the OpenSSH `authorized_keys` format and returns // a SSH certificate signed by the Host CA. GenerateHostCert(context.Context, *GenerateHostCertRequest) (*GenerateHostCertResponse, error) + // UpsertTrustedCluster upserts a Trusted Cluster in a backend. + UpsertTrustedCluster(context.Context, *UpsertTrustedClusterRequest) (*types.TrustedClusterV2, error) + // CreateTrustedCluster creates a Trusted Cluster in a backend. + CreateTrustedCluster(context.Context, *CreateTrustedClusterRequest) (*types.TrustedClusterV2, error) + // UpdateTrustedCluster updates a Trusted Cluster in a backend. + UpdateTrustedCluster(context.Context, *UpdateTrustedClusterRequest) (*types.TrustedClusterV2, error) mustEmbedUnimplementedTrustServiceServer() } @@ -197,6 +242,15 @@ func (UnimplementedTrustServiceServer) RotateExternalCertAuthority(context.Conte func (UnimplementedTrustServiceServer) GenerateHostCert(context.Context, *GenerateHostCertRequest) (*GenerateHostCertResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GenerateHostCert not implemented") } +func (UnimplementedTrustServiceServer) UpsertTrustedCluster(context.Context, *UpsertTrustedClusterRequest) (*types.TrustedClusterV2, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpsertTrustedCluster not implemented") +} +func (UnimplementedTrustServiceServer) CreateTrustedCluster(context.Context, *CreateTrustedClusterRequest) (*types.TrustedClusterV2, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateTrustedCluster not implemented") +} +func (UnimplementedTrustServiceServer) UpdateTrustedCluster(context.Context, *UpdateTrustedClusterRequest) (*types.TrustedClusterV2, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateTrustedCluster not implemented") +} func (UnimplementedTrustServiceServer) mustEmbedUnimplementedTrustServiceServer() {} func (UnimplementedTrustServiceServer) testEmbeddedByValue() {} @@ -344,6 +398,60 @@ func _TrustService_GenerateHostCert_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } +func _TrustService_UpsertTrustedCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertTrustedClusterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrustServiceServer).UpsertTrustedCluster(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrustService_UpsertTrustedCluster_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrustServiceServer).UpsertTrustedCluster(ctx, req.(*UpsertTrustedClusterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TrustService_CreateTrustedCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateTrustedClusterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrustServiceServer).CreateTrustedCluster(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrustService_CreateTrustedCluster_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrustServiceServer).CreateTrustedCluster(ctx, req.(*CreateTrustedClusterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TrustService_UpdateTrustedCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateTrustedClusterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrustServiceServer).UpdateTrustedCluster(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrustService_UpdateTrustedCluster_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrustServiceServer).UpdateTrustedCluster(ctx, req.(*UpdateTrustedClusterRequest)) + } + return interceptor(ctx, in, info, handler) +} + // TrustService_ServiceDesc is the grpc.ServiceDesc for TrustService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -379,6 +487,18 @@ var TrustService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GenerateHostCert", Handler: _TrustService_GenerateHostCert_Handler, }, + { + MethodName: "UpsertTrustedCluster", + Handler: _TrustService_UpsertTrustedCluster_Handler, + }, + { + MethodName: "CreateTrustedCluster", + Handler: _TrustService_CreateTrustedCluster_Handler, + }, + { + MethodName: "UpdateTrustedCluster", + Handler: _TrustService_UpdateTrustedCluster_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "teleport/trust/v1/trust_service.proto", diff --git a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate.pb.go b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate.pb.go index 47cc1b04c3b40..f187235904554 100644 --- a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate.pb.go +++ b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userloginstate/v1/userloginstate.proto diff --git a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service.pb.go b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service.pb.go index 94e7e6d20a167..a6b8739ee3c3b 100644 --- a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service.pb.go +++ b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userloginstate/v1/userloginstate_service.proto diff --git a/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser.pb.go b/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser.pb.go index a691f54eb07a9..d35b9ecbe57f4 100644 --- a/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser.pb.go +++ b/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userprovisioning/v2/statichostuser.proto diff --git a/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser_service.pb.go b/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser_service.pb.go index 851b9a6e51f61..aa0b0003d9055 100644 --- a/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser_service.pb.go +++ b/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userprovisioning/v2/statichostuser_service.proto diff --git a/api/gen/proto/go/teleport/users/v1/users_service.pb.go b/api/gen/proto/go/teleport/users/v1/users_service.pb.go index 02f3e0e61422a..8ba0773b86f3e 100644 --- a/api/gen/proto/go/teleport/users/v1/users_service.pb.go +++ b/api/gen/proto/go/teleport/users/v1/users_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/users/v1/users_service.proto diff --git a/api/gen/proto/go/teleport/usertasks/v1/user_tasks.pb.go b/api/gen/proto/go/teleport/usertasks/v1/user_tasks.pb.go index 9ca8b2c58e82c..65e4e25b7e9c6 100644 --- a/api/gen/proto/go/teleport/usertasks/v1/user_tasks.pb.go +++ b/api/gen/proto/go/teleport/usertasks/v1/user_tasks.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/usertasks/v1/user_tasks.proto diff --git a/api/gen/proto/go/teleport/usertasks/v1/user_tasks_service.pb.go b/api/gen/proto/go/teleport/usertasks/v1/user_tasks_service.pb.go index 48b39ad547064..4356cbbb9032e 100644 --- a/api/gen/proto/go/teleport/usertasks/v1/user_tasks_service.pb.go +++ b/api/gen/proto/go/teleport/usertasks/v1/user_tasks_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/usertasks/v1/user_tasks_service.proto diff --git a/api/gen/proto/go/teleport/vnet/v1/vnet_config.pb.go b/api/gen/proto/go/teleport/vnet/v1/vnet_config.pb.go index 9fab3a94da42a..46ed1e982180c 100644 --- a/api/gen/proto/go/teleport/vnet/v1/vnet_config.pb.go +++ b/api/gen/proto/go/teleport/vnet/v1/vnet_config.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/vnet/v1/vnet_config.proto diff --git a/api/gen/proto/go/teleport/vnet/v1/vnet_config_service.pb.go b/api/gen/proto/go/teleport/vnet/v1/vnet_config_service.pb.go index d57baacc956f0..f55eff558f365 100644 --- a/api/gen/proto/go/teleport/vnet/v1/vnet_config_service.pb.go +++ b/api/gen/proto/go/teleport/vnet/v1/vnet_config_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/vnet/v1/vnet_config_service.proto diff --git a/api/gen/proto/go/teleport/workloadidentity/v1/attrs.pb.go b/api/gen/proto/go/teleport/workloadidentity/v1/attrs.pb.go index bc75d65f597e0..b2e7023eabb94 100644 --- a/api/gen/proto/go/teleport/workloadidentity/v1/attrs.pb.go +++ b/api/gen/proto/go/teleport/workloadidentity/v1/attrs.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/workloadidentity/v1/attrs.proto @@ -347,7 +347,10 @@ type Attrs struct { Workload *WorkloadAttrs `protobuf:"bytes,1,opt,name=workload,proto3" json:"workload,omitempty"` // Attributes sourced from the user/bot making the request for a workload // identity credential. - User *UserAttrs `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` + User *UserAttrs `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` + // Attributes sourced from the join process that the Bot underwent. This will + // be unset if the principal making the request is not a Bot. + Join *JoinAttrs `protobuf:"bytes,3,opt,name=join,proto3" json:"join,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -396,6 +399,13 @@ func (x *Attrs) GetUser() *UserAttrs { return nil } +func (x *Attrs) GetJoin() *JoinAttrs { + if x != nil { + return x.Join + } + return nil +} + var File_teleport_workloadidentity_v1_attrs_proto protoreflect.FileDescriptor var file_teleport_workloadidentity_v1_attrs_proto_rawDesc = []byte{ @@ -403,77 +413,84 @@ var file_teleport_workloadidentity_v1_attrs_proto_rawDesc = []byte{ 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x22, 0xc6, 0x02, 0x0a, 0x17, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, - 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x12, 0x59, 0x0a, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, - 0x65, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x65, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, - 0x72, 0x73, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x03, 0x70, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0xab, 0x01, 0x0a, 0x0d, 0x57, 0x6f, 0x72, - 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x75, 0x6e, - 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x41, 0x74, 0x74, 0x72, 0x73, 0x55, 0x6e, 0x69, 0x78, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x12, - 0x55, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, - 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x22, 0x81, 0x02, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, - 0x74, 0x74, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x62, - 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, 0x73, 0x42, 0x6f, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x62, 0x6f, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x62, 0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x6f, - 0x74, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x1a, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc6, 0x02, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x70, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x12, 0x59, 0x0a, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, + 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x65, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, + 0x73, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, + 0x70, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0xab, 0x01, 0x0a, 0x0d, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x75, 0x6e, 0x69, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, + 0x74, 0x74, 0x72, 0x73, 0x55, 0x6e, 0x69, 0x78, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x12, 0x55, + 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x74, 0x74, 0x72, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, - 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8d, 0x01, 0x0a, 0x05, 0x41, - 0x74, 0x74, 0x72, 0x73, 0x12, 0x47, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, - 0x74, 0x72, 0x73, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3b, 0x0a, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, - 0x74, 0x74, 0x72, 0x73, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x42, 0x64, 0x5a, 0x62, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, - 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, - 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, 0x31, 0x3b, 0x77, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, + 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x65, 0x73, 0x22, 0x81, 0x02, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x74, + 0x74, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x62, 0x6f, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, 0x73, 0x42, 0x6f, 0x74, 0x12, 0x19, + 0x0a, 0x08, 0x62, 0x6f, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x62, 0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x6f, 0x74, + 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x62, 0x6f, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, + 0x64, 0x12, 0x4b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x74, 0x74, 0x72, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, + 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x01, 0x0a, 0x05, 0x41, 0x74, + 0x74, 0x72, 0x73, 0x12, 0x47, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, + 0x72, 0x73, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3b, 0x0a, 0x04, + 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x74, + 0x74, 0x72, 0x73, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x04, 0x6a, 0x6f, 0x69, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, + 0x52, 0x04, 0x6a, 0x6f, 0x69, 0x6e, 0x42, 0x64, 0x5a, 0x62, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, 0x31, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -497,6 +514,7 @@ var file_teleport_workloadidentity_v1_attrs_proto_goTypes = []any{ (*Attrs)(nil), // 4: teleport.workloadidentity.v1.Attrs nil, // 5: teleport.workloadidentity.v1.WorkloadAttrsKubernetes.LabelsEntry nil, // 6: teleport.workloadidentity.v1.UserAttrs.LabelsEntry + (*JoinAttrs)(nil), // 7: teleport.workloadidentity.v1.JoinAttrs } var file_teleport_workloadidentity_v1_attrs_proto_depIdxs = []int32{ 5, // 0: teleport.workloadidentity.v1.WorkloadAttrsKubernetes.labels:type_name -> teleport.workloadidentity.v1.WorkloadAttrsKubernetes.LabelsEntry @@ -505,11 +523,12 @@ var file_teleport_workloadidentity_v1_attrs_proto_depIdxs = []int32{ 6, // 3: teleport.workloadidentity.v1.UserAttrs.labels:type_name -> teleport.workloadidentity.v1.UserAttrs.LabelsEntry 2, // 4: teleport.workloadidentity.v1.Attrs.workload:type_name -> teleport.workloadidentity.v1.WorkloadAttrs 3, // 5: teleport.workloadidentity.v1.Attrs.user:type_name -> teleport.workloadidentity.v1.UserAttrs - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 7, // 6: teleport.workloadidentity.v1.Attrs.join:type_name -> teleport.workloadidentity.v1.JoinAttrs + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_teleport_workloadidentity_v1_attrs_proto_init() } @@ -517,6 +536,7 @@ func file_teleport_workloadidentity_v1_attrs_proto_init() { if File_teleport_workloadidentity_v1_attrs_proto != nil { return } + file_teleport_workloadidentity_v1_join_attrs_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/gen/proto/go/teleport/workloadidentity/v1/issuance_service.pb.go b/api/gen/proto/go/teleport/workloadidentity/v1/issuance_service.pb.go index 36166c852d564..48f8d95b87375 100644 --- a/api/gen/proto/go/teleport/workloadidentity/v1/issuance_service.pb.go +++ b/api/gen/proto/go/teleport/workloadidentity/v1/issuance_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/workloadidentity/v1/issuance_service.proto diff --git a/api/gen/proto/go/teleport/workloadidentity/v1/join_attrs.pb.go b/api/gen/proto/go/teleport/workloadidentity/v1/join_attrs.pb.go new file mode 100644 index 0000000000000..f0bc0cfb3e7f5 --- /dev/null +++ b/api/gen/proto/go/teleport/workloadidentity/v1/join_attrs.pb.go @@ -0,0 +1,1772 @@ +// Copyright 2025 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.1 +// protoc (unknown) +// source: teleport/workloadidentity/v1/join_attrs.proto + +package workloadidentityv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// The collection of attributes that result from the join process. +type JoinAttrs struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The collection of attributes that result from the join process but are not + // specific to any particular join method. + Meta *JoinAttrsMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + // Attributes that are specific to the GitLab (`gitlab`) join method. + Gitlab *JoinAttrsGitLab `protobuf:"bytes,2,opt,name=gitlab,proto3" json:"gitlab,omitempty"` + // Attributes that are specific to the GitHub (`github`) join method. + Github *JoinAttrsGitHub `protobuf:"bytes,3,opt,name=github,proto3" json:"github,omitempty"` + // Attributes that are specific to the AWS IAM (`iam`) join method. + Iam *JoinAttrsAWSIAM `protobuf:"bytes,4,opt,name=iam,proto3" json:"iam,omitempty"` + // Attributes that are specific to the TPM (`tpm`) join method. + Tpm *JoinAttrsTPM `protobuf:"bytes,5,opt,name=tpm,proto3" json:"tpm,omitempty"` + // Attributes that are specific to the Azure (`azure`) join method. + Azure *JoinAttrsAzure `protobuf:"bytes,6,opt,name=azure,proto3" json:"azure,omitempty"` + // Attributes that are specific to the CircleCI (`circleci`) join method. + Circleci *JoinAttrsCircleCI `protobuf:"bytes,7,opt,name=circleci,proto3" json:"circleci,omitempty"` + // Attributes that are specific to the Bitbucket (`bitbucket`) join method. + Bitbucket *JoinAttrsBitbucket `protobuf:"bytes,8,opt,name=bitbucket,proto3" json:"bitbucket,omitempty"` + // Attributes that are specific to the Terraform Cloud (`terraform_cloud`) join method. + TerraformCloud *JoinAttrsTerraformCloud `protobuf:"bytes,9,opt,name=terraform_cloud,json=terraformCloud,proto3" json:"terraform_cloud,omitempty"` + // Attributes that are specific to the Spacelift (`spacelift`) join method. + Spacelift *JoinAttrsSpacelift `protobuf:"bytes,10,opt,name=spacelift,proto3" json:"spacelift,omitempty"` + // Attributes that are specific to the GCP (`gcp`) join method. + Gcp *JoinAttrsGCP `protobuf:"bytes,11,opt,name=gcp,proto3" json:"gcp,omitempty"` + // Attributes that are specific to the Kubernetes (`kubernetes`) join method. + Kubernetes *JoinAttrsKubernetes `protobuf:"bytes,12,opt,name=kubernetes,proto3" json:"kubernetes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrs) Reset() { + *x = JoinAttrs{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrs) ProtoMessage() {} + +func (x *JoinAttrs) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrs.ProtoReflect.Descriptor instead. +func (*JoinAttrs) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{0} +} + +func (x *JoinAttrs) GetMeta() *JoinAttrsMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *JoinAttrs) GetGitlab() *JoinAttrsGitLab { + if x != nil { + return x.Gitlab + } + return nil +} + +func (x *JoinAttrs) GetGithub() *JoinAttrsGitHub { + if x != nil { + return x.Github + } + return nil +} + +func (x *JoinAttrs) GetIam() *JoinAttrsAWSIAM { + if x != nil { + return x.Iam + } + return nil +} + +func (x *JoinAttrs) GetTpm() *JoinAttrsTPM { + if x != nil { + return x.Tpm + } + return nil +} + +func (x *JoinAttrs) GetAzure() *JoinAttrsAzure { + if x != nil { + return x.Azure + } + return nil +} + +func (x *JoinAttrs) GetCircleci() *JoinAttrsCircleCI { + if x != nil { + return x.Circleci + } + return nil +} + +func (x *JoinAttrs) GetBitbucket() *JoinAttrsBitbucket { + if x != nil { + return x.Bitbucket + } + return nil +} + +func (x *JoinAttrs) GetTerraformCloud() *JoinAttrsTerraformCloud { + if x != nil { + return x.TerraformCloud + } + return nil +} + +func (x *JoinAttrs) GetSpacelift() *JoinAttrsSpacelift { + if x != nil { + return x.Spacelift + } + return nil +} + +func (x *JoinAttrs) GetGcp() *JoinAttrsGCP { + if x != nil { + return x.Gcp + } + return nil +} + +func (x *JoinAttrs) GetKubernetes() *JoinAttrsKubernetes { + if x != nil { + return x.Kubernetes + } + return nil +} + +// The collection of attributes that result from the join process but are not +// specific to any particular join method. +type JoinAttrsMeta struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The name of the join token that was used to join. + // + // This field is omitted if the join token that was used to join was of the + // `token` method as in this case, the name of the join token is sensitive. + // + // Example: `my-gitlab-join-token` + JoinTokenName string `protobuf:"bytes,1,opt,name=join_token_name,json=joinTokenName,proto3" json:"join_token_name,omitempty"` + // The name of the join method that was used to join. + // + // Example: `gitlab` + JoinMethod string `protobuf:"bytes,2,opt,name=join_method,json=joinMethod,proto3" json:"join_method,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsMeta) Reset() { + *x = JoinAttrsMeta{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsMeta) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsMeta) ProtoMessage() {} + +func (x *JoinAttrsMeta) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsMeta.ProtoReflect.Descriptor instead. +func (*JoinAttrsMeta) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{1} +} + +func (x *JoinAttrsMeta) GetJoinTokenName() string { + if x != nil { + return x.JoinTokenName + } + return "" +} + +func (x *JoinAttrsMeta) GetJoinMethod() string { + if x != nil { + return x.JoinMethod + } + return "" +} + +// Attributes that are specific to the GitLab join method. +// +// Typically, these are mapped directly from the claims of the GitLab JWT that +// was used to join. You can view the documentation for those claims at: +// https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html#token-payload +type JoinAttrsGitLab struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The `sub` claim of the GitLab JWT that was used to join. + // For example: `project_path:mygroup/my-project:ref_type:branch:ref:main` + Sub string `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"` + // The ref that the pipeline is running against. + // For example: `main` + Ref string `protobuf:"bytes,2,opt,name=ref,proto3" json:"ref,omitempty"` + // The type of ref that the pipeline is running against. + // This is typically `branch` or `tag`. + RefType string `protobuf:"bytes,3,opt,name=ref_type,json=refType,proto3" json:"ref_type,omitempty"` + // Whether or not the ref that the pipeline is running against is protected. + RefProtected bool `protobuf:"varint,4,opt,name=ref_protected,json=refProtected,proto3" json:"ref_protected,omitempty"` + // The path of the namespace of the project that the pipeline is running within. + // For example: `mygroup` + NamespacePath string `protobuf:"bytes,5,opt,name=namespace_path,json=namespacePath,proto3" json:"namespace_path,omitempty"` + // The full qualified path of the project that the pipeline is running within. + // This includes the namespace path. + // For example: `mygroup/my-project` + ProjectPath string `protobuf:"bytes,6,opt,name=project_path,json=projectPath,proto3" json:"project_path,omitempty"` + // The name of the user that triggered the pipeline run. + UserLogin string `protobuf:"bytes,7,opt,name=user_login,json=userLogin,proto3" json:"user_login,omitempty"` + // The email of the user that triggered the pipeline run. + UserEmail string `protobuf:"bytes,8,opt,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"` + // The ID of the pipeline. + PipelineId string `protobuf:"bytes,9,opt,name=pipeline_id,json=pipelineId,proto3" json:"pipeline_id,omitempty"` + // The source of the pipeline. + // For example: `push` or `web` + PipelineSource string `protobuf:"bytes,10,opt,name=pipeline_source,json=pipelineSource,proto3" json:"pipeline_source,omitempty"` + // The environment the pipeline is running against, if any. + Environment string `protobuf:"bytes,11,opt,name=environment,proto3" json:"environment,omitempty"` + // Whether or not the pipeline is running against a protected environment. + // If there is no configured environment, this field is false. + EnvironmentProtected bool `protobuf:"varint,12,opt,name=environment_protected,json=environmentProtected,proto3" json:"environment_protected,omitempty"` + // The ID of the runner that this pipeline is running on. + RunnerId int64 `protobuf:"varint,13,opt,name=runner_id,json=runnerId,proto3" json:"runner_id,omitempty"` + // The type of runner that is processing the pipeline. + // Either `gitlab-hosted` or `self-hosted`. + RunnerEnvironment string `protobuf:"bytes,14,opt,name=runner_environment,json=runnerEnvironment,proto3" json:"runner_environment,omitempty"` + // The SHA of the commit that triggered the pipeline run. + Sha string `protobuf:"bytes,15,opt,name=sha,proto3" json:"sha,omitempty"` + // The ref URI of the CI config configuring the pipeline. + CiConfigRefUri string `protobuf:"bytes,16,opt,name=ci_config_ref_uri,json=ciConfigRefUri,proto3" json:"ci_config_ref_uri,omitempty"` + // The Git SHA of the CI config ref configuring the pipeline. + CiConfigSha string `protobuf:"bytes,17,opt,name=ci_config_sha,json=ciConfigSha,proto3" json:"ci_config_sha,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsGitLab) Reset() { + *x = JoinAttrsGitLab{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsGitLab) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsGitLab) ProtoMessage() {} + +func (x *JoinAttrsGitLab) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsGitLab.ProtoReflect.Descriptor instead. +func (*JoinAttrsGitLab) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{2} +} + +func (x *JoinAttrsGitLab) GetSub() string { + if x != nil { + return x.Sub + } + return "" +} + +func (x *JoinAttrsGitLab) GetRef() string { + if x != nil { + return x.Ref + } + return "" +} + +func (x *JoinAttrsGitLab) GetRefType() string { + if x != nil { + return x.RefType + } + return "" +} + +func (x *JoinAttrsGitLab) GetRefProtected() bool { + if x != nil { + return x.RefProtected + } + return false +} + +func (x *JoinAttrsGitLab) GetNamespacePath() string { + if x != nil { + return x.NamespacePath + } + return "" +} + +func (x *JoinAttrsGitLab) GetProjectPath() string { + if x != nil { + return x.ProjectPath + } + return "" +} + +func (x *JoinAttrsGitLab) GetUserLogin() string { + if x != nil { + return x.UserLogin + } + return "" +} + +func (x *JoinAttrsGitLab) GetUserEmail() string { + if x != nil { + return x.UserEmail + } + return "" +} + +func (x *JoinAttrsGitLab) GetPipelineId() string { + if x != nil { + return x.PipelineId + } + return "" +} + +func (x *JoinAttrsGitLab) GetPipelineSource() string { + if x != nil { + return x.PipelineSource + } + return "" +} + +func (x *JoinAttrsGitLab) GetEnvironment() string { + if x != nil { + return x.Environment + } + return "" +} + +func (x *JoinAttrsGitLab) GetEnvironmentProtected() bool { + if x != nil { + return x.EnvironmentProtected + } + return false +} + +func (x *JoinAttrsGitLab) GetRunnerId() int64 { + if x != nil { + return x.RunnerId + } + return 0 +} + +func (x *JoinAttrsGitLab) GetRunnerEnvironment() string { + if x != nil { + return x.RunnerEnvironment + } + return "" +} + +func (x *JoinAttrsGitLab) GetSha() string { + if x != nil { + return x.Sha + } + return "" +} + +func (x *JoinAttrsGitLab) GetCiConfigRefUri() string { + if x != nil { + return x.CiConfigRefUri + } + return "" +} + +func (x *JoinAttrsGitLab) GetCiConfigSha() string { + if x != nil { + return x.CiConfigSha + } + return "" +} + +// Attributes that are specific to the GitHub (`github`) join method. +// +// Typically, these are mapped directly from the claims of the GitHub JWT that +// was used to join. You can view the documentation for those claims at: +// https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#understanding-the-oidc-token +type JoinAttrsGitHub struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The `sub` claim of the GitHub JWT that was used to join. + Sub string `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"` + // The username of the actor that initiated the workflow run. + Actor string `protobuf:"bytes,2,opt,name=actor,proto3" json:"actor,omitempty"` + // The name of the environment that the workflow is running against, if any. + Environment string `protobuf:"bytes,3,opt,name=environment,proto3" json:"environment,omitempty"` + // The ref that the workflow is running against. + Ref string `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"` + // The type of ref that the workflow is running against. + // For example, `branch`. + RefType string `protobuf:"bytes,5,opt,name=ref_type,json=refType,proto3" json:"ref_type,omitempty"` + // The name of the repository that the workflow is running within. + Repository string `protobuf:"bytes,6,opt,name=repository,proto3" json:"repository,omitempty"` + // The name of the owner of the repository that the workflow is running within. + RepositoryOwner string `protobuf:"bytes,7,opt,name=repository_owner,json=repositoryOwner,proto3" json:"repository_owner,omitempty"` + // The name of the workflow that is running. + Workflow string `protobuf:"bytes,8,opt,name=workflow,proto3" json:"workflow,omitempty"` + // The name of the event that triggered the workflow run. + EventName string `protobuf:"bytes,9,opt,name=event_name,json=eventName,proto3" json:"event_name,omitempty"` + // The SHA of the commit that triggered the workflow run. + Sha string `protobuf:"bytes,10,opt,name=sha,proto3" json:"sha,omitempty"` + // The ID of this GitHub actions workflow run. + RunId string `protobuf:"bytes,11,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsGitHub) Reset() { + *x = JoinAttrsGitHub{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsGitHub) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsGitHub) ProtoMessage() {} + +func (x *JoinAttrsGitHub) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsGitHub.ProtoReflect.Descriptor instead. +func (*JoinAttrsGitHub) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{3} +} + +func (x *JoinAttrsGitHub) GetSub() string { + if x != nil { + return x.Sub + } + return "" +} + +func (x *JoinAttrsGitHub) GetActor() string { + if x != nil { + return x.Actor + } + return "" +} + +func (x *JoinAttrsGitHub) GetEnvironment() string { + if x != nil { + return x.Environment + } + return "" +} + +func (x *JoinAttrsGitHub) GetRef() string { + if x != nil { + return x.Ref + } + return "" +} + +func (x *JoinAttrsGitHub) GetRefType() string { + if x != nil { + return x.RefType + } + return "" +} + +func (x *JoinAttrsGitHub) GetRepository() string { + if x != nil { + return x.Repository + } + return "" +} + +func (x *JoinAttrsGitHub) GetRepositoryOwner() string { + if x != nil { + return x.RepositoryOwner + } + return "" +} + +func (x *JoinAttrsGitHub) GetWorkflow() string { + if x != nil { + return x.Workflow + } + return "" +} + +func (x *JoinAttrsGitHub) GetEventName() string { + if x != nil { + return x.EventName + } + return "" +} + +func (x *JoinAttrsGitHub) GetSha() string { + if x != nil { + return x.Sha + } + return "" +} + +func (x *JoinAttrsGitHub) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +// Attributes that are specific to the AWS IAM (`iam`) join method. +// +// Typically, these are mapped directly from the results of the +// STS GetCallerIdentity call that is made as part of the join process. +type JoinAttrsAWSIAM struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The identifier of the account that the joining entity is a part of. + // For example: `123456789012` + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + // The AWS ARN of the joining entity. + // For example: `arn:aws:sts::123456789012:assumed-role/my-role-name/my-role-session-name` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsAWSIAM) Reset() { + *x = JoinAttrsAWSIAM{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsAWSIAM) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsAWSIAM) ProtoMessage() {} + +func (x *JoinAttrsAWSIAM) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsAWSIAM.ProtoReflect.Descriptor instead. +func (*JoinAttrsAWSIAM) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{4} +} + +func (x *JoinAttrsAWSIAM) GetAccount() string { + if x != nil { + return x.Account + } + return "" +} + +func (x *JoinAttrsAWSIAM) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +// Attributes that are specific to the TPM (`tpm`) join method. +type JoinAttrsTPM struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The SHA256 hash of the PKIX formatted EK public key, encoded in hex. + // This effectively identifies a specific TPM. + EkPubHash string `protobuf:"bytes,1,opt,name=ek_pub_hash,json=ekPubHash,proto3" json:"ek_pub_hash,omitempty"` + // The serial number of the EK certificate, if present. + EkCertSerial string `protobuf:"bytes,2,opt,name=ek_cert_serial,json=ekCertSerial,proto3" json:"ek_cert_serial,omitempty"` + // Whether or not the EK certificate was verified against a certificate + // authority. + EkCertVerified bool `protobuf:"varint,3,opt,name=ek_cert_verified,json=ekCertVerified,proto3" json:"ek_cert_verified,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsTPM) Reset() { + *x = JoinAttrsTPM{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsTPM) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsTPM) ProtoMessage() {} + +func (x *JoinAttrsTPM) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsTPM.ProtoReflect.Descriptor instead. +func (*JoinAttrsTPM) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{5} +} + +func (x *JoinAttrsTPM) GetEkPubHash() string { + if x != nil { + return x.EkPubHash + } + return "" +} + +func (x *JoinAttrsTPM) GetEkCertSerial() string { + if x != nil { + return x.EkCertSerial + } + return "" +} + +func (x *JoinAttrsTPM) GetEkCertVerified() bool { + if x != nil { + return x.EkCertVerified + } + return false +} + +// Attributes that are specific to the Azure (`azure`) join method. +type JoinAttrsAzure struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The subscription ID of the Azure account that the joining entity is a part of. + Subscription string `protobuf:"bytes,1,opt,name=subscription,proto3" json:"subscription,omitempty"` + // The resource group of the Azure account that the joining entity is a part of. + ResourceGroup string `protobuf:"bytes,2,opt,name=resource_group,json=resourceGroup,proto3" json:"resource_group,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsAzure) Reset() { + *x = JoinAttrsAzure{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsAzure) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsAzure) ProtoMessage() {} + +func (x *JoinAttrsAzure) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsAzure.ProtoReflect.Descriptor instead. +func (*JoinAttrsAzure) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{6} +} + +func (x *JoinAttrsAzure) GetSubscription() string { + if x != nil { + return x.Subscription + } + return "" +} + +func (x *JoinAttrsAzure) GetResourceGroup() string { + if x != nil { + return x.ResourceGroup + } + return "" +} + +// Attributes that are specific to the CircleCI (`circleci`) join method. +// These are mapped from the claims of the JWT issued by CircleCI to runs, +// which is documented at: https://circleci.com/docs/openid-connect-tokens/ +type JoinAttrsCircleCI struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The `sub` claim of the CircleCI JWT that was used to join. + // For example: `org/ORGANIZATION_ID/project/PROJECT_ID/user/USER_ID` + Sub string `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"` + // The UUIDs of the contexts used in the job. + ContextIds []string `protobuf:"bytes,2,rep,name=context_ids,json=contextIds,proto3" json:"context_ids,omitempty"` + // The UUID of the project in which the job is running. + ProjectId string `protobuf:"bytes,3,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsCircleCI) Reset() { + *x = JoinAttrsCircleCI{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsCircleCI) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsCircleCI) ProtoMessage() {} + +func (x *JoinAttrsCircleCI) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsCircleCI.ProtoReflect.Descriptor instead. +func (*JoinAttrsCircleCI) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{7} +} + +func (x *JoinAttrsCircleCI) GetSub() string { + if x != nil { + return x.Sub + } + return "" +} + +func (x *JoinAttrsCircleCI) GetContextIds() []string { + if x != nil { + return x.ContextIds + } + return nil +} + +func (x *JoinAttrsCircleCI) GetProjectId() string { + if x != nil { + return x.ProjectId + } + return "" +} + +// Attributes that are specific to the Bitbucket (`bitbucket`) join method. +// These are mapped from the claims of the JWT issued by BitBucket to runs, +// which is documented at: https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/ +type JoinAttrsBitbucket struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The `sub` claim of the Bitbucket JWT that was used to join. + Sub string `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"` + // The UUID of the pipeline step. + StepUuid string `protobuf:"bytes,2,opt,name=step_uuid,json=stepUuid,proto3" json:"step_uuid,omitempty"` + // The UUID of the repository the pipeline step is running within. + RepositoryUuid string `protobuf:"bytes,3,opt,name=repository_uuid,json=repositoryUuid,proto3" json:"repository_uuid,omitempty"` + // The UUID of the pipeline the step is running within. + PipelineUuid string `protobuf:"bytes,4,opt,name=pipeline_uuid,json=pipelineUuid,proto3" json:"pipeline_uuid,omitempty"` + // The UUID of the workspace the pipeline belongs to. + WorkspaceUuid string `protobuf:"bytes,5,opt,name=workspace_uuid,json=workspaceUuid,proto3" json:"workspace_uuid,omitempty"` + // The UUID of the deployment environment the pipeline is running against. + DeploymentEnvironmentUuid string `protobuf:"bytes,6,opt,name=deployment_environment_uuid,json=deploymentEnvironmentUuid,proto3" json:"deployment_environment_uuid,omitempty"` + // The name of the branch the pipeline is running against. + BranchName string `protobuf:"bytes,7,opt,name=branch_name,json=branchName,proto3" json:"branch_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsBitbucket) Reset() { + *x = JoinAttrsBitbucket{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsBitbucket) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsBitbucket) ProtoMessage() {} + +func (x *JoinAttrsBitbucket) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsBitbucket.ProtoReflect.Descriptor instead. +func (*JoinAttrsBitbucket) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{8} +} + +func (x *JoinAttrsBitbucket) GetSub() string { + if x != nil { + return x.Sub + } + return "" +} + +func (x *JoinAttrsBitbucket) GetStepUuid() string { + if x != nil { + return x.StepUuid + } + return "" +} + +func (x *JoinAttrsBitbucket) GetRepositoryUuid() string { + if x != nil { + return x.RepositoryUuid + } + return "" +} + +func (x *JoinAttrsBitbucket) GetPipelineUuid() string { + if x != nil { + return x.PipelineUuid + } + return "" +} + +func (x *JoinAttrsBitbucket) GetWorkspaceUuid() string { + if x != nil { + return x.WorkspaceUuid + } + return "" +} + +func (x *JoinAttrsBitbucket) GetDeploymentEnvironmentUuid() string { + if x != nil { + return x.DeploymentEnvironmentUuid + } + return "" +} + +func (x *JoinAttrsBitbucket) GetBranchName() string { + if x != nil { + return x.BranchName + } + return "" +} + +// Attributes that are specific to the Terraform Cloud (`terraform_cloud`) join method. +// These are mapped from the claims of the JWT issued by Terraform Cloud to runs, +// which is documented at: https://developer.hashicorp.com/terraform/enterprise/workspaces/dynamic-provider-credentials/workload-identity-tokens +type JoinAttrsTerraformCloud struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The `sub` claim of the Terraform Cloud JWT that was used to join. + Sub string `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"` + // The name of the organization the project and workspace belong to. + OrganizationName string `protobuf:"bytes,2,opt,name=organization_name,json=organizationName,proto3" json:"organization_name,omitempty"` + // The name of the project the workspace belongs to. + ProjectName string `protobuf:"bytes,3,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` + // The name of the workspace that the plan/apply is running within. + WorkspaceName string `protobuf:"bytes,4,opt,name=workspace_name,json=workspaceName,proto3" json:"workspace_name,omitempty"` + // The fully qualified workspace path, including the organization and project + // name. + // For example: `organization::project::workspace:` + FullWorkspace string `protobuf:"bytes,5,opt,name=full_workspace,json=fullWorkspace,proto3" json:"full_workspace,omitempty"` + // The ID of the run that is being executed. + RunId string `protobuf:"bytes,6,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + // The phase of the run that is being executed, either `plan` or `apply`. + RunPhase string `protobuf:"bytes,7,opt,name=run_phase,json=runPhase,proto3" json:"run_phase,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsTerraformCloud) Reset() { + *x = JoinAttrsTerraformCloud{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsTerraformCloud) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsTerraformCloud) ProtoMessage() {} + +func (x *JoinAttrsTerraformCloud) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsTerraformCloud.ProtoReflect.Descriptor instead. +func (*JoinAttrsTerraformCloud) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{9} +} + +func (x *JoinAttrsTerraformCloud) GetSub() string { + if x != nil { + return x.Sub + } + return "" +} + +func (x *JoinAttrsTerraformCloud) GetOrganizationName() string { + if x != nil { + return x.OrganizationName + } + return "" +} + +func (x *JoinAttrsTerraformCloud) GetProjectName() string { + if x != nil { + return x.ProjectName + } + return "" +} + +func (x *JoinAttrsTerraformCloud) GetWorkspaceName() string { + if x != nil { + return x.WorkspaceName + } + return "" +} + +func (x *JoinAttrsTerraformCloud) GetFullWorkspace() string { + if x != nil { + return x.FullWorkspace + } + return "" +} + +func (x *JoinAttrsTerraformCloud) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +func (x *JoinAttrsTerraformCloud) GetRunPhase() string { + if x != nil { + return x.RunPhase + } + return "" +} + +// Attributes that are specific to the Spacelift (`spacelift`) join method. +// These are mapped from the claims of the JWT issued by Spacelift to runs, +// which is documented at: https://docs.spacelift.io/integrations/cloud-providers/oidc/#standard-claims +type JoinAttrsSpacelift struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The `sub` claim of the Spacelift JWT that was used to join. + Sub string `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"` + // The ID of the space in which the run is executing. + SpaceId string `protobuf:"bytes,2,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` + // The type of the caller that owns the run, either `stack` or `module`. + CallerType string `protobuf:"bytes,3,opt,name=caller_type,json=callerType,proto3" json:"caller_type,omitempty"` + // The ID of the caller that generated the run. + CallerId string `protobuf:"bytes,4,opt,name=caller_id,json=callerId,proto3" json:"caller_id,omitempty"` + // The type of the run, either `PROPOSED`, `TRACKED`, `TASK`, `TESTING` or `DESTROY`. + RunType string `protobuf:"bytes,5,opt,name=run_type,json=runType,proto3" json:"run_type,omitempty"` + // The ID of the run. + RunId string `protobuf:"bytes,6,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` + // The configured scope of the token, either `read` or `write`. + Scope string `protobuf:"bytes,7,opt,name=scope,proto3" json:"scope,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsSpacelift) Reset() { + *x = JoinAttrsSpacelift{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsSpacelift) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsSpacelift) ProtoMessage() {} + +func (x *JoinAttrsSpacelift) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsSpacelift.ProtoReflect.Descriptor instead. +func (*JoinAttrsSpacelift) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{10} +} + +func (x *JoinAttrsSpacelift) GetSub() string { + if x != nil { + return x.Sub + } + return "" +} + +func (x *JoinAttrsSpacelift) GetSpaceId() string { + if x != nil { + return x.SpaceId + } + return "" +} + +func (x *JoinAttrsSpacelift) GetCallerType() string { + if x != nil { + return x.CallerType + } + return "" +} + +func (x *JoinAttrsSpacelift) GetCallerId() string { + if x != nil { + return x.CallerId + } + return "" +} + +func (x *JoinAttrsSpacelift) GetRunType() string { + if x != nil { + return x.RunType + } + return "" +} + +func (x *JoinAttrsSpacelift) GetRunId() string { + if x != nil { + return x.RunId + } + return "" +} + +func (x *JoinAttrsSpacelift) GetScope() string { + if x != nil { + return x.Scope + } + return "" +} + +// Attributes specific to the GCP join method when the joining entity is on a +// GCE instance. +type JoinAttrsGCPGCE struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The name of the GCE instance that the joining entity is running on. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The zone of the GCE instance that the joining entity is running on. + Zone string `protobuf:"bytes,2,opt,name=zone,proto3" json:"zone,omitempty"` + // The ID of the GCE instance that the joining entity is running on. + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + // The project ID of the GCP project that the instance is running within. + Project string `protobuf:"bytes,4,opt,name=project,proto3" json:"project,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsGCPGCE) Reset() { + *x = JoinAttrsGCPGCE{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsGCPGCE) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsGCPGCE) ProtoMessage() {} + +func (x *JoinAttrsGCPGCE) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsGCPGCE.ProtoReflect.Descriptor instead. +func (*JoinAttrsGCPGCE) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{11} +} + +func (x *JoinAttrsGCPGCE) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *JoinAttrsGCPGCE) GetZone() string { + if x != nil { + return x.Zone + } + return "" +} + +func (x *JoinAttrsGCPGCE) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *JoinAttrsGCPGCE) GetProject() string { + if x != nil { + return x.Project + } + return "" +} + +// Attributes that are specific to the GCP (`gcp`) join method. +// These are mapped from the claims of the JWT instance identity token, which +// is documented at: https://cloud.google.com/compute/docs/instances/verifying-instance-identity#payload +type JoinAttrsGCP struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The service account email of the service account that the instance is running as. + ServiceAccount string `protobuf:"bytes,1,opt,name=service_account,json=serviceAccount,proto3" json:"service_account,omitempty"` + // Attributes specific to the GCP join method when the joining entity is on a + // GCE instance. This may not be present if the joining entity is not on + // GCE. + Gce *JoinAttrsGCPGCE `protobuf:"bytes,2,opt,name=gce,proto3" json:"gce,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsGCP) Reset() { + *x = JoinAttrsGCP{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsGCP) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsGCP) ProtoMessage() {} + +func (x *JoinAttrsGCP) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsGCP.ProtoReflect.Descriptor instead. +func (*JoinAttrsGCP) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{12} +} + +func (x *JoinAttrsGCP) GetServiceAccount() string { + if x != nil { + return x.ServiceAccount + } + return "" +} + +func (x *JoinAttrsGCP) GetGce() *JoinAttrsGCPGCE { + if x != nil { + return x.Gce + } + return nil +} + +// Attributes that are specific to the Kubernetes (`kubernetes`) join method +// when a pod-bound service account token is used. +type JoinAttrsKubernetesPod struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The name of the service account that the joining entity is running as. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsKubernetesPod) Reset() { + *x = JoinAttrsKubernetesPod{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsKubernetesPod) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsKubernetesPod) ProtoMessage() {} + +func (x *JoinAttrsKubernetesPod) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsKubernetesPod.ProtoReflect.Descriptor instead. +func (*JoinAttrsKubernetesPod) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{13} +} + +func (x *JoinAttrsKubernetesPod) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// Attributes that are specific to the Kubernetes (`kubernetes`) join method +// when a service account token is used. +type JoinAttrsKubernetesServiceAccount struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The name of the service account that the joining entity is running as. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The namespace of the service account that the joining entity is running as. + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsKubernetesServiceAccount) Reset() { + *x = JoinAttrsKubernetesServiceAccount{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsKubernetesServiceAccount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsKubernetesServiceAccount) ProtoMessage() {} + +func (x *JoinAttrsKubernetesServiceAccount) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsKubernetesServiceAccount.ProtoReflect.Descriptor instead. +func (*JoinAttrsKubernetesServiceAccount) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{14} +} + +func (x *JoinAttrsKubernetesServiceAccount) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *JoinAttrsKubernetesServiceAccount) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +// Attributes that are specific to the Kubernetes (`kubernetes`) join method. +type JoinAttrsKubernetes struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The fully qualified identifier of the entity based on the Kubernetes + // token. For a service account, this takes the form of + // `system:serviceaccount::`. + Subject string `protobuf:"bytes,1,opt,name=subject,proto3" json:"subject,omitempty"` + // Attributes specific to the Kubernetes join method when the joining entity + // is a service account token. This will only be present if the joining entity + // is a service account (as opposed to a human user or similar). + ServiceAccount *JoinAttrsKubernetesServiceAccount `protobuf:"bytes,2,opt,name=service_account,json=serviceAccount,proto3" json:"service_account,omitempty"` + // Attributes specific to the Kubernetes join method when the joining entity + // is a pod-bound service account token. This will only be present if the + // joining entity is a service account, and, the token has been bound to a + // pod. + Pod *JoinAttrsKubernetesPod `protobuf:"bytes,3,opt,name=pod,proto3" json:"pod,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinAttrsKubernetes) Reset() { + *x = JoinAttrsKubernetes{} + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinAttrsKubernetes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinAttrsKubernetes) ProtoMessage() {} + +func (x *JoinAttrsKubernetes) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinAttrsKubernetes.ProtoReflect.Descriptor instead. +func (*JoinAttrsKubernetes) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP(), []int{15} +} + +func (x *JoinAttrsKubernetes) GetSubject() string { + if x != nil { + return x.Subject + } + return "" +} + +func (x *JoinAttrsKubernetes) GetServiceAccount() *JoinAttrsKubernetesServiceAccount { + if x != nil { + return x.ServiceAccount + } + return nil +} + +func (x *JoinAttrsKubernetes) GetPod() *JoinAttrsKubernetesPod { + if x != nil { + return x.Pod + } + return nil +} + +var File_teleport_workloadidentity_v1_join_attrs_proto protoreflect.FileDescriptor + +var file_teleport_workloadidentity_v1_join_attrs_proto_rawDesc = []byte{ + 0x0a, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x6a, + 0x6f, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x1c, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x22, 0xfb, 0x06, + 0x0a, 0x09, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x04, 0x6d, + 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, + 0x72, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x45, 0x0a, 0x06, + 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, + 0x41, 0x74, 0x74, 0x72, 0x73, 0x47, 0x69, 0x74, 0x4c, 0x61, 0x62, 0x52, 0x06, 0x67, 0x69, 0x74, + 0x6c, 0x61, 0x62, 0x12, 0x45, 0x0a, 0x06, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x47, 0x69, 0x74, 0x48, + 0x75, 0x62, 0x52, 0x06, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x12, 0x3f, 0x0a, 0x03, 0x69, 0x61, + 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, + 0x41, 0x57, 0x53, 0x49, 0x41, 0x4d, 0x52, 0x03, 0x69, 0x61, 0x6d, 0x12, 0x3c, 0x0a, 0x03, 0x74, + 0x70, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, + 0x73, 0x54, 0x50, 0x4d, 0x52, 0x03, 0x74, 0x70, 0x6d, 0x12, 0x42, 0x0a, 0x05, 0x61, 0x7a, 0x75, + 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, + 0x73, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x52, 0x05, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x4b, 0x0a, + 0x08, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x63, 0x69, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, + 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x43, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x43, 0x49, + 0x52, 0x08, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x63, 0x69, 0x12, 0x4e, 0x0a, 0x09, 0x62, 0x69, + 0x74, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, + 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x42, 0x69, 0x74, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, + 0x09, 0x62, 0x69, 0x74, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x5e, 0x0a, 0x0f, 0x74, 0x65, + 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x54, 0x65, 0x72, 0x72, + 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x52, 0x0e, 0x74, 0x65, 0x72, 0x72, + 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x12, 0x4e, 0x0a, 0x09, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x6c, 0x69, 0x66, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, + 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x53, 0x70, 0x61, 0x63, 0x65, 0x6c, 0x69, 0x66, 0x74, 0x52, + 0x09, 0x73, 0x70, 0x61, 0x63, 0x65, 0x6c, 0x69, 0x66, 0x74, 0x12, 0x3c, 0x0a, 0x03, 0x67, 0x63, + 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, + 0x47, 0x43, 0x50, 0x52, 0x03, 0x67, 0x63, 0x70, 0x12, 0x51, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, + 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x52, + 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0d, 0x4a, + 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0f, + 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6a, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0xcb, 0x04, 0x0a, 0x0f, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, + 0x74, 0x72, 0x73, 0x47, 0x69, 0x74, 0x4c, 0x61, 0x62, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x62, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x10, 0x0a, 0x03, 0x72, + 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, + 0x08, 0x72, 0x65, 0x66, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x72, 0x65, 0x66, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x5f, + 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x72, 0x65, 0x66, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, + 0x0e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, + 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, + 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, + 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x69, 0x70, 0x65, + 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, + 0x6e, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x33, 0x0a, 0x15, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x14, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, + 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x72, 0x75, 0x6e, 0x6e, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x11, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x68, 0x61, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x73, 0x68, 0x61, 0x12, 0x29, 0x0a, 0x11, 0x63, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x63, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x66, 0x55, 0x72, 0x69, 0x12, + 0x22, 0x0a, 0x0d, 0x63, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x68, 0x61, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x53, 0x68, 0x61, 0x22, 0xb7, 0x02, 0x0a, 0x0f, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, + 0x73, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x62, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x20, 0x0a, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x66, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x66, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x29, + 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x68, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x73, 0x68, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x3d, 0x0a, + 0x0f, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x41, 0x57, 0x53, 0x49, 0x41, 0x4d, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x22, 0x7e, 0x0a, 0x0c, + 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x54, 0x50, 0x4d, 0x12, 0x1e, 0x0a, 0x0b, + 0x65, 0x6b, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x65, 0x6b, 0x50, 0x75, 0x62, 0x48, 0x61, 0x73, 0x68, 0x12, 0x24, 0x0a, 0x0e, + 0x65, 0x6b, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6b, 0x43, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x6b, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x6b, + 0x43, 0x65, 0x72, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x5b, 0x0a, 0x0e, + 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x22, + 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x65, 0x0a, 0x11, 0x4a, 0x6f, 0x69, + 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x43, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x43, 0x49, 0x12, 0x10, + 0x0a, 0x03, 0x73, 0x75, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x75, 0x62, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, + 0x22, 0x99, 0x02, 0x0a, 0x12, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x42, 0x69, + 0x74, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x62, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x65, + 0x70, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, + 0x65, 0x70, 0x55, 0x75, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x55, 0x75, 0x69, 0x64, 0x12, + 0x23, 0x0a, 0x0d, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x55, 0x75, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x3e, 0x0a, 0x1b, 0x64, + 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x19, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, + 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xfd, 0x01, 0x0a, + 0x17, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x54, 0x65, 0x72, 0x72, 0x61, 0x66, + 0x6f, 0x72, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x62, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x75, 0x6c, 0x6c, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x72, 0x75, 0x6e, 0x50, 0x68, 0x61, 0x73, 0x65, 0x22, 0xc7, 0x01, 0x0a, + 0x12, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x53, 0x70, 0x61, 0x63, 0x65, 0x6c, + 0x69, 0x66, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x73, 0x75, 0x62, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, + 0x0a, 0x08, 0x72, 0x75, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x22, 0x63, 0x0a, 0x0f, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, + 0x74, 0x72, 0x73, 0x47, 0x43, 0x50, 0x47, 0x43, 0x45, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x7a, 0x6f, 0x6e, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x78, 0x0a, 0x0c, 0x4a, + 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x47, 0x43, 0x50, 0x12, 0x27, 0x0a, 0x0f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3f, 0x0a, 0x03, 0x67, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, + 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x47, 0x43, 0x50, 0x47, 0x43, 0x45, + 0x52, 0x03, 0x67, 0x63, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, + 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x50, 0x6f, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x55, 0x0a, 0x21, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, + 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xe1, 0x01, 0x0a, 0x13, 0x4a, + 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x68, 0x0a, 0x0f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, + 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x03, 0x70, 0x6f, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x72, 0x73, 0x4b, 0x75, 0x62, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x50, 0x6f, 0x64, 0x52, 0x03, 0x70, 0x6f, 0x64, 0x42, 0x64, + 0x5a, 0x62, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, + 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x77, 0x6f, + 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, + 0x31, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_teleport_workloadidentity_v1_join_attrs_proto_rawDescOnce sync.Once + file_teleport_workloadidentity_v1_join_attrs_proto_rawDescData = file_teleport_workloadidentity_v1_join_attrs_proto_rawDesc +) + +func file_teleport_workloadidentity_v1_join_attrs_proto_rawDescGZIP() []byte { + file_teleport_workloadidentity_v1_join_attrs_proto_rawDescOnce.Do(func() { + file_teleport_workloadidentity_v1_join_attrs_proto_rawDescData = protoimpl.X.CompressGZIP(file_teleport_workloadidentity_v1_join_attrs_proto_rawDescData) + }) + return file_teleport_workloadidentity_v1_join_attrs_proto_rawDescData +} + +var file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_teleport_workloadidentity_v1_join_attrs_proto_goTypes = []any{ + (*JoinAttrs)(nil), // 0: teleport.workloadidentity.v1.JoinAttrs + (*JoinAttrsMeta)(nil), // 1: teleport.workloadidentity.v1.JoinAttrsMeta + (*JoinAttrsGitLab)(nil), // 2: teleport.workloadidentity.v1.JoinAttrsGitLab + (*JoinAttrsGitHub)(nil), // 3: teleport.workloadidentity.v1.JoinAttrsGitHub + (*JoinAttrsAWSIAM)(nil), // 4: teleport.workloadidentity.v1.JoinAttrsAWSIAM + (*JoinAttrsTPM)(nil), // 5: teleport.workloadidentity.v1.JoinAttrsTPM + (*JoinAttrsAzure)(nil), // 6: teleport.workloadidentity.v1.JoinAttrsAzure + (*JoinAttrsCircleCI)(nil), // 7: teleport.workloadidentity.v1.JoinAttrsCircleCI + (*JoinAttrsBitbucket)(nil), // 8: teleport.workloadidentity.v1.JoinAttrsBitbucket + (*JoinAttrsTerraformCloud)(nil), // 9: teleport.workloadidentity.v1.JoinAttrsTerraformCloud + (*JoinAttrsSpacelift)(nil), // 10: teleport.workloadidentity.v1.JoinAttrsSpacelift + (*JoinAttrsGCPGCE)(nil), // 11: teleport.workloadidentity.v1.JoinAttrsGCPGCE + (*JoinAttrsGCP)(nil), // 12: teleport.workloadidentity.v1.JoinAttrsGCP + (*JoinAttrsKubernetesPod)(nil), // 13: teleport.workloadidentity.v1.JoinAttrsKubernetesPod + (*JoinAttrsKubernetesServiceAccount)(nil), // 14: teleport.workloadidentity.v1.JoinAttrsKubernetesServiceAccount + (*JoinAttrsKubernetes)(nil), // 15: teleport.workloadidentity.v1.JoinAttrsKubernetes +} +var file_teleport_workloadidentity_v1_join_attrs_proto_depIdxs = []int32{ + 1, // 0: teleport.workloadidentity.v1.JoinAttrs.meta:type_name -> teleport.workloadidentity.v1.JoinAttrsMeta + 2, // 1: teleport.workloadidentity.v1.JoinAttrs.gitlab:type_name -> teleport.workloadidentity.v1.JoinAttrsGitLab + 3, // 2: teleport.workloadidentity.v1.JoinAttrs.github:type_name -> teleport.workloadidentity.v1.JoinAttrsGitHub + 4, // 3: teleport.workloadidentity.v1.JoinAttrs.iam:type_name -> teleport.workloadidentity.v1.JoinAttrsAWSIAM + 5, // 4: teleport.workloadidentity.v1.JoinAttrs.tpm:type_name -> teleport.workloadidentity.v1.JoinAttrsTPM + 6, // 5: teleport.workloadidentity.v1.JoinAttrs.azure:type_name -> teleport.workloadidentity.v1.JoinAttrsAzure + 7, // 6: teleport.workloadidentity.v1.JoinAttrs.circleci:type_name -> teleport.workloadidentity.v1.JoinAttrsCircleCI + 8, // 7: teleport.workloadidentity.v1.JoinAttrs.bitbucket:type_name -> teleport.workloadidentity.v1.JoinAttrsBitbucket + 9, // 8: teleport.workloadidentity.v1.JoinAttrs.terraform_cloud:type_name -> teleport.workloadidentity.v1.JoinAttrsTerraformCloud + 10, // 9: teleport.workloadidentity.v1.JoinAttrs.spacelift:type_name -> teleport.workloadidentity.v1.JoinAttrsSpacelift + 12, // 10: teleport.workloadidentity.v1.JoinAttrs.gcp:type_name -> teleport.workloadidentity.v1.JoinAttrsGCP + 15, // 11: teleport.workloadidentity.v1.JoinAttrs.kubernetes:type_name -> teleport.workloadidentity.v1.JoinAttrsKubernetes + 11, // 12: teleport.workloadidentity.v1.JoinAttrsGCP.gce:type_name -> teleport.workloadidentity.v1.JoinAttrsGCPGCE + 14, // 13: teleport.workloadidentity.v1.JoinAttrsKubernetes.service_account:type_name -> teleport.workloadidentity.v1.JoinAttrsKubernetesServiceAccount + 13, // 14: teleport.workloadidentity.v1.JoinAttrsKubernetes.pod:type_name -> teleport.workloadidentity.v1.JoinAttrsKubernetesPod + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name +} + +func init() { file_teleport_workloadidentity_v1_join_attrs_proto_init() } +func file_teleport_workloadidentity_v1_join_attrs_proto_init() { + if File_teleport_workloadidentity_v1_join_attrs_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_teleport_workloadidentity_v1_join_attrs_proto_rawDesc, + NumEnums: 0, + NumMessages: 16, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_teleport_workloadidentity_v1_join_attrs_proto_goTypes, + DependencyIndexes: file_teleport_workloadidentity_v1_join_attrs_proto_depIdxs, + MessageInfos: file_teleport_workloadidentity_v1_join_attrs_proto_msgTypes, + }.Build() + File_teleport_workloadidentity_v1_join_attrs_proto = out.File + file_teleport_workloadidentity_v1_join_attrs_proto_rawDesc = nil + file_teleport_workloadidentity_v1_join_attrs_proto_goTypes = nil + file_teleport_workloadidentity_v1_join_attrs_proto_depIdxs = nil +} diff --git a/api/gen/proto/go/teleport/workloadidentity/v1/resource.pb.go b/api/gen/proto/go/teleport/workloadidentity/v1/resource.pb.go index 3b9ce3b695f5c..75a923c48c3fa 100644 --- a/api/gen/proto/go/teleport/workloadidentity/v1/resource.pb.go +++ b/api/gen/proto/go/teleport/workloadidentity/v1/resource.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/workloadidentity/v1/resource.proto @@ -270,6 +270,55 @@ func (x *WorkloadIdentityRules) GetAllow() []*WorkloadIdentityRule { return nil } +// Configuration specific to the issuance of X509-SVIDs. +type WorkloadIdentitySPIFFEX509 struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The DNS Subject Alternative Names (SANs) that should be included in an + // X509-SVID issued using this WorkloadIdentity. + // + // Each entry in this list supports templating using attributes. + DnsSans []string `protobuf:"bytes,1,rep,name=dns_sans,json=dnsSans,proto3" json:"dns_sans,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkloadIdentitySPIFFEX509) Reset() { + *x = WorkloadIdentitySPIFFEX509{} + mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkloadIdentitySPIFFEX509) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkloadIdentitySPIFFEX509) ProtoMessage() {} + +func (x *WorkloadIdentitySPIFFEX509) ProtoReflect() protoreflect.Message { + mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkloadIdentitySPIFFEX509.ProtoReflect.Descriptor instead. +func (*WorkloadIdentitySPIFFEX509) Descriptor() ([]byte, []int) { + return file_teleport_workloadidentity_v1_resource_proto_rawDescGZIP(), []int{4} +} + +func (x *WorkloadIdentitySPIFFEX509) GetDnsSans() []string { + if x != nil { + return x.DnsSans + } + return nil +} + // Configuration pertaining to the issuance of SPIFFE-compatible workload // identity credentials. type WorkloadIdentitySPIFFE struct { @@ -283,14 +332,16 @@ type WorkloadIdentitySPIFFE struct { // A freeform text field which is provided to workloads along with a // credential produced by this WorkloadIdentity. This can be used to provide // additional context that can be used to select between multiple credentials. - Hint string `protobuf:"bytes,2,opt,name=hint,proto3" json:"hint,omitempty"` + Hint string `protobuf:"bytes,2,opt,name=hint,proto3" json:"hint,omitempty"` + // Configuration specific to X509-SVIDs. + X509 *WorkloadIdentitySPIFFEX509 `protobuf:"bytes,3,opt,name=x509,proto3" json:"x509,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WorkloadIdentitySPIFFE) Reset() { *x = WorkloadIdentitySPIFFE{} - mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[4] + mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -302,7 +353,7 @@ func (x *WorkloadIdentitySPIFFE) String() string { func (*WorkloadIdentitySPIFFE) ProtoMessage() {} func (x *WorkloadIdentitySPIFFE) ProtoReflect() protoreflect.Message { - mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[4] + mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -315,7 +366,7 @@ func (x *WorkloadIdentitySPIFFE) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkloadIdentitySPIFFE.ProtoReflect.Descriptor instead. func (*WorkloadIdentitySPIFFE) Descriptor() ([]byte, []int) { - return file_teleport_workloadidentity_v1_resource_proto_rawDescGZIP(), []int{4} + return file_teleport_workloadidentity_v1_resource_proto_rawDescGZIP(), []int{5} } func (x *WorkloadIdentitySPIFFE) GetId() string { @@ -332,6 +383,13 @@ func (x *WorkloadIdentitySPIFFE) GetHint() string { return "" } +func (x *WorkloadIdentitySPIFFE) GetX509() *WorkloadIdentitySPIFFEX509 { + if x != nil { + return x.X509 + } + return nil +} + // The spec for the WorkloadIdentity resource. type WorkloadIdentitySpec struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -346,7 +404,7 @@ type WorkloadIdentitySpec struct { func (x *WorkloadIdentitySpec) Reset() { *x = WorkloadIdentitySpec{} - mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[5] + mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -358,7 +416,7 @@ func (x *WorkloadIdentitySpec) String() string { func (*WorkloadIdentitySpec) ProtoMessage() {} func (x *WorkloadIdentitySpec) ProtoReflect() protoreflect.Message { - mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[5] + mi := &file_teleport_workloadidentity_v1_resource_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -371,7 +429,7 @@ func (x *WorkloadIdentitySpec) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkloadIdentitySpec.ProtoReflect.Descriptor instead. func (*WorkloadIdentitySpec) Descriptor() ([]byte, []int) { - return file_teleport_workloadidentity_v1_resource_proto_rawDescGZIP(), []int{5} + return file_teleport_workloadidentity_v1_resource_proto_rawDescGZIP(), []int{6} } func (x *WorkloadIdentitySpec) GetRules() *WorkloadIdentityRules { @@ -430,29 +488,37 @@ var file_teleport_workloadidentity_v1_resource_proto_rawDesc = []byte{ 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x22, 0x3c, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x50, 0x49, 0x46, 0x46, 0x45, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, - 0x69, 0x6e, 0x74, 0x22, 0xaf, 0x01, 0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x70, 0x65, 0x63, 0x12, 0x49, 0x0a, 0x05, - 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x22, 0x37, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x50, 0x49, 0x46, 0x46, 0x45, 0x58, + 0x35, 0x30, 0x39, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6e, 0x73, 0x53, 0x61, 0x6e, 0x73, 0x22, 0x8a, + 0x01, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x53, 0x50, 0x49, 0x46, 0x46, 0x45, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x12, 0x4c, 0x0a, + 0x04, 0x78, 0x35, 0x30, 0x39, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, - 0x6f, 0x61, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, - 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x06, 0x73, 0x70, 0x69, 0x66, 0x66, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x50, 0x49, 0x46, 0x46, 0x45, 0x52, 0x06, 0x73, - 0x70, 0x69, 0x66, 0x66, 0x65, 0x42, 0x64, 0x5a, 0x62, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, - 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, 0x31, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, - 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6f, 0x61, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x50, 0x49, 0x46, 0x46, + 0x45, 0x58, 0x35, 0x30, 0x39, 0x52, 0x04, 0x78, 0x35, 0x30, 0x39, 0x22, 0xaf, 0x01, 0x0a, 0x14, + 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x49, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, + 0x4c, 0x0a, 0x06, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, + 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, + 0x50, 0x49, 0x46, 0x46, 0x45, 0x52, 0x06, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x42, 0x64, 0x5a, + 0x62, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x77, 0x6f, 0x72, + 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x76, 0x31, + 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -467,28 +533,30 @@ func file_teleport_workloadidentity_v1_resource_proto_rawDescGZIP() []byte { return file_teleport_workloadidentity_v1_resource_proto_rawDescData } -var file_teleport_workloadidentity_v1_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_teleport_workloadidentity_v1_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_teleport_workloadidentity_v1_resource_proto_goTypes = []any{ - (*WorkloadIdentity)(nil), // 0: teleport.workloadidentity.v1.WorkloadIdentity - (*WorkloadIdentityCondition)(nil), // 1: teleport.workloadidentity.v1.WorkloadIdentityCondition - (*WorkloadIdentityRule)(nil), // 2: teleport.workloadidentity.v1.WorkloadIdentityRule - (*WorkloadIdentityRules)(nil), // 3: teleport.workloadidentity.v1.WorkloadIdentityRules - (*WorkloadIdentitySPIFFE)(nil), // 4: teleport.workloadidentity.v1.WorkloadIdentitySPIFFE - (*WorkloadIdentitySpec)(nil), // 5: teleport.workloadidentity.v1.WorkloadIdentitySpec - (*v1.Metadata)(nil), // 6: teleport.header.v1.Metadata + (*WorkloadIdentity)(nil), // 0: teleport.workloadidentity.v1.WorkloadIdentity + (*WorkloadIdentityCondition)(nil), // 1: teleport.workloadidentity.v1.WorkloadIdentityCondition + (*WorkloadIdentityRule)(nil), // 2: teleport.workloadidentity.v1.WorkloadIdentityRule + (*WorkloadIdentityRules)(nil), // 3: teleport.workloadidentity.v1.WorkloadIdentityRules + (*WorkloadIdentitySPIFFEX509)(nil), // 4: teleport.workloadidentity.v1.WorkloadIdentitySPIFFEX509 + (*WorkloadIdentitySPIFFE)(nil), // 5: teleport.workloadidentity.v1.WorkloadIdentitySPIFFE + (*WorkloadIdentitySpec)(nil), // 6: teleport.workloadidentity.v1.WorkloadIdentitySpec + (*v1.Metadata)(nil), // 7: teleport.header.v1.Metadata } var file_teleport_workloadidentity_v1_resource_proto_depIdxs = []int32{ - 6, // 0: teleport.workloadidentity.v1.WorkloadIdentity.metadata:type_name -> teleport.header.v1.Metadata - 5, // 1: teleport.workloadidentity.v1.WorkloadIdentity.spec:type_name -> teleport.workloadidentity.v1.WorkloadIdentitySpec + 7, // 0: teleport.workloadidentity.v1.WorkloadIdentity.metadata:type_name -> teleport.header.v1.Metadata + 6, // 1: teleport.workloadidentity.v1.WorkloadIdentity.spec:type_name -> teleport.workloadidentity.v1.WorkloadIdentitySpec 1, // 2: teleport.workloadidentity.v1.WorkloadIdentityRule.conditions:type_name -> teleport.workloadidentity.v1.WorkloadIdentityCondition 2, // 3: teleport.workloadidentity.v1.WorkloadIdentityRules.allow:type_name -> teleport.workloadidentity.v1.WorkloadIdentityRule - 3, // 4: teleport.workloadidentity.v1.WorkloadIdentitySpec.rules:type_name -> teleport.workloadidentity.v1.WorkloadIdentityRules - 4, // 5: teleport.workloadidentity.v1.WorkloadIdentitySpec.spiffe:type_name -> teleport.workloadidentity.v1.WorkloadIdentitySPIFFE - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 4, // 4: teleport.workloadidentity.v1.WorkloadIdentitySPIFFE.x509:type_name -> teleport.workloadidentity.v1.WorkloadIdentitySPIFFEX509 + 3, // 5: teleport.workloadidentity.v1.WorkloadIdentitySpec.rules:type_name -> teleport.workloadidentity.v1.WorkloadIdentityRules + 5, // 6: teleport.workloadidentity.v1.WorkloadIdentitySpec.spiffe:type_name -> teleport.workloadidentity.v1.WorkloadIdentitySPIFFE + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_teleport_workloadidentity_v1_resource_proto_init() } @@ -502,7 +570,7 @@ func file_teleport_workloadidentity_v1_resource_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_workloadidentity_v1_resource_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/api/gen/proto/go/teleport/workloadidentity/v1/resource_service.pb.go b/api/gen/proto/go/teleport/workloadidentity/v1/resource_service.pb.go index 0c2ca023db938..0a32a67a1f38f 100644 --- a/api/gen/proto/go/teleport/workloadidentity/v1/resource_service.pb.go +++ b/api/gen/proto/go/teleport/workloadidentity/v1/resource_service.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/workloadidentity/v1/resource_service.proto diff --git a/api/gen/proto/go/userpreferences/v1/access_graph.pb.go b/api/gen/proto/go/userpreferences/v1/access_graph.pb.go index dc4878ae0a10d..da763ea0f2e94 100644 --- a/api/gen/proto/go/userpreferences/v1/access_graph.pb.go +++ b/api/gen/proto/go/userpreferences/v1/access_graph.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/access_graph.proto diff --git a/api/gen/proto/go/userpreferences/v1/assist.pb.go b/api/gen/proto/go/userpreferences/v1/assist.pb.go index b84132e2ac5c9..e757960c0ed10 100644 --- a/api/gen/proto/go/userpreferences/v1/assist.pb.go +++ b/api/gen/proto/go/userpreferences/v1/assist.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/assist.proto diff --git a/api/gen/proto/go/userpreferences/v1/cluster_preferences.pb.go b/api/gen/proto/go/userpreferences/v1/cluster_preferences.pb.go index 466fafb8a6c9f..c77a39093f94d 100644 --- a/api/gen/proto/go/userpreferences/v1/cluster_preferences.pb.go +++ b/api/gen/proto/go/userpreferences/v1/cluster_preferences.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/cluster_preferences.proto diff --git a/api/gen/proto/go/userpreferences/v1/onboard.pb.go b/api/gen/proto/go/userpreferences/v1/onboard.pb.go index 2212834896d79..4088c0ce805de 100644 --- a/api/gen/proto/go/userpreferences/v1/onboard.pb.go +++ b/api/gen/proto/go/userpreferences/v1/onboard.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/onboard.proto diff --git a/api/gen/proto/go/userpreferences/v1/sidenav_preferences.pb.go b/api/gen/proto/go/userpreferences/v1/sidenav_preferences.pb.go index 1e3329f173011..4ee547cce5245 100644 --- a/api/gen/proto/go/userpreferences/v1/sidenav_preferences.pb.go +++ b/api/gen/proto/go/userpreferences/v1/sidenav_preferences.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/sidenav_preferences.proto diff --git a/api/gen/proto/go/userpreferences/v1/theme.pb.go b/api/gen/proto/go/userpreferences/v1/theme.pb.go index 7d6a579015f19..62e274bbe9d62 100644 --- a/api/gen/proto/go/userpreferences/v1/theme.pb.go +++ b/api/gen/proto/go/userpreferences/v1/theme.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/theme.proto diff --git a/api/gen/proto/go/userpreferences/v1/unified_resource_preferences.pb.go b/api/gen/proto/go/userpreferences/v1/unified_resource_preferences.pb.go index 6c8dae4a985d6..0806e1631a803 100644 --- a/api/gen/proto/go/userpreferences/v1/unified_resource_preferences.pb.go +++ b/api/gen/proto/go/userpreferences/v1/unified_resource_preferences.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/unified_resource_preferences.proto diff --git a/api/gen/proto/go/userpreferences/v1/userpreferences.pb.go b/api/gen/proto/go/userpreferences/v1/userpreferences.pb.go index c14a0344a16b2..c4280fee817c7 100644 --- a/api/gen/proto/go/userpreferences/v1/userpreferences.pb.go +++ b/api/gen/proto/go/userpreferences/v1/userpreferences.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/userpreferences/v1/userpreferences.proto diff --git a/api/go.mod b/api/go.mod index e01ed6bbbfcd6..9903d99ab6692 100644 --- a/api/go.mod +++ b/api/go.mod @@ -27,8 +27,8 @@ require ( golang.org/x/net v0.33.0 golang.org/x/term v0.27.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 - google.golang.org/grpc v1.68.0 - google.golang.org/protobuf v1.36.0 + google.golang.org/grpc v1.69.2 + google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/api/go.sum b/api/go.sum index 4c35d634358ec..19daf5f2230ad 100644 --- a/api/go.sum +++ b/api/go.sum @@ -919,6 +919,8 @@ go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZk go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -1545,8 +1547,8 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1565,8 +1567,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/api/proto/teleport/autoupdate/v1/autoupdate.proto b/api/proto/teleport/autoupdate/v1/autoupdate.proto index faf7ec93db129..73f6d440f998e 100644 --- a/api/proto/teleport/autoupdate/v1/autoupdate.proto +++ b/api/proto/teleport/autoupdate/v1/autoupdate.proto @@ -170,6 +170,7 @@ message AutoUpdateAgentRolloutSpec { } // AutoUpdateAgentRolloutStatus tracks the current agent rollout status. +// The status is reset if any spec field changes except the mode. message AutoUpdateAgentRolloutStatus { repeated AutoUpdateAgentRolloutStatusGroup groups = 1; AutoUpdateAgentRolloutState state = 2; @@ -182,6 +183,11 @@ message AutoUpdateAgentRolloutStatus { // before the rollout start time and the maintenance window belongs to the previous rollout. // When the timestamp is nil, the controller will ignore the start time and check and allow groups to activate. google.protobuf.Timestamp start_time = 3; + + // Time override is an optional timestamp making the autoupdate_agent_rollout controller use a specific time instead + // of the system clock when evaluating time-based criteria. This field is used for testing and troubleshooting + // purposes. + google.protobuf.Timestamp time_override = 4; } // AutoUpdateAgentRolloutStatusGroup tracks the current agent rollout status of a specific group. diff --git a/api/proto/teleport/decision/v1alpha1/database_access.proto b/api/proto/teleport/decision/v1alpha1/database_access.proto index 652df267ef33c..cd852a45d0b6d 100644 --- a/api/proto/teleport/decision/v1alpha1/database_access.proto +++ b/api/proto/teleport/decision/v1alpha1/database_access.proto @@ -19,6 +19,7 @@ package teleport.decision.v1alpha1; import "teleport/decision/v1alpha1/denial_metadata.proto"; import "teleport/decision/v1alpha1/permit_metadata.proto"; import "teleport/decision/v1alpha1/request_metadata.proto"; +import "teleport/decision/v1alpha1/resource.proto"; import "teleport/decision/v1alpha1/tls_identity.proto"; option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1;decisionpb"; @@ -28,6 +29,7 @@ option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport message EvaluateDatabaseAccessRequest { RequestMetadata metadata = 1; TLSIdentity tls_identity = 2; + Resource database = 3; } // EvaluateDatabaseAccessResponse describes the result of a database access diff --git a/api/proto/teleport/legacy/client/proto/authservice.proto b/api/proto/teleport/legacy/client/proto/authservice.proto index d5852268b27e2..fc6dc146ff248 100644 --- a/api/proto/teleport/legacy/client/proto/authservice.proto +++ b/api/proto/teleport/legacy/client/proto/authservice.proto @@ -3308,7 +3308,11 @@ service AuthService { // GetTrustedClusters gets all current Trusted Cluster resources. rpc GetTrustedClusters(google.protobuf.Empty) returns (types.TrustedClusterV2List); // UpsertTrustedCluster upserts a Trusted Cluster in a backend. - rpc UpsertTrustedCluster(types.TrustedClusterV2) returns (types.TrustedClusterV2); + // + // Deprecated: Use [teleport.trust.v1.UpsertTrustedCluster] instead. + rpc UpsertTrustedCluster(types.TrustedClusterV2) returns (types.TrustedClusterV2) { + option deprecated = true; + } // DeleteTrustedCluster deletes an existing Trusted Cluster in a backend by name. rpc DeleteTrustedCluster(types.ResourceRequest) returns (google.protobuf.Empty); diff --git a/api/proto/teleport/legacy/types/events/events.proto b/api/proto/teleport/legacy/types/events/events.proto index bd1b79a5fba17..c82a6e6976e0b 100644 --- a/api/proto/teleport/legacy/types/events/events.proto +++ b/api/proto/teleport/legacy/types/events/events.proto @@ -1547,6 +1547,33 @@ message AccessRequestCreate { ]; } +// AccessRequestExpire is emitted when access request has expired. +message AccessRequestExpire { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // RequestID is access request ID + string RequestID = 3 [(gogoproto.jsontag) = "id"]; + + // ResourceExpiry is the time at which the access request resource will expire. + google.protobuf.Timestamp ResourceExpiry = 4 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = true, + (gogoproto.jsontag) = "expiry,omitempty" + ]; +} + // ResourceID is a unique identifier for a teleport resource. This is duplicated // from api/types/types.proto to decouple the api and events types and because // neither file currently imports the other. @@ -3123,6 +3150,12 @@ message DatabaseSessionStart { // connection. This can be useful for backend process cancellation or // termination and it is not a sensitive or secret value. uint32 PostgresPID = 8 [(gogoproto.jsontag) = "postgres_pid,omitempty"]; + // Client is the common client event metadata. + ClientMetadata Client = 9 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // DatabaseSessionQuery is emitted when a user executes a database query. @@ -4709,6 +4742,7 @@ message OneOf { events.WorkloadIdentityDelete WorkloadIdentityDelete = 196; events.GitCommand GitCommand = 197; events.UserLoginAccessListInvalid UserLoginAccessListInvalid = 198; + events.AccessRequestExpire AccessRequestExpire = 199; } } diff --git a/api/proto/teleport/legacy/types/types.proto b/api/proto/teleport/legacy/types/types.proto index 3d4081073e01e..92394397d1bf6 100644 --- a/api/proto/teleport/legacy/types/types.proto +++ b/api/proto/teleport/legacy/types/types.proto @@ -2134,7 +2134,9 @@ message AuthPreferenceSpecV2 { string Type = 1 [(gogoproto.jsontag) = "type"]; // SecondFactor is the type of mult-factor. + // Deprecated: Prefer using SecondFactors instead. string SecondFactor = 2 [ + deprecated = true, (gogoproto.jsontag) = "second_factor,omitempty", (gogoproto.casttype) = "github.com/gravitational/teleport/api/constants.SecondFactorType" ]; @@ -2233,7 +2235,9 @@ message AuthPreferenceSpecV2 { // 1 is "legacy", 2 is "balanced-v1", 3 is "fips-v1", 4 is "hsm-v1". SignatureAlgorithmSuite signature_algorithm_suite = 20; - // SecondFactors is a list of supported second factor types. + // SecondFactors is a list of supported multi-factor types. + // 1 is "otp", 2 is "webauthn", 3 is "sso", + // If unspecified, the current default value is [1], or ["otp"]. repeated SecondFactorType SecondFactors = 21 [(gogoproto.jsontag) = "second_factors,omitempty"]; } @@ -2729,6 +2733,13 @@ message AccessRequestSpecV3 { (gogoproto.nullable) = true, (gogoproto.jsontag) = "assume_start_time,omitempty" ]; + + // ResourceExpiry is the time at which the access request resource will expire. + google.protobuf.Timestamp ResourceExpiry = 22 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = true, + (gogoproto.jsontag) = "expiry,omitempty" + ]; } enum AccessRequestScope { diff --git a/api/proto/teleport/machineid/v1/bot_instance.proto b/api/proto/teleport/machineid/v1/bot_instance.proto index 6ff4719ed15f8..5904e8896a6bd 100644 --- a/api/proto/teleport/machineid/v1/bot_instance.proto +++ b/api/proto/teleport/machineid/v1/bot_instance.proto @@ -20,6 +20,7 @@ import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "teleport/header/v1/metadata.proto"; +import "teleport/workloadidentity/v1/join_attrs.proto"; option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1;machineidv1"; @@ -108,6 +109,11 @@ message BotInstanceStatusAuthentication { reserved 7; reserved "fingerprint"; + + // The attributes generated during the join process. Typically, this is + // information from the join attestation process itself. This field will + // eventually replace the `metadata` field, which is structureless. + teleport.workloadidentity.v1.JoinAttrs join_attrs = 8; } // BotInstanceStatus holds the status of a BotInstance. diff --git a/api/proto/teleport/trust/v1/trust_service.proto b/api/proto/teleport/trust/v1/trust_service.proto index a6b3fc3282c44..7d8748f2375f7 100644 --- a/api/proto/teleport/trust/v1/trust_service.proto +++ b/api/proto/teleport/trust/v1/trust_service.proto @@ -40,6 +40,31 @@ service TrustService { // GenerateHostCert takes a public key in the OpenSSH `authorized_keys` format and returns // a SSH certificate signed by the Host CA. rpc GenerateHostCert(GenerateHostCertRequest) returns (GenerateHostCertResponse); + + // UpsertTrustedCluster upserts a Trusted Cluster in a backend. + rpc UpsertTrustedCluster(UpsertTrustedClusterRequest) returns (types.TrustedClusterV2); + // CreateTrustedCluster creates a Trusted Cluster in a backend. + rpc CreateTrustedCluster(CreateTrustedClusterRequest) returns (types.TrustedClusterV2); + // UpdateTrustedCluster updates a Trusted Cluster in a backend. + rpc UpdateTrustedCluster(UpdateTrustedClusterRequest) returns (types.TrustedClusterV2); +} + +// Request for UpsertTrustedCluster. +message UpsertTrustedClusterRequest { + // TrustedCluster specifies a Trusted Cluster resource. + types.TrustedClusterV2 trusted_cluster = 1; +} + +// Request for CreateTrustedCluster. +message CreateTrustedClusterRequest { + // TrustedCluster specifies a Trusted Cluster resource. + types.TrustedClusterV2 trusted_cluster = 1; +} + +// Request for UpdateTrustedCluster. +message UpdateTrustedClusterRequest { + // TrustedCluster specifies a Trusted Cluster resource. + types.TrustedClusterV2 trusted_cluster = 1; } // Request for GetCertAuthority diff --git a/api/proto/teleport/workloadidentity/v1/attrs.proto b/api/proto/teleport/workloadidentity/v1/attrs.proto index 3bcdd27ed2517..1a3b0099bac88 100644 --- a/api/proto/teleport/workloadidentity/v1/attrs.proto +++ b/api/proto/teleport/workloadidentity/v1/attrs.proto @@ -16,6 +16,8 @@ syntax = "proto3"; package teleport.workloadidentity.v1; +import "teleport/workloadidentity/v1/join_attrs.proto"; + option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1;workloadidentityv1"; // Attributes sourced from the Kubernetes workload attestor. @@ -80,4 +82,7 @@ message Attrs { // Attributes sourced from the user/bot making the request for a workload // identity credential. UserAttrs user = 2; + // Attributes sourced from the join process that the Bot underwent. This will + // be unset if the principal making the request is not a Bot. + JoinAttrs join = 3; } diff --git a/api/proto/teleport/workloadidentity/v1/join_attrs.proto b/api/proto/teleport/workloadidentity/v1/join_attrs.proto new file mode 100644 index 0000000000000..369bb46dd0336 --- /dev/null +++ b/api/proto/teleport/workloadidentity/v1/join_attrs.proto @@ -0,0 +1,312 @@ +// Copyright 2025 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package teleport.workloadidentity.v1; + +option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1;workloadidentityv1"; + +// The collection of attributes that result from the join process. +message JoinAttrs { + // The collection of attributes that result from the join process but are not + // specific to any particular join method. + JoinAttrsMeta meta = 1; + // Attributes that are specific to the GitLab (`gitlab`) join method. + JoinAttrsGitLab gitlab = 2; + // Attributes that are specific to the GitHub (`github`) join method. + JoinAttrsGitHub github = 3; + // Attributes that are specific to the AWS IAM (`iam`) join method. + JoinAttrsAWSIAM iam = 4; + // Attributes that are specific to the TPM (`tpm`) join method. + JoinAttrsTPM tpm = 5; + // Attributes that are specific to the Azure (`azure`) join method. + JoinAttrsAzure azure = 6; + // Attributes that are specific to the CircleCI (`circleci`) join method. + JoinAttrsCircleCI circleci = 7; + // Attributes that are specific to the Bitbucket (`bitbucket`) join method. + JoinAttrsBitbucket bitbucket = 8; + // Attributes that are specific to the Terraform Cloud (`terraform_cloud`) join method. + JoinAttrsTerraformCloud terraform_cloud = 9; + // Attributes that are specific to the Spacelift (`spacelift`) join method. + JoinAttrsSpacelift spacelift = 10; + // Attributes that are specific to the GCP (`gcp`) join method. + JoinAttrsGCP gcp = 11; + // Attributes that are specific to the Kubernetes (`kubernetes`) join method. + JoinAttrsKubernetes kubernetes = 12; +} + +// The collection of attributes that result from the join process but are not +// specific to any particular join method. +message JoinAttrsMeta { + // The name of the join token that was used to join. + // + // This field is omitted if the join token that was used to join was of the + // `token` method as in this case, the name of the join token is sensitive. + // + // Example: `my-gitlab-join-token` + string join_token_name = 1; + // The name of the join method that was used to join. + // + // Example: `gitlab` + string join_method = 2; +} + +// Attributes that are specific to the GitLab join method. +// +// Typically, these are mapped directly from the claims of the GitLab JWT that +// was used to join. You can view the documentation for those claims at: +// https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html#token-payload +message JoinAttrsGitLab { + // The `sub` claim of the GitLab JWT that was used to join. + // For example: `project_path:mygroup/my-project:ref_type:branch:ref:main` + string sub = 1; + // The ref that the pipeline is running against. + // For example: `main` + string ref = 2; + // The type of ref that the pipeline is running against. + // This is typically `branch` or `tag`. + string ref_type = 3; + // Whether or not the ref that the pipeline is running against is protected. + bool ref_protected = 4; + // The path of the namespace of the project that the pipeline is running within. + // For example: `mygroup` + string namespace_path = 5; + // The full qualified path of the project that the pipeline is running within. + // This includes the namespace path. + // For example: `mygroup/my-project` + string project_path = 6; + // The name of the user that triggered the pipeline run. + string user_login = 7; + // The email of the user that triggered the pipeline run. + string user_email = 8; + // The ID of the pipeline. + string pipeline_id = 9; + // The source of the pipeline. + // For example: `push` or `web` + string pipeline_source = 10; + // The environment the pipeline is running against, if any. + string environment = 11; + // Whether or not the pipeline is running against a protected environment. + // If there is no configured environment, this field is false. + bool environment_protected = 12; + // The ID of the runner that this pipeline is running on. + int64 runner_id = 13; + // The type of runner that is processing the pipeline. + // Either `gitlab-hosted` or `self-hosted`. + string runner_environment = 14; + // The SHA of the commit that triggered the pipeline run. + string sha = 15; + // The ref URI of the CI config configuring the pipeline. + string ci_config_ref_uri = 16; + // The Git SHA of the CI config ref configuring the pipeline. + string ci_config_sha = 17; +} + +// Attributes that are specific to the GitHub (`github`) join method. +// +// Typically, these are mapped directly from the claims of the GitHub JWT that +// was used to join. You can view the documentation for those claims at: +// https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#understanding-the-oidc-token +message JoinAttrsGitHub { + // The `sub` claim of the GitHub JWT that was used to join. + string sub = 1; + // The username of the actor that initiated the workflow run. + string actor = 2; + // The name of the environment that the workflow is running against, if any. + string environment = 3; + // The ref that the workflow is running against. + string ref = 4; + // The type of ref that the workflow is running against. + // For example, `branch`. + string ref_type = 5; + // The name of the repository that the workflow is running within. + string repository = 6; + // The name of the owner of the repository that the workflow is running within. + string repository_owner = 7; + // The name of the workflow that is running. + string workflow = 8; + // The name of the event that triggered the workflow run. + string event_name = 9; + // The SHA of the commit that triggered the workflow run. + string sha = 10; + // The ID of this GitHub actions workflow run. + string run_id = 11; +} + +// Attributes that are specific to the AWS IAM (`iam`) join method. +// +// Typically, these are mapped directly from the results of the +// STS GetCallerIdentity call that is made as part of the join process. +message JoinAttrsAWSIAM { + // The identifier of the account that the joining entity is a part of. + // For example: `123456789012` + string account = 1; + // The AWS ARN of the joining entity. + // For example: `arn:aws:sts::123456789012:assumed-role/my-role-name/my-role-session-name` + string arn = 2; +} + +// Attributes that are specific to the TPM (`tpm`) join method. +message JoinAttrsTPM { + // The SHA256 hash of the PKIX formatted EK public key, encoded in hex. + // This effectively identifies a specific TPM. + string ek_pub_hash = 1; + // The serial number of the EK certificate, if present. + string ek_cert_serial = 2; + // Whether or not the EK certificate was verified against a certificate + // authority. + bool ek_cert_verified = 3; +} + +// Attributes that are specific to the Azure (`azure`) join method. +message JoinAttrsAzure { + // The subscription ID of the Azure account that the joining entity is a part of. + string subscription = 1; + // The resource group of the Azure account that the joining entity is a part of. + string resource_group = 2; +} + +// Attributes that are specific to the CircleCI (`circleci`) join method. +// These are mapped from the claims of the JWT issued by CircleCI to runs, +// which is documented at: https://circleci.com/docs/openid-connect-tokens/ +message JoinAttrsCircleCI { + // The `sub` claim of the CircleCI JWT that was used to join. + // For example: `org/ORGANIZATION_ID/project/PROJECT_ID/user/USER_ID` + string sub = 1; + // The UUIDs of the contexts used in the job. + repeated string context_ids = 2; + // The UUID of the project in which the job is running. + string project_id = 3; +} + +// Attributes that are specific to the Bitbucket (`bitbucket`) join method. +// These are mapped from the claims of the JWT issued by BitBucket to runs, +// which is documented at: https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/ +message JoinAttrsBitbucket { + // The `sub` claim of the Bitbucket JWT that was used to join. + string sub = 1; + // The UUID of the pipeline step. + string step_uuid = 2; + // The UUID of the repository the pipeline step is running within. + string repository_uuid = 3; + // The UUID of the pipeline the step is running within. + string pipeline_uuid = 4; + // The UUID of the workspace the pipeline belongs to. + string workspace_uuid = 5; + // The UUID of the deployment environment the pipeline is running against. + string deployment_environment_uuid = 6; + // The name of the branch the pipeline is running against. + string branch_name = 7; +} + +// Attributes that are specific to the Terraform Cloud (`terraform_cloud`) join method. +// These are mapped from the claims of the JWT issued by Terraform Cloud to runs, +// which is documented at: https://developer.hashicorp.com/terraform/enterprise/workspaces/dynamic-provider-credentials/workload-identity-tokens +message JoinAttrsTerraformCloud { + // The `sub` claim of the Terraform Cloud JWT that was used to join. + string sub = 1; + // The name of the organization the project and workspace belong to. + string organization_name = 2; + // The name of the project the workspace belongs to. + string project_name = 3; + // The name of the workspace that the plan/apply is running within. + string workspace_name = 4; + // The fully qualified workspace path, including the organization and project + // name. + // For example: `organization::project::workspace:` + string full_workspace = 5; + // The ID of the run that is being executed. + string run_id = 6; + // The phase of the run that is being executed, either `plan` or `apply`. + string run_phase = 7; +} + +// Attributes that are specific to the Spacelift (`spacelift`) join method. +// These are mapped from the claims of the JWT issued by Spacelift to runs, +// which is documented at: https://docs.spacelift.io/integrations/cloud-providers/oidc/#standard-claims +message JoinAttrsSpacelift { + // The `sub` claim of the Spacelift JWT that was used to join. + string sub = 1; + // The ID of the space in which the run is executing. + string space_id = 2; + // The type of the caller that owns the run, either `stack` or `module`. + string caller_type = 3; + // The ID of the caller that generated the run. + string caller_id = 4; + // The type of the run, either `PROPOSED`, `TRACKED`, `TASK`, `TESTING` or `DESTROY`. + string run_type = 5; + // The ID of the run. + string run_id = 6; + // The configured scope of the token, either `read` or `write`. + string scope = 7; +} + +// Attributes specific to the GCP join method when the joining entity is on a +// GCE instance. +message JoinAttrsGCPGCE { + // The name of the GCE instance that the joining entity is running on. + string name = 1; + // The zone of the GCE instance that the joining entity is running on. + string zone = 2; + // The ID of the GCE instance that the joining entity is running on. + string id = 3; + // The project ID of the GCP project that the instance is running within. + string project = 4; +} + +// Attributes that are specific to the GCP (`gcp`) join method. +// These are mapped from the claims of the JWT instance identity token, which +// is documented at: https://cloud.google.com/compute/docs/instances/verifying-instance-identity#payload +message JoinAttrsGCP { + // The service account email of the service account that the instance is running as. + string service_account = 1; + // Attributes specific to the GCP join method when the joining entity is on a + // GCE instance. This may not be present if the joining entity is not on + // GCE. + JoinAttrsGCPGCE gce = 2; +} + +// Attributes that are specific to the Kubernetes (`kubernetes`) join method +// when a pod-bound service account token is used. +message JoinAttrsKubernetesPod { + // The name of the service account that the joining entity is running as. + string name = 1; +} + +// Attributes that are specific to the Kubernetes (`kubernetes`) join method +// when a service account token is used. +message JoinAttrsKubernetesServiceAccount { + // The name of the service account that the joining entity is running as. + string name = 1; + // The namespace of the service account that the joining entity is running as. + string namespace = 2; +} + +// Attributes that are specific to the Kubernetes (`kubernetes`) join method. +message JoinAttrsKubernetes { + // The fully qualified identifier of the entity based on the Kubernetes + // token. For a service account, this takes the form of + // `system:serviceaccount::`. + string subject = 1; + // Attributes specific to the Kubernetes join method when the joining entity + // is a service account token. This will only be present if the joining entity + // is a service account (as opposed to a human user or similar). + JoinAttrsKubernetesServiceAccount service_account = 2; + // Attributes specific to the Kubernetes join method when the joining entity + // is a pod-bound service account token. This will only be present if the + // joining entity is a service account, and, the token has been bound to a + // pod. + JoinAttrsKubernetesPod pod = 3; +} diff --git a/api/proto/teleport/workloadidentity/v1/resource.proto b/api/proto/teleport/workloadidentity/v1/resource.proto index 69c7467417cb4..b0faf7f94b99e 100644 --- a/api/proto/teleport/workloadidentity/v1/resource.proto +++ b/api/proto/teleport/workloadidentity/v1/resource.proto @@ -60,6 +60,15 @@ message WorkloadIdentityRules { repeated WorkloadIdentityRule allow = 1; } +// Configuration specific to the issuance of X509-SVIDs. +message WorkloadIdentitySPIFFEX509 { + // The DNS Subject Alternative Names (SANs) that should be included in an + // X509-SVID issued using this WorkloadIdentity. + // + // Each entry in this list supports templating using attributes. + repeated string dns_sans = 1; +} + // Configuration pertaining to the issuance of SPIFFE-compatible workload // identity credentials. message WorkloadIdentitySPIFFE { @@ -73,6 +82,8 @@ message WorkloadIdentitySPIFFE { // credential produced by this WorkloadIdentity. This can be used to provide // additional context that can be used to select between multiple credentials. string hint = 2; + // Configuration specific to X509-SVIDs. + WorkloadIdentitySPIFFEX509 x509 = 3; } // The spec for the WorkloadIdentity resource. diff --git a/api/types/access_request.go b/api/types/access_request.go index 3854e4270e1f7..56b7446dc6e45 100644 --- a/api/types/access_request.go +++ b/api/types/access_request.go @@ -445,12 +445,17 @@ func (r *AccessRequestV3) SetName(name string) { // Expiry gets Expiry func (r *AccessRequestV3) Expiry() time.Time { + // Fallback on existing expiry in metadata if not set in spec. + if r.Spec.ResourceExpiry != nil { + return *r.Spec.ResourceExpiry + } return r.Metadata.Expiry() } // SetExpiry sets Expiry func (r *AccessRequestV3) SetExpiry(expiry time.Time) { - r.Metadata.SetExpiry(expiry.UTC()) + t := expiry.UTC() + r.Spec.ResourceExpiry = &t } // GetMetadata gets Metadata diff --git a/api/types/events/events.go b/api/types/events/events.go index d7ca44bf972ea..dea112a392f95 100644 --- a/api/types/events/events.go +++ b/api/types/events/events.go @@ -503,6 +503,10 @@ func (m *AccessRequestCreate) TrimToMaxSize(maxSize int) AuditEvent { return out } +func (m *AccessRequestExpire) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + func (m *AccessRequestResourceSearch) TrimToMaxSize(maxSize int) AuditEvent { size := m.Size() if size <= maxSize { diff --git a/api/types/events/events.pb.go b/api/types/events/events.pb.go index f6568e9a5373d..356494ebb66eb 100644 --- a/api/types/events/events.pb.go +++ b/api/types/events/events.pb.go @@ -2894,6 +2894,54 @@ func (m *AccessRequestCreate) XXX_DiscardUnknown() { var xxx_messageInfo_AccessRequestCreate proto.InternalMessageInfo +// AccessRequestExpire is emitted when access request has expired. +type AccessRequestExpire struct { + // Metadata is a common event metadata + Metadata `protobuf:"bytes,1,opt,name=Metadata,proto3,embedded=Metadata" json:""` + // ResourceMetadata is a common resource event metadata + ResourceMetadata `protobuf:"bytes,2,opt,name=Resource,proto3,embedded=Resource" json:""` + // RequestID is access request ID + RequestID string `protobuf:"bytes,3,opt,name=RequestID,proto3" json:"id"` + // ResourceExpiry is the time at which the access request resource will expire. + ResourceExpiry *time.Time `protobuf:"bytes,4,opt,name=ResourceExpiry,proto3,stdtime" json:"expiry,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AccessRequestExpire) Reset() { *m = AccessRequestExpire{} } +func (m *AccessRequestExpire) String() string { return proto.CompactTextString(m) } +func (*AccessRequestExpire) ProtoMessage() {} +func (*AccessRequestExpire) Descriptor() ([]byte, []int) { + return fileDescriptor_007ba1c3d6266d56, []int{47} +} +func (m *AccessRequestExpire) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AccessRequestExpire) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AccessRequestExpire.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AccessRequestExpire) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccessRequestExpire.Merge(m, src) +} +func (m *AccessRequestExpire) XXX_Size() int { + return m.Size() +} +func (m *AccessRequestExpire) XXX_DiscardUnknown() { + xxx_messageInfo_AccessRequestExpire.DiscardUnknown(m) +} + +var xxx_messageInfo_AccessRequestExpire proto.InternalMessageInfo + // ResourceID is a unique identifier for a teleport resource. This is duplicated // from api/types/types.proto to decouple the api and events types and because // neither file currently imports the other. @@ -2919,7 +2967,7 @@ func (m *ResourceID) Reset() { *m = ResourceID{} } func (m *ResourceID) String() string { return proto.CompactTextString(m) } func (*ResourceID) ProtoMessage() {} func (*ResourceID) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{47} + return fileDescriptor_007ba1c3d6266d56, []int{48} } func (m *ResourceID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2965,7 +3013,7 @@ func (m *AccessRequestDelete) Reset() { *m = AccessRequestDelete{} } func (m *AccessRequestDelete) String() string { return proto.CompactTextString(m) } func (*AccessRequestDelete) ProtoMessage() {} func (*AccessRequestDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{48} + return fileDescriptor_007ba1c3d6266d56, []int{49} } func (m *AccessRequestDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3020,7 +3068,7 @@ func (m *PortForward) Reset() { *m = PortForward{} } func (m *PortForward) String() string { return proto.CompactTextString(m) } func (*PortForward) ProtoMessage() {} func (*PortForward) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{49} + return fileDescriptor_007ba1c3d6266d56, []int{50} } func (m *PortForward) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3068,7 +3116,7 @@ func (m *X11Forward) Reset() { *m = X11Forward{} } func (m *X11Forward) String() string { return proto.CompactTextString(m) } func (*X11Forward) ProtoMessage() {} func (*X11Forward) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{50} + return fileDescriptor_007ba1c3d6266d56, []int{51} } func (m *X11Forward) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3114,7 +3162,7 @@ func (m *CommandMetadata) Reset() { *m = CommandMetadata{} } func (m *CommandMetadata) String() string { return proto.CompactTextString(m) } func (*CommandMetadata) ProtoMessage() {} func (*CommandMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{51} + return fileDescriptor_007ba1c3d6266d56, []int{52} } func (m *CommandMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3171,7 +3219,7 @@ func (m *Exec) Reset() { *m = Exec{} } func (m *Exec) String() string { return proto.CompactTextString(m) } func (*Exec) ProtoMessage() {} func (*Exec) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{52} + return fileDescriptor_007ba1c3d6266d56, []int{53} } func (m *Exec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3227,7 +3275,7 @@ func (m *SCP) Reset() { *m = SCP{} } func (m *SCP) String() string { return proto.CompactTextString(m) } func (*SCP) ProtoMessage() {} func (*SCP) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{53} + return fileDescriptor_007ba1c3d6266d56, []int{54} } func (m *SCP) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3279,7 +3327,7 @@ func (m *SFTPAttributes) Reset() { *m = SFTPAttributes{} } func (m *SFTPAttributes) String() string { return proto.CompactTextString(m) } func (*SFTPAttributes) ProtoMessage() {} func (*SFTPAttributes) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{54} + return fileDescriptor_007ba1c3d6266d56, []int{55} } func (m *SFTPAttributes) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3346,7 +3394,7 @@ func (m *SFTP) Reset() { *m = SFTP{} } func (m *SFTP) String() string { return proto.CompactTextString(m) } func (*SFTP) ProtoMessage() {} func (*SFTP) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{55} + return fileDescriptor_007ba1c3d6266d56, []int{56} } func (m *SFTP) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3398,7 +3446,7 @@ func (m *SFTPSummary) Reset() { *m = SFTPSummary{} } func (m *SFTPSummary) String() string { return proto.CompactTextString(m) } func (*SFTPSummary) ProtoMessage() {} func (*SFTPSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{56} + return fileDescriptor_007ba1c3d6266d56, []int{57} } func (m *SFTPSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3441,7 +3489,7 @@ func (m *FileTransferStat) Reset() { *m = FileTransferStat{} } func (m *FileTransferStat) String() string { return proto.CompactTextString(m) } func (*FileTransferStat) ProtoMessage() {} func (*FileTransferStat) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{57} + return fileDescriptor_007ba1c3d6266d56, []int{58} } func (m *FileTransferStat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3493,7 +3541,7 @@ func (m *Subsystem) Reset() { *m = Subsystem{} } func (m *Subsystem) String() string { return proto.CompactTextString(m) } func (*Subsystem) ProtoMessage() {} func (*Subsystem) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{58} + return fileDescriptor_007ba1c3d6266d56, []int{59} } func (m *Subsystem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3545,7 +3593,7 @@ func (m *ClientDisconnect) Reset() { *m = ClientDisconnect{} } func (m *ClientDisconnect) String() string { return proto.CompactTextString(m) } func (*ClientDisconnect) ProtoMessage() {} func (*ClientDisconnect) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{59} + return fileDescriptor_007ba1c3d6266d56, []int{60} } func (m *ClientDisconnect) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3593,7 +3641,7 @@ func (m *AuthAttempt) Reset() { *m = AuthAttempt{} } func (m *AuthAttempt) String() string { return proto.CompactTextString(m) } func (*AuthAttempt) ProtoMessage() {} func (*AuthAttempt) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{60} + return fileDescriptor_007ba1c3d6266d56, []int{61} } func (m *AuthAttempt) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3639,7 +3687,7 @@ func (m *UserTokenCreate) Reset() { *m = UserTokenCreate{} } func (m *UserTokenCreate) String() string { return proto.CompactTextString(m) } func (*UserTokenCreate) ProtoMessage() {} func (*UserTokenCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{61} + return fileDescriptor_007ba1c3d6266d56, []int{62} } func (m *UserTokenCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3687,7 +3735,7 @@ func (m *RoleCreate) Reset() { *m = RoleCreate{} } func (m *RoleCreate) String() string { return proto.CompactTextString(m) } func (*RoleCreate) ProtoMessage() {} func (*RoleCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{62} + return fileDescriptor_007ba1c3d6266d56, []int{63} } func (m *RoleCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3735,7 +3783,7 @@ func (m *RoleUpdate) Reset() { *m = RoleUpdate{} } func (m *RoleUpdate) String() string { return proto.CompactTextString(m) } func (*RoleUpdate) ProtoMessage() {} func (*RoleUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{63} + return fileDescriptor_007ba1c3d6266d56, []int{64} } func (m *RoleUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3783,7 +3831,7 @@ func (m *RoleDelete) Reset() { *m = RoleDelete{} } func (m *RoleDelete) String() string { return proto.CompactTextString(m) } func (*RoleDelete) ProtoMessage() {} func (*RoleDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{64} + return fileDescriptor_007ba1c3d6266d56, []int{65} } func (m *RoleDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3829,7 +3877,7 @@ func (m *BotCreate) Reset() { *m = BotCreate{} } func (m *BotCreate) String() string { return proto.CompactTextString(m) } func (*BotCreate) ProtoMessage() {} func (*BotCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{65} + return fileDescriptor_007ba1c3d6266d56, []int{66} } func (m *BotCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3875,7 +3923,7 @@ func (m *BotUpdate) Reset() { *m = BotUpdate{} } func (m *BotUpdate) String() string { return proto.CompactTextString(m) } func (*BotUpdate) ProtoMessage() {} func (*BotUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{66} + return fileDescriptor_007ba1c3d6266d56, []int{67} } func (m *BotUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3921,7 +3969,7 @@ func (m *BotDelete) Reset() { *m = BotDelete{} } func (m *BotDelete) String() string { return proto.CompactTextString(m) } func (*BotDelete) ProtoMessage() {} func (*BotDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{67} + return fileDescriptor_007ba1c3d6266d56, []int{68} } func (m *BotDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3969,7 +4017,7 @@ func (m *TrustedClusterCreate) Reset() { *m = TrustedClusterCreate{} } func (m *TrustedClusterCreate) String() string { return proto.CompactTextString(m) } func (*TrustedClusterCreate) ProtoMessage() {} func (*TrustedClusterCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{68} + return fileDescriptor_007ba1c3d6266d56, []int{69} } func (m *TrustedClusterCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4017,7 +4065,7 @@ func (m *TrustedClusterDelete) Reset() { *m = TrustedClusterDelete{} } func (m *TrustedClusterDelete) String() string { return proto.CompactTextString(m) } func (*TrustedClusterDelete) ProtoMessage() {} func (*TrustedClusterDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{69} + return fileDescriptor_007ba1c3d6266d56, []int{70} } func (m *TrustedClusterDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4063,7 +4111,7 @@ func (m *ProvisionTokenCreate) Reset() { *m = ProvisionTokenCreate{} } func (m *ProvisionTokenCreate) String() string { return proto.CompactTextString(m) } func (*ProvisionTokenCreate) ProtoMessage() {} func (*ProvisionTokenCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{70} + return fileDescriptor_007ba1c3d6266d56, []int{71} } func (m *ProvisionTokenCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4112,7 +4160,7 @@ func (m *TrustedClusterTokenCreate) Reset() { *m = TrustedClusterTokenCr func (m *TrustedClusterTokenCreate) String() string { return proto.CompactTextString(m) } func (*TrustedClusterTokenCreate) ProtoMessage() {} func (*TrustedClusterTokenCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{71} + return fileDescriptor_007ba1c3d6266d56, []int{72} } func (m *TrustedClusterTokenCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4160,7 +4208,7 @@ func (m *GithubConnectorCreate) Reset() { *m = GithubConnectorCreate{} } func (m *GithubConnectorCreate) String() string { return proto.CompactTextString(m) } func (*GithubConnectorCreate) ProtoMessage() {} func (*GithubConnectorCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{72} + return fileDescriptor_007ba1c3d6266d56, []int{73} } func (m *GithubConnectorCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4208,7 +4256,7 @@ func (m *GithubConnectorUpdate) Reset() { *m = GithubConnectorUpdate{} } func (m *GithubConnectorUpdate) String() string { return proto.CompactTextString(m) } func (*GithubConnectorUpdate) ProtoMessage() {} func (*GithubConnectorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{73} + return fileDescriptor_007ba1c3d6266d56, []int{74} } func (m *GithubConnectorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4256,7 +4304,7 @@ func (m *GithubConnectorDelete) Reset() { *m = GithubConnectorDelete{} } func (m *GithubConnectorDelete) String() string { return proto.CompactTextString(m) } func (*GithubConnectorDelete) ProtoMessage() {} func (*GithubConnectorDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{74} + return fileDescriptor_007ba1c3d6266d56, []int{75} } func (m *GithubConnectorDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4302,7 +4350,7 @@ func (m *OIDCConnectorCreate) Reset() { *m = OIDCConnectorCreate{} } func (m *OIDCConnectorCreate) String() string { return proto.CompactTextString(m) } func (*OIDCConnectorCreate) ProtoMessage() {} func (*OIDCConnectorCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{75} + return fileDescriptor_007ba1c3d6266d56, []int{76} } func (m *OIDCConnectorCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4348,7 +4396,7 @@ func (m *OIDCConnectorUpdate) Reset() { *m = OIDCConnectorUpdate{} } func (m *OIDCConnectorUpdate) String() string { return proto.CompactTextString(m) } func (*OIDCConnectorUpdate) ProtoMessage() {} func (*OIDCConnectorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{76} + return fileDescriptor_007ba1c3d6266d56, []int{77} } func (m *OIDCConnectorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4394,7 +4442,7 @@ func (m *OIDCConnectorDelete) Reset() { *m = OIDCConnectorDelete{} } func (m *OIDCConnectorDelete) String() string { return proto.CompactTextString(m) } func (*OIDCConnectorDelete) ProtoMessage() {} func (*OIDCConnectorDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{77} + return fileDescriptor_007ba1c3d6266d56, []int{78} } func (m *OIDCConnectorDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4442,7 +4490,7 @@ func (m *SAMLConnectorCreate) Reset() { *m = SAMLConnectorCreate{} } func (m *SAMLConnectorCreate) String() string { return proto.CompactTextString(m) } func (*SAMLConnectorCreate) ProtoMessage() {} func (*SAMLConnectorCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{78} + return fileDescriptor_007ba1c3d6266d56, []int{79} } func (m *SAMLConnectorCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4490,7 +4538,7 @@ func (m *SAMLConnectorUpdate) Reset() { *m = SAMLConnectorUpdate{} } func (m *SAMLConnectorUpdate) String() string { return proto.CompactTextString(m) } func (*SAMLConnectorUpdate) ProtoMessage() {} func (*SAMLConnectorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{79} + return fileDescriptor_007ba1c3d6266d56, []int{80} } func (m *SAMLConnectorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4536,7 +4584,7 @@ func (m *SAMLConnectorDelete) Reset() { *m = SAMLConnectorDelete{} } func (m *SAMLConnectorDelete) String() string { return proto.CompactTextString(m) } func (*SAMLConnectorDelete) ProtoMessage() {} func (*SAMLConnectorDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{80} + return fileDescriptor_007ba1c3d6266d56, []int{81} } func (m *SAMLConnectorDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4602,7 +4650,7 @@ func (m *KubeRequest) Reset() { *m = KubeRequest{} } func (m *KubeRequest) String() string { return proto.CompactTextString(m) } func (*KubeRequest) ProtoMessage() {} func (*KubeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{81} + return fileDescriptor_007ba1c3d6266d56, []int{82} } func (m *KubeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4653,7 +4701,7 @@ func (m *AppMetadata) Reset() { *m = AppMetadata{} } func (m *AppMetadata) String() string { return proto.CompactTextString(m) } func (*AppMetadata) ProtoMessage() {} func (*AppMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{82} + return fileDescriptor_007ba1c3d6266d56, []int{83} } func (m *AppMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4701,7 +4749,7 @@ func (m *AppCreate) Reset() { *m = AppCreate{} } func (m *AppCreate) String() string { return proto.CompactTextString(m) } func (*AppCreate) ProtoMessage() {} func (*AppCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{83} + return fileDescriptor_007ba1c3d6266d56, []int{84} } func (m *AppCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4749,7 +4797,7 @@ func (m *AppUpdate) Reset() { *m = AppUpdate{} } func (m *AppUpdate) String() string { return proto.CompactTextString(m) } func (*AppUpdate) ProtoMessage() {} func (*AppUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{84} + return fileDescriptor_007ba1c3d6266d56, []int{85} } func (m *AppUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4795,7 +4843,7 @@ func (m *AppDelete) Reset() { *m = AppDelete{} } func (m *AppDelete) String() string { return proto.CompactTextString(m) } func (*AppDelete) ProtoMessage() {} func (*AppDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{85} + return fileDescriptor_007ba1c3d6266d56, []int{86} } func (m *AppDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4850,7 +4898,7 @@ func (m *AppSessionStart) Reset() { *m = AppSessionStart{} } func (m *AppSessionStart) String() string { return proto.CompactTextString(m) } func (*AppSessionStart) ProtoMessage() {} func (*AppSessionStart) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{86} + return fileDescriptor_007ba1c3d6266d56, []int{87} } func (m *AppSessionStart) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4902,7 +4950,7 @@ func (m *AppSessionEnd) Reset() { *m = AppSessionEnd{} } func (m *AppSessionEnd) String() string { return proto.CompactTextString(m) } func (*AppSessionEnd) ProtoMessage() {} func (*AppSessionEnd) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{87} + return fileDescriptor_007ba1c3d6266d56, []int{88} } func (m *AppSessionEnd) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4959,7 +5007,7 @@ func (m *AppSessionChunk) Reset() { *m = AppSessionChunk{} } func (m *AppSessionChunk) String() string { return proto.CompactTextString(m) } func (*AppSessionChunk) ProtoMessage() {} func (*AppSessionChunk) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{88} + return fileDescriptor_007ba1c3d6266d56, []int{89} } func (m *AppSessionChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5013,7 +5061,7 @@ func (m *AppSessionRequest) Reset() { *m = AppSessionRequest{} } func (m *AppSessionRequest) String() string { return proto.CompactTextString(m) } func (*AppSessionRequest) ProtoMessage() {} func (*AppSessionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{89} + return fileDescriptor_007ba1c3d6266d56, []int{90} } func (m *AppSessionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5061,7 +5109,7 @@ func (m *AWSRequestMetadata) Reset() { *m = AWSRequestMetadata{} } func (m *AWSRequestMetadata) String() string { return proto.CompactTextString(m) } func (*AWSRequestMetadata) ProtoMessage() {} func (*AWSRequestMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{90} + return fileDescriptor_007ba1c3d6266d56, []int{91} } func (m *AWSRequestMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5127,7 +5175,7 @@ func (m *DatabaseMetadata) Reset() { *m = DatabaseMetadata{} } func (m *DatabaseMetadata) String() string { return proto.CompactTextString(m) } func (*DatabaseMetadata) ProtoMessage() {} func (*DatabaseMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{91} + return fileDescriptor_007ba1c3d6266d56, []int{92} } func (m *DatabaseMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5175,7 +5223,7 @@ func (m *DatabaseCreate) Reset() { *m = DatabaseCreate{} } func (m *DatabaseCreate) String() string { return proto.CompactTextString(m) } func (*DatabaseCreate) ProtoMessage() {} func (*DatabaseCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{92} + return fileDescriptor_007ba1c3d6266d56, []int{93} } func (m *DatabaseCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5223,7 +5271,7 @@ func (m *DatabaseUpdate) Reset() { *m = DatabaseUpdate{} } func (m *DatabaseUpdate) String() string { return proto.CompactTextString(m) } func (*DatabaseUpdate) ProtoMessage() {} func (*DatabaseUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{93} + return fileDescriptor_007ba1c3d6266d56, []int{94} } func (m *DatabaseUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5269,7 +5317,7 @@ func (m *DatabaseDelete) Reset() { *m = DatabaseDelete{} } func (m *DatabaseDelete) String() string { return proto.CompactTextString(m) } func (*DatabaseDelete) ProtoMessage() {} func (*DatabaseDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{94} + return fileDescriptor_007ba1c3d6266d56, []int{95} } func (m *DatabaseDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5317,7 +5365,9 @@ type DatabaseSessionStart struct { // PostgresPID is the Postgres backend PID that was created for a Postgres // connection. This can be useful for backend process cancellation or // termination and it is not a sensitive or secret value. - PostgresPID uint32 `protobuf:"varint,8,opt,name=PostgresPID,proto3" json:"postgres_pid,omitempty"` + PostgresPID uint32 `protobuf:"varint,8,opt,name=PostgresPID,proto3" json:"postgres_pid,omitempty"` + // Client is the common client event metadata. + ClientMetadata `protobuf:"bytes,9,opt,name=Client,proto3,embedded=Client" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -5327,7 +5377,7 @@ func (m *DatabaseSessionStart) Reset() { *m = DatabaseSessionStart{} } func (m *DatabaseSessionStart) String() string { return proto.CompactTextString(m) } func (*DatabaseSessionStart) ProtoMessage() {} func (*DatabaseSessionStart) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{95} + return fileDescriptor_007ba1c3d6266d56, []int{96} } func (m *DatabaseSessionStart) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5381,7 +5431,7 @@ func (m *DatabaseSessionQuery) Reset() { *m = DatabaseSessionQuery{} } func (m *DatabaseSessionQuery) String() string { return proto.CompactTextString(m) } func (*DatabaseSessionQuery) ProtoMessage() {} func (*DatabaseSessionQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{96} + return fileDescriptor_007ba1c3d6266d56, []int{97} } func (m *DatabaseSessionQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5436,7 +5486,7 @@ func (m *DatabaseSessionCommandResult) Reset() { *m = DatabaseSessionCom func (m *DatabaseSessionCommandResult) String() string { return proto.CompactTextString(m) } func (*DatabaseSessionCommandResult) ProtoMessage() {} func (*DatabaseSessionCommandResult) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{97} + return fileDescriptor_007ba1c3d6266d56, []int{98} } func (m *DatabaseSessionCommandResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5488,7 +5538,7 @@ func (m *DatabasePermissionUpdate) Reset() { *m = DatabasePermissionUpda func (m *DatabasePermissionUpdate) String() string { return proto.CompactTextString(m) } func (*DatabasePermissionUpdate) ProtoMessage() {} func (*DatabasePermissionUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{98} + return fileDescriptor_007ba1c3d6266d56, []int{99} } func (m *DatabasePermissionUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5532,7 +5582,7 @@ func (m *DatabasePermissionEntry) Reset() { *m = DatabasePermissionEntry func (m *DatabasePermissionEntry) String() string { return proto.CompactTextString(m) } func (*DatabasePermissionEntry) ProtoMessage() {} func (*DatabasePermissionEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{99} + return fileDescriptor_007ba1c3d6266d56, []int{100} } func (m *DatabasePermissionEntry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5587,7 +5637,7 @@ func (m *DatabaseUserCreate) Reset() { *m = DatabaseUserCreate{} } func (m *DatabaseUserCreate) String() string { return proto.CompactTextString(m) } func (*DatabaseUserCreate) ProtoMessage() {} func (*DatabaseUserCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{100} + return fileDescriptor_007ba1c3d6266d56, []int{101} } func (m *DatabaseUserCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5642,7 +5692,7 @@ func (m *DatabaseUserDeactivate) Reset() { *m = DatabaseUserDeactivate{} func (m *DatabaseUserDeactivate) String() string { return proto.CompactTextString(m) } func (*DatabaseUserDeactivate) ProtoMessage() {} func (*DatabaseUserDeactivate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{101} + return fileDescriptor_007ba1c3d6266d56, []int{102} } func (m *DatabaseUserDeactivate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5695,7 +5745,7 @@ func (m *PostgresParse) Reset() { *m = PostgresParse{} } func (m *PostgresParse) String() string { return proto.CompactTextString(m) } func (*PostgresParse) ProtoMessage() {} func (*PostgresParse) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{102} + return fileDescriptor_007ba1c3d6266d56, []int{103} } func (m *PostgresParse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5750,7 +5800,7 @@ func (m *PostgresBind) Reset() { *m = PostgresBind{} } func (m *PostgresBind) String() string { return proto.CompactTextString(m) } func (*PostgresBind) ProtoMessage() {} func (*PostgresBind) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{103} + return fileDescriptor_007ba1c3d6266d56, []int{104} } func (m *PostgresBind) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5801,7 +5851,7 @@ func (m *PostgresExecute) Reset() { *m = PostgresExecute{} } func (m *PostgresExecute) String() string { return proto.CompactTextString(m) } func (*PostgresExecute) ProtoMessage() {} func (*PostgresExecute) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{104} + return fileDescriptor_007ba1c3d6266d56, []int{105} } func (m *PostgresExecute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5854,7 +5904,7 @@ func (m *PostgresClose) Reset() { *m = PostgresClose{} } func (m *PostgresClose) String() string { return proto.CompactTextString(m) } func (*PostgresClose) ProtoMessage() {} func (*PostgresClose) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{105} + return fileDescriptor_007ba1c3d6266d56, []int{106} } func (m *PostgresClose) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5907,7 +5957,7 @@ func (m *PostgresFunctionCall) Reset() { *m = PostgresFunctionCall{} } func (m *PostgresFunctionCall) String() string { return proto.CompactTextString(m) } func (*PostgresFunctionCall) ProtoMessage() {} func (*PostgresFunctionCall) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{106} + return fileDescriptor_007ba1c3d6266d56, []int{107} } func (m *PostgresFunctionCall) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5975,7 +6025,7 @@ func (m *WindowsDesktopSessionStart) Reset() { *m = WindowsDesktopSessio func (m *WindowsDesktopSessionStart) String() string { return proto.CompactTextString(m) } func (*WindowsDesktopSessionStart) ProtoMessage() {} func (*WindowsDesktopSessionStart) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{107} + return fileDescriptor_007ba1c3d6266d56, []int{108} } func (m *WindowsDesktopSessionStart) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6027,7 +6077,7 @@ func (m *DatabaseSessionEnd) Reset() { *m = DatabaseSessionEnd{} } func (m *DatabaseSessionEnd) String() string { return proto.CompactTextString(m) } func (*DatabaseSessionEnd) ProtoMessage() {} func (*DatabaseSessionEnd) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{108} + return fileDescriptor_007ba1c3d6266d56, []int{109} } func (m *DatabaseSessionEnd) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6073,7 +6123,7 @@ func (m *MFADeviceMetadata) Reset() { *m = MFADeviceMetadata{} } func (m *MFADeviceMetadata) String() string { return proto.CompactTextString(m) } func (*MFADeviceMetadata) ProtoMessage() {} func (*MFADeviceMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{109} + return fileDescriptor_007ba1c3d6266d56, []int{110} } func (m *MFADeviceMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6121,7 +6171,7 @@ func (m *MFADeviceAdd) Reset() { *m = MFADeviceAdd{} } func (m *MFADeviceAdd) String() string { return proto.CompactTextString(m) } func (*MFADeviceAdd) ProtoMessage() {} func (*MFADeviceAdd) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{110} + return fileDescriptor_007ba1c3d6266d56, []int{111} } func (m *MFADeviceAdd) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6169,7 +6219,7 @@ func (m *MFADeviceDelete) Reset() { *m = MFADeviceDelete{} } func (m *MFADeviceDelete) String() string { return proto.CompactTextString(m) } func (*MFADeviceDelete) ProtoMessage() {} func (*MFADeviceDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{111} + return fileDescriptor_007ba1c3d6266d56, []int{112} } func (m *MFADeviceDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6213,7 +6263,7 @@ func (m *BillingInformationUpdate) Reset() { *m = BillingInformationUpda func (m *BillingInformationUpdate) String() string { return proto.CompactTextString(m) } func (*BillingInformationUpdate) ProtoMessage() {} func (*BillingInformationUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{112} + return fileDescriptor_007ba1c3d6266d56, []int{113} } func (m *BillingInformationUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6257,7 +6307,7 @@ func (m *BillingCardCreate) Reset() { *m = BillingCardCreate{} } func (m *BillingCardCreate) String() string { return proto.CompactTextString(m) } func (*BillingCardCreate) ProtoMessage() {} func (*BillingCardCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{113} + return fileDescriptor_007ba1c3d6266d56, []int{114} } func (m *BillingCardCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6301,7 +6351,7 @@ func (m *BillingCardDelete) Reset() { *m = BillingCardDelete{} } func (m *BillingCardDelete) String() string { return proto.CompactTextString(m) } func (*BillingCardDelete) ProtoMessage() {} func (*BillingCardDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{114} + return fileDescriptor_007ba1c3d6266d56, []int{115} } func (m *BillingCardDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6355,7 +6405,7 @@ func (m *LockCreate) Reset() { *m = LockCreate{} } func (m *LockCreate) String() string { return proto.CompactTextString(m) } func (*LockCreate) ProtoMessage() {} func (*LockCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{115} + return fileDescriptor_007ba1c3d6266d56, []int{116} } func (m *LockCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6403,7 +6453,7 @@ func (m *LockDelete) Reset() { *m = LockDelete{} } func (m *LockDelete) String() string { return proto.CompactTextString(m) } func (*LockDelete) ProtoMessage() {} func (*LockDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{116} + return fileDescriptor_007ba1c3d6266d56, []int{117} } func (m *LockDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6447,7 +6497,7 @@ func (m *RecoveryCodeGenerate) Reset() { *m = RecoveryCodeGenerate{} } func (m *RecoveryCodeGenerate) String() string { return proto.CompactTextString(m) } func (*RecoveryCodeGenerate) ProtoMessage() {} func (*RecoveryCodeGenerate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{117} + return fileDescriptor_007ba1c3d6266d56, []int{118} } func (m *RecoveryCodeGenerate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6494,7 +6544,7 @@ func (m *RecoveryCodeUsed) Reset() { *m = RecoveryCodeUsed{} } func (m *RecoveryCodeUsed) String() string { return proto.CompactTextString(m) } func (*RecoveryCodeUsed) ProtoMessage() {} func (*RecoveryCodeUsed) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{118} + return fileDescriptor_007ba1c3d6266d56, []int{119} } func (m *RecoveryCodeUsed) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6560,7 +6610,7 @@ func (m *WindowsDesktopSessionEnd) Reset() { *m = WindowsDesktopSessionE func (m *WindowsDesktopSessionEnd) String() string { return proto.CompactTextString(m) } func (*WindowsDesktopSessionEnd) ProtoMessage() {} func (*WindowsDesktopSessionEnd) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{119} + return fileDescriptor_007ba1c3d6266d56, []int{120} } func (m *WindowsDesktopSessionEnd) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6608,7 +6658,7 @@ func (m *CertificateCreate) Reset() { *m = CertificateCreate{} } func (m *CertificateCreate) String() string { return proto.CompactTextString(m) } func (*CertificateCreate) ProtoMessage() {} func (*CertificateCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{120} + return fileDescriptor_007ba1c3d6266d56, []int{121} } func (m *CertificateCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6656,7 +6706,7 @@ func (m *RenewableCertificateGenerationMismatch) Reset() { func (m *RenewableCertificateGenerationMismatch) String() string { return proto.CompactTextString(m) } func (*RenewableCertificateGenerationMismatch) ProtoMessage() {} func (*RenewableCertificateGenerationMismatch) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{121} + return fileDescriptor_007ba1c3d6266d56, []int{122} } func (m *RenewableCertificateGenerationMismatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6714,7 +6764,7 @@ func (m *BotJoin) Reset() { *m = BotJoin{} } func (m *BotJoin) String() string { return proto.CompactTextString(m) } func (*BotJoin) ProtoMessage() {} func (*BotJoin) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{122} + return fileDescriptor_007ba1c3d6266d56, []int{123} } func (m *BotJoin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6776,7 +6826,7 @@ func (m *InstanceJoin) Reset() { *m = InstanceJoin{} } func (m *InstanceJoin) String() string { return proto.CompactTextString(m) } func (*InstanceJoin) ProtoMessage() {} func (*InstanceJoin) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{123} + return fileDescriptor_007ba1c3d6266d56, []int{124} } func (m *InstanceJoin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6824,7 +6874,7 @@ func (m *Unknown) Reset() { *m = Unknown{} } func (m *Unknown) String() string { return proto.CompactTextString(m) } func (*Unknown) ProtoMessage() {} func (*Unknown) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{124} + return fileDescriptor_007ba1c3d6266d56, []int{125} } func (m *Unknown) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6881,7 +6931,7 @@ func (m *DeviceMetadata) Reset() { *m = DeviceMetadata{} } func (m *DeviceMetadata) String() string { return proto.CompactTextString(m) } func (*DeviceMetadata) ProtoMessage() {} func (*DeviceMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{125} + return fileDescriptor_007ba1c3d6266d56, []int{126} } func (m *DeviceMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6932,7 +6982,7 @@ func (m *DeviceEvent) Reset() { *m = DeviceEvent{} } func (m *DeviceEvent) String() string { return proto.CompactTextString(m) } func (*DeviceEvent) ProtoMessage() {} func (*DeviceEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{126} + return fileDescriptor_007ba1c3d6266d56, []int{127} } func (m *DeviceEvent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6984,7 +7034,7 @@ func (m *DeviceEvent2) Reset() { *m = DeviceEvent2{} } func (m *DeviceEvent2) String() string { return proto.CompactTextString(m) } func (*DeviceEvent2) ProtoMessage() {} func (*DeviceEvent2) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{127} + return fileDescriptor_007ba1c3d6266d56, []int{128} } func (m *DeviceEvent2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7032,7 +7082,7 @@ func (m *DiscoveryConfigCreate) Reset() { *m = DiscoveryConfigCreate{} } func (m *DiscoveryConfigCreate) String() string { return proto.CompactTextString(m) } func (*DiscoveryConfigCreate) ProtoMessage() {} func (*DiscoveryConfigCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{128} + return fileDescriptor_007ba1c3d6266d56, []int{129} } func (m *DiscoveryConfigCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7080,7 +7130,7 @@ func (m *DiscoveryConfigUpdate) Reset() { *m = DiscoveryConfigUpdate{} } func (m *DiscoveryConfigUpdate) String() string { return proto.CompactTextString(m) } func (*DiscoveryConfigUpdate) ProtoMessage() {} func (*DiscoveryConfigUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{129} + return fileDescriptor_007ba1c3d6266d56, []int{130} } func (m *DiscoveryConfigUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7128,7 +7178,7 @@ func (m *DiscoveryConfigDelete) Reset() { *m = DiscoveryConfigDelete{} } func (m *DiscoveryConfigDelete) String() string { return proto.CompactTextString(m) } func (*DiscoveryConfigDelete) ProtoMessage() {} func (*DiscoveryConfigDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{130} + return fileDescriptor_007ba1c3d6266d56, []int{131} } func (m *DiscoveryConfigDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7174,7 +7224,7 @@ func (m *DiscoveryConfigDeleteAll) Reset() { *m = DiscoveryConfigDeleteA func (m *DiscoveryConfigDeleteAll) String() string { return proto.CompactTextString(m) } func (*DiscoveryConfigDeleteAll) ProtoMessage() {} func (*DiscoveryConfigDeleteAll) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{131} + return fileDescriptor_007ba1c3d6266d56, []int{132} } func (m *DiscoveryConfigDeleteAll) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7223,7 +7273,7 @@ func (m *IntegrationCreate) Reset() { *m = IntegrationCreate{} } func (m *IntegrationCreate) String() string { return proto.CompactTextString(m) } func (*IntegrationCreate) ProtoMessage() {} func (*IntegrationCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{132} + return fileDescriptor_007ba1c3d6266d56, []int{133} } func (m *IntegrationCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7272,7 +7322,7 @@ func (m *IntegrationUpdate) Reset() { *m = IntegrationUpdate{} } func (m *IntegrationUpdate) String() string { return proto.CompactTextString(m) } func (*IntegrationUpdate) ProtoMessage() {} func (*IntegrationUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{133} + return fileDescriptor_007ba1c3d6266d56, []int{134} } func (m *IntegrationUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7321,7 +7371,7 @@ func (m *IntegrationDelete) Reset() { *m = IntegrationDelete{} } func (m *IntegrationDelete) String() string { return proto.CompactTextString(m) } func (*IntegrationDelete) ProtoMessage() {} func (*IntegrationDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{134} + return fileDescriptor_007ba1c3d6266d56, []int{135} } func (m *IntegrationDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7369,7 +7419,7 @@ func (m *IntegrationMetadata) Reset() { *m = IntegrationMetadata{} } func (m *IntegrationMetadata) String() string { return proto.CompactTextString(m) } func (*IntegrationMetadata) ProtoMessage() {} func (*IntegrationMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{135} + return fileDescriptor_007ba1c3d6266d56, []int{136} } func (m *IntegrationMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7414,7 +7464,7 @@ func (m *AWSOIDCIntegrationMetadata) Reset() { *m = AWSOIDCIntegrationMe func (m *AWSOIDCIntegrationMetadata) String() string { return proto.CompactTextString(m) } func (*AWSOIDCIntegrationMetadata) ProtoMessage() {} func (*AWSOIDCIntegrationMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{136} + return fileDescriptor_007ba1c3d6266d56, []int{137} } func (m *AWSOIDCIntegrationMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7458,7 +7508,7 @@ func (m *AzureOIDCIntegrationMetadata) Reset() { *m = AzureOIDCIntegrati func (m *AzureOIDCIntegrationMetadata) String() string { return proto.CompactTextString(m) } func (*AzureOIDCIntegrationMetadata) ProtoMessage() {} func (*AzureOIDCIntegrationMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{137} + return fileDescriptor_007ba1c3d6266d56, []int{138} } func (m *AzureOIDCIntegrationMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7500,7 +7550,7 @@ func (m *GitHubIntegrationMetadata) Reset() { *m = GitHubIntegrationMeta func (m *GitHubIntegrationMetadata) String() string { return proto.CompactTextString(m) } func (*GitHubIntegrationMetadata) ProtoMessage() {} func (*GitHubIntegrationMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{138} + return fileDescriptor_007ba1c3d6266d56, []int{139} } func (m *GitHubIntegrationMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7549,7 +7599,7 @@ func (m *PluginCreate) Reset() { *m = PluginCreate{} } func (m *PluginCreate) String() string { return proto.CompactTextString(m) } func (*PluginCreate) ProtoMessage() {} func (*PluginCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{139} + return fileDescriptor_007ba1c3d6266d56, []int{140} } func (m *PluginCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7598,7 +7648,7 @@ func (m *PluginUpdate) Reset() { *m = PluginUpdate{} } func (m *PluginUpdate) String() string { return proto.CompactTextString(m) } func (*PluginUpdate) ProtoMessage() {} func (*PluginUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{140} + return fileDescriptor_007ba1c3d6266d56, []int{141} } func (m *PluginUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7647,7 +7697,7 @@ func (m *PluginDelete) Reset() { *m = PluginDelete{} } func (m *PluginDelete) String() string { return proto.CompactTextString(m) } func (*PluginDelete) ProtoMessage() {} func (*PluginDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{141} + return fileDescriptor_007ba1c3d6266d56, []int{142} } func (m *PluginDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7696,7 +7746,7 @@ func (m *PluginMetadata) Reset() { *m = PluginMetadata{} } func (m *PluginMetadata) String() string { return proto.CompactTextString(m) } func (*PluginMetadata) ProtoMessage() {} func (*PluginMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{142} + return fileDescriptor_007ba1c3d6266d56, []int{143} } func (m *PluginMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7922,6 +7972,7 @@ type OneOf struct { // *OneOf_WorkloadIdentityDelete // *OneOf_GitCommand // *OneOf_UserLoginAccessListInvalid + // *OneOf_AccessRequestExpire Event isOneOf_Event `protobuf_oneof:"Event"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -7932,7 +7983,7 @@ func (m *OneOf) Reset() { *m = OneOf{} } func (m *OneOf) String() string { return proto.CompactTextString(m) } func (*OneOf) ProtoMessage() {} func (*OneOf) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{143} + return fileDescriptor_007ba1c3d6266d56, []int{144} } func (m *OneOf) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8549,6 +8600,9 @@ type OneOf_GitCommand struct { type OneOf_UserLoginAccessListInvalid struct { UserLoginAccessListInvalid *UserLoginAccessListInvalid `protobuf:"bytes,198,opt,name=UserLoginAccessListInvalid,proto3,oneof" json:"UserLoginAccessListInvalid,omitempty"` } +type OneOf_AccessRequestExpire struct { + AccessRequestExpire *AccessRequestExpire `protobuf:"bytes,199,opt,name=AccessRequestExpire,proto3,oneof" json:"AccessRequestExpire,omitempty"` +} func (*OneOf_UserLogin) isOneOf_Event() {} func (*OneOf_UserCreate) isOneOf_Event() {} @@ -8744,6 +8798,7 @@ func (*OneOf_WorkloadIdentityUpdate) isOneOf_Event() {} func (*OneOf_WorkloadIdentityDelete) isOneOf_Event() {} func (*OneOf_GitCommand) isOneOf_Event() {} func (*OneOf_UserLoginAccessListInvalid) isOneOf_Event() {} +func (*OneOf_AccessRequestExpire) isOneOf_Event() {} func (m *OneOf) GetEvent() isOneOf_Event { if m != nil { @@ -10110,6 +10165,13 @@ func (m *OneOf) GetUserLoginAccessListInvalid() *UserLoginAccessListInvalid { return nil } +func (m *OneOf) GetAccessRequestExpire() *AccessRequestExpire { + if x, ok := m.GetEvent().(*OneOf_AccessRequestExpire); ok { + return x.AccessRequestExpire + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*OneOf) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -10307,6 +10369,7 @@ func (*OneOf) XXX_OneofWrappers() []interface{} { (*OneOf_WorkloadIdentityDelete)(nil), (*OneOf_GitCommand)(nil), (*OneOf_UserLoginAccessListInvalid)(nil), + (*OneOf_AccessRequestExpire)(nil), } } @@ -10327,7 +10390,7 @@ func (m *StreamStatus) Reset() { *m = StreamStatus{} } func (m *StreamStatus) String() string { return proto.CompactTextString(m) } func (*StreamStatus) ProtoMessage() {} func (*StreamStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{144} + return fileDescriptor_007ba1c3d6266d56, []int{145} } func (m *StreamStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10373,7 +10436,7 @@ func (m *SessionUpload) Reset() { *m = SessionUpload{} } func (m *SessionUpload) String() string { return proto.CompactTextString(m) } func (*SessionUpload) ProtoMessage() {} func (*SessionUpload) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{145} + return fileDescriptor_007ba1c3d6266d56, []int{146} } func (m *SessionUpload) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10492,7 +10555,7 @@ func (m *Identity) Reset() { *m = Identity{} } func (m *Identity) String() string { return proto.CompactTextString(m) } func (*Identity) ProtoMessage() {} func (*Identity) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{146} + return fileDescriptor_007ba1c3d6266d56, []int{147} } func (m *Identity) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10551,7 +10614,7 @@ func (m *RouteToApp) Reset() { *m = RouteToApp{} } func (m *RouteToApp) String() string { return proto.CompactTextString(m) } func (*RouteToApp) ProtoMessage() {} func (*RouteToApp) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{147} + return fileDescriptor_007ba1c3d6266d56, []int{148} } func (m *RouteToApp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10601,7 +10664,7 @@ func (m *RouteToDatabase) Reset() { *m = RouteToDatabase{} } func (m *RouteToDatabase) String() string { return proto.CompactTextString(m) } func (*RouteToDatabase) ProtoMessage() {} func (*RouteToDatabase) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{148} + return fileDescriptor_007ba1c3d6266d56, []int{149} } func (m *RouteToDatabase) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10651,7 +10714,7 @@ func (m *DeviceExtensions) Reset() { *m = DeviceExtensions{} } func (m *DeviceExtensions) String() string { return proto.CompactTextString(m) } func (*DeviceExtensions) ProtoMessage() {} func (*DeviceExtensions) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{149} + return fileDescriptor_007ba1c3d6266d56, []int{150} } func (m *DeviceExtensions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10708,7 +10771,7 @@ func (m *AccessRequestResourceSearch) Reset() { *m = AccessRequestResour func (m *AccessRequestResourceSearch) String() string { return proto.CompactTextString(m) } func (*AccessRequestResourceSearch) ProtoMessage() {} func (*AccessRequestResourceSearch) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{150} + return fileDescriptor_007ba1c3d6266d56, []int{151} } func (m *AccessRequestResourceSearch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10759,7 +10822,7 @@ func (m *MySQLStatementPrepare) Reset() { *m = MySQLStatementPrepare{} } func (m *MySQLStatementPrepare) String() string { return proto.CompactTextString(m) } func (*MySQLStatementPrepare) ProtoMessage() {} func (*MySQLStatementPrepare) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{151} + return fileDescriptor_007ba1c3d6266d56, []int{152} } func (m *MySQLStatementPrepare) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10812,7 +10875,7 @@ func (m *MySQLStatementExecute) Reset() { *m = MySQLStatementExecute{} } func (m *MySQLStatementExecute) String() string { return proto.CompactTextString(m) } func (*MySQLStatementExecute) ProtoMessage() {} func (*MySQLStatementExecute) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{152} + return fileDescriptor_007ba1c3d6266d56, []int{153} } func (m *MySQLStatementExecute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10867,7 +10930,7 @@ func (m *MySQLStatementSendLongData) Reset() { *m = MySQLStatementSendLo func (m *MySQLStatementSendLongData) String() string { return proto.CompactTextString(m) } func (*MySQLStatementSendLongData) ProtoMessage() {} func (*MySQLStatementSendLongData) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{153} + return fileDescriptor_007ba1c3d6266d56, []int{154} } func (m *MySQLStatementSendLongData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10918,7 +10981,7 @@ func (m *MySQLStatementClose) Reset() { *m = MySQLStatementClose{} } func (m *MySQLStatementClose) String() string { return proto.CompactTextString(m) } func (*MySQLStatementClose) ProtoMessage() {} func (*MySQLStatementClose) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{154} + return fileDescriptor_007ba1c3d6266d56, []int{155} } func (m *MySQLStatementClose) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10969,7 +11032,7 @@ func (m *MySQLStatementReset) Reset() { *m = MySQLStatementReset{} } func (m *MySQLStatementReset) String() string { return proto.CompactTextString(m) } func (*MySQLStatementReset) ProtoMessage() {} func (*MySQLStatementReset) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{155} + return fileDescriptor_007ba1c3d6266d56, []int{156} } func (m *MySQLStatementReset) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11022,7 +11085,7 @@ func (m *MySQLStatementFetch) Reset() { *m = MySQLStatementFetch{} } func (m *MySQLStatementFetch) String() string { return proto.CompactTextString(m) } func (*MySQLStatementFetch) ProtoMessage() {} func (*MySQLStatementFetch) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{156} + return fileDescriptor_007ba1c3d6266d56, []int{157} } func (m *MySQLStatementFetch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11075,7 +11138,7 @@ func (m *MySQLStatementBulkExecute) Reset() { *m = MySQLStatementBulkExe func (m *MySQLStatementBulkExecute) String() string { return proto.CompactTextString(m) } func (*MySQLStatementBulkExecute) ProtoMessage() {} func (*MySQLStatementBulkExecute) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{157} + return fileDescriptor_007ba1c3d6266d56, []int{158} } func (m *MySQLStatementBulkExecute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11126,7 +11189,7 @@ func (m *MySQLInitDB) Reset() { *m = MySQLInitDB{} } func (m *MySQLInitDB) String() string { return proto.CompactTextString(m) } func (*MySQLInitDB) ProtoMessage() {} func (*MySQLInitDB) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{158} + return fileDescriptor_007ba1c3d6266d56, []int{159} } func (m *MySQLInitDB) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11176,7 +11239,7 @@ func (m *MySQLCreateDB) Reset() { *m = MySQLCreateDB{} } func (m *MySQLCreateDB) String() string { return proto.CompactTextString(m) } func (*MySQLCreateDB) ProtoMessage() {} func (*MySQLCreateDB) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{159} + return fileDescriptor_007ba1c3d6266d56, []int{160} } func (m *MySQLCreateDB) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11226,7 +11289,7 @@ func (m *MySQLDropDB) Reset() { *m = MySQLDropDB{} } func (m *MySQLDropDB) String() string { return proto.CompactTextString(m) } func (*MySQLDropDB) ProtoMessage() {} func (*MySQLDropDB) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{160} + return fileDescriptor_007ba1c3d6266d56, []int{161} } func (m *MySQLDropDB) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11274,7 +11337,7 @@ func (m *MySQLShutDown) Reset() { *m = MySQLShutDown{} } func (m *MySQLShutDown) String() string { return proto.CompactTextString(m) } func (*MySQLShutDown) ProtoMessage() {} func (*MySQLShutDown) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{161} + return fileDescriptor_007ba1c3d6266d56, []int{162} } func (m *MySQLShutDown) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11325,7 +11388,7 @@ func (m *MySQLProcessKill) Reset() { *m = MySQLProcessKill{} } func (m *MySQLProcessKill) String() string { return proto.CompactTextString(m) } func (*MySQLProcessKill) ProtoMessage() {} func (*MySQLProcessKill) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{162} + return fileDescriptor_007ba1c3d6266d56, []int{163} } func (m *MySQLProcessKill) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11374,7 +11437,7 @@ func (m *MySQLDebug) Reset() { *m = MySQLDebug{} } func (m *MySQLDebug) String() string { return proto.CompactTextString(m) } func (*MySQLDebug) ProtoMessage() {} func (*MySQLDebug) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{163} + return fileDescriptor_007ba1c3d6266d56, []int{164} } func (m *MySQLDebug) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11424,7 +11487,7 @@ func (m *MySQLRefresh) Reset() { *m = MySQLRefresh{} } func (m *MySQLRefresh) String() string { return proto.CompactTextString(m) } func (*MySQLRefresh) ProtoMessage() {} func (*MySQLRefresh) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{164} + return fileDescriptor_007ba1c3d6266d56, []int{165} } func (m *MySQLRefresh) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11476,7 +11539,7 @@ func (m *SQLServerRPCRequest) Reset() { *m = SQLServerRPCRequest{} } func (m *SQLServerRPCRequest) String() string { return proto.CompactTextString(m) } func (*SQLServerRPCRequest) ProtoMessage() {} func (*SQLServerRPCRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{165} + return fileDescriptor_007ba1c3d6266d56, []int{166} } func (m *SQLServerRPCRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11526,7 +11589,7 @@ func (m *DatabaseSessionMalformedPacket) Reset() { *m = DatabaseSessionM func (m *DatabaseSessionMalformedPacket) String() string { return proto.CompactTextString(m) } func (*DatabaseSessionMalformedPacket) ProtoMessage() {} func (*DatabaseSessionMalformedPacket) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{166} + return fileDescriptor_007ba1c3d6266d56, []int{167} } func (m *DatabaseSessionMalformedPacket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11593,7 +11656,7 @@ func (m *ElasticsearchRequest) Reset() { *m = ElasticsearchRequest{} } func (m *ElasticsearchRequest) String() string { return proto.CompactTextString(m) } func (*ElasticsearchRequest) ProtoMessage() {} func (*ElasticsearchRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{167} + return fileDescriptor_007ba1c3d6266d56, []int{168} } func (m *ElasticsearchRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11659,7 +11722,7 @@ func (m *OpenSearchRequest) Reset() { *m = OpenSearchRequest{} } func (m *OpenSearchRequest) String() string { return proto.CompactTextString(m) } func (*OpenSearchRequest) ProtoMessage() {} func (*OpenSearchRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{168} + return fileDescriptor_007ba1c3d6266d56, []int{169} } func (m *OpenSearchRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11719,7 +11782,7 @@ func (m *DynamoDBRequest) Reset() { *m = DynamoDBRequest{} } func (m *DynamoDBRequest) String() string { return proto.CompactTextString(m) } func (*DynamoDBRequest) ProtoMessage() {} func (*DynamoDBRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{169} + return fileDescriptor_007ba1c3d6266d56, []int{170} } func (m *DynamoDBRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11785,7 +11848,7 @@ func (m *AppSessionDynamoDBRequest) Reset() { *m = AppSessionDynamoDBReq func (m *AppSessionDynamoDBRequest) String() string { return proto.CompactTextString(m) } func (*AppSessionDynamoDBRequest) ProtoMessage() {} func (*AppSessionDynamoDBRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{170} + return fileDescriptor_007ba1c3d6266d56, []int{171} } func (m *AppSessionDynamoDBRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11827,7 +11890,7 @@ func (m *UpgradeWindowStartMetadata) Reset() { *m = UpgradeWindowStartMe func (m *UpgradeWindowStartMetadata) String() string { return proto.CompactTextString(m) } func (*UpgradeWindowStartMetadata) ProtoMessage() {} func (*UpgradeWindowStartMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{171} + return fileDescriptor_007ba1c3d6266d56, []int{172} } func (m *UpgradeWindowStartMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11875,7 +11938,7 @@ func (m *UpgradeWindowStartUpdate) Reset() { *m = UpgradeWindowStartUpda func (m *UpgradeWindowStartUpdate) String() string { return proto.CompactTextString(m) } func (*UpgradeWindowStartUpdate) ProtoMessage() {} func (*UpgradeWindowStartUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{172} + return fileDescriptor_007ba1c3d6266d56, []int{173} } func (m *UpgradeWindowStartUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11926,7 +11989,7 @@ func (m *SessionRecordingAccess) Reset() { *m = SessionRecordingAccess{} func (m *SessionRecordingAccess) String() string { return proto.CompactTextString(m) } func (*SessionRecordingAccess) ProtoMessage() {} func (*SessionRecordingAccess) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{173} + return fileDescriptor_007ba1c3d6266d56, []int{174} } func (m *SessionRecordingAccess) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11968,7 +12031,7 @@ func (m *KubeClusterMetadata) Reset() { *m = KubeClusterMetadata{} } func (m *KubeClusterMetadata) String() string { return proto.CompactTextString(m) } func (*KubeClusterMetadata) ProtoMessage() {} func (*KubeClusterMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{174} + return fileDescriptor_007ba1c3d6266d56, []int{175} } func (m *KubeClusterMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12016,7 +12079,7 @@ func (m *KubernetesClusterCreate) Reset() { *m = KubernetesClusterCreate func (m *KubernetesClusterCreate) String() string { return proto.CompactTextString(m) } func (*KubernetesClusterCreate) ProtoMessage() {} func (*KubernetesClusterCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{175} + return fileDescriptor_007ba1c3d6266d56, []int{176} } func (m *KubernetesClusterCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12064,7 +12127,7 @@ func (m *KubernetesClusterUpdate) Reset() { *m = KubernetesClusterUpdate func (m *KubernetesClusterUpdate) String() string { return proto.CompactTextString(m) } func (*KubernetesClusterUpdate) ProtoMessage() {} func (*KubernetesClusterUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{176} + return fileDescriptor_007ba1c3d6266d56, []int{177} } func (m *KubernetesClusterUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12110,7 +12173,7 @@ func (m *KubernetesClusterDelete) Reset() { *m = KubernetesClusterDelete func (m *KubernetesClusterDelete) String() string { return proto.CompactTextString(m) } func (*KubernetesClusterDelete) ProtoMessage() {} func (*KubernetesClusterDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{177} + return fileDescriptor_007ba1c3d6266d56, []int{178} } func (m *KubernetesClusterDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12173,7 +12236,7 @@ func (m *SSMRun) Reset() { *m = SSMRun{} } func (m *SSMRun) String() string { return proto.CompactTextString(m) } func (*SSMRun) ProtoMessage() {} func (*SSMRun) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{178} + return fileDescriptor_007ba1c3d6266d56, []int{179} } func (m *SSMRun) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12226,7 +12289,7 @@ func (m *CassandraPrepare) Reset() { *m = CassandraPrepare{} } func (m *CassandraPrepare) String() string { return proto.CompactTextString(m) } func (*CassandraPrepare) ProtoMessage() {} func (*CassandraPrepare) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{179} + return fileDescriptor_007ba1c3d6266d56, []int{180} } func (m *CassandraPrepare) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12276,7 +12339,7 @@ func (m *CassandraExecute) Reset() { *m = CassandraExecute{} } func (m *CassandraExecute) String() string { return proto.CompactTextString(m) } func (*CassandraExecute) ProtoMessage() {} func (*CassandraExecute) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{180} + return fileDescriptor_007ba1c3d6266d56, []int{181} } func (m *CassandraExecute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12332,7 +12395,7 @@ func (m *CassandraBatch) Reset() { *m = CassandraBatch{} } func (m *CassandraBatch) String() string { return proto.CompactTextString(m) } func (*CassandraBatch) ProtoMessage() {} func (*CassandraBatch) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{181} + return fileDescriptor_007ba1c3d6266d56, []int{182} } func (m *CassandraBatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12378,7 +12441,7 @@ func (m *CassandraBatch_BatchChild) Reset() { *m = CassandraBatch_BatchC func (m *CassandraBatch_BatchChild) String() string { return proto.CompactTextString(m) } func (*CassandraBatch_BatchChild) ProtoMessage() {} func (*CassandraBatch_BatchChild) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{181, 0} + return fileDescriptor_007ba1c3d6266d56, []int{182, 0} } func (m *CassandraBatch_BatchChild) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12422,7 +12485,7 @@ func (m *CassandraBatch_BatchChild_Value) Reset() { *m = CassandraBatch_ func (m *CassandraBatch_BatchChild_Value) String() string { return proto.CompactTextString(m) } func (*CassandraBatch_BatchChild_Value) ProtoMessage() {} func (*CassandraBatch_BatchChild_Value) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{181, 0, 0} + return fileDescriptor_007ba1c3d6266d56, []int{182, 0, 0} } func (m *CassandraBatch_BatchChild_Value) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12473,7 +12536,7 @@ func (m *CassandraRegister) Reset() { *m = CassandraRegister{} } func (m *CassandraRegister) String() string { return proto.CompactTextString(m) } func (*CassandraRegister) ProtoMessage() {} func (*CassandraRegister) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{182} + return fileDescriptor_007ba1c3d6266d56, []int{183} } func (m *CassandraRegister) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12519,7 +12582,7 @@ func (m *LoginRuleCreate) Reset() { *m = LoginRuleCreate{} } func (m *LoginRuleCreate) String() string { return proto.CompactTextString(m) } func (*LoginRuleCreate) ProtoMessage() {} func (*LoginRuleCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{183} + return fileDescriptor_007ba1c3d6266d56, []int{184} } func (m *LoginRuleCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12565,7 +12628,7 @@ func (m *LoginRuleDelete) Reset() { *m = LoginRuleDelete{} } func (m *LoginRuleDelete) String() string { return proto.CompactTextString(m) } func (*LoginRuleDelete) ProtoMessage() {} func (*LoginRuleDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{184} + return fileDescriptor_007ba1c3d6266d56, []int{185} } func (m *LoginRuleDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12615,7 +12678,7 @@ func (m *SAMLIdPAuthAttempt) Reset() { *m = SAMLIdPAuthAttempt{} } func (m *SAMLIdPAuthAttempt) String() string { return proto.CompactTextString(m) } func (*SAMLIdPAuthAttempt) ProtoMessage() {} func (*SAMLIdPAuthAttempt) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{185} + return fileDescriptor_007ba1c3d6266d56, []int{186} } func (m *SAMLIdPAuthAttempt) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12661,7 +12724,7 @@ func (m *SAMLIdPServiceProviderCreate) Reset() { *m = SAMLIdPServiceProv func (m *SAMLIdPServiceProviderCreate) String() string { return proto.CompactTextString(m) } func (*SAMLIdPServiceProviderCreate) ProtoMessage() {} func (*SAMLIdPServiceProviderCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{186} + return fileDescriptor_007ba1c3d6266d56, []int{187} } func (m *SAMLIdPServiceProviderCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12707,7 +12770,7 @@ func (m *SAMLIdPServiceProviderUpdate) Reset() { *m = SAMLIdPServiceProv func (m *SAMLIdPServiceProviderUpdate) String() string { return proto.CompactTextString(m) } func (*SAMLIdPServiceProviderUpdate) ProtoMessage() {} func (*SAMLIdPServiceProviderUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{187} + return fileDescriptor_007ba1c3d6266d56, []int{188} } func (m *SAMLIdPServiceProviderUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12753,7 +12816,7 @@ func (m *SAMLIdPServiceProviderDelete) Reset() { *m = SAMLIdPServiceProv func (m *SAMLIdPServiceProviderDelete) String() string { return proto.CompactTextString(m) } func (*SAMLIdPServiceProviderDelete) ProtoMessage() {} func (*SAMLIdPServiceProviderDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{188} + return fileDescriptor_007ba1c3d6266d56, []int{189} } func (m *SAMLIdPServiceProviderDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12797,7 +12860,7 @@ func (m *SAMLIdPServiceProviderDeleteAll) Reset() { *m = SAMLIdPServiceP func (m *SAMLIdPServiceProviderDeleteAll) String() string { return proto.CompactTextString(m) } func (*SAMLIdPServiceProviderDeleteAll) ProtoMessage() {} func (*SAMLIdPServiceProviderDeleteAll) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{189} + return fileDescriptor_007ba1c3d6266d56, []int{190} } func (m *SAMLIdPServiceProviderDeleteAll) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12843,7 +12906,7 @@ func (m *OktaResourcesUpdate) Reset() { *m = OktaResourcesUpdate{} } func (m *OktaResourcesUpdate) String() string { return proto.CompactTextString(m) } func (*OktaResourcesUpdate) ProtoMessage() {} func (*OktaResourcesUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{190} + return fileDescriptor_007ba1c3d6266d56, []int{191} } func (m *OktaResourcesUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12889,7 +12952,7 @@ func (m *OktaSyncFailure) Reset() { *m = OktaSyncFailure{} } func (m *OktaSyncFailure) String() string { return proto.CompactTextString(m) } func (*OktaSyncFailure) ProtoMessage() {} func (*OktaSyncFailure) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{191} + return fileDescriptor_007ba1c3d6266d56, []int{192} } func (m *OktaSyncFailure) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12939,7 +13002,7 @@ func (m *OktaAssignmentResult) Reset() { *m = OktaAssignmentResult{} } func (m *OktaAssignmentResult) String() string { return proto.CompactTextString(m) } func (*OktaAssignmentResult) ProtoMessage() {} func (*OktaAssignmentResult) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{192} + return fileDescriptor_007ba1c3d6266d56, []int{193} } func (m *OktaAssignmentResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12985,7 +13048,7 @@ func (m *AccessListCreate) Reset() { *m = AccessListCreate{} } func (m *AccessListCreate) String() string { return proto.CompactTextString(m) } func (*AccessListCreate) ProtoMessage() {} func (*AccessListCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{193} + return fileDescriptor_007ba1c3d6266d56, []int{194} } func (m *AccessListCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13031,7 +13094,7 @@ func (m *AccessListUpdate) Reset() { *m = AccessListUpdate{} } func (m *AccessListUpdate) String() string { return proto.CompactTextString(m) } func (*AccessListUpdate) ProtoMessage() {} func (*AccessListUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{194} + return fileDescriptor_007ba1c3d6266d56, []int{195} } func (m *AccessListUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13077,7 +13140,7 @@ func (m *AccessListDelete) Reset() { *m = AccessListDelete{} } func (m *AccessListDelete) String() string { return proto.CompactTextString(m) } func (*AccessListDelete) ProtoMessage() {} func (*AccessListDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{195} + return fileDescriptor_007ba1c3d6266d56, []int{196} } func (m *AccessListDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13125,7 +13188,7 @@ func (m *AccessListMemberCreate) Reset() { *m = AccessListMemberCreate{} func (m *AccessListMemberCreate) String() string { return proto.CompactTextString(m) } func (*AccessListMemberCreate) ProtoMessage() {} func (*AccessListMemberCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{196} + return fileDescriptor_007ba1c3d6266d56, []int{197} } func (m *AccessListMemberCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13173,7 +13236,7 @@ func (m *AccessListMemberUpdate) Reset() { *m = AccessListMemberUpdate{} func (m *AccessListMemberUpdate) String() string { return proto.CompactTextString(m) } func (*AccessListMemberUpdate) ProtoMessage() {} func (*AccessListMemberUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{197} + return fileDescriptor_007ba1c3d6266d56, []int{198} } func (m *AccessListMemberUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13221,7 +13284,7 @@ func (m *AccessListMemberDelete) Reset() { *m = AccessListMemberDelete{} func (m *AccessListMemberDelete) String() string { return proto.CompactTextString(m) } func (*AccessListMemberDelete) ProtoMessage() {} func (*AccessListMemberDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{198} + return fileDescriptor_007ba1c3d6266d56, []int{199} } func (m *AccessListMemberDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13271,7 +13334,7 @@ func (m *AccessListMemberDeleteAllForAccessList) Reset() { func (m *AccessListMemberDeleteAllForAccessList) String() string { return proto.CompactTextString(m) } func (*AccessListMemberDeleteAllForAccessList) ProtoMessage() {} func (*AccessListMemberDeleteAllForAccessList) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{199} + return fileDescriptor_007ba1c3d6266d56, []int{200} } func (m *AccessListMemberDeleteAllForAccessList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13319,7 +13382,7 @@ func (m *AccessListReview) Reset() { *m = AccessListReview{} } func (m *AccessListReview) String() string { return proto.CompactTextString(m) } func (*AccessListReview) ProtoMessage() {} func (*AccessListReview) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{200} + return fileDescriptor_007ba1c3d6266d56, []int{201} } func (m *AccessListReview) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13367,7 +13430,7 @@ func (m *AuditQueryRun) Reset() { *m = AuditQueryRun{} } func (m *AuditQueryRun) String() string { return proto.CompactTextString(m) } func (*AuditQueryRun) ProtoMessage() {} func (*AuditQueryRun) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{201} + return fileDescriptor_007ba1c3d6266d56, []int{202} } func (m *AuditQueryRun) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13417,7 +13480,7 @@ func (m *AuditQueryDetails) Reset() { *m = AuditQueryDetails{} } func (m *AuditQueryDetails) String() string { return proto.CompactTextString(m) } func (*AuditQueryDetails) ProtoMessage() {} func (*AuditQueryDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{202} + return fileDescriptor_007ba1c3d6266d56, []int{203} } func (m *AuditQueryDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13473,7 +13536,7 @@ func (m *SecurityReportRun) Reset() { *m = SecurityReportRun{} } func (m *SecurityReportRun) String() string { return proto.CompactTextString(m) } func (*SecurityReportRun) ProtoMessage() {} func (*SecurityReportRun) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{203} + return fileDescriptor_007ba1c3d6266d56, []int{204} } func (m *SecurityReportRun) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13521,7 +13584,7 @@ func (m *ExternalAuditStorageEnable) Reset() { *m = ExternalAuditStorage func (m *ExternalAuditStorageEnable) String() string { return proto.CompactTextString(m) } func (*ExternalAuditStorageEnable) ProtoMessage() {} func (*ExternalAuditStorageEnable) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{204} + return fileDescriptor_007ba1c3d6266d56, []int{205} } func (m *ExternalAuditStorageEnable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13569,7 +13632,7 @@ func (m *ExternalAuditStorageDisable) Reset() { *m = ExternalAuditStorag func (m *ExternalAuditStorageDisable) String() string { return proto.CompactTextString(m) } func (*ExternalAuditStorageDisable) ProtoMessage() {} func (*ExternalAuditStorageDisable) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{205} + return fileDescriptor_007ba1c3d6266d56, []int{206} } func (m *ExternalAuditStorageDisable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13630,7 +13693,7 @@ func (m *ExternalAuditStorageDetails) Reset() { *m = ExternalAuditStorag func (m *ExternalAuditStorageDetails) String() string { return proto.CompactTextString(m) } func (*ExternalAuditStorageDetails) ProtoMessage() {} func (*ExternalAuditStorageDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{206} + return fileDescriptor_007ba1c3d6266d56, []int{207} } func (m *ExternalAuditStorageDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13688,7 +13751,7 @@ func (m *OktaAccessListSync) Reset() { *m = OktaAccessListSync{} } func (m *OktaAccessListSync) String() string { return proto.CompactTextString(m) } func (*OktaAccessListSync) ProtoMessage() {} func (*OktaAccessListSync) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{207} + return fileDescriptor_007ba1c3d6266d56, []int{208} } func (m *OktaAccessListSync) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13752,7 +13815,7 @@ func (m *OktaUserSync) Reset() { *m = OktaUserSync{} } func (m *OktaUserSync) String() string { return proto.CompactTextString(m) } func (*OktaUserSync) ProtoMessage() {} func (*OktaUserSync) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{208} + return fileDescriptor_007ba1c3d6266d56, []int{209} } func (m *OktaUserSync) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13822,7 +13885,7 @@ func (m *SPIFFESVIDIssued) Reset() { *m = SPIFFESVIDIssued{} } func (m *SPIFFESVIDIssued) String() string { return proto.CompactTextString(m) } func (*SPIFFESVIDIssued) ProtoMessage() {} func (*SPIFFESVIDIssued) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{209} + return fileDescriptor_007ba1c3d6266d56, []int{210} } func (m *SPIFFESVIDIssued) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13873,7 +13936,7 @@ func (m *AuthPreferenceUpdate) Reset() { *m = AuthPreferenceUpdate{} } func (m *AuthPreferenceUpdate) String() string { return proto.CompactTextString(m) } func (*AuthPreferenceUpdate) ProtoMessage() {} func (*AuthPreferenceUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{210} + return fileDescriptor_007ba1c3d6266d56, []int{211} } func (m *AuthPreferenceUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13921,7 +13984,7 @@ func (m *ClusterNetworkingConfigUpdate) Reset() { *m = ClusterNetworking func (m *ClusterNetworkingConfigUpdate) String() string { return proto.CompactTextString(m) } func (*ClusterNetworkingConfigUpdate) ProtoMessage() {} func (*ClusterNetworkingConfigUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{211} + return fileDescriptor_007ba1c3d6266d56, []int{212} } func (m *ClusterNetworkingConfigUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13969,7 +14032,7 @@ func (m *SessionRecordingConfigUpdate) Reset() { *m = SessionRecordingCo func (m *SessionRecordingConfigUpdate) String() string { return proto.CompactTextString(m) } func (*SessionRecordingConfigUpdate) ProtoMessage() {} func (*SessionRecordingConfigUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{212} + return fileDescriptor_007ba1c3d6266d56, []int{213} } func (m *SessionRecordingConfigUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14019,7 +14082,7 @@ func (m *AccessPathChanged) Reset() { *m = AccessPathChanged{} } func (m *AccessPathChanged) String() string { return proto.CompactTextString(m) } func (*AccessPathChanged) ProtoMessage() {} func (*AccessPathChanged) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{213} + return fileDescriptor_007ba1c3d6266d56, []int{214} } func (m *AccessPathChanged) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14073,7 +14136,7 @@ func (m *SpannerRPC) Reset() { *m = SpannerRPC{} } func (m *SpannerRPC) String() string { return proto.CompactTextString(m) } func (*SpannerRPC) ProtoMessage() {} func (*SpannerRPC) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{214} + return fileDescriptor_007ba1c3d6266d56, []int{215} } func (m *SpannerRPC) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14121,7 +14184,7 @@ func (m *AccessGraphSettingsUpdate) Reset() { *m = AccessGraphSettingsUp func (m *AccessGraphSettingsUpdate) String() string { return proto.CompactTextString(m) } func (*AccessGraphSettingsUpdate) ProtoMessage() {} func (*AccessGraphSettingsUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{215} + return fileDescriptor_007ba1c3d6266d56, []int{216} } func (m *AccessGraphSettingsUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14169,7 +14232,7 @@ func (m *SPIFFEFederationCreate) Reset() { *m = SPIFFEFederationCreate{} func (m *SPIFFEFederationCreate) String() string { return proto.CompactTextString(m) } func (*SPIFFEFederationCreate) ProtoMessage() {} func (*SPIFFEFederationCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{216} + return fileDescriptor_007ba1c3d6266d56, []int{217} } func (m *SPIFFEFederationCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14217,7 +14280,7 @@ func (m *SPIFFEFederationDelete) Reset() { *m = SPIFFEFederationDelete{} func (m *SPIFFEFederationDelete) String() string { return proto.CompactTextString(m) } func (*SPIFFEFederationDelete) ProtoMessage() {} func (*SPIFFEFederationDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{217} + return fileDescriptor_007ba1c3d6266d56, []int{218} } func (m *SPIFFEFederationDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14267,7 +14330,7 @@ func (m *AutoUpdateConfigCreate) Reset() { *m = AutoUpdateConfigCreate{} func (m *AutoUpdateConfigCreate) String() string { return proto.CompactTextString(m) } func (*AutoUpdateConfigCreate) ProtoMessage() {} func (*AutoUpdateConfigCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{218} + return fileDescriptor_007ba1c3d6266d56, []int{219} } func (m *AutoUpdateConfigCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14317,7 +14380,7 @@ func (m *AutoUpdateConfigUpdate) Reset() { *m = AutoUpdateConfigUpdate{} func (m *AutoUpdateConfigUpdate) String() string { return proto.CompactTextString(m) } func (*AutoUpdateConfigUpdate) ProtoMessage() {} func (*AutoUpdateConfigUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{219} + return fileDescriptor_007ba1c3d6266d56, []int{220} } func (m *AutoUpdateConfigUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14367,7 +14430,7 @@ func (m *AutoUpdateConfigDelete) Reset() { *m = AutoUpdateConfigDelete{} func (m *AutoUpdateConfigDelete) String() string { return proto.CompactTextString(m) } func (*AutoUpdateConfigDelete) ProtoMessage() {} func (*AutoUpdateConfigDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{220} + return fileDescriptor_007ba1c3d6266d56, []int{221} } func (m *AutoUpdateConfigDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14417,7 +14480,7 @@ func (m *AutoUpdateVersionCreate) Reset() { *m = AutoUpdateVersionCreate func (m *AutoUpdateVersionCreate) String() string { return proto.CompactTextString(m) } func (*AutoUpdateVersionCreate) ProtoMessage() {} func (*AutoUpdateVersionCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{221} + return fileDescriptor_007ba1c3d6266d56, []int{222} } func (m *AutoUpdateVersionCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14467,7 +14530,7 @@ func (m *AutoUpdateVersionUpdate) Reset() { *m = AutoUpdateVersionUpdate func (m *AutoUpdateVersionUpdate) String() string { return proto.CompactTextString(m) } func (*AutoUpdateVersionUpdate) ProtoMessage() {} func (*AutoUpdateVersionUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{222} + return fileDescriptor_007ba1c3d6266d56, []int{223} } func (m *AutoUpdateVersionUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14517,7 +14580,7 @@ func (m *AutoUpdateVersionDelete) Reset() { *m = AutoUpdateVersionDelete func (m *AutoUpdateVersionDelete) String() string { return proto.CompactTextString(m) } func (*AutoUpdateVersionDelete) ProtoMessage() {} func (*AutoUpdateVersionDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{223} + return fileDescriptor_007ba1c3d6266d56, []int{224} } func (m *AutoUpdateVersionDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14567,7 +14630,7 @@ func (m *StaticHostUserCreate) Reset() { *m = StaticHostUserCreate{} } func (m *StaticHostUserCreate) String() string { return proto.CompactTextString(m) } func (*StaticHostUserCreate) ProtoMessage() {} func (*StaticHostUserCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{224} + return fileDescriptor_007ba1c3d6266d56, []int{225} } func (m *StaticHostUserCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14617,7 +14680,7 @@ func (m *StaticHostUserUpdate) Reset() { *m = StaticHostUserUpdate{} } func (m *StaticHostUserUpdate) String() string { return proto.CompactTextString(m) } func (*StaticHostUserUpdate) ProtoMessage() {} func (*StaticHostUserUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{225} + return fileDescriptor_007ba1c3d6266d56, []int{226} } func (m *StaticHostUserUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14667,7 +14730,7 @@ func (m *StaticHostUserDelete) Reset() { *m = StaticHostUserDelete{} } func (m *StaticHostUserDelete) String() string { return proto.CompactTextString(m) } func (*StaticHostUserDelete) ProtoMessage() {} func (*StaticHostUserDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{226} + return fileDescriptor_007ba1c3d6266d56, []int{227} } func (m *StaticHostUserDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14719,7 +14782,7 @@ func (m *CrownJewelCreate) Reset() { *m = CrownJewelCreate{} } func (m *CrownJewelCreate) String() string { return proto.CompactTextString(m) } func (*CrownJewelCreate) ProtoMessage() {} func (*CrownJewelCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{227} + return fileDescriptor_007ba1c3d6266d56, []int{228} } func (m *CrownJewelCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14773,7 +14836,7 @@ func (m *CrownJewelUpdate) Reset() { *m = CrownJewelUpdate{} } func (m *CrownJewelUpdate) String() string { return proto.CompactTextString(m) } func (*CrownJewelUpdate) ProtoMessage() {} func (*CrownJewelUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{228} + return fileDescriptor_007ba1c3d6266d56, []int{229} } func (m *CrownJewelUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14823,7 +14886,7 @@ func (m *CrownJewelDelete) Reset() { *m = CrownJewelDelete{} } func (m *CrownJewelDelete) String() string { return proto.CompactTextString(m) } func (*CrownJewelDelete) ProtoMessage() {} func (*CrownJewelDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{229} + return fileDescriptor_007ba1c3d6266d56, []int{230} } func (m *CrownJewelDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14875,7 +14938,7 @@ func (m *UserTaskCreate) Reset() { *m = UserTaskCreate{} } func (m *UserTaskCreate) String() string { return proto.CompactTextString(m) } func (*UserTaskCreate) ProtoMessage() {} func (*UserTaskCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{230} + return fileDescriptor_007ba1c3d6266d56, []int{231} } func (m *UserTaskCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14931,7 +14994,7 @@ func (m *UserTaskUpdate) Reset() { *m = UserTaskUpdate{} } func (m *UserTaskUpdate) String() string { return proto.CompactTextString(m) } func (*UserTaskUpdate) ProtoMessage() {} func (*UserTaskUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{231} + return fileDescriptor_007ba1c3d6266d56, []int{232} } func (m *UserTaskUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -14977,7 +15040,7 @@ func (m *UserTaskMetadata) Reset() { *m = UserTaskMetadata{} } func (m *UserTaskMetadata) String() string { return proto.CompactTextString(m) } func (*UserTaskMetadata) ProtoMessage() {} func (*UserTaskMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{232} + return fileDescriptor_007ba1c3d6266d56, []int{233} } func (m *UserTaskMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15027,7 +15090,7 @@ func (m *UserTaskDelete) Reset() { *m = UserTaskDelete{} } func (m *UserTaskDelete) String() string { return proto.CompactTextString(m) } func (*UserTaskDelete) ProtoMessage() {} func (*UserTaskDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{233} + return fileDescriptor_007ba1c3d6266d56, []int{234} } func (m *UserTaskDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15081,7 +15144,7 @@ func (m *ContactCreate) Reset() { *m = ContactCreate{} } func (m *ContactCreate) String() string { return proto.CompactTextString(m) } func (*ContactCreate) ProtoMessage() {} func (*ContactCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{234} + return fileDescriptor_007ba1c3d6266d56, []int{235} } func (m *ContactCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15135,7 +15198,7 @@ func (m *ContactDelete) Reset() { *m = ContactDelete{} } func (m *ContactDelete) String() string { return proto.CompactTextString(m) } func (*ContactDelete) ProtoMessage() {} func (*ContactDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{235} + return fileDescriptor_007ba1c3d6266d56, []int{236} } func (m *ContactDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15185,7 +15248,7 @@ func (m *WorkloadIdentityCreate) Reset() { *m = WorkloadIdentityCreate{} func (m *WorkloadIdentityCreate) String() string { return proto.CompactTextString(m) } func (*WorkloadIdentityCreate) ProtoMessage() {} func (*WorkloadIdentityCreate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{236} + return fileDescriptor_007ba1c3d6266d56, []int{237} } func (m *WorkloadIdentityCreate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15235,7 +15298,7 @@ func (m *WorkloadIdentityUpdate) Reset() { *m = WorkloadIdentityUpdate{} func (m *WorkloadIdentityUpdate) String() string { return proto.CompactTextString(m) } func (*WorkloadIdentityUpdate) ProtoMessage() {} func (*WorkloadIdentityUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{237} + return fileDescriptor_007ba1c3d6266d56, []int{238} } func (m *WorkloadIdentityUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15283,7 +15346,7 @@ func (m *WorkloadIdentityDelete) Reset() { *m = WorkloadIdentityDelete{} func (m *WorkloadIdentityDelete) String() string { return proto.CompactTextString(m) } func (*WorkloadIdentityDelete) ProtoMessage() {} func (*WorkloadIdentityDelete) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{238} + return fileDescriptor_007ba1c3d6266d56, []int{239} } func (m *WorkloadIdentityDelete) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15342,7 +15405,7 @@ func (m *GitCommand) Reset() { *m = GitCommand{} } func (m *GitCommand) String() string { return proto.CompactTextString(m) } func (*GitCommand) ProtoMessage() {} func (*GitCommand) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{239} + return fileDescriptor_007ba1c3d6266d56, []int{240} } func (m *GitCommand) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15390,7 +15453,7 @@ func (m *GitCommandAction) Reset() { *m = GitCommandAction{} } func (m *GitCommandAction) String() string { return proto.CompactTextString(m) } func (*GitCommandAction) ProtoMessage() {} func (*GitCommandAction) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{240} + return fileDescriptor_007ba1c3d6266d56, []int{241} } func (m *GitCommandAction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15436,7 +15499,7 @@ func (m *AccessListInvalidMetadata) Reset() { *m = AccessListInvalidMeta func (m *AccessListInvalidMetadata) String() string { return proto.CompactTextString(m) } func (*AccessListInvalidMetadata) ProtoMessage() {} func (*AccessListInvalidMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{241} + return fileDescriptor_007ba1c3d6266d56, []int{242} } func (m *AccessListInvalidMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15484,7 +15547,7 @@ func (m *UserLoginAccessListInvalid) Reset() { *m = UserLoginAccessListI func (m *UserLoginAccessListInvalid) String() string { return proto.CompactTextString(m) } func (*UserLoginAccessListInvalid) ProtoMessage() {} func (*UserLoginAccessListInvalid) Descriptor() ([]byte, []int) { - return fileDescriptor_007ba1c3d6266d56, []int{242} + return fileDescriptor_007ba1c3d6266d56, []int{243} } func (m *UserLoginAccessListInvalid) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15575,6 +15638,7 @@ func init() { proto.RegisterType((*UserDelete)(nil), "events.UserDelete") proto.RegisterType((*UserPasswordChange)(nil), "events.UserPasswordChange") proto.RegisterType((*AccessRequestCreate)(nil), "events.AccessRequestCreate") + proto.RegisterType((*AccessRequestExpire)(nil), "events.AccessRequestExpire") proto.RegisterType((*ResourceID)(nil), "events.ResourceID") proto.RegisterType((*AccessRequestDelete)(nil), "events.AccessRequestDelete") proto.RegisterType((*PortForward)(nil), "events.PortForward") @@ -15788,1109 +15852,1113 @@ func init() { } var fileDescriptor_007ba1c3d6266d56 = []byte{ - // 17620 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6d, 0x74, 0x25, 0xc9, - 0x75, 0x18, 0x86, 0xf7, 0x81, 0x07, 0xe0, 0xe2, 0x63, 0x30, 0x35, 0x5f, 0x3d, 0xb3, 0x33, 0xfb, - 0x76, 0x7b, 0xb9, 0xc3, 0x99, 0xe5, 0x2e, 0x86, 0x3b, 0x3b, 0xbb, 0xcb, 0xfd, 0xe2, 0xf2, 0x01, - 0x0f, 0x18, 0xbc, 0x19, 0x7c, 0x6d, 0x3f, 0xcc, 0x0c, 0x97, 0x14, 0xf9, 0xd4, 0x78, 0x5d, 0x03, - 0xf4, 0xce, 0x7b, 0xdd, 0x4f, 0xdd, 0xfd, 0x06, 0x83, 0x75, 0x3e, 0x44, 0x47, 0x96, 0x25, 0x8b, - 0xa2, 0x19, 0x2a, 0x12, 0x65, 0xd9, 0x49, 0x28, 0x3b, 0x4e, 0x64, 0x59, 0x16, 0x43, 0x47, 0x91, - 0x44, 0x49, 0x8c, 0x25, 0xd3, 0x76, 0x68, 0xe9, 0x58, 0x47, 0x72, 0x12, 0xc7, 0x27, 0x71, 0x40, - 0x47, 0x89, 0xff, 0xe0, 0x24, 0x39, 0x3a, 0x89, 0x4e, 0xcc, 0x38, 0x76, 0x4e, 0x4e, 0xdd, 0xaa, - 0xee, 0xae, 0xfe, 0x7a, 0xf8, 0x5c, 0x61, 0xc1, 0xc1, 0x9f, 0x19, 0xbc, 0x7b, 0x6f, 0xdd, 0xaa, - 0xbe, 0x75, 0xab, 0xea, 0x56, 0xd5, 0xad, 0x7b, 0xe1, 0xaa, 0x47, 0x5b, 0xb4, 0x63, 0x3b, 0xde, - 0xb5, 0x16, 0x5d, 0xd5, 0x9b, 0x1b, 0xd7, 0xbc, 0x8d, 0x0e, 0x75, 0xaf, 0xd1, 0x87, 0xd4, 0xf2, - 0xfc, 0xff, 0x26, 0x3a, 0x8e, 0xed, 0xd9, 0xa4, 0xc4, 0x7f, 0x5d, 0x38, 0xbd, 0x6a, 0xaf, 0xda, - 0x08, 0xba, 0xc6, 0xfe, 0xe2, 0xd8, 0x0b, 0x17, 0x57, 0x6d, 0x7b, 0xb5, 0x45, 0xaf, 0xe1, 0xaf, - 0x95, 0xee, 0xfd, 0x6b, 0xae, 0xe7, 0x74, 0x9b, 0x9e, 0xc0, 0x96, 0xe3, 0x58, 0xcf, 0x6c, 0x53, - 0xd7, 0xd3, 0xdb, 0x1d, 0x41, 0xf0, 0x64, 0x9c, 0x60, 0xdd, 0xd1, 0x3b, 0x1d, 0xea, 0x88, 0xca, - 0x2f, 0x7c, 0x34, 0x68, 0xa7, 0xde, 0x6c, 0x52, 0xd7, 0x6d, 0x99, 0xae, 0x77, 0xed, 0xe1, 0x8b, - 0xd2, 0x2f, 0x41, 0xf8, 0x74, 0xfa, 0x07, 0xe1, 0xbf, 0x82, 0xe4, 0x85, 0x74, 0x12, 0xbf, 0xc6, - 0x58, 0xd5, 0xea, 0x57, 0xf2, 0x30, 0x38, 0x4f, 0x3d, 0xdd, 0xd0, 0x3d, 0x9d, 0x5c, 0x84, 0xfe, - 0x9a, 0x65, 0xd0, 0x47, 0x4a, 0xee, 0xa9, 0xdc, 0x95, 0xc2, 0x64, 0x69, 0x6b, 0xb3, 0x9c, 0xa7, - 0xa6, 0xc6, 0x81, 0xe4, 0x12, 0x14, 0x97, 0x37, 0x3a, 0x54, 0xc9, 0x3f, 0x95, 0xbb, 0x32, 0x34, - 0x39, 0xb4, 0xb5, 0x59, 0xee, 0x47, 0xa1, 0x69, 0x08, 0x26, 0x4f, 0x43, 0xbe, 0x56, 0x55, 0x0a, - 0x88, 0x3c, 0xb9, 0xb5, 0x59, 0x1e, 0xed, 0x9a, 0xc6, 0xf3, 0x76, 0xdb, 0xf4, 0x68, 0xbb, 0xe3, - 0x6d, 0x68, 0xf9, 0x5a, 0x95, 0x5c, 0x86, 0xe2, 0x94, 0x6d, 0x50, 0xa5, 0x88, 0x44, 0x64, 0x6b, - 0xb3, 0x3c, 0xd6, 0xb4, 0x0d, 0x2a, 0x51, 0x21, 0x9e, 0x7c, 0x0a, 0x8a, 0xcb, 0x66, 0x9b, 0x2a, - 0xfd, 0x4f, 0xe5, 0xae, 0x0c, 0x5f, 0xbf, 0x30, 0xc1, 0xc5, 0x37, 0xe1, 0x8b, 0x6f, 0x62, 0xd9, - 0x97, 0xef, 0xe4, 0xf8, 0x77, 0x36, 0xcb, 0x7d, 0x5b, 0x9b, 0xe5, 0x22, 0x13, 0xf9, 0x97, 0xbf, - 0x5b, 0xce, 0x69, 0x58, 0x92, 0xbc, 0x09, 0xc3, 0x53, 0xad, 0xae, 0xeb, 0x51, 0x67, 0x41, 0x6f, - 0x53, 0xa5, 0x84, 0x15, 0x5e, 0xd8, 0xda, 0x2c, 0x9f, 0x6d, 0x72, 0x70, 0xc3, 0xd2, 0xdb, 0x72, - 0xc5, 0x32, 0xb9, 0xfa, 0xeb, 0x39, 0x38, 0x51, 0xa7, 0xae, 0x6b, 0xda, 0x56, 0x20, 0x9b, 0x67, - 0x61, 0x48, 0x80, 0x6a, 0x55, 0x94, 0xcf, 0xd0, 0xe4, 0xc0, 0xd6, 0x66, 0xb9, 0xe0, 0x9a, 0x86, - 0x16, 0x62, 0xc8, 0xc7, 0x61, 0xe0, 0x9e, 0xe9, 0xad, 0xcd, 0xcf, 0x54, 0x84, 0x9c, 0xce, 0x6e, - 0x6d, 0x96, 0xc9, 0xba, 0xe9, 0xad, 0x35, 0xda, 0xf7, 0x75, 0xa9, 0x42, 0x9f, 0x8c, 0xcc, 0xc1, - 0xf8, 0x92, 0x63, 0x3e, 0xd4, 0x3d, 0x7a, 0x9b, 0x6e, 0x2c, 0xd9, 0x2d, 0xb3, 0xb9, 0x21, 0xa4, - 0xf8, 0xd4, 0xd6, 0x66, 0xf9, 0x62, 0x87, 0xe3, 0x1a, 0x0f, 0xe8, 0x46, 0xa3, 0x83, 0x58, 0x89, - 0x49, 0xa2, 0xa4, 0xfa, 0x1b, 0x25, 0x18, 0xb9, 0xe3, 0x52, 0x27, 0x68, 0xf7, 0x65, 0x28, 0xb2, - 0xdf, 0xa2, 0xc9, 0x28, 0xf3, 0xae, 0x4b, 0x1d, 0x59, 0xe6, 0x0c, 0x4f, 0xae, 0x42, 0xff, 0x9c, - 0xbd, 0x6a, 0x5a, 0xa2, 0xd9, 0xa7, 0xb6, 0x36, 0xcb, 0x27, 0x5a, 0x0c, 0x20, 0x51, 0x72, 0x0a, - 0xf2, 0x49, 0x18, 0xa9, 0xb5, 0x99, 0x0e, 0xd9, 0x96, 0xee, 0xd9, 0x8e, 0x68, 0x2d, 0x4a, 0xd7, - 0x94, 0xe0, 0x52, 0xc1, 0x08, 0x3d, 0x79, 0x1d, 0xa0, 0x72, 0xaf, 0xae, 0xd9, 0x2d, 0x5a, 0xd1, - 0x16, 0x84, 0x32, 0x60, 0x69, 0x7d, 0xdd, 0x6d, 0x38, 0x76, 0x8b, 0x36, 0x74, 0x47, 0xae, 0x56, - 0xa2, 0x26, 0xd3, 0x30, 0x56, 0xc1, 0x51, 0xa1, 0xd1, 0x1f, 0xea, 0x52, 0xd7, 0x73, 0x95, 0xfe, - 0xa7, 0x0a, 0x57, 0x86, 0x26, 0x2f, 0x6d, 0x6d, 0x96, 0xcf, 0xf3, 0xf1, 0xd2, 0x70, 0x04, 0x4a, - 0x62, 0x11, 0x2b, 0x44, 0x26, 0x61, 0xb4, 0xf2, 0x7e, 0xd7, 0xa1, 0x35, 0x83, 0x5a, 0x9e, 0xe9, - 0x6d, 0x08, 0x0d, 0xb9, 0xb8, 0xb5, 0x59, 0x56, 0x74, 0x86, 0x68, 0x98, 0x02, 0x23, 0x31, 0x89, - 0x16, 0x21, 0x8b, 0x70, 0xf2, 0xe6, 0xd4, 0x52, 0x9d, 0x3a, 0x0f, 0xcd, 0x26, 0xad, 0x34, 0x9b, - 0x76, 0xd7, 0xf2, 0x94, 0x01, 0xe4, 0xf3, 0xf4, 0xd6, 0x66, 0xf9, 0xd2, 0x6a, 0xb3, 0xd3, 0x70, - 0x39, 0xb6, 0xa1, 0x73, 0xb4, 0xc4, 0x2c, 0x59, 0x96, 0x7c, 0x06, 0x46, 0x97, 0x1d, 0xa6, 0x85, - 0x46, 0x95, 0x32, 0xb8, 0x32, 0x88, 0xfa, 0x7f, 0x76, 0x42, 0xcc, 0x54, 0x1c, 0xea, 0xf7, 0x2c, - 0x6f, 0xac, 0xc7, 0x0b, 0x34, 0x0c, 0xc4, 0xc9, 0x8d, 0x8d, 0xb0, 0x22, 0x14, 0x14, 0xf6, 0xf1, - 0xa6, 0x43, 0x8d, 0x84, 0xb6, 0x0d, 0x61, 0x9b, 0xaf, 0x6e, 0x6d, 0x96, 0x9f, 0x75, 0x04, 0x4d, - 0xa3, 0xa7, 0xda, 0x65, 0xb2, 0x22, 0xd3, 0x30, 0xc8, 0xb4, 0xe9, 0xb6, 0x69, 0x19, 0x0a, 0x3c, - 0x95, 0xbb, 0x32, 0x76, 0x7d, 0xdc, 0x6f, 0xbd, 0x0f, 0x9f, 0x3c, 0xb7, 0xb5, 0x59, 0x3e, 0xc5, - 0x74, 0xb0, 0xf1, 0xc0, 0xb4, 0xe4, 0x29, 0x22, 0x28, 0xca, 0x46, 0xd1, 0xa4, 0xed, 0xe1, 0xd0, - 0x1d, 0x0e, 0x47, 0xd1, 0x8a, 0xed, 0xc5, 0x87, 0xad, 0x4f, 0x46, 0xa6, 0x60, 0x74, 0xd2, 0xf6, - 0x6a, 0x96, 0xeb, 0xe9, 0x56, 0x93, 0xd6, 0xaa, 0xca, 0x08, 0x96, 0x43, 0xb5, 0x60, 0xe5, 0x4c, - 0x81, 0x69, 0x44, 0x26, 0xa5, 0x68, 0x19, 0xf5, 0x5f, 0x16, 0x61, 0x8c, 0xf5, 0x89, 0x34, 0x7c, - 0x2a, 0x6c, 0x26, 0x60, 0x10, 0x56, 0x8b, 0xdb, 0xd1, 0x9b, 0x54, 0x8c, 0x24, 0xfc, 0x0a, 0xcb, - 0x07, 0x4a, 0x3c, 0xe3, 0xf4, 0xe4, 0x2a, 0x0c, 0x72, 0x50, 0xad, 0x2a, 0x06, 0xd7, 0xe8, 0xd6, - 0x66, 0x79, 0xc8, 0x45, 0x58, 0xc3, 0x34, 0xb4, 0x00, 0xcd, 0xb4, 0x9b, 0xff, 0x3d, 0x6b, 0xbb, - 0x1e, 0x63, 0x2e, 0xc6, 0x16, 0x7e, 0x86, 0x28, 0xb0, 0x26, 0x50, 0xb2, 0x76, 0x47, 0x0b, 0x91, - 0xd7, 0x00, 0x38, 0xa4, 0x62, 0x18, 0x8e, 0x18, 0x60, 0xe7, 0xb7, 0x36, 0xcb, 0x67, 0x04, 0x0b, - 0xdd, 0x30, 0xe4, 0xd1, 0x29, 0x11, 0x93, 0x36, 0x8c, 0xf0, 0x5f, 0x73, 0xfa, 0x0a, 0x6d, 0xf1, - 0xd1, 0x35, 0x7c, 0xfd, 0x8a, 0xdf, 0x89, 0x51, 0xe9, 0x4c, 0xc8, 0xa4, 0xd3, 0x96, 0xe7, 0x6c, - 0x4c, 0x96, 0xc5, 0x84, 0x7c, 0x4e, 0x54, 0xd5, 0x42, 0x9c, 0x3c, 0x15, 0xc8, 0x65, 0xd8, 0x3c, - 0x3d, 0x63, 0x3b, 0xeb, 0xba, 0x63, 0x50, 0x63, 0x72, 0x43, 0x9e, 0xa7, 0xef, 0xfb, 0xe0, 0xc6, - 0x8a, 0xac, 0x7a, 0x32, 0x39, 0xeb, 0x74, 0xce, 0xad, 0xde, 0x5d, 0x41, 0x95, 0x1b, 0x48, 0x48, - 0xcb, 0xed, 0xae, 0xc4, 0xd5, 0x2c, 0x5a, 0x86, 0x4d, 0x05, 0x1c, 0x70, 0x97, 0x3a, 0x6c, 0x12, - 0xc7, 0x51, 0x27, 0xa6, 0x02, 0xc1, 0xe4, 0x21, 0xc7, 0x24, 0x79, 0x88, 0x22, 0x17, 0xde, 0x86, - 0x93, 0x09, 0x51, 0x90, 0x71, 0x28, 0x3c, 0xa0, 0x1b, 0x5c, 0x5d, 0x34, 0xf6, 0x27, 0x39, 0x0d, - 0xfd, 0x0f, 0xf5, 0x56, 0x57, 0x2c, 0xa1, 0x1a, 0xff, 0xf1, 0x7a, 0xfe, 0x13, 0x39, 0xb6, 0xe2, - 0x90, 0x29, 0xdb, 0xb2, 0x68, 0xd3, 0x93, 0x17, 0x9d, 0x57, 0x60, 0x68, 0xce, 0x6e, 0xea, 0x2d, - 0xec, 0x47, 0xae, 0x77, 0xca, 0xd6, 0x66, 0xf9, 0x34, 0xeb, 0xc0, 0x89, 0x16, 0xc3, 0x48, 0x6d, - 0x0a, 0x49, 0x99, 0x02, 0x68, 0xb4, 0x6d, 0x7b, 0x14, 0x0b, 0xe6, 0x43, 0x05, 0xc0, 0x82, 0x0e, - 0xa2, 0x64, 0x05, 0x08, 0x89, 0xc9, 0x35, 0x18, 0x5c, 0x62, 0xeb, 0x6c, 0xd3, 0x6e, 0x09, 0xe5, - 0xc3, 0xa5, 0x00, 0xd7, 0x5e, 0x79, 0xac, 0xfa, 0x44, 0xea, 0x2c, 0x8c, 0x4d, 0xb5, 0x4c, 0x6a, - 0x79, 0x72, 0xab, 0xd9, 0x48, 0xae, 0xac, 0x52, 0xcb, 0x93, 0x5b, 0x8d, 0x63, 0x5e, 0x67, 0x50, - 0xb9, 0xd5, 0x01, 0xa9, 0xfa, 0xfb, 0x05, 0x38, 0x7f, 0xbb, 0xbb, 0x42, 0x1d, 0x8b, 0x7a, 0xd4, - 0x15, 0x0b, 0x72, 0xc0, 0x75, 0x01, 0x4e, 0x26, 0x90, 0x82, 0x3b, 0x2e, 0x94, 0x0f, 0x02, 0x64, - 0x43, 0xac, 0xf1, 0xf2, 0x6c, 0x9b, 0x28, 0x4a, 0x66, 0xe1, 0x44, 0x08, 0x64, 0x8d, 0x70, 0x95, - 0x3c, 0x2e, 0x25, 0x4f, 0x6e, 0x6d, 0x96, 0x2f, 0x48, 0xdc, 0x58, 0xb3, 0x65, 0x0d, 0x8e, 0x17, - 0x23, 0xb7, 0x61, 0x3c, 0x04, 0xdd, 0x74, 0xec, 0x6e, 0xc7, 0x55, 0x0a, 0xc8, 0xaa, 0xbc, 0xb5, - 0x59, 0x7e, 0x42, 0x62, 0xb5, 0x8a, 0x48, 0x79, 0x01, 0x8f, 0x17, 0x24, 0x3f, 0x92, 0x93, 0xb9, - 0x89, 0x51, 0x58, 0xc4, 0x51, 0xf8, 0xaa, 0x3f, 0x0a, 0x33, 0x85, 0x34, 0x11, 0x2f, 0x29, 0x06, - 0x65, 0xac, 0x19, 0x89, 0x41, 0x99, 0xa8, 0xf1, 0xc2, 0x14, 0x9c, 0x49, 0xe5, 0xb5, 0x2b, 0xad, - 0xfe, 0x17, 0x05, 0x99, 0xcb, 0x92, 0x6d, 0x04, 0x9d, 0xb9, 0x28, 0x77, 0xe6, 0x92, 0x6d, 0xe0, - 0x54, 0x9f, 0x0b, 0xd7, 0x4e, 0xa9, 0xb1, 0x1d, 0xdb, 0x88, 0xcf, 0xfa, 0xc9, 0xb2, 0xe4, 0xf3, - 0x70, 0x36, 0x01, 0xe4, 0xd3, 0x35, 0xd7, 0xfe, 0xcb, 0x5b, 0x9b, 0x65, 0x35, 0x85, 0x6b, 0x7c, - 0xf6, 0xce, 0xe0, 0x42, 0x74, 0x38, 0x27, 0x49, 0xdd, 0xb6, 0x3c, 0xdd, 0xb4, 0x84, 0x71, 0xc9, - 0x47, 0xc9, 0x47, 0xb7, 0x36, 0xcb, 0xcf, 0xc8, 0x3a, 0xe8, 0xd3, 0xc4, 0x1b, 0x9f, 0xc5, 0x87, - 0x18, 0xa0, 0xa4, 0xa0, 0x6a, 0x6d, 0x7d, 0xd5, 0xb7, 0x98, 0xaf, 0x6c, 0x6d, 0x96, 0x3f, 0x92, - 0x5a, 0x87, 0xc9, 0xa8, 0xe4, 0x15, 0x3a, 0x8b, 0x13, 0xd1, 0x80, 0x84, 0xb8, 0x05, 0xdb, 0xa0, - 0xf8, 0x0d, 0xfd, 0xc8, 0x5f, 0xdd, 0xda, 0x2c, 0x3f, 0x29, 0xf1, 0xb7, 0x6c, 0x83, 0xc6, 0x9b, - 0x9f, 0x52, 0x5a, 0xfd, 0xf5, 0x02, 0x3c, 0x59, 0xaf, 0xcc, 0xcf, 0xd5, 0x0c, 0xdf, 0xa4, 0x59, - 0x72, 0xec, 0x87, 0xa6, 0x21, 0x8d, 0xde, 0x15, 0x38, 0x17, 0x43, 0x4d, 0xa3, 0x15, 0x15, 0x18, - 0xd3, 0xf8, 0x6d, 0xbe, 0xb9, 0xd4, 0x11, 0x34, 0x0d, 0x6e, 0x6a, 0x45, 0x17, 0xed, 0x2c, 0x46, - 0xac, 0x8f, 0x62, 0xa8, 0xfa, 0x9a, 0xed, 0x78, 0xcd, 0xae, 0x27, 0x94, 0x00, 0xfb, 0x28, 0x51, - 0x87, 0x2b, 0x88, 0x7a, 0x54, 0xe1, 0xf3, 0x21, 0x3f, 0x9e, 0x83, 0xf1, 0x8a, 0xe7, 0x39, 0xe6, - 0x4a, 0xd7, 0xa3, 0xf3, 0x7a, 0xa7, 0x63, 0x5a, 0xab, 0x38, 0xd6, 0x87, 0xaf, 0xbf, 0x19, 0xac, - 0x91, 0x3d, 0x25, 0x31, 0x11, 0x2f, 0x2e, 0x0d, 0x51, 0xdd, 0x47, 0x35, 0xda, 0x1c, 0x27, 0x0f, - 0xd1, 0x78, 0x39, 0x36, 0x44, 0x53, 0x79, 0xed, 0x6a, 0x88, 0x7e, 0xa5, 0x00, 0x17, 0x17, 0x1f, - 0x78, 0xba, 0x46, 0x5d, 0xbb, 0xeb, 0x34, 0xa9, 0x7b, 0xa7, 0x63, 0xe8, 0x1e, 0x0d, 0x47, 0x6a, - 0x19, 0xfa, 0x2b, 0x86, 0x41, 0x0d, 0x64, 0xd7, 0xcf, 0xb7, 0x7d, 0x3a, 0x03, 0x68, 0x1c, 0x4e, - 0x9e, 0x85, 0x01, 0x51, 0x06, 0xb9, 0xf7, 0x4f, 0x0e, 0x6f, 0x6d, 0x96, 0x07, 0xba, 0x1c, 0xa4, - 0xf9, 0x38, 0x46, 0x56, 0xa5, 0x2d, 0xca, 0xc8, 0x0a, 0x21, 0x99, 0xc1, 0x41, 0x9a, 0x8f, 0x23, - 0xef, 0xc0, 0x18, 0xb2, 0x0d, 0xda, 0x23, 0xe6, 0xbe, 0xd3, 0xbe, 0x74, 0xe5, 0xc6, 0xf2, 0xa5, - 0x09, 0x5b, 0xd3, 0x70, 0xfc, 0x02, 0x5a, 0x8c, 0x01, 0xb9, 0x07, 0xe3, 0xa2, 0x11, 0x21, 0xd3, - 0xfe, 0x1e, 0x4c, 0xcf, 0x6c, 0x6d, 0x96, 0x4f, 0x8a, 0xf6, 0x4b, 0x6c, 0x13, 0x4c, 0x18, 0x63, - 0xd1, 0xec, 0x90, 0x71, 0x69, 0x3b, 0xc6, 0xe2, 0x8b, 0x65, 0xc6, 0x71, 0x26, 0xea, 0xbb, 0x30, - 0x22, 0x17, 0x24, 0x67, 0x71, 0x6b, 0xcd, 0xc7, 0x09, 0x6e, 0xca, 0x4d, 0x03, 0xf7, 0xd3, 0x2f, - 0xc2, 0x70, 0x95, 0xba, 0x4d, 0xc7, 0xec, 0x30, 0xab, 0x41, 0x28, 0xf9, 0x89, 0xad, 0xcd, 0xf2, - 0xb0, 0x11, 0x82, 0x35, 0x99, 0x46, 0xfd, 0xbf, 0x73, 0x70, 0x96, 0xf1, 0xae, 0xb8, 0xae, 0xb9, - 0x6a, 0xb5, 0xe5, 0x65, 0xfb, 0x79, 0x28, 0xd5, 0xb1, 0x3e, 0x51, 0xd3, 0xe9, 0xad, 0xcd, 0xf2, - 0x38, 0x6f, 0x81, 0xa4, 0x87, 0x82, 0x26, 0xd8, 0x57, 0xe6, 0xb7, 0xd9, 0x57, 0x32, 0x93, 0xd6, - 0xd3, 0x1d, 0xcf, 0xb4, 0x56, 0xeb, 0x9e, 0xee, 0x75, 0xdd, 0x88, 0x49, 0x2b, 0x30, 0x0d, 0x17, - 0x51, 0x11, 0x93, 0x36, 0x52, 0x88, 0xbc, 0x0d, 0x23, 0xd3, 0x96, 0x11, 0x32, 0xe1, 0x13, 0xe2, - 0x13, 0xcc, 0xd2, 0xa4, 0x08, 0x4f, 0xb2, 0x88, 0x14, 0x50, 0xff, 0x56, 0x0e, 0x14, 0xbe, 0x09, - 0x9c, 0x33, 0x5d, 0x6f, 0x9e, 0xb6, 0x57, 0xa4, 0xd9, 0x69, 0xc6, 0xdf, 0x55, 0x32, 0x9c, 0xb4, - 0x16, 0xa1, 0x29, 0x20, 0x76, 0x95, 0x2d, 0xd3, 0x4d, 0x6c, 0x3f, 0x62, 0xa5, 0x48, 0x0d, 0x06, - 0x38, 0x67, 0x6e, 0x4b, 0x0c, 0x5f, 0x57, 0x7c, 0x45, 0x88, 0x57, 0xcd, 0x95, 0xa1, 0xcd, 0x89, - 0xe5, 0x0d, 0x8d, 0x28, 0xaf, 0x7e, 0xad, 0x00, 0xe3, 0xf1, 0x42, 0xe4, 0x1e, 0x0c, 0xde, 0xb2, - 0x4d, 0x8b, 0x1a, 0x8b, 0x16, 0xb6, 0xb0, 0xf7, 0xe1, 0x88, 0x6f, 0x8b, 0x9f, 0x7a, 0x0f, 0xcb, - 0x34, 0x64, 0x0b, 0x16, 0xcf, 0x4a, 0x02, 0x66, 0xe4, 0x33, 0x30, 0xc4, 0x6c, 0xc0, 0x87, 0xc8, - 0x39, 0xbf, 0x2d, 0xe7, 0xa7, 0x04, 0xe7, 0xd3, 0x0e, 0x2f, 0x94, 0x64, 0x1d, 0xb2, 0x63, 0x7a, - 0xa5, 0x51, 0xdd, 0xb5, 0x2d, 0xd1, 0xf3, 0xa8, 0x57, 0x0e, 0x42, 0x64, 0xbd, 0xe2, 0x34, 0xcc, - 0x74, 0xe5, 0x1f, 0x8b, 0xdd, 0x20, 0xed, 0x5d, 0xb8, 0xac, 0xe2, 0x3d, 0x20, 0x11, 0x13, 0x0b, - 0x4e, 0x08, 0x81, 0xae, 0x99, 0x1d, 0xb4, 0xfa, 0x71, 0x5d, 0x1b, 0xbb, 0x7e, 0x79, 0xc2, 0x3f, - 0x14, 0x9b, 0x90, 0x8e, 0xd4, 0x1e, 0xbe, 0x38, 0x31, 0x1f, 0x90, 0xe3, 0xce, 0x14, 0x75, 0x32, - 0xc6, 0x42, 0xee, 0xed, 0x76, 0x84, 0x5c, 0xfd, 0xd1, 0x3c, 0xbc, 0x10, 0x76, 0x91, 0x46, 0x1f, - 0x9a, 0x74, 0x3d, 0xe4, 0x28, 0xf6, 0xc8, 0x6c, 0x88, 0xb9, 0x53, 0x6b, 0xba, 0xb5, 0x4a, 0x0d, - 0x72, 0x15, 0xfa, 0x35, 0xbb, 0x45, 0x5d, 0x25, 0x87, 0xe6, 0x21, 0x4e, 0x5f, 0x0e, 0x03, 0xc8, - 0x87, 0x2c, 0x48, 0x41, 0x6c, 0x28, 0x2d, 0x3b, 0xba, 0xe9, 0xf9, 0x9a, 0x54, 0x49, 0x6a, 0xd2, - 0x0e, 0x6a, 0x9c, 0xe0, 0x3c, 0xf8, 0x1a, 0x83, 0x82, 0xf7, 0x10, 0x20, 0x0b, 0x9e, 0x93, 0x5c, - 0x78, 0x0d, 0x86, 0x25, 0xe2, 0x5d, 0x2d, 0x22, 0xdf, 0x2c, 0xca, 0x63, 0xcb, 0x6f, 0x96, 0x18, - 0x5b, 0xd7, 0xd8, 0x98, 0x70, 0x5d, 0x66, 0xc5, 0xf0, 0x41, 0x25, 0x34, 0x1f, 0x41, 0x51, 0xcd, - 0x47, 0x10, 0x79, 0x09, 0x06, 0x39, 0x8b, 0x60, 0xbf, 0x8c, 0x7b, 0x6d, 0x07, 0x61, 0x51, 0x53, - 0x20, 0x20, 0x24, 0xbf, 0x98, 0x83, 0x4b, 0x3d, 0x25, 0x81, 0xca, 0x37, 0x7c, 0xfd, 0xe5, 0x3d, - 0x89, 0x71, 0xf2, 0x85, 0xad, 0xcd, 0xf2, 0x55, 0x49, 0x33, 0x1c, 0x89, 0xa6, 0xd1, 0xe4, 0x44, - 0x52, 0xbb, 0x7a, 0x37, 0x85, 0x19, 0xab, 0xbc, 0xd2, 0x19, 0x3c, 0xaa, 0xb2, 0x9a, 0x1b, 0x7e, - 0x23, 0x8b, 0xa1, 0xb1, 0x2a, 0xbe, 0xf7, 0xbe, 0x4f, 0x92, 0x52, 0x4d, 0x06, 0x17, 0xd2, 0x84, - 0x73, 0x1c, 0x53, 0xd5, 0x37, 0x16, 0xef, 0xcf, 0xdb, 0x96, 0xb7, 0xe6, 0x57, 0xd0, 0x2f, 0x9f, - 0xf5, 0x60, 0x05, 0x86, 0xbe, 0xd1, 0xb0, 0xef, 0x37, 0xda, 0x8c, 0x2a, 0xa5, 0x8e, 0x2c, 0x4e, - 0x6c, 0x62, 0x17, 0x63, 0xdc, 0x9f, 0xf2, 0x4a, 0xe1, 0x49, 0x9c, 0x3f, 0x2f, 0x24, 0x27, 0xb8, - 0x58, 0x21, 0xb5, 0x06, 0x23, 0x73, 0x76, 0xf3, 0x41, 0xa0, 0x2e, 0xaf, 0x41, 0x69, 0x59, 0x77, - 0x56, 0xa9, 0x87, 0xb2, 0x18, 0xbe, 0x7e, 0x72, 0x82, 0x9f, 0x6e, 0x33, 0x22, 0x8e, 0x98, 0x1c, - 0x13, 0xb3, 0x4f, 0xc9, 0xc3, 0xdf, 0x9a, 0x28, 0xa0, 0x7e, 0xb7, 0x1f, 0x46, 0xc4, 0x49, 0x2c, - 0xae, 0x1e, 0xe4, 0xf5, 0xf0, 0x6c, 0x5b, 0x4c, 0x97, 0xc1, 0x69, 0x54, 0x70, 0x8a, 0x36, 0xc2, - 0x98, 0xfd, 0xc1, 0x66, 0x39, 0xb7, 0xb5, 0x59, 0xee, 0xd3, 0x06, 0xa5, 0x4d, 0x6c, 0xb8, 0xbe, - 0x49, 0x0b, 0xba, 0x7c, 0xb6, 0x1a, 0x2b, 0xcb, 0xd7, 0xbb, 0xb7, 0x61, 0x40, 0xb4, 0x41, 0x68, - 0xdc, 0xb9, 0xf0, 0xec, 0x24, 0x72, 0xa2, 0x1c, 0x2b, 0xed, 0x97, 0x22, 0x6f, 0x42, 0x89, 0x9f, - 0x25, 0x08, 0x01, 0x9c, 0x4d, 0x3f, 0x7b, 0x89, 0x15, 0x17, 0x65, 0xc8, 0x2c, 0x40, 0x78, 0x8e, - 0x10, 0x1c, 0xa0, 0x0b, 0x0e, 0xc9, 0x13, 0x86, 0x18, 0x17, 0xa9, 0x2c, 0x79, 0x05, 0x46, 0x96, - 0xa9, 0xd3, 0x36, 0x2d, 0xbd, 0x55, 0x37, 0xdf, 0xf7, 0xcf, 0xd0, 0x71, 0xa1, 0x77, 0xcd, 0xf7, - 0xe5, 0x91, 0x1b, 0xa1, 0x23, 0x9f, 0x4b, 0xdb, 0xa7, 0x0f, 0x60, 0x43, 0x9e, 0xde, 0x76, 0x03, - 0x1b, 0x6b, 0x4f, 0xca, 0xb6, 0xfd, 0x1d, 0x18, 0x8d, 0x6c, 0xd1, 0xc4, 0x21, 0xe9, 0xa5, 0x24, - 0x6b, 0x69, 0xbf, 0x19, 0x63, 0x1b, 0xe5, 0xc0, 0x34, 0xb9, 0x66, 0x99, 0x9e, 0xa9, 0xb7, 0xa6, - 0xec, 0x76, 0x5b, 0xb7, 0x0c, 0x65, 0x28, 0xd4, 0x64, 0x93, 0x63, 0x1a, 0x4d, 0x8e, 0x92, 0x35, - 0x39, 0x5a, 0x88, 0xdc, 0x86, 0x71, 0xd1, 0x87, 0x1a, 0x6d, 0xda, 0x0e, 0xb3, 0x3d, 0xf0, 0x0c, - 0x54, 0x1c, 0x03, 0xb8, 0x1c, 0xd7, 0x70, 0x7c, 0xa4, 0x6c, 0xdc, 0xc7, 0x0b, 0xde, 0x2a, 0x0e, - 0x0e, 0x8f, 0x8f, 0xc4, 0x8f, 0xad, 0xd5, 0xbf, 0x51, 0x80, 0x61, 0x41, 0xca, 0x96, 0xee, 0x63, - 0x05, 0xdf, 0x8f, 0x82, 0xa7, 0x2a, 0x6a, 0xe9, 0xa0, 0x14, 0x55, 0xfd, 0x62, 0x3e, 0x98, 0x8d, - 0x96, 0x1c, 0xd3, 0xda, 0xdf, 0x6c, 0x74, 0x19, 0x60, 0x6a, 0xad, 0x6b, 0x3d, 0xe0, 0xd7, 0x73, - 0xf9, 0xf0, 0x7a, 0xae, 0x69, 0x6a, 0x12, 0x86, 0x5c, 0x82, 0x62, 0x95, 0xf1, 0x67, 0x3d, 0x33, - 0x32, 0x39, 0xf4, 0x1d, 0xce, 0x29, 0xf7, 0x82, 0x86, 0x60, 0xb6, 0x99, 0x9b, 0xdc, 0xf0, 0x28, - 0x37, 0x9f, 0x0b, 0x7c, 0x33, 0xb7, 0xc2, 0x00, 0x1a, 0x87, 0x93, 0x1b, 0x70, 0xb2, 0x4a, 0x5b, - 0xfa, 0xc6, 0xbc, 0xd9, 0x6a, 0x99, 0x2e, 0x6d, 0xda, 0x96, 0xe1, 0xa2, 0x90, 0x45, 0x75, 0x6d, - 0x57, 0x4b, 0x12, 0x10, 0x15, 0x4a, 0x8b, 0xf7, 0xef, 0xbb, 0xd4, 0x43, 0xf1, 0x15, 0x26, 0x81, - 0x4d, 0xce, 0x36, 0x42, 0x34, 0x81, 0x51, 0xbf, 0x9e, 0x63, 0xbb, 0x25, 0xf7, 0x81, 0x67, 0x77, - 0x02, 0x2d, 0xdf, 0x97, 0x48, 0xae, 0x86, 0x76, 0x45, 0x1e, 0xbf, 0xf6, 0x84, 0xf8, 0xda, 0x01, - 0x61, 0x5b, 0x84, 0x16, 0x45, 0xea, 0x57, 0x15, 0xb6, 0xf9, 0x2a, 0xf5, 0x8f, 0xf3, 0x70, 0x4e, - 0xb4, 0x78, 0xaa, 0x65, 0x76, 0x56, 0x6c, 0xdd, 0x31, 0x34, 0xda, 0xa4, 0xe6, 0x43, 0x7a, 0x34, - 0x07, 0x5e, 0x74, 0xe8, 0x14, 0xf7, 0x31, 0x74, 0xae, 0xe3, 0xc6, 0x93, 0x49, 0x06, 0x0f, 0x98, - 0xb9, 0x51, 0x31, 0xbe, 0xb5, 0x59, 0x1e, 0x31, 0x38, 0x18, 0xaf, 0x18, 0x34, 0x99, 0x88, 0x29, - 0xc9, 0x1c, 0xb5, 0x56, 0xbd, 0x35, 0x54, 0x92, 0x7e, 0xae, 0x24, 0x2d, 0x84, 0x68, 0x02, 0xa3, - 0xfe, 0xef, 0x79, 0x38, 0x1d, 0x17, 0x79, 0x9d, 0x5a, 0xc6, 0xb1, 0xbc, 0x3f, 0x18, 0x79, 0xff, - 0x49, 0x01, 0x9e, 0x10, 0x65, 0xea, 0x6b, 0xba, 0x43, 0x8d, 0xaa, 0xe9, 0xd0, 0xa6, 0x67, 0x3b, - 0x1b, 0x47, 0xd8, 0x80, 0x3a, 0x38, 0xb1, 0xdf, 0x80, 0x92, 0x38, 0x6e, 0xe0, 0xeb, 0xcc, 0x58, - 0xd0, 0x12, 0x84, 0x26, 0x56, 0x28, 0x7e, 0x54, 0x11, 0xeb, 0xac, 0xd2, 0x4e, 0x3a, 0xeb, 0x13, - 0x30, 0x1a, 0x88, 0x1e, 0x37, 0xbe, 0x03, 0xa1, 0xb5, 0x65, 0xf8, 0x08, 0xdc, 0xfb, 0x6a, 0x51, - 0x42, 0xac, 0xcd, 0x07, 0xd4, 0xaa, 0x68, 0x0d, 0x8d, 0x8a, 0xda, 0x82, 0x72, 0xa6, 0xa1, 0xc9, - 0x44, 0xea, 0x66, 0x11, 0x2e, 0xa4, 0x77, 0xbb, 0x46, 0x75, 0xe3, 0xb8, 0xd7, 0xbf, 0x2f, 0x7b, - 0x9d, 0x3c, 0x0d, 0xc5, 0x25, 0xdd, 0x5b, 0x13, 0xd7, 0xfd, 0x78, 0x07, 0x7d, 0xdf, 0x6c, 0xd1, - 0x46, 0x47, 0xf7, 0xd6, 0x34, 0x44, 0x49, 0x73, 0x06, 0x20, 0xc7, 0x94, 0x39, 0x43, 0x5a, 0xec, - 0x87, 0x9f, 0xca, 0x5d, 0x29, 0xa6, 0x2e, 0xf6, 0xdf, 0x2d, 0x66, 0xcd, 0x2b, 0xf7, 0x1c, 0xd3, - 0xa3, 0xc7, 0x1a, 0x76, 0xac, 0x61, 0xfb, 0xd4, 0xb0, 0x7f, 0x9c, 0x87, 0xd1, 0x60, 0xd3, 0xf4, - 0x1e, 0x6d, 0x1e, 0xce, 0x5a, 0x15, 0x6e, 0x65, 0x0a, 0xfb, 0xde, 0xca, 0xec, 0x47, 0xa1, 0xd4, - 0xe0, 0x88, 0x95, 0x9b, 0x06, 0x28, 0x31, 0x7e, 0xc4, 0x1a, 0x1c, 0xac, 0x3e, 0x0d, 0x03, 0xf3, - 0xfa, 0x23, 0xb3, 0xdd, 0x6d, 0x0b, 0x2b, 0x1d, 0xdd, 0xd7, 0xda, 0xfa, 0x23, 0xcd, 0x87, 0xab, - 0xff, 0x6d, 0x0e, 0xc6, 0x84, 0x50, 0x05, 0xf3, 0x7d, 0x49, 0x35, 0x94, 0x4e, 0x7e, 0xdf, 0xd2, - 0x29, 0xec, 0x5d, 0x3a, 0xea, 0x5f, 0x29, 0x80, 0x32, 0x63, 0xb6, 0xe8, 0xb2, 0xa3, 0x5b, 0xee, - 0x7d, 0xea, 0x88, 0xed, 0xf4, 0x34, 0x63, 0xb5, 0xaf, 0x0f, 0x94, 0xa6, 0x94, 0xfc, 0x9e, 0xa6, - 0x94, 0x8f, 0xc1, 0x90, 0x68, 0x4c, 0xe0, 0x3a, 0x89, 0xa3, 0xc6, 0xf1, 0x81, 0x5a, 0x88, 0x67, - 0xc4, 0x95, 0x4e, 0xc7, 0xb1, 0x1f, 0x52, 0x87, 0xdf, 0x8a, 0x09, 0x62, 0xdd, 0x07, 0x6a, 0x21, - 0x5e, 0xe2, 0x4c, 0x7d, 0x7b, 0x51, 0xe6, 0x4c, 0x1d, 0x2d, 0xc4, 0x93, 0x2b, 0x30, 0x38, 0x67, - 0x37, 0x75, 0x14, 0x34, 0x9f, 0x56, 0x46, 0xb6, 0x36, 0xcb, 0x83, 0x2d, 0x01, 0xd3, 0x02, 0x2c, - 0xa3, 0xac, 0xda, 0xeb, 0x56, 0xcb, 0xd6, 0xb9, 0xb3, 0xcd, 0x20, 0xa7, 0x34, 0x04, 0x4c, 0x0b, - 0xb0, 0x8c, 0x92, 0xc9, 0x1c, 0x9d, 0x98, 0x06, 0x43, 0x9e, 0xf7, 0x05, 0x4c, 0x0b, 0xb0, 0xea, - 0xd7, 0x8b, 0x4c, 0x7b, 0x5d, 0xf3, 0xfd, 0xc7, 0x7e, 0x5d, 0x08, 0x07, 0x4c, 0xff, 0x1e, 0x06, - 0xcc, 0x63, 0x73, 0x60, 0xa7, 0xfe, 0xcb, 0x01, 0x00, 0x21, 0xfd, 0xe9, 0xe3, 0xcd, 0xe1, 0xfe, - 0xb4, 0xa6, 0x0a, 0x27, 0xa7, 0xad, 0x35, 0xdd, 0x6a, 0x52, 0x23, 0x3c, 0xb6, 0x2c, 0xe1, 0xd0, - 0x46, 0xa7, 0x4b, 0x2a, 0x90, 0xe1, 0xb9, 0xa5, 0x96, 0x2c, 0x40, 0x5e, 0x84, 0xe1, 0x9a, 0xe5, - 0x51, 0x47, 0x6f, 0x7a, 0xe6, 0x43, 0x2a, 0xa6, 0x06, 0xbc, 0x89, 0x36, 0x43, 0xb0, 0x26, 0xd3, - 0x90, 0x1b, 0x30, 0xb2, 0xa4, 0x3b, 0x9e, 0xd9, 0x34, 0x3b, 0xba, 0xe5, 0xb9, 0xca, 0x20, 0xce, - 0x68, 0x68, 0x61, 0x74, 0x24, 0xb8, 0x16, 0xa1, 0x22, 0x9f, 0x83, 0x21, 0xdc, 0x9a, 0xa2, 0x7f, - 0xf8, 0xd0, 0xb6, 0x17, 0x95, 0xcf, 0x84, 0xee, 0x88, 0xfc, 0xf4, 0x15, 0x6f, 0x9c, 0xe3, 0x77, - 0x95, 0x01, 0x47, 0xf2, 0x69, 0x18, 0x98, 0xb6, 0x0c, 0x64, 0x0e, 0xdb, 0x32, 0x57, 0x05, 0xf3, - 0xb3, 0x21, 0x73, 0xbb, 0x13, 0xe3, 0xed, 0xb3, 0x4b, 0x1f, 0x65, 0xc3, 0x1f, 0xdc, 0x28, 0x1b, - 0xf9, 0x00, 0x8e, 0xc5, 0x47, 0x0f, 0xea, 0x58, 0x7c, 0x6c, 0x8f, 0xc7, 0xe2, 0xea, 0xfb, 0x30, - 0x3c, 0xb9, 0x34, 0x13, 0x8c, 0xde, 0xf3, 0x50, 0x58, 0x12, 0x9e, 0x11, 0x45, 0x6e, 0xcf, 0x74, - 0x4c, 0x43, 0x63, 0x30, 0x72, 0x15, 0x06, 0xa7, 0xd0, 0xdd, 0x4e, 0xdc, 0x22, 0x16, 0xf9, 0xfa, - 0xd7, 0x44, 0x18, 0x7a, 0xdd, 0xfa, 0x68, 0xf2, 0x2c, 0x0c, 0x2c, 0x39, 0xf6, 0xaa, 0xa3, 0xb7, - 0xc5, 0x1a, 0x8c, 0xae, 0x29, 0x1d, 0x0e, 0xd2, 0x7c, 0x9c, 0xfa, 0x53, 0x39, 0xdf, 0x6c, 0x67, - 0x25, 0xea, 0x5d, 0x3c, 0x9a, 0xc7, 0xba, 0x07, 0x79, 0x09, 0x97, 0x83, 0x34, 0x1f, 0x47, 0xae, - 0x42, 0xff, 0xb4, 0xe3, 0xd8, 0x8e, 0xec, 0x53, 0x4f, 0x19, 0x40, 0xbe, 0xee, 0x45, 0x0a, 0xf2, - 0x2a, 0x0c, 0xf3, 0x39, 0x87, 0x9f, 0x68, 0x16, 0x7a, 0xdd, 0x94, 0xca, 0x94, 0xea, 0xb7, 0x0b, - 0x92, 0xcd, 0xc6, 0x25, 0xfe, 0x18, 0xde, 0x0a, 0xbc, 0x04, 0x85, 0xc9, 0xa5, 0x19, 0x31, 0x01, - 0x9e, 0xf2, 0x8b, 0x4a, 0xaa, 0x12, 0x2b, 0xc7, 0xa8, 0xc9, 0x45, 0x28, 0x2e, 0x31, 0xf5, 0x29, - 0xa1, 0x7a, 0x0c, 0x6e, 0x6d, 0x96, 0x8b, 0x1d, 0xa6, 0x3f, 0x08, 0x45, 0x2c, 0xdb, 0xcc, 0xf0, - 0x1d, 0x13, 0xc7, 0x86, 0xfb, 0x98, 0x8b, 0x50, 0xac, 0x38, 0xab, 0x0f, 0xc5, 0xac, 0x85, 0x58, - 0xdd, 0x59, 0x7d, 0xa8, 0x21, 0x94, 0x5c, 0x03, 0xd0, 0xa8, 0xd7, 0x75, 0x2c, 0x7c, 0xee, 0x32, - 0x84, 0xe7, 0x6f, 0x38, 0x1b, 0x3a, 0x08, 0x6d, 0x34, 0x6d, 0x83, 0x6a, 0x12, 0x89, 0xfa, 0xd7, - 0xc3, 0x8b, 0x9d, 0xaa, 0xe9, 0x3e, 0x38, 0xee, 0xc2, 0x5d, 0x74, 0xa1, 0x2e, 0x8e, 0x38, 0x93, - 0x9d, 0x54, 0x86, 0xfe, 0x99, 0x96, 0xbe, 0xea, 0x62, 0x1f, 0x0a, 0xdf, 0xb5, 0xfb, 0x0c, 0xa0, - 0x71, 0x78, 0xac, 0x9f, 0x06, 0xb7, 0xef, 0xa7, 0xaf, 0xf6, 0x07, 0xa3, 0x6d, 0x81, 0x7a, 0xeb, - 0xb6, 0x73, 0xdc, 0x55, 0x3b, 0xed, 0xaa, 0xcb, 0x30, 0x50, 0x77, 0x9a, 0xd2, 0xd1, 0x05, 0xee, - 0x07, 0x5c, 0xa7, 0xc9, 0x8f, 0x2d, 0x7c, 0x24, 0xa3, 0xab, 0xba, 0x1e, 0xd2, 0x0d, 0x84, 0x74, - 0x86, 0xeb, 0x09, 0x3a, 0x81, 0x14, 0x74, 0x4b, 0xb6, 0xe3, 0x89, 0x8e, 0x0b, 0xe8, 0x3a, 0xb6, - 0xe3, 0x69, 0x3e, 0x92, 0x7c, 0x0c, 0x60, 0x79, 0x6a, 0xc9, 0x77, 0xee, 0x1f, 0x0a, 0x7d, 0x0f, - 0x85, 0x57, 0xbf, 0x26, 0xa1, 0xc9, 0x32, 0x0c, 0x2d, 0x76, 0xa8, 0xc3, 0xb7, 0x42, 0xfc, 0x01, - 0xcb, 0x47, 0x63, 0xa2, 0x15, 0xfd, 0x3e, 0x21, 0xfe, 0x0f, 0xc8, 0xf9, 0xfa, 0x62, 0xfb, 0x3f, - 0xb5, 0x90, 0x11, 0x79, 0x15, 0x4a, 0x15, 0x6e, 0xe7, 0x0d, 0x23, 0xcb, 0x40, 0x64, 0xb8, 0x05, - 0xe5, 0x28, 0xbe, 0x67, 0xd7, 0xf1, 0x6f, 0x4d, 0x90, 0xab, 0x57, 0x61, 0x3c, 0x5e, 0x0d, 0x19, - 0x86, 0x81, 0xa9, 0xc5, 0x85, 0x85, 0xe9, 0xa9, 0xe5, 0xf1, 0x3e, 0x32, 0x08, 0xc5, 0xfa, 0xf4, - 0x42, 0x75, 0x3c, 0xa7, 0xfe, 0x92, 0x34, 0x83, 0x30, 0xd5, 0x3a, 0xbe, 0x1a, 0xde, 0xd7, 0x7d, - 0xcb, 0x38, 0xde, 0x87, 0xe2, 0x89, 0x41, 0xdb, 0xf4, 0x3c, 0x6a, 0x88, 0x55, 0x02, 0xef, 0x0b, - 0xbd, 0x47, 0x5a, 0x02, 0x4f, 0x9e, 0x87, 0x51, 0x84, 0x89, 0x2b, 0x42, 0xbe, 0x3f, 0x16, 0x05, - 0x9c, 0x47, 0x5a, 0x14, 0xa9, 0xfe, 0x5e, 0x78, 0x3b, 0x3c, 0x47, 0xf5, 0xa3, 0x7a, 0xa3, 0xf8, - 0x21, 0xe9, 0x2f, 0xf5, 0x5f, 0x17, 0xf9, 0x93, 0x13, 0xfe, 0x3e, 0xf1, 0x30, 0x44, 0x19, 0x1e, - 0xe9, 0x16, 0x76, 0x71, 0xa4, 0xfb, 0x3c, 0x94, 0xe6, 0xa9, 0xb7, 0x66, 0xfb, 0x8e, 0x5f, 0xe8, - 0xa1, 0xd7, 0x46, 0x88, 0xec, 0xa1, 0xc7, 0x69, 0xc8, 0x03, 0x20, 0xfe, 0xe3, 0xc3, 0xc0, 0xf1, - 0xdb, 0x3f, 0x42, 0x3e, 0x97, 0xd8, 0xa7, 0xd4, 0xf1, 0x89, 0x32, 0xfa, 0xf4, 0x9f, 0x0e, 0x1c, - 0xcb, 0x25, 0x4f, 0xac, 0x7f, 0xb5, 0x59, 0x2e, 0x71, 0x1a, 0x2d, 0x85, 0x2d, 0x79, 0x07, 0x86, - 0xe6, 0x67, 0x2a, 0xe2, 0x21, 0x22, 0xf7, 0x8a, 0x38, 0x1f, 0x48, 0xd1, 0x47, 0x04, 0x22, 0xc1, - 0xf7, 0x3d, 0xed, 0xfb, 0x7a, 0xf2, 0x1d, 0x62, 0xc8, 0x85, 0x69, 0x0b, 0x7f, 0x29, 0x24, 0x4e, - 0x17, 0x02, 0x6d, 0x89, 0xbe, 0x1f, 0x8a, 0xcb, 0x8a, 0x63, 0x63, 0xda, 0x32, 0xb8, 0x8f, 0xd1, - 0xbd, 0x08, 0x27, 0x2b, 0x9d, 0x4e, 0xcb, 0xa4, 0x06, 0xea, 0x8b, 0xd6, 0x6d, 0x51, 0x57, 0xb8, - 0xfc, 0xe0, 0xe3, 0x13, 0x9d, 0x23, 0x1b, 0xf8, 0xfc, 0xb5, 0xe1, 0x74, 0xa3, 0xfe, 0x99, 0xc9, - 0xb2, 0xea, 0xcf, 0xe4, 0xe1, 0xec, 0x94, 0x43, 0x75, 0x8f, 0xce, 0xcf, 0x54, 0x2a, 0x5d, 0xf4, - 0x91, 0x6b, 0xb5, 0xa8, 0xb5, 0x7a, 0x38, 0xc3, 0xfa, 0x0d, 0x18, 0x0b, 0x1a, 0x50, 0x6f, 0xda, - 0x1d, 0x2a, 0x3f, 0xe4, 0x6a, 0xfa, 0x98, 0x86, 0xcb, 0x50, 0x5a, 0x8c, 0x94, 0xdc, 0x86, 0x53, - 0x01, 0xa4, 0xd2, 0x6a, 0xd9, 0xeb, 0x1a, 0xed, 0xba, 0xdc, 0x11, 0x77, 0x90, 0x3b, 0xe2, 0x86, - 0x1c, 0x74, 0x86, 0x6f, 0x38, 0x8c, 0x40, 0x4b, 0x2b, 0xa5, 0x7e, 0xad, 0x00, 0xe7, 0xee, 0xea, - 0x2d, 0xd3, 0x08, 0x45, 0xa3, 0x51, 0xb7, 0x63, 0x5b, 0x2e, 0x3d, 0x42, 0xa3, 0x34, 0x32, 0x14, - 0x8a, 0x07, 0x32, 0x14, 0x92, 0x5d, 0xd4, 0xbf, 0xef, 0x2e, 0x2a, 0xed, 0xa9, 0x8b, 0xfe, 0xb7, - 0x1c, 0x8c, 0xfb, 0x0f, 0x0d, 0xe4, 0x47, 0xe3, 0x92, 0x17, 0x3c, 0x1e, 0x21, 0xc6, 0xfc, 0xae, - 0x11, 0x4f, 0xea, 0x30, 0x30, 0xfd, 0xa8, 0x63, 0x3a, 0xd4, 0xdd, 0x81, 0xd3, 0xf8, 0x25, 0x71, - 0x5c, 0x72, 0x92, 0xf2, 0x22, 0x89, 0x93, 0x12, 0x0e, 0xc6, 0xe7, 0x83, 0xfc, 0xa9, 0xc5, 0xa4, - 0xff, 0x12, 0x9e, 0x3f, 0x1f, 0x14, 0x4f, 0x32, 0x22, 0xef, 0x41, 0x43, 0x52, 0xf2, 0x0c, 0x14, - 0x96, 0x97, 0xe7, 0xc4, 0x4c, 0x8a, 0x11, 0x08, 0x3c, 0x4f, 0x7e, 0x1f, 0xc9, 0xb0, 0xea, 0x3f, - 0xcb, 0x03, 0x30, 0x55, 0xe0, 0xc3, 0xf5, 0x50, 0x94, 0x70, 0x12, 0x06, 0x7d, 0x81, 0x0b, 0x35, - 0x0c, 0x5e, 0x09, 0xc4, 0x3b, 0x22, 0x5e, 0x77, 0xf0, 0x22, 0xa4, 0xec, 0x3b, 0x92, 0xf3, 0x7b, - 0x00, 0xdc, 0xd9, 0xa0, 0x23, 0xb9, 0xef, 0x3e, 0xfe, 0x31, 0x18, 0x12, 0x33, 0x9e, 0x1d, 0x39, - 0xff, 0x6f, 0xfa, 0x40, 0x2d, 0xc4, 0xc7, 0xa6, 0xd6, 0xd2, 0x3e, 0x16, 0x62, 0x5f, 0xbc, 0xbc, - 0x57, 0x8e, 0xc5, 0x7b, 0xc0, 0xe2, 0xfd, 0x92, 0x10, 0x2f, 0x7f, 0x31, 0x74, 0x64, 0xc5, 0x7b, - 0x60, 0x67, 0xdf, 0xea, 0x3f, 0xce, 0x01, 0x61, 0xcd, 0x5a, 0xd2, 0x5d, 0x77, 0xdd, 0x76, 0x0c, - 0xee, 0x9c, 0x7e, 0x28, 0x82, 0x39, 0xb8, 0xfb, 0xca, 0x6f, 0x0f, 0xc2, 0xa9, 0x88, 0xe3, 0xef, - 0x11, 0x9f, 0xac, 0xae, 0x46, 0x47, 0x53, 0xaf, 0x57, 0x2f, 0x1f, 0x91, 0x2f, 0x44, 0xfb, 0x23, - 0x0f, 0xde, 0xa4, 0x9b, 0xd0, 0x17, 0x60, 0x44, 0xfc, 0x60, 0x2b, 0xb4, 0x7f, 0xd3, 0x85, 0xa3, - 0xd4, 0x65, 0x00, 0x2d, 0x82, 0x26, 0x2f, 0xc3, 0x10, 0x1b, 0x30, 0xab, 0x18, 0xac, 0x64, 0x20, - 0x7c, 0x51, 0x62, 0xf8, 0x40, 0x79, 0x3d, 0x09, 0x28, 0xa5, 0x77, 0x4b, 0x83, 0x3b, 0x78, 0xb7, - 0xf4, 0x79, 0x18, 0xae, 0x58, 0x96, 0xed, 0xe1, 0x26, 0xdd, 0x15, 0x57, 0x13, 0x99, 0x56, 0xf9, - 0x33, 0xf8, 0x18, 0x3f, 0xa4, 0x4f, 0x35, 0xcb, 0x65, 0x86, 0xe4, 0xba, 0xff, 0x2a, 0x86, 0x3a, - 0xc2, 0xab, 0x1c, 0xaf, 0x67, 0x1c, 0x01, 0x4b, 0x3e, 0x8a, 0xc1, 0xce, 0x1b, 0x5d, 0x72, 0xec, - 0x8e, 0xed, 0x52, 0x83, 0x0b, 0x6a, 0x38, 0x0c, 0x6d, 0xd0, 0x11, 0x08, 0x7c, 0x37, 0x17, 0x09, - 0x1c, 0x12, 0x29, 0x42, 0xee, 0xc3, 0x69, 0xff, 0xa2, 0x38, 0x78, 0xa1, 0x58, 0xab, 0xba, 0xca, - 0x08, 0xbe, 0x4a, 0x22, 0x71, 0x65, 0xa8, 0x55, 0x27, 0x9f, 0xf4, 0xaf, 0x45, 0xfc, 0x27, 0x8e, - 0x0d, 0xd3, 0x90, 0xbb, 0x3a, 0x95, 0x1f, 0xf9, 0x41, 0x18, 0x9e, 0xd7, 0x1f, 0x55, 0xbb, 0xe2, - 0xec, 0x65, 0x74, 0xe7, 0xb7, 0x2f, 0x6d, 0xfd, 0x51, 0xc3, 0x10, 0xe5, 0x62, 0x36, 0x85, 0xcc, - 0x92, 0x34, 0xe0, 0xec, 0x92, 0x63, 0xb7, 0x6d, 0x8f, 0x1a, 0xb1, 0xc7, 0x7e, 0x27, 0xc2, 0xd7, - 0xc1, 0x1d, 0x41, 0xd1, 0xe8, 0xf1, 0xea, 0x2f, 0x83, 0x0d, 0x69, 0xc3, 0x89, 0x8a, 0xeb, 0x76, - 0xdb, 0x34, 0xbc, 0xa1, 0x1a, 0xdf, 0xf6, 0x33, 0x3e, 0x2a, 0xbc, 0x96, 0x9f, 0xd0, 0xb1, 0x28, - 0xbf, 0xa0, 0x6a, 0x78, 0xa6, 0x5c, 0x23, 0x7e, 0x4b, 0x9c, 0xf7, 0xad, 0xe2, 0xe0, 0xd8, 0xf8, - 0x09, 0xed, 0x5c, 0xb2, 0x31, 0xcb, 0xa6, 0xd7, 0xa2, 0xea, 0xb7, 0x72, 0x00, 0xa1, 0x80, 0xc9, - 0x0b, 0xd1, 0x88, 0x48, 0xb9, 0xf0, 0xa2, 0x43, 0x44, 0x4b, 0x88, 0x84, 0x40, 0x22, 0x17, 0xa1, - 0x88, 0x11, 0x35, 0xf2, 0xe1, 0xc1, 0xea, 0x03, 0xd3, 0x32, 0x34, 0x84, 0x32, 0xac, 0xf4, 0xf4, - 0x1d, 0xb1, 0x78, 0xa9, 0xcf, 0xad, 0xc2, 0x2a, 0x9c, 0xa8, 0x77, 0x57, 0xfc, 0xba, 0xa5, 0x77, - 0x7c, 0x18, 0xd8, 0xc3, 0xed, 0xae, 0x04, 0x8f, 0x5f, 0x23, 0x61, 0x53, 0xa2, 0x45, 0xd4, 0xaf, - 0xe7, 0x62, 0xb3, 0xe0, 0x21, 0x2e, 0x7a, 0x1f, 0x49, 0xfa, 0x69, 0x24, 0xa7, 0x25, 0xf5, 0x0f, - 0x0b, 0x30, 0xbc, 0x64, 0x3b, 0x9e, 0x08, 0x51, 0x72, 0xb4, 0x57, 0x21, 0x69, 0xaf, 0x54, 0xdc, - 0xc5, 0x5e, 0xe9, 0x22, 0x14, 0x25, 0x17, 0x65, 0x7e, 0x2f, 0x62, 0x18, 0x8e, 0x86, 0xd0, 0x0f, - 0xf8, 0xc9, 0x45, 0xf2, 0x12, 0x74, 0x60, 0xdf, 0xae, 0x06, 0x3f, 0x9c, 0x07, 0xf8, 0xf4, 0x8b, - 0x2f, 0x3e, 0xc6, 0x5d, 0xaa, 0xfe, 0xe5, 0x1c, 0x9c, 0x10, 0x57, 0x8b, 0x52, 0x34, 0xb4, 0x01, - 0xff, 0x52, 0x58, 0x9e, 0x49, 0x38, 0x48, 0xf3, 0x71, 0x6c, 0xd1, 0x9a, 0x7e, 0x64, 0x7a, 0x78, - 0xbb, 0x22, 0x85, 0x43, 0xa3, 0x02, 0x26, 0x2f, 0x5a, 0x3e, 0x1d, 0x79, 0xc1, 0xbf, 0x34, 0x2d, - 0x84, 0x2b, 0x35, 0x2b, 0x30, 0x9d, 0x7a, 0x71, 0xaa, 0xfe, 0x6a, 0x11, 0x8a, 0xd3, 0x8f, 0x68, - 0xf3, 0x88, 0x77, 0x8d, 0x74, 0x14, 0x5b, 0xdc, 0xe7, 0x51, 0xec, 0x5e, 0xbc, 0x40, 0xde, 0x0e, - 0xfb, 0xb3, 0x14, 0xad, 0x3e, 0xd6, 0xf3, 0xf1, 0xea, 0xfd, 0x9e, 0x3e, 0x7a, 0x4e, 0x44, 0x7f, - 0xbf, 0x00, 0x85, 0xfa, 0xd4, 0xd2, 0xb1, 0xde, 0x1c, 0xaa, 0xde, 0xf4, 0xbe, 0x65, 0x57, 0x83, - 0x8b, 0xb3, 0xc1, 0xd0, 0xaf, 0x35, 0x76, 0x47, 0xf6, 0x27, 0x05, 0x18, 0xab, 0xcf, 0x2c, 0x2f, - 0x49, 0x67, 0xd7, 0xb7, 0xb9, 0xef, 0x21, 0x7a, 0xc1, 0xf1, 0x2e, 0xbd, 0x98, 0xb0, 0xc0, 0xee, - 0xd4, 0x2c, 0xef, 0x95, 0x1b, 0x77, 0xf5, 0x56, 0x97, 0xe2, 0x61, 0x11, 0xf7, 0x54, 0x76, 0xcd, - 0xf7, 0xe9, 0xd7, 0x30, 0x34, 0x82, 0xcf, 0x80, 0xbc, 0x01, 0x85, 0x3b, 0xc2, 0x87, 0x24, 0x8b, - 0xcf, 0x4b, 0xd7, 0x39, 0x1f, 0x36, 0x09, 0x16, 0xba, 0xa6, 0x81, 0x1c, 0x58, 0x29, 0x56, 0xf8, - 0xa6, 0x30, 0x19, 0x76, 0x54, 0x78, 0xd5, 0x2f, 0x7c, 0xb3, 0x56, 0x25, 0x75, 0x18, 0x5e, 0xa2, - 0x4e, 0xdb, 0xc4, 0x8e, 0xf2, 0xe7, 0xec, 0xde, 0x4c, 0xd8, 0xde, 0x6a, 0xb8, 0x13, 0x16, 0x42, - 0x66, 0x32, 0x17, 0xf2, 0x2e, 0x00, 0xb7, 0xaa, 0x76, 0x18, 0x61, 0xf3, 0x12, 0xee, 0x54, 0xb8, - 0x31, 0x9c, 0x62, 0x95, 0x4a, 0xcc, 0xc8, 0x03, 0x18, 0x9f, 0xb7, 0x0d, 0xf3, 0xbe, 0xc9, 0x9d, - 0x45, 0xb1, 0x82, 0xd2, 0xf6, 0x2e, 0x5a, 0xcc, 0xf8, 0x6d, 0x4b, 0xe5, 0xd2, 0xaa, 0x49, 0x30, - 0x56, 0xff, 0x4e, 0x3f, 0x14, 0x59, 0xb7, 0x1f, 0x8f, 0xdf, 0xfd, 0x8c, 0xdf, 0x0a, 0x8c, 0xdf, - 0xb3, 0x9d, 0x07, 0xa6, 0xb5, 0x1a, 0xf8, 0xf1, 0x8b, 0xdd, 0x34, 0xfa, 0x1e, 0xad, 0x73, 0x5c, - 0x23, 0x70, 0xf9, 0xd7, 0x12, 0xe4, 0xdb, 0x8c, 0xe0, 0xd7, 0x00, 0xf8, 0xeb, 0x7c, 0xa4, 0x19, - 0x0c, 0xc3, 0x79, 0xf0, 0xb7, 0xfb, 0xf8, 0x34, 0x40, 0x0e, 0xe7, 0x11, 0x12, 0x93, 0xab, 0xbe, - 0xf7, 0xc6, 0x10, 0xbe, 0x14, 0xc0, 0x63, 0x03, 0xf4, 0xde, 0x90, 0x8d, 0x00, 0xee, 0xc7, 0xb1, - 0x04, 0x20, 0xdd, 0x88, 0x41, 0x4c, 0x10, 0x91, 0xc9, 0x41, 0x04, 0xd0, 0x4b, 0xb9, 0x10, 0xd3, - 0x24, 0x1e, 0xe4, 0x95, 0xd8, 0x95, 0x3d, 0x89, 0x70, 0xcb, 0xbc, 0xb1, 0x0f, 0x5d, 0xbe, 0x46, - 0xb6, 0x73, 0xf9, 0x52, 0xff, 0x66, 0x01, 0x86, 0x19, 0xb7, 0x7a, 0xb7, 0xdd, 0xd6, 0x9d, 0x8d, - 0x63, 0x45, 0xde, 0x8f, 0x22, 0x37, 0xe0, 0xa4, 0xec, 0xe2, 0xcf, 0x4c, 0x57, 0x3f, 0x18, 0x53, - 0x70, 0x60, 0x15, 0x27, 0xe0, 0xb6, 0x25, 0xce, 0xfb, 0x9e, 0x00, 0xe3, 0x69, 0x88, 0xab, 0x25, - 0x79, 0xa9, 0x3f, 0x9d, 0x83, 0xf1, 0x38, 0x34, 0xd0, 0xfd, 0x5c, 0xaa, 0xee, 0x3f, 0x0f, 0x43, - 0xe2, 0xd2, 0x5f, 0x37, 0x84, 0x0f, 0xe2, 0xd8, 0xd6, 0x66, 0x19, 0xf0, 0xc5, 0x75, 0xc3, 0xa1, - 0xba, 0xa1, 0x85, 0x04, 0xe4, 0x65, 0x18, 0xc1, 0x1f, 0xf7, 0x1c, 0xd3, 0xf3, 0x28, 0xef, 0x8c, - 0x22, 0xbf, 0xc7, 0xe0, 0x05, 0xd6, 0x39, 0x42, 0x8b, 0x90, 0xa9, 0xbf, 0x9b, 0x87, 0xa1, 0x7a, - 0x77, 0xc5, 0xdd, 0x70, 0x3d, 0xda, 0x3e, 0xe2, 0x3a, 0xe4, 0x1f, 0x2b, 0x14, 0x53, 0x8f, 0x15, - 0x9e, 0xf1, 0x87, 0x96, 0x74, 0xde, 0x1e, 0x6c, 0x0c, 0x7c, 0x3f, 0xca, 0x50, 0x8b, 0x4a, 0xbb, - 0xd7, 0x22, 0xf5, 0x6f, 0xe7, 0x61, 0x9c, 0x5f, 0x37, 0x57, 0x4d, 0xb7, 0x79, 0x00, 0x4f, 0x60, - 0x0e, 0x5f, 0xa6, 0xfb, 0x73, 0xd1, 0xd8, 0xc1, 0xc3, 0x22, 0xf5, 0x0b, 0x79, 0x18, 0xae, 0x74, - 0xbd, 0xb5, 0x8a, 0x87, 0xf3, 0xdb, 0x63, 0xb9, 0x47, 0xfe, 0x87, 0x39, 0x38, 0xc1, 0x1a, 0xb2, - 0x6c, 0x3f, 0xa0, 0xd6, 0x01, 0x1c, 0xd7, 0xcb, 0xc7, 0xee, 0xf9, 0x3d, 0x1e, 0xbb, 0xfb, 0xb2, - 0x2c, 0xec, 0x4e, 0x96, 0x78, 0xc9, 0xa4, 0xd9, 0x2d, 0x7a, 0xb4, 0x3f, 0xe3, 0x00, 0x2f, 0x99, - 0x7c, 0x81, 0x1c, 0xc0, 0xa5, 0xe6, 0xf7, 0x97, 0x40, 0x0e, 0xe0, 0x44, 0xf6, 0xfb, 0x43, 0x20, - 0xdf, 0xce, 0xc1, 0xd0, 0xa4, 0xed, 0x1d, 0xf1, 0x81, 0x2f, 0xbe, 0xe2, 0x68, 0xab, 0xb9, 0xff, - 0x15, 0x47, 0x5b, 0x37, 0xd5, 0x9f, 0xcd, 0xc3, 0x69, 0x11, 0xc1, 0x5f, 0x9c, 0x81, 0x1d, 0x4f, - 0xc7, 0x62, 0xb0, 0x25, 0x45, 0x73, 0x3c, 0x0f, 0x09, 0xd1, 0xfc, 0x42, 0x01, 0x4e, 0x63, 0xc0, - 0x61, 0xb6, 0xa3, 0xfa, 0x3e, 0xb0, 0x45, 0x48, 0x33, 0xea, 0x3a, 0x30, 0x9f, 0xe2, 0x3a, 0xf0, - 0xaf, 0x36, 0xcb, 0xaf, 0xac, 0x9a, 0xde, 0x5a, 0x77, 0x65, 0xa2, 0x69, 0xb7, 0xaf, 0xad, 0x3a, - 0xfa, 0x43, 0x93, 0x5f, 0x9a, 0xeb, 0xad, 0x6b, 0x61, 0x62, 0x9d, 0x8e, 0x29, 0xd2, 0xe4, 0xd4, - 0x71, 0xa7, 0xc4, 0xb8, 0xfa, 0x4e, 0x07, 0x2e, 0xc0, 0x2d, 0xdb, 0xb4, 0x84, 0x27, 0x2e, 0x37, - 0x74, 0xeb, 0x5b, 0x9b, 0xe5, 0x33, 0xef, 0xd9, 0xa6, 0xd5, 0x88, 0xbb, 0xe3, 0xee, 0xb6, 0xbe, - 0x90, 0xb5, 0x26, 0x55, 0xa3, 0xfe, 0x37, 0x39, 0x38, 0x1f, 0xd5, 0xe2, 0xef, 0x07, 0xdb, 0xf1, - 0x2f, 0xe5, 0xe1, 0xcc, 0x4d, 0x14, 0x4e, 0xe0, 0xfe, 0x74, 0x3c, 0x6f, 0x89, 0xc1, 0x99, 0x22, - 0x9b, 0x63, 0x8b, 0x32, 0x5b, 0x36, 0xc7, 0x93, 0xba, 0x90, 0xcd, 0x3f, 0xca, 0xc1, 0xa9, 0xc5, - 0x5a, 0x75, 0xea, 0xfb, 0x64, 0x44, 0x25, 0xbf, 0xe7, 0x88, 0x1b, 0x9c, 0x89, 0xef, 0x39, 0xe2, - 0xa6, 0xe7, 0x57, 0xf2, 0x70, 0xaa, 0x5e, 0x99, 0x9f, 0xfb, 0x7e, 0x99, 0xc1, 0xa7, 0x64, 0x5f, - 0x5d, 0xff, 0x10, 0x4c, 0xd8, 0x02, 0xf2, 0x67, 0xde, 0xbd, 0x9e, 0xed, 0xc3, 0x9b, 0x14, 0xca, - 0x11, 0x9f, 0xba, 0x0f, 0x44, 0x28, 0x4c, 0xf3, 0x23, 0xd4, 0x47, 0x5c, 0xf3, 0xff, 0x5e, 0x09, - 0x86, 0x6f, 0x77, 0x57, 0xa8, 0x70, 0xe9, 0x7a, 0xac, 0x4f, 0x7e, 0xaf, 0xc3, 0xb0, 0x10, 0x03, - 0xde, 0x70, 0x48, 0x21, 0x27, 0x45, 0x08, 0x21, 0x1e, 0xd5, 0x4b, 0x26, 0x22, 0x17, 0xa1, 0x78, - 0x97, 0x3a, 0x2b, 0xf2, 0x6b, 0xec, 0x87, 0xd4, 0x59, 0xd1, 0x10, 0x4a, 0xe6, 0xc2, 0x87, 0x26, - 0x95, 0xa5, 0x1a, 0xa6, 0x3b, 0x12, 0x97, 0x86, 0x98, 0xbf, 0x29, 0xf0, 0x16, 0xd5, 0x3b, 0x26, - 0x4f, 0x94, 0x24, 0x47, 0x82, 0x88, 0x97, 0x24, 0x0b, 0x70, 0x52, 0x76, 0x17, 0xe4, 0xb9, 0x7e, - 0x06, 0x53, 0xd8, 0xa5, 0x65, 0xf9, 0x49, 0x16, 0x25, 0x6f, 0xc3, 0x88, 0x0f, 0x44, 0xc7, 0xc7, - 0xa1, 0x30, 0xc1, 0x44, 0xc0, 0x2a, 0x96, 0x0f, 0x20, 0x52, 0x40, 0x66, 0x80, 0x97, 0x18, 0x90, - 0xc2, 0x20, 0xe6, 0x48, 0x1a, 0x29, 0x40, 0x5e, 0x46, 0x06, 0xf8, 0x38, 0x0a, 0x1d, 0xa6, 0x86, - 0xf1, 0xa9, 0x32, 0x5e, 0x00, 0x39, 0x02, 0xce, 0x1f, 0xa4, 0x47, 0xc8, 0xc8, 0x22, 0x40, 0xe8, - 0xd8, 0x22, 0xc2, 0x7e, 0xec, 0xda, 0xe5, 0x46, 0x62, 0x21, 0xdf, 0xe4, 0x8d, 0xee, 0xe5, 0x26, - 0x4f, 0xfd, 0x99, 0x02, 0x0c, 0x57, 0x3a, 0x9d, 0x60, 0x28, 0xbc, 0x00, 0xa5, 0x4a, 0xa7, 0x73, - 0x47, 0xab, 0xc9, 0x09, 0x00, 0xf4, 0x4e, 0xa7, 0xd1, 0x75, 0x4c, 0xd9, 0x93, 0x9a, 0x13, 0x91, - 0x29, 0x18, 0xad, 0x74, 0x3a, 0x4b, 0xdd, 0x95, 0x96, 0xd9, 0x94, 0xf2, 0x97, 0xf1, 0x0c, 0x8f, - 0x9d, 0x4e, 0xa3, 0x83, 0x98, 0x78, 0x12, 0xbb, 0x68, 0x19, 0xf2, 0x79, 0x0c, 0x96, 0x25, 0xd2, - 0x67, 0xf1, 0x04, 0x3d, 0x6a, 0x10, 0xfa, 0x3f, 0x6c, 0xdb, 0x44, 0x40, 0xc4, 0x53, 0x24, 0x5c, - 0xf4, 0x13, 0x5b, 0xb0, 0x8a, 0x12, 0x69, 0xb2, 0x42, 0x96, 0xe4, 0xe3, 0x30, 0x50, 0xe9, 0x74, - 0xa4, 0xdb, 0x2a, 0x74, 0x6c, 0x63, 0xa5, 0xe2, 0x19, 0x0a, 0x05, 0x99, 0xf8, 0x2c, 0x71, 0xbf, - 0x6d, 0x3b, 0x1e, 0x0e, 0xa9, 0xd1, 0xf0, 0xb3, 0xfc, 0x0b, 0x71, 0x5b, 0x8e, 0x4f, 0xa3, 0x45, - 0xcb, 0x5c, 0x78, 0x13, 0xc6, 0xa2, 0x2d, 0xde, 0x55, 0x9e, 0x86, 0xef, 0xe5, 0x50, 0x2a, 0x47, - 0xfc, 0x39, 0xc1, 0x4b, 0x50, 0xa8, 0x74, 0x3a, 0x62, 0x52, 0x3b, 0x95, 0xd2, 0xa9, 0xf1, 0xe8, - 0x03, 0x95, 0x4e, 0xc7, 0xff, 0xf4, 0x23, 0xfe, 0x2e, 0x69, 0x4f, 0x9f, 0xfe, 0x6d, 0xfe, 0xe9, - 0x47, 0xfb, 0xcd, 0x90, 0xfa, 0xab, 0x05, 0x38, 0x51, 0xe9, 0x74, 0x8e, 0xf3, 0x3b, 0x1c, 0x54, - 0x8c, 0x83, 0x17, 0x01, 0xa4, 0x39, 0x76, 0x20, 0x78, 0x35, 0x39, 0x2c, 0xcd, 0xaf, 0x4a, 0x4e, - 0x93, 0x88, 0x7c, 0xf5, 0x1b, 0xdc, 0x95, 0xfa, 0x7d, 0xa1, 0x80, 0x13, 0xdf, 0x51, 0x8f, 0xd7, - 0xf6, 0x61, 0xe9, 0x36, 0xd1, 0x07, 0xa5, 0x5d, 0xf5, 0xc1, 0xef, 0x44, 0x06, 0x0f, 0xe6, 0x0b, - 0x38, 0xee, 0x85, 0xfe, 0x7d, 0xd9, 0xd6, 0x63, 0xb2, 0x30, 0x45, 0x10, 0x29, 0x3f, 0x67, 0x9a, - 0x08, 0x69, 0xd6, 0x64, 0xa8, 0x86, 0x69, 0x68, 0x31, 0x5a, 0xbf, 0x0f, 0x07, 0x76, 0xd5, 0x87, - 0x9b, 0x79, 0x0c, 0x5b, 0x10, 0x84, 0x44, 0xdb, 0xff, 0x16, 0xe5, 0x1a, 0x00, 0x77, 0x5f, 0x08, - 0xfc, 0xf3, 0x47, 0x79, 0xf4, 0x23, 0x9e, 0x4a, 0x4d, 0x44, 0x3f, 0x0a, 0x49, 0x02, 0x77, 0xa7, - 0x42, 0xaa, 0xbb, 0xd3, 0x55, 0x18, 0xd4, 0xf4, 0xf5, 0x77, 0xba, 0xd4, 0xd9, 0x10, 0x36, 0x11, - 0x8f, 0x38, 0xaa, 0xaf, 0x37, 0x7e, 0x88, 0x01, 0xb5, 0x00, 0x4d, 0xd4, 0x20, 0xee, 0x85, 0xe4, - 0x56, 0xc2, 0x0f, 0xda, 0x83, 0x68, 0x17, 0x7b, 0x51, 0x74, 0xf2, 0x3a, 0x14, 0x2a, 0xf7, 0xea, - 0x42, 0xb2, 0x41, 0xd7, 0x56, 0xee, 0xd5, 0x85, 0xbc, 0x32, 0xcb, 0xde, 0xab, 0xab, 0x5f, 0xc8, - 0x03, 0x49, 0x52, 0x92, 0x57, 0x60, 0x08, 0xa1, 0xab, 0x4c, 0x67, 0xe4, 0x1c, 0xbc, 0xeb, 0x6e, - 0xc3, 0x41, 0x68, 0xc4, 0x42, 0xf4, 0x49, 0xc9, 0x6b, 0x98, 0xe5, 0x5c, 0x64, 0x81, 0x8c, 0xe4, - 0xe0, 0x5d, 0x77, 0xfd, 0xbc, 0xe0, 0xb1, 0x24, 0xe7, 0x82, 0x18, 0x8d, 0xcb, 0x7b, 0xf5, 0x59, - 0xdb, 0xf5, 0x84, 0xa8, 0xb9, 0x71, 0xb9, 0xee, 0x62, 0xf2, 0xe7, 0x88, 0x71, 0xc9, 0xc9, 0x30, - 0x81, 0xdd, 0xbd, 0x3a, 0x7f, 0x21, 0x66, 0x68, 0x76, 0xcb, 0xb7, 0x4a, 0x79, 0x02, 0xbb, 0x75, - 0xb7, 0xc1, 0x5f, 0x97, 0x19, 0x98, 0x5e, 0x3d, 0x92, 0xc0, 0x2e, 0x52, 0x4a, 0xfd, 0xc9, 0x41, - 0x18, 0xaf, 0xea, 0x9e, 0xbe, 0xa2, 0xbb, 0x54, 0xda, 0x92, 0x9f, 0xf0, 0x61, 0xfe, 0xe7, 0x48, - 0x72, 0x30, 0x56, 0x52, 0xbe, 0x26, 0x5e, 0x80, 0xbc, 0x11, 0xf2, 0x0d, 0xd2, 0x0b, 0xcb, 0xf9, - 0x0a, 0x57, 0x1a, 0x1d, 0x01, 0xd6, 0x12, 0x84, 0xe4, 0x79, 0x18, 0xf6, 0x61, 0x6c, 0x17, 0x51, - 0x08, 0x75, 0xc6, 0x58, 0x61, 0x9b, 0x08, 0x4d, 0x46, 0x93, 0xd7, 0x60, 0xc4, 0xff, 0x29, 0xd9, - 0xe7, 0x3c, 0xf9, 0xe2, 0x4a, 0x62, 0x0b, 0x26, 0x93, 0xca, 0x45, 0x71, 0x7e, 0xeb, 0x8f, 0x14, - 0x8d, 0xe5, 0x37, 0x8c, 0x90, 0x92, 0x1f, 0x82, 0x31, 0xff, 0xb7, 0xd8, 0x75, 0x70, 0xef, 0xc3, - 0xe7, 0x83, 0xec, 0xed, 0x31, 0xb1, 0x4e, 0x44, 0xc9, 0xf9, 0xfe, 0xe3, 0x09, 0x3f, 0x65, 0x9f, - 0xb1, 0x92, 0xdc, 0x7e, 0xc4, 0x2a, 0x20, 0x35, 0x38, 0xe9, 0x43, 0x42, 0x0d, 0x1d, 0x08, 0xb7, - 0x9d, 0xc6, 0x4a, 0x23, 0x55, 0x49, 0x93, 0xa5, 0x48, 0x0b, 0x2e, 0x46, 0x80, 0x86, 0xbb, 0x66, - 0xde, 0xf7, 0xc4, 0x9e, 0x51, 0x84, 0xff, 0x16, 0x39, 0x5a, 0x03, 0xae, 0x9c, 0xc6, 0x4f, 0xb6, - 0x1c, 0x4d, 0xcc, 0xd6, 0x93, 0x1b, 0xa9, 0xc3, 0x69, 0x1f, 0x7f, 0x73, 0x6a, 0x69, 0xc9, 0xb1, - 0xdf, 0xa3, 0x4d, 0xaf, 0x56, 0x15, 0x7b, 0x6e, 0x0c, 0x0b, 0x69, 0xac, 0x34, 0x56, 0x9b, 0x1d, - 0xa6, 0x14, 0x0c, 0x17, 0x65, 0x9e, 0x5a, 0x98, 0xdc, 0x85, 0x33, 0x12, 0x5c, 0xca, 0x04, 0x0f, - 0xe1, 0xa1, 0x80, 0xe0, 0x9a, 0x9e, 0x0c, 0x3e, 0xbd, 0x38, 0x79, 0x13, 0x46, 0x7d, 0x04, 0xbf, - 0x8a, 0x1c, 0xc6, 0xab, 0x48, 0x1c, 0x92, 0xc6, 0x4a, 0x23, 0xfe, 0x90, 0x39, 0x4a, 0x2c, 0x6b, - 0xd4, 0xf2, 0x46, 0x87, 0x0a, 0xb7, 0x60, 0x5f, 0xa3, 0xbc, 0x8d, 0x4e, 0xaa, 0x32, 0x32, 0x52, - 0xf2, 0x76, 0xa8, 0x51, 0x8b, 0x8e, 0xb9, 0x6a, 0xf2, 0xed, 0xb8, 0xff, 0x76, 0x79, 0xa5, 0x61, - 0x23, 0x30, 0x4d, 0x3f, 0x38, 0xf9, 0x85, 0x0a, 0x9c, 0x4a, 0xd1, 0xb1, 0x5d, 0xed, 0x18, 0xbf, - 0x98, 0x0f, 0x1b, 0x71, 0xc4, 0xb7, 0x8d, 0x93, 0x30, 0xe8, 0x7f, 0x89, 0x30, 0x1e, 0x94, 0xac, - 0xa1, 0x19, 0xe7, 0xe1, 0xe3, 0x23, 0xe2, 0x38, 0xe2, 0x5b, 0xc9, 0x83, 0x10, 0xc7, 0x77, 0x72, - 0xa1, 0x38, 0x8e, 0xf8, 0xf6, 0xf2, 0x27, 0x8a, 0xe1, 0x9c, 0x74, 0xbc, 0xc7, 0x3c, 0x28, 0x33, - 0x39, 0x74, 0xa6, 0x2d, 0xed, 0xe2, 0x0d, 0xb1, 0xac, 0x9a, 0x03, 0x7b, 0x53, 0x4d, 0xf2, 0x26, - 0x0c, 0x2f, 0xd9, 0xae, 0xb7, 0xea, 0x50, 0x77, 0x29, 0x48, 0x5f, 0x81, 0xef, 0xcf, 0x3b, 0x02, - 0xdc, 0xe8, 0x44, 0x66, 0x7f, 0x99, 0x5c, 0xfd, 0x27, 0x85, 0x84, 0x36, 0x70, 0xc3, 0xf5, 0x48, - 0x6a, 0xc3, 0x01, 0x0c, 0x75, 0x72, 0x3d, 0x5c, 0x05, 0xb9, 0x85, 0xdf, 0x2f, 0xc5, 0xe6, 0x5c, - 0x11, 0x06, 0x7e, 0x94, 0x84, 0x7c, 0x16, 0xce, 0x45, 0x00, 0x4b, 0xba, 0xa3, 0xb7, 0xa9, 0x17, - 0xa6, 0x0a, 0xc5, 0x68, 0x6b, 0x7e, 0xe9, 0x46, 0x27, 0x40, 0xcb, 0xe9, 0x47, 0x33, 0x38, 0x48, - 0xaa, 0x35, 0xb0, 0x0b, 0x3f, 0xed, 0xaf, 0x16, 0x42, 0x43, 0x27, 0x1a, 0x35, 0x59, 0xa3, 0x6e, - 0xb7, 0xe5, 0x3d, 0xbe, 0x1d, 0xbc, 0xb7, 0x9c, 0x34, 0xb3, 0x70, 0xa2, 0x72, 0xff, 0x3e, 0x6d, - 0x7a, 0x7e, 0x30, 0x78, 0x57, 0xc4, 0xc9, 0xe4, 0x1b, 0x0f, 0x81, 0x12, 0xc1, 0xbd, 0xe5, 0x7e, - 0x8d, 0x17, 0x53, 0xff, 0x69, 0x11, 0x94, 0xc0, 0xf0, 0x0f, 0xde, 0x2b, 0x1e, 0xe2, 0x22, 0xfb, - 0xa1, 0xe8, 0x15, 0x13, 0x4e, 0x86, 0xc2, 0x10, 0x0f, 0xc5, 0x44, 0xf2, 0xfa, 0x72, 0x9c, 0x59, - 0x48, 0xc8, 0xf7, 0x12, 0x17, 0xc4, 0x5e, 0x82, 0x84, 0xef, 0x41, 0x1b, 0x2e, 0x67, 0xa1, 0x25, - 0xb9, 0x92, 0x2f, 0xe5, 0xe0, 0xb4, 0xdf, 0x29, 0x8b, 0x2b, 0xcc, 0xa8, 0x9e, 0xb2, 0xbb, 0x56, - 0xf0, 0x8a, 0xea, 0xf5, 0xec, 0xea, 0x78, 0x27, 0x4d, 0xa4, 0x15, 0xe6, 0x2d, 0x09, 0x22, 0xc2, - 0x04, 0x0a, 0x61, 0x23, 0x4d, 0xa3, 0x89, 0x44, 0x5a, 0x6a, 0xbd, 0x17, 0x6e, 0xc2, 0xf9, 0x4c, - 0x96, 0xdb, 0x19, 0xb1, 0xfd, 0xb2, 0x11, 0xfb, 0xdf, 0xe5, 0xc2, 0x89, 0x28, 0x26, 0x24, 0x32, - 0x01, 0x10, 0x82, 0xc4, 0xb6, 0x16, 0x1f, 0x69, 0x85, 0x42, 0xd3, 0x24, 0x0a, 0xb2, 0x08, 0x25, - 0x21, 0x16, 0x9e, 0x96, 0xfb, 0x63, 0xdb, 0xf4, 0xc2, 0x84, 0x2c, 0x07, 0xdc, 0xb2, 0x8a, 0x6f, - 0x16, 0x6c, 0x2e, 0xbc, 0x06, 0xc3, 0x7b, 0xfd, 0xae, 0x2f, 0x15, 0x80, 0xc8, 0x7b, 0xd0, 0x43, - 0x34, 0xd0, 0x8f, 0xf0, 0x14, 0x76, 0x05, 0x06, 0xd9, 0x27, 0x60, 0xa2, 0x1a, 0x29, 0x30, 0x75, - 0x57, 0xc0, 0xb4, 0x00, 0x1b, 0x46, 0x85, 0x1b, 0x48, 0x8f, 0x0a, 0xa7, 0xfe, 0x74, 0x01, 0xce, - 0xca, 0x1d, 0x52, 0xa5, 0x98, 0xeb, 0xe2, 0xb8, 0x53, 0x3e, 0xc0, 0x4e, 0x51, 0xa1, 0xc4, 0xb7, - 0x1e, 0x22, 0xe9, 0x08, 0x3f, 0x16, 0x42, 0x88, 0x26, 0x30, 0xea, 0xff, 0x92, 0x87, 0xd1, 0xc0, - 0xbc, 0xd3, 0x1d, 0xf7, 0x31, 0xee, 0x8e, 0x4f, 0xc0, 0x28, 0xc6, 0xf5, 0x6a, 0x53, 0x8b, 0xc7, - 0xbe, 0xea, 0x97, 0xb2, 0x04, 0xf9, 0x08, 0x91, 0x10, 0x2e, 0x42, 0xc8, 0xb4, 0x9f, 0x5b, 0x7e, - 0x52, 0xb4, 0x35, 0x6e, 0xf6, 0x71, 0xb8, 0xfa, 0x57, 0x0b, 0x30, 0xe2, 0x4b, 0x79, 0xd2, 0x3c, - 0xaa, 0xf7, 0x3c, 0x87, 0x2b, 0xe4, 0x6b, 0x00, 0x4b, 0xb6, 0xe3, 0xe9, 0xad, 0x85, 0x50, 0xf3, - 0xf1, 0x80, 0xb4, 0x83, 0x50, 0x5e, 0x46, 0x22, 0xc1, 0xf5, 0x2b, 0x34, 0xab, 0xf9, 0xc4, 0xc4, - 0xd7, 0xaf, 0x00, 0xaa, 0x49, 0x14, 0xea, 0x6f, 0xe6, 0xe1, 0x84, 0xdf, 0x49, 0xd3, 0x8f, 0x68, - 0xb3, 0xfb, 0x38, 0xcf, 0x4d, 0x51, 0x69, 0xf7, 0x6f, 0x2b, 0x6d, 0xf5, 0xff, 0x92, 0x26, 0x92, - 0xa9, 0x96, 0x7d, 0x3c, 0x91, 0xfc, 0x69, 0xe8, 0xb8, 0xfa, 0x23, 0x05, 0x38, 0xed, 0x4b, 0x7d, - 0xa6, 0x6b, 0xe1, 0xd1, 0xc2, 0x94, 0xde, 0x6a, 0x3d, 0xce, 0xbb, 0xf1, 0x61, 0x5f, 0x10, 0x8b, - 0x22, 0x50, 0xa6, 0x48, 0xce, 0x79, 0x5f, 0x80, 0x1b, 0xb6, 0x69, 0x68, 0x32, 0x11, 0x79, 0x1b, - 0x46, 0xfc, 0x9f, 0x15, 0x67, 0xd5, 0xdf, 0x82, 0xe3, 0x45, 0x41, 0x50, 0x48, 0x77, 0x22, 0xd1, - 0x35, 0x22, 0x05, 0xd4, 0x2f, 0x0c, 0xc0, 0x85, 0x7b, 0xa6, 0x65, 0xd8, 0xeb, 0xae, 0x9f, 0xdb, - 0xf5, 0xc8, 0x1f, 0x94, 0x1d, 0x76, 0x4e, 0xd7, 0x77, 0xe0, 0x4c, 0x5c, 0xa4, 0x4e, 0x10, 0x71, - 0x5f, 0xf4, 0xce, 0x3a, 0x27, 0x68, 0xf8, 0x59, 0x5e, 0xc5, 0x6d, 0x9b, 0x96, 0x5e, 0x32, 0x9e, - 0x26, 0x76, 0x60, 0x27, 0x69, 0x62, 0x9f, 0x83, 0x52, 0xd5, 0x6e, 0xeb, 0xa6, 0x1f, 0x67, 0x09, - 0x47, 0x71, 0x50, 0x2f, 0x62, 0x34, 0x41, 0xc1, 0xf8, 0x8b, 0x8a, 0xb1, 0xcb, 0x86, 0x42, 0xfe, - 0x7e, 0x01, 0x66, 0xa5, 0x69, 0x32, 0x11, 0xb1, 0x61, 0x54, 0x54, 0x27, 0xee, 0xc6, 0x00, 0x37, - 0x4f, 0x2f, 0xfb, 0x32, 0xca, 0x56, 0xab, 0x89, 0x48, 0x39, 0xbe, 0x8d, 0xe2, 0xd9, 0x6b, 0xc5, - 0xc7, 0xf0, 0x5b, 0x32, 0x2d, 0xca, 0x5f, 0x12, 0x02, 0x4e, 0x32, 0xc3, 0x49, 0x21, 0xe0, 0x2c, - 0x23, 0x13, 0x91, 0x69, 0x38, 0x89, 0x71, 0xd1, 0x83, 0xad, 0x14, 0x53, 0x89, 0x11, 0x34, 0x2a, - 0xf1, 0xca, 0x85, 0x87, 0x52, 0x67, 0x1f, 0xd7, 0x68, 0x0a, 0xb4, 0x96, 0x2c, 0x41, 0xce, 0x43, - 0x61, 0x61, 0xae, 0x82, 0x77, 0x35, 0x83, 0x3c, 0x27, 0x99, 0xd5, 0xd2, 0x35, 0x06, 0xbb, 0xf0, - 0x29, 0x20, 0xc9, 0xcf, 0xd9, 0xd5, 0x7d, 0xcc, 0x3f, 0x90, 0xb6, 0x7c, 0x47, 0xdd, 0xa3, 0xe6, - 0x20, 0x26, 0xc2, 0x48, 0x3a, 0xc0, 0xfe, 0x0f, 0x32, 0x1d, 0x60, 0xe9, 0x40, 0xd3, 0x01, 0xaa, - 0xbf, 0x9c, 0x83, 0x93, 0x89, 0xdc, 0x01, 0xe4, 0x25, 0x00, 0x0e, 0x91, 0x62, 0xb4, 0x62, 0x08, - 0xa1, 0x30, 0x9f, 0x80, 0x58, 0x1e, 0x43, 0x32, 0x72, 0x0d, 0x06, 0xf9, 0x2f, 0x11, 0xa5, 0x2c, - 0x59, 0xa4, 0xdb, 0x35, 0x0d, 0x2d, 0x20, 0x0a, 0x6b, 0xc1, 0x1b, 0xc9, 0x42, 0x6a, 0x11, 0x6f, - 0xa3, 0x13, 0xd4, 0xc2, 0xc8, 0xd4, 0x9f, 0xcc, 0xc3, 0x48, 0xd0, 0xe0, 0x8a, 0x71, 0x58, 0x3a, - 0x57, 0x12, 0x69, 0x18, 0x0a, 0xdb, 0xa5, 0x61, 0x88, 0xcd, 0xb7, 0x22, 0xef, 0xc2, 0xc1, 0xbd, - 0xca, 0xfa, 0x72, 0x1e, 0x4e, 0x04, 0xb5, 0x1e, 0xe2, 0xe5, 0xd7, 0x87, 0x48, 0x24, 0x5f, 0xca, - 0x81, 0x32, 0x69, 0xb6, 0x5a, 0xa6, 0xb5, 0x5a, 0xb3, 0xee, 0xdb, 0x4e, 0x1b, 0x27, 0xc4, 0xc3, - 0x3b, 0xc2, 0x55, 0xff, 0x7c, 0x0e, 0x4e, 0x8a, 0x06, 0x4d, 0xe9, 0x8e, 0x71, 0x78, 0xe7, 0x63, - 0xf1, 0x96, 0x1c, 0x9e, 0xbe, 0xa8, 0xdf, 0xcc, 0x03, 0xcc, 0xd9, 0xcd, 0x07, 0x47, 0xfc, 0x51, - 0xd7, 0x1b, 0x50, 0xe2, 0x6e, 0xf1, 0x42, 0x63, 0x4f, 0x8a, 0xc7, 0x4b, 0xec, 0xd3, 0x38, 0x62, - 0x72, 0x5c, 0xcc, 0xc7, 0x25, 0xee, 0x59, 0xaf, 0xe4, 0x34, 0x51, 0x84, 0x55, 0xca, 0xe8, 0xc4, - 0x82, 0x11, 0x54, 0xca, 0x60, 0xd1, 0x4a, 0xb7, 0x36, 0xcb, 0xc5, 0x96, 0xdd, 0x7c, 0xa0, 0x21, - 0xbd, 0xfa, 0xaf, 0x73, 0x5c, 0x76, 0x47, 0xfc, 0x69, 0xaa, 0xff, 0xf9, 0xc5, 0x5d, 0x7e, 0xfe, - 0x5f, 0xc8, 0xc1, 0x69, 0x8d, 0x36, 0xed, 0x87, 0xd4, 0xd9, 0x98, 0xb2, 0x0d, 0x7a, 0x93, 0x5a, - 0xd4, 0x39, 0xac, 0x11, 0xf5, 0x5b, 0x98, 0xb7, 0x26, 0x6c, 0xcc, 0x1d, 0x97, 0x1a, 0x47, 0x27, - 0xa7, 0x90, 0xfa, 0x2b, 0x03, 0xa0, 0xa4, 0x5a, 0xbd, 0x47, 0xd6, 0x9c, 0xcb, 0xdc, 0xca, 0x14, - 0x0f, 0x6a, 0x2b, 0xd3, 0xbf, 0xbb, 0xad, 0x4c, 0x69, 0xb7, 0x5b, 0x99, 0x81, 0x9d, 0x6c, 0x65, - 0xda, 0xf1, 0xad, 0xcc, 0x20, 0x6e, 0x65, 0x5e, 0xea, 0xb9, 0x95, 0x99, 0xb6, 0x8c, 0x3d, 0x6e, - 0x64, 0x8e, 0x6c, 0xbe, 0xeb, 0xbd, 0xec, 0xc0, 0xae, 0xb0, 0x49, 0xb1, 0x69, 0x3b, 0x06, 0x35, - 0xc4, 0xc6, 0x0b, 0x4f, 0xfd, 0x1d, 0x01, 0xd3, 0x02, 0x6c, 0x22, 0x79, 0xf8, 0xe8, 0x4e, 0x92, - 0x87, 0x1f, 0xc0, 0xfe, 0xeb, 0x8b, 0x79, 0x38, 0x39, 0x45, 0x1d, 0x8f, 0xc7, 0xa2, 0x3d, 0x08, - 0x97, 0xb8, 0x0a, 0x9c, 0x90, 0x18, 0xa2, 0x45, 0x9e, 0x0f, 0xdd, 0xfc, 0x9a, 0xd4, 0xf1, 0xe2, - 0x5e, 0x82, 0x71, 0x7a, 0x56, 0xbd, 0x9f, 0xc0, 0x4f, 0x8c, 0xdd, 0xa0, 0x7a, 0x1f, 0xce, 0x05, - 0x69, 0x8a, 0x5f, 0x5a, 0x40, 0x2f, 0xe5, 0xe4, 0x2b, 0xee, 0x3e, 0x27, 0x9f, 0xfa, 0x4b, 0x39, - 0xb8, 0xac, 0x51, 0x8b, 0xae, 0xeb, 0x2b, 0x2d, 0x2a, 0x35, 0x4b, 0xac, 0x0c, 0x6c, 0xd6, 0x30, - 0xdd, 0xb6, 0xee, 0x35, 0xd7, 0xf6, 0x25, 0xa3, 0x19, 0x18, 0x91, 0xe7, 0xaf, 0x5d, 0xcc, 0x6d, - 0x91, 0x72, 0xea, 0xaf, 0x14, 0x61, 0x60, 0xd2, 0xf6, 0x6e, 0xd9, 0xfb, 0x4c, 0x12, 0x19, 0x4e, - 0xf9, 0xf9, 0x5d, 0x9c, 0xf5, 0x7c, 0x1c, 0x2b, 0x97, 0xf2, 0x66, 0xa0, 0x0b, 0xe9, 0x8a, 0x9d, - 0xc8, 0x2f, 0xe2, 0x93, 0xed, 0x32, 0x3d, 0xe4, 0x2b, 0x30, 0x84, 0x21, 0x64, 0xa4, 0xd3, 0x58, - 0x74, 0xd0, 0xf6, 0x18, 0x30, 0x5e, 0x47, 0x48, 0x4a, 0x3e, 0x1b, 0x09, 0x9e, 0x5b, 0xda, 0x7f, - 0x3a, 0x49, 0x39, 0x8e, 0xee, 0x4b, 0xfc, 0x22, 0x0f, 0xdb, 0x24, 0xa5, 0xde, 0xc1, 0x53, 0x94, - 0x58, 0x93, 0x02, 0xc2, 0x03, 0x4c, 0xf5, 0x38, 0x05, 0xa3, 0x93, 0xb6, 0x27, 0x39, 0x03, 0x0f, - 0x85, 0x6f, 0x49, 0x99, 0xe4, 0xd3, 0x3d, 0x81, 0xa3, 0x65, 0xd4, 0x3f, 0x29, 0xc2, 0x88, 0xff, - 0xf3, 0x90, 0x74, 0xe7, 0x05, 0x28, 0xcd, 0xda, 0x52, 0xf6, 0x11, 0x74, 0x20, 0x5e, 0xb3, 0xdd, - 0x98, 0x67, 0xb4, 0x20, 0x62, 0x52, 0x5f, 0xb0, 0x0d, 0xd9, 0xfd, 0x1d, 0xa5, 0x6e, 0xd9, 0x46, - 0xe2, 0x0d, 0x72, 0x40, 0x48, 0x2e, 0x43, 0x11, 0x5f, 0x0e, 0x48, 0x07, 0xf9, 0xb1, 0xd7, 0x02, - 0x88, 0x97, 0xb4, 0xb2, 0xb4, 0x5b, 0xad, 0x1c, 0xd8, 0xab, 0x56, 0x0e, 0x1e, 0xac, 0x56, 0xbe, - 0x0b, 0x23, 0x58, 0x93, 0x9f, 0xbc, 0x70, 0xfb, 0x85, 0xf5, 0xbc, 0x58, 0xfb, 0x46, 0x79, 0xbb, - 0x45, 0x0a, 0x43, 0x5c, 0xf2, 0x22, 0xac, 0x62, 0xba, 0x0b, 0xfb, 0xd8, 0x4e, 0xff, 0x93, 0x1c, - 0x0c, 0xdc, 0xb1, 0x1e, 0x58, 0xf6, 0xfa, 0xfe, 0x34, 0xee, 0x25, 0x18, 0x16, 0x6c, 0xa4, 0xd5, - 0x05, 0x9f, 0x95, 0x77, 0x39, 0xb8, 0x81, 0x9c, 0x34, 0x99, 0x8a, 0xbc, 0x19, 0x14, 0xc2, 0xc7, - 0x41, 0x85, 0x30, 0x7f, 0x8f, 0x5f, 0xa8, 0x19, 0x4d, 0xe0, 0x21, 0x93, 0x93, 0x8b, 0x50, 0xac, - 0xb2, 0xa6, 0x4a, 0x81, 0x7c, 0x59, 0x53, 0x34, 0x84, 0xaa, 0x5f, 0x2c, 0xc2, 0x58, 0xec, 0xe0, - 0xeb, 0x39, 0x18, 0x12, 0x07, 0x4f, 0xa6, 0x9f, 0x51, 0x04, 0x1f, 0x0f, 0x05, 0x40, 0x6d, 0x90, - 0xff, 0x59, 0x33, 0xc8, 0x27, 0x61, 0xc0, 0x76, 0x71, 0x51, 0xc4, 0x6f, 0x19, 0x0b, 0x87, 0xd0, - 0x62, 0x9d, 0xb5, 0x9d, 0x0f, 0x0e, 0x41, 0x22, 0x6b, 0xa4, 0xed, 0xe2, 0xa7, 0xdd, 0x80, 0x21, - 0xdd, 0x75, 0xa9, 0xd7, 0xf0, 0xf4, 0x55, 0x39, 0xc9, 0x48, 0x00, 0x94, 0x47, 0x07, 0x02, 0x97, - 0xf5, 0x55, 0xf2, 0x29, 0x18, 0x6d, 0x3a, 0x14, 0x97, 0x4d, 0xbd, 0xc5, 0x5a, 0x29, 0x99, 0xb5, - 0x11, 0x84, 0x7c, 0x7f, 0x12, 0x22, 0x6a, 0x06, 0xb9, 0x0b, 0xa3, 0xe2, 0x73, 0xb8, 0xe7, 0x3e, - 0x0e, 0xb4, 0xb1, 0x70, 0x19, 0xe3, 0x22, 0xe1, 0xbe, 0xfb, 0xe2, 0x01, 0x87, 0x4c, 0x2e, 0xf3, - 0x35, 0x24, 0x52, 0xb2, 0x08, 0x64, 0x9d, 0xae, 0x34, 0xf4, 0xae, 0xb7, 0xc6, 0xea, 0xe2, 0x31, - 0xf2, 0x45, 0x36, 0x50, 0x7c, 0xf5, 0x90, 0xc4, 0xca, 0x8f, 0x41, 0xd6, 0xe9, 0x4a, 0x25, 0x82, - 0x24, 0xf7, 0xe0, 0x4c, 0xb2, 0x08, 0xfb, 0x64, 0x7e, 0x39, 0xf0, 0xcc, 0xd6, 0x66, 0xb9, 0x9c, - 0x4a, 0x20, 0xb1, 0x3d, 0x95, 0x60, 0x5b, 0x33, 0x6e, 0x15, 0x07, 0x07, 0xc6, 0x07, 0xb5, 0x31, - 0x56, 0xd6, 0x37, 0x21, 0x4d, 0x43, 0xfd, 0xbd, 0x1c, 0x33, 0x15, 0xd9, 0x07, 0x61, 0x3a, 0x74, - 0xa6, 0xeb, 0xed, 0x5d, 0xea, 0x7a, 0x3b, 0x4c, 0x5c, 0x5a, 0x72, 0x7b, 0xcc, 0xae, 0x9a, 0xc0, - 0x92, 0x09, 0x28, 0x19, 0xf2, 0xa9, 0xd9, 0xd9, 0x68, 0x27, 0xf8, 0xf5, 0x68, 0x82, 0x8a, 0x5c, - 0x81, 0x22, 0x5b, 0xb2, 0xe2, 0x5b, 0x66, 0xd9, 0xba, 0xd0, 0x90, 0x42, 0xfd, 0xe1, 0x3c, 0x8c, - 0x48, 0x5f, 0x73, 0x7d, 0x5f, 0x9f, 0xf3, 0xfa, 0xce, 0x9a, 0xe9, 0x3b, 0xbd, 0xe0, 0x5e, 0xca, - 0x6f, 0xf2, 0x8d, 0x40, 0x14, 0x3b, 0xba, 0x90, 0x12, 0x82, 0x79, 0x45, 0x7c, 0x68, 0x69, 0xe7, - 0xdb, 0x47, 0x46, 0x7f, 0xab, 0x38, 0x98, 0x1f, 0x2f, 0xdc, 0x2a, 0x0e, 0x16, 0xc7, 0xfb, 0x31, - 0x98, 0x17, 0xc6, 0xcf, 0xe6, 0x7b, 0x73, 0xeb, 0xbe, 0xb9, 0x7a, 0xc4, 0xdf, 0x8e, 0x1c, 0x6c, - 0xa0, 0xb3, 0x98, 0x6c, 0x8e, 0xf8, 0x43, 0x92, 0x0f, 0x54, 0x36, 0xc7, 0x89, 0x4e, 0x85, 0x6c, - 0xfe, 0x69, 0x0e, 0x94, 0x54, 0xd9, 0x54, 0x0e, 0xc9, 0x0f, 0xe2, 0xe0, 0xd2, 0x9d, 0xfe, 0x51, - 0x1e, 0x4e, 0xd6, 0x2c, 0x8f, 0xae, 0xf2, 0x1d, 0xe3, 0x11, 0x9f, 0x2a, 0x6e, 0xc3, 0xb0, 0xf4, - 0x31, 0xa2, 0xcf, 0x9f, 0x08, 0xf6, 0xe3, 0x21, 0x2a, 0x83, 0x93, 0x5c, 0xfa, 0xe0, 0x5e, 0xe2, - 0xc4, 0x85, 0x7c, 0xc4, 0xe7, 0x9c, 0xa3, 0x21, 0xe4, 0x23, 0x3e, 0x79, 0x7d, 0x48, 0x85, 0xfc, - 0x77, 0xf3, 0x70, 0x2a, 0xa5, 0x72, 0x72, 0x19, 0x06, 0xea, 0xdd, 0x15, 0x8c, 0xdd, 0x95, 0x0b, - 0x3d, 0x86, 0xdd, 0xee, 0x0a, 0x86, 0xed, 0xd2, 0x7c, 0x24, 0x59, 0xc6, 0xc7, 0xf5, 0x8b, 0xb5, - 0xea, 0x94, 0x90, 0xaa, 0x2a, 0x85, 0x09, 0x60, 0xe0, 0xb4, 0x2f, 0x0b, 0x1e, 0xe0, 0xdb, 0xa6, - 0xd1, 0x8c, 0x3d, 0xc0, 0x67, 0x65, 0xc8, 0x0f, 0xc0, 0x50, 0xe5, 0xfd, 0xae, 0x43, 0x91, 0x2f, - 0x97, 0xf8, 0x47, 0x02, 0xbe, 0x3e, 0x22, 0x8d, 0x33, 0x8f, 0x25, 0xc0, 0x28, 0xe2, 0xbc, 0x43, - 0x86, 0x64, 0x11, 0x4a, 0x37, 0x4d, 0x6f, 0xb6, 0xbb, 0x22, 0x7a, 0x21, 0x88, 0xef, 0xc5, 0xa1, - 0x69, 0x7c, 0x71, 0x57, 0xce, 0xe3, 0x14, 0xcb, 0x7b, 0x20, 0x5e, 0x40, 0xfd, 0xc9, 0x1c, 0x5c, - 0xc8, 0xfe, 0x5c, 0xf2, 0x71, 0x18, 0x60, 0x5b, 0xfd, 0x8a, 0xb6, 0x20, 0x64, 0xc9, 0x73, 0x0d, - 0xdb, 0x2d, 0xda, 0xd0, 0x1d, 0x79, 0xf7, 0xe0, 0x93, 0x91, 0xb7, 0x60, 0xb8, 0xe6, 0xba, 0x5d, - 0xea, 0xd4, 0x5f, 0xba, 0xa3, 0xd5, 0xc4, 0x26, 0x13, 0x37, 0x31, 0x26, 0x82, 0x1b, 0xee, 0x4b, - 0xb1, 0x70, 0x5f, 0x32, 0xbd, 0xfa, 0x63, 0x39, 0xb8, 0xd8, 0x4b, 0x4c, 0xe4, 0x25, 0x18, 0x5c, - 0xa6, 0x96, 0x6e, 0x79, 0xb5, 0xaa, 0x68, 0x12, 0xee, 0xd9, 0x3c, 0x84, 0x45, 0xb7, 0x1e, 0x01, - 0x21, 0x2b, 0xc4, 0x0f, 0x2a, 0x03, 0xcf, 0x08, 0x7e, 0xa8, 0x8a, 0xb0, 0x58, 0x21, 0x9f, 0x50, - 0xfd, 0x2c, 0x9c, 0xcf, 0x94, 0x2a, 0xf9, 0x24, 0x8c, 0x2c, 0x3a, 0xab, 0xba, 0x65, 0xbe, 0xcf, - 0x07, 0x45, 0x2e, 0xdc, 0x17, 0xdb, 0x12, 0x5c, 0xde, 0xab, 0xc9, 0xf4, 0xea, 0xef, 0xe7, 0x61, - 0x64, 0xa9, 0xd5, 0x5d, 0x35, 0xa5, 0x65, 0x6e, 0xcf, 0xbb, 0x03, 0xdf, 0x56, 0xcf, 0xef, 0xce, - 0x56, 0x67, 0x93, 0x83, 0xb3, 0xc7, 0xc9, 0xc1, 0x2f, 0x47, 0xde, 0x84, 0x52, 0x07, 0xbf, 0x23, - 0x7e, 0x6e, 0xcc, 0xbf, 0x2e, 0xeb, 0xdc, 0x98, 0x97, 0x61, 0xb3, 0x41, 0x73, 0x1f, 0xb3, 0x41, - 0x58, 0x56, 0x12, 0x68, 0xb8, 0xa4, 0x1d, 0x0b, 0xf4, 0x40, 0x04, 0x1a, 0x2e, 0x5f, 0xc7, 0x02, - 0xdd, 0x87, 0x40, 0x7f, 0x25, 0x0f, 0x63, 0xd1, 0x2a, 0xc9, 0xc7, 0x61, 0x98, 0x57, 0xc3, 0x4f, - 0xb1, 0x72, 0x92, 0x0b, 0x74, 0x08, 0xd6, 0x80, 0xff, 0x10, 0xc7, 0x71, 0x27, 0xd6, 0x74, 0xb7, - 0x11, 0x9e, 0x27, 0xf1, 0xdb, 0xe6, 0x41, 0xee, 0xb7, 0x15, 0x43, 0x69, 0x63, 0x6b, 0xba, 0x3b, - 0x15, 0xfe, 0x26, 0xd3, 0x40, 0x1c, 0xda, 0x75, 0x69, 0x94, 0x41, 0x11, 0x19, 0x88, 0x2c, 0xf2, - 0x71, 0xac, 0x76, 0x92, 0xc3, 0x64, 0x36, 0x9f, 0x0b, 0x9a, 0x8d, 0xca, 0xd0, 0xbf, 0x83, 0x14, - 0xf7, 0x12, 0x7d, 0xfa, 0xa1, 0x2c, 0x27, 0xa8, 0xea, 0x9e, 0xce, 0x8f, 0x10, 0xfc, 0x0e, 0x50, - 0xbf, 0xe1, 0x41, 0xff, 0xa2, 0x45, 0x17, 0xef, 0x93, 0x17, 0x61, 0x88, 0x29, 0xcc, 0x9c, 0xcd, - 0xfa, 0x32, 0x27, 0xbc, 0x3d, 0x24, 0x4d, 0x42, 0xc4, 0x6c, 0x9f, 0x16, 0x52, 0x91, 0x1b, 0x00, - 0xe1, 0x83, 0x38, 0xa1, 0x7d, 0x44, 0x2e, 0xc3, 0x31, 0xb3, 0x7d, 0x9a, 0x44, 0xe7, 0x97, 0x12, - 0xcf, 0x89, 0x0a, 0xc9, 0x52, 0x1c, 0xe3, 0x97, 0x12, 0xe3, 0x63, 0x0e, 0x08, 0xfb, 0xb5, 0xa4, - 0xbb, 0xee, 0xba, 0xed, 0x18, 0x53, 0x6b, 0xba, 0xb5, 0x4a, 0xe3, 0x7b, 0xbd, 0x24, 0xc5, 0x6c, - 0x9f, 0x96, 0x52, 0x8e, 0xbc, 0x0e, 0x23, 0xb2, 0xfb, 0x6b, 0xdc, 0x45, 0x45, 0xc6, 0xcd, 0xf6, - 0x69, 0x11, 0x5a, 0xf2, 0x2a, 0x0c, 0x8b, 0xdf, 0xb7, 0x6c, 0x71, 0xff, 0x2d, 0x45, 0x4e, 0x92, - 0x50, 0xb3, 0x7d, 0x9a, 0x4c, 0x29, 0x55, 0xba, 0xe4, 0x98, 0x96, 0x27, 0x5e, 0x54, 0xc7, 0x2b, - 0x45, 0x9c, 0x54, 0x29, 0xfe, 0x26, 0x6f, 0xc1, 0x68, 0x10, 0x92, 0xea, 0x3d, 0xda, 0xf4, 0xc4, - 0x51, 0xfd, 0x99, 0x58, 0x61, 0x8e, 0x9c, 0xed, 0xd3, 0xa2, 0xd4, 0xe4, 0x0a, 0x94, 0x34, 0xea, - 0x9a, 0xef, 0xfb, 0x97, 0xdb, 0x63, 0xd2, 0x38, 0x37, 0xdf, 0x67, 0x52, 0x12, 0x78, 0xd6, 0x3b, - 0xe1, 0x6d, 0xba, 0x38, 0x58, 0x27, 0xb1, 0x5a, 0xa6, 0x2d, 0x83, 0xf5, 0x8e, 0xe4, 0x4a, 0xf1, - 0xa9, 0x30, 0x50, 0x97, 0xc8, 0x53, 0x3b, 0x1c, 0x8f, 0x88, 0x20, 0x63, 0x67, 0xfb, 0xb4, 0x18, - 0xbd, 0x24, 0xd5, 0xaa, 0xe9, 0x3e, 0x10, 0x01, 0x56, 0xe3, 0x52, 0x65, 0x28, 0x49, 0xaa, 0xec, - 0xa7, 0x54, 0xf5, 0x02, 0xf5, 0xd6, 0x6d, 0xe7, 0x81, 0x08, 0xa7, 0x1a, 0xaf, 0x5a, 0x60, 0xa5, - 0xaa, 0x05, 0x44, 0xae, 0x9a, 0x0d, 0xb8, 0xb1, 0xf4, 0xaa, 0x75, 0x4f, 0x97, 0xab, 0xe6, 0xe7, - 0x86, 0x7e, 0x27, 0xcd, 0x51, 0xfd, 0x21, 0x55, 0x4e, 0xa4, 0x76, 0x28, 0xe2, 0xa4, 0x0e, 0xc5, - 0xdf, 0xac, 0x52, 0x29, 0x89, 0xbd, 0x32, 0x1e, 0xad, 0x54, 0x42, 0xb1, 0x4a, 0xe5, 0x74, 0xf7, - 0x37, 0xe4, 0x4c, 0xe9, 0xca, 0xc9, 0x68, 0x07, 0x85, 0x18, 0xd6, 0x41, 0x52, 0x46, 0xf5, 0x32, - 0x66, 0x61, 0x56, 0x08, 0x92, 0x0f, 0x07, 0x2d, 0x9c, 0x5a, 0x9a, 0xed, 0xd3, 0x30, 0x3f, 0xb3, - 0xca, 0xf3, 0x7b, 0x2b, 0xa7, 0x90, 0x62, 0xc4, 0xa7, 0x60, 0xb0, 0xd9, 0x3e, 0x8d, 0xe7, 0xfe, - 0x7e, 0x51, 0xca, 0x81, 0xa8, 0x9c, 0x8e, 0x4e, 0x11, 0x01, 0x82, 0x4d, 0x11, 0x61, 0xa6, 0xc4, - 0x99, 0x64, 0xa6, 0x3f, 0xe5, 0x4c, 0x74, 0xa9, 0x89, 0xe3, 0x67, 0xfb, 0xb4, 0x64, 0x76, 0xc0, - 0x57, 0x23, 0xc9, 0xef, 0x94, 0xb3, 0xb1, 0x70, 0x65, 0x21, 0x8a, 0x89, 0x4b, 0x4e, 0x93, 0xb7, - 0x08, 0xa7, 0x78, 0xee, 0x5c, 0x11, 0x70, 0x4c, 0x4c, 0x56, 0xe7, 0xa2, 0xdb, 0xac, 0x14, 0x92, - 0xd9, 0x3e, 0x2d, 0xad, 0x24, 0x99, 0x4a, 0xa4, 0xa0, 0x53, 0x94, 0xa8, 0x27, 0x4f, 0x0c, 0x3d, - 0xdb, 0xa7, 0x25, 0x92, 0xd6, 0xdd, 0x90, 0x73, 0xbf, 0x29, 0xe7, 0xa3, 0x9d, 0x18, 0x62, 0x58, - 0x27, 0x4a, 0x39, 0xe2, 0x6e, 0xc8, 0xf9, 0xc0, 0x94, 0x0b, 0xc9, 0x52, 0xe1, 0xcc, 0x29, 0xe5, - 0x0d, 0xd3, 0xd2, 0x53, 0x1c, 0x29, 0x4f, 0x88, 0x44, 0xc7, 0xa2, 0x7c, 0x1a, 0xcd, 0x6c, 0x9f, - 0x96, 0x9e, 0x1e, 0x49, 0x4b, 0xcf, 0x0d, 0xa4, 0x5c, 0xec, 0xc5, 0x33, 0x68, 0x5d, 0x7a, 0x5e, - 0x21, 0xbd, 0x47, 0xa6, 0x16, 0xe5, 0x52, 0x74, 0x43, 0x96, 0x49, 0x38, 0xdb, 0xa7, 0xf5, 0xc8, - 0xf7, 0x72, 0x27, 0x23, 0x6d, 0x8a, 0xf2, 0x64, 0x34, 0xcf, 0x79, 0x2a, 0xd1, 0x6c, 0x9f, 0x96, - 0x91, 0x74, 0xe5, 0x4e, 0x46, 0x56, 0x0d, 0xa5, 0xdc, 0x93, 0x6d, 0x20, 0x8f, 0x8c, 0x9c, 0x1c, - 0x8b, 0xa9, 0x09, 0x29, 0x94, 0xa7, 0xa2, 0xaa, 0x9b, 0x42, 0xc2, 0x54, 0x37, 0x2d, 0x95, 0xc5, - 0x62, 0x6a, 0x06, 0x05, 0xe5, 0xe9, 0x1e, 0x0c, 0x83, 0x36, 0xa6, 0xe6, 0x5e, 0x58, 0x4c, 0x4d, - 0x61, 0xa0, 0xa8, 0x51, 0x86, 0x29, 0x24, 0x8c, 0x61, 0x5a, 0xf2, 0x83, 0xc5, 0xd4, 0x48, 0xf7, - 0xca, 0x33, 0x3d, 0x18, 0x86, 0x2d, 0x4c, 0x8b, 0x91, 0xff, 0x6a, 0x24, 0xd4, 0xbc, 0xf2, 0x91, - 0xe8, 0xbc, 0x21, 0xa1, 0xd8, 0xbc, 0x21, 0x07, 0xa5, 0x9f, 0x4a, 0xc4, 0xc1, 0x55, 0x9e, 0x8d, - 0x0e, 0xf3, 0x18, 0x9a, 0x0d, 0xf3, 0x78, 0xe4, 0xdc, 0xa9, 0x44, 0x3c, 0x50, 0xe5, 0x72, 0x16, - 0x13, 0x44, 0x47, 0x99, 0xf0, 0x08, 0xa2, 0xb5, 0x94, 0x80, 0x94, 0xca, 0x47, 0xa3, 0x5e, 0xe8, - 0x09, 0x82, 0xd9, 0x3e, 0x2d, 0x25, 0x8c, 0xa5, 0x96, 0x1e, 0x7d, 0x49, 0xb9, 0x12, 0x1d, 0xb6, - 0x69, 0x34, 0x6c, 0xd8, 0xa6, 0x46, 0x6e, 0x9a, 0x4b, 0x7b, 0x2a, 0xa3, 0x5c, 0x8d, 0x1a, 0x66, - 0x49, 0x0a, 0x66, 0x98, 0xa5, 0x3c, 0xb1, 0xd1, 0xd2, 0x23, 0x02, 0x29, 0xcf, 0xf5, 0x6c, 0x21, - 0xd2, 0xa4, 0xb4, 0x90, 0x07, 0xc8, 0x09, 0x6d, 0xa7, 0x3b, 0x9d, 0x96, 0xad, 0x1b, 0xca, 0xc7, - 0x52, 0x6d, 0x27, 0x8e, 0x94, 0x6c, 0x27, 0x0e, 0x60, 0xab, 0xbc, 0xfc, 0x22, 0x43, 0x79, 0x3e, - 0xba, 0xca, 0xcb, 0x38, 0xb6, 0xca, 0x47, 0x5e, 0x6f, 0x4c, 0x25, 0x5e, 0x2f, 0x28, 0x2f, 0x44, - 0x15, 0x20, 0x86, 0x66, 0x0a, 0x10, 0x7f, 0xef, 0xf0, 0xf9, 0x6c, 0x7f, 0x7f, 0x65, 0x02, 0xb9, - 0x3d, 0xe5, 0x73, 0xcb, 0xa2, 0x9b, 0xed, 0xd3, 0xb2, 0xdf, 0x0c, 0xd4, 0x52, 0xdc, 0xf7, 0x95, - 0x6b, 0x51, 0x05, 0x4b, 0x10, 0x30, 0x05, 0x4b, 0x3a, 0xfd, 0xd7, 0x52, 0xfc, 0xef, 0x95, 0x8f, - 0x67, 0xb2, 0x0a, 0xbe, 0x39, 0xc5, 0x6b, 0xff, 0x86, 0xec, 0x40, 0xaf, 0xbc, 0x18, 0x5d, 0xec, - 0x42, 0x0c, 0x5b, 0xec, 0x24, 0x47, 0xfb, 0x1b, 0xb2, 0xeb, 0xb8, 0x72, 0x3d, 0x59, 0x2a, 0x5c, - 0x22, 0x25, 0x17, 0x73, 0x2d, 0xdd, 0xe3, 0x5a, 0x79, 0x29, 0xaa, 0x75, 0x69, 0x34, 0x4c, 0xeb, - 0x52, 0xbd, 0xb5, 0x67, 0x92, 0x8e, 0xd3, 0xca, 0x8d, 0xf8, 0x26, 0x3b, 0x8a, 0x67, 0x96, 0x4f, - 0xc2, 0xd9, 0xfa, 0x53, 0xf1, 0xd0, 0x80, 0xca, 0xcb, 0xb1, 0x4b, 0xea, 0x08, 0x96, 0xd9, 0xb7, - 0xb1, 0x50, 0x82, 0x9f, 0x8a, 0x47, 0xd3, 0x53, 0x5e, 0x49, 0xe7, 0x10, 0xe8, 0x4a, 0x3c, 0xfa, - 0xde, 0xa7, 0xe2, 0x01, 0xe8, 0x94, 0x57, 0xd3, 0x39, 0x04, 0xd2, 0x8d, 0x07, 0xac, 0x7b, 0x51, - 0x0a, 0x89, 0xaf, 0x7c, 0x22, 0x6a, 0x3a, 0x06, 0x08, 0x66, 0x3a, 0x86, 0x81, 0xf3, 0x5f, 0x94, - 0x42, 0xc9, 0x2b, 0xaf, 0x25, 0x8a, 0x04, 0x8d, 0x95, 0x02, 0xce, 0xbf, 0x28, 0x85, 0x60, 0x57, - 0x5e, 0x4f, 0x14, 0x09, 0x5a, 0x27, 0x05, 0x6a, 0x37, 0x7a, 0xbd, 0xb6, 0x55, 0xde, 0x88, 0x1e, - 0x5d, 0x67, 0x53, 0xce, 0xf6, 0x69, 0xbd, 0x5e, 0xed, 0x7e, 0x3e, 0xdb, 0x0d, 0x5d, 0x79, 0x33, - 0x3a, 0x84, 0xb3, 0xe8, 0xd8, 0x10, 0xce, 0x74, 0x65, 0x7f, 0x2b, 0x16, 0x79, 0x43, 0x79, 0x2b, - 0x3a, 0xc5, 0x45, 0x90, 0x6c, 0x8a, 0x8b, 0xc7, 0xe9, 0x88, 0x84, 0x94, 0x50, 0x3e, 0x19, 0x9d, - 0xe2, 0x64, 0x1c, 0x9b, 0xe2, 0x22, 0xe1, 0x27, 0xa6, 0x12, 0x91, 0x0e, 0x94, 0xb7, 0xa3, 0x53, - 0x5c, 0x0c, 0xcd, 0xa6, 0xb8, 0x78, 0x6c, 0x84, 0xb7, 0x62, 0x0f, 0xfe, 0x95, 0x4f, 0xa5, 0xb7, - 0x1f, 0x91, 0x72, 0xfb, 0x79, 0x78, 0x00, 0x2d, 0xfd, 0xe5, 0xba, 0x52, 0x89, 0x8e, 0xdf, 0x34, - 0x1a, 0x36, 0x7e, 0x53, 0x5f, 0xbd, 0xc7, 0x37, 0x0e, 0x42, 0xab, 0x26, 0x7b, 0x6c, 0x1c, 0x42, - 0x53, 0x24, 0x05, 0x1c, 0xd9, 0x23, 0xf3, 0x8d, 0xd0, 0x54, 0xc6, 0x1e, 0xd9, 0xdf, 0x06, 0xc5, - 0xe8, 0xd9, 0xec, 0x9a, 0xf0, 0x8a, 0x56, 0xaa, 0xd1, 0xd9, 0x35, 0x41, 0xc0, 0x66, 0xd7, 0xa4, - 0x2f, 0xf5, 0x0c, 0x8c, 0x0b, 0x2d, 0xe2, 0xce, 0xde, 0xa6, 0xb5, 0xaa, 0x4c, 0xc7, 0x5e, 0x87, - 0xc6, 0xf0, 0x6c, 0x76, 0x8a, 0xc3, 0x70, 0xbd, 0xe6, 0xb0, 0xa9, 0x96, 0xd9, 0x59, 0xb1, 0x75, - 0xc7, 0xa8, 0x53, 0xcb, 0x50, 0x66, 0x62, 0xeb, 0x75, 0x0a, 0x0d, 0xae, 0xd7, 0x29, 0x70, 0x0c, - 0x68, 0x17, 0x83, 0x6b, 0xb4, 0x49, 0xcd, 0x87, 0x54, 0xb9, 0x89, 0x6c, 0xcb, 0x59, 0x6c, 0x05, - 0xd9, 0x6c, 0x9f, 0x96, 0xc5, 0x81, 0xd9, 0xea, 0xf3, 0x1b, 0xf5, 0x77, 0xe6, 0x82, 0x60, 0x09, - 0x4b, 0x0e, 0xed, 0xe8, 0x0e, 0x55, 0x66, 0xa3, 0xb6, 0x7a, 0x2a, 0x11, 0xb3, 0xd5, 0x53, 0x11, - 0x49, 0xb6, 0xfe, 0x58, 0xa8, 0xf5, 0x62, 0x1b, 0x8e, 0x88, 0xf4, 0xd2, 0x6c, 0x76, 0x8a, 0x22, - 0x98, 0x80, 0xe6, 0x6c, 0x6b, 0x15, 0x4f, 0x2a, 0x6e, 0x45, 0x67, 0xa7, 0x6c, 0x4a, 0x36, 0x3b, - 0x65, 0x63, 0x99, 0xaa, 0x47, 0xb1, 0x7c, 0x0c, 0xde, 0x8e, 0xaa, 0x7a, 0x0a, 0x09, 0x53, 0xf5, - 0x14, 0x70, 0x92, 0xa1, 0x46, 0x5d, 0xea, 0x29, 0x73, 0xbd, 0x18, 0x22, 0x49, 0x92, 0x21, 0x82, - 0x93, 0x0c, 0x67, 0xa8, 0xd7, 0x5c, 0x53, 0xe6, 0x7b, 0x31, 0x44, 0x92, 0x24, 0x43, 0x04, 0xb3, - 0xcd, 0x66, 0x14, 0x3c, 0xd9, 0x6d, 0x3d, 0xf0, 0xfb, 0x6c, 0x21, 0xba, 0xd9, 0xcc, 0x24, 0x64, - 0x9b, 0xcd, 0x4c, 0x24, 0xf9, 0xb1, 0x1d, 0x7b, 0xed, 0x2b, 0x8b, 0x58, 0xe1, 0x44, 0x68, 0x17, - 0xec, 0xa4, 0xd4, 0x6c, 0x9f, 0xb6, 0xd3, 0x57, 0x01, 0x1f, 0x0b, 0x5c, 0x5c, 0x95, 0x25, 0xac, - 0xea, 0x44, 0x70, 0x56, 0xc1, 0xc1, 0xb3, 0x7d, 0x5a, 0xe0, 0x04, 0xfb, 0x2a, 0x0c, 0xe3, 0x47, - 0xd5, 0x2c, 0xd3, 0xab, 0x4e, 0x2a, 0xef, 0x44, 0xb7, 0x4c, 0x12, 0x8a, 0x6d, 0x99, 0xa4, 0x9f, - 0x6c, 0x12, 0xc7, 0x9f, 0x7c, 0x8a, 0xa9, 0x4e, 0x2a, 0x5a, 0x74, 0x12, 0x8f, 0x20, 0xd9, 0x24, - 0x1e, 0x01, 0x04, 0xf5, 0x56, 0x1d, 0xbb, 0x53, 0x9d, 0x54, 0xea, 0x29, 0xf5, 0x72, 0x54, 0x50, - 0x2f, 0xff, 0x19, 0xd4, 0x5b, 0x5f, 0xeb, 0x7a, 0x55, 0xf6, 0x8d, 0xcb, 0x29, 0xf5, 0xfa, 0xc8, - 0xa0, 0x5e, 0x1f, 0xc0, 0xa6, 0x42, 0x04, 0x2c, 0x39, 0x36, 0x9b, 0xb4, 0x6f, 0x9b, 0xad, 0x96, - 0x72, 0x27, 0x3a, 0x15, 0xc6, 0xf1, 0x6c, 0x2a, 0x8c, 0xc3, 0x98, 0xe9, 0xc9, 0x5b, 0x45, 0x57, - 0xba, 0xab, 0xca, 0xdd, 0xa8, 0xe9, 0x19, 0x62, 0x98, 0xe9, 0x19, 0xfe, 0xc2, 0xdd, 0x05, 0xfb, - 0xa5, 0xd1, 0xfb, 0x0e, 0x75, 0xd7, 0x94, 0x7b, 0xb1, 0xdd, 0x85, 0x84, 0xc3, 0xdd, 0x85, 0xf4, - 0x9b, 0xac, 0xc2, 0x13, 0x91, 0x85, 0xc6, 0xbf, 0xb4, 0xa9, 0x53, 0xdd, 0x69, 0xae, 0x29, 0x9f, - 0x46, 0x56, 0xcf, 0xa4, 0x2e, 0x55, 0x51, 0xd2, 0xd9, 0x3e, 0xad, 0x17, 0x27, 0xdc, 0x96, 0xbf, - 0x33, 0xc7, 0xe3, 0xd6, 0x6a, 0x4b, 0x53, 0xfe, 0x26, 0xf4, 0xdd, 0xd8, 0xb6, 0x3c, 0x49, 0x82, - 0xdb, 0xf2, 0x24, 0x98, 0x74, 0xe0, 0xc9, 0xd8, 0x56, 0x6d, 0x5e, 0x6f, 0xb1, 0x7d, 0x09, 0x35, - 0x96, 0xf4, 0xe6, 0x03, 0xea, 0x29, 0x9f, 0x41, 0xde, 0x97, 0x33, 0x36, 0x7c, 0x31, 0xea, 0xd9, - 0x3e, 0x6d, 0x1b, 0x7e, 0x44, 0x85, 0x62, 0x7d, 0x66, 0x79, 0x49, 0xf9, 0x6c, 0xf4, 0x7c, 0x93, - 0xc1, 0x66, 0xfb, 0x34, 0xc4, 0x31, 0x2b, 0xed, 0x4e, 0x67, 0xd5, 0xd1, 0x0d, 0xca, 0x0d, 0x2d, - 0xb4, 0xdd, 0x84, 0x01, 0xfa, 0x03, 0x51, 0x2b, 0x2d, 0x8b, 0x8e, 0x59, 0x69, 0x59, 0x38, 0xa6, - 0xa8, 0x91, 0x14, 0x2d, 0xca, 0xe7, 0xa2, 0x8a, 0x1a, 0x41, 0x32, 0x45, 0x8d, 0x26, 0x74, 0xf9, - 0x34, 0x9c, 0x0d, 0xf6, 0xf3, 0x62, 0xfd, 0xe5, 0x9d, 0xa6, 0x7c, 0x1e, 0xf9, 0x3c, 0x99, 0xb8, - 0x0c, 0x88, 0x50, 0xcd, 0xf6, 0x69, 0x19, 0xe5, 0xd9, 0x8a, 0x9b, 0x48, 0x61, 0x26, 0xcc, 0x8b, - 0x1f, 0x8c, 0xae, 0xb8, 0x19, 0x64, 0x6c, 0xc5, 0xcd, 0x40, 0xa5, 0x32, 0x17, 0x42, 0xd5, 0xb7, - 0x61, 0x1e, 0xc8, 0x34, 0x8b, 0x43, 0x2a, 0x73, 0x61, 0xa9, 0xad, 0x6c, 0xc3, 0x3c, 0xb0, 0xd6, - 0xb2, 0x38, 0x90, 0x2b, 0x50, 0xaa, 0xd7, 0xe7, 0xb5, 0xae, 0xa5, 0x34, 0x63, 0xbe, 0xbd, 0x08, - 0x9d, 0xed, 0xd3, 0x04, 0x9e, 0x99, 0x41, 0xd3, 0x2d, 0xdd, 0xf5, 0xcc, 0xa6, 0x8b, 0x23, 0xc6, - 0x1f, 0x21, 0x46, 0xd4, 0x0c, 0x4a, 0xa3, 0x61, 0x66, 0x50, 0x1a, 0x9c, 0xd9, 0x8b, 0x53, 0xba, - 0xeb, 0xea, 0x96, 0xe1, 0xe8, 0x93, 0xb8, 0x4c, 0xd0, 0xd8, 0xdb, 0xb1, 0x08, 0x96, 0xd9, 0x8b, - 0x51, 0x08, 0x1e, 0xbe, 0xfb, 0x10, 0xdf, 0xcc, 0xb9, 0x1f, 0x3b, 0x7c, 0x8f, 0xe1, 0xf1, 0xf0, - 0x3d, 0x06, 0x43, 0xbb, 0xd3, 0x87, 0x69, 0x74, 0xd5, 0x64, 0x22, 0x52, 0x56, 0x63, 0x76, 0x67, - 0x9c, 0x00, 0xed, 0xce, 0x38, 0x30, 0xd2, 0x24, 0x7f, 0xb9, 0x5d, 0xcb, 0x68, 0x52, 0xb8, 0xca, - 0x26, 0xca, 0xb0, 0xf5, 0x3b, 0x1c, 0x1c, 0xd5, 0x0d, 0x4b, 0x6f, 0xdb, 0xd5, 0x49, 0x5f, 0xea, - 0x66, 0x74, 0xfd, 0xce, 0x24, 0x64, 0xeb, 0x77, 0x26, 0x92, 0xcd, 0xae, 0xfe, 0x46, 0x6b, 0x4d, - 0x77, 0xa8, 0x51, 0x35, 0x1d, 0x3c, 0x59, 0xdc, 0xe0, 0x5b, 0xc3, 0xf7, 0xa2, 0xb3, 0x6b, 0x0f, - 0x52, 0x36, 0xbb, 0xf6, 0x40, 0x33, 0x23, 0x2f, 0x1d, 0xad, 0x51, 0xdd, 0x50, 0x1e, 0x44, 0x8d, - 0xbc, 0x6c, 0x4a, 0x66, 0xe4, 0x65, 0x63, 0xb3, 0x3f, 0xe7, 0x9e, 0x63, 0x7a, 0x54, 0x69, 0xed, - 0xe4, 0x73, 0x90, 0x34, 0xfb, 0x73, 0x10, 0xcd, 0x36, 0x84, 0xf1, 0x0e, 0x69, 0x47, 0x37, 0x84, - 0xc9, 0x6e, 0x88, 0x97, 0x60, 0x16, 0x8b, 0x78, 0x42, 0xa8, 0x58, 0x51, 0x8b, 0x45, 0x80, 0x99, - 0xc5, 0x12, 0x3e, 0x32, 0x8c, 0x3c, 0x1c, 0x53, 0xec, 0xe8, 0x1a, 0x2a, 0xe3, 0xd8, 0x1a, 0x1a, - 0x79, 0x64, 0xf6, 0x6a, 0xe4, 0x55, 0x84, 0xd2, 0x89, 0x5a, 0x1d, 0x12, 0x8a, 0x59, 0x1d, 0xf2, - 0xfb, 0x89, 0x29, 0x38, 0x81, 0xb7, 0xe0, 0x5a, 0x37, 0xb8, 0xc7, 0xf9, 0xa1, 0xe8, 0x67, 0xc6, - 0xd0, 0xec, 0x33, 0x63, 0xa0, 0x08, 0x13, 0x31, 0x6d, 0x39, 0x19, 0x4c, 0xc2, 0xf3, 0xc1, 0x18, - 0x88, 0xcc, 0x01, 0xa9, 0x57, 0xe6, 0xe7, 0x6a, 0xc6, 0x92, 0x7c, 0x45, 0xe6, 0x46, 0x4f, 0x60, - 0x93, 0x14, 0xb3, 0x7d, 0x5a, 0x4a, 0x39, 0xf2, 0x1e, 0x5c, 0x14, 0x50, 0xf1, 0x3e, 0x7c, 0xc9, - 0xb1, 0x1f, 0x9a, 0x46, 0xb0, 0x20, 0x78, 0x51, 0xaf, 0xbb, 0x5e, 0xb4, 0xb3, 0x7d, 0x5a, 0x4f, - 0x5e, 0xd9, 0x75, 0x89, 0xf5, 0xa1, 0xbb, 0x93, 0xba, 0x82, 0x45, 0xa2, 0x27, 0xaf, 0xec, 0xba, - 0x84, 0xdc, 0x1f, 0xee, 0xa4, 0xae, 0xa0, 0x13, 0x7a, 0xf2, 0x22, 0x2e, 0x94, 0x7b, 0xe1, 0x2b, - 0xad, 0x96, 0xb2, 0x8e, 0xd5, 0x7d, 0x74, 0x27, 0xd5, 0x55, 0xd0, 0xe0, 0xdc, 0x8e, 0x23, 0x9b, - 0xa5, 0x17, 0x3b, 0xd4, 0xaa, 0x47, 0x16, 0xa0, 0x47, 0xd1, 0x59, 0x3a, 0x41, 0xc0, 0x66, 0xe9, - 0x04, 0x90, 0x0d, 0x28, 0xf9, 0x71, 0x8d, 0xb2, 0x11, 0x1d, 0x50, 0x32, 0x8e, 0x0d, 0xa8, 0xc8, - 0x43, 0x9c, 0x45, 0x38, 0xb5, 0xf8, 0xc0, 0xd3, 0x7d, 0x0b, 0xd2, 0x15, 0x5d, 0xf9, 0x7e, 0xec, - 0x92, 0x29, 0x49, 0x82, 0x97, 0x4c, 0x49, 0x30, 0x1b, 0x23, 0x0c, 0x5c, 0xdf, 0xb0, 0x9a, 0x33, - 0xba, 0xd9, 0xea, 0x3a, 0x54, 0xf9, 0x33, 0xd1, 0x31, 0x12, 0x43, 0xb3, 0x31, 0x12, 0x03, 0xb1, - 0x05, 0x9a, 0x81, 0x2a, 0xae, 0x6b, 0xae, 0x5a, 0x62, 0x5f, 0xd9, 0x6d, 0x79, 0xca, 0xbf, 0x15, - 0x5d, 0xa0, 0xd3, 0x68, 0xd8, 0x02, 0x9d, 0x06, 0xc7, 0x53, 0x27, 0xd6, 0x0b, 0x6c, 0xf1, 0x90, - 0xef, 0x2a, 0xff, 0xed, 0xd8, 0xa9, 0x53, 0x0a, 0x0d, 0x9e, 0x3a, 0xa5, 0xc0, 0xd9, 0xfa, 0xc8, - 0x6d, 0xb2, 0x39, 0x33, 0xb8, 0xab, 0xfe, 0x77, 0xa2, 0xeb, 0x63, 0x1c, 0xcf, 0xd6, 0xc7, 0x38, - 0x2c, 0xca, 0x47, 0x74, 0xc1, 0xbf, 0x9b, 0xc5, 0x27, 0x90, 0x7f, 0xa2, 0x0c, 0xb9, 0x29, 0xf3, - 0x11, 0x23, 0xe5, 0x87, 0x73, 0x59, 0x8c, 0x82, 0xe1, 0x91, 0x28, 0x14, 0x65, 0xa4, 0xd1, 0x87, - 0x26, 0x5d, 0x57, 0xbe, 0x90, 0xc9, 0x88, 0x13, 0x44, 0x19, 0x71, 0x18, 0x79, 0x17, 0xce, 0x86, - 0xb0, 0x79, 0xda, 0x5e, 0x09, 0x66, 0xa6, 0x3f, 0x9b, 0x8b, 0x9a, 0xc1, 0xe9, 0x64, 0xcc, 0x0c, - 0x4e, 0xc7, 0xa4, 0xb1, 0x16, 0xa2, 0xfb, 0xf7, 0xb6, 0x61, 0x1d, 0x48, 0x30, 0x83, 0x41, 0x1a, - 0x6b, 0x21, 0xcd, 0x1f, 0xd9, 0x86, 0x75, 0x20, 0xd3, 0x0c, 0x06, 0xe4, 0xc7, 0x73, 0x70, 0x39, - 0x1d, 0x55, 0x69, 0xb5, 0x66, 0x6c, 0x27, 0xc4, 0x29, 0x7f, 0x2e, 0x17, 0x3d, 0x68, 0xd8, 0x59, - 0xb1, 0xd9, 0x3e, 0x6d, 0x87, 0x15, 0x90, 0x4f, 0xc2, 0x68, 0xa5, 0x6b, 0x98, 0x1e, 0x5e, 0xbc, - 0x31, 0xc3, 0xf9, 0x47, 0x73, 0xb1, 0x2d, 0x8e, 0x8c, 0xc5, 0x2d, 0x8e, 0x0c, 0x20, 0xb7, 0xe0, - 0x64, 0x9d, 0x36, 0xbb, 0x8e, 0xe9, 0x6d, 0x68, 0xb4, 0x63, 0x3b, 0x1e, 0xe3, 0xf1, 0xe7, 0x73, - 0xd1, 0x49, 0x2c, 0x41, 0xc1, 0x26, 0xb1, 0x04, 0x90, 0xdc, 0x4d, 0xdc, 0xca, 0x8b, 0xce, 0xfc, - 0xb1, 0x5c, 0xcf, 0x6b, 0xf9, 0xa0, 0x2f, 0xd3, 0x8b, 0x93, 0xa5, 0xd8, 0x2d, 0xba, 0xe0, 0xfa, - 0xe3, 0xb9, 0x1e, 0xd7, 0xe8, 0xd2, 0x0c, 0x97, 0x04, 0x33, 0x8e, 0x29, 0x49, 0xef, 0x95, 0xbf, - 0x90, 0xeb, 0x71, 0xed, 0x1d, 0x72, 0x4c, 0xcb, 0x97, 0xff, 0x32, 0xf7, 0x14, 0x11, 0x8c, 0x7e, - 0x22, 0x97, 0x74, 0x15, 0x09, 0xca, 0x4b, 0x84, 0xac, 0xd8, 0x1d, 0x37, 0x50, 0xfa, 0x2f, 0xe6, - 0x92, 0xbe, 0x79, 0x61, 0xb1, 0xf0, 0x17, 0xa1, 0x70, 0x61, 0xfa, 0x91, 0x47, 0x1d, 0x4b, 0x6f, - 0x61, 0x77, 0xd6, 0x3d, 0xdb, 0xd1, 0x57, 0xe9, 0xb4, 0xa5, 0xaf, 0xb4, 0xa8, 0xf2, 0x93, 0xb9, - 0xa8, 0x05, 0x9b, 0x4d, 0xca, 0x2c, 0xd8, 0x6c, 0x2c, 0x59, 0x83, 0x27, 0xd2, 0xb0, 0x55, 0xd3, - 0xc5, 0x7a, 0xbe, 0x94, 0x8b, 0x9a, 0xb0, 0x3d, 0x68, 0x99, 0x09, 0xdb, 0x03, 0x4d, 0xae, 0xc3, - 0xd0, 0xa4, 0xed, 0x4f, 0xbf, 0x7f, 0x31, 0xe6, 0x0c, 0x19, 0x60, 0x66, 0xfb, 0xb4, 0x90, 0x4c, - 0x94, 0x11, 0x83, 0xfa, 0xcb, 0xc9, 0x32, 0xe1, 0xe5, 0x53, 0xf0, 0x43, 0x94, 0x11, 0xe2, 0xfe, - 0xf7, 0x93, 0x65, 0xc2, 0x3b, 0xae, 0xe0, 0x07, 0x9b, 0x49, 0x78, 0x8d, 0xf3, 0x33, 0x15, 0x66, - 0xb7, 0x4d, 0xad, 0xe9, 0xad, 0x16, 0xb5, 0x56, 0xa9, 0xf2, 0x95, 0xd8, 0x4c, 0x92, 0x4e, 0xc6, - 0x66, 0x92, 0x74, 0x0c, 0xf9, 0x01, 0x38, 0x77, 0x57, 0x6f, 0x99, 0x46, 0x88, 0xf3, 0x53, 0xa0, - 0x2b, 0x3f, 0x95, 0x8b, 0xee, 0xa6, 0x33, 0xe8, 0xd8, 0x6e, 0x3a, 0x03, 0x45, 0xe6, 0x81, 0xe0, - 0x32, 0x1a, 0xcc, 0x16, 0x6c, 0x7d, 0x56, 0xfe, 0x83, 0x5c, 0xd4, 0x4e, 0x4d, 0x92, 0x30, 0x3b, - 0x35, 0x09, 0x25, 0x8d, 0xec, 0x44, 0x26, 0xca, 0x4f, 0xe7, 0xa2, 0xa7, 0x35, 0x59, 0x84, 0xb3, - 0x7d, 0x5a, 0x76, 0x36, 0x94, 0x9b, 0x30, 0x5e, 0x5f, 0xaa, 0xcd, 0xcc, 0x4c, 0xd7, 0xef, 0xd6, - 0xaa, 0xf8, 0x8a, 0xc2, 0x50, 0x7e, 0x26, 0xb6, 0x62, 0xc5, 0x09, 0xd8, 0x8a, 0x15, 0x87, 0x91, - 0x37, 0x60, 0x84, 0xb5, 0x9f, 0x0d, 0x18, 0xfc, 0xe4, 0xaf, 0xe6, 0xa2, 0xe6, 0x94, 0x8c, 0x64, - 0xe6, 0x94, 0xfc, 0x9b, 0xd4, 0xe1, 0x34, 0x93, 0xe2, 0x92, 0x43, 0xef, 0x53, 0x87, 0x5a, 0x4d, - 0x7f, 0x4c, 0xff, 0x6c, 0x2e, 0x6a, 0x65, 0xa4, 0x11, 0x31, 0x2b, 0x23, 0x0d, 0x4e, 0x1e, 0xc0, - 0xc5, 0xf8, 0x49, 0x90, 0xfc, 0x48, 0x56, 0xf9, 0x4b, 0xb9, 0x98, 0x31, 0xdc, 0x83, 0x18, 0x8d, - 0xe1, 0x1e, 0x78, 0x62, 0xc1, 0x25, 0x71, 0xac, 0x22, 0x1c, 0x2e, 0xe3, 0xb5, 0xfd, 0x1c, 0xaf, - 0xed, 0xd9, 0xd0, 0x21, 0xb0, 0x07, 0xf5, 0x6c, 0x9f, 0xd6, 0x9b, 0x1d, 0xd3, 0xb3, 0x64, 0xba, - 0x0e, 0xe5, 0x2f, 0xe7, 0xd2, 0x3d, 0x52, 0x22, 0x6e, 0xca, 0x69, 0x79, 0x3e, 0xde, 0xcd, 0x4a, - 0x36, 0xa1, 0xfc, 0x95, 0xd8, 0x78, 0x4b, 0x27, 0x63, 0xe3, 0x2d, 0x23, 0x5b, 0xc5, 0x2d, 0x38, - 0xc9, 0x95, 0x7a, 0x49, 0xc7, 0x61, 0x68, 0xad, 0x52, 0x43, 0xf9, 0x0f, 0x63, 0xab, 0x5d, 0x82, - 0x02, 0x5d, 0x7b, 0xe2, 0x40, 0x36, 0x75, 0xd7, 0x3b, 0xba, 0x65, 0xe1, 0x31, 0xab, 0xf2, 0x1f, - 0xc5, 0xa6, 0xee, 0x10, 0x85, 0x8e, 0xbb, 0xc1, 0x2f, 0xa6, 0x09, 0xbd, 0x12, 0x35, 0x29, 0xff, - 0x71, 0x4c, 0x13, 0x7a, 0x11, 0x33, 0x4d, 0xe8, 0x99, 0xf5, 0xe9, 0x6e, 0xc6, 0x83, 0x75, 0xe5, - 0x6b, 0xb1, 0x15, 0x39, 0x95, 0x8a, 0xad, 0xc8, 0xe9, 0xef, 0xdd, 0xef, 0x66, 0x3c, 0xf6, 0x56, - 0x7e, 0xbe, 0x37, 0xdf, 0x70, 0xa5, 0x4f, 0x7f, 0x2b, 0x7e, 0x37, 0xe3, 0xa1, 0xb4, 0xf2, 0x57, - 0x7b, 0xf3, 0x0d, 0x1d, 0xfb, 0xd2, 0xdf, 0x59, 0x37, 0xb2, 0x1f, 0x19, 0x2b, 0x7f, 0x2d, 0x3e, - 0x75, 0x65, 0x10, 0xe2, 0xd4, 0x95, 0xf5, 0x52, 0x79, 0x05, 0xce, 0x73, 0x0d, 0xb9, 0xe9, 0xe8, - 0x9d, 0xb5, 0x3a, 0xf5, 0x3c, 0xd3, 0x5a, 0xf5, 0x77, 0x62, 0xff, 0x49, 0x2e, 0x76, 0x3c, 0x96, - 0x45, 0x89, 0xc7, 0x63, 0x59, 0x48, 0xa6, 0xbc, 0x89, 0xe7, 0xc4, 0xca, 0x5f, 0x8f, 0x29, 0x6f, - 0x82, 0x82, 0x29, 0x6f, 0xf2, 0x15, 0xf2, 0xad, 0x94, 0x57, 0xb3, 0xca, 0x7f, 0x9a, 0xcd, 0x2b, - 0x68, 0x5f, 0xca, 0x63, 0xdb, 0x5b, 0x29, 0x8f, 0x43, 0x95, 0xff, 0x2c, 0x9b, 0x57, 0xe8, 0x83, - 0x94, 0x7c, 0x53, 0xfa, 0x2e, 0x9c, 0xe5, 0xb3, 0xf9, 0x0c, 0x35, 0x68, 0xe4, 0x43, 0x7f, 0x21, - 0x36, 0xf6, 0xd3, 0xc9, 0xf0, 0xc8, 0x3d, 0x15, 0x93, 0xc6, 0x5a, 0xb4, 0xf5, 0x6f, 0x6c, 0xc3, - 0x3a, 0xdc, 0x10, 0xa4, 0x63, 0xd8, 0x7a, 0x23, 0x3f, 0x7e, 0x53, 0x7e, 0x31, 0xb6, 0xde, 0xc8, - 0x48, 0x74, 0xe7, 0x90, 0x5f, 0xca, 0xbd, 0x11, 0x7d, 0xe8, 0xa5, 0xfc, 0xcd, 0xd4, 0xc2, 0x41, - 0x07, 0x44, 0x5f, 0x85, 0xbd, 0x11, 0x7d, 0xd4, 0xa4, 0xfc, 0x52, 0x6a, 0xe1, 0xe0, 0x03, 0xa2, - 0x2f, 0xa0, 0xd8, 0x16, 0xa9, 0xeb, 0xd9, 0x9c, 0x55, 0x64, 0x7a, 0xf8, 0x5b, 0xf1, 0x2d, 0x52, - 0x2a, 0x19, 0x6e, 0x91, 0x52, 0x31, 0x69, 0xac, 0xc5, 0xe7, 0xfd, 0xf2, 0x36, 0xac, 0xa5, 0x8d, - 0x5d, 0x2a, 0x26, 0x8d, 0xb5, 0xf8, 0xf8, 0xaf, 0x6f, 0xc3, 0x5a, 0xda, 0xd8, 0xa5, 0x62, 0x98, - 0x39, 0x16, 0x62, 0xee, 0x52, 0xc7, 0x0d, 0xd5, 0xef, 0x3f, 0x8f, 0x99, 0x63, 0x19, 0x74, 0xcc, - 0x1c, 0xcb, 0x40, 0xa5, 0x72, 0x17, 0x42, 0xf9, 0xc6, 0x76, 0xdc, 0xc3, 0x7b, 0x99, 0x0c, 0x54, - 0x2a, 0x77, 0x21, 0x97, 0xbf, 0xbd, 0x1d, 0xf7, 0xf0, 0x62, 0x26, 0x03, 0xc5, 0x8c, 0xa2, 0xba, - 0xa7, 0x7b, 0x66, 0x73, 0xd6, 0x76, 0x3d, 0x69, 0x91, 0xff, 0x2f, 0x62, 0x46, 0x51, 0x1a, 0x11, - 0x33, 0x8a, 0xd2, 0xe0, 0x49, 0xa6, 0x42, 0x1a, 0xbf, 0xd2, 0x93, 0x69, 0x68, 0x69, 0xa5, 0xc1, - 0x93, 0x4c, 0x85, 0x10, 0xfe, 0xcb, 0x9e, 0x4c, 0x43, 0x4f, 0xf9, 0x34, 0x38, 0xb3, 0x4c, 0xa7, - 0x1c, 0x7b, 0xdd, 0xba, 0x45, 0xd7, 0x69, 0x4b, 0x7c, 0xfa, 0xaf, 0xc6, 0x2c, 0xd3, 0x38, 0x01, - 0xde, 0xa2, 0xc4, 0x60, 0x51, 0x46, 0xe2, 0x73, 0x7f, 0x2d, 0x93, 0x51, 0x78, 0x4c, 0x14, 0x87, - 0x45, 0x19, 0x89, 0x4f, 0xfc, 0xf5, 0x4c, 0x46, 0xe1, 0x31, 0x51, 0x1c, 0x46, 0x2a, 0x30, 0x86, - 0x6f, 0x25, 0x74, 0xd7, 0xf7, 0xfc, 0xfc, 0xad, 0x5c, 0xf4, 0xd6, 0x2b, 0x8a, 0x9e, 0xed, 0xd3, - 0x62, 0x05, 0x64, 0x16, 0xe2, 0x93, 0xbe, 0x95, 0xc1, 0x22, 0xf4, 0x77, 0x8c, 0x42, 0x64, 0x16, - 0xe2, 0x63, 0xfe, 0xab, 0x0c, 0x16, 0xa1, 0xc3, 0x63, 0x14, 0x42, 0x3e, 0x01, 0xc3, 0xf5, 0x99, - 0xe5, 0x25, 0x3f, 0x99, 0xe0, 0xdf, 0xc9, 0xc5, 0x5e, 0x15, 0x85, 0x38, 0x7c, 0x55, 0x14, 0xfe, - 0x24, 0x9f, 0x84, 0xd1, 0x29, 0xdb, 0xf2, 0xf4, 0xa6, 0xbf, 0x01, 0xfd, 0xed, 0xd8, 0x19, 0x4a, - 0x04, 0x3b, 0xdb, 0xa7, 0x45, 0xc9, 0xa5, 0xf2, 0xa2, 0xed, 0xbf, 0x93, 0x5e, 0x3e, 0x68, 0x7a, - 0x94, 0x9c, 0xcd, 0x68, 0xf7, 0x6c, 0xe7, 0x41, 0xcb, 0xd6, 0x0d, 0x3f, 0x7e, 0xa5, 0x68, 0xc8, - 0xdf, 0x8d, 0xcd, 0x68, 0xe9, 0x64, 0x6c, 0x46, 0x4b, 0xc7, 0xa4, 0xb1, 0x16, 0x5d, 0xf4, 0xed, - 0x6d, 0x58, 0x87, 0xf3, 0x70, 0x3a, 0x26, 0x8d, 0xb5, 0xf8, 0xfc, 0xbf, 0xb7, 0x0d, 0xeb, 0x70, - 0x1e, 0x4e, 0xc7, 0x30, 0xd3, 0xfa, 0xa6, 0xe9, 0xf9, 0x0f, 0xdb, 0xfe, 0x7e, 0xcc, 0xb4, 0x0e, - 0x51, 0xcc, 0xb4, 0x0e, 0x7f, 0x11, 0x0a, 0x17, 0x82, 0xa7, 0x92, 0xe1, 0xde, 0xb5, 0x66, 0x3d, - 0x64, 0xfb, 0x63, 0xe5, 0x1f, 0xc4, 0x4e, 0x45, 0xb2, 0x49, 0x67, 0xfb, 0xb4, 0x1e, 0x8c, 0x26, - 0x07, 0xa0, 0x1f, 0x4f, 0xde, 0x6f, 0x95, 0x06, 0xbf, 0x99, 0x1b, 0xff, 0x8d, 0xdc, 0xad, 0xd2, - 0xe0, 0x6f, 0xe4, 0xc6, 0x7f, 0x93, 0xfd, 0xff, 0x9b, 0xb9, 0xf1, 0xdf, 0xca, 0x69, 0xe7, 0xc3, - 0x59, 0xb4, 0xb2, 0x4a, 0x2d, 0x6f, 0xa9, 0xa5, 0x8b, 0x35, 0x20, 0x15, 0xc5, 0x7f, 0xa6, 0xa2, - 0x44, 0x9e, 0xb7, 0xaf, 0xe5, 0x60, 0xa4, 0xee, 0x39, 0x54, 0x6f, 0x8b, 0xb0, 0x89, 0x17, 0x60, - 0x90, 0xfb, 0xca, 0xfb, 0x51, 0x03, 0xb4, 0xe0, 0x37, 0xb9, 0x0c, 0x63, 0x73, 0xba, 0xeb, 0x61, - 0x13, 0x6b, 0x96, 0x41, 0x1f, 0xe1, 0x3b, 0xd1, 0x82, 0x16, 0x83, 0x92, 0x39, 0x4e, 0xc7, 0xcb, - 0x61, 0xa4, 0xdc, 0xc2, 0xb6, 0xd1, 0x02, 0x07, 0xbf, 0xb3, 0x59, 0xee, 0xc3, 0xe0, 0x80, 0xb1, - 0xb2, 0xea, 0xef, 0xe5, 0x20, 0xe1, 0xc5, 0xbf, 0xf7, 0xf0, 0x20, 0x8b, 0x70, 0x22, 0x16, 0x9d, - 0x59, 0x3c, 0x76, 0xdd, 0x61, 0xf0, 0xe6, 0x78, 0x69, 0xf2, 0xd1, 0xe0, 0x91, 0xe5, 0x1d, 0x6d, - 0x4e, 0x44, 0x82, 0xc4, 0x1c, 0x26, 0x5d, 0xa7, 0xa5, 0x49, 0x28, 0x11, 0xe9, 0xeb, 0x7b, 0xe3, - 0x61, 0xe8, 0x59, 0x72, 0x59, 0xc4, 0x2a, 0xc9, 0x85, 0xf1, 0x23, 0xbb, 0x2e, 0x75, 0xe4, 0xf8, - 0x91, 0x18, 0x9b, 0xe4, 0x93, 0x30, 0x52, 0x6b, 0x77, 0xa8, 0xe3, 0xda, 0x96, 0xee, 0xd9, 0x8e, - 0x88, 0xcc, 0x80, 0x31, 0x14, 0x4c, 0x09, 0x2e, 0xc7, 0x50, 0x90, 0xe9, 0xc9, 0x55, 0x3f, 0x0d, - 0x63, 0x01, 0x83, 0xfe, 0xe2, 0x0b, 0xe8, 0x78, 0x16, 0x7e, 0x4e, 0xc1, 0x48, 0xef, 0xb8, 0x3a, - 0x3e, 0xc7, 0x0d, 0x48, 0xbb, 0x0c, 0x20, 0x93, 0x22, 0x05, 0x79, 0x1e, 0x4a, 0xa8, 0xc7, 0x2e, - 0xa6, 0x57, 0x15, 0x51, 0x2d, 0x5b, 0x08, 0x91, 0xe3, 0x67, 0x70, 0x1a, 0x72, 0x1b, 0xc6, 0x43, - 0xdf, 0x8c, 0x9b, 0x8e, 0xdd, 0xed, 0xf8, 0x09, 0x95, 0xca, 0x5b, 0x9b, 0xe5, 0x27, 0x1e, 0x04, - 0xb8, 0xc6, 0x2a, 0x22, 0x25, 0x16, 0x89, 0x82, 0x64, 0x16, 0x4e, 0x84, 0x30, 0x26, 0x22, 0x3f, - 0x91, 0x1b, 0x26, 0xd1, 0x95, 0x78, 0x31, 0x71, 0x46, 0x92, 0xe8, 0xc6, 0x8a, 0x91, 0x1a, 0x0c, - 0xf8, 0x21, 0x2d, 0x07, 0xb7, 0x55, 0xd2, 0x53, 0x22, 0xa4, 0xe5, 0x80, 0x1c, 0xcc, 0xd2, 0x2f, - 0x4f, 0x66, 0x60, 0x4c, 0xb3, 0xbb, 0x1e, 0x5d, 0xb6, 0xc5, 0xa1, 0x86, 0x08, 0x9d, 0x8a, 0x6d, - 0x72, 0x18, 0xa6, 0xe1, 0xd9, 0x8d, 0x26, 0xc7, 0x49, 0x6d, 0x8a, 0x95, 0x22, 0x0b, 0x70, 0x32, - 0xe1, 0xc5, 0x82, 0xaf, 0x77, 0x87, 0x78, 0x70, 0x42, 0xe9, 0xf3, 0x92, 0xcc, 0x92, 0x45, 0xc9, - 0x8f, 0xe6, 0xa0, 0xb4, 0xec, 0xe8, 0xa6, 0xe7, 0x8a, 0x97, 0xbc, 0x67, 0x26, 0xd6, 0x1d, 0xbd, - 0xc3, 0xf4, 0x63, 0x02, 0xa3, 0x3a, 0xdf, 0xd5, 0x5b, 0x5d, 0xea, 0x4e, 0xde, 0x63, 0x5f, 0xf7, - 0x3f, 0x6c, 0x96, 0xdf, 0xe0, 0x31, 0x50, 0x26, 0x9a, 0x76, 0xfb, 0xda, 0xaa, 0xa3, 0x3f, 0x34, - 0x3d, 0xdc, 0x91, 0xe8, 0xad, 0x6b, 0x1e, 0x6d, 0xe1, 0x91, 0xfc, 0x35, 0xbd, 0x63, 0x5e, 0xc3, - 0xec, 0x01, 0xd7, 0x02, 0x4e, 0xbc, 0x06, 0xa6, 0x02, 0x1e, 0xfe, 0x25, 0xab, 0x00, 0xc7, 0x91, - 0x05, 0x00, 0xf1, 0xa9, 0x95, 0x4e, 0x47, 0x3c, 0x0b, 0x96, 0x0e, 0xb2, 0x7d, 0x0c, 0x57, 0xec, - 0x40, 0x60, 0x7a, 0x47, 0x8a, 0x98, 0xad, 0x49, 0x1c, 0x98, 0x16, 0x2c, 0x8b, 0x16, 0xf9, 0x62, - 0x1a, 0x0d, 0x25, 0xee, 0x37, 0x36, 0x45, 0x48, 0xf1, 0x62, 0x64, 0x05, 0x4e, 0x08, 0xbe, 0x41, - 0x7e, 0x9d, 0xb1, 0xe8, 0xac, 0x10, 0x43, 0x73, 0xa5, 0x0d, 0xda, 0x68, 0x08, 0xb0, 0x5c, 0x47, - 0xac, 0x04, 0x99, 0x0c, 0xf3, 0x81, 0x2f, 0xe8, 0x6d, 0xea, 0x2a, 0x27, 0x50, 0x63, 0x2f, 0x6e, - 0x6d, 0x96, 0x15, 0xbf, 0x3c, 0x46, 0x77, 0x95, 0x45, 0x17, 0x2d, 0x22, 0xf3, 0xe0, 0x5a, 0x3f, - 0x9e, 0xc2, 0x23, 0xae, 0xf3, 0xd1, 0x22, 0x64, 0x0a, 0x46, 0x83, 0x57, 0x49, 0x77, 0xee, 0xd4, - 0xaa, 0xf8, 0xee, 0x58, 0x04, 0xf8, 0x8d, 0x65, 0xc0, 0x91, 0x99, 0x44, 0xca, 0x48, 0x71, 0x62, - 0xf8, 0x43, 0xe4, 0x58, 0x9c, 0x98, 0x4e, 0x4a, 0x9c, 0x98, 0x25, 0xf2, 0x16, 0x0c, 0x57, 0xee, - 0xd5, 0x45, 0xfc, 0x1b, 0x57, 0x39, 0x15, 0xa6, 0x53, 0xd3, 0xd7, 0xdd, 0x86, 0x1f, 0x2b, 0x47, - 0x6e, 0xba, 0x4c, 0x4f, 0xa6, 0x61, 0x2c, 0xe2, 0xd8, 0xe8, 0x2a, 0xa7, 0x91, 0x03, 0xb6, 0x5c, - 0x47, 0x4c, 0xc3, 0x11, 0x28, 0x79, 0x78, 0x45, 0x0b, 0x31, 0xad, 0xa9, 0x9a, 0x2e, 0xa6, 0xa6, - 0xd2, 0x28, 0x86, 0xda, 0xc1, 0x57, 0xcc, 0x83, 0x5c, 0x6b, 0x0c, 0x81, 0x6a, 0x38, 0x1c, 0x27, - 0xf7, 0x68, 0xac, 0x18, 0x79, 0x0f, 0x08, 0x26, 0xb3, 0xa2, 0x86, 0x7f, 0xcf, 0x5d, 0xab, 0xba, - 0xca, 0x59, 0x8c, 0x6e, 0x4f, 0xe2, 0xd1, 0x37, 0x6a, 0xd5, 0xc9, 0xcb, 0x62, 0xfa, 0x78, 0x52, - 0xe7, 0xa5, 0x1a, 0x7e, 0xe4, 0x8d, 0x86, 0x19, 0xc9, 0xf4, 0x9d, 0xc2, 0x95, 0xac, 0xc3, 0xb9, - 0x25, 0x87, 0x3e, 0x34, 0xed, 0xae, 0xeb, 0x2f, 0x1f, 0xfe, 0xbc, 0x75, 0x6e, 0xdb, 0x79, 0xeb, - 0x69, 0x51, 0xf1, 0x99, 0x8e, 0x43, 0x1f, 0x36, 0xfc, 0x98, 0xe6, 0x91, 0x90, 0xbc, 0x59, 0xdc, - 0x31, 0x5f, 0xf9, 0xfb, 0x5d, 0x87, 0x0a, 0xb8, 0x49, 0x5d, 0x45, 0x09, 0xa7, 0x5a, 0x1e, 0x86, - 0xc9, 0x0c, 0x70, 0x91, 0x7c, 0xe5, 0xd1, 0x62, 0x44, 0x03, 0x72, 0x73, 0xca, 0xf7, 0x79, 0xa8, - 0x34, 0x79, 0x56, 0x67, 0xe5, 0x3c, 0x32, 0x53, 0x99, 0x58, 0x56, 0x9b, 0x41, 0x7e, 0x83, 0x86, - 0x2e, 0xf0, 0xb2, 0x58, 0x92, 0xa5, 0xc9, 0x1c, 0x8c, 0x2f, 0x39, 0x78, 0x02, 0x7b, 0x9b, 0x6e, - 0x2c, 0xd9, 0x2d, 0xb3, 0xb9, 0x81, 0x8f, 0xa9, 0xc5, 0x54, 0xd9, 0xe1, 0xb8, 0xc6, 0x03, 0xba, - 0xd1, 0xe8, 0x20, 0x56, 0x5e, 0x56, 0xe2, 0x25, 0xe5, 0x78, 0xe3, 0x4f, 0xec, 0x2c, 0xde, 0x38, - 0x85, 0x71, 0xe1, 0x31, 0xf1, 0xc8, 0xa3, 0x16, 0x5b, 0xea, 0x5d, 0xf1, 0x70, 0x5a, 0x89, 0x79, - 0x58, 0x04, 0x78, 0x3e, 0x75, 0x88, 0x51, 0x46, 0x03, 0xb0, 0xdc, 0xb0, 0x78, 0x91, 0x64, 0x50, - 0xee, 0x4b, 0x7b, 0x08, 0xca, 0xfd, 0x7f, 0x14, 0xe4, 0xf9, 0x97, 0x5c, 0x84, 0xa2, 0x94, 0x33, - 0x0b, 0x23, 0x0e, 0x63, 0x7e, 0x81, 0xa2, 0x08, 0xa4, 0x3e, 0x24, 0x6c, 0x97, 0x20, 0x12, 0x14, - 0x26, 0x49, 0x0d, 0xa3, 0xd0, 0x6a, 0x21, 0x01, 0x26, 0xa8, 0xec, 0xae, 0xb4, 0xcc, 0x26, 0x66, - 0x9d, 0x28, 0x48, 0xd1, 0x59, 0x10, 0xca, 0x93, 0x4e, 0x48, 0x24, 0xe4, 0x3a, 0x0c, 0xfb, 0x27, - 0xff, 0x61, 0xc4, 0x6d, 0x4c, 0x46, 0x20, 0x66, 0x6b, 0x91, 0xeb, 0x40, 0x22, 0x22, 0xaf, 0x03, - 0x84, 0xd3, 0x81, 0xb0, 0xb4, 0x70, 0xa9, 0x90, 0x67, 0x0f, 0x79, 0xa9, 0x08, 0xa9, 0xd9, 0xc4, - 0x29, 0xab, 0xa3, 0x9f, 0x92, 0x17, 0x27, 0xce, 0x88, 0x0e, 0xcb, 0x0a, 0x12, 0x2d, 0x42, 0x16, - 0xe1, 0x64, 0x42, 0x03, 0x45, 0x7c, 0xee, 0xa7, 0xb7, 0x36, 0xcb, 0x97, 0x52, 0xd4, 0x57, 0x5e, - 0x98, 0x13, 0x65, 0xc9, 0x33, 0x50, 0xb8, 0xa3, 0xd5, 0x44, 0x8c, 0x60, 0x1e, 0x5e, 0x3a, 0x12, - 0xef, 0x8b, 0x61, 0xc9, 0x6b, 0x00, 0x3c, 0x07, 0xcf, 0x92, 0xed, 0x78, 0x68, 0x51, 0x8c, 0x4e, - 0x9e, 0x67, 0x63, 0x99, 0xe7, 0xe8, 0x69, 0xb0, 0x65, 0x4c, 0xfe, 0xe8, 0x90, 0x58, 0xfd, 0xb3, - 0xf9, 0xc4, 0xb2, 0xc6, 0x04, 0x2f, 0x5a, 0x21, 0x75, 0x3e, 0x0a, 0xde, 0x6f, 0x3a, 0x17, 0xbc, - 0x44, 0x44, 0xae, 0xc0, 0xe0, 0x12, 0x9b, 0x54, 0x9a, 0x76, 0x4b, 0xa8, 0x02, 0x06, 0x8a, 0xeb, - 0x08, 0x98, 0x16, 0x60, 0xc9, 0x75, 0x29, 0x09, 0xb5, 0x14, 0xb1, 0xdf, 0x4f, 0x42, 0x1d, 0x0f, - 0x5d, 0x8f, 0xe9, 0xa8, 0xaf, 0xc7, 0x92, 0xda, 0x89, 0x32, 0x29, 0x4b, 0x6a, 0x98, 0xc4, 0x2e, - 0x30, 0x68, 0xfb, 0xb7, 0x33, 0x68, 0xd5, 0xdf, 0xce, 0x25, 0x87, 0x28, 0xb9, 0x91, 0x0c, 0x9e, - 0x8d, 0xeb, 0x57, 0x00, 0x94, 0x6b, 0x0d, 0xc2, 0x68, 0x47, 0xc2, 0x60, 0xe7, 0xf7, 0x1c, 0x06, - 0xbb, 0xb0, 0xcb, 0x30, 0xd8, 0xea, 0xff, 0x5b, 0xec, 0xf9, 0x38, 0xe0, 0x50, 0xc2, 0x25, 0xbe, - 0xc6, 0x36, 0x65, 0xac, 0xf6, 0x8a, 0x9b, 0xd8, 0x5a, 0x70, 0xdf, 0xe7, 0x86, 0xce, 0x47, 0xa5, - 0xab, 0x45, 0x29, 0xc9, 0xdb, 0x30, 0xe2, 0x7f, 0x00, 0x86, 0x57, 0x97, 0xc2, 0x82, 0x07, 0x0b, - 0x62, 0x2c, 0x10, 0x79, 0xa4, 0x00, 0x79, 0x19, 0x86, 0xd0, 0x1c, 0xea, 0xe8, 0x4d, 0x3f, 0xf6, - 0x3e, 0x0f, 0xd6, 0xef, 0x03, 0xe5, 0x90, 0x80, 0x01, 0x25, 0xf9, 0x1c, 0x94, 0x44, 0x02, 0x9a, - 0x12, 0x2e, 0xd1, 0xd7, 0x76, 0xf0, 0x9a, 0x62, 0x42, 0x4e, 0x3e, 0xc3, 0x37, 0x38, 0x08, 0x88, - 0x6c, 0x70, 0x78, 0xde, 0x99, 0x65, 0x38, 0xb5, 0xe4, 0x50, 0x03, 0xdf, 0xed, 0x4c, 0x3f, 0xea, - 0x38, 0x22, 0x35, 0x10, 0x9f, 0x20, 0x70, 0x7d, 0xeb, 0xf8, 0x68, 0xb6, 0xf2, 0x0a, 0xbc, 0x1c, - 0x00, 0x3c, 0xa5, 0x38, 0x33, 0x7a, 0x78, 0x4b, 0x6e, 0xd3, 0x8d, 0x75, 0xdb, 0x31, 0x78, 0xf6, - 0x1c, 0x31, 0xf5, 0x0b, 0x41, 0x3f, 0x10, 0x28, 0xd9, 0xe8, 0x89, 0x16, 0xba, 0xf0, 0x1a, 0x0c, - 0xef, 0x35, 0x81, 0xcb, 0x37, 0xf2, 0x19, 0xcf, 0xec, 0x1e, 0xdf, 0x1c, 0x9a, 0x41, 0x62, 0xf7, - 0xfe, 0x8c, 0xc4, 0xee, 0x7f, 0x92, 0xcf, 0x78, 0x43, 0xf8, 0x58, 0x27, 0x60, 0x0e, 0x84, 0x11, - 0x4d, 0xc0, 0x1c, 0xe6, 0xbe, 0x36, 0x0d, 0x4d, 0x26, 0x8a, 0xa5, 0x6a, 0x2f, 0x6d, 0x9b, 0xaa, - 0xfd, 0x17, 0x0a, 0xbd, 0xde, 0x58, 0x1e, 0xcb, 0x7e, 0x37, 0xb2, 0xbf, 0x0e, 0xc3, 0x81, 0x64, - 0x6b, 0x55, 0xb4, 0x97, 0x46, 0x83, 0x74, 0x51, 0x1c, 0x8c, 0x65, 0x24, 0x22, 0x72, 0x95, 0xb7, - 0xb5, 0x6e, 0xbe, 0xcf, 0x13, 0x97, 0x8c, 0x8a, 0x94, 0x14, 0xba, 0xa7, 0x37, 0x5c, 0xf3, 0x7d, - 0xaa, 0x05, 0x68, 0x8c, 0x49, 0x9b, 0xf6, 0xdc, 0xf4, 0xb8, 0x8f, 0x76, 0xde, 0x47, 0x29, 0x42, - 0xe4, 0x4f, 0x6c, 0x8f, 0x85, 0xb8, 0x0b, 0x21, 0xfe, 0x71, 0x3e, 0xf5, 0x41, 0xf2, 0xb1, 0x10, - 0x77, 0x33, 0x5b, 0x3c, 0x0f, 0x43, 0x9a, 0xbd, 0xee, 0x4e, 0xe1, 0x9e, 0x88, 0xcf, 0x15, 0x38, - 0x51, 0x3b, 0xf6, 0xba, 0xdb, 0xc0, 0xdd, 0x8e, 0x16, 0x12, 0xa8, 0xdf, 0xcb, 0xf7, 0x78, 0xb2, - 0x7d, 0x2c, 0xf8, 0x0f, 0x72, 0x89, 0xfc, 0xb5, 0x7c, 0xe4, 0x49, 0xf8, 0xe3, 0x2b, 0xec, 0x6b, - 0x00, 0xf5, 0xe6, 0x1a, 0x6d, 0xeb, 0x52, 0xf2, 0x37, 0x3c, 0xb2, 0x70, 0x11, 0x2a, 0x92, 0x86, - 0x87, 0x24, 0xea, 0x37, 0xf3, 0xb1, 0x37, 0xf1, 0xc7, 0xb2, 0xdb, 0xb1, 0xec, 0x02, 0xad, 0x13, - 0xcf, 0xfc, 0x8f, 0x25, 0xb7, 0x53, 0xc9, 0xfd, 0x58, 0x3e, 0x16, 0x11, 0xe1, 0xb1, 0x95, 0x1d, - 0x1b, 0x80, 0xc9, 0x48, 0x0d, 0x8f, 0xad, 0x26, 0x3d, 0x0f, 0x43, 0x42, 0x0e, 0xc1, 0x52, 0xc1, - 0xe7, 0x7d, 0x0e, 0xc4, 0x03, 0xda, 0x80, 0x40, 0xfd, 0x73, 0x79, 0x88, 0x46, 0xaa, 0x78, 0x4c, - 0x75, 0xe8, 0xd7, 0xf2, 0xd1, 0x18, 0x1d, 0x8f, 0xaf, 0xfe, 0x4c, 0x00, 0xd4, 0xbb, 0x2b, 0x4d, - 0xe1, 0x09, 0xd3, 0x2f, 0x9d, 0xf0, 0x07, 0x50, 0x4d, 0xa2, 0x50, 0xff, 0x4d, 0x3e, 0x35, 0x70, - 0xc8, 0xe3, 0x2b, 0xc0, 0x97, 0xf0, 0x54, 0xbc, 0x69, 0x85, 0x13, 0x39, 0x1e, 0x42, 0xb2, 0xf1, - 0x97, 0xc8, 0x18, 0xea, 0x13, 0x92, 0x4f, 0xa4, 0x98, 0x6b, 0x98, 0xcf, 0x24, 0x34, 0xd7, 0xe4, - 0xc3, 0x7c, 0xc9, 0x70, 0xfb, 0xdd, 0xfc, 0x76, 0x71, 0x56, 0x1e, 0xe7, 0x55, 0x75, 0x60, 0x49, - 0xdf, 0xc0, 0x78, 0xa0, 0xac, 0x27, 0x46, 0x78, 0x3e, 0xcb, 0x0e, 0x07, 0xc9, 0xd7, 0x76, 0x82, - 0x4a, 0xfd, 0x17, 0xfd, 0xe9, 0x41, 0x3e, 0x1e, 0x5f, 0x11, 0x5e, 0x84, 0xe2, 0x92, 0xee, 0xad, - 0x09, 0x4d, 0xc6, 0xdb, 0xc0, 0x8e, 0xee, 0xad, 0x69, 0x08, 0x25, 0x57, 0x61, 0x50, 0xd3, 0xd7, - 0xf9, 0x99, 0x67, 0x29, 0xcc, 0x35, 0xea, 0xe8, 0xeb, 0x0d, 0x7e, 0xee, 0x19, 0xa0, 0x89, 0x1a, - 0xe4, 0xba, 0xe5, 0x27, 0xdf, 0x98, 0x68, 0x91, 0xe7, 0xba, 0x0d, 0x32, 0xdc, 0x5e, 0x84, 0xe2, - 0xa4, 0x6d, 0x6c, 0xe0, 0xcd, 0xd7, 0x08, 0xaf, 0x6c, 0xc5, 0x36, 0x36, 0x34, 0x84, 0x92, 0x1f, - 0xcf, 0xc1, 0xc0, 0x2c, 0xd5, 0x0d, 0x36, 0x42, 0x86, 0x7a, 0x39, 0xac, 0x7c, 0xfa, 0x60, 0x1c, - 0x56, 0x4e, 0xae, 0xf1, 0xca, 0x64, 0x45, 0x11, 0xf5, 0x93, 0x9b, 0x30, 0x38, 0xa5, 0x7b, 0x74, - 0xd5, 0x76, 0x36, 0xd0, 0x05, 0x67, 0x2c, 0x7c, 0x28, 0x12, 0xd1, 0x1f, 0x9f, 0x88, 0xdf, 0x8c, - 0x35, 0xc5, 0x2f, 0x2d, 0x28, 0xcc, 0xc4, 0xc2, 0x6f, 0xe6, 0x44, 0x5e, 0x77, 0x14, 0x0b, 0xbf, - 0xc2, 0xd3, 0x04, 0x26, 0x3c, 0x56, 0x1e, 0x49, 0x3f, 0x56, 0x46, 0xeb, 0x11, 0xdd, 0xf4, 0x30, - 0xc3, 0xec, 0x28, 0x2e, 0xfa, 0xdc, 0x7a, 0x44, 0x28, 0x26, 0x98, 0xd5, 0x24, 0x12, 0xf5, 0xbb, - 0xfd, 0x90, 0x1a, 0x12, 0xe0, 0x58, 0xc9, 0x8f, 0x95, 0x3c, 0x54, 0xf2, 0x6a, 0x42, 0xc9, 0x2f, - 0x24, 0x83, 0x4c, 0x7c, 0x48, 0x35, 0xfc, 0xab, 0xc5, 0x44, 0x88, 0x9a, 0xc7, 0x7b, 0x77, 0x19, - 0x4a, 0xaf, 0x7f, 0x5b, 0xe9, 0x05, 0x03, 0xa2, 0xb4, 0xed, 0x80, 0x18, 0xd8, 0xe9, 0x80, 0x18, - 0xcc, 0x1c, 0x10, 0xa1, 0x82, 0x0c, 0x65, 0x2a, 0x48, 0x4d, 0x0c, 0x1a, 0xe8, 0x9d, 0x29, 0xe7, - 0xe2, 0xd6, 0x66, 0x79, 0x8c, 0x8d, 0xa6, 0xd4, 0x14, 0x39, 0xc8, 0x42, 0xfd, 0xbd, 0x62, 0x8f, - 0xb8, 0x52, 0x87, 0xa2, 0x23, 0x2f, 0x41, 0xa1, 0xd2, 0xe9, 0x08, 0xfd, 0x38, 0x25, 0x85, 0xb4, - 0xca, 0x28, 0xc5, 0xa8, 0xc9, 0xeb, 0x50, 0xa8, 0xdc, 0xab, 0xc7, 0xb3, 0xe3, 0x54, 0xee, 0xd5, - 0xc5, 0x97, 0x64, 0x96, 0xbd, 0x57, 0x27, 0x6f, 0x86, 0x61, 0x6a, 0xd7, 0xba, 0xd6, 0x03, 0xb1, - 0x51, 0x14, 0x9e, 0xba, 0xbe, 0x27, 0x4f, 0x93, 0xa1, 0xd8, 0x76, 0x31, 0x46, 0x1b, 0xd3, 0xa6, - 0xd2, 0xce, 0xb5, 0x69, 0x60, 0x5b, 0x6d, 0x1a, 0xdc, 0xa9, 0x36, 0x0d, 0xed, 0x40, 0x9b, 0x60, - 0x5b, 0x6d, 0x1a, 0xde, 0xbf, 0x36, 0x75, 0xe0, 0x42, 0x32, 0x16, 0x60, 0xa0, 0x11, 0x1a, 0x90, - 0x24, 0x56, 0x38, 0x96, 0xe0, 0xd5, 0x7f, 0x97, 0x63, 0x1b, 0xeb, 0x88, 0x6e, 0xb8, 0x0c, 0x2f, - 0xbb, 0xb6, 0x25, 0x4b, 0xab, 0xdf, 0xc8, 0x67, 0x87, 0x30, 0x3c, 0x9a, 0x53, 0xdc, 0x0f, 0xa6, - 0x4a, 0xa9, 0x18, 0x7b, 0x3c, 0x91, 0x29, 0xe5, 0x18, 0xdb, 0x34, 0x99, 0x7d, 0x3d, 0x9f, 0x15, - 0x57, 0x71, 0x5f, 0x12, 0x7b, 0x36, 0xe9, 0x0c, 0x87, 0x2e, 0xfe, 0x6e, 0xd4, 0x0b, 0x6e, 0x06, - 0x46, 0x64, 0x21, 0x0a, 0x29, 0xed, 0x44, 0xc0, 0x91, 0x72, 0xe4, 0xcd, 0x20, 0x89, 0x91, 0xe4, - 0x1f, 0x83, 0x9e, 0x6e, 0xfe, 0x98, 0x8d, 0xb9, 0xc7, 0xc8, 0xe4, 0xe4, 0x79, 0x28, 0xcd, 0x60, - 0x56, 0x00, 0x79, 0xb0, 0xf3, 0x3c, 0x01, 0xb2, 0xd7, 0x0a, 0xa7, 0x51, 0x7f, 0x3b, 0x07, 0xa7, - 0x6e, 0x77, 0x57, 0xa8, 0x70, 0xb4, 0x0b, 0xda, 0xf0, 0x1e, 0x00, 0x03, 0x0b, 0x87, 0x99, 0x1c, - 0x3a, 0xcc, 0x7c, 0x4c, 0x8e, 0xbf, 0x18, 0x2b, 0x30, 0x11, 0x52, 0x73, 0x67, 0x99, 0x4b, 0xbe, - 0xcf, 0xe9, 0x83, 0xee, 0x0a, 0x6d, 0x24, 0xbc, 0x66, 0x24, 0xee, 0x17, 0xde, 0xe2, 0xde, 0xfc, - 0x7b, 0x75, 0x50, 0xf9, 0xe5, 0x7c, 0x66, 0xc8, 0xcb, 0x23, 0x9b, 0x49, 0xf6, 0xb3, 0xa9, 0xbd, - 0x12, 0xcf, 0x28, 0x9b, 0x42, 0x12, 0xe3, 0x98, 0xc6, 0x25, 0x5d, 0x60, 0x47, 0x3c, 0xbf, 0xf1, - 0x07, 0x2a, 0xb0, 0x3f, 0xcc, 0x65, 0x86, 0x26, 0x3d, 0xaa, 0x02, 0x53, 0xff, 0xd7, 0x82, 0x1f, - 0x11, 0x75, 0x5f, 0x9f, 0xf0, 0x3c, 0x0c, 0x89, 0x47, 0x72, 0x51, 0x3f, 0x61, 0x71, 0x6c, 0x88, - 0xc7, 0xd0, 0x01, 0x01, 0x33, 0x29, 0x24, 0x27, 0x66, 0xc9, 0x4f, 0x58, 0x72, 0x60, 0xd6, 0x24, - 0x12, 0x66, 0x34, 0x4c, 0x3f, 0x32, 0x3d, 0xb4, 0x40, 0x58, 0x5f, 0x16, 0xb8, 0xd1, 0x40, 0x1f, - 0x99, 0x1e, 0xb7, 0x3f, 0x02, 0x34, 0x33, 0x08, 0xb8, 0x2d, 0x22, 0xe6, 0x3d, 0x34, 0x08, 0xb8, - 0xa9, 0xa2, 0x09, 0x0c, 0x6b, 0xad, 0x70, 0xbe, 0x15, 0x2e, 0x2d, 0xa2, 0xb5, 0xc2, 0x5d, 0x17, - 0x5b, 0x1b, 0x10, 0x30, 0x8e, 0x1a, 0x5d, 0x0d, 0x9d, 0xf8, 0x90, 0xa3, 0x83, 0x10, 0x4d, 0x60, - 0xc8, 0x75, 0x18, 0xab, 0x7b, 0xba, 0x65, 0xe8, 0x8e, 0xb1, 0xd8, 0xf5, 0x3a, 0x5d, 0x4f, 0x36, - 0x80, 0x5d, 0xcf, 0xb0, 0xbb, 0x9e, 0x16, 0xa3, 0x20, 0x1f, 0x87, 0x51, 0x1f, 0x32, 0xed, 0x38, - 0xb6, 0x23, 0x5b, 0x39, 0xae, 0x67, 0x50, 0xc7, 0xd1, 0xa2, 0x04, 0xe4, 0x13, 0x30, 0x5a, 0xb3, - 0x1e, 0xda, 0x4d, 0x1e, 0x1c, 0x41, 0x9b, 0x13, 0x36, 0x0f, 0xbe, 0x18, 0x33, 0x03, 0x44, 0xa3, - 0xeb, 0xb4, 0xb4, 0x28, 0xa1, 0xba, 0x95, 0x4f, 0x06, 0x8e, 0x7d, 0x7c, 0x37, 0x48, 0x57, 0xa3, - 0x8e, 0x7b, 0xe8, 0xad, 0x8a, 0xc6, 0xa7, 0xec, 0x37, 0xcc, 0x6d, 0xd0, 0xeb, 0x30, 0x78, 0x9b, - 0x6e, 0x70, 0x1f, 0xd3, 0x52, 0xe8, 0x96, 0xfc, 0x40, 0xc0, 0xe4, 0xd3, 0x5d, 0x9f, 0x4e, 0xfd, - 0x56, 0x3e, 0x19, 0x12, 0xf7, 0xf1, 0x15, 0xf6, 0xc7, 0x61, 0x00, 0x45, 0x59, 0xf3, 0xaf, 0x17, - 0x50, 0x80, 0x28, 0xee, 0xa8, 0xb7, 0xb3, 0x4f, 0xa6, 0xfe, 0x7c, 0x29, 0x1e, 0x27, 0xf9, 0xf1, - 0x95, 0xde, 0x1b, 0x30, 0x3c, 0x65, 0x5b, 0xae, 0xe9, 0x7a, 0xd4, 0x6a, 0xfa, 0x0a, 0x8b, 0x8e, - 0xff, 0xcd, 0x10, 0x2c, 0xdb, 0x80, 0x12, 0xf5, 0x5e, 0x94, 0x97, 0xbc, 0x02, 0x43, 0x28, 0x72, - 0xb4, 0x39, 0xf9, 0x84, 0x87, 0x37, 0x13, 0x2b, 0x0c, 0x18, 0xb7, 0x38, 0x43, 0x52, 0x72, 0x07, - 0x06, 0xa7, 0xd6, 0xcc, 0x96, 0xe1, 0x50, 0x0b, 0x7d, 0x93, 0xa5, 0x70, 0x34, 0xd1, 0xbe, 0x9c, - 0xc0, 0x7f, 0x91, 0x96, 0x37, 0xa7, 0x29, 0x8a, 0x45, 0x1e, 0x8b, 0x09, 0xd8, 0x85, 0x9f, 0xce, - 0x03, 0x84, 0x05, 0xc8, 0x53, 0x90, 0x0f, 0xf2, 0x98, 0xa3, 0x4b, 0x4c, 0x44, 0x83, 0xf2, 0xb8, - 0x54, 0x88, 0xb1, 0x9d, 0xdf, 0x76, 0x6c, 0xdf, 0x81, 0x12, 0x3f, 0x5d, 0x43, 0xaf, 0x75, 0x29, - 0x74, 0x6b, 0x66, 0x83, 0x27, 0x90, 0x9e, 0xdb, 0xd2, 0x68, 0x79, 0x46, 0x3c, 0xc0, 0x39, 0xb3, - 0x0b, 0x4d, 0xe8, 0xc7, 0xbf, 0xc8, 0x65, 0x28, 0x2e, 0xfb, 0x69, 0x8a, 0x47, 0xf9, 0x2c, 0x1d, - 0x93, 0x1f, 0xe2, 0x59, 0x37, 0x4d, 0xd9, 0x96, 0xc7, 0xaa, 0xc6, 0x56, 0x8f, 0x08, 0xb9, 0x08, - 0x58, 0x44, 0x2e, 0x02, 0xa6, 0xfe, 0xd7, 0xf9, 0x94, 0x08, 0xde, 0x8f, 0xef, 0x30, 0x79, 0x0d, - 0x00, 0x5f, 0x9e, 0x33, 0x79, 0xfa, 0xcf, 0x41, 0x70, 0x94, 0x20, 0x23, 0x54, 0xdb, 0xc8, 0xb6, - 0x23, 0x24, 0x56, 0xff, 0x61, 0x2e, 0x11, 0xf6, 0x79, 0x5f, 0x72, 0x94, 0xad, 0xb2, 0xfc, 0x1e, - 0xcd, 0x58, 0xbf, 0x2f, 0x0a, 0xbb, 0xeb, 0x8b, 0xe8, 0xb7, 0x1c, 0x80, 0x65, 0x7a, 0x98, 0xdf, - 0xf2, 0xdd, 0x7c, 0x5a, 0x10, 0xec, 0xa3, 0xa9, 0xe2, 0x37, 0x02, 0xa3, 0xb4, 0x18, 0x4b, 0x3b, - 0x80, 0xd0, 0x78, 0x2a, 0x75, 0x61, 0xa6, 0x7e, 0x1e, 0x4e, 0xc4, 0x42, 0x43, 0x8b, 0xac, 0xd6, - 0x97, 0x7b, 0xc7, 0x98, 0xce, 0x8e, 0x59, 0x10, 0x21, 0x53, 0xff, 0xbf, 0x5c, 0xef, 0xc0, 0xe0, - 0x87, 0xae, 0x3a, 0x29, 0x02, 0x28, 0xfc, 0xe9, 0x08, 0xe0, 0x00, 0xb6, 0xc1, 0x47, 0x5b, 0x00, - 0x1f, 0x92, 0xc9, 0xe3, 0x83, 0x16, 0xc0, 0xcf, 0xe7, 0xb6, 0x8d, 0xeb, 0x7e, 0xd8, 0x32, 0x50, - 0xff, 0xa7, 0x5c, 0x6a, 0xfc, 0xf5, 0x7d, 0xb5, 0xeb, 0x4d, 0x28, 0x71, 0x17, 0x1e, 0xd1, 0x2a, - 0x29, 0x63, 0x1d, 0x83, 0x66, 0x94, 0x17, 0x65, 0xc8, 0x1c, 0x0c, 0xf0, 0x36, 0x18, 0xa2, 0x37, - 0x3e, 0xd2, 0x23, 0x08, 0xbc, 0x91, 0x35, 0x39, 0x0a, 0xb4, 0xfa, 0x3b, 0xb9, 0x44, 0x38, 0xf8, - 0x43, 0xfc, 0xb6, 0x70, 0xaa, 0x2e, 0xec, 0x7c, 0xaa, 0x56, 0xff, 0x79, 0x3e, 0x3d, 0x1a, 0xfd, - 0x21, 0x7e, 0xc8, 0x41, 0x1c, 0xa7, 0xed, 0x6d, 0xdd, 0x5a, 0x86, 0xb1, 0xa8, 0x2c, 0xc4, 0xb2, - 0xf5, 0x64, 0x7a, 0x4c, 0xfe, 0x8c, 0x56, 0xc4, 0x78, 0xa8, 0xdf, 0xc9, 0x25, 0x03, 0xe9, 0x1f, - 0xfa, 0xfc, 0xb4, 0x37, 0x6d, 0x89, 0x7e, 0xca, 0x87, 0x64, 0xad, 0x39, 0x88, 0x4f, 0xf9, 0x90, - 0xac, 0x1a, 0x7b, 0xfb, 0x94, 0x5f, 0xcc, 0x67, 0xe5, 0x21, 0x38, 0xf4, 0x0f, 0xfa, 0x8c, 0x2c, - 0x64, 0xde, 0x32, 0xf1, 0x69, 0x4f, 0x65, 0x05, 0xfe, 0xcf, 0xe0, 0x99, 0xe0, 0xb3, 0xb7, 0x31, - 0x9e, 0x2a, 0xac, 0x0f, 0x89, 0x22, 0x1f, 0x0d, 0x61, 0x7d, 0x48, 0x86, 0xca, 0x87, 0x4f, 0x58, - 0xbf, 0x91, 0xdf, 0x69, 0xf2, 0x8b, 0x63, 0xe1, 0x25, 0x84, 0xf7, 0xe5, 0x7c, 0x32, 0x29, 0xcb, - 0xa1, 0x8b, 0x69, 0x06, 0x4a, 0x22, 0x3d, 0x4c, 0xa6, 0x70, 0x38, 0x3e, 0xcb, 0xa2, 0x11, 0xdf, - 0x71, 0x03, 0xc4, 0x45, 0xce, 0xce, 0x44, 0xc2, 0x69, 0xd5, 0xef, 0xe5, 0x62, 0x19, 0x4c, 0x0e, - 0xe5, 0x08, 0x61, 0x4f, 0x4b, 0x12, 0x79, 0xcb, 0x3f, 0xcc, 0x2c, 0xc6, 0x22, 0xc8, 0x07, 0xdf, - 0x53, 0xa5, 0x9e, 0x6e, 0xb6, 0xe2, 0xe5, 0x45, 0xfc, 0x81, 0x6f, 0xe5, 0xe1, 0x64, 0x82, 0x94, - 0x5c, 0x8e, 0x44, 0xfc, 0xc1, 0x63, 0xc9, 0x98, 0xa3, 0x3a, 0x8f, 0xfd, 0xb3, 0x8b, 0x93, 0xd4, - 0xcb, 0x50, 0xac, 0xea, 0x1b, 0xfc, 0xdb, 0xfa, 0x39, 0x4b, 0x43, 0xdf, 0x90, 0x4f, 0xdc, 0x10, - 0x4f, 0x56, 0xe0, 0x0c, 0xbf, 0x0f, 0x31, 0x6d, 0x6b, 0xd9, 0x6c, 0xd3, 0x9a, 0x35, 0x6f, 0xb6, - 0x5a, 0xa6, 0x2b, 0x2e, 0xf5, 0x9e, 0xdf, 0xda, 0x2c, 0x5f, 0xf1, 0x6c, 0x4f, 0x6f, 0x35, 0xa8, - 0x4f, 0xd6, 0xf0, 0xcc, 0x36, 0x6d, 0x98, 0x56, 0xa3, 0x8d, 0x94, 0x12, 0xcb, 0x74, 0x56, 0xa4, - 0xc6, 0x93, 0x05, 0xd4, 0x9b, 0xba, 0x65, 0x51, 0xa3, 0x66, 0x4d, 0x6e, 0x78, 0x94, 0x5f, 0x06, - 0x16, 0xf8, 0x91, 0x20, 0x7f, 0x87, 0xce, 0xd1, 0x8c, 0xf1, 0x0a, 0x23, 0xd0, 0x52, 0x0a, 0xa9, - 0xbf, 0x59, 0x4c, 0x49, 0x5e, 0x73, 0x84, 0xd4, 0xc7, 0xef, 0xe9, 0xe2, 0x36, 0x3d, 0x7d, 0x0d, - 0x06, 0x44, 0x34, 0x66, 0x71, 0xc1, 0x80, 0x8e, 0xf3, 0x0f, 0x39, 0x48, 0xbe, 0xa1, 0x11, 0x54, - 0xa4, 0x05, 0x17, 0x96, 0x59, 0x37, 0xa5, 0x77, 0x66, 0x69, 0x0f, 0x9d, 0xd9, 0x83, 0x1f, 0x79, - 0x17, 0xce, 0x21, 0x36, 0xa5, 0x5b, 0x07, 0xb0, 0x2a, 0x0c, 0xa5, 0xc5, 0xab, 0x4a, 0xef, 0xdc, - 0xac, 0xf2, 0xe4, 0x33, 0x30, 0x12, 0x0c, 0x10, 0x93, 0xba, 0xe2, 0xe6, 0xa2, 0xc7, 0x38, 0xe3, - 0x71, 0xea, 0x18, 0x18, 0xdd, 0xd5, 0xa2, 0xb1, 0xce, 0x22, 0xbc, 0xd4, 0xff, 0x31, 0xd7, 0x2b, - 0x89, 0xce, 0xa1, 0xcf, 0xca, 0x6f, 0xc1, 0x80, 0xc1, 0x3f, 0x4a, 0xe8, 0x54, 0xef, 0x34, 0x3b, - 0x9c, 0x54, 0xf3, 0xcb, 0xa8, 0xff, 0x2c, 0xd7, 0x33, 0x77, 0xcf, 0x51, 0xff, 0xbc, 0x2f, 0x17, - 0x32, 0x3e, 0x4f, 0x4c, 0xa2, 0x57, 0x61, 0xdc, 0x0c, 0x93, 0x0b, 0x34, 0xc2, 0x50, 0x57, 0xda, - 0x09, 0x09, 0x8e, 0xa3, 0xeb, 0x06, 0x04, 0x0e, 0x5b, 0x8e, 0xef, 0x8d, 0xe6, 0x36, 0xba, 0x8e, - 0xc9, 0xc7, 0xa5, 0x76, 0xda, 0x8d, 0xb9, 0xaa, 0xb9, 0x77, 0x1c, 0x93, 0x55, 0xa0, 0x7b, 0x6b, - 0xd4, 0xd2, 0x1b, 0xeb, 0xb6, 0xf3, 0x00, 0x83, 0xa1, 0xf2, 0xc1, 0xa9, 0x9d, 0xe0, 0xf0, 0x7b, - 0x3e, 0x98, 0x3c, 0x03, 0xa3, 0xab, 0xad, 0x2e, 0x0d, 0xc2, 0x4f, 0xf2, 0xbb, 0x3e, 0x6d, 0x84, - 0x01, 0x83, 0x1b, 0x92, 0x4b, 0x00, 0x48, 0xe4, 0x61, 0x66, 0x25, 0xbc, 0xd8, 0xd3, 0x86, 0x18, - 0x64, 0x59, 0x74, 0xd7, 0x05, 0xae, 0xd5, 0x5c, 0x48, 0x8d, 0x96, 0x6d, 0xad, 0x36, 0x3c, 0xea, - 0xb4, 0xb1, 0xa1, 0xe8, 0xcc, 0xa0, 0x9d, 0x45, 0x0a, 0xbc, 0x3a, 0x71, 0xe7, 0x6c, 0x6b, 0x75, - 0x99, 0x3a, 0x6d, 0xd6, 0xd4, 0xe7, 0x81, 0x88, 0xa6, 0x3a, 0x78, 0xe8, 0xc1, 0x3f, 0x0e, 0xbd, - 0x19, 0x34, 0xf1, 0x11, 0xfc, 0x34, 0x04, 0x3f, 0xac, 0x0c, 0xc3, 0x3c, 0x06, 0x1f, 0x17, 0x1a, - 0xba, 0x30, 0x68, 0xc0, 0x41, 0x28, 0xaf, 0xb3, 0x20, 0xbc, 0x2b, 0xb8, 0x07, 0xb9, 0x26, 0x7e, - 0xa9, 0x5f, 0x2c, 0xa4, 0xa5, 0x1b, 0xda, 0x97, 0xa2, 0x85, 0xd3, 0x6a, 0x7e, 0x57, 0xd3, 0xea, - 0x09, 0xab, 0xdb, 0x6e, 0xe8, 0x9d, 0x4e, 0xe3, 0xbe, 0xd9, 0xc2, 0x27, 0x5c, 0xb8, 0xf0, 0x69, - 0xa3, 0x56, 0xb7, 0x5d, 0xe9, 0x74, 0x66, 0x38, 0x90, 0x3c, 0x07, 0x27, 0x19, 0x1d, 0x76, 0x52, - 0x40, 0x59, 0x44, 0x4a, 0xc6, 0x00, 0x83, 0xd8, 0xfa, 0xb4, 0xe7, 0x61, 0x50, 0xf0, 0xe4, 0x6b, - 0x55, 0xbf, 0x36, 0xc0, 0x99, 0xb9, 0xac, 0xe7, 0x02, 0x36, 0x7c, 0x72, 0xed, 0xd7, 0x86, 0xfc, - 0xf2, 0x18, 0xaa, 0xd9, 0xea, 0xb6, 0x79, 0xf4, 0xad, 0x01, 0x44, 0x06, 0xbf, 0xc9, 0x65, 0x18, - 0x63, 0x5c, 0x02, 0x81, 0xf1, 0xe8, 0xb6, 0xfd, 0x5a, 0x0c, 0x4a, 0xae, 0xc3, 0xe9, 0x08, 0x84, - 0xdb, 0xa0, 0xfc, 0x49, 0x42, 0xbf, 0x96, 0x8a, 0x53, 0xbf, 0x59, 0x88, 0x26, 0x41, 0x3a, 0x84, - 0x8e, 0x38, 0x07, 0x03, 0xb6, 0xb3, 0xda, 0xe8, 0x3a, 0x2d, 0x31, 0xf6, 0x4a, 0xb6, 0xb3, 0x7a, - 0xc7, 0x69, 0x91, 0x33, 0x50, 0x62, 0xbd, 0x63, 0x1a, 0x62, 0x88, 0xf5, 0xeb, 0x9d, 0x4e, 0xcd, - 0x20, 0x15, 0xde, 0x21, 0x18, 0x19, 0xb5, 0xd1, 0xc4, 0xad, 0x3d, 0x77, 0x4a, 0xe8, 0xe7, 0x2b, - 0x5e, 0x02, 0x89, 0xfd, 0x84, 0xf1, 0x52, 0xf9, 0x41, 0x40, 0x8c, 0x85, 0x81, 0xdb, 0x12, 0x83, - 0xf7, 0x49, 0x9c, 0x85, 0x40, 0x86, 0x2c, 0xf8, 0x26, 0xc6, 0x20, 0x55, 0x20, 0x21, 0x55, 0xdb, - 0x36, 0xcc, 0xfb, 0x26, 0xe5, 0x2f, 0x48, 0xfa, 0xf9, 0xc5, 0x6f, 0x12, 0xab, 0x8d, 0xfb, 0x4c, - 0xe6, 0x05, 0x84, 0xbc, 0xc1, 0x95, 0x90, 0xd3, 0xe1, 0xda, 0xc7, 0xfb, 0x96, 0xdb, 0x69, 0x31, - 0x14, 0x6a, 0x26, 0x96, 0xc7, 0x85, 0x50, 0xfd, 0xef, 0xfb, 0x93, 0x99, 0xb0, 0x0e, 0xc5, 0xae, - 0x99, 0x05, 0x10, 0x89, 0xee, 0xc2, 0xcb, 0xb5, 0x0b, 0x52, 0x54, 0x7b, 0x81, 0xc9, 0xe0, 0x21, - 0x95, 0x25, 0x57, 0x61, 0x90, 0x7f, 0x51, 0xad, 0x2a, 0xec, 0x1d, 0x74, 0x11, 0x73, 0x3b, 0xe6, - 0xfd, 0xfb, 0xe8, 0x4f, 0x16, 0xa0, 0xc9, 0x65, 0x18, 0xa8, 0x2e, 0xd4, 0xeb, 0x95, 0x05, 0xff, - 0xa6, 0x18, 0xdf, 0xb2, 0x18, 0x96, 0xdb, 0x70, 0x75, 0xcb, 0xd5, 0x7c, 0x24, 0x79, 0x06, 0x4a, - 0xb5, 0x25, 0x24, 0xe3, 0x2f, 0x34, 0x87, 0xb7, 0x36, 0xcb, 0x03, 0x66, 0x87, 0x53, 0x09, 0x14, - 0xd6, 0x7b, 0xb7, 0x56, 0x95, 0xdc, 0x25, 0x78, 0xbd, 0x0f, 0x4d, 0x03, 0xaf, 0x9d, 0xb5, 0x00, - 0x4d, 0x5e, 0x86, 0x91, 0x3a, 0x75, 0x4c, 0xbd, 0xb5, 0xd0, 0xc5, 0xad, 0xa2, 0x14, 0xf1, 0xd1, - 0x45, 0x78, 0xc3, 0x42, 0x84, 0x16, 0x21, 0x23, 0x17, 0xa1, 0x38, 0x6b, 0x5a, 0xfe, 0x73, 0x09, - 0xf4, 0xa7, 0x5f, 0x33, 0x2d, 0x4f, 0x43, 0x28, 0x79, 0x06, 0x0a, 0xb7, 0x96, 0x6b, 0xc2, 0x13, - 0x0c, 0x79, 0xbd, 0xe7, 0x45, 0xa2, 0x47, 0xde, 0x5a, 0xae, 0x91, 0x97, 0x61, 0x88, 0x2d, 0x62, - 0xd4, 0x6a, 0x52, 0x57, 0x19, 0xc6, 0x8f, 0xe1, 0x21, 0x0b, 0x7d, 0xa0, 0xec, 0xd3, 0x11, 0x50, - 0x92, 0xdb, 0x30, 0x1e, 0x0f, 0xa0, 0x2f, 0x9e, 0xec, 0xa0, 0xc5, 0xb5, 0x2e, 0x70, 0x69, 0x41, - 0x33, 0x13, 0x05, 0x89, 0x01, 0x4a, 0x1c, 0xc6, 0xf6, 0x75, 0x68, 0x75, 0xf2, 0x78, 0xcd, 0x57, - 0xb6, 0x36, 0xcb, 0x1f, 0x49, 0x30, 0x6d, 0x38, 0x82, 0x4a, 0xe2, 0x9e, 0xc9, 0x49, 0xfd, 0x3f, - 0xf3, 0xe9, 0xd9, 0xd5, 0x0e, 0x61, 0x76, 0xda, 0xe3, 0xc5, 0x77, 0x6c, 0x4c, 0x14, 0xf7, 0x31, - 0x26, 0xee, 0xc3, 0x89, 0x8a, 0xd1, 0x36, 0xad, 0x0a, 0xfe, 0x74, 0xe7, 0x67, 0x2a, 0x38, 0xdb, - 0x49, 0xaf, 0x17, 0x63, 0x68, 0xf1, 0x3d, 0x3c, 0x94, 0x32, 0x43, 0x35, 0x74, 0x8e, 0x6b, 0xb4, - 0xef, 0xeb, 0x8d, 0x26, 0x4f, 0x4c, 0xa6, 0xc5, 0x99, 0xaa, 0x3f, 0x95, 0xdf, 0x26, 0x21, 0xdc, - 0xe3, 0x28, 0x7d, 0xf5, 0x2b, 0xf9, 0xde, 0x39, 0xf9, 0x1e, 0x4b, 0xa1, 0xfc, 0x71, 0x3e, 0x25, - 0x43, 0xde, 0xbe, 0x24, 0x71, 0x15, 0x06, 0x39, 0x9b, 0xc0, 0xf3, 0x18, 0x27, 0x60, 0xae, 0xac, - 0x38, 0xf1, 0xfb, 0x68, 0xb2, 0x00, 0xa7, 0x2b, 0xf7, 0xef, 0xd3, 0xa6, 0x17, 0x06, 0xd5, 0x5e, - 0x08, 0x63, 0xd4, 0xf2, 0x20, 0xc2, 0x02, 0x1f, 0x06, 0xe5, 0xc6, 0x58, 0x2c, 0xa9, 0xe5, 0xc8, - 0x32, 0x9c, 0x8d, 0xc3, 0xeb, 0x7c, 0xd7, 0x52, 0x94, 0xe2, 0x0a, 0x27, 0x38, 0xf2, 0xff, 0xb4, - 0x8c, 0xb2, 0x69, 0xad, 0xc4, 0xd5, 0xa5, 0xbf, 0x57, 0x2b, 0x71, 0xa9, 0x49, 0x2d, 0xa7, 0x7e, - 0xab, 0x20, 0x27, 0x12, 0x7c, 0x7c, 0x7d, 0xc4, 0x6e, 0x44, 0x3c, 0xc3, 0x77, 0x3a, 0x64, 0x5e, - 0x16, 0x01, 0x56, 0x8c, 0xae, 0xe3, 0x3b, 0x51, 0x06, 0x01, 0x1e, 0x10, 0x28, 0x2f, 0x9d, 0x01, - 0x25, 0xa9, 0x41, 0xb1, 0xe2, 0xac, 0x72, 0x8b, 0x7c, 0xbb, 0x37, 0x67, 0xba, 0xb3, 0xea, 0xa6, - 0xbf, 0x39, 0x63, 0x2c, 0xd4, 0xbf, 0x98, 0xef, 0x91, 0xfb, 0xef, 0xb1, 0x9c, 0x44, 0x7e, 0x2e, - 0x9f, 0x95, 0xc5, 0xef, 0xa8, 0x7a, 0xbb, 0x7d, 0xc0, 0xc2, 0x39, 0xda, 0xae, 0x80, 0x07, 0x28, - 0x9c, 0x3f, 0xc8, 0x67, 0xa5, 0x24, 0x3c, 0x16, 0xce, 0xde, 0x26, 0xc8, 0x54, 0x91, 0x3e, 0xc6, - 0x36, 0xb7, 0xac, 0x0a, 0xfd, 0x7b, 0xf4, 0xf8, 0x4a, 0x13, 0xe9, 0xf1, 0x10, 0xde, 0x97, 0x96, - 0xfe, 0x61, 0x3e, 0x33, 0xf5, 0xe6, 0xb1, 0x4c, 0x0f, 0x52, 0xa6, 0xc7, 0x43, 0x7f, 0x5f, 0x43, - 0x3f, 0x55, 0xa6, 0xc7, 0x63, 0x7f, 0x5f, 0x7a, 0xfa, 0xfb, 0xf9, 0xf4, 0xe4, 0xb2, 0x87, 0xa0, - 0xa4, 0x07, 0xe1, 0x94, 0xe9, 0x77, 0x43, 0x71, 0x5f, 0xdd, 0xd0, 0xbf, 0x0f, 0x2b, 0x2a, 0x29, - 0xd0, 0x43, 0x1b, 0xf5, 0xdf, 0xaf, 0x02, 0x3d, 0x80, 0x21, 0xff, 0x38, 0x0b, 0xf4, 0x27, 0x0a, - 0xc9, 0x84, 0xca, 0x8f, 0xeb, 0x9a, 0xe4, 0xec, 0x71, 0x4d, 0xf2, 0xcb, 0x91, 0xb7, 0xe1, 0x44, - 0x28, 0x4b, 0x39, 0x30, 0x1a, 0xde, 0x78, 0x35, 0x19, 0xaa, 0xf1, 0x1e, 0xc3, 0x89, 0x08, 0x3e, - 0x71, 0x6a, 0xf5, 0x7b, 0x85, 0x64, 0x56, 0xea, 0xe3, 0xde, 0xd8, 0x63, 0x6f, 0xdc, 0x81, 0xb3, - 0x53, 0x5d, 0xc7, 0xa1, 0x96, 0x97, 0xde, 0x29, 0x78, 0x78, 0xdf, 0xe4, 0x14, 0x8d, 0x64, 0xe7, - 0x64, 0x14, 0x66, 0x6c, 0xc5, 0x83, 0x8c, 0x38, 0xdb, 0x81, 0x90, 0x6d, 0x97, 0x53, 0xa4, 0xb1, - 0x4d, 0x2f, 0xac, 0xfe, 0x6e, 0x3e, 0x99, 0x47, 0xfc, 0xb8, 0xeb, 0xf7, 0xd6, 0xf5, 0xea, 0x17, - 0x0b, 0xf1, 0x5c, 0xea, 0xc7, 0x0b, 0xc4, 0xde, 0xbb, 0xc3, 0x97, 0x24, 0x8e, 0x1b, 0xe9, 0x2b, - 0x7c, 0x78, 0xd6, 0x57, 0xf8, 0x78, 0xf5, 0x97, 0x8b, 0xf1, 0xbc, 0xf4, 0xc7, 0xdd, 0x71, 0x78, - 0xdd, 0x41, 0x16, 0xe1, 0xb4, 0x98, 0xdb, 0x7c, 0x10, 0x66, 0xc8, 0x10, 0xf3, 0x17, 0x4f, 0xb4, - 0x27, 0xa6, 0xc5, 0xae, 0x4b, 0x9d, 0x86, 0xa7, 0xbb, 0x0f, 0x1a, 0x98, 0x52, 0x43, 0x4b, 0x2d, - 0xc8, 0x18, 0x8a, 0x59, 0x2d, 0xca, 0x70, 0x30, 0x64, 0xe8, 0x4f, 0x88, 0x09, 0x86, 0x69, 0x05, - 0xd5, 0x5f, 0xcb, 0xc1, 0x78, 0xfc, 0x73, 0xc8, 0x04, 0x0c, 0xb2, 0xdf, 0x41, 0xa4, 0x00, 0x29, - 0x03, 0x38, 0xe7, 0xc8, 0xbd, 0x08, 0x7c, 0x1a, 0xf2, 0x0a, 0x0c, 0xa1, 0xc3, 0x06, 0x16, 0xc8, - 0x87, 0x01, 0x1a, 0xc2, 0x02, 0x98, 0x96, 0x96, 0x17, 0x0b, 0x49, 0xc9, 0x1b, 0x30, 0x5c, 0x0b, - 0x3d, 0xd3, 0xc4, 0x9d, 0x17, 0x3a, 0xc4, 0x4a, 0x25, 0x43, 0x02, 0x4d, 0xa6, 0x56, 0xbf, 0x93, - 0x0f, 0x55, 0xfd, 0xd8, 0x34, 0xdd, 0x97, 0x69, 0xfa, 0x8d, 0x02, 0x8c, 0x4e, 0xd9, 0x96, 0xa7, - 0x37, 0xbd, 0xe3, 0xf3, 0xa7, 0xfd, 0xec, 0xeb, 0x49, 0x19, 0xfa, 0xa7, 0xdb, 0xba, 0xd9, 0x12, - 0x86, 0x0f, 0x86, 0x91, 0xa5, 0x0c, 0xa0, 0x71, 0x38, 0xb9, 0x89, 0xc1, 0x53, 0x98, 0xa4, 0x03, - 0xf7, 0x9c, 0xb1, 0x30, 0xe2, 0xa6, 0x84, 0x12, 0x39, 0x67, 0x39, 0x80, 0x8f, 0x1c, 0xb9, 0xa4, - 0xdc, 0x67, 0xc7, 0x67, 0x31, 0x47, 0xa4, 0xcf, 0xbe, 0x5a, 0x80, 0xb3, 0x71, 0x37, 0xa1, 0xe3, - 0x01, 0x27, 0x3a, 0xef, 0xcf, 0xc0, 0xe9, 0xb8, 0x6c, 0xaa, 0x4c, 0x1a, 0xfd, 0xbd, 0xaf, 0xab, - 0x27, 0xb6, 0x36, 0xcb, 0x4f, 0x25, 0x3d, 0xb4, 0x58, 0x65, 0xa9, 0x17, 0xd8, 0xa9, 0x95, 0xa4, - 0xf6, 0xcc, 0x87, 0xe4, 0xed, 0xe0, 0x63, 0xde, 0x33, 0x3f, 0x97, 0x4f, 0xf6, 0xcc, 0xf1, 0x84, - 0xe7, 0x5f, 0xac, 0x17, 0x01, 0x6e, 0x9a, 0x9e, 0x08, 0x63, 0x78, 0xc4, 0xdd, 0x65, 0x25, 0x8f, - 0x9c, 0xe2, 0x9e, 0x3c, 0x72, 0xc2, 0xe0, 0x04, 0xfd, 0x7b, 0x08, 0x4e, 0xf0, 0x36, 0x0c, 0x08, - 0x39, 0x0a, 0x73, 0xff, 0x5c, 0xf8, 0x15, 0x08, 0xce, 0xaa, 0xde, 0x97, 0xfe, 0xb3, 0x30, 0x20, - 0x32, 0x95, 0x0b, 0x73, 0x1c, 0x9d, 0x73, 0x05, 0x48, 0xf3, 0xff, 0x20, 0x17, 0x01, 0x63, 0x4f, - 0xcb, 0xbe, 0xb3, 0x3c, 0x16, 0x35, 0xfb, 0x97, 0xd4, 0x60, 0x40, 0xf8, 0x37, 0x2a, 0x80, 0x0f, - 0x7f, 0x02, 0xb5, 0x0c, 0xfb, 0x99, 0xbb, 0x39, 0xf2, 0xa3, 0x2e, 0x41, 0x2c, 0xbf, 0x88, 0x12, - 0x20, 0xf5, 0x77, 0x72, 0x30, 0x1e, 0x2f, 0x44, 0x9e, 0x87, 0x12, 0xff, 0x4b, 0x18, 0xf6, 0x18, - 0x37, 0x8c, 0x97, 0x90, 0xe3, 0x86, 0x09, 0xea, 0x97, 0x61, 0x48, 0xf3, 0x9d, 0x56, 0xe5, 0xbc, - 0xe2, 0x81, 0x27, 0xab, 0xec, 0x69, 0x14, 0x50, 0x92, 0x67, 0xa0, 0xb0, 0xd8, 0xf2, 0xd3, 0x89, - 0xa3, 0x03, 0xb0, 0xdd, 0x92, 0x83, 0xa2, 0x31, 0x2c, 0x23, 0x5a, 0xa0, 0xeb, 0xc2, 0x2d, 0x0d, - 0x89, 0x2c, 0xba, 0x2e, 0x13, 0x2d, 0xd0, 0x75, 0xf5, 0x1f, 0xe5, 0x7c, 0x47, 0xa3, 0x39, 0xd3, - 0xf5, 0x6a, 0xd6, 0x43, 0xbd, 0x65, 0x06, 0x1d, 0x41, 0x6e, 0xc2, 0x58, 0x88, 0x94, 0x1e, 0x10, - 0xa2, 0x2b, 0xb0, 0x8e, 0x98, 0x46, 0xcb, 0x74, 0x3d, 0x9e, 0xf5, 0xe6, 0x29, 0x29, 0x09, 0x75, - 0xb4, 0x18, 0xb9, 0x2c, 0x29, 0xbf, 0xb4, 0xd9, 0x91, 0x5f, 0xa5, 0x09, 0xa7, 0xb1, 0x91, 0x79, - 0xd3, 0x75, 0x4d, 0x6b, 0x55, 0x4e, 0x2d, 0x8e, 0xdb, 0xae, 0x36, 0x87, 0x37, 0xe2, 0xc9, 0xde, - 0x23, 0x05, 0xd4, 0x7f, 0x93, 0x83, 0x0b, 0x8c, 0x13, 0x46, 0xc4, 0x4a, 0x7c, 0xd8, 0xbe, 0x06, - 0x70, 0xbb, 0x87, 0xa4, 0xc4, 0xa8, 0x7e, 0x3a, 0xf9, 0xca, 0x35, 0x46, 0x18, 0xe3, 0xde, 0x43, - 0xf6, 0x7b, 0x7a, 0xfe, 0xf7, 0xdc, 0x3c, 0xdf, 0x54, 0xdf, 0x36, 0x2d, 0x83, 0x9c, 0x87, 0x33, - 0x77, 0xea, 0xd3, 0x5a, 0xe3, 0x76, 0x6d, 0xa1, 0xda, 0xb8, 0xb3, 0x50, 0x5f, 0x9a, 0x9e, 0xaa, - 0xcd, 0xd4, 0xa6, 0xab, 0xe3, 0x7d, 0xe4, 0x14, 0x9c, 0x08, 0x51, 0xb3, 0x77, 0xe6, 0x2b, 0x0b, - 0xe3, 0x39, 0x72, 0x12, 0x46, 0x43, 0xe0, 0xe4, 0xe2, 0xf2, 0x78, 0xfe, 0xb9, 0x8f, 0xc2, 0x30, - 0xbe, 0xfd, 0x11, 0xea, 0x3a, 0x02, 0x83, 0x8b, 0x93, 0xf5, 0x69, 0xed, 0x2e, 0x32, 0x01, 0x28, - 0x55, 0xa7, 0x17, 0x18, 0xc3, 0xdc, 0x73, 0xff, 0x4f, 0x0e, 0xa0, 0x3e, 0xb3, 0xbc, 0x24, 0x08, - 0x87, 0x61, 0xa0, 0xb6, 0x70, 0xb7, 0x32, 0x57, 0x63, 0x74, 0x83, 0x50, 0x5c, 0x5c, 0x9a, 0x66, - 0x35, 0x0c, 0x41, 0xff, 0xd4, 0xdc, 0x62, 0x7d, 0x7a, 0x3c, 0xcf, 0x80, 0xda, 0x74, 0xa5, 0x3a, - 0x5e, 0x60, 0xc0, 0x7b, 0x5a, 0x6d, 0x79, 0x7a, 0xbc, 0xc8, 0xfe, 0x9c, 0xab, 0x2f, 0x57, 0x96, - 0xc7, 0xfb, 0xd9, 0x9f, 0x33, 0xf8, 0x67, 0x89, 0x31, 0xab, 0x4f, 0x2f, 0xe3, 0x8f, 0x01, 0xd6, - 0x84, 0x19, 0xff, 0xd7, 0x20, 0x43, 0x31, 0xd6, 0xd5, 0x9a, 0x36, 0x3e, 0xc4, 0x7e, 0x30, 0x96, - 0xec, 0x07, 0xb0, 0xc6, 0x69, 0xd3, 0xf3, 0x8b, 0x77, 0xa7, 0xc7, 0x87, 0x19, 0xaf, 0xf9, 0xdb, - 0x0c, 0x3c, 0xc2, 0xfe, 0xd4, 0xe6, 0xd9, 0x9f, 0xa3, 0x8c, 0x93, 0x36, 0x5d, 0x99, 0x5b, 0xaa, - 0x2c, 0xcf, 0x8e, 0x8f, 0xb1, 0xf6, 0x20, 0xcf, 0x13, 0xbc, 0xe4, 0x42, 0x65, 0x7e, 0x7a, 0x7c, - 0x5c, 0xd0, 0x54, 0xe7, 0x6a, 0x0b, 0xb7, 0xc7, 0x4f, 0x62, 0x43, 0xde, 0x9d, 0xc7, 0x1f, 0x84, - 0x15, 0xc0, 0xbf, 0x4e, 0x3d, 0xf7, 0x03, 0x50, 0x5a, 0xac, 0xe3, 0x7e, 0xfb, 0x1c, 0x9c, 0x5a, - 0xac, 0x37, 0x96, 0xdf, 0x5d, 0x9a, 0x8e, 0xc9, 0xfb, 0x24, 0x8c, 0xfa, 0x88, 0xb9, 0xda, 0xc2, - 0x9d, 0x4f, 0x73, 0x69, 0xfb, 0xa0, 0xf9, 0xca, 0xd4, 0x62, 0x7d, 0x3c, 0xcf, 0x7a, 0xc5, 0x07, - 0xdd, 0xab, 0x2d, 0x54, 0x17, 0xef, 0xd5, 0xc7, 0x0b, 0xcf, 0x3d, 0x84, 0x91, 0x2a, 0x65, 0x13, - 0xdb, 0xa2, 0x63, 0xae, 0x9a, 0x16, 0xb9, 0x04, 0xe7, 0xab, 0xd3, 0x77, 0x6b, 0x53, 0xd3, 0x8d, - 0x45, 0xad, 0x76, 0xb3, 0xb6, 0x10, 0xab, 0xe9, 0x0c, 0x9c, 0x8c, 0xa2, 0x2b, 0x4b, 0xb5, 0xf1, - 0x1c, 0x39, 0x0b, 0x24, 0x0a, 0xbe, 0x55, 0x99, 0x9f, 0x19, 0xcf, 0x13, 0x05, 0x4e, 0x47, 0xe1, - 0xb5, 0x85, 0xe5, 0x3b, 0x0b, 0xd3, 0xe3, 0x85, 0xe7, 0xfe, 0x5a, 0x0e, 0xce, 0xa4, 0x26, 0xba, - 0x21, 0x2a, 0x3c, 0x39, 0x3d, 0x57, 0xa9, 0x2f, 0xd7, 0xa6, 0xea, 0xd3, 0x15, 0x6d, 0x6a, 0xb6, - 0x31, 0x55, 0x59, 0x9e, 0xbe, 0xb9, 0xa8, 0xbd, 0xdb, 0xb8, 0x39, 0xbd, 0x30, 0xad, 0x55, 0xe6, - 0xc6, 0xfb, 0xc8, 0x33, 0x50, 0xce, 0xa0, 0xa9, 0x4f, 0x4f, 0xdd, 0xd1, 0x6a, 0xcb, 0xef, 0x8e, - 0xe7, 0xc8, 0xd3, 0x70, 0x29, 0x93, 0x88, 0xfd, 0x1e, 0xcf, 0x93, 0x27, 0xe1, 0x42, 0x16, 0xc9, - 0x3b, 0x73, 0xe3, 0x85, 0xe7, 0x7e, 0x36, 0x07, 0x24, 0x99, 0xa9, 0x84, 0x3c, 0x05, 0x17, 0x99, - 0x5e, 0x34, 0xb2, 0x1b, 0xf8, 0x34, 0x5c, 0x4a, 0xa5, 0x90, 0x9a, 0x57, 0x86, 0x27, 0x32, 0x48, - 0x44, 0xe3, 0x2e, 0x82, 0x92, 0x4e, 0x80, 0x4d, 0xfb, 0xd5, 0x1c, 0x9c, 0x49, 0xf5, 0xb5, 0x27, - 0x57, 0xe0, 0x23, 0x95, 0xea, 0x3c, 0xeb, 0x9b, 0xa9, 0xe5, 0xda, 0xe2, 0x42, 0xbd, 0x31, 0x3f, - 0x53, 0x69, 0x30, 0xed, 0xbb, 0x53, 0x8f, 0xf5, 0xe6, 0x65, 0x50, 0x7b, 0x50, 0x4e, 0xcd, 0x56, - 0x16, 0x6e, 0xb2, 0xe1, 0x47, 0x3e, 0x02, 0x4f, 0x65, 0xd2, 0x4d, 0x2f, 0x54, 0x26, 0xe7, 0xa6, - 0xab, 0xe3, 0x79, 0xf2, 0x2c, 0x3c, 0x9d, 0x49, 0x55, 0xad, 0xd5, 0x39, 0x59, 0xe1, 0x39, 0x3d, - 0xb2, 0x1d, 0x63, 0x5f, 0x39, 0xb5, 0xb8, 0xb0, 0x5c, 0x99, 0x5a, 0x4e, 0xd3, 0xec, 0xf3, 0x70, - 0x26, 0x82, 0x9d, 0xbc, 0x53, 0xaf, 0x2d, 0x4c, 0xd7, 0xeb, 0xe3, 0xb9, 0x04, 0x2a, 0x10, 0x6d, - 0x7e, 0xb2, 0xfa, 0x9d, 0xff, 0xf9, 0xc9, 0xbe, 0xef, 0xfc, 0xd1, 0x93, 0xb9, 0x3f, 0xf8, 0xa3, - 0x27, 0x73, 0xff, 0xfc, 0x8f, 0x9e, 0xcc, 0x7d, 0xe6, 0xfa, 0x6e, 0x92, 0xdc, 0xf0, 0x99, 0x70, - 0xa5, 0x84, 0x16, 0xf1, 0x4b, 0xff, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x27, 0xb1, 0xfc, 0x82, - 0xbb, 0x92, 0x01, 0x00, + // 17695 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x78, 0x25, 0xc9, + 0x75, 0x18, 0x86, 0xfb, 0xc0, 0x05, 0x70, 0xf0, 0x18, 0x4c, 0xcd, 0xab, 0x67, 0x76, 0x66, 0xef, + 0x6e, 0x2f, 0x77, 0x38, 0xb3, 0xdc, 0xc5, 0x70, 0x67, 0x67, 0x77, 0xb9, 0x2f, 0x2e, 0x2f, 0x70, + 0x81, 0xc1, 0x9d, 0xc1, 0x6b, 0xfb, 0x62, 0x66, 0xb8, 0xa4, 0xc8, 0xab, 0xc6, 0xed, 0x1a, 0xa0, + 0x77, 0xee, 0xed, 0xbe, 0xea, 0xee, 0x3b, 0x18, 0xac, 0xf3, 0x10, 0x6d, 0x59, 0x96, 0x6c, 0x8a, + 0x66, 0xa8, 0x48, 0x94, 0x65, 0x3b, 0xa1, 0xec, 0x38, 0x91, 0x65, 0x59, 0x8c, 0x1c, 0x45, 0x12, + 0x25, 0x31, 0x96, 0x4c, 0x3f, 0x28, 0xe9, 0xb3, 0x3e, 0xc9, 0x49, 0x1c, 0x7f, 0x89, 0x03, 0x3a, + 0x4a, 0xfc, 0x07, 0x5f, 0x92, 0x4f, 0x5f, 0xa2, 0x2f, 0x66, 0x1c, 0x3b, 0x9f, 0xbf, 0x3a, 0x55, + 0xdd, 0x5d, 0xfd, 0xba, 0x78, 0xae, 0xb0, 0xe0, 0xe0, 0xcf, 0x0c, 0xee, 0x39, 0xa7, 0x4e, 0x55, + 0x9f, 0x3a, 0x55, 0x75, 0xaa, 0xea, 0xd4, 0x39, 0x70, 0xd5, 0xa3, 0x2d, 0xda, 0xb1, 0x1d, 0xef, + 0x5a, 0x8b, 0xae, 0xea, 0xcd, 0x8d, 0x6b, 0xde, 0x46, 0x87, 0xba, 0xd7, 0xe8, 0x43, 0x6a, 0x79, + 0xfe, 0x7f, 0x13, 0x1d, 0xc7, 0xf6, 0x6c, 0x52, 0xe2, 0xbf, 0x2e, 0x9c, 0x5e, 0xb5, 0x57, 0x6d, + 0x04, 0x5d, 0x63, 0x7f, 0x71, 0xec, 0x85, 0x8b, 0xab, 0xb6, 0xbd, 0xda, 0xa2, 0xd7, 0xf0, 0xd7, + 0x4a, 0xf7, 0xfe, 0x35, 0xd7, 0x73, 0xba, 0x4d, 0x4f, 0x60, 0xcb, 0x71, 0xac, 0x67, 0xb6, 0xa9, + 0xeb, 0xe9, 0xed, 0x8e, 0x20, 0x78, 0x32, 0x4e, 0xb0, 0xee, 0xe8, 0x9d, 0x0e, 0x75, 0x44, 0xe5, + 0x17, 0x3e, 0x1a, 0xb4, 0x53, 0x6f, 0x36, 0xa9, 0xeb, 0xb6, 0x4c, 0xd7, 0xbb, 0xf6, 0xf0, 0x45, + 0xe9, 0x97, 0x20, 0x7c, 0x3a, 0xfd, 0x83, 0xf0, 0x5f, 0x41, 0xf2, 0x42, 0x3a, 0x89, 0x5f, 0x63, + 0xac, 0x6a, 0xf5, 0x2b, 0x79, 0x18, 0x9c, 0xa7, 0x9e, 0x6e, 0xe8, 0x9e, 0x4e, 0x2e, 0x42, 0x7f, + 0xcd, 0x32, 0xe8, 0x23, 0x25, 0xf7, 0x54, 0xee, 0x4a, 0x61, 0xb2, 0xb4, 0xb5, 0x59, 0xce, 0x53, + 0x53, 0xe3, 0x40, 0x72, 0x09, 0x8a, 0xcb, 0x1b, 0x1d, 0xaa, 0xe4, 0x9f, 0xca, 0x5d, 0x19, 0x9a, + 0x1c, 0xda, 0xda, 0x2c, 0xf7, 0xa3, 0xd0, 0x34, 0x04, 0x93, 0xa7, 0x21, 0x5f, 0xab, 0x2a, 0x05, + 0x44, 0x9e, 0xdc, 0xda, 0x2c, 0x8f, 0x76, 0x4d, 0xe3, 0x79, 0xbb, 0x6d, 0x7a, 0xb4, 0xdd, 0xf1, + 0x36, 0xb4, 0x7c, 0xad, 0x4a, 0x2e, 0x43, 0x71, 0xca, 0x36, 0xa8, 0x52, 0x44, 0x22, 0xb2, 0xb5, + 0x59, 0x1e, 0x6b, 0xda, 0x06, 0x95, 0xa8, 0x10, 0x4f, 0x3e, 0x05, 0xc5, 0x65, 0xb3, 0x4d, 0x95, + 0xfe, 0xa7, 0x72, 0x57, 0x86, 0xaf, 0x5f, 0x98, 0xe0, 0xe2, 0x9b, 0xf0, 0xc5, 0x37, 0xb1, 0xec, + 0xcb, 0x77, 0x72, 0xfc, 0xdb, 0x9b, 0xe5, 0xbe, 0xad, 0xcd, 0x72, 0x91, 0x89, 0xfc, 0xcb, 0xdf, + 0x29, 0xe7, 0x34, 0x2c, 0x49, 0xde, 0x84, 0xe1, 0xa9, 0x56, 0xd7, 0xf5, 0xa8, 0xb3, 0xa0, 0xb7, + 0xa9, 0x52, 0xc2, 0x0a, 0x2f, 0x6c, 0x6d, 0x96, 0xcf, 0x36, 0x39, 0xb8, 0x61, 0xe9, 0x6d, 0xb9, + 0x62, 0x99, 0x5c, 0xfd, 0xd5, 0x1c, 0x9c, 0xa8, 0x53, 0xd7, 0x35, 0x6d, 0x2b, 0x90, 0xcd, 0xb3, + 0x30, 0x24, 0x40, 0xb5, 0x2a, 0xca, 0x67, 0x68, 0x72, 0x60, 0x6b, 0xb3, 0x5c, 0x70, 0x4d, 0x43, + 0x0b, 0x31, 0xe4, 0xe3, 0x30, 0x70, 0xcf, 0xf4, 0xd6, 0xe6, 0x67, 0x2a, 0x42, 0x4e, 0x67, 0xb7, + 0x36, 0xcb, 0x64, 0xdd, 0xf4, 0xd6, 0x1a, 0xed, 0xfb, 0xba, 0x54, 0xa1, 0x4f, 0x46, 0xe6, 0x60, + 0x7c, 0xc9, 0x31, 0x1f, 0xea, 0x1e, 0xbd, 0x4d, 0x37, 0x96, 0xec, 0x96, 0xd9, 0xdc, 0x10, 0x52, + 0x7c, 0x6a, 0x6b, 0xb3, 0x7c, 0xb1, 0xc3, 0x71, 0x8d, 0x07, 0x74, 0xa3, 0xd1, 0x41, 0xac, 0xc4, + 0x24, 0x51, 0x52, 0xfd, 0xb5, 0x12, 0x8c, 0xdc, 0x71, 0xa9, 0x13, 0xb4, 0xfb, 0x32, 0x14, 0xd9, + 0x6f, 0xd1, 0x64, 0x94, 0x79, 0xd7, 0xa5, 0x8e, 0x2c, 0x73, 0x86, 0x27, 0x57, 0xa1, 0x7f, 0xce, + 0x5e, 0x35, 0x2d, 0xd1, 0xec, 0x53, 0x5b, 0x9b, 0xe5, 0x13, 0x2d, 0x06, 0x90, 0x28, 0x39, 0x05, + 0xf9, 0x24, 0x8c, 0xd4, 0xda, 0x4c, 0x87, 0x6c, 0x4b, 0xf7, 0x6c, 0x47, 0xb4, 0x16, 0xa5, 0x6b, + 0x4a, 0x70, 0xa9, 0x60, 0x84, 0x9e, 0xbc, 0x0e, 0x50, 0xb9, 0x57, 0xd7, 0xec, 0x16, 0xad, 0x68, + 0x0b, 0x42, 0x19, 0xb0, 0xb4, 0xbe, 0xee, 0x36, 0x1c, 0xbb, 0x45, 0x1b, 0xba, 0x23, 0x57, 0x2b, + 0x51, 0x93, 0x69, 0x18, 0xab, 0xe0, 0xa8, 0xd0, 0xe8, 0x0f, 0x74, 0xa9, 0xeb, 0xb9, 0x4a, 0xff, + 0x53, 0x85, 0x2b, 0x43, 0x93, 0x97, 0xb6, 0x36, 0xcb, 0xe7, 0xf9, 0x78, 0x69, 0x38, 0x02, 0x25, + 0xb1, 0x88, 0x15, 0x22, 0x93, 0x30, 0x5a, 0x79, 0xbf, 0xeb, 0xd0, 0x9a, 0x41, 0x2d, 0xcf, 0xf4, + 0x36, 0x84, 0x86, 0x5c, 0xdc, 0xda, 0x2c, 0x2b, 0x3a, 0x43, 0x34, 0x4c, 0x81, 0x91, 0x98, 0x44, + 0x8b, 0x90, 0x45, 0x38, 0x79, 0x73, 0x6a, 0xa9, 0x4e, 0x9d, 0x87, 0x66, 0x93, 0x56, 0x9a, 0x4d, + 0xbb, 0x6b, 0x79, 0xca, 0x00, 0xf2, 0x79, 0x7a, 0x6b, 0xb3, 0x7c, 0x69, 0xb5, 0xd9, 0x69, 0xb8, + 0x1c, 0xdb, 0xd0, 0x39, 0x5a, 0x62, 0x96, 0x2c, 0x4b, 0x3e, 0x03, 0xa3, 0xcb, 0x0e, 0xd3, 0x42, + 0xa3, 0x4a, 0x19, 0x5c, 0x19, 0x44, 0xfd, 0x3f, 0x3b, 0x21, 0x66, 0x2a, 0x0e, 0xf5, 0x7b, 0x96, + 0x37, 0xd6, 0xe3, 0x05, 0x1a, 0x06, 0xe2, 0xe4, 0xc6, 0x46, 0x58, 0x11, 0x0a, 0x0a, 0xfb, 0x78, + 0xd3, 0xa1, 0x46, 0x42, 0xdb, 0x86, 0xb0, 0xcd, 0x57, 0xb7, 0x36, 0xcb, 0xcf, 0x3a, 0x82, 0xa6, + 0xd1, 0x53, 0xed, 0x32, 0x59, 0x91, 0x69, 0x18, 0x64, 0xda, 0x74, 0xdb, 0xb4, 0x0c, 0x05, 0x9e, + 0xca, 0x5d, 0x19, 0xbb, 0x3e, 0xee, 0xb7, 0xde, 0x87, 0x4f, 0x9e, 0xdb, 0xda, 0x2c, 0x9f, 0x62, + 0x3a, 0xd8, 0x78, 0x60, 0x5a, 0xf2, 0x14, 0x11, 0x14, 0x65, 0xa3, 0x68, 0xd2, 0xf6, 0x70, 0xe8, + 0x0e, 0x87, 0xa3, 0x68, 0xc5, 0xf6, 0xe2, 0xc3, 0xd6, 0x27, 0x23, 0x53, 0x30, 0x3a, 0x69, 0x7b, + 0x35, 0xcb, 0xf5, 0x74, 0xab, 0x49, 0x6b, 0x55, 0x65, 0x04, 0xcb, 0xa1, 0x5a, 0xb0, 0x72, 0xa6, + 0xc0, 0x34, 0x22, 0x93, 0x52, 0xb4, 0x8c, 0xfa, 0xaf, 0x8a, 0x30, 0xc6, 0xfa, 0x44, 0x1a, 0x3e, + 0x15, 0x36, 0x13, 0x30, 0x08, 0xab, 0xc5, 0xed, 0xe8, 0x4d, 0x2a, 0x46, 0x12, 0x7e, 0x85, 0xe5, + 0x03, 0x25, 0x9e, 0x71, 0x7a, 0x72, 0x15, 0x06, 0x39, 0xa8, 0x56, 0x15, 0x83, 0x6b, 0x74, 0x6b, + 0xb3, 0x3c, 0xe4, 0x22, 0xac, 0x61, 0x1a, 0x5a, 0x80, 0x66, 0xda, 0xcd, 0xff, 0x9e, 0xb5, 0x5d, + 0x8f, 0x31, 0x17, 0x63, 0x0b, 0x3f, 0x43, 0x14, 0x58, 0x13, 0x28, 0x59, 0xbb, 0xa3, 0x85, 0xc8, + 0x6b, 0x00, 0x1c, 0x52, 0x31, 0x0c, 0x47, 0x0c, 0xb0, 0xf3, 0x5b, 0x9b, 0xe5, 0x33, 0x82, 0x85, + 0x6e, 0x18, 0xf2, 0xe8, 0x94, 0x88, 0x49, 0x1b, 0x46, 0xf8, 0xaf, 0x39, 0x7d, 0x85, 0xb6, 0xf8, + 0xe8, 0x1a, 0xbe, 0x7e, 0xc5, 0xef, 0xc4, 0xa8, 0x74, 0x26, 0x64, 0xd2, 0x69, 0xcb, 0x73, 0x36, + 0x26, 0xcb, 0x62, 0x42, 0x3e, 0x27, 0xaa, 0x6a, 0x21, 0x4e, 0x9e, 0x0a, 0xe4, 0x32, 0x6c, 0x9e, + 0x9e, 0xb1, 0x9d, 0x75, 0xdd, 0x31, 0xa8, 0x31, 0xb9, 0x21, 0xcf, 0xd3, 0xf7, 0x7d, 0x70, 0x63, + 0x45, 0x56, 0x3d, 0x99, 0x9c, 0x75, 0x3a, 0xe7, 0x56, 0xef, 0xae, 0xa0, 0xca, 0x0d, 0x24, 0xa4, + 0xe5, 0x76, 0x57, 0xe2, 0x6a, 0x16, 0x2d, 0xc3, 0xa6, 0x02, 0x0e, 0xb8, 0x4b, 0x1d, 0x36, 0x89, + 0xe3, 0xa8, 0x13, 0x53, 0x81, 0x60, 0xf2, 0x90, 0x63, 0x92, 0x3c, 0x44, 0x91, 0x0b, 0x6f, 0xc3, + 0xc9, 0x84, 0x28, 0xc8, 0x38, 0x14, 0x1e, 0xd0, 0x0d, 0xae, 0x2e, 0x1a, 0xfb, 0x93, 0x9c, 0x86, + 0xfe, 0x87, 0x7a, 0xab, 0x2b, 0x96, 0x50, 0x8d, 0xff, 0x78, 0x3d, 0xff, 0x89, 0x1c, 0x5b, 0x71, + 0xc8, 0x94, 0x6d, 0x59, 0xb4, 0xe9, 0xc9, 0x8b, 0xce, 0x2b, 0x30, 0x34, 0x67, 0x37, 0xf5, 0x16, + 0xf6, 0x23, 0xd7, 0x3b, 0x65, 0x6b, 0xb3, 0x7c, 0x9a, 0x75, 0xe0, 0x44, 0x8b, 0x61, 0xa4, 0x36, + 0x85, 0xa4, 0x4c, 0x01, 0x34, 0xda, 0xb6, 0x3d, 0x8a, 0x05, 0xf3, 0xa1, 0x02, 0x60, 0x41, 0x07, + 0x51, 0xb2, 0x02, 0x84, 0xc4, 0xe4, 0x1a, 0x0c, 0x2e, 0xb1, 0x75, 0xb6, 0x69, 0xb7, 0x84, 0xf2, + 0xe1, 0x52, 0x80, 0x6b, 0xaf, 0x3c, 0x56, 0x7d, 0x22, 0x75, 0x16, 0xc6, 0xa6, 0x5a, 0x26, 0xb5, + 0x3c, 0xb9, 0xd5, 0x6c, 0x24, 0x57, 0x56, 0xa9, 0xe5, 0xc9, 0xad, 0xc6, 0x31, 0xaf, 0x33, 0xa8, + 0xdc, 0xea, 0x80, 0x54, 0xfd, 0xbd, 0x02, 0x9c, 0xbf, 0xdd, 0x5d, 0xa1, 0x8e, 0x45, 0x3d, 0xea, + 0x8a, 0x05, 0x39, 0xe0, 0xba, 0x00, 0x27, 0x13, 0x48, 0xc1, 0x1d, 0x17, 0xca, 0x07, 0x01, 0xb2, + 0x21, 0xd6, 0x78, 0x79, 0xb6, 0x4d, 0x14, 0x25, 0xb3, 0x70, 0x22, 0x04, 0xb2, 0x46, 0xb8, 0x4a, + 0x1e, 0x97, 0x92, 0x27, 0xb7, 0x36, 0xcb, 0x17, 0x24, 0x6e, 0xac, 0xd9, 0xb2, 0x06, 0xc7, 0x8b, + 0x91, 0xdb, 0x30, 0x1e, 0x82, 0x6e, 0x3a, 0x76, 0xb7, 0xe3, 0x2a, 0x05, 0x64, 0x55, 0xde, 0xda, + 0x2c, 0x3f, 0x21, 0xb1, 0x5a, 0x45, 0xa4, 0xbc, 0x80, 0xc7, 0x0b, 0x92, 0x1f, 0xca, 0xc9, 0xdc, + 0xc4, 0x28, 0x2c, 0xe2, 0x28, 0x7c, 0xd5, 0x1f, 0x85, 0x99, 0x42, 0x9a, 0x88, 0x97, 0x14, 0x83, + 0x32, 0xd6, 0x8c, 0xc4, 0xa0, 0x4c, 0xd4, 0x78, 0x61, 0x0a, 0xce, 0xa4, 0xf2, 0xda, 0x95, 0x56, + 0xff, 0xcb, 0x82, 0xcc, 0x65, 0xc9, 0x36, 0x82, 0xce, 0x5c, 0x94, 0x3b, 0x73, 0xc9, 0x36, 0x70, + 0xaa, 0xcf, 0x85, 0x6b, 0xa7, 0xd4, 0xd8, 0x8e, 0x6d, 0xc4, 0x67, 0xfd, 0x64, 0x59, 0xf2, 0x79, + 0x38, 0x9b, 0x00, 0xf2, 0xe9, 0x9a, 0x6b, 0xff, 0xe5, 0xad, 0xcd, 0xb2, 0x9a, 0xc2, 0x35, 0x3e, + 0x7b, 0x67, 0x70, 0x21, 0x3a, 0x9c, 0x93, 0xa4, 0x6e, 0x5b, 0x9e, 0x6e, 0x5a, 0xc2, 0xb8, 0xe4, + 0xa3, 0xe4, 0xa3, 0x5b, 0x9b, 0xe5, 0x67, 0x64, 0x1d, 0xf4, 0x69, 0xe2, 0x8d, 0xcf, 0xe2, 0x43, + 0x0c, 0x50, 0x52, 0x50, 0xb5, 0xb6, 0xbe, 0xea, 0x5b, 0xcc, 0x57, 0xb6, 0x36, 0xcb, 0x1f, 0x49, + 0xad, 0xc3, 0x64, 0x54, 0xf2, 0x0a, 0x9d, 0xc5, 0x89, 0x68, 0x40, 0x42, 0xdc, 0x82, 0x6d, 0x50, + 0xfc, 0x86, 0x7e, 0xe4, 0xaf, 0x6e, 0x6d, 0x96, 0x9f, 0x94, 0xf8, 0x5b, 0xb6, 0x41, 0xe3, 0xcd, + 0x4f, 0x29, 0xad, 0xfe, 0x6a, 0x01, 0x9e, 0xac, 0x57, 0xe6, 0xe7, 0x6a, 0x86, 0x6f, 0xd2, 0x2c, + 0x39, 0xf6, 0x43, 0xd3, 0x90, 0x46, 0xef, 0x0a, 0x9c, 0x8b, 0xa1, 0xa6, 0xd1, 0x8a, 0x0a, 0x8c, + 0x69, 0xfc, 0x36, 0xdf, 0x5c, 0xea, 0x08, 0x9a, 0x06, 0x37, 0xb5, 0xa2, 0x8b, 0x76, 0x16, 0x23, + 0xd6, 0x47, 0x31, 0x54, 0x7d, 0xcd, 0x76, 0xbc, 0x66, 0xd7, 0x13, 0x4a, 0x80, 0x7d, 0x94, 0xa8, + 0xc3, 0x15, 0x44, 0x3d, 0xaa, 0xf0, 0xf9, 0x90, 0x1f, 0xcd, 0xc1, 0x78, 0xc5, 0xf3, 0x1c, 0x73, + 0xa5, 0xeb, 0xd1, 0x79, 0xbd, 0xd3, 0x31, 0xad, 0x55, 0x1c, 0xeb, 0xc3, 0xd7, 0xdf, 0x0c, 0xd6, + 0xc8, 0x9e, 0x92, 0x98, 0x88, 0x17, 0x97, 0x86, 0xa8, 0xee, 0xa3, 0x1a, 0x6d, 0x8e, 0x93, 0x87, + 0x68, 0xbc, 0x1c, 0x1b, 0xa2, 0xa9, 0xbc, 0x76, 0x35, 0x44, 0xbf, 0x52, 0x80, 0x8b, 0x8b, 0x0f, + 0x3c, 0x5d, 0xa3, 0xae, 0xdd, 0x75, 0x9a, 0xd4, 0xbd, 0xd3, 0x31, 0x74, 0x8f, 0x86, 0x23, 0xb5, + 0x0c, 0xfd, 0x15, 0xc3, 0xa0, 0x06, 0xb2, 0xeb, 0xe7, 0xdb, 0x3e, 0x9d, 0x01, 0x34, 0x0e, 0x27, + 0xcf, 0xc2, 0x80, 0x28, 0x83, 0xdc, 0xfb, 0x27, 0x87, 0xb7, 0x36, 0xcb, 0x03, 0x5d, 0x0e, 0xd2, + 0x7c, 0x1c, 0x23, 0xab, 0xd2, 0x16, 0x65, 0x64, 0x85, 0x90, 0xcc, 0xe0, 0x20, 0xcd, 0xc7, 0x91, + 0x77, 0x60, 0x0c, 0xd9, 0x06, 0xed, 0x11, 0x73, 0xdf, 0x69, 0x5f, 0xba, 0x72, 0x63, 0xf9, 0xd2, + 0x84, 0xad, 0x69, 0x38, 0x7e, 0x01, 0x2d, 0xc6, 0x80, 0xdc, 0x83, 0x71, 0xd1, 0x88, 0x90, 0x69, + 0x7f, 0x0f, 0xa6, 0x67, 0xb6, 0x36, 0xcb, 0x27, 0x45, 0xfb, 0x25, 0xb6, 0x09, 0x26, 0x8c, 0xb1, + 0x68, 0x76, 0xc8, 0xb8, 0xb4, 0x1d, 0x63, 0xf1, 0xc5, 0x32, 0xe3, 0x38, 0x13, 0xf5, 0x5d, 0x18, + 0x91, 0x0b, 0x92, 0xb3, 0xb8, 0xb5, 0xe6, 0xe3, 0x04, 0x37, 0xe5, 0xa6, 0x81, 0xfb, 0xe9, 0x17, + 0x61, 0xb8, 0x4a, 0xdd, 0xa6, 0x63, 0x76, 0x98, 0xd5, 0x20, 0x94, 0xfc, 0xc4, 0xd6, 0x66, 0x79, + 0xd8, 0x08, 0xc1, 0x9a, 0x4c, 0xa3, 0xfe, 0x3f, 0x39, 0x38, 0xcb, 0x78, 0x57, 0x5c, 0xd7, 0x5c, + 0xb5, 0xda, 0xf2, 0xb2, 0xfd, 0x3c, 0x94, 0xea, 0x58, 0x9f, 0xa8, 0xe9, 0xf4, 0xd6, 0x66, 0x79, + 0x9c, 0xb7, 0x40, 0xd2, 0x43, 0x41, 0x13, 0xec, 0x2b, 0xf3, 0xdb, 0xec, 0x2b, 0x99, 0x49, 0xeb, + 0xe9, 0x8e, 0x67, 0x5a, 0xab, 0x75, 0x4f, 0xf7, 0xba, 0x6e, 0xc4, 0xa4, 0x15, 0x98, 0x86, 0x8b, + 0xa8, 0x88, 0x49, 0x1b, 0x29, 0x44, 0xde, 0x86, 0x91, 0x69, 0xcb, 0x08, 0x99, 0xf0, 0x09, 0xf1, + 0x09, 0x66, 0x69, 0x52, 0x84, 0x27, 0x59, 0x44, 0x0a, 0xa8, 0x7f, 0x3b, 0x07, 0x0a, 0xdf, 0x04, + 0xce, 0x99, 0xae, 0x37, 0x4f, 0xdb, 0x2b, 0xd2, 0xec, 0x34, 0xe3, 0xef, 0x2a, 0x19, 0x4e, 0x5a, + 0x8b, 0xd0, 0x14, 0x10, 0xbb, 0xca, 0x96, 0xe9, 0x26, 0xb6, 0x1f, 0xb1, 0x52, 0xa4, 0x06, 0x03, + 0x9c, 0x33, 0xb7, 0x25, 0x86, 0xaf, 0x2b, 0xbe, 0x22, 0xc4, 0xab, 0xe6, 0xca, 0xd0, 0xe6, 0xc4, + 0xf2, 0x86, 0x46, 0x94, 0x57, 0xbf, 0x56, 0x80, 0xf1, 0x78, 0x21, 0x72, 0x0f, 0x06, 0x6f, 0xd9, + 0xa6, 0x45, 0x8d, 0x45, 0x0b, 0x5b, 0xd8, 0xfb, 0x70, 0xc4, 0xb7, 0xc5, 0x4f, 0xbd, 0x87, 0x65, + 0x1a, 0xb2, 0x05, 0x8b, 0x67, 0x25, 0x01, 0x33, 0xf2, 0x19, 0x18, 0x62, 0x36, 0xe0, 0x43, 0xe4, + 0x9c, 0xdf, 0x96, 0xf3, 0x53, 0x82, 0xf3, 0x69, 0x87, 0x17, 0x4a, 0xb2, 0x0e, 0xd9, 0x31, 0xbd, + 0xd2, 0xa8, 0xee, 0xda, 0x96, 0xe8, 0x79, 0xd4, 0x2b, 0x07, 0x21, 0xb2, 0x5e, 0x71, 0x1a, 0x66, + 0xba, 0xf2, 0x8f, 0xc5, 0x6e, 0x90, 0xf6, 0x2e, 0x5c, 0x56, 0xf1, 0x1e, 0x90, 0x88, 0x89, 0x05, + 0x27, 0x84, 0x40, 0xd7, 0xcc, 0x0e, 0x5a, 0xfd, 0xb8, 0xae, 0x8d, 0x5d, 0xbf, 0x3c, 0xe1, 0x1f, + 0x8a, 0x4d, 0x48, 0x47, 0x6a, 0x0f, 0x5f, 0x9c, 0x98, 0x0f, 0xc8, 0x71, 0x67, 0x8a, 0x3a, 0x19, + 0x63, 0x21, 0xf7, 0x76, 0x3b, 0x42, 0xae, 0xfe, 0x70, 0x1e, 0x5e, 0x08, 0xbb, 0x48, 0xa3, 0x0f, + 0x4d, 0xba, 0x1e, 0x72, 0x14, 0x7b, 0x64, 0x36, 0xc4, 0xdc, 0xa9, 0x35, 0xdd, 0x5a, 0xa5, 0x06, + 0xb9, 0x0a, 0xfd, 0x9a, 0xdd, 0xa2, 0xae, 0x92, 0x43, 0xf3, 0x10, 0xa7, 0x2f, 0x87, 0x01, 0xe4, + 0x43, 0x16, 0xa4, 0x20, 0x36, 0x94, 0x96, 0x1d, 0xdd, 0xf4, 0x7c, 0x4d, 0xaa, 0x24, 0x35, 0x69, + 0x07, 0x35, 0x4e, 0x70, 0x1e, 0x7c, 0x8d, 0x41, 0xc1, 0x7b, 0x08, 0x90, 0x05, 0xcf, 0x49, 0x2e, + 0xbc, 0x06, 0xc3, 0x12, 0xf1, 0xae, 0x16, 0x91, 0x6f, 0x14, 0xe5, 0xb1, 0xe5, 0x37, 0x4b, 0x8c, + 0xad, 0x6b, 0x6c, 0x4c, 0xb8, 0x2e, 0xb3, 0x62, 0xf8, 0xa0, 0x12, 0x9a, 0x8f, 0xa0, 0xa8, 0xe6, + 0x23, 0x88, 0xbc, 0x04, 0x83, 0x9c, 0x45, 0xb0, 0x5f, 0xc6, 0xbd, 0xb6, 0x83, 0xb0, 0xa8, 0x29, + 0x10, 0x10, 0x92, 0x9f, 0xcb, 0xc1, 0xa5, 0x9e, 0x92, 0x40, 0xe5, 0x1b, 0xbe, 0xfe, 0xf2, 0x9e, + 0xc4, 0x38, 0xf9, 0xc2, 0xd6, 0x66, 0xf9, 0xaa, 0xa4, 0x19, 0x8e, 0x44, 0xd3, 0x68, 0x72, 0x22, + 0xa9, 0x5d, 0xbd, 0x9b, 0xc2, 0x8c, 0x55, 0x5e, 0xe9, 0x0c, 0x1e, 0x55, 0x59, 0xcd, 0x0d, 0xbf, + 0x91, 0xc5, 0xd0, 0x58, 0x15, 0xdf, 0x7b, 0xdf, 0x27, 0x49, 0xa9, 0x26, 0x83, 0x0b, 0x69, 0xc2, + 0x39, 0x8e, 0xa9, 0xea, 0x1b, 0x8b, 0xf7, 0xe7, 0x6d, 0xcb, 0x5b, 0xf3, 0x2b, 0xe8, 0x97, 0xcf, + 0x7a, 0xb0, 0x02, 0x43, 0xdf, 0x68, 0xd8, 0xf7, 0x1b, 0x6d, 0x46, 0x95, 0x52, 0x47, 0x16, 0x27, + 0x36, 0xb1, 0x8b, 0x31, 0xee, 0x4f, 0x79, 0xa5, 0xf0, 0x24, 0xce, 0x9f, 0x17, 0x92, 0x13, 0x5c, + 0xac, 0x90, 0x5a, 0x83, 0x91, 0x39, 0xbb, 0xf9, 0x20, 0x50, 0x97, 0xd7, 0xa0, 0xb4, 0xac, 0x3b, + 0xab, 0xd4, 0x43, 0x59, 0x0c, 0x5f, 0x3f, 0x39, 0xc1, 0x4f, 0xb7, 0x19, 0x11, 0x47, 0x4c, 0x8e, + 0x89, 0xd9, 0xa7, 0xe4, 0xe1, 0x6f, 0x4d, 0x14, 0x50, 0xbf, 0xd3, 0x0f, 0x23, 0xe2, 0x24, 0x16, + 0x57, 0x0f, 0xf2, 0x7a, 0x78, 0xb6, 0x2d, 0xa6, 0xcb, 0xe0, 0x34, 0x2a, 0x38, 0x45, 0x1b, 0x61, + 0xcc, 0x7e, 0x7f, 0xb3, 0x9c, 0xdb, 0xda, 0x2c, 0xf7, 0x69, 0x83, 0xd2, 0x26, 0x36, 0x5c, 0xdf, + 0xa4, 0x05, 0x5d, 0x3e, 0x5b, 0x8d, 0x95, 0xe5, 0xeb, 0xdd, 0xdb, 0x30, 0x20, 0xda, 0x20, 0x34, + 0xee, 0x5c, 0x78, 0x76, 0x12, 0x39, 0x51, 0x8e, 0x95, 0xf6, 0x4b, 0x91, 0x37, 0xa1, 0xc4, 0xcf, + 0x12, 0x84, 0x00, 0xce, 0xa6, 0x9f, 0xbd, 0xc4, 0x8a, 0x8b, 0x32, 0x64, 0x16, 0x20, 0x3c, 0x47, + 0x08, 0x0e, 0xd0, 0x05, 0x87, 0xe4, 0x09, 0x43, 0x8c, 0x8b, 0x54, 0x96, 0xbc, 0x02, 0x23, 0xcb, + 0xd4, 0x69, 0x9b, 0x96, 0xde, 0xaa, 0x9b, 0xef, 0xfb, 0x67, 0xe8, 0xb8, 0xd0, 0xbb, 0xe6, 0xfb, + 0xf2, 0xc8, 0x8d, 0xd0, 0x91, 0xcf, 0xa5, 0xed, 0xd3, 0x07, 0xb0, 0x21, 0x4f, 0x6f, 0xbb, 0x81, + 0x8d, 0xb5, 0x27, 0x65, 0xdb, 0xfe, 0x0e, 0x8c, 0x46, 0xb6, 0x68, 0xe2, 0x90, 0xf4, 0x52, 0x92, + 0xb5, 0xb4, 0xdf, 0x8c, 0xb1, 0x8d, 0x72, 0x60, 0x9a, 0x5c, 0xb3, 0x4c, 0xcf, 0xd4, 0x5b, 0x53, + 0x76, 0xbb, 0xad, 0x5b, 0x86, 0x32, 0x14, 0x6a, 0xb2, 0xc9, 0x31, 0x8d, 0x26, 0x47, 0xc9, 0x9a, + 0x1c, 0x2d, 0x44, 0x6e, 0xc3, 0xb8, 0xe8, 0x43, 0x8d, 0x36, 0x6d, 0x87, 0xd9, 0x1e, 0x78, 0x06, + 0x2a, 0x8e, 0x01, 0x5c, 0x8e, 0x6b, 0x38, 0x3e, 0x52, 0x36, 0xee, 0xe3, 0x05, 0x6f, 0x15, 0x07, + 0x87, 0xc7, 0x47, 0xe2, 0xc7, 0xd6, 0xea, 0xdf, 0x2c, 0xc0, 0xb0, 0x20, 0x65, 0x4b, 0xf7, 0xb1, + 0x82, 0xef, 0x47, 0xc1, 0x53, 0x15, 0xb5, 0x74, 0x50, 0x8a, 0xaa, 0x7e, 0x31, 0x1f, 0xcc, 0x46, + 0x4b, 0x8e, 0x69, 0xed, 0x6f, 0x36, 0xba, 0x0c, 0x30, 0xb5, 0xd6, 0xb5, 0x1e, 0xf0, 0xeb, 0xb9, + 0x7c, 0x78, 0x3d, 0xd7, 0x34, 0x35, 0x09, 0x43, 0x2e, 0x41, 0xb1, 0xca, 0xf8, 0xb3, 0x9e, 0x19, + 0x99, 0x1c, 0xfa, 0x36, 0xe7, 0x94, 0x7b, 0x41, 0x43, 0x30, 0xdb, 0xcc, 0x4d, 0x6e, 0x78, 0x94, + 0x9b, 0xcf, 0x05, 0xbe, 0x99, 0x5b, 0x61, 0x00, 0x8d, 0xc3, 0xc9, 0x0d, 0x38, 0x59, 0xa5, 0x2d, + 0x7d, 0x63, 0xde, 0x6c, 0xb5, 0x4c, 0x97, 0x36, 0x6d, 0xcb, 0x70, 0x51, 0xc8, 0xa2, 0xba, 0xb6, + 0xab, 0x25, 0x09, 0x88, 0x0a, 0xa5, 0xc5, 0xfb, 0xf7, 0x5d, 0xea, 0xa1, 0xf8, 0x0a, 0x93, 0xc0, + 0x26, 0x67, 0x1b, 0x21, 0x9a, 0xc0, 0xa8, 0x5f, 0xcf, 0xb1, 0xdd, 0x92, 0xfb, 0xc0, 0xb3, 0x3b, + 0x81, 0x96, 0xef, 0x4b, 0x24, 0x57, 0x43, 0xbb, 0x22, 0x8f, 0x5f, 0x7b, 0x42, 0x7c, 0xed, 0x80, + 0xb0, 0x2d, 0x42, 0x8b, 0x22, 0xf5, 0xab, 0x0a, 0xdb, 0x7c, 0x95, 0xfa, 0x47, 0x79, 0x38, 0x27, + 0x5a, 0x3c, 0xd5, 0x32, 0x3b, 0x2b, 0xb6, 0xee, 0x18, 0x1a, 0x6d, 0x52, 0xf3, 0x21, 0x3d, 0x9a, + 0x03, 0x2f, 0x3a, 0x74, 0x8a, 0xfb, 0x18, 0x3a, 0xd7, 0x71, 0xe3, 0xc9, 0x24, 0x83, 0x07, 0xcc, + 0xdc, 0xa8, 0x18, 0xdf, 0xda, 0x2c, 0x8f, 0x18, 0x1c, 0x8c, 0x57, 0x0c, 0x9a, 0x4c, 0xc4, 0x94, + 0x64, 0x8e, 0x5a, 0xab, 0xde, 0x1a, 0x2a, 0x49, 0x3f, 0x57, 0x92, 0x16, 0x42, 0x34, 0x81, 0x51, + 0xff, 0x8f, 0x3c, 0x9c, 0x8e, 0x8b, 0xbc, 0x4e, 0x2d, 0xe3, 0x58, 0xde, 0x1f, 0x8c, 0xbc, 0xff, + 0xb8, 0x00, 0x4f, 0x88, 0x32, 0xf5, 0x35, 0xdd, 0xa1, 0x46, 0xd5, 0x74, 0x68, 0xd3, 0xb3, 0x9d, + 0x8d, 0x23, 0x6c, 0x40, 0x1d, 0x9c, 0xd8, 0x6f, 0x40, 0x49, 0x1c, 0x37, 0xf0, 0x75, 0x66, 0x2c, + 0x68, 0x09, 0x42, 0x13, 0x2b, 0x14, 0x3f, 0xaa, 0x88, 0x75, 0x56, 0x69, 0x27, 0x9d, 0xf5, 0x09, + 0x18, 0x0d, 0x44, 0x8f, 0x1b, 0xdf, 0x81, 0xd0, 0xda, 0x32, 0x7c, 0x04, 0xee, 0x7d, 0xb5, 0x28, + 0x21, 0xd6, 0xe6, 0x03, 0x6a, 0x55, 0xb4, 0x86, 0x46, 0x45, 0x6d, 0x41, 0x39, 0xd3, 0xd0, 0x64, + 0x22, 0x75, 0xb3, 0x08, 0x17, 0xd2, 0xbb, 0x5d, 0xa3, 0xba, 0x71, 0xdc, 0xeb, 0xdf, 0x93, 0xbd, + 0x4e, 0x9e, 0x86, 0xe2, 0x92, 0xee, 0xad, 0x89, 0xeb, 0x7e, 0xbc, 0x83, 0xbe, 0x6f, 0xb6, 0x68, + 0xa3, 0xa3, 0x7b, 0x6b, 0x1a, 0xa2, 0xa4, 0x39, 0x03, 0x90, 0x63, 0xca, 0x9c, 0x21, 0x2d, 0xf6, + 0xc3, 0x4f, 0xe5, 0xae, 0x14, 0x53, 0x17, 0xfb, 0xef, 0x14, 0xb3, 0xe6, 0x95, 0x7b, 0x8e, 0xe9, + 0xd1, 0x63, 0x0d, 0x3b, 0xd6, 0xb0, 0x7d, 0x6a, 0xd8, 0x3f, 0xc9, 0xc3, 0x68, 0xb0, 0x69, 0x7a, + 0x8f, 0x36, 0x0f, 0x67, 0xad, 0x0a, 0xb7, 0x32, 0x85, 0x7d, 0x6f, 0x65, 0xf6, 0xa3, 0x50, 0x6a, + 0x70, 0xc4, 0xca, 0x4d, 0x03, 0x94, 0x18, 0x3f, 0x62, 0x0d, 0x0e, 0x56, 0x9f, 0x86, 0x81, 0x79, + 0xfd, 0x91, 0xd9, 0xee, 0xb6, 0x85, 0x95, 0x8e, 0xee, 0x6b, 0x6d, 0xfd, 0x91, 0xe6, 0xc3, 0xd5, + 0xff, 0x2e, 0x07, 0x63, 0x42, 0xa8, 0x82, 0xf9, 0xbe, 0xa4, 0x1a, 0x4a, 0x27, 0xbf, 0x6f, 0xe9, + 0x14, 0xf6, 0x2e, 0x1d, 0xf5, 0xaf, 0x14, 0x40, 0x99, 0x31, 0x5b, 0x74, 0xd9, 0xd1, 0x2d, 0xf7, + 0x3e, 0x75, 0xc4, 0x76, 0x7a, 0x9a, 0xb1, 0xda, 0xd7, 0x07, 0x4a, 0x53, 0x4a, 0x7e, 0x4f, 0x53, + 0xca, 0xc7, 0x60, 0x48, 0x34, 0x26, 0x70, 0x9d, 0xc4, 0x51, 0xe3, 0xf8, 0x40, 0x2d, 0xc4, 0x33, + 0xe2, 0x4a, 0xa7, 0xe3, 0xd8, 0x0f, 0xa9, 0xc3, 0x6f, 0xc5, 0x04, 0xb1, 0xee, 0x03, 0xb5, 0x10, + 0x2f, 0x71, 0xa6, 0xbe, 0xbd, 0x28, 0x73, 0xa6, 0x8e, 0x16, 0xe2, 0xc9, 0x15, 0x18, 0x9c, 0xb3, + 0x9b, 0x3a, 0x0a, 0x9a, 0x4f, 0x2b, 0x23, 0x5b, 0x9b, 0xe5, 0xc1, 0x96, 0x80, 0x69, 0x01, 0x96, + 0x51, 0x56, 0xed, 0x75, 0xab, 0x65, 0xeb, 0xdc, 0xd9, 0x66, 0x90, 0x53, 0x1a, 0x02, 0xa6, 0x05, + 0x58, 0x46, 0xc9, 0x64, 0x8e, 0x4e, 0x4c, 0x83, 0x21, 0xcf, 0xfb, 0x02, 0xa6, 0x05, 0x58, 0xf5, + 0xeb, 0x45, 0xa6, 0xbd, 0xae, 0xf9, 0xfe, 0x63, 0xbf, 0x2e, 0x84, 0x03, 0xa6, 0x7f, 0x0f, 0x03, + 0xe6, 0xb1, 0x39, 0xb0, 0x53, 0xff, 0xd5, 0x00, 0x80, 0x90, 0xfe, 0xf4, 0xf1, 0xe6, 0x70, 0x7f, + 0x5a, 0x53, 0x85, 0x93, 0xd3, 0xd6, 0x9a, 0x6e, 0x35, 0xa9, 0x11, 0x1e, 0x5b, 0x96, 0x70, 0x68, + 0xa3, 0xd3, 0x25, 0x15, 0xc8, 0xf0, 0xdc, 0x52, 0x4b, 0x16, 0x20, 0x2f, 0xc2, 0x70, 0xcd, 0xf2, + 0xa8, 0xa3, 0x37, 0x3d, 0xf3, 0x21, 0x15, 0x53, 0x03, 0xde, 0x44, 0x9b, 0x21, 0x58, 0x93, 0x69, + 0xc8, 0x0d, 0x18, 0x59, 0xd2, 0x1d, 0xcf, 0x6c, 0x9a, 0x1d, 0xdd, 0xf2, 0x5c, 0x65, 0x10, 0x67, + 0x34, 0xb4, 0x30, 0x3a, 0x12, 0x5c, 0x8b, 0x50, 0x91, 0xcf, 0xc1, 0x10, 0x6e, 0x4d, 0xd1, 0x3f, + 0x7c, 0x68, 0xdb, 0x8b, 0xca, 0x67, 0x42, 0x77, 0x44, 0x7e, 0xfa, 0x8a, 0x37, 0xce, 0xf1, 0xbb, + 0xca, 0x80, 0x23, 0xf9, 0x34, 0x0c, 0x4c, 0x5b, 0x06, 0x32, 0x87, 0x6d, 0x99, 0xab, 0x82, 0xf9, + 0xd9, 0x90, 0xb9, 0xdd, 0x89, 0xf1, 0xf6, 0xd9, 0xa5, 0x8f, 0xb2, 0xe1, 0x0f, 0x6e, 0x94, 0x8d, + 0x7c, 0x00, 0xc7, 0xe2, 0xa3, 0x07, 0x75, 0x2c, 0x3e, 0xb6, 0xc7, 0x63, 0x71, 0xf5, 0x7d, 0x18, + 0x9e, 0x5c, 0x9a, 0x09, 0x46, 0xef, 0x79, 0x28, 0x2c, 0x09, 0xcf, 0x88, 0x22, 0xb7, 0x67, 0x3a, + 0xa6, 0xa1, 0x31, 0x18, 0xb9, 0x0a, 0x83, 0x53, 0xe8, 0x6e, 0x27, 0x6e, 0x11, 0x8b, 0x7c, 0xfd, + 0x6b, 0x22, 0x0c, 0xbd, 0x6e, 0x7d, 0x34, 0x79, 0x16, 0x06, 0x96, 0x1c, 0x7b, 0xd5, 0xd1, 0xdb, + 0x62, 0x0d, 0x46, 0xd7, 0x94, 0x0e, 0x07, 0x69, 0x3e, 0x4e, 0xfd, 0xf1, 0x9c, 0x6f, 0xb6, 0xb3, + 0x12, 0xf5, 0x2e, 0x1e, 0xcd, 0x63, 0xdd, 0x83, 0xbc, 0x84, 0xcb, 0x41, 0x9a, 0x8f, 0x23, 0x57, + 0xa1, 0x7f, 0xda, 0x71, 0x6c, 0x47, 0xf6, 0xa9, 0xa7, 0x0c, 0x20, 0x5f, 0xf7, 0x22, 0x05, 0x79, + 0x15, 0x86, 0xf9, 0x9c, 0xc3, 0x4f, 0x34, 0x0b, 0xbd, 0x6e, 0x4a, 0x65, 0x4a, 0xf5, 0x5b, 0x05, + 0xc9, 0x66, 0xe3, 0x12, 0x7f, 0x0c, 0x6f, 0x05, 0x5e, 0x82, 0xc2, 0xe4, 0xd2, 0x8c, 0x98, 0x00, + 0x4f, 0xf9, 0x45, 0x25, 0x55, 0x89, 0x95, 0x63, 0xd4, 0xe4, 0x22, 0x14, 0x97, 0x98, 0xfa, 0x94, + 0x50, 0x3d, 0x06, 0xb7, 0x36, 0xcb, 0xc5, 0x0e, 0xd3, 0x1f, 0x84, 0x22, 0x96, 0x6d, 0x66, 0xf8, + 0x8e, 0x89, 0x63, 0xc3, 0x7d, 0xcc, 0x45, 0x28, 0x56, 0x9c, 0xd5, 0x87, 0x62, 0xd6, 0x42, 0xac, + 0xee, 0xac, 0x3e, 0xd4, 0x10, 0x4a, 0xae, 0x01, 0x68, 0xd4, 0xeb, 0x3a, 0x16, 0x3e, 0x77, 0x19, + 0xc2, 0xf3, 0x37, 0x9c, 0x0d, 0x1d, 0x84, 0x36, 0x9a, 0xb6, 0x41, 0x35, 0x89, 0x44, 0xfd, 0x1b, + 0xe1, 0xc5, 0x4e, 0xd5, 0x74, 0x1f, 0x1c, 0x77, 0xe1, 0x2e, 0xba, 0x50, 0x17, 0x47, 0x9c, 0xc9, + 0x4e, 0x2a, 0x43, 0xff, 0x4c, 0x4b, 0x5f, 0x75, 0xb1, 0x0f, 0x85, 0xef, 0xda, 0x7d, 0x06, 0xd0, + 0x38, 0x3c, 0xd6, 0x4f, 0x83, 0xdb, 0xf7, 0xd3, 0x57, 0xfb, 0x83, 0xd1, 0xb6, 0x40, 0xbd, 0x75, + 0xdb, 0x39, 0xee, 0xaa, 0x9d, 0x76, 0xd5, 0x65, 0x18, 0xa8, 0x3b, 0x4d, 0xe9, 0xe8, 0x02, 0xf7, + 0x03, 0xae, 0xd3, 0xe4, 0xc7, 0x16, 0x3e, 0x92, 0xd1, 0x55, 0x5d, 0x0f, 0xe9, 0x06, 0x42, 0x3a, + 0xc3, 0xf5, 0x04, 0x9d, 0x40, 0x0a, 0xba, 0x25, 0xdb, 0xf1, 0x44, 0xc7, 0x05, 0x74, 0x1d, 0xdb, + 0xf1, 0x34, 0x1f, 0x49, 0x3e, 0x06, 0xb0, 0x3c, 0xb5, 0xe4, 0x3b, 0xf7, 0x0f, 0x85, 0xbe, 0x87, + 0xc2, 0xab, 0x5f, 0x93, 0xd0, 0x64, 0x19, 0x86, 0x16, 0x3b, 0xd4, 0xe1, 0x5b, 0x21, 0xfe, 0x80, + 0xe5, 0xa3, 0x31, 0xd1, 0x8a, 0x7e, 0x9f, 0x10, 0xff, 0x07, 0xe4, 0x7c, 0x7d, 0xb1, 0xfd, 0x9f, + 0x5a, 0xc8, 0x88, 0xbc, 0x0a, 0xa5, 0x0a, 0xb7, 0xf3, 0x86, 0x91, 0x65, 0x20, 0x32, 0xdc, 0x82, + 0x72, 0x14, 0xdf, 0xb3, 0xeb, 0xf8, 0xb7, 0x26, 0xc8, 0xd5, 0xab, 0x30, 0x1e, 0xaf, 0x86, 0x0c, + 0xc3, 0xc0, 0xd4, 0xe2, 0xc2, 0xc2, 0xf4, 0xd4, 0xf2, 0x78, 0x1f, 0x19, 0x84, 0x62, 0x7d, 0x7a, + 0xa1, 0x3a, 0x9e, 0x53, 0x7f, 0x5e, 0x9a, 0x41, 0x98, 0x6a, 0x1d, 0x5f, 0x0d, 0xef, 0xeb, 0xbe, + 0x65, 0x1c, 0xef, 0x43, 0xf1, 0xc4, 0xa0, 0x6d, 0x7a, 0x1e, 0x35, 0xc4, 0x2a, 0x81, 0xf7, 0x85, + 0xde, 0x23, 0x2d, 0x81, 0x27, 0xcf, 0xc3, 0x28, 0xc2, 0xc4, 0x15, 0x21, 0xdf, 0x1f, 0x8b, 0x02, + 0xce, 0x23, 0x2d, 0x8a, 0x54, 0x7f, 0x37, 0xbc, 0x1d, 0x9e, 0xa3, 0xfa, 0x51, 0xbd, 0x51, 0xfc, + 0x90, 0xf4, 0x97, 0xfa, 0x6f, 0x8a, 0xfc, 0xc9, 0x09, 0x7f, 0x9f, 0x78, 0x18, 0xa2, 0x0c, 0x8f, + 0x74, 0x0b, 0xbb, 0x38, 0xd2, 0x7d, 0x1e, 0x4a, 0xf3, 0xd4, 0x5b, 0xb3, 0x7d, 0xc7, 0x2f, 0xf4, + 0xd0, 0x6b, 0x23, 0x44, 0xf6, 0xd0, 0xe3, 0x34, 0xe4, 0x01, 0x10, 0xff, 0xf1, 0x61, 0xe0, 0xf8, + 0xed, 0x1f, 0x21, 0x9f, 0x4b, 0xec, 0x53, 0xea, 0xf8, 0x44, 0x19, 0x7d, 0xfa, 0x4f, 0x07, 0x8e, + 0xe5, 0x92, 0x27, 0xd6, 0xbf, 0xde, 0x2c, 0x97, 0x38, 0x8d, 0x96, 0xc2, 0x96, 0xbc, 0x03, 0x43, + 0xf3, 0x33, 0x15, 0xf1, 0x10, 0x91, 0x7b, 0x45, 0x9c, 0x0f, 0xa4, 0xe8, 0x23, 0x02, 0x91, 0xe0, + 0xfb, 0x9e, 0xf6, 0x7d, 0x3d, 0xf9, 0x0e, 0x31, 0xe4, 0xc2, 0xb4, 0x85, 0xbf, 0x14, 0x12, 0xa7, + 0x0b, 0x81, 0xb6, 0x44, 0xdf, 0x0f, 0xc5, 0x65, 0xc5, 0xb1, 0x31, 0x6d, 0x19, 0xdc, 0xc7, 0xe8, + 0x5e, 0x84, 0x93, 0x95, 0x4e, 0xa7, 0x65, 0x52, 0x03, 0xf5, 0x45, 0xeb, 0xb6, 0xa8, 0x2b, 0x5c, + 0x7e, 0xf0, 0xf1, 0x89, 0xce, 0x91, 0x0d, 0x7c, 0xfe, 0xda, 0x70, 0xba, 0x51, 0xff, 0xcc, 0x64, + 0x59, 0xf5, 0x27, 0xf3, 0x70, 0x76, 0xca, 0xa1, 0xba, 0x47, 0xe7, 0x67, 0x2a, 0x95, 0x2e, 0xfa, + 0xc8, 0xb5, 0x5a, 0xd4, 0x5a, 0x3d, 0x9c, 0x61, 0xfd, 0x06, 0x8c, 0x05, 0x0d, 0xa8, 0x37, 0xed, + 0x0e, 0x95, 0x1f, 0x72, 0x35, 0x7d, 0x4c, 0xc3, 0x65, 0x28, 0x2d, 0x46, 0x4a, 0x6e, 0xc3, 0xa9, + 0x00, 0x52, 0x69, 0xb5, 0xec, 0x75, 0x8d, 0x76, 0x5d, 0xee, 0x88, 0x3b, 0xc8, 0x1d, 0x71, 0x43, + 0x0e, 0x3a, 0xc3, 0x37, 0x1c, 0x46, 0xa0, 0xa5, 0x95, 0x52, 0xbf, 0x56, 0x80, 0x73, 0x77, 0xf5, + 0x96, 0x69, 0x84, 0xa2, 0xd1, 0xa8, 0xdb, 0xb1, 0x2d, 0x97, 0x1e, 0xa1, 0x51, 0x1a, 0x19, 0x0a, + 0xc5, 0x03, 0x19, 0x0a, 0xc9, 0x2e, 0xea, 0xdf, 0x77, 0x17, 0x95, 0xf6, 0xd4, 0x45, 0xff, 0x7b, + 0x0e, 0xc6, 0xfd, 0x87, 0x06, 0xf2, 0xa3, 0x71, 0xc9, 0x0b, 0x1e, 0x8f, 0x10, 0x63, 0x7e, 0xd7, + 0x88, 0x27, 0x75, 0x18, 0x98, 0x7e, 0xd4, 0x31, 0x1d, 0xea, 0xee, 0xc0, 0x69, 0xfc, 0x92, 0x38, + 0x2e, 0x39, 0x49, 0x79, 0x91, 0xc4, 0x49, 0x09, 0x07, 0xe3, 0xf3, 0x41, 0xfe, 0xd4, 0x62, 0xd2, + 0x7f, 0x09, 0xcf, 0x9f, 0x0f, 0x8a, 0x27, 0x19, 0x91, 0xf7, 0xa0, 0x21, 0x29, 0x79, 0x06, 0x0a, + 0xcb, 0xcb, 0x73, 0x62, 0x26, 0xc5, 0x08, 0x04, 0x9e, 0x27, 0xbf, 0x8f, 0x64, 0x58, 0xf5, 0x9f, + 0xe7, 0x01, 0x98, 0x2a, 0xf0, 0xe1, 0x7a, 0x28, 0x4a, 0x38, 0x09, 0x83, 0xbe, 0xc0, 0x85, 0x1a, + 0x06, 0xaf, 0x04, 0xe2, 0x1d, 0x11, 0xaf, 0x3b, 0x78, 0x11, 0x52, 0xf6, 0x1d, 0xc9, 0xf9, 0x3d, + 0x00, 0xee, 0x6c, 0xd0, 0x91, 0xdc, 0x77, 0x1f, 0xff, 0x18, 0x0c, 0x89, 0x19, 0xcf, 0x8e, 0x9c, + 0xff, 0x37, 0x7d, 0xa0, 0x16, 0xe2, 0x63, 0x53, 0x6b, 0x69, 0x1f, 0x0b, 0xb1, 0x2f, 0x5e, 0xde, + 0x2b, 0xc7, 0xe2, 0x3d, 0x60, 0xf1, 0x7e, 0x49, 0x88, 0x97, 0xbf, 0x18, 0x3a, 0xb2, 0xe2, 0x3d, + 0xb0, 0xb3, 0x6f, 0xf5, 0x9f, 0xe4, 0x80, 0xb0, 0x66, 0x2d, 0xe9, 0xae, 0xbb, 0x6e, 0x3b, 0x06, + 0x77, 0x4e, 0x3f, 0x14, 0xc1, 0x1c, 0xdc, 0x7d, 0xe5, 0xb7, 0x06, 0xe1, 0x54, 0xc4, 0xf1, 0xf7, + 0x88, 0x4f, 0x56, 0x57, 0xa3, 0xa3, 0xa9, 0xd7, 0xab, 0x97, 0x8f, 0xc8, 0x17, 0xa2, 0xfd, 0x91, + 0x07, 0x6f, 0xd2, 0x4d, 0xe8, 0x0b, 0x30, 0x22, 0x7e, 0xb0, 0x15, 0xda, 0xbf, 0xe9, 0xc2, 0x51, + 0xea, 0x32, 0x80, 0x16, 0x41, 0x93, 0x97, 0x61, 0x88, 0x0d, 0x98, 0x55, 0x0c, 0x56, 0x32, 0x10, + 0xbe, 0x28, 0x31, 0x7c, 0xa0, 0xbc, 0x9e, 0x04, 0x94, 0xd2, 0xbb, 0xa5, 0xc1, 0x1d, 0xbc, 0x5b, + 0xfa, 0x3c, 0x0c, 0x57, 0x2c, 0xcb, 0xf6, 0x70, 0x93, 0xee, 0x8a, 0xab, 0x89, 0x4c, 0xab, 0xfc, + 0x19, 0x7c, 0x8c, 0x1f, 0xd2, 0xa7, 0x9a, 0xe5, 0x32, 0x43, 0x72, 0xdd, 0x7f, 0x15, 0x43, 0x1d, + 0xe1, 0x55, 0x8e, 0xd7, 0x33, 0x8e, 0x80, 0x25, 0x1f, 0xc5, 0x60, 0xe7, 0x8d, 0x2e, 0x39, 0x76, + 0xc7, 0x76, 0xa9, 0xc1, 0x05, 0x35, 0x1c, 0x86, 0x36, 0xe8, 0x08, 0x04, 0xbe, 0x9b, 0x8b, 0x04, + 0x0e, 0x89, 0x14, 0x21, 0xf7, 0xe1, 0xb4, 0x7f, 0x51, 0x1c, 0xbc, 0x50, 0xac, 0x55, 0x5d, 0x65, + 0x04, 0x5f, 0x25, 0x91, 0xb8, 0x32, 0xd4, 0xaa, 0x93, 0x4f, 0xfa, 0xd7, 0x22, 0xfe, 0x13, 0xc7, + 0x86, 0x69, 0xc8, 0x5d, 0x9d, 0xca, 0x8f, 0x7c, 0x3f, 0x0c, 0xcf, 0xeb, 0x8f, 0xaa, 0x5d, 0x71, + 0xf6, 0x32, 0xba, 0xf3, 0xdb, 0x97, 0xb6, 0xfe, 0xa8, 0x61, 0x88, 0x72, 0x31, 0x9b, 0x42, 0x66, + 0x49, 0x1a, 0x70, 0x76, 0xc9, 0xb1, 0xdb, 0xb6, 0x47, 0x8d, 0xd8, 0x63, 0xbf, 0x13, 0xe1, 0xeb, + 0xe0, 0x8e, 0xa0, 0x68, 0xf4, 0x78, 0xf5, 0x97, 0xc1, 0x86, 0xb4, 0xe1, 0x44, 0xc5, 0x75, 0xbb, + 0x6d, 0x1a, 0xde, 0x50, 0x8d, 0x6f, 0xfb, 0x19, 0x1f, 0x15, 0x5e, 0xcb, 0x4f, 0xe8, 0x58, 0x94, + 0x5f, 0x50, 0x35, 0x3c, 0x53, 0xae, 0x11, 0xbf, 0x25, 0xce, 0xfb, 0x56, 0x71, 0x70, 0x6c, 0xfc, + 0x84, 0x76, 0x2e, 0xd9, 0x98, 0x65, 0xd3, 0x6b, 0x51, 0xf5, 0xaf, 0xe6, 0x63, 0xb3, 0x08, 0xb7, + 0xaf, 0xf6, 0x35, 0x8b, 0xc8, 0xb3, 0x41, 0x7e, 0x8f, 0xb3, 0xc1, 0x47, 0x92, 0x3e, 0x0f, 0x29, + 0x43, 0xfc, 0xfb, 0x61, 0xcc, 0x2f, 0x81, 0xed, 0xde, 0x08, 0x96, 0x89, 0x6c, 0x51, 0x5e, 0x14, + 0xa2, 0x1c, 0x47, 0x03, 0x73, 0x23, 0x26, 0xbf, 0x18, 0x3f, 0xf5, 0x9b, 0x39, 0x80, 0x50, 0x01, + 0xc9, 0x0b, 0xd1, 0x88, 0x51, 0xb9, 0xf0, 0x22, 0x48, 0x44, 0x93, 0x88, 0x84, 0x88, 0x22, 0x17, + 0xa1, 0x88, 0x11, 0x47, 0xf2, 0xe1, 0xc1, 0xf3, 0x03, 0xd3, 0x32, 0x34, 0x84, 0x32, 0xac, 0x14, + 0x1a, 0x00, 0xb1, 0xe8, 0xf4, 0xc0, 0xad, 0xe6, 0x2a, 0x9c, 0xa8, 0x77, 0x57, 0xfc, 0xba, 0xa5, + 0x77, 0x8e, 0x18, 0xf8, 0xc4, 0xed, 0xae, 0x04, 0x8f, 0x83, 0x23, 0x61, 0x65, 0xa2, 0x45, 0xd4, + 0xaf, 0xe7, 0x62, 0xfd, 0x7b, 0x88, 0x46, 0xc1, 0x8e, 0xfa, 0x54, 0xfd, 0x83, 0x02, 0x0c, 0x2f, + 0xd9, 0x8e, 0x27, 0x42, 0xb8, 0x1c, 0xed, 0x55, 0x5a, 0xda, 0x4b, 0x16, 0x77, 0xb1, 0x97, 0xbc, + 0x08, 0x45, 0xc9, 0x85, 0x9b, 0xdf, 0x1b, 0x19, 0x86, 0xa3, 0x21, 0xf4, 0x03, 0x7e, 0x92, 0x92, + 0xbc, 0x24, 0x1e, 0xd8, 0xb7, 0x2b, 0xc6, 0x0f, 0xe6, 0x01, 0x3e, 0xfd, 0xe2, 0x8b, 0x8f, 0x71, + 0x97, 0xaa, 0x7f, 0x39, 0x07, 0x27, 0xc4, 0xd5, 0xab, 0x14, 0x2d, 0x6e, 0xc0, 0xbf, 0x34, 0x97, + 0x67, 0x12, 0x0e, 0xd2, 0x7c, 0x1c, 0x5b, 0xd4, 0xa7, 0x1f, 0x99, 0x1e, 0xde, 0x3e, 0x49, 0xe1, + 0xe2, 0xa8, 0x80, 0xc9, 0x8b, 0xba, 0x4f, 0x47, 0x5e, 0xf0, 0x2f, 0x95, 0x0b, 0xa1, 0x25, 0xc3, + 0x0a, 0x4c, 0xa7, 0x5e, 0x2c, 0xab, 0xbf, 0x5c, 0x84, 0xe2, 0xf4, 0x23, 0xda, 0x3c, 0xe2, 0x5d, + 0x23, 0x1d, 0x55, 0x17, 0xf7, 0x79, 0x54, 0xbd, 0x17, 0x2f, 0x99, 0xb7, 0xc3, 0xfe, 0x2c, 0x45, + 0xab, 0x8f, 0xf5, 0x7c, 0xbc, 0x7a, 0xbf, 0xa7, 0x8f, 0x9e, 0x93, 0xd5, 0x3f, 0x28, 0x40, 0xa1, + 0x3e, 0xb5, 0x74, 0xac, 0x37, 0x87, 0xaa, 0x37, 0xbd, 0xbd, 0x10, 0xd4, 0xe0, 0x62, 0x71, 0x30, + 0xf4, 0xfb, 0x8d, 0xdd, 0x21, 0xfe, 0x71, 0x01, 0xc6, 0xea, 0x33, 0xcb, 0x4b, 0xd2, 0xd9, 0xfe, + 0x6d, 0xee, 0x9b, 0x89, 0x5e, 0x82, 0xbc, 0x4b, 0x2f, 0x26, 0xcc, 0xaa, 0x3b, 0x35, 0xcb, 0x7b, + 0xe5, 0xc6, 0x5d, 0xbd, 0xd5, 0xa5, 0x78, 0x98, 0xc6, 0x3d, 0xb9, 0x5d, 0xf3, 0x7d, 0xfa, 0x35, + 0x0c, 0x1d, 0xe1, 0x33, 0x20, 0x6f, 0x40, 0xe1, 0x8e, 0xf0, 0xb1, 0xc9, 0xe2, 0xf3, 0xd2, 0x75, + 0xce, 0x87, 0x4d, 0x82, 0x85, 0xae, 0x69, 0x20, 0x07, 0x56, 0x8a, 0x15, 0xbe, 0x29, 0x4c, 0x86, + 0x1d, 0x15, 0x5e, 0xf5, 0x0b, 0xdf, 0xac, 0x55, 0x49, 0x1d, 0x86, 0x97, 0xa8, 0xd3, 0x36, 0xb1, + 0xa3, 0xfc, 0x39, 0xbb, 0x37, 0x13, 0xb6, 0xf7, 0x1c, 0xee, 0x84, 0x85, 0x90, 0x99, 0xcc, 0x85, + 0xbc, 0x0b, 0xc0, 0xad, 0xaa, 0x1d, 0x46, 0x20, 0xbd, 0x84, 0x3b, 0x39, 0xbe, 0x59, 0x48, 0xb1, + 0xda, 0x25, 0x66, 0xe4, 0x01, 0x8c, 0xcf, 0xdb, 0x86, 0x79, 0xdf, 0xe4, 0xce, 0xb4, 0x58, 0x41, + 0x69, 0x7b, 0x17, 0x36, 0xb6, 0x39, 0x68, 0x4b, 0xe5, 0xd2, 0xaa, 0x49, 0x30, 0x56, 0xff, 0x6e, + 0x3f, 0x14, 0x59, 0xb7, 0x1f, 0x8f, 0xdf, 0xfd, 0x8c, 0xdf, 0x0a, 0x8c, 0xdf, 0xb3, 0x9d, 0x07, + 0xa6, 0xb5, 0x1a, 0xbc, 0x73, 0x10, 0xa7, 0x0d, 0xe8, 0x9b, 0xb5, 0xce, 0x71, 0x8d, 0xe0, 0x49, + 0x84, 0x96, 0x20, 0xdf, 0x66, 0x04, 0xbf, 0x06, 0xc0, 0xa3, 0x17, 0x20, 0xcd, 0x60, 0x18, 0xee, + 0x84, 0xc7, 0x36, 0xc0, 0xa7, 0x13, 0x72, 0xb8, 0x93, 0x90, 0x98, 0x5c, 0xf5, 0xbd, 0x5b, 0x86, + 0xf0, 0x25, 0x05, 0x1e, 0xab, 0xa0, 0x77, 0x8b, 0x6c, 0x04, 0x70, 0x3f, 0x97, 0x25, 0x00, 0xe9, + 0xc6, 0x10, 0x62, 0x82, 0x88, 0x4c, 0x0e, 0x22, 0xc0, 0x60, 0xca, 0x85, 0xa1, 0x26, 0xf1, 0x20, + 0xaf, 0xc4, 0x5c, 0x1a, 0x48, 0x84, 0x5b, 0xa6, 0x47, 0x43, 0xe8, 0x12, 0x37, 0xb2, 0x9d, 0x4b, + 0x9c, 0xfa, 0xb7, 0x0a, 0x30, 0xcc, 0xb8, 0xd5, 0xbb, 0xed, 0xb6, 0xee, 0x6c, 0x1c, 0x2b, 0xf2, + 0x7e, 0x14, 0xb9, 0x01, 0x27, 0xe5, 0x27, 0x10, 0xcc, 0x74, 0xf5, 0x83, 0x55, 0x05, 0x5b, 0xf8, + 0x38, 0x01, 0xb7, 0x2d, 0x71, 0xde, 0xf7, 0x04, 0x18, 0x4f, 0x8b, 0x5c, 0x2d, 0xc9, 0x4b, 0xfd, + 0x89, 0x1c, 0x8c, 0xc7, 0xa1, 0x81, 0xee, 0xe7, 0x52, 0x75, 0xff, 0x79, 0x18, 0x12, 0x4e, 0x11, + 0xba, 0x21, 0x7c, 0x34, 0xc7, 0xb6, 0x36, 0xcb, 0x80, 0x2f, 0xd2, 0x1b, 0x0e, 0xd5, 0x0d, 0x2d, + 0x24, 0x20, 0x2f, 0xc3, 0x08, 0xfe, 0xb8, 0xe7, 0x98, 0x9e, 0x47, 0x79, 0x67, 0x14, 0xf9, 0x3d, + 0x0f, 0x2f, 0xb0, 0xce, 0x11, 0x5a, 0x84, 0x4c, 0xfd, 0x9d, 0x3c, 0x0c, 0xd5, 0xbb, 0x2b, 0xee, + 0x86, 0xeb, 0xd1, 0xf6, 0x11, 0xd7, 0x21, 0xff, 0x58, 0xa1, 0x98, 0x7a, 0xac, 0xf0, 0x8c, 0x3f, + 0xb4, 0xa4, 0xfb, 0x88, 0x60, 0x63, 0xe0, 0xfb, 0x99, 0x86, 0x5a, 0x54, 0xda, 0xbd, 0x16, 0xa9, + 0x7f, 0x27, 0x0f, 0xe3, 0xfc, 0x3a, 0xbe, 0x6a, 0xba, 0xcd, 0x03, 0x78, 0x22, 0x74, 0xf8, 0x32, + 0xdd, 0x9f, 0x0b, 0xcb, 0x0e, 0x1e, 0x5e, 0xa9, 0x5f, 0xc8, 0xc3, 0x70, 0xa5, 0xeb, 0xad, 0x55, + 0x3c, 0x9c, 0xdf, 0x1e, 0xcb, 0x3d, 0xf2, 0x6f, 0xe7, 0xe0, 0x04, 0x6b, 0xc8, 0xb2, 0xfd, 0x80, + 0x5a, 0x07, 0x70, 0x9d, 0x71, 0x10, 0x07, 0x91, 0xbe, 0x2c, 0x0b, 0xbb, 0x93, 0x25, 0x5e, 0xc2, + 0x69, 0x76, 0x8b, 0x1e, 0xed, 0xcf, 0x38, 0xc0, 0x4b, 0x38, 0x5f, 0x20, 0x07, 0x70, 0xe9, 0xfb, + 0xbd, 0x25, 0x90, 0x03, 0x38, 0x91, 0xfd, 0xde, 0x10, 0xc8, 0xb7, 0x72, 0x30, 0x34, 0x69, 0x7b, + 0x47, 0x7c, 0xe0, 0x8b, 0xaf, 0x38, 0xda, 0x6a, 0xee, 0x7f, 0xc5, 0xd1, 0xd6, 0x4d, 0xf5, 0xa7, + 0xf2, 0x70, 0x5a, 0x64, 0x38, 0x10, 0x67, 0x60, 0xc7, 0xd3, 0xb1, 0x18, 0x6c, 0x49, 0xd1, 0x1c, + 0xcf, 0x43, 0x42, 0x34, 0x3f, 0x5b, 0x80, 0xd3, 0x18, 0x90, 0x99, 0xed, 0xa8, 0xbe, 0x07, 0x6c, + 0x11, 0xd2, 0x8c, 0xba, 0x56, 0xcc, 0xa7, 0xb8, 0x56, 0xfc, 0xeb, 0xcd, 0xf2, 0x2b, 0xab, 0xa6, + 0xb7, 0xd6, 0x5d, 0x99, 0x68, 0xda, 0xed, 0x6b, 0xab, 0x8e, 0xfe, 0xd0, 0xe4, 0x4e, 0x05, 0x7a, + 0xeb, 0x5a, 0x98, 0x78, 0xa8, 0x63, 0x8a, 0x34, 0x42, 0x75, 0xdc, 0x29, 0x31, 0xae, 0xbe, 0x53, + 0x86, 0x0b, 0x70, 0xcb, 0x36, 0x2d, 0xe1, 0xa9, 0xcc, 0x0d, 0xdd, 0xfa, 0xd6, 0x66, 0xf9, 0xcc, + 0x7b, 0xb6, 0x69, 0x35, 0xe2, 0xee, 0xca, 0xbb, 0xad, 0x2f, 0x64, 0xad, 0x49, 0xd5, 0xa8, 0xff, + 0x6d, 0x0e, 0xce, 0x47, 0xb5, 0xf8, 0x7b, 0xc1, 0x76, 0xfc, 0x4b, 0x79, 0x38, 0x73, 0x13, 0x85, + 0x13, 0xb8, 0x87, 0x1d, 0xcf, 0x5b, 0x62, 0x70, 0xa6, 0xc8, 0xe6, 0xd8, 0xa2, 0xcc, 0x96, 0xcd, + 0xf1, 0xa4, 0x2e, 0x64, 0xf3, 0x8f, 0x73, 0x70, 0x6a, 0xb1, 0x56, 0x9d, 0xfa, 0x1e, 0x19, 0x51, + 0xc9, 0xef, 0x39, 0xe2, 0x06, 0x67, 0xe2, 0x7b, 0x8e, 0xb8, 0xe9, 0xf9, 0x95, 0x3c, 0x9c, 0xaa, + 0x57, 0xe6, 0xe7, 0xbe, 0x57, 0x66, 0xf0, 0x29, 0xd9, 0x97, 0xd9, 0x3f, 0x04, 0x13, 0xb6, 0x80, + 0xfc, 0x99, 0x77, 0xaf, 0x67, 0xfb, 0x38, 0x27, 0x85, 0x72, 0xc4, 0xa7, 0xee, 0x03, 0x11, 0x0a, + 0xd3, 0xfc, 0x08, 0xf5, 0x11, 0xd7, 0xfc, 0xbf, 0x5f, 0x82, 0xe1, 0xdb, 0xdd, 0x15, 0x2a, 0x5c, + 0xba, 0x1e, 0xeb, 0x93, 0xdf, 0xeb, 0x30, 0x2c, 0xc4, 0x80, 0x37, 0x1c, 0x52, 0x48, 0x4e, 0x11, + 0x62, 0x89, 0x47, 0x3d, 0x93, 0x89, 0xc8, 0x45, 0x28, 0xde, 0xa5, 0xce, 0x8a, 0xfc, 0x5a, 0xfd, + 0x21, 0x75, 0x56, 0x34, 0x84, 0x92, 0xb9, 0xf0, 0x21, 0x4e, 0x65, 0xa9, 0x86, 0xe9, 0xa0, 0xc4, + 0xa5, 0x21, 0xe6, 0xb7, 0x0a, 0xbc, 0x69, 0xf5, 0x8e, 0xc9, 0x13, 0x49, 0xc9, 0x91, 0x32, 0xe2, + 0x25, 0xc9, 0x02, 0x9c, 0x94, 0xdd, 0x05, 0x79, 0x2e, 0xa4, 0xc1, 0x14, 0x76, 0x69, 0x59, 0x90, + 0x92, 0x45, 0xc9, 0xdb, 0x30, 0xe2, 0x03, 0xd1, 0xf1, 0x71, 0x28, 0x4c, 0xc0, 0x11, 0xb0, 0x8a, + 0xe5, 0x4b, 0x88, 0x14, 0x90, 0x19, 0xe0, 0x25, 0x06, 0xa4, 0x30, 0x88, 0x39, 0xda, 0x46, 0x0a, + 0x90, 0x97, 0x91, 0x01, 0x3e, 0x1e, 0x43, 0x87, 0xa9, 0x61, 0x7c, 0xca, 0x8d, 0x17, 0x40, 0x8e, + 0x80, 0xf3, 0x07, 0xfb, 0x11, 0x32, 0xb2, 0x08, 0x10, 0x3a, 0xb6, 0x88, 0xb0, 0x28, 0xbb, 0x76, + 0xb9, 0x91, 0x58, 0xc8, 0x37, 0x79, 0xa3, 0x7b, 0xb9, 0xc9, 0x53, 0x7f, 0xb2, 0x00, 0xc3, 0x95, + 0x4e, 0x27, 0x18, 0x0a, 0x2f, 0x40, 0xa9, 0xd2, 0xe9, 0xdc, 0xd1, 0x6a, 0x72, 0x82, 0x04, 0xbd, + 0xd3, 0x69, 0x74, 0x1d, 0x53, 0xf6, 0x34, 0xe7, 0x44, 0x64, 0x0a, 0x46, 0x2b, 0x9d, 0xce, 0x52, + 0x77, 0xa5, 0x65, 0x36, 0xa5, 0xfc, 0x6e, 0x3c, 0x03, 0x66, 0xa7, 0xd3, 0xe8, 0x20, 0x26, 0x9e, + 0xe4, 0x2f, 0x5a, 0x86, 0x7c, 0x1e, 0x83, 0x89, 0x89, 0xf4, 0x62, 0x3c, 0x81, 0x91, 0x1a, 0xa4, + 0x46, 0x08, 0xdb, 0x36, 0x11, 0x10, 0xf1, 0x14, 0x12, 0x17, 0xfd, 0xc4, 0x1f, 0xac, 0xa2, 0x44, + 0x1a, 0xb1, 0x90, 0x25, 0xf9, 0x38, 0x0c, 0x54, 0x3a, 0x1d, 0xe9, 0xb6, 0x0a, 0x1d, 0xdb, 0x58, + 0xa9, 0x78, 0x06, 0x47, 0x41, 0x26, 0x3e, 0x4b, 0xdc, 0x6f, 0xdb, 0x8e, 0x87, 0x43, 0x6a, 0x34, + 0xfc, 0x2c, 0xff, 0x42, 0xdc, 0x96, 0xe3, 0xf7, 0x68, 0xd1, 0x32, 0x17, 0xde, 0x84, 0xb1, 0x68, + 0x8b, 0x77, 0x95, 0xc7, 0xe2, 0xbb, 0x39, 0x94, 0xca, 0x11, 0x7f, 0x6e, 0xf1, 0x12, 0x14, 0x2a, + 0x9d, 0x8e, 0x98, 0xd4, 0x4e, 0xa5, 0x74, 0x6a, 0x3c, 0x3a, 0x43, 0xa5, 0xd3, 0xf1, 0x3f, 0xfd, + 0x88, 0xbf, 0xdb, 0xda, 0xd3, 0xa7, 0x7f, 0x8b, 0x7f, 0xfa, 0xd1, 0x7e, 0x53, 0xa5, 0xfe, 0x72, + 0x01, 0x4e, 0x54, 0x3a, 0x9d, 0xe3, 0xfc, 0x17, 0x07, 0x15, 0x03, 0xe2, 0x45, 0x00, 0x69, 0x8e, + 0x1d, 0x08, 0x5e, 0x95, 0x0e, 0x4b, 0xf3, 0xab, 0x92, 0xd3, 0x24, 0x22, 0x5f, 0xfd, 0x06, 0x77, + 0xa5, 0x7e, 0x5f, 0x28, 0xe0, 0xc4, 0x77, 0xd4, 0xe3, 0xd9, 0x7d, 0x58, 0xba, 0x4d, 0xf4, 0x41, + 0x69, 0x57, 0x7d, 0xf0, 0x5b, 0x91, 0xc1, 0x83, 0xf9, 0x14, 0x8e, 0x7b, 0xa1, 0x7f, 0x5f, 0xb6, + 0xf5, 0x98, 0x2c, 0x4c, 0x11, 0x64, 0xcb, 0xcf, 0x29, 0x27, 0x42, 0xbe, 0x35, 0x19, 0xaa, 0x61, + 0x1a, 0x5a, 0x8c, 0xd6, 0xef, 0xc3, 0x81, 0x5d, 0xf5, 0xe1, 0x66, 0x1e, 0xc3, 0x3a, 0x04, 0x21, + 0xe3, 0xf6, 0xbf, 0x45, 0xb9, 0x06, 0xc0, 0xdd, 0x17, 0x02, 0xff, 0xfc, 0x51, 0x1e, 0x1d, 0x8a, + 0xa7, 0x9a, 0x13, 0xd1, 0xa1, 0x42, 0x92, 0xc0, 0xdd, 0xa9, 0x90, 0xea, 0xee, 0x74, 0x15, 0x06, + 0x35, 0x7d, 0xfd, 0x9d, 0x2e, 0x15, 0x8f, 0x99, 0xfc, 0x88, 0xac, 0xfa, 0x7a, 0xe3, 0x07, 0x18, + 0x50, 0x0b, 0xd0, 0x44, 0x0d, 0xe2, 0x82, 0x48, 0x6e, 0x25, 0xfc, 0xa0, 0x3d, 0x88, 0x06, 0xb2, + 0x17, 0x45, 0x27, 0xaf, 0x43, 0xa1, 0x72, 0xaf, 0x2e, 0x24, 0x1b, 0x74, 0x6d, 0xe5, 0x5e, 0x5d, + 0xc8, 0x2b, 0xb3, 0xec, 0xbd, 0xba, 0xfa, 0x85, 0x3c, 0x90, 0x24, 0x25, 0x79, 0x05, 0x86, 0x10, + 0xba, 0xca, 0x74, 0x46, 0xce, 0x51, 0xbc, 0xee, 0x36, 0x1c, 0x84, 0x46, 0x2c, 0x44, 0x9f, 0x94, + 0xbc, 0x86, 0x59, 0xe0, 0x45, 0x96, 0xcc, 0x48, 0x8e, 0xe2, 0x75, 0xd7, 0xcf, 0x9b, 0x1e, 0x4b, + 0x02, 0x2f, 0x88, 0xd1, 0xb8, 0xbc, 0x57, 0x9f, 0xb5, 0x5d, 0x4f, 0x88, 0x9a, 0x1b, 0x97, 0xeb, + 0x2e, 0x26, 0xc7, 0x8e, 0x18, 0x97, 0x9c, 0x0c, 0x13, 0xfc, 0xdd, 0xab, 0xf3, 0x17, 0x74, 0x86, + 0x66, 0xb7, 0x7c, 0xab, 0x94, 0x27, 0xf8, 0x5b, 0x77, 0x1b, 0xfc, 0xf5, 0x9d, 0x81, 0xe9, 0xe7, + 0x23, 0x09, 0xfe, 0x22, 0xa5, 0xd4, 0x1f, 0x1b, 0x84, 0xf1, 0xaa, 0xee, 0xe9, 0x2b, 0xba, 0x4b, + 0xa5, 0x2d, 0xf9, 0x09, 0x1f, 0xe6, 0x7f, 0x8e, 0x24, 0x07, 0x63, 0x25, 0xe5, 0x6b, 0xe2, 0x05, + 0xc8, 0x1b, 0x21, 0xdf, 0x20, 0xfd, 0xb2, 0x9c, 0xcf, 0x71, 0xa5, 0xd1, 0x11, 0x60, 0x2d, 0x41, + 0x48, 0x9e, 0x87, 0x61, 0x1f, 0xc6, 0x76, 0x11, 0x85, 0x50, 0x67, 0x8c, 0x15, 0xb6, 0x89, 0xd0, + 0x64, 0x34, 0x79, 0x0d, 0x46, 0xfc, 0x9f, 0x92, 0x7d, 0xce, 0x93, 0x53, 0xae, 0x24, 0xb6, 0x60, + 0x32, 0xa9, 0x5c, 0x14, 0xe7, 0xb7, 0xfe, 0x48, 0xd1, 0x58, 0xfe, 0xc7, 0x08, 0x29, 0xf9, 0x01, + 0x18, 0xf3, 0x7f, 0x8b, 0x5d, 0x07, 0xf7, 0x3e, 0x7c, 0x3e, 0xc8, 0x6e, 0x1f, 0x13, 0xeb, 0x44, + 0x94, 0x9c, 0xef, 0x3f, 0x9e, 0xf0, 0x53, 0x1a, 0x1a, 0x2b, 0xc9, 0xed, 0x47, 0xac, 0x02, 0x52, + 0x83, 0x93, 0x3e, 0x24, 0xd4, 0xd0, 0x81, 0x70, 0xdb, 0x69, 0xac, 0x34, 0x52, 0x95, 0x34, 0x59, + 0x8a, 0xb4, 0xe0, 0x62, 0x04, 0x68, 0xb8, 0x6b, 0xe6, 0x7d, 0x4f, 0xec, 0x19, 0x45, 0x78, 0x74, + 0x91, 0xc3, 0x36, 0xe0, 0xca, 0x69, 0xfc, 0x64, 0xd4, 0xd1, 0xc4, 0x75, 0x3d, 0xb9, 0x91, 0x3a, + 0x9c, 0xf6, 0xf1, 0x37, 0xa7, 0x96, 0x96, 0x1c, 0xfb, 0x3d, 0xda, 0xf4, 0x6a, 0x55, 0xb1, 0xe7, + 0xc6, 0xb0, 0x99, 0xc6, 0x4a, 0x63, 0xb5, 0xd9, 0x61, 0x4a, 0xc1, 0x70, 0x51, 0xe6, 0xa9, 0x85, + 0xc9, 0x5d, 0x38, 0x23, 0xc1, 0xa5, 0x4c, 0xf9, 0x10, 0x1e, 0x0a, 0x08, 0xae, 0xe9, 0xc9, 0xf2, + 0xd3, 0x8b, 0x93, 0x37, 0x61, 0xd4, 0x47, 0xf0, 0xab, 0xc8, 0x61, 0xbc, 0x8a, 0xc4, 0x21, 0x69, + 0xac, 0x34, 0xe2, 0x0f, 0xbd, 0xa3, 0xc4, 0xb2, 0x46, 0x2d, 0x6f, 0x74, 0xa8, 0x70, 0x0b, 0xf6, + 0x35, 0xca, 0xdb, 0xe8, 0xa4, 0x2a, 0x23, 0x23, 0x25, 0x6f, 0x87, 0x1a, 0xb5, 0xe8, 0x98, 0xab, + 0x26, 0xdf, 0x8e, 0xfb, 0x6f, 0xbb, 0x57, 0x1a, 0x36, 0x02, 0xd3, 0xf4, 0x83, 0x93, 0x5f, 0xa8, + 0xc0, 0xa9, 0x14, 0x1d, 0xdb, 0xd5, 0x8e, 0xf1, 0x8b, 0xf9, 0xb0, 0x11, 0x47, 0x7c, 0xdb, 0x38, + 0x09, 0x83, 0xfe, 0x97, 0x08, 0xe3, 0x41, 0xc9, 0x1a, 0x9a, 0x71, 0x1e, 0x3e, 0x3e, 0x22, 0x8e, + 0x23, 0xbe, 0x95, 0x3c, 0x08, 0x71, 0x7c, 0x3b, 0x17, 0x8a, 0xe3, 0x88, 0x6f, 0x2f, 0x7f, 0xbb, + 0x18, 0xce, 0x49, 0xc7, 0x7b, 0xcc, 0x83, 0x32, 0x93, 0x43, 0x67, 0xda, 0xd2, 0x2e, 0xde, 0x10, + 0xcb, 0xaa, 0x39, 0xb0, 0x37, 0xd5, 0x24, 0x6f, 0xc2, 0xf0, 0x92, 0xed, 0x7a, 0xab, 0x0e, 0x75, + 0x97, 0x82, 0xf4, 0x1e, 0xf8, 0xfe, 0xbc, 0x23, 0xc0, 0x8d, 0x4e, 0x64, 0xf6, 0x97, 0xc9, 0xa5, + 0x48, 0x6e, 0x43, 0xbb, 0x8f, 0xe4, 0xa6, 0xfe, 0xd3, 0x42, 0x42, 0x97, 0xb8, 0xd9, 0x7b, 0x24, + 0x75, 0xe9, 0x00, 0x26, 0x0a, 0x72, 0x3d, 0x5c, 0x43, 0xf9, 0xfe, 0xa0, 0x5f, 0x8a, 0x7c, 0xba, + 0x22, 0xb6, 0x07, 0x51, 0x12, 0xf2, 0x59, 0x38, 0x17, 0x01, 0x2c, 0xe9, 0x8e, 0xde, 0xa6, 0x5e, + 0x98, 0x88, 0x15, 0x63, 0xd9, 0xf9, 0xa5, 0x1b, 0x9d, 0x00, 0x2d, 0x27, 0x77, 0xcd, 0xe0, 0x20, + 0x29, 0xe6, 0xc0, 0x2e, 0xbc, 0xbc, 0xbf, 0x5a, 0x08, 0xcd, 0xa4, 0x68, 0x4c, 0x6a, 0x8d, 0xba, + 0xdd, 0x96, 0xf7, 0xf8, 0x76, 0xf0, 0xde, 0x32, 0xfe, 0xcc, 0xc2, 0x89, 0xca, 0xfd, 0xfb, 0xb4, + 0xe9, 0xf9, 0xa1, 0xf6, 0x5d, 0x11, 0x85, 0x94, 0x6f, 0x5b, 0x04, 0x4a, 0x84, 0x4e, 0x97, 0xfb, + 0x35, 0x5e, 0x4c, 0xfd, 0x67, 0x45, 0x50, 0x82, 0x6d, 0x43, 0xf0, 0xda, 0xf1, 0x10, 0x97, 0xe8, + 0x0f, 0x45, 0xaf, 0x98, 0x70, 0x32, 0x14, 0x86, 0x78, 0x66, 0xa6, 0xf4, 0xe3, 0xb6, 0xa4, 0x1c, + 0x67, 0x16, 0x12, 0xf2, 0x9d, 0xc8, 0x05, 0xb1, 0x13, 0x21, 0xe1, 0x6b, 0xd2, 0x86, 0xcb, 0x59, + 0x68, 0x49, 0xae, 0xe4, 0x4b, 0x39, 0x38, 0xed, 0x77, 0xca, 0xe2, 0x0a, 0x33, 0xc9, 0xa7, 0xec, + 0xae, 0x15, 0xbc, 0xc1, 0x7a, 0x3d, 0xbb, 0x3a, 0xde, 0x49, 0x13, 0x69, 0x85, 0x79, 0x4b, 0x82, + 0x78, 0x3b, 0x81, 0x42, 0xd8, 0x48, 0xd3, 0x68, 0x22, 0x91, 0x96, 0x5a, 0xef, 0x85, 0x9b, 0x70, + 0x3e, 0x93, 0xe5, 0x76, 0x26, 0x70, 0xbf, 0x6c, 0x02, 0xff, 0xf7, 0xb9, 0x70, 0x22, 0x8a, 0x09, + 0x89, 0x4c, 0x00, 0x84, 0x20, 0xb1, 0x29, 0xc6, 0x27, 0x5e, 0xa1, 0xd0, 0x34, 0x89, 0x82, 0x2c, + 0x42, 0x49, 0x88, 0x85, 0x27, 0x3d, 0xff, 0xd8, 0x36, 0xbd, 0x30, 0x21, 0xcb, 0x01, 0x37, 0xbc, + 0xe2, 0x9b, 0x05, 0x9b, 0x0b, 0xaf, 0xc1, 0xf0, 0x5e, 0xbf, 0xeb, 0x4b, 0x05, 0x20, 0xf2, 0x0e, + 0xf6, 0x10, 0xcd, 0xfb, 0x23, 0x3c, 0x85, 0x5d, 0x81, 0x41, 0xf6, 0x09, 0x98, 0x06, 0x48, 0x0a, + 0xfb, 0xdd, 0x15, 0x30, 0x2d, 0xc0, 0x86, 0x31, 0xf7, 0x06, 0xd2, 0x63, 0xee, 0xa9, 0x3f, 0x51, + 0x80, 0xb3, 0x72, 0x87, 0x54, 0x29, 0x66, 0x12, 0x39, 0xee, 0x94, 0x0f, 0xb0, 0x53, 0x54, 0x28, + 0xf1, 0x8d, 0x8b, 0x48, 0xe9, 0xc2, 0x0f, 0x95, 0x10, 0xa2, 0x09, 0x8c, 0xfa, 0xbf, 0xe6, 0x61, + 0x34, 0x30, 0x0e, 0x75, 0xc7, 0x7d, 0x8c, 0xbb, 0xe3, 0x13, 0x30, 0x8a, 0x51, 0xd3, 0xda, 0xd4, + 0xe2, 0x91, 0xc5, 0xfa, 0xa5, 0x1c, 0x4c, 0x3e, 0x42, 0xa4, 0xdb, 0x8b, 0x10, 0x32, 0xed, 0xe7, + 0x96, 0x9f, 0x14, 0xcb, 0x8e, 0x9b, 0x7d, 0x1c, 0xae, 0xfe, 0xb5, 0x02, 0x8c, 0xf8, 0x52, 0x9e, + 0x34, 0x8f, 0xea, 0x2d, 0xd1, 0xe1, 0x0a, 0xf9, 0x1a, 0xc0, 0x92, 0xed, 0x78, 0x7a, 0x6b, 0x21, + 0xd4, 0x7c, 0x3c, 0x5e, 0xed, 0x20, 0x94, 0x97, 0x91, 0x48, 0x70, 0xfd, 0x0a, 0xcd, 0x6a, 0x3e, + 0x31, 0xf1, 0xf5, 0x2b, 0x80, 0x6a, 0x12, 0x85, 0xfa, 0xeb, 0x79, 0x38, 0xe1, 0x77, 0xd2, 0xf4, + 0x23, 0xda, 0xec, 0x3e, 0xce, 0x73, 0x53, 0x54, 0xda, 0xfd, 0xdb, 0x4a, 0x5b, 0xfd, 0xbf, 0xa5, + 0x89, 0x64, 0xaa, 0x65, 0x1f, 0x4f, 0x24, 0x7f, 0x12, 0x3a, 0xae, 0xfe, 0x50, 0x01, 0x4e, 0xfb, + 0x52, 0x9f, 0xe9, 0x5a, 0x78, 0x30, 0x31, 0xa5, 0xb7, 0x5a, 0x8f, 0xf3, 0x6e, 0x7c, 0xd8, 0x17, + 0xc4, 0xa2, 0x08, 0x43, 0x2a, 0x52, 0x9f, 0xde, 0x17, 0xe0, 0x86, 0x6d, 0x1a, 0x9a, 0x4c, 0x44, + 0xde, 0x86, 0x11, 0xff, 0x67, 0xc5, 0x59, 0xf5, 0xb7, 0xe0, 0x78, 0xcd, 0x10, 0x14, 0xd2, 0x9d, + 0x48, 0x6c, 0x8e, 0x48, 0x01, 0xf5, 0x0b, 0x03, 0x70, 0xe1, 0x9e, 0x69, 0x19, 0xf6, 0xba, 0xeb, + 0x67, 0xce, 0x3d, 0xf2, 0xc7, 0x6c, 0x87, 0x9d, 0x31, 0xf7, 0x1d, 0x38, 0x13, 0x17, 0xa9, 0x13, + 0xe4, 0x33, 0x10, 0xbd, 0xb3, 0xce, 0x09, 0x1a, 0x7e, 0x0e, 0x5d, 0x71, 0x57, 0xa7, 0xa5, 0x97, + 0x8c, 0x27, 0xe1, 0x1d, 0xd8, 0x49, 0x12, 0xde, 0xe7, 0xa0, 0x54, 0xb5, 0xdb, 0xba, 0xe9, 0x47, + 0x69, 0xc2, 0x51, 0x1c, 0xd4, 0x8b, 0x18, 0x4d, 0x50, 0x30, 0xfe, 0xa2, 0x62, 0xec, 0xb2, 0xa1, + 0x90, 0xbf, 0x5f, 0x80, 0x59, 0x69, 0x9a, 0x4c, 0x44, 0x6c, 0x18, 0x15, 0xd5, 0x89, 0x9b, 0x35, + 0xc0, 0xcd, 0xd3, 0xcb, 0xbe, 0x8c, 0xb2, 0xd5, 0x6a, 0x22, 0x52, 0x8e, 0x6f, 0xa3, 0x78, 0x6e, + 0x60, 0xf1, 0x31, 0xfc, 0x8e, 0x4d, 0x8b, 0xf2, 0x97, 0x84, 0x80, 0x93, 0xcc, 0x70, 0x52, 0x08, + 0x38, 0xcb, 0xc8, 0x44, 0x64, 0x1a, 0x4e, 0x62, 0xd4, 0xf9, 0x60, 0x2b, 0xc5, 0x54, 0x62, 0x04, + 0x8d, 0x4a, 0xbc, 0xb0, 0xe1, 0x81, 0xea, 0xd9, 0xc7, 0x35, 0x9a, 0x02, 0xad, 0x25, 0x4b, 0x90, + 0xf3, 0x50, 0x58, 0x98, 0xab, 0xe0, 0x4d, 0xcf, 0x20, 0xcf, 0xf8, 0x66, 0xb5, 0x74, 0x8d, 0xc1, + 0x2e, 0x7c, 0x0a, 0x48, 0xf2, 0x73, 0x76, 0x75, 0x9b, 0xf3, 0x0f, 0xa5, 0x2d, 0xdf, 0x51, 0xf7, + 0xc7, 0x39, 0x88, 0x89, 0x30, 0x92, 0x6c, 0xb1, 0xff, 0x83, 0x4c, 0xb6, 0x58, 0x3a, 0xd0, 0x64, + 0x8b, 0xea, 0x2f, 0xe4, 0xe0, 0x64, 0x22, 0x33, 0x03, 0x79, 0x09, 0x80, 0x43, 0xa4, 0x08, 0xaf, + 0x18, 0x80, 0x28, 0xcc, 0xd6, 0x20, 0x96, 0xc7, 0x90, 0x8c, 0x5c, 0x83, 0x41, 0xfe, 0x4b, 0xc4, + 0x38, 0x4b, 0x16, 0xe9, 0x76, 0x4d, 0x43, 0x0b, 0x88, 0xc2, 0x5a, 0xf0, 0x3e, 0xb3, 0x90, 0x5a, + 0xc4, 0xdb, 0xe8, 0x04, 0xb5, 0x30, 0x32, 0xf5, 0xc7, 0xf2, 0x30, 0x12, 0x34, 0xb8, 0x62, 0x1c, + 0x96, 0xce, 0x95, 0x44, 0x92, 0x8b, 0xc2, 0x76, 0x49, 0x2e, 0x62, 0xf3, 0xad, 0xc8, 0x6a, 0x71, + 0x70, 0x6f, 0xba, 0xbe, 0x9c, 0x87, 0x13, 0x41, 0xad, 0x87, 0x78, 0x75, 0xf6, 0x21, 0x12, 0xc9, + 0x97, 0x72, 0xa0, 0x4c, 0x9a, 0xad, 0x96, 0x69, 0xad, 0xd6, 0xac, 0xfb, 0xb6, 0xd3, 0xc6, 0x09, + 0xf1, 0xf0, 0x8e, 0x70, 0xd5, 0x3f, 0x97, 0x83, 0x93, 0xa2, 0x41, 0x53, 0xba, 0x63, 0x1c, 0xde, + 0xf9, 0x58, 0xbc, 0x25, 0x87, 0xa7, 0x2f, 0xea, 0x37, 0xf2, 0x00, 0x73, 0x76, 0xf3, 0xc1, 0x11, + 0x7f, 0x12, 0xf6, 0x06, 0x94, 0xb8, 0x53, 0xbd, 0xd0, 0xd8, 0x93, 0xe2, 0xe9, 0x13, 0xfb, 0x34, + 0x8e, 0x98, 0x1c, 0x17, 0xf3, 0x71, 0x89, 0xfb, 0xe5, 0x2b, 0x39, 0x4d, 0x14, 0x61, 0x95, 0x32, + 0x3a, 0xb1, 0x60, 0x04, 0x95, 0x32, 0x58, 0xb4, 0xd2, 0xad, 0xcd, 0x72, 0xb1, 0x65, 0x37, 0x1f, + 0x68, 0x48, 0xaf, 0xfe, 0x9b, 0x1c, 0x97, 0xdd, 0x11, 0x7f, 0xd8, 0xea, 0x7f, 0x7e, 0x71, 0x97, + 0x9f, 0xff, 0xe7, 0x73, 0x70, 0x5a, 0xa3, 0x4d, 0xfb, 0x21, 0x75, 0x36, 0xa6, 0x6c, 0x83, 0xde, + 0xa4, 0x16, 0x75, 0x0e, 0x6b, 0x44, 0xfd, 0x06, 0x66, 0x05, 0x0a, 0x1b, 0x73, 0xc7, 0xa5, 0xc6, + 0xd1, 0xc9, 0xd8, 0xa4, 0xfe, 0xd2, 0x00, 0x28, 0xa9, 0x56, 0xef, 0x91, 0x35, 0xe7, 0x32, 0xb7, + 0x32, 0xc5, 0x83, 0xda, 0xca, 0xf4, 0xef, 0x6e, 0x2b, 0x53, 0xda, 0xed, 0x56, 0x66, 0x60, 0x27, + 0x5b, 0x99, 0x76, 0x7c, 0x2b, 0x33, 0x88, 0x5b, 0x99, 0x97, 0x7a, 0x6e, 0x65, 0xa6, 0x2d, 0x63, + 0x8f, 0x1b, 0x99, 0x23, 0x9b, 0x4d, 0x7c, 0x2f, 0x3b, 0xb0, 0x2b, 0x6c, 0x52, 0x6c, 0xda, 0x8e, + 0x41, 0x0d, 0xb1, 0xf1, 0xc2, 0x53, 0x7f, 0x47, 0xc0, 0xb4, 0x00, 0x9b, 0x48, 0xcd, 0x3e, 0xba, + 0x93, 0xd4, 0xec, 0x07, 0xb0, 0xff, 0xfa, 0x62, 0x1e, 0x4e, 0x4e, 0x51, 0xc7, 0xe3, 0x91, 0x6c, + 0x0f, 0xc2, 0xa1, 0xae, 0x02, 0x27, 0x24, 0x86, 0x68, 0x91, 0xe7, 0x43, 0x27, 0xc1, 0x26, 0x75, + 0xbc, 0xb8, 0x8f, 0x61, 0x9c, 0x9e, 0x55, 0xef, 0xa7, 0x47, 0x14, 0x63, 0x37, 0xa8, 0xde, 0x87, + 0x73, 0x41, 0x9a, 0xe2, 0x97, 0x16, 0xd0, 0x4b, 0x7e, 0x32, 0xc5, 0x3d, 0xf8, 0xc9, 0xfc, 0x7c, + 0x0e, 0x2e, 0x6b, 0xd4, 0xa2, 0xeb, 0xfa, 0x4a, 0x8b, 0x4a, 0xcd, 0x12, 0x2b, 0x03, 0x9b, 0x35, + 0x4c, 0xb7, 0xad, 0x7b, 0xcd, 0xb5, 0x7d, 0xc9, 0x68, 0x06, 0x46, 0xe4, 0xf9, 0x6b, 0x17, 0x73, + 0x5b, 0xa4, 0x9c, 0xfa, 0x4b, 0x45, 0x18, 0x98, 0xb4, 0xbd, 0x5b, 0xf6, 0x3e, 0x53, 0x70, 0x86, + 0x53, 0x7e, 0x7e, 0x17, 0x67, 0x3d, 0x1f, 0xc7, 0xca, 0xa5, 0xac, 0x1b, 0xe8, 0x80, 0xba, 0x62, + 0x27, 0xb2, 0xb7, 0xf8, 0x64, 0xbb, 0x4c, 0xbe, 0xf9, 0x0a, 0x0c, 0x61, 0x00, 0x1a, 0xe9, 0x34, + 0x16, 0xdd, 0xbb, 0x3d, 0x06, 0x8c, 0xd7, 0x11, 0x92, 0x92, 0xcf, 0x46, 0x42, 0xef, 0x96, 0xf6, + 0x9f, 0xac, 0x53, 0x8e, 0xc2, 0xfb, 0x12, 0xbf, 0xc8, 0xc3, 0x36, 0x49, 0x89, 0x8d, 0xf0, 0x14, + 0x25, 0xd6, 0xa4, 0x80, 0xf0, 0x00, 0x13, 0x69, 0x4e, 0xc1, 0xe8, 0xa4, 0xed, 0x49, 0xae, 0xc4, + 0x43, 0xe1, 0x4b, 0x54, 0x26, 0xf9, 0x74, 0x3f, 0xe2, 0x68, 0x19, 0xf5, 0x8f, 0x8b, 0x30, 0xe2, + 0xff, 0x3c, 0x24, 0xdd, 0x79, 0x01, 0x4a, 0xb3, 0xb6, 0x94, 0xbb, 0x04, 0xdd, 0x8f, 0xd7, 0x6c, + 0x37, 0xe6, 0x57, 0x2d, 0x88, 0x98, 0xd4, 0x17, 0x6c, 0x43, 0x76, 0x9e, 0x47, 0xa9, 0x5b, 0xb6, + 0x91, 0x78, 0xc1, 0x1c, 0x10, 0x92, 0xcb, 0x50, 0xc4, 0x77, 0x07, 0xd2, 0x41, 0x7e, 0xec, 0xad, + 0x01, 0xe2, 0x25, 0xad, 0x2c, 0xed, 0x56, 0x2b, 0x07, 0xf6, 0xaa, 0x95, 0x83, 0x07, 0xab, 0x95, + 0xef, 0xc2, 0x08, 0xd6, 0xe4, 0xa7, 0x86, 0xdc, 0x7e, 0x61, 0x3d, 0x2f, 0xd6, 0xbe, 0x51, 0xde, + 0x6e, 0x91, 0x20, 0x12, 0x97, 0xbc, 0x08, 0xab, 0x98, 0xee, 0xc2, 0x3e, 0xb6, 0xd3, 0xff, 0x34, + 0x07, 0x03, 0x77, 0xac, 0x07, 0x96, 0xbd, 0xbe, 0x3f, 0x8d, 0x7b, 0x09, 0x86, 0x05, 0x1b, 0x69, + 0x75, 0xc1, 0x47, 0xe9, 0x5d, 0x0e, 0x6e, 0x20, 0x27, 0x4d, 0xa6, 0x22, 0x6f, 0x06, 0x85, 0xf0, + 0x69, 0x51, 0x21, 0xcc, 0xfe, 0xe3, 0x17, 0x6a, 0x46, 0xd3, 0x7f, 0xc8, 0xe4, 0xe4, 0x22, 0x14, + 0xab, 0xac, 0xa9, 0x52, 0x18, 0x60, 0xd6, 0x14, 0x0d, 0xa1, 0xea, 0x17, 0x8b, 0x30, 0x16, 0x3b, + 0xf8, 0x7a, 0x0e, 0x86, 0xc4, 0xc1, 0x93, 0xe9, 0xe7, 0x23, 0xc1, 0xa7, 0x47, 0x01, 0x50, 0x1b, + 0xe4, 0x7f, 0xd6, 0x0c, 0xf2, 0x49, 0x18, 0xb0, 0x5d, 0x5c, 0x14, 0xf1, 0x5b, 0xc6, 0xc2, 0x21, + 0xb4, 0x58, 0x67, 0x6d, 0xe7, 0x83, 0x43, 0x90, 0xc8, 0x1a, 0x69, 0xbb, 0xf8, 0x69, 0x37, 0x60, + 0x48, 0x77, 0x5d, 0xea, 0x35, 0x3c, 0x7d, 0x55, 0x4e, 0x51, 0x12, 0x00, 0xe5, 0xd1, 0x81, 0xc0, + 0x65, 0x7d, 0x95, 0x7c, 0x0a, 0x46, 0x9b, 0x0e, 0xc5, 0x65, 0x53, 0x6f, 0xb1, 0x56, 0x4a, 0x66, + 0x6d, 0x04, 0x21, 0xdf, 0x9f, 0x84, 0x88, 0x9a, 0x41, 0xee, 0xc2, 0xa8, 0xf8, 0x1c, 0xee, 0xf7, + 0x8f, 0x03, 0x6d, 0x2c, 0x5c, 0xc6, 0xb8, 0x48, 0xb8, 0xe7, 0xbf, 0x78, 0xfe, 0x21, 0x93, 0xcb, + 0x7c, 0x0d, 0x89, 0x94, 0x2c, 0x02, 0x59, 0xa7, 0x2b, 0x0d, 0xbd, 0xeb, 0xad, 0xb1, 0xba, 0x78, + 0x84, 0x7d, 0x91, 0x6b, 0x15, 0xdf, 0x4c, 0x24, 0xb1, 0xf2, 0x53, 0x92, 0x75, 0xba, 0x52, 0x89, + 0x20, 0xc9, 0x3d, 0x38, 0x93, 0x2c, 0xc2, 0x3e, 0x99, 0x5f, 0x0e, 0x3c, 0xb3, 0xb5, 0x59, 0x2e, + 0xa7, 0x12, 0x48, 0x6c, 0x4f, 0x25, 0xd8, 0xd6, 0x8c, 0x5b, 0xc5, 0xc1, 0x81, 0xf1, 0x41, 0x6d, + 0x8c, 0x95, 0xf5, 0x4d, 0x48, 0xd3, 0x50, 0x7f, 0x37, 0xc7, 0x4c, 0x45, 0xf6, 0x41, 0x98, 0x6c, + 0x9e, 0xe9, 0x7a, 0x7b, 0x97, 0xba, 0xde, 0x0e, 0xd3, 0xc2, 0x96, 0xdc, 0x1e, 0xb3, 0xab, 0x26, + 0xb0, 0x64, 0x02, 0x4a, 0x86, 0x7c, 0x6a, 0x76, 0x36, 0xda, 0x09, 0x7e, 0x3d, 0x9a, 0xa0, 0x22, + 0x57, 0xa0, 0xc8, 0x96, 0xac, 0xf8, 0x96, 0x59, 0xb6, 0x2e, 0x34, 0xa4, 0x50, 0x7f, 0x30, 0x0f, + 0x23, 0xd2, 0xd7, 0x5c, 0xdf, 0xd7, 0xe7, 0xbc, 0xbe, 0xb3, 0x66, 0xfa, 0x4e, 0x2f, 0xb8, 0x97, + 0xf2, 0x9b, 0x7c, 0x23, 0x10, 0xc5, 0x8e, 0x2e, 0xa4, 0x84, 0x60, 0x5e, 0x11, 0x1f, 0x5a, 0xda, + 0xf9, 0xf6, 0x91, 0xd1, 0xdf, 0x2a, 0x0e, 0xe6, 0xc7, 0x0b, 0xb7, 0x8a, 0x83, 0xc5, 0xf1, 0x7e, + 0x0c, 0x05, 0x86, 0xd1, 0xb7, 0xf9, 0xde, 0xdc, 0xba, 0x6f, 0xae, 0x1e, 0xf1, 0x97, 0x27, 0x07, + 0x1b, 0x26, 0x2d, 0x26, 0x9b, 0x23, 0xfe, 0x0c, 0xe5, 0x03, 0x95, 0xcd, 0x71, 0x1a, 0x59, 0x21, + 0x9b, 0x7f, 0x96, 0x03, 0x25, 0x55, 0x36, 0x95, 0x43, 0xf2, 0x83, 0x38, 0xb8, 0x64, 0xb2, 0x7f, + 0x98, 0x87, 0x93, 0x35, 0xcb, 0xa3, 0xab, 0x7c, 0xc7, 0x78, 0xc4, 0xa7, 0x8a, 0xdb, 0x30, 0x2c, + 0x7d, 0x8c, 0xe8, 0xf3, 0x27, 0x82, 0xfd, 0x78, 0x88, 0xca, 0xe0, 0x24, 0x97, 0x3e, 0xb8, 0x77, + 0x3c, 0x71, 0x21, 0x1f, 0xf1, 0x39, 0xe7, 0x68, 0x08, 0xf9, 0x88, 0x4f, 0x5e, 0x1f, 0x52, 0x21, + 0xff, 0xbd, 0x3c, 0x9c, 0x4a, 0xa9, 0x9c, 0x5c, 0x86, 0x81, 0x7a, 0x77, 0x05, 0x23, 0x7f, 0xe5, + 0x42, 0x8f, 0x61, 0xb7, 0xbb, 0x82, 0x41, 0xbf, 0x34, 0x1f, 0x49, 0x96, 0xf1, 0x69, 0xfe, 0x62, + 0xad, 0x3a, 0x25, 0xa4, 0xaa, 0x4a, 0x41, 0x06, 0x18, 0x38, 0xed, 0xcb, 0x82, 0xe7, 0xfb, 0xb6, + 0x69, 0x34, 0x63, 0xcf, 0xf7, 0x59, 0x19, 0xf2, 0x7d, 0x30, 0x54, 0x79, 0xbf, 0xeb, 0x50, 0xe4, + 0xcb, 0x25, 0xfe, 0x91, 0x80, 0xaf, 0x8f, 0x48, 0xe3, 0xcc, 0x23, 0x11, 0x30, 0x8a, 0x38, 0xef, + 0x90, 0x21, 0x59, 0x84, 0xd2, 0x4d, 0xd3, 0x9b, 0xed, 0xae, 0x88, 0x5e, 0x08, 0xa2, 0x83, 0x71, + 0x68, 0x1a, 0x5f, 0xdc, 0x95, 0xf3, 0x28, 0xc7, 0xf2, 0x1e, 0x88, 0x17, 0x50, 0x7f, 0x2c, 0x07, + 0x17, 0xb2, 0x3f, 0x97, 0x7c, 0x1c, 0x06, 0xd8, 0x56, 0xbf, 0xa2, 0x2d, 0x08, 0x59, 0xf2, 0x4c, + 0xce, 0x76, 0x8b, 0x36, 0x74, 0x47, 0xde, 0x3d, 0xf8, 0x64, 0xe4, 0x2d, 0x18, 0xae, 0xb9, 0x6e, + 0x97, 0x3a, 0xf5, 0x97, 0xee, 0x68, 0x35, 0xb1, 0xc9, 0xc4, 0x4d, 0x8c, 0x89, 0xe0, 0x86, 0xfb, + 0x52, 0x2c, 0x58, 0x98, 0x4c, 0xaf, 0xfe, 0x48, 0x0e, 0x2e, 0xf6, 0x12, 0x13, 0x79, 0x09, 0x06, + 0x97, 0xa9, 0xa5, 0x5b, 0x5e, 0xad, 0x2a, 0x9a, 0x84, 0x7b, 0x36, 0x0f, 0x61, 0xd1, 0xad, 0x47, + 0x40, 0xc8, 0x0a, 0xf1, 0x83, 0xca, 0xc0, 0x33, 0x82, 0x1f, 0xaa, 0x22, 0x2c, 0x56, 0xc8, 0x27, + 0x54, 0x3f, 0x0b, 0xe7, 0x33, 0xa5, 0x4a, 0x3e, 0x09, 0x23, 0x8b, 0xce, 0xaa, 0x6e, 0x99, 0xef, + 0xf3, 0x41, 0x91, 0x0b, 0xf7, 0xc5, 0xb6, 0x04, 0x97, 0xf7, 0x6a, 0x32, 0xbd, 0xfa, 0x7b, 0x79, + 0x18, 0x59, 0x6a, 0x75, 0x57, 0x4d, 0x69, 0x99, 0xdb, 0xf3, 0xee, 0xc0, 0xb7, 0xd5, 0xf3, 0xbb, + 0xb3, 0xd5, 0xd9, 0xe4, 0xe0, 0xec, 0x71, 0x72, 0xf0, 0xcb, 0x91, 0x37, 0xa1, 0xd4, 0xc1, 0xef, + 0x88, 0x9f, 0x1b, 0xf3, 0xaf, 0xcb, 0x3a, 0x37, 0xe6, 0x65, 0xd8, 0x6c, 0xd0, 0xdc, 0xc7, 0x6c, + 0x10, 0x96, 0x95, 0x04, 0x1a, 0x2e, 0x69, 0xc7, 0x02, 0x3d, 0x10, 0x81, 0x86, 0xcb, 0xd7, 0xb1, + 0x40, 0xf7, 0x21, 0xd0, 0x5f, 0xca, 0xc3, 0x58, 0xb4, 0x4a, 0xf2, 0x71, 0x18, 0xe6, 0xd5, 0xf0, + 0x53, 0xac, 0x9c, 0xe4, 0x02, 0x1d, 0x82, 0x35, 0xe0, 0x3f, 0xc4, 0x71, 0xdc, 0x89, 0x35, 0xdd, + 0x6d, 0x84, 0xe7, 0x49, 0xfc, 0xb6, 0x79, 0x90, 0xfb, 0x6d, 0xc5, 0x50, 0xda, 0xd8, 0x9a, 0xee, + 0x4e, 0x85, 0xbf, 0xc9, 0x34, 0x10, 0x87, 0x76, 0x5d, 0x1a, 0x65, 0x50, 0x44, 0x06, 0x22, 0x47, + 0x7f, 0x1c, 0xab, 0x9d, 0xe4, 0x30, 0x99, 0xcd, 0xe7, 0x82, 0x66, 0xa3, 0x32, 0xf4, 0xf7, 0x3e, + 0x93, 0x7d, 0x66, 0x6b, 0xb3, 0x7c, 0x46, 0xa2, 0x4f, 0x3f, 0x94, 0xe5, 0x04, 0x55, 0xdd, 0xd3, + 0xf9, 0x11, 0x82, 0xdf, 0x01, 0xea, 0x9f, 0xe9, 0x42, 0xff, 0xa2, 0x45, 0x17, 0xef, 0x93, 0x17, + 0x61, 0x88, 0x29, 0xcc, 0x9c, 0xcd, 0xfa, 0x32, 0x27, 0xbc, 0x3d, 0x24, 0x4d, 0x42, 0xc4, 0x6c, + 0x9f, 0x16, 0x52, 0x91, 0x1b, 0x00, 0xe1, 0x83, 0x38, 0xa1, 0x7d, 0x44, 0x2e, 0xc3, 0x31, 0xb3, + 0x7d, 0x9a, 0x44, 0xe7, 0x97, 0x12, 0xcf, 0x89, 0x0a, 0xc9, 0x52, 0x1c, 0xe3, 0x97, 0x12, 0xe3, + 0x63, 0x0e, 0x08, 0xfb, 0xb5, 0xa4, 0xbb, 0xee, 0xba, 0xed, 0x18, 0x53, 0x6b, 0xba, 0xb5, 0x4a, + 0xe3, 0x7b, 0xbd, 0x24, 0xc5, 0x6c, 0x9f, 0x96, 0x52, 0x8e, 0xbc, 0x0e, 0x23, 0xb2, 0xfb, 0x6b, + 0xdc, 0x45, 0x45, 0xc6, 0xcd, 0xf6, 0x69, 0x11, 0x5a, 0xf2, 0x2a, 0x0c, 0x8b, 0xdf, 0xb7, 0x6c, + 0x71, 0xff, 0x2d, 0xc5, 0x5d, 0x92, 0x50, 0xb3, 0x7d, 0x9a, 0x4c, 0x29, 0x55, 0xba, 0xe4, 0x98, + 0x96, 0x27, 0x5e, 0x54, 0xc7, 0x2b, 0x45, 0x9c, 0x54, 0x29, 0xfe, 0x26, 0x6f, 0xc1, 0x68, 0x10, + 0xd0, 0xea, 0x3d, 0xda, 0xf4, 0xc4, 0x51, 0xfd, 0x99, 0x58, 0x61, 0x8e, 0x9c, 0xed, 0xd3, 0xa2, + 0xd4, 0xe4, 0x0a, 0x94, 0x34, 0xea, 0x9a, 0xef, 0xfb, 0x97, 0xdb, 0x63, 0xd2, 0x38, 0x37, 0xdf, + 0x67, 0x52, 0x12, 0x78, 0xd6, 0x3b, 0xe1, 0x6d, 0xba, 0x38, 0x58, 0x27, 0xb1, 0x5a, 0xa6, 0x2d, + 0x83, 0xf5, 0x8e, 0xe4, 0x4a, 0xf1, 0xa9, 0x30, 0xcc, 0x97, 0xc8, 0x72, 0x3b, 0x1c, 0x8f, 0xa7, + 0x20, 0x63, 0x67, 0xfb, 0xb4, 0x18, 0xbd, 0x24, 0xd5, 0xaa, 0xe9, 0x3e, 0x10, 0xe1, 0x59, 0xe3, + 0x52, 0x65, 0x28, 0x49, 0xaa, 0xec, 0xa7, 0x54, 0xf5, 0x02, 0xf5, 0xd6, 0x6d, 0xe7, 0x81, 0x08, + 0xc6, 0x1a, 0xaf, 0x5a, 0x60, 0xa5, 0xaa, 0x05, 0x44, 0xae, 0x9a, 0x0d, 0xb8, 0xb1, 0xf4, 0xaa, + 0x75, 0x4f, 0x97, 0xab, 0xe6, 0xe7, 0x86, 0x7e, 0x27, 0xcd, 0x51, 0xfd, 0x21, 0x55, 0x4e, 0xa4, + 0x76, 0x28, 0xe2, 0xa4, 0x0e, 0xc5, 0xdf, 0xac, 0x52, 0x29, 0x05, 0xbe, 0x32, 0x1e, 0xad, 0x54, + 0x42, 0xb1, 0x4a, 0xe5, 0x64, 0xf9, 0x37, 0xe4, 0x3c, 0xeb, 0xca, 0xc9, 0x68, 0x07, 0x85, 0x18, + 0xd6, 0x41, 0x52, 0x3e, 0xf6, 0x32, 0xe6, 0x70, 0x56, 0x08, 0x92, 0x0f, 0x07, 0x2d, 0x9c, 0x5a, + 0x9a, 0xed, 0xd3, 0x30, 0xbb, 0xb3, 0xca, 0xb3, 0x83, 0x2b, 0xa7, 0x90, 0x62, 0xc4, 0xa7, 0x60, + 0xb0, 0xd9, 0x3e, 0x8d, 0x67, 0x0e, 0x7f, 0x51, 0xca, 0xa0, 0xa8, 0x9c, 0x8e, 0x4e, 0x11, 0x01, + 0x82, 0x4d, 0x11, 0x61, 0x9e, 0xc5, 0x99, 0x64, 0x9e, 0x40, 0xe5, 0x4c, 0x74, 0xa9, 0x89, 0xe3, + 0x67, 0xfb, 0xb4, 0x64, 0x6e, 0xc1, 0x57, 0x23, 0xa9, 0xf3, 0x94, 0xb3, 0xb1, 0x60, 0x67, 0x21, + 0x8a, 0x89, 0x4b, 0x4e, 0xb2, 0xb7, 0x08, 0xa7, 0x78, 0xe6, 0x5d, 0x11, 0xae, 0x4c, 0x4c, 0x56, + 0xe7, 0xa2, 0xdb, 0xac, 0x14, 0x92, 0xd9, 0x3e, 0x2d, 0xad, 0x24, 0x99, 0x4a, 0x24, 0xb0, 0x53, + 0x94, 0xa8, 0x27, 0x4f, 0x0c, 0x3d, 0xdb, 0xa7, 0x25, 0x52, 0xde, 0xdd, 0x90, 0x33, 0xc7, 0x29, + 0xe7, 0xa3, 0x9d, 0x18, 0x62, 0x58, 0x27, 0x4a, 0x19, 0xe6, 0x6e, 0xc8, 0xd9, 0xc4, 0x94, 0x0b, + 0xc9, 0x52, 0xe1, 0xcc, 0x29, 0x65, 0x1d, 0xd3, 0xd2, 0x13, 0x24, 0x29, 0x4f, 0x88, 0x34, 0xc9, + 0xa2, 0x7c, 0x1a, 0xcd, 0x6c, 0x9f, 0x96, 0x9e, 0x5c, 0x49, 0x4b, 0xcf, 0x2c, 0xa4, 0x5c, 0xec, + 0xc5, 0x33, 0x68, 0x5d, 0x7a, 0x56, 0x22, 0xbd, 0x47, 0x9e, 0x17, 0xe5, 0x52, 0x74, 0x43, 0x96, + 0x49, 0x38, 0xdb, 0xa7, 0xf5, 0xc8, 0x16, 0x73, 0x27, 0x23, 0xe9, 0x8a, 0xf2, 0x64, 0x34, 0x4b, + 0x7a, 0x2a, 0xd1, 0x6c, 0x9f, 0x96, 0x91, 0xb2, 0xe5, 0x4e, 0x46, 0x4e, 0x0e, 0xa5, 0xdc, 0x93, + 0x6d, 0x20, 0x8f, 0x8c, 0x8c, 0x1e, 0x8b, 0xa9, 0xe9, 0x2c, 0x94, 0xa7, 0xa2, 0xaa, 0x9b, 0x42, + 0xc2, 0x54, 0x37, 0x2d, 0x11, 0xc6, 0x62, 0x6a, 0xfe, 0x05, 0xe5, 0xe9, 0x1e, 0x0c, 0x83, 0x36, + 0xa6, 0x66, 0x6e, 0x58, 0x4c, 0x4d, 0x80, 0xa0, 0xa8, 0x51, 0x86, 0x29, 0x24, 0x8c, 0x61, 0x5a, + 0xea, 0x84, 0xc5, 0xd4, 0x38, 0xf9, 0xca, 0x33, 0x3d, 0x18, 0x86, 0x2d, 0x4c, 0x8b, 0xb0, 0xff, + 0x6a, 0x24, 0x50, 0xbd, 0xf2, 0x91, 0xe8, 0xbc, 0x21, 0xa1, 0xd8, 0xbc, 0x21, 0x87, 0xb4, 0x9f, + 0x4a, 0x44, 0xd1, 0x55, 0x9e, 0x8d, 0x0e, 0xf3, 0x18, 0x9a, 0x0d, 0xf3, 0x78, 0xdc, 0xdd, 0xa9, + 0x44, 0x34, 0x51, 0xe5, 0x72, 0x16, 0x13, 0x44, 0x47, 0x99, 0xf0, 0xf8, 0xa3, 0xb5, 0x94, 0x70, + 0x96, 0xca, 0x47, 0xa3, 0x5e, 0xe8, 0x09, 0x82, 0xd9, 0x3e, 0x2d, 0x25, 0x08, 0xa6, 0x96, 0x1e, + 0xbb, 0x49, 0xb9, 0x12, 0x1d, 0xb6, 0x69, 0x34, 0x6c, 0xd8, 0xa6, 0xc6, 0x7d, 0x9a, 0x4b, 0x7b, + 0x2a, 0xa3, 0x5c, 0x8d, 0x1a, 0x66, 0x49, 0x0a, 0x66, 0x98, 0xa5, 0x3c, 0xb1, 0xd1, 0xd2, 0x23, + 0x02, 0x29, 0xcf, 0xf5, 0x6c, 0x21, 0xd2, 0xa4, 0xb4, 0x90, 0x07, 0xc8, 0x09, 0x6d, 0xa7, 0x3b, + 0x9d, 0x96, 0xad, 0x1b, 0xca, 0xc7, 0x52, 0x6d, 0x27, 0x8e, 0x94, 0x6c, 0x27, 0x0e, 0x60, 0xab, + 0xbc, 0xfc, 0x22, 0x43, 0x79, 0x3e, 0xba, 0xca, 0xcb, 0x38, 0xb6, 0xca, 0x47, 0x5e, 0x6f, 0x4c, + 0x25, 0x5e, 0x2f, 0x28, 0x2f, 0x44, 0x15, 0x20, 0x86, 0x66, 0x0a, 0x10, 0x7f, 0xef, 0xf0, 0xf9, + 0x6c, 0x7f, 0x7f, 0x65, 0x02, 0xb9, 0x3d, 0xe5, 0x73, 0xcb, 0xa2, 0x9b, 0xed, 0xd3, 0xb2, 0xdf, + 0x0c, 0xd4, 0x52, 0xdc, 0xf7, 0x95, 0x6b, 0x51, 0x05, 0x4b, 0x10, 0x30, 0x05, 0x4b, 0x3a, 0xfd, + 0xd7, 0x52, 0xfc, 0xef, 0x95, 0x8f, 0x67, 0xb2, 0x0a, 0xbe, 0x39, 0xc5, 0x6b, 0xff, 0x86, 0xec, + 0x40, 0xaf, 0xbc, 0x18, 0x5d, 0xec, 0x42, 0x0c, 0x5b, 0xec, 0x24, 0x47, 0xfb, 0x1b, 0xb2, 0xeb, + 0xb8, 0x72, 0x3d, 0x59, 0x2a, 0x5c, 0x22, 0x25, 0x17, 0x73, 0x2d, 0xdd, 0xe3, 0x5a, 0x79, 0x29, + 0xaa, 0x75, 0x69, 0x34, 0x4c, 0xeb, 0x52, 0xbd, 0xb5, 0x67, 0x92, 0x8e, 0xd3, 0xca, 0x8d, 0xf8, + 0x26, 0x3b, 0x8a, 0x67, 0x96, 0x4f, 0xc2, 0xd9, 0xfa, 0x53, 0xf1, 0xc0, 0x82, 0xca, 0xcb, 0xb1, + 0x4b, 0xea, 0x08, 0x96, 0xd9, 0xb7, 0xb1, 0x40, 0x84, 0x9f, 0x8a, 0xc7, 0xe2, 0x53, 0x5e, 0x49, + 0xe7, 0x10, 0xe8, 0x4a, 0x3c, 0x76, 0xdf, 0xa7, 0xe2, 0xe1, 0xeb, 0x94, 0x57, 0xd3, 0x39, 0x04, + 0xd2, 0x8d, 0x87, 0xbb, 0x7b, 0x51, 0x0a, 0xa8, 0xaf, 0x7c, 0x22, 0x6a, 0x3a, 0x06, 0x08, 0x66, + 0x3a, 0x86, 0x61, 0xf7, 0x5f, 0x94, 0x02, 0xd1, 0x2b, 0xaf, 0x25, 0x8a, 0x04, 0x8d, 0x95, 0xc2, + 0xd5, 0xbf, 0x28, 0x05, 0x70, 0x57, 0x5e, 0x4f, 0x14, 0x09, 0x5a, 0x27, 0x85, 0x79, 0x37, 0x7a, + 0xbd, 0xb6, 0x55, 0xde, 0x88, 0x1e, 0x5d, 0x67, 0x53, 0xce, 0xf6, 0x69, 0xbd, 0x5e, 0xed, 0x7e, + 0x3e, 0xdb, 0x0d, 0x5d, 0x79, 0x33, 0x3a, 0x84, 0xb3, 0xe8, 0xd8, 0x10, 0xce, 0x74, 0x65, 0x7f, + 0x2b, 0x16, 0x79, 0x43, 0x79, 0x2b, 0x3a, 0xc5, 0x45, 0x90, 0x6c, 0x8a, 0x8b, 0xc7, 0xe9, 0x88, + 0x84, 0x94, 0x50, 0x3e, 0x19, 0x9d, 0xe2, 0x64, 0x1c, 0x9b, 0xe2, 0x22, 0xe1, 0x27, 0xa6, 0x12, + 0x91, 0x0e, 0x94, 0xb7, 0xa3, 0x53, 0x5c, 0x0c, 0xcd, 0xa6, 0xb8, 0x78, 0x6c, 0x84, 0xb7, 0x62, + 0x0f, 0xfe, 0x95, 0x4f, 0xa5, 0xb7, 0x1f, 0x91, 0x72, 0xfb, 0x79, 0x78, 0x00, 0x2d, 0xfd, 0xe5, + 0xba, 0x52, 0x89, 0x8e, 0xdf, 0x34, 0x1a, 0x36, 0x7e, 0x53, 0x5f, 0xbd, 0xc7, 0x37, 0x0e, 0x42, + 0xab, 0x26, 0x7b, 0x6c, 0x1c, 0x42, 0x53, 0x24, 0x05, 0x1c, 0xd9, 0x23, 0xf3, 0x8d, 0xd0, 0x54, + 0xc6, 0x1e, 0xd9, 0xdf, 0x06, 0xc5, 0xe8, 0xd9, 0xec, 0x9a, 0xf0, 0x8a, 0x56, 0xaa, 0xd1, 0xd9, + 0x35, 0x41, 0xc0, 0x66, 0xd7, 0xa4, 0x2f, 0xf5, 0x0c, 0x8c, 0x0b, 0x2d, 0xe2, 0xce, 0xde, 0xa6, + 0xb5, 0xaa, 0x4c, 0xc7, 0x5e, 0x87, 0xc6, 0xf0, 0x6c, 0x76, 0x8a, 0xc3, 0x70, 0xbd, 0xe6, 0xb0, + 0xa9, 0x96, 0xd9, 0x59, 0xb1, 0x75, 0xc7, 0xa8, 0x53, 0xcb, 0x50, 0x66, 0x62, 0xeb, 0x75, 0x0a, + 0x0d, 0xae, 0xd7, 0x29, 0x70, 0x0c, 0x68, 0x17, 0x83, 0x6b, 0xb4, 0x49, 0xcd, 0x87, 0x54, 0xb9, + 0x89, 0x6c, 0xcb, 0x59, 0x6c, 0x05, 0xd9, 0x6c, 0x9f, 0x96, 0xc5, 0x81, 0xd9, 0xea, 0xf3, 0x1b, + 0xf5, 0x77, 0xe6, 0x82, 0x60, 0x09, 0x4b, 0x0e, 0xed, 0xe8, 0x0e, 0x55, 0x66, 0xa3, 0xb6, 0x7a, + 0x2a, 0x11, 0xb3, 0xd5, 0x53, 0x11, 0x49, 0xb6, 0xfe, 0x58, 0xa8, 0xf5, 0x62, 0x1b, 0x8e, 0x88, + 0xf4, 0xd2, 0x6c, 0x76, 0x8a, 0x22, 0x98, 0x80, 0xe6, 0x6c, 0x6b, 0x15, 0x4f, 0x2a, 0x6e, 0x45, + 0x67, 0xa7, 0x6c, 0x4a, 0x36, 0x3b, 0x65, 0x63, 0x99, 0xaa, 0x47, 0xb1, 0x7c, 0x0c, 0xde, 0x8e, + 0xaa, 0x7a, 0x0a, 0x09, 0x53, 0xf5, 0x14, 0x70, 0x92, 0xa1, 0x46, 0x5d, 0xea, 0x29, 0x73, 0xbd, + 0x18, 0x22, 0x49, 0x92, 0x21, 0x82, 0x93, 0x0c, 0x67, 0xa8, 0xd7, 0x5c, 0x53, 0xe6, 0x7b, 0x31, + 0x44, 0x92, 0x24, 0x43, 0x04, 0xb3, 0xcd, 0x66, 0x14, 0x3c, 0xd9, 0x6d, 0x3d, 0xf0, 0xfb, 0x6c, + 0x21, 0xba, 0xd9, 0xcc, 0x24, 0x64, 0x9b, 0xcd, 0x4c, 0x24, 0xf9, 0x91, 0x1d, 0x7b, 0xed, 0x2b, + 0x8b, 0x58, 0xe1, 0x44, 0x68, 0x17, 0xec, 0xa4, 0xd4, 0x6c, 0x9f, 0xb6, 0xd3, 0x57, 0x01, 0x1f, + 0x0b, 0x5c, 0x5c, 0x95, 0x25, 0xac, 0xea, 0x44, 0x70, 0x56, 0xc1, 0xc1, 0xb3, 0x7d, 0x5a, 0xe0, + 0x04, 0xfb, 0x2a, 0x0c, 0xe3, 0x47, 0xd5, 0x2c, 0xd3, 0xab, 0x4e, 0x2a, 0xef, 0x44, 0xb7, 0x4c, + 0x12, 0x8a, 0x6d, 0x99, 0xa4, 0x9f, 0x6c, 0x12, 0xc7, 0x9f, 0x7c, 0x8a, 0xa9, 0x4e, 0x2a, 0x5a, + 0x74, 0x12, 0x8f, 0x20, 0xd9, 0x24, 0x1e, 0x01, 0x04, 0xf5, 0x56, 0x1d, 0xbb, 0x53, 0x9d, 0x54, + 0xea, 0x29, 0xf5, 0x72, 0x54, 0x50, 0x2f, 0xff, 0x19, 0xd4, 0x5b, 0x5f, 0xeb, 0x7a, 0x55, 0xf6, + 0x8d, 0xcb, 0x29, 0xf5, 0xfa, 0xc8, 0xa0, 0x5e, 0x1f, 0xc0, 0xa6, 0x42, 0x04, 0x2c, 0x39, 0x36, + 0x9b, 0xb4, 0x6f, 0x9b, 0xad, 0x96, 0x72, 0x27, 0x3a, 0x15, 0xc6, 0xf1, 0x6c, 0x2a, 0x8c, 0xc3, + 0x98, 0xe9, 0xc9, 0x5b, 0x45, 0x57, 0xba, 0xab, 0xca, 0xdd, 0xa8, 0xe9, 0x19, 0x62, 0x98, 0xe9, + 0x19, 0xfe, 0xc2, 0xdd, 0x05, 0xfb, 0xa5, 0xd1, 0xfb, 0x0e, 0x75, 0xd7, 0x94, 0x7b, 0xb1, 0xdd, + 0x85, 0x84, 0xc3, 0xdd, 0x85, 0xf4, 0x9b, 0xac, 0xc2, 0x13, 0x91, 0x85, 0xc6, 0xbf, 0xb4, 0xa9, + 0x53, 0xdd, 0x69, 0xae, 0x29, 0x9f, 0x46, 0x56, 0xcf, 0xa4, 0x2e, 0x55, 0x51, 0xd2, 0xd9, 0x3e, + 0xad, 0x17, 0x27, 0xdc, 0x96, 0xbf, 0x33, 0xc7, 0xa3, 0xde, 0x6a, 0x4b, 0x53, 0xfe, 0x26, 0xf4, + 0xdd, 0xd8, 0xb6, 0x3c, 0x49, 0x82, 0xdb, 0xf2, 0x24, 0x98, 0x74, 0xe0, 0xc9, 0xd8, 0x56, 0x6d, + 0x5e, 0x6f, 0xb1, 0x7d, 0x09, 0x35, 0x96, 0xf4, 0xe6, 0x03, 0xea, 0x29, 0x9f, 0x41, 0xde, 0x97, + 0x33, 0x36, 0x7c, 0x31, 0xea, 0xd9, 0x3e, 0x6d, 0x1b, 0x7e, 0x44, 0x85, 0x62, 0x7d, 0x66, 0x79, + 0x49, 0xf9, 0x6c, 0xf4, 0x7c, 0x93, 0xc1, 0x66, 0xfb, 0x34, 0xc4, 0x31, 0x2b, 0xed, 0x4e, 0x67, + 0xd5, 0xd1, 0x0d, 0xca, 0x0d, 0x2d, 0xb4, 0xdd, 0x84, 0x01, 0xfa, 0x7d, 0x51, 0x2b, 0x2d, 0x8b, + 0x8e, 0x59, 0x69, 0x59, 0x38, 0xa6, 0xa8, 0x91, 0x04, 0x2f, 0xca, 0xe7, 0xa2, 0x8a, 0x1a, 0x41, + 0x32, 0x45, 0x8d, 0xa6, 0x83, 0xf9, 0x34, 0x9c, 0x0d, 0xf6, 0xf3, 0x62, 0xfd, 0xe5, 0x9d, 0xa6, + 0x7c, 0x1e, 0xf9, 0x3c, 0x99, 0xb8, 0x0c, 0x88, 0x50, 0xcd, 0xf6, 0x69, 0x19, 0xe5, 0xd9, 0x8a, + 0x9b, 0x48, 0x80, 0x26, 0xcc, 0x8b, 0xef, 0x8f, 0xae, 0xb8, 0x19, 0x64, 0x6c, 0xc5, 0xcd, 0x40, + 0xa5, 0x32, 0x17, 0x42, 0xd5, 0xb7, 0x61, 0x1e, 0xc8, 0x34, 0x8b, 0x43, 0x2a, 0x73, 0x61, 0xa9, + 0xad, 0x6c, 0xc3, 0x3c, 0xb0, 0xd6, 0xb2, 0x38, 0x90, 0x2b, 0x50, 0xaa, 0xd7, 0xe7, 0xb5, 0xae, + 0xa5, 0x34, 0x63, 0xbe, 0xbd, 0x08, 0x9d, 0xed, 0xd3, 0x04, 0x9e, 0x99, 0x41, 0xd3, 0x2d, 0xdd, + 0xf5, 0xcc, 0xa6, 0x8b, 0x23, 0xc6, 0x1f, 0x21, 0x46, 0xd4, 0x0c, 0x4a, 0xa3, 0x61, 0x66, 0x50, + 0x1a, 0x9c, 0xd9, 0x8b, 0x53, 0xba, 0xeb, 0xea, 0x96, 0xe1, 0xe8, 0x93, 0xb8, 0x4c, 0xd0, 0xd8, + 0xdb, 0xb1, 0x08, 0x96, 0xd9, 0x8b, 0x51, 0x08, 0x1e, 0xbe, 0xfb, 0x10, 0xdf, 0xcc, 0xb9, 0x1f, + 0x3b, 0x7c, 0x8f, 0xe1, 0xf1, 0xf0, 0x3d, 0x06, 0x43, 0xbb, 0xd3, 0x87, 0x69, 0x74, 0xd5, 0x64, + 0x22, 0x52, 0x56, 0x63, 0x76, 0x67, 0x9c, 0x00, 0xed, 0xce, 0x38, 0x30, 0xd2, 0x24, 0x7f, 0xb9, + 0x5d, 0xcb, 0x68, 0x52, 0xb8, 0xca, 0x26, 0xca, 0xb0, 0xf5, 0x3b, 0x1c, 0x1c, 0xd5, 0x0d, 0x4b, + 0x6f, 0xdb, 0xd5, 0x49, 0x5f, 0xea, 0x66, 0x74, 0xfd, 0xce, 0x24, 0x64, 0xeb, 0x77, 0x26, 0x92, + 0xcd, 0xae, 0xfe, 0x46, 0x6b, 0x4d, 0x77, 0xa8, 0x51, 0x35, 0x1d, 0x3c, 0x59, 0xdc, 0xe0, 0x5b, + 0xc3, 0xf7, 0xa2, 0xb3, 0x6b, 0x0f, 0x52, 0x36, 0xbb, 0xf6, 0x40, 0x33, 0x23, 0x2f, 0x1d, 0xad, + 0x51, 0xdd, 0x50, 0x1e, 0x44, 0x8d, 0xbc, 0x6c, 0x4a, 0x66, 0xe4, 0x65, 0x63, 0xb3, 0x3f, 0xe7, + 0x9e, 0x63, 0x7a, 0x54, 0x69, 0xed, 0xe4, 0x73, 0x90, 0x34, 0xfb, 0x73, 0x10, 0xcd, 0x36, 0x84, + 0xf1, 0x0e, 0x69, 0x47, 0x37, 0x84, 0xc9, 0x6e, 0x88, 0x97, 0x60, 0x16, 0x8b, 0x78, 0x42, 0xa8, + 0x58, 0x51, 0x8b, 0x45, 0x80, 0x99, 0xc5, 0x12, 0x3e, 0x32, 0x8c, 0x3c, 0x1c, 0x53, 0xec, 0xe8, + 0x1a, 0x2a, 0xe3, 0xd8, 0x1a, 0x1a, 0x79, 0x64, 0xf6, 0x6a, 0xe4, 0x55, 0x84, 0xd2, 0x89, 0x5a, + 0x1d, 0x12, 0x8a, 0x59, 0x1d, 0xf2, 0xfb, 0x89, 0x29, 0x38, 0x81, 0xb7, 0xe0, 0x5a, 0x37, 0xb8, + 0xc7, 0xf9, 0x81, 0xe8, 0x67, 0xc6, 0xd0, 0xec, 0x33, 0x63, 0xa0, 0x08, 0x13, 0x31, 0x6d, 0x39, + 0x19, 0x4c, 0xc2, 0xf3, 0xc1, 0x18, 0x88, 0xcc, 0x01, 0xa9, 0x57, 0xe6, 0xe7, 0x6a, 0xc6, 0x92, + 0x7c, 0x45, 0xe6, 0x46, 0x4f, 0x60, 0x93, 0x14, 0xb3, 0x7d, 0x5a, 0x4a, 0x39, 0xf2, 0x1e, 0x5c, + 0x14, 0x50, 0xf1, 0x3e, 0x7c, 0xc9, 0xb1, 0x1f, 0x9a, 0x46, 0xb0, 0x20, 0x78, 0x51, 0xaf, 0xbb, + 0x5e, 0xb4, 0xb3, 0x7d, 0x5a, 0x4f, 0x5e, 0xd9, 0x75, 0x89, 0xf5, 0xa1, 0xbb, 0x93, 0xba, 0x82, + 0x45, 0xa2, 0x27, 0xaf, 0xec, 0xba, 0x84, 0xdc, 0x1f, 0xee, 0xa4, 0xae, 0xa0, 0x13, 0x7a, 0xf2, + 0x22, 0x2e, 0x94, 0x7b, 0xe1, 0x2b, 0xad, 0x96, 0xb2, 0x8e, 0xd5, 0x7d, 0x74, 0x27, 0xd5, 0x55, + 0xd0, 0xe0, 0xdc, 0x8e, 0x23, 0x9b, 0xa5, 0x17, 0x3b, 0xd4, 0xaa, 0x47, 0x16, 0xa0, 0x47, 0xd1, + 0x59, 0x3a, 0x41, 0xc0, 0x66, 0xe9, 0x04, 0x90, 0x0d, 0x28, 0xf9, 0x71, 0x8d, 0xb2, 0x11, 0x1d, + 0x50, 0x32, 0x8e, 0x0d, 0xa8, 0xc8, 0x43, 0x9c, 0x45, 0x38, 0xb5, 0xf8, 0xc0, 0xd3, 0x7d, 0x0b, + 0xd2, 0x15, 0x5d, 0xf9, 0x7e, 0xec, 0x92, 0x29, 0x49, 0x82, 0x97, 0x4c, 0x49, 0x30, 0x1b, 0x23, + 0x0c, 0x5c, 0xdf, 0xb0, 0x9a, 0x33, 0xba, 0xd9, 0xea, 0x3a, 0x54, 0xf9, 0x53, 0xd1, 0x31, 0x12, + 0x43, 0xb3, 0x31, 0x12, 0x03, 0xb1, 0x05, 0x9a, 0x81, 0x2a, 0xae, 0x6b, 0xae, 0x5a, 0x62, 0x5f, + 0xd9, 0x6d, 0x79, 0xca, 0xbf, 0x17, 0x5d, 0xa0, 0xd3, 0x68, 0xd8, 0x02, 0x9d, 0x06, 0xc7, 0x53, + 0x27, 0xd6, 0x0b, 0x6c, 0xf1, 0x90, 0xef, 0x2a, 0xff, 0xfd, 0xd8, 0xa9, 0x53, 0x0a, 0x0d, 0x9e, + 0x3a, 0xa5, 0xc0, 0xd9, 0xfa, 0xc8, 0x6d, 0xb2, 0x39, 0x33, 0xb8, 0xab, 0xfe, 0x0f, 0xa2, 0xeb, + 0x63, 0x1c, 0xcf, 0xd6, 0xc7, 0x38, 0x2c, 0xca, 0x47, 0x74, 0xc1, 0x7f, 0x98, 0xc5, 0x27, 0x90, + 0x7f, 0xa2, 0x0c, 0xb9, 0x29, 0xf3, 0x11, 0x23, 0xe5, 0x07, 0x73, 0x59, 0x8c, 0x82, 0xe1, 0x91, + 0x28, 0x14, 0x65, 0xa4, 0xd1, 0x87, 0x26, 0x5d, 0x57, 0xbe, 0x90, 0xc9, 0x88, 0x13, 0x44, 0x19, + 0x71, 0x18, 0x79, 0x17, 0xce, 0x86, 0xb0, 0x79, 0xda, 0x5e, 0x09, 0x66, 0xa6, 0x3f, 0x9d, 0x8b, + 0x9a, 0xc1, 0xe9, 0x64, 0xcc, 0x0c, 0x4e, 0xc7, 0xa4, 0xb1, 0x16, 0xa2, 0xfb, 0x33, 0xdb, 0xb0, + 0x0e, 0x24, 0x98, 0xc1, 0x20, 0x8d, 0xb5, 0x90, 0xe6, 0x0f, 0x6d, 0xc3, 0x3a, 0x90, 0x69, 0x06, + 0x03, 0xf2, 0xa3, 0x39, 0xb8, 0x9c, 0x8e, 0xaa, 0xb4, 0x5a, 0x33, 0xb6, 0x13, 0xe2, 0x94, 0x3f, + 0x9b, 0x8b, 0x1e, 0x34, 0xec, 0xac, 0xd8, 0x6c, 0x9f, 0xb6, 0xc3, 0x0a, 0xc8, 0x27, 0x61, 0xb4, + 0xd2, 0x35, 0x4c, 0x0f, 0x2f, 0xde, 0x98, 0xe1, 0xfc, 0xc3, 0xb9, 0xd8, 0x16, 0x47, 0xc6, 0xe2, + 0x16, 0x47, 0x06, 0x90, 0x5b, 0x70, 0xb2, 0x4e, 0x9b, 0x5d, 0xc7, 0xf4, 0x36, 0x34, 0xda, 0xb1, + 0x1d, 0x8f, 0xf1, 0xf8, 0x73, 0xb9, 0xe8, 0x24, 0x96, 0xa0, 0x60, 0x93, 0x58, 0x02, 0x48, 0xee, + 0x26, 0x6e, 0xe5, 0x45, 0x67, 0xfe, 0x48, 0xae, 0xe7, 0xb5, 0x7c, 0xd0, 0x97, 0xe9, 0xc5, 0xc9, + 0x52, 0xec, 0x16, 0x5d, 0x70, 0xfd, 0xd1, 0x5c, 0x8f, 0x6b, 0x74, 0x69, 0x86, 0x4b, 0x82, 0x19, + 0xc7, 0x94, 0x94, 0xf9, 0xca, 0x9f, 0xcf, 0xf5, 0xb8, 0xf6, 0x0e, 0x39, 0xa6, 0x65, 0xdb, 0x7f, + 0x99, 0x7b, 0x8a, 0x08, 0x46, 0x7f, 0x21, 0x97, 0x74, 0x15, 0x09, 0xca, 0x4b, 0x84, 0xac, 0xd8, + 0x1d, 0x37, 0x50, 0xfa, 0x2f, 0xe6, 0x92, 0xbe, 0x79, 0x61, 0xb1, 0xf0, 0x17, 0xa1, 0x70, 0x61, + 0xfa, 0x91, 0x47, 0x1d, 0x4b, 0x6f, 0x61, 0x77, 0xd6, 0x3d, 0xdb, 0xd1, 0x57, 0xe9, 0xb4, 0xa5, + 0xaf, 0xb4, 0xa8, 0xf2, 0x63, 0xb9, 0xa8, 0x05, 0x9b, 0x4d, 0xca, 0x2c, 0xd8, 0x6c, 0x2c, 0x59, + 0x83, 0x27, 0xd2, 0xb0, 0x55, 0xd3, 0xc5, 0x7a, 0xbe, 0x94, 0x8b, 0x9a, 0xb0, 0x3d, 0x68, 0x99, + 0x09, 0xdb, 0x03, 0x4d, 0xae, 0xc3, 0xd0, 0xa4, 0xed, 0x4f, 0xbf, 0x7f, 0x31, 0xe6, 0x0c, 0x19, + 0x60, 0x66, 0xfb, 0xb4, 0x90, 0x4c, 0x94, 0x11, 0x83, 0xfa, 0xcb, 0xc9, 0x32, 0xe1, 0xe5, 0x53, + 0xf0, 0x43, 0x94, 0x11, 0xe2, 0xfe, 0x8f, 0x92, 0x65, 0xc2, 0x3b, 0xae, 0xe0, 0x07, 0x9b, 0x49, + 0x78, 0x8d, 0xf3, 0x33, 0x15, 0x66, 0xb7, 0x4d, 0xad, 0xe9, 0xad, 0x16, 0xb5, 0x56, 0xa9, 0xf2, + 0x95, 0xd8, 0x4c, 0x92, 0x4e, 0xc6, 0x66, 0x92, 0x74, 0x0c, 0xf9, 0x3e, 0x38, 0x77, 0x57, 0x6f, + 0x99, 0x46, 0x88, 0xf3, 0x13, 0xa8, 0x2b, 0x3f, 0x9e, 0x8b, 0xee, 0xa6, 0x33, 0xe8, 0xd8, 0x6e, + 0x3a, 0x03, 0x45, 0xe6, 0x81, 0xe0, 0x32, 0x1a, 0xcc, 0x16, 0x6c, 0x7d, 0x56, 0xfe, 0xe3, 0x5c, + 0xd4, 0x4e, 0x4d, 0x92, 0x30, 0x3b, 0x35, 0x09, 0x25, 0x8d, 0xec, 0x44, 0x26, 0xca, 0x4f, 0xe4, + 0xa2, 0xa7, 0x35, 0x59, 0x84, 0xb3, 0x7d, 0x5a, 0x76, 0x36, 0x94, 0x9b, 0x30, 0x5e, 0x5f, 0xaa, + 0xcd, 0xcc, 0x4c, 0xd7, 0xef, 0xd6, 0xaa, 0xf8, 0x8a, 0xc2, 0x50, 0x7e, 0x32, 0xb6, 0x62, 0xc5, + 0x09, 0xd8, 0x8a, 0x15, 0x87, 0x91, 0x37, 0x60, 0x84, 0xb5, 0x9f, 0x0d, 0x18, 0xfc, 0xe4, 0xaf, + 0xe6, 0xa2, 0xe6, 0x94, 0x8c, 0x64, 0xe6, 0x94, 0xfc, 0x9b, 0xd4, 0xe1, 0x34, 0x93, 0xe2, 0x92, + 0x43, 0xef, 0x53, 0x87, 0x5a, 0x4d, 0x7f, 0x4c, 0xff, 0x54, 0x2e, 0x6a, 0x65, 0xa4, 0x11, 0x31, + 0x2b, 0x23, 0x0d, 0x4e, 0x1e, 0xc0, 0xc5, 0xf8, 0x49, 0x90, 0xfc, 0x48, 0x56, 0xf9, 0x4b, 0xb9, + 0x98, 0x31, 0xdc, 0x83, 0x18, 0x8d, 0xe1, 0x1e, 0x78, 0x62, 0xc1, 0x25, 0x71, 0xac, 0x22, 0x1c, + 0x2e, 0xe3, 0xb5, 0xfd, 0x34, 0xaf, 0xed, 0xd9, 0xd0, 0x21, 0xb0, 0x07, 0xf5, 0x6c, 0x9f, 0xd6, + 0x9b, 0x1d, 0xd3, 0xb3, 0x64, 0xba, 0x0e, 0xe5, 0x2f, 0xe7, 0xd2, 0x3d, 0x52, 0x22, 0x6e, 0xca, + 0x69, 0x79, 0x3e, 0xde, 0xcd, 0x4a, 0x36, 0xa1, 0xfc, 0x95, 0xd8, 0x78, 0x4b, 0x27, 0x63, 0xe3, + 0x2d, 0x23, 0x5b, 0xc5, 0x2d, 0x38, 0xc9, 0x95, 0x7a, 0x49, 0xc7, 0x61, 0x68, 0xad, 0x52, 0x43, + 0xf9, 0xab, 0xb1, 0xd5, 0x2e, 0x41, 0x81, 0xae, 0x3d, 0x71, 0x20, 0x9b, 0xba, 0xeb, 0x1d, 0xdd, + 0xb2, 0xf0, 0x98, 0x55, 0xf9, 0x4f, 0x62, 0x53, 0x77, 0x88, 0x42, 0xc7, 0xdd, 0xe0, 0x17, 0xd3, + 0x84, 0x5e, 0x89, 0x9a, 0x94, 0xff, 0x34, 0xa6, 0x09, 0xbd, 0x88, 0x99, 0x26, 0xf4, 0xcc, 0xfa, + 0x74, 0x37, 0xe3, 0xc1, 0xba, 0xf2, 0xb5, 0xd8, 0x8a, 0x9c, 0x4a, 0xc5, 0x56, 0xe4, 0xf4, 0xf7, + 0xee, 0x77, 0x33, 0x1e, 0x7b, 0x2b, 0x3f, 0xd3, 0x9b, 0x6f, 0xb8, 0xd2, 0xa7, 0xbf, 0x15, 0xbf, + 0x9b, 0xf1, 0x50, 0x5a, 0xf9, 0x6b, 0xbd, 0xf9, 0x86, 0x8e, 0x7d, 0xe9, 0xef, 0xac, 0x1b, 0xd9, + 0x8f, 0x8c, 0x95, 0xbf, 0x1e, 0x9f, 0xba, 0x32, 0x08, 0x71, 0xea, 0xca, 0x7a, 0xa9, 0xbc, 0x02, + 0xe7, 0xb9, 0x86, 0xdc, 0x74, 0xf4, 0xce, 0x5a, 0x9d, 0x7a, 0x9e, 0x69, 0xad, 0xfa, 0x3b, 0xb1, + 0xff, 0x2c, 0x17, 0x3b, 0x1e, 0xcb, 0xa2, 0xc4, 0xe3, 0xb1, 0x2c, 0x24, 0x53, 0xde, 0xc4, 0x73, + 0x62, 0xe5, 0x6f, 0xc4, 0x94, 0x37, 0x41, 0xc1, 0x94, 0x37, 0xf9, 0x0a, 0xf9, 0x56, 0xca, 0xab, + 0x59, 0xe5, 0x3f, 0xcf, 0xe6, 0x15, 0xb4, 0x2f, 0xe5, 0xb1, 0xed, 0xad, 0x94, 0xc7, 0xa1, 0xca, + 0x7f, 0x91, 0xcd, 0x2b, 0xf4, 0x41, 0x4a, 0xbe, 0x29, 0x7d, 0x17, 0xce, 0xf2, 0xd9, 0x7c, 0x86, + 0x1a, 0x34, 0xf2, 0xa1, 0x3f, 0x1b, 0x1b, 0xfb, 0xe9, 0x64, 0x78, 0xe4, 0x9e, 0x8a, 0x49, 0x63, + 0x2d, 0xda, 0xfa, 0x37, 0xb7, 0x61, 0x1d, 0x6e, 0x08, 0xd2, 0x31, 0x6c, 0xbd, 0x91, 0x1f, 0xbf, + 0x29, 0x3f, 0x17, 0x5b, 0x6f, 0x64, 0x24, 0xba, 0x73, 0xc8, 0x2f, 0xe5, 0xde, 0x88, 0x3e, 0xf4, + 0x52, 0xfe, 0x56, 0x6a, 0xe1, 0xa0, 0x03, 0xa2, 0xaf, 0xc2, 0xde, 0x88, 0x3e, 0x6a, 0x52, 0x7e, + 0x3e, 0xb5, 0x70, 0xf0, 0x01, 0xd1, 0x17, 0x50, 0x6c, 0x8b, 0xd4, 0xf5, 0x6c, 0xce, 0x2a, 0x32, + 0x3d, 0xfc, 0xed, 0xf8, 0x16, 0x29, 0x95, 0x0c, 0xb7, 0x48, 0xa9, 0x98, 0x34, 0xd6, 0xe2, 0xf3, + 0x7e, 0x61, 0x1b, 0xd6, 0xd2, 0xc6, 0x2e, 0x15, 0x93, 0xc6, 0x5a, 0x7c, 0xfc, 0xd7, 0xb7, 0x61, + 0x2d, 0x6d, 0xec, 0x52, 0x31, 0xcc, 0x1c, 0x0b, 0x31, 0x77, 0xa9, 0xe3, 0x86, 0xea, 0xf7, 0x5f, + 0xc6, 0xcc, 0xb1, 0x0c, 0x3a, 0x66, 0x8e, 0x65, 0xa0, 0x52, 0xb9, 0x0b, 0xa1, 0xfc, 0xe2, 0x76, + 0xdc, 0xc3, 0x7b, 0x99, 0x0c, 0x54, 0x2a, 0x77, 0x21, 0x97, 0xbf, 0xb3, 0x1d, 0xf7, 0xf0, 0x62, + 0x26, 0x03, 0xc5, 0x8c, 0xa2, 0xba, 0xa7, 0x7b, 0x66, 0x73, 0xd6, 0x76, 0x3d, 0x69, 0x91, 0xff, + 0xaf, 0x62, 0x46, 0x51, 0x1a, 0x11, 0x33, 0x8a, 0xd2, 0xe0, 0x49, 0xa6, 0x42, 0x1a, 0xbf, 0xd4, + 0x93, 0x69, 0x68, 0x69, 0xa5, 0xc1, 0x93, 0x4c, 0x85, 0x10, 0xfe, 0xeb, 0x9e, 0x4c, 0x43, 0x4f, + 0xf9, 0x34, 0x38, 0xb3, 0x4c, 0xa7, 0x1c, 0x7b, 0xdd, 0xba, 0x45, 0xd7, 0x69, 0x4b, 0x7c, 0xfa, + 0x2f, 0xc7, 0x2c, 0xd3, 0x38, 0x01, 0xde, 0xa2, 0xc4, 0x60, 0x51, 0x46, 0xe2, 0x73, 0x7f, 0x25, + 0x93, 0x51, 0x78, 0x4c, 0x14, 0x87, 0x45, 0x19, 0x89, 0x4f, 0xfc, 0xd5, 0x4c, 0x46, 0xe1, 0x31, + 0x51, 0x1c, 0x46, 0x2a, 0x30, 0x86, 0x6f, 0x25, 0x74, 0xd7, 0xf7, 0xfc, 0xfc, 0x8d, 0x5c, 0xf4, + 0xd6, 0x2b, 0x8a, 0x9e, 0xed, 0xd3, 0x62, 0x05, 0x64, 0x16, 0xe2, 0x93, 0xbe, 0x99, 0xc1, 0x22, + 0xf4, 0x77, 0x8c, 0x42, 0x64, 0x16, 0xe2, 0x63, 0xfe, 0x9b, 0x0c, 0x16, 0xa1, 0xc3, 0x63, 0x14, + 0x42, 0x3e, 0x01, 0xc3, 0xf5, 0x99, 0xe5, 0x25, 0x3f, 0x99, 0xe0, 0xdf, 0xcd, 0xc5, 0x5e, 0x15, + 0x85, 0x38, 0x7c, 0x55, 0x14, 0xfe, 0x24, 0x9f, 0x84, 0xd1, 0x29, 0xdb, 0xf2, 0xf4, 0xa6, 0xbf, + 0x01, 0xfd, 0xcd, 0xd8, 0x19, 0x4a, 0x04, 0x3b, 0xdb, 0xa7, 0x45, 0xc9, 0xa5, 0xf2, 0xa2, 0xed, + 0xbf, 0x95, 0x5e, 0x3e, 0x68, 0x7a, 0x94, 0x9c, 0xcd, 0x68, 0xf7, 0x6c, 0xe7, 0x41, 0xcb, 0xd6, + 0x0d, 0x3f, 0x7e, 0xa5, 0x68, 0xc8, 0xdf, 0x8b, 0xcd, 0x68, 0xe9, 0x64, 0x6c, 0x46, 0x4b, 0xc7, + 0xa4, 0xb1, 0x16, 0x5d, 0xf4, 0xad, 0x6d, 0x58, 0x87, 0xf3, 0x70, 0x3a, 0x26, 0x8d, 0xb5, 0xf8, + 0xfc, 0xbf, 0xbf, 0x0d, 0xeb, 0x70, 0x1e, 0x4e, 0xc7, 0x30, 0xd3, 0xfa, 0xa6, 0xe9, 0xf9, 0x0f, + 0xdb, 0xfe, 0x41, 0xcc, 0xb4, 0x0e, 0x51, 0xcc, 0xb4, 0x0e, 0x7f, 0x11, 0x0a, 0x17, 0x82, 0xa7, + 0x92, 0xe1, 0xde, 0xb5, 0x66, 0x3d, 0x64, 0xfb, 0x63, 0xe5, 0x1f, 0xc6, 0x4e, 0x45, 0xb2, 0x49, + 0x67, 0xfb, 0xb4, 0x1e, 0x8c, 0xc8, 0x52, 0xcc, 0x4f, 0x91, 0x47, 0xc8, 0x53, 0xfe, 0x51, 0xae, + 0x87, 0xa3, 0x22, 0xa7, 0x49, 0x38, 0x2a, 0x72, 0xf0, 0xe4, 0x00, 0xf4, 0xe3, 0x59, 0xfe, 0xad, + 0xd2, 0xe0, 0x37, 0x72, 0xe3, 0xbf, 0x96, 0xbb, 0x55, 0x1a, 0xfc, 0xb5, 0xdc, 0xf8, 0xaf, 0xb3, + 0xff, 0x7f, 0x3d, 0x37, 0xfe, 0x1b, 0x39, 0xed, 0x7c, 0x38, 0x2f, 0x57, 0x56, 0xa9, 0xe5, 0x2d, + 0xb5, 0x74, 0xb1, 0xaa, 0xa4, 0xa2, 0xf8, 0xcf, 0x54, 0x94, 0xc8, 0x1c, 0xf7, 0xb5, 0x1c, 0x8c, + 0xd4, 0x3d, 0x87, 0xea, 0x6d, 0x11, 0x88, 0xf1, 0x02, 0x0c, 0x72, 0xef, 0x7b, 0x3f, 0x0e, 0x81, + 0x16, 0xfc, 0x26, 0x97, 0x61, 0x6c, 0x4e, 0x77, 0x3d, 0x6c, 0x62, 0xcd, 0x32, 0xe8, 0x23, 0x7c, + 0x79, 0x5a, 0xd0, 0x62, 0x50, 0x32, 0xc7, 0xe9, 0x78, 0x39, 0x8c, 0xbd, 0x5b, 0xd8, 0x36, 0xfe, + 0xe0, 0xe0, 0xb7, 0x37, 0xcb, 0x7d, 0x18, 0x6e, 0x30, 0x56, 0x56, 0xfd, 0xdd, 0x1c, 0x24, 0xde, + 0x05, 0xec, 0x3d, 0xe0, 0xc8, 0x22, 0x9c, 0x88, 0xc5, 0x7b, 0x16, 0xcf, 0x67, 0x77, 0x18, 0x0e, + 0x3a, 0x5e, 0x9a, 0x7c, 0x34, 0x78, 0xb6, 0x79, 0x47, 0x9b, 0x13, 0xb1, 0x25, 0x31, 0x2b, 0x4a, + 0xd7, 0x69, 0x69, 0x12, 0x4a, 0xc4, 0x0e, 0xfb, 0xee, 0x78, 0x18, 0xcc, 0x96, 0x5c, 0x16, 0xd1, + 0x4f, 0x72, 0x61, 0x44, 0xca, 0xae, 0x4b, 0x1d, 0x39, 0x22, 0x25, 0x46, 0x3b, 0xf9, 0x24, 0x8c, + 0xd4, 0xda, 0x1d, 0xea, 0xb8, 0xb6, 0xa5, 0x7b, 0xb6, 0x23, 0x62, 0x3d, 0x60, 0x54, 0x06, 0x53, + 0x82, 0xcb, 0x51, 0x19, 0x64, 0x7a, 0x72, 0xd5, 0x4f, 0xec, 0x58, 0xc0, 0x30, 0xc2, 0xf8, 0xa6, + 0x1a, 0x13, 0x3b, 0x4a, 0x25, 0x38, 0x05, 0x23, 0xbd, 0xe3, 0xea, 0xf8, 0xc0, 0x37, 0x20, 0xed, + 0x32, 0x80, 0x4c, 0x8a, 0x14, 0xe4, 0x79, 0x28, 0xe1, 0xc8, 0x70, 0x31, 0x61, 0xab, 0x88, 0x93, + 0xd9, 0x42, 0x88, 0x1c, 0x91, 0x83, 0xd3, 0x90, 0xdb, 0x30, 0x1e, 0x7a, 0x7b, 0xdc, 0x74, 0xec, + 0x6e, 0xc7, 0x4f, 0xd1, 0x54, 0xde, 0xda, 0x2c, 0x3f, 0xf1, 0x20, 0xc0, 0x35, 0x56, 0x11, 0x29, + 0xb1, 0x48, 0x14, 0x24, 0xb3, 0x70, 0x22, 0x84, 0x31, 0x11, 0xf9, 0xa9, 0xe1, 0x30, 0x2d, 0xaf, + 0xc4, 0x8b, 0x89, 0x33, 0x92, 0x96, 0x37, 0x56, 0x8c, 0xd4, 0x60, 0xc0, 0x0f, 0x92, 0x39, 0xb8, + 0xad, 0x92, 0x9e, 0x12, 0x41, 0x32, 0x07, 0xe4, 0xf0, 0x98, 0x7e, 0x79, 0x32, 0x03, 0x63, 0x9a, + 0xdd, 0xf5, 0xe8, 0xb2, 0x2d, 0x8e, 0x49, 0x44, 0x30, 0x56, 0x6c, 0x93, 0xc3, 0x30, 0x0d, 0xcf, + 0x6e, 0x34, 0x39, 0x4e, 0x6a, 0x53, 0xac, 0x14, 0x59, 0x80, 0x93, 0x09, 0xbf, 0x18, 0x7c, 0x0f, + 0x3c, 0xc4, 0xc3, 0x1d, 0x4a, 0x9f, 0x97, 0x64, 0x96, 0x2c, 0x4a, 0x7e, 0x38, 0x07, 0xa5, 0x65, + 0x47, 0x37, 0x3d, 0x57, 0xbc, 0x0d, 0x3e, 0x33, 0xb1, 0xee, 0xe8, 0x1d, 0xa6, 0x1f, 0x13, 0x18, + 0x27, 0xfa, 0xae, 0xde, 0xea, 0x52, 0x77, 0xf2, 0x1e, 0xfb, 0xba, 0xff, 0x71, 0xb3, 0xfc, 0x06, + 0x8f, 0xaa, 0x32, 0xd1, 0xb4, 0xdb, 0xd7, 0x56, 0x1d, 0xfd, 0xa1, 0xe9, 0xe1, 0x1e, 0x47, 0x6f, + 0x5d, 0xf3, 0x68, 0x0b, 0x0f, 0xf9, 0xaf, 0xe9, 0x1d, 0xf3, 0x1a, 0xe6, 0x23, 0xb8, 0x16, 0x70, + 0xe2, 0x35, 0x30, 0x15, 0xf0, 0xf0, 0x2f, 0x59, 0x05, 0x38, 0x8e, 0x2c, 0x00, 0x88, 0x4f, 0xad, + 0x74, 0x3a, 0xe2, 0xa1, 0xb1, 0x74, 0x34, 0xee, 0x63, 0xb8, 0x62, 0x07, 0x02, 0xd3, 0x3b, 0x52, + 0x0c, 0x6e, 0x4d, 0xe2, 0xc0, 0xb4, 0x60, 0x59, 0xb4, 0xc8, 0x17, 0xd3, 0x68, 0x28, 0x71, 0xbf, + 0xb1, 0x29, 0x42, 0x8a, 0x17, 0x23, 0x2b, 0x70, 0x42, 0xf0, 0x0d, 0x32, 0xf6, 0x8c, 0x45, 0x67, + 0x85, 0x18, 0x9a, 0x2b, 0x6d, 0xd0, 0x46, 0x43, 0x80, 0xe5, 0x3a, 0x62, 0x25, 0xc8, 0x64, 0x98, + 0x61, 0x7c, 0x41, 0x6f, 0x53, 0x57, 0x39, 0x81, 0x1a, 0x7b, 0x71, 0x6b, 0xb3, 0xac, 0xf8, 0xe5, + 0x31, 0x5e, 0xac, 0x2c, 0xba, 0x68, 0x11, 0x99, 0x07, 0xd7, 0xfa, 0xf1, 0x14, 0x1e, 0x71, 0x9d, + 0x8f, 0x16, 0x21, 0x53, 0x30, 0x1a, 0xbc, 0x73, 0xba, 0x73, 0xa7, 0x56, 0xc5, 0x97, 0xcc, 0x22, + 0x64, 0x70, 0x2c, 0xa7, 0x8e, 0xcc, 0x24, 0x52, 0x46, 0x8a, 0x3c, 0xc3, 0x9f, 0x36, 0xc7, 0x22, + 0xcf, 0x74, 0x52, 0x22, 0xcf, 0x2c, 0x91, 0xb7, 0x60, 0xb8, 0x72, 0xaf, 0x2e, 0x22, 0xea, 0xb8, + 0xca, 0xa9, 0x30, 0x41, 0x9b, 0xbe, 0xee, 0x36, 0xfc, 0xe8, 0x3b, 0x72, 0xd3, 0x65, 0x7a, 0x32, + 0x0d, 0x63, 0x91, 0x45, 0xd3, 0x55, 0x4e, 0x23, 0x07, 0x6c, 0xb9, 0x8e, 0x98, 0x86, 0x23, 0x50, + 0xf2, 0xf0, 0x8a, 0x16, 0x62, 0x5a, 0x53, 0x35, 0x5d, 0x4c, 0x76, 0xa5, 0x51, 0x0c, 0xde, 0x83, + 0xef, 0xa2, 0x07, 0xb9, 0xd6, 0x18, 0x02, 0xd5, 0x70, 0x38, 0x4e, 0xee, 0xd1, 0x58, 0x31, 0xf2, + 0x1e, 0x10, 0x4c, 0x8f, 0x45, 0x0d, 0xff, 0xe6, 0xbc, 0x56, 0x75, 0x95, 0xb3, 0x18, 0x2f, 0x9f, + 0xc4, 0xe3, 0x79, 0xd4, 0xaa, 0x93, 0x97, 0xc5, 0xf4, 0xf1, 0xa4, 0xce, 0x4b, 0x35, 0xfc, 0x58, + 0x1e, 0x0d, 0x33, 0x92, 0x3b, 0x3c, 0x85, 0x2b, 0x59, 0x87, 0x73, 0x4b, 0x0e, 0x7d, 0x68, 0xda, + 0x5d, 0xd7, 0x5f, 0x3e, 0xfc, 0x79, 0xeb, 0xdc, 0xb6, 0xf3, 0xd6, 0xd3, 0xa2, 0xe2, 0x33, 0x1d, + 0x87, 0x3e, 0x6c, 0xf8, 0x51, 0xd2, 0x23, 0x41, 0x7e, 0xb3, 0xb8, 0x63, 0x06, 0xf4, 0xf7, 0xbb, + 0x0e, 0x15, 0x70, 0x93, 0xba, 0x8a, 0x12, 0x4e, 0xb5, 0x3c, 0xb0, 0x93, 0x19, 0xe0, 0x22, 0x19, + 0xd0, 0xa3, 0xc5, 0x88, 0x06, 0xe4, 0xe6, 0x94, 0xef, 0x45, 0x51, 0x69, 0xf2, 0x3c, 0xd1, 0xca, + 0x79, 0x64, 0xa6, 0x32, 0xb1, 0xac, 0x36, 0x83, 0x8c, 0x09, 0x0d, 0x5d, 0xe0, 0x65, 0xb1, 0x24, + 0x4b, 0x93, 0x39, 0x18, 0x5f, 0x72, 0xf0, 0x4c, 0xf7, 0x36, 0xdd, 0x58, 0xb2, 0x5b, 0x66, 0x73, + 0x03, 0x9f, 0x67, 0x8b, 0xa9, 0xb2, 0xc3, 0x71, 0x8d, 0x07, 0x74, 0xa3, 0xd1, 0x41, 0xac, 0xbc, + 0xac, 0xc4, 0x4b, 0xca, 0x11, 0xcc, 0x9f, 0xd8, 0x59, 0x04, 0x73, 0x0a, 0xe3, 0xc2, 0x07, 0xe3, + 0x91, 0x47, 0x2d, 0xb6, 0xd4, 0xbb, 0xe2, 0x29, 0xb6, 0x12, 0xf3, 0xd9, 0x08, 0xf0, 0x7c, 0xea, + 0x10, 0xa3, 0x8c, 0x06, 0x60, 0xb9, 0x61, 0xf1, 0x22, 0xc9, 0x30, 0xdf, 0x97, 0xf6, 0x10, 0xe6, + 0xfb, 0xff, 0x2c, 0xc8, 0xf3, 0x2f, 0xb9, 0x08, 0x45, 0x29, 0x0b, 0x17, 0xc6, 0x30, 0xc6, 0x8c, + 0x05, 0x45, 0x11, 0x9a, 0x7d, 0x48, 0xd8, 0x2e, 0x41, 0x6c, 0x29, 0x4c, 0xbb, 0x1a, 0xc6, 0xb5, + 0xd5, 0x42, 0x02, 0x4c, 0x79, 0xd9, 0x5d, 0x69, 0x99, 0x4d, 0xcc, 0x63, 0x51, 0x90, 0xe2, 0xbd, + 0x20, 0x94, 0xa7, 0xb1, 0x90, 0x48, 0xc8, 0x75, 0x18, 0xf6, 0xef, 0x12, 0xc2, 0x18, 0xde, 0x98, + 0xde, 0x40, 0xcc, 0xd6, 0x22, 0x7b, 0x82, 0x44, 0x44, 0x5e, 0x07, 0x08, 0xa7, 0x03, 0x61, 0x69, + 0xe1, 0x52, 0x21, 0xcf, 0x1e, 0xf2, 0x52, 0x11, 0x52, 0xb3, 0x89, 0x53, 0x56, 0x47, 0x3f, 0xc9, + 0x2f, 0x4e, 0x9c, 0x11, 0x1d, 0x96, 0x15, 0x24, 0x5a, 0x84, 0x2c, 0xc2, 0xc9, 0x84, 0x06, 0x8a, + 0x88, 0xdf, 0x4f, 0x6f, 0x6d, 0x96, 0x2f, 0xa5, 0xa8, 0xaf, 0xbc, 0x30, 0x27, 0xca, 0x92, 0x67, + 0xa0, 0x70, 0x47, 0xab, 0x89, 0xa8, 0xc3, 0x3c, 0x60, 0x75, 0x24, 0x82, 0x18, 0xc3, 0x92, 0xd7, + 0x00, 0x78, 0x56, 0x9f, 0x25, 0xdb, 0xf1, 0xd0, 0xa2, 0x18, 0x9d, 0x3c, 0xcf, 0xc6, 0x32, 0xcf, + 0xfa, 0xd3, 0x60, 0xcb, 0x98, 0xfc, 0xd1, 0x21, 0xb1, 0xfa, 0xa7, 0xf3, 0x89, 0x65, 0x8d, 0x09, + 0x5e, 0xb4, 0x42, 0xea, 0x7c, 0x14, 0xbc, 0xdf, 0x74, 0x2e, 0x78, 0x89, 0x88, 0x5c, 0x81, 0xc1, + 0x25, 0x36, 0xa9, 0x34, 0xed, 0x96, 0x50, 0x05, 0x0c, 0x3d, 0xd7, 0x11, 0x30, 0x2d, 0xc0, 0x92, + 0xeb, 0x52, 0x5a, 0x6b, 0x29, 0x07, 0x80, 0x9f, 0xd6, 0x3a, 0x1e, 0x0c, 0x1f, 0x13, 0x5c, 0x5f, + 0x8f, 0xa5, 0xc9, 0x13, 0x65, 0x52, 0x96, 0xd4, 0x30, 0x2d, 0x5e, 0x60, 0xd0, 0xf6, 0x6f, 0x67, + 0xd0, 0xaa, 0xbf, 0x99, 0x4b, 0x0e, 0x51, 0x72, 0x23, 0x19, 0x8e, 0x1b, 0xd7, 0xaf, 0x00, 0x28, + 0xd7, 0x1a, 0x04, 0xe6, 0x8e, 0x04, 0xd6, 0xce, 0xef, 0x39, 0xb0, 0x76, 0x61, 0x97, 0x81, 0xb5, + 0xd5, 0xff, 0xaf, 0xd8, 0xf3, 0xb9, 0xc1, 0xa1, 0x04, 0x60, 0x7c, 0x8d, 0x6d, 0xca, 0x58, 0xed, + 0x15, 0x37, 0xb1, 0xb5, 0xe0, 0xde, 0xd4, 0x0d, 0x9d, 0x8f, 0x4a, 0x57, 0x8b, 0x52, 0x92, 0xb7, + 0x61, 0xc4, 0xff, 0x00, 0x0c, 0xd8, 0x2e, 0x05, 0x1a, 0x0f, 0x16, 0xc4, 0x58, 0x68, 0xf3, 0x48, + 0x01, 0xf2, 0x32, 0x0c, 0xa1, 0x39, 0xd4, 0xd1, 0x9b, 0x7e, 0x34, 0x7f, 0x1e, 0xfe, 0xdf, 0x07, + 0xca, 0x41, 0x06, 0x03, 0x4a, 0xf2, 0x39, 0x28, 0x89, 0x94, 0x36, 0x25, 0x5c, 0xa2, 0xaf, 0xed, + 0xe0, 0x7d, 0xc6, 0x84, 0x9c, 0xce, 0x86, 0x6f, 0x70, 0x10, 0x10, 0xd9, 0xe0, 0xf0, 0x4c, 0x36, + 0xcb, 0x70, 0x6a, 0xc9, 0xa1, 0x06, 0xbe, 0x04, 0x9a, 0x7e, 0xd4, 0x71, 0x44, 0xb2, 0x21, 0x3e, + 0x41, 0xe0, 0xfa, 0xd6, 0xf1, 0xd1, 0x6c, 0xe5, 0x15, 0x78, 0x39, 0xa4, 0x78, 0x4a, 0x71, 0x66, + 0xf4, 0xf0, 0x96, 0xdc, 0xa6, 0x1b, 0xeb, 0xb6, 0x63, 0xf0, 0x7c, 0x3c, 0x62, 0xea, 0x17, 0x82, + 0x7e, 0x20, 0x50, 0xb2, 0xd1, 0x13, 0x2d, 0x74, 0xe1, 0x35, 0x18, 0xde, 0x6b, 0x4a, 0x98, 0x5f, + 0xcc, 0x67, 0x3c, 0xdc, 0x7b, 0x7c, 0xb3, 0x72, 0x06, 0xa9, 0xe2, 0xfb, 0x33, 0x52, 0xc5, 0xff, + 0x71, 0x3e, 0xe3, 0x55, 0xe2, 0x63, 0x9d, 0xd2, 0x39, 0x10, 0x46, 0x34, 0xa5, 0x73, 0x98, 0x4d, + 0xdb, 0x34, 0x34, 0x99, 0x28, 0x96, 0xfc, 0xbd, 0xb4, 0x6d, 0xf2, 0xf7, 0x9f, 0x2d, 0xf4, 0x7a, + 0xb5, 0x79, 0x2c, 0xfb, 0xdd, 0xc8, 0xfe, 0x3a, 0x0c, 0x07, 0x92, 0xad, 0x55, 0xd1, 0x5e, 0x1a, + 0x0d, 0x12, 0x50, 0x71, 0x30, 0x96, 0x91, 0x88, 0xc8, 0x55, 0xde, 0xd6, 0xba, 0xf9, 0x3e, 0x4f, + 0x85, 0x32, 0x2a, 0x92, 0x5c, 0xe8, 0x9e, 0xde, 0x70, 0xcd, 0xf7, 0xa9, 0x16, 0xa0, 0x31, 0xca, + 0x6d, 0xda, 0x03, 0xd6, 0xe3, 0x3e, 0xda, 0x79, 0x1f, 0xa5, 0x08, 0x91, 0x3f, 0xda, 0x3d, 0x16, + 0xe2, 0x2e, 0x84, 0xf8, 0x47, 0xf9, 0xd4, 0x27, 0xce, 0xc7, 0x42, 0xdc, 0xcd, 0x6c, 0xf1, 0x3c, + 0x0c, 0x69, 0xf6, 0xba, 0x3b, 0x85, 0x7b, 0x22, 0x3e, 0x57, 0xe0, 0x44, 0xed, 0xd8, 0xeb, 0x6e, + 0x03, 0x77, 0x3b, 0x5a, 0x48, 0xa0, 0x7e, 0x37, 0xdf, 0xe3, 0x11, 0xf8, 0xb1, 0xe0, 0x3f, 0xc8, + 0x25, 0xf2, 0x57, 0xf2, 0x91, 0x47, 0xe6, 0x8f, 0xaf, 0xb0, 0xaf, 0x01, 0xd4, 0x9b, 0x6b, 0xb4, + 0xad, 0x4b, 0xe9, 0xe4, 0xf0, 0xc8, 0xc2, 0x45, 0xa8, 0x48, 0x43, 0x1e, 0x92, 0xa8, 0xdf, 0xc8, + 0xc7, 0x5e, 0xd9, 0x1f, 0xcb, 0x6e, 0xc7, 0xb2, 0x0b, 0xb4, 0x4e, 0x04, 0x0e, 0x38, 0x96, 0xdc, + 0x4e, 0x25, 0xf7, 0x23, 0xf9, 0x58, 0x8c, 0x85, 0xc7, 0x56, 0x76, 0x6c, 0x00, 0x26, 0x63, 0x3f, + 0x3c, 0xb6, 0x9a, 0xf4, 0x3c, 0x0c, 0x09, 0x39, 0x04, 0x4b, 0x05, 0x9f, 0xf7, 0x39, 0x10, 0x0f, + 0x68, 0x03, 0x02, 0xf5, 0xcf, 0xe6, 0x21, 0x1a, 0xfb, 0xe2, 0x31, 0xd5, 0xa1, 0x5f, 0xc9, 0x47, + 0xa3, 0x7e, 0x3c, 0xbe, 0xfa, 0x33, 0x01, 0x50, 0xef, 0xae, 0x34, 0x85, 0x6f, 0x4d, 0xbf, 0x74, + 0xc2, 0x1f, 0x40, 0x35, 0x89, 0x42, 0xfd, 0xb7, 0xf9, 0xd4, 0x50, 0x24, 0x8f, 0xaf, 0x00, 0x5f, + 0xc2, 0x53, 0xf1, 0xa6, 0x15, 0x4e, 0xe4, 0x78, 0x08, 0xc9, 0xc6, 0x5f, 0x22, 0x07, 0xa9, 0x4f, + 0x48, 0x3e, 0x91, 0x62, 0xae, 0x61, 0x86, 0x94, 0xd0, 0x5c, 0x93, 0x0f, 0xf3, 0x25, 0xc3, 0xed, + 0x77, 0xf2, 0xdb, 0x45, 0x6e, 0x79, 0x9c, 0x57, 0xd5, 0x81, 0x25, 0x7d, 0x03, 0x23, 0x8c, 0xb2, + 0x9e, 0x18, 0xe1, 0x19, 0x32, 0x3b, 0x1c, 0x24, 0x5f, 0xdb, 0x09, 0x2a, 0xf5, 0x5f, 0xf6, 0xa7, + 0x87, 0x0d, 0x79, 0x7c, 0x45, 0x78, 0x11, 0x8a, 0x4b, 0xba, 0xb7, 0x26, 0x34, 0x19, 0x6f, 0x03, + 0x3b, 0xba, 0xb7, 0xa6, 0x21, 0x94, 0x5c, 0x85, 0x41, 0x4d, 0x5f, 0xe7, 0x67, 0x9e, 0xa5, 0x30, + 0x7b, 0xa9, 0xa3, 0xaf, 0x37, 0xf8, 0xb9, 0x67, 0x80, 0x26, 0x6a, 0x90, 0x3d, 0x97, 0x9f, 0x7c, + 0x63, 0xea, 0x46, 0x9e, 0x3d, 0x37, 0xc8, 0x99, 0x7b, 0x11, 0x8a, 0x93, 0xb6, 0xb1, 0x81, 0x37, + 0x5f, 0x23, 0xbc, 0xb2, 0x15, 0xdb, 0xd8, 0xd0, 0x10, 0x4a, 0x7e, 0x34, 0x07, 0x03, 0xb3, 0x54, + 0x37, 0xd8, 0x08, 0x19, 0xea, 0xe5, 0xb0, 0xf2, 0xe9, 0x83, 0x71, 0x58, 0x39, 0xb9, 0xc6, 0x2b, + 0x93, 0x15, 0x45, 0xd4, 0x4f, 0x6e, 0xc2, 0xe0, 0x94, 0xee, 0xd1, 0x55, 0xdb, 0xd9, 0x40, 0x17, + 0x9c, 0xb1, 0xf0, 0xe9, 0x49, 0x44, 0x7f, 0x7c, 0x22, 0x7e, 0x33, 0xd6, 0x14, 0xbf, 0xb4, 0xa0, + 0x30, 0x13, 0x0b, 0xbf, 0x99, 0x13, 0x99, 0xe2, 0x51, 0x2c, 0xfc, 0x0a, 0x4f, 0x13, 0x98, 0xf0, + 0x58, 0x79, 0x24, 0xfd, 0x58, 0x19, 0xad, 0x47, 0x74, 0xd3, 0xc3, 0x9c, 0xb5, 0xa3, 0xb8, 0xe8, + 0x73, 0xeb, 0x11, 0xa1, 0x98, 0xb2, 0x56, 0x93, 0x48, 0xd4, 0xef, 0xf4, 0x43, 0x6a, 0x90, 0x81, + 0x63, 0x25, 0x3f, 0x56, 0xf2, 0x50, 0xc9, 0xab, 0x09, 0x25, 0xbf, 0x90, 0x0c, 0x5b, 0xf1, 0x21, + 0xd5, 0xf0, 0xaf, 0x16, 0x13, 0x41, 0x6f, 0x1e, 0xef, 0xdd, 0x65, 0x28, 0xbd, 0xfe, 0x6d, 0xa5, + 0x17, 0x0c, 0x88, 0xd2, 0xb6, 0x03, 0x62, 0x60, 0xa7, 0x03, 0x62, 0x30, 0x73, 0x40, 0x84, 0x0a, + 0x32, 0x94, 0xa9, 0x20, 0x35, 0x31, 0x68, 0xa0, 0x77, 0xee, 0x9d, 0x8b, 0x5b, 0x9b, 0xe5, 0x31, + 0x36, 0x9a, 0x52, 0x93, 0xee, 0x20, 0x0b, 0xf5, 0x77, 0x8b, 0x3d, 0x22, 0x55, 0x1d, 0x8a, 0x8e, + 0xbc, 0x04, 0x85, 0x4a, 0xa7, 0x23, 0xf4, 0xe3, 0x94, 0x14, 0x24, 0x2b, 0xa3, 0x14, 0xa3, 0x26, + 0xaf, 0x43, 0xa1, 0x72, 0xaf, 0x1e, 0xcf, 0xb7, 0x53, 0xb9, 0x57, 0x17, 0x5f, 0x92, 0x59, 0xf6, + 0x5e, 0x9d, 0xbc, 0x19, 0x06, 0xbe, 0x5d, 0xeb, 0x5a, 0x0f, 0xc4, 0x46, 0x51, 0x78, 0xea, 0xfa, + 0x9e, 0x3c, 0x4d, 0x86, 0x62, 0xdb, 0xc5, 0x18, 0x6d, 0x4c, 0x9b, 0x4a, 0x3b, 0xd7, 0xa6, 0x81, + 0x6d, 0xb5, 0x69, 0x70, 0xa7, 0xda, 0x34, 0xb4, 0x03, 0x6d, 0x82, 0x6d, 0xb5, 0x69, 0x78, 0xff, + 0xda, 0xd4, 0x81, 0x0b, 0xc9, 0xe8, 0x82, 0x81, 0x46, 0x68, 0x40, 0x92, 0x58, 0xe1, 0x58, 0x82, + 0x57, 0xff, 0x5d, 0x8e, 0x6d, 0xac, 0x23, 0xba, 0xe1, 0x32, 0xbc, 0xec, 0xda, 0x96, 0x2c, 0xad, + 0xfe, 0x62, 0x3e, 0x3b, 0x28, 0xe2, 0xd1, 0x9c, 0xe2, 0xbe, 0x3f, 0x55, 0x4a, 0xc5, 0xd8, 0x73, + 0x8c, 0x4c, 0x29, 0xc7, 0xd8, 0xa6, 0xc9, 0xec, 0xeb, 0xf9, 0xac, 0x48, 0x8d, 0xfb, 0x92, 0xd8, + 0xb3, 0x49, 0x67, 0x38, 0x74, 0xf1, 0x77, 0xa3, 0x5e, 0x70, 0x33, 0x30, 0x22, 0x0b, 0x51, 0x48, + 0x69, 0x27, 0x02, 0x8e, 0x94, 0x23, 0x6f, 0x06, 0x69, 0x91, 0x24, 0xff, 0x18, 0xf4, 0x74, 0xf3, + 0xc7, 0x6c, 0xcc, 0x3d, 0x46, 0x26, 0x27, 0xcf, 0x43, 0x69, 0x06, 0xf3, 0x0c, 0xc8, 0x83, 0x9d, + 0x67, 0x1e, 0x90, 0xbd, 0x56, 0x38, 0x8d, 0xfa, 0x9b, 0x39, 0x38, 0x75, 0xbb, 0xbb, 0x42, 0x85, + 0xa3, 0x5d, 0xd0, 0x86, 0xf7, 0x00, 0x18, 0x58, 0x38, 0xcc, 0xe4, 0xd0, 0x61, 0xe6, 0x63, 0x72, + 0x44, 0xc7, 0x58, 0x81, 0x89, 0x90, 0x9a, 0x3b, 0xcb, 0x5c, 0xf2, 0x7d, 0x4e, 0x1f, 0x74, 0x57, + 0x68, 0x23, 0xe1, 0x35, 0x23, 0x71, 0xbf, 0xf0, 0x16, 0xf7, 0xe6, 0xdf, 0xab, 0x83, 0xca, 0x2f, + 0xe4, 0x33, 0x83, 0x68, 0x1e, 0xd9, 0xdc, 0xb4, 0x9f, 0x4d, 0xed, 0x95, 0x78, 0x8e, 0xda, 0x14, + 0x92, 0x18, 0xc7, 0x34, 0x2e, 0xe9, 0x02, 0x3b, 0xe2, 0x19, 0x93, 0x3f, 0x50, 0x81, 0xfd, 0x41, + 0x2e, 0x33, 0xd8, 0xe9, 0x51, 0x15, 0x98, 0xfa, 0xbf, 0x15, 0xfc, 0x18, 0xab, 0xfb, 0xfa, 0x84, + 0xe7, 0x61, 0x48, 0x3c, 0xbb, 0x8b, 0xfa, 0x09, 0x8b, 0x63, 0x43, 0x3c, 0x86, 0x0e, 0x08, 0x98, + 0x49, 0x21, 0x39, 0x31, 0x4b, 0x7e, 0xc2, 0x92, 0x03, 0xb3, 0x26, 0x91, 0x30, 0xa3, 0x61, 0xfa, + 0x91, 0xe9, 0xa1, 0x05, 0xc2, 0xfa, 0xb2, 0xc0, 0x8d, 0x06, 0xfa, 0xc8, 0xf4, 0xb8, 0xfd, 0x11, + 0xa0, 0x99, 0x41, 0xc0, 0x6d, 0x11, 0x31, 0xef, 0xa1, 0x41, 0xc0, 0x4d, 0x15, 0x4d, 0x60, 0x58, + 0x6b, 0x85, 0xf3, 0xad, 0x70, 0x69, 0x11, 0xad, 0x15, 0xee, 0xba, 0xd8, 0xda, 0x80, 0x80, 0x71, + 0xd4, 0xe8, 0x6a, 0xe8, 0xc4, 0x87, 0x1c, 0x1d, 0x84, 0x68, 0x02, 0x43, 0xae, 0xc3, 0x58, 0xdd, + 0xd3, 0x2d, 0x43, 0x77, 0x8c, 0xc5, 0xae, 0xd7, 0xe9, 0x7a, 0xb2, 0x01, 0xec, 0x7a, 0x86, 0xdd, + 0xf5, 0xb4, 0x18, 0x05, 0xf9, 0x38, 0x8c, 0xfa, 0x90, 0x69, 0xc7, 0xb1, 0x1d, 0xd9, 0xca, 0x71, + 0x3d, 0x83, 0x3a, 0x8e, 0x16, 0x25, 0x20, 0x9f, 0x80, 0xd1, 0x9a, 0xf5, 0xd0, 0x6e, 0xf2, 0x70, + 0x0b, 0xda, 0x9c, 0xb0, 0x79, 0xf0, 0xc5, 0x98, 0x19, 0x20, 0x1a, 0x5d, 0xa7, 0xa5, 0x45, 0x09, + 0xd5, 0xad, 0x7c, 0x32, 0x14, 0xed, 0xe3, 0xbb, 0x41, 0xba, 0x1a, 0x75, 0xdc, 0x43, 0x6f, 0x55, + 0x34, 0x3e, 0x65, 0xbf, 0x61, 0x6e, 0x83, 0x5e, 0x87, 0xc1, 0xdb, 0x74, 0x83, 0xfb, 0x98, 0x96, + 0x42, 0xb7, 0xe4, 0x07, 0x02, 0x26, 0x9f, 0xee, 0xfa, 0x74, 0xea, 0x37, 0xf3, 0xc9, 0x20, 0xbb, + 0x8f, 0xaf, 0xb0, 0x3f, 0x0e, 0x03, 0x28, 0xca, 0x9a, 0x7f, 0xbd, 0x80, 0x02, 0x44, 0x71, 0x47, + 0xbd, 0x9d, 0x7d, 0x32, 0xf5, 0x67, 0x4a, 0xf1, 0xc8, 0xcb, 0x8f, 0xaf, 0xf4, 0xde, 0x80, 0xe1, + 0x29, 0xdb, 0x72, 0x4d, 0xd7, 0xa3, 0x56, 0xd3, 0x57, 0x58, 0x74, 0xfc, 0x6f, 0x86, 0x60, 0xd9, + 0x06, 0x94, 0xa8, 0xf7, 0xa2, 0xbc, 0xe4, 0x15, 0x18, 0x42, 0x91, 0xa3, 0xcd, 0xc9, 0x27, 0x3c, + 0xbc, 0x99, 0x58, 0x61, 0xc0, 0xb8, 0xc5, 0x19, 0x92, 0x92, 0x3b, 0x30, 0x38, 0xb5, 0x66, 0xb6, + 0x0c, 0x87, 0x5a, 0xe8, 0x9b, 0x2c, 0x05, 0xb8, 0x89, 0xf6, 0xe5, 0x04, 0xfe, 0x8b, 0xb4, 0xbc, + 0x39, 0x4d, 0x51, 0x2c, 0xf2, 0x58, 0x4c, 0xc0, 0x2e, 0xfc, 0x44, 0x1e, 0x20, 0x2c, 0x40, 0x9e, + 0x82, 0x7c, 0x90, 0x19, 0x1d, 0x5d, 0x62, 0x22, 0x1a, 0x94, 0xc7, 0xa5, 0x42, 0x8c, 0xed, 0xfc, + 0xb6, 0x63, 0xfb, 0x0e, 0x94, 0xf8, 0xe9, 0x1a, 0x7a, 0xad, 0x4b, 0xc1, 0x60, 0x33, 0x1b, 0x3c, + 0x81, 0xf4, 0xdc, 0x96, 0x46, 0xcb, 0x33, 0xe2, 0x01, 0xce, 0x99, 0x5d, 0x68, 0x42, 0x3f, 0xfe, + 0x45, 0x2e, 0x43, 0x71, 0xd9, 0x4f, 0x7c, 0x3c, 0xca, 0x67, 0xe9, 0x98, 0xfc, 0x10, 0xcf, 0xba, + 0x69, 0xca, 0xb6, 0x3c, 0x56, 0x35, 0xb6, 0x7a, 0x44, 0xc8, 0x45, 0xc0, 0x22, 0x72, 0x11, 0x30, + 0xf5, 0x1f, 0xe5, 0x53, 0x62, 0x82, 0x3f, 0xbe, 0xc3, 0xe4, 0x35, 0x00, 0x7c, 0x79, 0xce, 0xe4, + 0xe9, 0x3f, 0x07, 0xc1, 0x51, 0x82, 0x8c, 0x50, 0x6d, 0x23, 0xdb, 0x8e, 0x90, 0x58, 0xfd, 0xed, + 0x5c, 0x22, 0x90, 0xf4, 0xbe, 0xe4, 0x28, 0x5b, 0x65, 0xf9, 0x3d, 0x9a, 0xb1, 0x7e, 0x5f, 0x14, + 0x76, 0xd7, 0x17, 0xd1, 0x6f, 0x39, 0x00, 0xcb, 0xf4, 0x30, 0xbf, 0xe5, 0x3b, 0xf9, 0xb4, 0xb0, + 0xda, 0x47, 0x53, 0xc5, 0x6f, 0x04, 0x46, 0x69, 0x31, 0x96, 0xc8, 0x00, 0xa1, 0xf1, 0xe4, 0xec, + 0xc2, 0x4c, 0xfd, 0x3c, 0x9c, 0x88, 0x05, 0x9b, 0x16, 0x79, 0xb2, 0x2f, 0xf7, 0x8e, 0x5a, 0x9d, + 0x1d, 0xb3, 0x20, 0x42, 0xa6, 0xfe, 0xff, 0xb9, 0xde, 0xa1, 0xc6, 0x0f, 0x5d, 0x75, 0x52, 0x04, + 0x50, 0xf8, 0x93, 0x11, 0xc0, 0x01, 0x6c, 0x83, 0x8f, 0xb6, 0x00, 0x3e, 0x24, 0x93, 0xc7, 0x07, + 0x2d, 0x80, 0x9f, 0xc9, 0x6d, 0x1b, 0x29, 0xfe, 0xb0, 0x65, 0xa0, 0xfe, 0xcf, 0xb9, 0xd4, 0x88, + 0xee, 0xfb, 0x6a, 0xd7, 0x9b, 0x50, 0xe2, 0x2e, 0x3c, 0xa2, 0x55, 0x52, 0x0e, 0x3c, 0x06, 0xcd, + 0x28, 0x2f, 0xca, 0x90, 0x39, 0x18, 0xe0, 0x6d, 0x30, 0x44, 0x6f, 0x7c, 0xa4, 0x47, 0x58, 0x79, + 0x23, 0x6b, 0x72, 0x14, 0x68, 0xf5, 0xb7, 0x72, 0x89, 0x00, 0xf3, 0x87, 0xf8, 0x6d, 0xe1, 0x54, + 0x5d, 0xd8, 0xf9, 0x54, 0xad, 0xfe, 0x8b, 0x7c, 0x7a, 0x7c, 0xfb, 0x43, 0xfc, 0x90, 0x83, 0x38, + 0x4e, 0xdb, 0xdb, 0xba, 0xb5, 0x0c, 0x63, 0x51, 0x59, 0x88, 0x65, 0xeb, 0xc9, 0xf4, 0x28, 0xff, + 0x19, 0xad, 0x88, 0xf1, 0x50, 0xbf, 0x9d, 0x4b, 0x86, 0xe6, 0x3f, 0xf4, 0xf9, 0x69, 0x6f, 0xda, + 0x12, 0xfd, 0x94, 0x0f, 0xc9, 0x5a, 0x73, 0x10, 0x9f, 0xf2, 0x21, 0x59, 0x35, 0xf6, 0xf6, 0x29, + 0x3f, 0x97, 0xcf, 0xca, 0x6c, 0x70, 0xe8, 0x1f, 0xf4, 0x19, 0x59, 0xc8, 0xbc, 0x65, 0xe2, 0xd3, + 0x9e, 0xca, 0x4a, 0x25, 0x90, 0xc1, 0x33, 0xc1, 0x67, 0x6f, 0x63, 0x3c, 0x55, 0x58, 0x1f, 0x12, + 0x45, 0x3e, 0x1a, 0xc2, 0xfa, 0x90, 0x0c, 0x95, 0x0f, 0x9f, 0xb0, 0x7e, 0x2d, 0xbf, 0xd3, 0x74, + 0x1a, 0xc7, 0xc2, 0x4b, 0x08, 0xef, 0xcb, 0xf9, 0x64, 0x9a, 0x97, 0x43, 0x17, 0xd3, 0x0c, 0x94, + 0x44, 0xc2, 0x99, 0x4c, 0xe1, 0x70, 0x7c, 0x96, 0x45, 0x23, 0xbe, 0xe3, 0x06, 0x88, 0x8b, 0x9c, + 0x9d, 0x89, 0x84, 0xd3, 0xaa, 0xdf, 0xcd, 0xc5, 0x72, 0xa2, 0x1c, 0xca, 0x11, 0xc2, 0x9e, 0x96, + 0x24, 0xf2, 0x96, 0x7f, 0x98, 0x59, 0x8c, 0xc5, 0xa4, 0x0f, 0xbe, 0xa7, 0x4a, 0x3d, 0xdd, 0x6c, + 0xc5, 0xcb, 0x8b, 0xf8, 0x03, 0xdf, 0xcc, 0xc3, 0xc9, 0x04, 0x29, 0xb9, 0x1c, 0x89, 0xf8, 0x83, + 0xc7, 0x92, 0x31, 0x47, 0x75, 0x1e, 0xfb, 0x67, 0x17, 0x27, 0xa9, 0x97, 0xa1, 0x58, 0xd5, 0x37, + 0xf8, 0xb7, 0xf5, 0x73, 0x96, 0x86, 0xbe, 0x21, 0x9f, 0xb8, 0x21, 0x9e, 0xac, 0xc0, 0x19, 0x7e, + 0x1f, 0x62, 0xda, 0xd6, 0xb2, 0xd9, 0xa6, 0x35, 0x6b, 0xde, 0x6c, 0xb5, 0x4c, 0x57, 0x5c, 0xea, + 0x3d, 0xbf, 0xb5, 0x59, 0xbe, 0xe2, 0xd9, 0x9e, 0xde, 0x6a, 0x50, 0x9f, 0xac, 0xe1, 0x99, 0x6d, + 0xda, 0x30, 0xad, 0x46, 0x1b, 0x29, 0x25, 0x96, 0xe9, 0xac, 0x48, 0x8d, 0xa7, 0x1f, 0xa8, 0x37, + 0x75, 0xcb, 0xa2, 0x46, 0xcd, 0x9a, 0xdc, 0xf0, 0x28, 0xbf, 0x0c, 0x2c, 0xf0, 0x23, 0x41, 0xfe, + 0x0e, 0x9d, 0xa3, 0x19, 0xe3, 0x15, 0x46, 0xa0, 0xa5, 0x14, 0x52, 0x7f, 0xbd, 0x98, 0x92, 0x0e, + 0xe7, 0x08, 0xa9, 0x8f, 0xdf, 0xd3, 0xc5, 0x6d, 0x7a, 0xfa, 0x1a, 0x0c, 0x88, 0xf8, 0xce, 0xe2, + 0x82, 0x01, 0x1d, 0xe7, 0x1f, 0x72, 0x90, 0x7c, 0x43, 0x23, 0xa8, 0x48, 0x0b, 0x2e, 0x2c, 0xb3, + 0x6e, 0x4a, 0xef, 0xcc, 0xd2, 0x1e, 0x3a, 0xb3, 0x07, 0x3f, 0xf2, 0x2e, 0x9c, 0x43, 0x6c, 0x4a, + 0xb7, 0x0e, 0x60, 0x55, 0x18, 0x4a, 0x8b, 0x57, 0x95, 0xde, 0xb9, 0x59, 0xe5, 0xc9, 0x67, 0x60, + 0x24, 0x18, 0x20, 0x26, 0x75, 0xc5, 0xcd, 0x45, 0x8f, 0x71, 0xc6, 0xe3, 0xd4, 0x31, 0x30, 0xba, + 0xab, 0x45, 0x63, 0x9d, 0x45, 0x78, 0xa9, 0xff, 0x53, 0xae, 0x57, 0x5a, 0x9e, 0x43, 0x9f, 0x95, + 0xdf, 0x82, 0x01, 0x83, 0x7f, 0x94, 0xd0, 0xa9, 0xde, 0x89, 0x7b, 0x38, 0xa9, 0xe6, 0x97, 0x51, + 0xff, 0x79, 0xae, 0x67, 0x36, 0xa0, 0xa3, 0xfe, 0x79, 0x5f, 0x2e, 0x64, 0x7c, 0x9e, 0x98, 0x44, + 0xaf, 0xc2, 0xb8, 0x19, 0xa6, 0x2b, 0x68, 0x84, 0xa1, 0xae, 0xb4, 0x13, 0x12, 0x1c, 0x47, 0xd7, + 0x0d, 0x08, 0x1c, 0xb6, 0x1c, 0xdf, 0x1b, 0xcd, 0x6d, 0x74, 0x1d, 0x93, 0x8f, 0x4b, 0xed, 0xb4, + 0x1b, 0x73, 0x55, 0x73, 0xef, 0x38, 0x26, 0xab, 0x40, 0xf7, 0xd6, 0xa8, 0xa5, 0x37, 0xd6, 0x6d, + 0xe7, 0x01, 0x06, 0x43, 0xe5, 0x83, 0x53, 0x3b, 0xc1, 0xe1, 0xf7, 0x7c, 0x30, 0x79, 0x06, 0x46, + 0x57, 0x5b, 0x5d, 0x1a, 0x84, 0x9f, 0xe4, 0x77, 0x7d, 0xda, 0x08, 0x03, 0x06, 0x37, 0x24, 0x97, + 0x00, 0x90, 0xc8, 0xc3, 0x5c, 0x4d, 0x78, 0xb1, 0xa7, 0x0d, 0x31, 0xc8, 0xb2, 0xe8, 0xae, 0x0b, + 0x5c, 0xab, 0xb9, 0x90, 0x1a, 0x2d, 0xdb, 0x5a, 0x6d, 0x78, 0xd4, 0x69, 0x63, 0x43, 0xd1, 0x99, + 0x41, 0x3b, 0x8b, 0x14, 0x78, 0x75, 0xe2, 0xce, 0xd9, 0xd6, 0xea, 0x32, 0x75, 0xda, 0xac, 0xa9, + 0xcf, 0x03, 0x11, 0x4d, 0x75, 0xf0, 0xd0, 0x83, 0x7f, 0x1c, 0x7a, 0x33, 0x68, 0xe2, 0x23, 0xf8, + 0x69, 0x08, 0x7e, 0x58, 0x19, 0x86, 0x79, 0x0c, 0x3e, 0x2e, 0x34, 0x74, 0x61, 0xd0, 0x80, 0x83, + 0x50, 0x5e, 0x67, 0x41, 0x78, 0x57, 0x70, 0x0f, 0x72, 0x4d, 0xfc, 0x52, 0xbf, 0x58, 0x48, 0x4b, + 0x60, 0xb4, 0x2f, 0x45, 0x0b, 0xa7, 0xd5, 0xfc, 0xae, 0xa6, 0xd5, 0x13, 0x56, 0xb7, 0xdd, 0xd0, + 0x3b, 0x9d, 0xc6, 0x7d, 0xb3, 0x85, 0x4f, 0xb8, 0x70, 0xe1, 0xd3, 0x46, 0xad, 0x6e, 0xbb, 0xd2, + 0xe9, 0xcc, 0x70, 0x20, 0x79, 0x0e, 0x4e, 0x32, 0x3a, 0xec, 0xa4, 0x80, 0xb2, 0x88, 0x94, 0x8c, + 0x01, 0x06, 0xb1, 0xf5, 0x69, 0xcf, 0xc3, 0xa0, 0xe0, 0xc9, 0xd7, 0xaa, 0x7e, 0x6d, 0x80, 0x33, + 0x73, 0x59, 0xcf, 0x05, 0x6c, 0xf8, 0xe4, 0xda, 0xaf, 0x0d, 0xf9, 0xe5, 0x31, 0x54, 0xb3, 0xd5, + 0x6d, 0xf3, 0xe8, 0x5b, 0x03, 0x88, 0x0c, 0x7e, 0x93, 0xcb, 0x30, 0xc6, 0xb8, 0x04, 0x02, 0xe3, + 0xd1, 0x6d, 0xfb, 0xb5, 0x18, 0x94, 0x5c, 0x87, 0xd3, 0x11, 0x08, 0xb7, 0x41, 0xf9, 0x93, 0x84, + 0x7e, 0x2d, 0x15, 0xa7, 0x7e, 0xa3, 0x10, 0x4d, 0xab, 0x74, 0x08, 0x1d, 0x71, 0x0e, 0x06, 0x6c, + 0x67, 0xb5, 0xd1, 0x75, 0x5a, 0x62, 0xec, 0x95, 0x6c, 0x67, 0xf5, 0x8e, 0xd3, 0x22, 0x67, 0xa0, + 0xc4, 0x7a, 0xc7, 0x34, 0xc4, 0x10, 0xeb, 0xd7, 0x3b, 0x9d, 0x9a, 0x41, 0x2a, 0xbc, 0x43, 0x30, + 0x32, 0x6a, 0xa3, 0x89, 0x5b, 0x7b, 0xee, 0x94, 0xd0, 0xcf, 0x57, 0xbc, 0x04, 0x12, 0xfb, 0x09, + 0xe3, 0xa5, 0xf2, 0x83, 0x80, 0x18, 0x0b, 0x03, 0xb7, 0x25, 0x06, 0xef, 0x93, 0x38, 0x0b, 0x81, + 0x0c, 0x59, 0xf0, 0x4d, 0x8c, 0x41, 0xaa, 0x40, 0x42, 0xaa, 0xb6, 0x6d, 0x98, 0xf7, 0x4d, 0xca, + 0x5f, 0x90, 0xf4, 0xf3, 0x8b, 0xdf, 0x24, 0x56, 0x1b, 0xf7, 0x99, 0xcc, 0x0b, 0x08, 0x79, 0x83, + 0x2b, 0x21, 0xa7, 0xc3, 0xb5, 0x8f, 0xf7, 0x2d, 0xb7, 0xd3, 0x62, 0x28, 0xd4, 0x4c, 0x2c, 0x8f, + 0x0b, 0xa1, 0xfa, 0x3f, 0xf4, 0x27, 0x73, 0x6b, 0x1d, 0x8a, 0x5d, 0x33, 0x0b, 0x20, 0x52, 0xe7, + 0x85, 0x97, 0x6b, 0x17, 0xa4, 0x38, 0xf9, 0x02, 0x93, 0xc1, 0x43, 0x2a, 0x4b, 0xae, 0xc2, 0x20, + 0xff, 0xa2, 0x5a, 0x55, 0xd8, 0x3b, 0xe8, 0x22, 0xe6, 0x76, 0xcc, 0xfb, 0xf7, 0xd1, 0x9f, 0x2c, + 0x40, 0x93, 0xcb, 0x30, 0x50, 0x5d, 0xa8, 0xd7, 0x2b, 0x0b, 0xfe, 0x4d, 0x31, 0xbe, 0x65, 0x31, + 0x2c, 0xb7, 0xe1, 0xea, 0x96, 0xab, 0xf9, 0x48, 0xf2, 0x0c, 0x94, 0x6a, 0x4b, 0x48, 0xc6, 0x5f, + 0x68, 0x0e, 0x6f, 0x6d, 0x96, 0x07, 0xcc, 0x0e, 0xa7, 0x12, 0x28, 0xac, 0xf7, 0x6e, 0xad, 0x2a, + 0xb9, 0x4b, 0xf0, 0x7a, 0x1f, 0x9a, 0x06, 0x5e, 0x3b, 0x6b, 0x01, 0x9a, 0xbc, 0x0c, 0x23, 0x75, + 0xea, 0x98, 0x7a, 0x6b, 0xa1, 0x8b, 0x5b, 0x45, 0x29, 0xe2, 0xa3, 0x8b, 0xf0, 0x86, 0x85, 0x08, + 0x2d, 0x42, 0x46, 0x2e, 0x42, 0x71, 0xd6, 0xb4, 0xfc, 0xe7, 0x12, 0xe8, 0x4f, 0xbf, 0x66, 0x5a, + 0x9e, 0x86, 0x50, 0xf2, 0x0c, 0x14, 0x6e, 0x2d, 0xd7, 0x84, 0x27, 0x18, 0xf2, 0x7a, 0xcf, 0x8b, + 0x44, 0x8f, 0xbc, 0xb5, 0x5c, 0x23, 0x2f, 0xc3, 0x10, 0x5b, 0xc4, 0xa8, 0xd5, 0xa4, 0xae, 0x32, + 0x8c, 0x1f, 0xc3, 0x43, 0x16, 0xfa, 0x40, 0xd9, 0xa7, 0x23, 0xa0, 0x24, 0xb7, 0x61, 0x3c, 0x1e, + 0x92, 0x5f, 0x3c, 0xd9, 0x41, 0x8b, 0x6b, 0x5d, 0xe0, 0xd2, 0x82, 0x66, 0x26, 0x0a, 0x12, 0x03, + 0x94, 0x38, 0x8c, 0xed, 0xeb, 0xd0, 0xea, 0xe4, 0xf1, 0x9a, 0xaf, 0x6c, 0x6d, 0x96, 0x3f, 0x92, + 0x60, 0xda, 0x70, 0x04, 0x95, 0xc4, 0x3d, 0x93, 0x93, 0xfa, 0x7f, 0xe5, 0xd3, 0xf3, 0xb5, 0x1d, + 0xc2, 0xec, 0xb4, 0xc7, 0x8b, 0xef, 0xd8, 0x98, 0x28, 0xee, 0x63, 0x4c, 0xdc, 0x87, 0x13, 0x15, + 0xa3, 0x6d, 0x5a, 0x15, 0xfc, 0xe9, 0xce, 0xcf, 0x54, 0x70, 0xb6, 0x93, 0x5e, 0x2f, 0xc6, 0xd0, + 0xe2, 0x7b, 0x78, 0x28, 0x65, 0x86, 0x6a, 0xe8, 0x1c, 0xd7, 0x68, 0xdf, 0xd7, 0x1b, 0x4d, 0x9e, + 0xea, 0x4c, 0x8b, 0x33, 0x55, 0x7f, 0x3c, 0xbf, 0x4d, 0x8a, 0xb9, 0xc7, 0x51, 0xfa, 0xea, 0x57, + 0xf2, 0xbd, 0xb3, 0xfc, 0x3d, 0x96, 0x42, 0xf9, 0xa3, 0x7c, 0x4a, 0xce, 0xbd, 0x7d, 0x49, 0xe2, + 0x2a, 0x0c, 0x72, 0x36, 0x81, 0xe7, 0x31, 0x4e, 0xc0, 0x5c, 0x59, 0x71, 0xe2, 0xf7, 0xd1, 0x64, + 0x01, 0x4e, 0x57, 0xee, 0xdf, 0xa7, 0x4d, 0x2f, 0x0c, 0xaa, 0xbd, 0x10, 0xc6, 0xa8, 0xe5, 0x41, + 0x84, 0x05, 0x3e, 0x0c, 0xca, 0x8d, 0xb1, 0x58, 0x52, 0xcb, 0x91, 0x65, 0x38, 0x1b, 0x87, 0xd7, + 0xf9, 0xae, 0xa5, 0x28, 0xc5, 0x15, 0x4e, 0x70, 0xe4, 0xff, 0x69, 0x19, 0x65, 0xd3, 0x5a, 0x89, + 0xab, 0x4b, 0x7f, 0xaf, 0x56, 0xe2, 0x52, 0x93, 0x5a, 0x4e, 0xfd, 0x66, 0x41, 0x4e, 0x4d, 0xf8, + 0xf8, 0xfa, 0x88, 0xdd, 0x88, 0x78, 0x86, 0xef, 0x74, 0xc8, 0xbc, 0x2c, 0x02, 0xac, 0x18, 0x5d, + 0xc7, 0x77, 0xa2, 0x0c, 0x02, 0x3c, 0x20, 0x50, 0x5e, 0x3a, 0x03, 0x4a, 0x52, 0x83, 0x62, 0xc5, + 0x59, 0xe5, 0x16, 0xf9, 0x76, 0x6f, 0xce, 0x74, 0x67, 0xd5, 0x4d, 0x7f, 0x73, 0xc6, 0x58, 0xa8, + 0x7f, 0x31, 0xdf, 0x23, 0x9b, 0xe0, 0x63, 0x39, 0x89, 0xfc, 0x74, 0x3e, 0x2b, 0x2f, 0xe0, 0x51, + 0xf5, 0x76, 0xfb, 0x80, 0x85, 0x73, 0xb4, 0x5d, 0x01, 0x0f, 0x50, 0x38, 0xbf, 0x9f, 0xcf, 0x4a, + 0x72, 0x78, 0x2c, 0x9c, 0xbd, 0x4d, 0x90, 0xa9, 0x22, 0x7d, 0x8c, 0x6d, 0x6e, 0x59, 0x15, 0xfa, + 0xf7, 0xe8, 0xf1, 0x95, 0x26, 0xd2, 0xe3, 0x21, 0xbc, 0x2f, 0x2d, 0xfd, 0x83, 0x7c, 0x66, 0x32, + 0xcf, 0x63, 0x99, 0x1e, 0xa4, 0x4c, 0x8f, 0x87, 0xfe, 0xbe, 0x86, 0x7e, 0xaa, 0x4c, 0x8f, 0xc7, + 0xfe, 0xbe, 0xf4, 0xf4, 0xf7, 0xf2, 0xe9, 0xe9, 0x6a, 0x0f, 0x41, 0x49, 0x0f, 0xc2, 0x29, 0xd3, + 0xef, 0x86, 0xe2, 0xbe, 0xba, 0xa1, 0x7f, 0x1f, 0x56, 0x54, 0x52, 0xa0, 0x87, 0x36, 0xea, 0xbf, + 0x57, 0x05, 0x7a, 0x00, 0x43, 0xfe, 0x71, 0x16, 0xe8, 0x5f, 0x28, 0x24, 0x53, 0x34, 0x3f, 0xae, + 0x6b, 0x92, 0xb3, 0xc7, 0x35, 0xc9, 0x2f, 0x47, 0xde, 0x86, 0x13, 0xa1, 0x2c, 0xe5, 0xc0, 0x68, + 0x78, 0xe3, 0xd5, 0x64, 0xa8, 0xc6, 0x7b, 0x0c, 0x27, 0x22, 0xf8, 0xc4, 0xa9, 0xd5, 0xef, 0x16, + 0x92, 0x79, 0xae, 0x8f, 0x7b, 0x63, 0x8f, 0xbd, 0x71, 0x07, 0xce, 0x4e, 0x75, 0x1d, 0x87, 0x5a, + 0x5e, 0x7a, 0xa7, 0xe0, 0xe1, 0x7d, 0x93, 0x53, 0x34, 0x92, 0x9d, 0x93, 0x51, 0x98, 0xb1, 0x15, + 0x0f, 0x32, 0xe2, 0x6c, 0x07, 0x42, 0xb6, 0x5d, 0x4e, 0x91, 0xc6, 0x36, 0xbd, 0xb0, 0xfa, 0x3b, + 0xf9, 0x64, 0x66, 0xf2, 0xe3, 0xae, 0xdf, 0x5b, 0xd7, 0xab, 0x5f, 0x2c, 0xc4, 0xb3, 0xb3, 0x1f, + 0x2f, 0x10, 0x7b, 0xef, 0x0e, 0x5f, 0x92, 0x38, 0x6e, 0xa4, 0xaf, 0xf0, 0xe1, 0x59, 0x5f, 0xe1, + 0xe3, 0xd5, 0x5f, 0x28, 0xc6, 0x33, 0xdd, 0x1f, 0x77, 0xc7, 0xe1, 0x75, 0x07, 0x59, 0x84, 0xd3, + 0x62, 0x6e, 0xf3, 0x41, 0x98, 0x21, 0x43, 0xcc, 0x5f, 0x3c, 0xd1, 0x9e, 0x98, 0x16, 0xbb, 0x2e, + 0x75, 0x1a, 0x9e, 0xee, 0x3e, 0x68, 0x60, 0x4a, 0x0d, 0x2d, 0xb5, 0x20, 0x63, 0x28, 0x66, 0xb5, + 0x28, 0xc3, 0xc1, 0x90, 0xa1, 0x3f, 0x21, 0x26, 0x18, 0xa6, 0x15, 0x54, 0x7f, 0x25, 0x07, 0xe3, + 0xf1, 0xcf, 0x21, 0x13, 0x30, 0xc8, 0x7e, 0x07, 0x91, 0x02, 0xa4, 0x0c, 0xe0, 0x9c, 0x23, 0xf7, + 0x22, 0xf0, 0x69, 0xc8, 0x2b, 0x30, 0x84, 0x0e, 0x1b, 0x58, 0x20, 0x1f, 0x06, 0x68, 0x08, 0x0b, + 0x60, 0x5a, 0x5a, 0x5e, 0x2c, 0x24, 0x25, 0x6f, 0xc0, 0x70, 0x2d, 0xf4, 0x4c, 0x13, 0x77, 0x5e, + 0xe8, 0x10, 0x2b, 0x95, 0x0c, 0x09, 0x34, 0x99, 0x5a, 0xfd, 0x76, 0x3e, 0x54, 0xf5, 0x63, 0xd3, + 0x74, 0x5f, 0xa6, 0xe9, 0x2f, 0x16, 0x60, 0x74, 0xca, 0xb6, 0x3c, 0xbd, 0xe9, 0x1d, 0x9f, 0x3f, + 0xed, 0x67, 0x5f, 0x4f, 0xca, 0xd0, 0x3f, 0xdd, 0xd6, 0xcd, 0x96, 0x30, 0x7c, 0x30, 0x8c, 0x2c, + 0x65, 0x00, 0x8d, 0xc3, 0xc9, 0x4d, 0x0c, 0x9e, 0xc2, 0x24, 0x1d, 0xb8, 0xe7, 0x8c, 0x85, 0x11, + 0x37, 0x25, 0x94, 0xc8, 0x39, 0xcb, 0x01, 0x7c, 0xe4, 0xc8, 0x25, 0xe5, 0x3e, 0x3b, 0x3e, 0x8b, + 0x39, 0x22, 0x7d, 0xf6, 0xd5, 0x02, 0x9c, 0x8d, 0xbb, 0x09, 0x1d, 0x0f, 0x38, 0xd1, 0x79, 0x7f, + 0x0a, 0x4e, 0xc7, 0x65, 0x53, 0x65, 0xd2, 0xe8, 0xef, 0x7d, 0x5d, 0x3d, 0xb1, 0xb5, 0x59, 0x7e, + 0x2a, 0xe9, 0xa1, 0xc5, 0x2a, 0x4b, 0xbd, 0xc0, 0x4e, 0xad, 0x24, 0xb5, 0x67, 0x3e, 0x24, 0x6f, + 0x07, 0x1f, 0xf3, 0x9e, 0xf9, 0xe9, 0x7c, 0xb2, 0x67, 0x8e, 0x27, 0x3c, 0xff, 0x62, 0xbd, 0x08, + 0x70, 0xd3, 0xf4, 0x44, 0x18, 0xc3, 0x23, 0xee, 0x2e, 0x2b, 0x79, 0xe4, 0x14, 0xf7, 0xe4, 0x91, + 0x13, 0x06, 0x27, 0xe8, 0xdf, 0x43, 0x70, 0x82, 0xb7, 0x61, 0x40, 0xc8, 0x51, 0x98, 0xfb, 0xe7, + 0xc2, 0xaf, 0x40, 0x70, 0x56, 0xf5, 0xbe, 0xf4, 0x9f, 0x85, 0x01, 0x91, 0xa9, 0x5c, 0x98, 0xe3, + 0xe8, 0x9c, 0x2b, 0x40, 0x9a, 0xff, 0x07, 0xb9, 0x08, 0x18, 0x7b, 0x5a, 0xf6, 0x9d, 0xe5, 0xb1, + 0xa8, 0xd9, 0xbf, 0xa4, 0x06, 0x03, 0xc2, 0xbf, 0x51, 0x01, 0x7c, 0xf8, 0x13, 0xa8, 0x65, 0xd8, + 0xcf, 0xdc, 0xcd, 0x91, 0x1f, 0x75, 0x09, 0x62, 0xf9, 0x45, 0x94, 0x00, 0xa9, 0xbf, 0x95, 0x83, + 0xf1, 0x78, 0x21, 0xf2, 0x3c, 0x94, 0xf8, 0x5f, 0xc2, 0xb0, 0xc7, 0xb8, 0x61, 0xbc, 0x84, 0x1c, + 0x37, 0x4c, 0x50, 0xbf, 0x0c, 0x43, 0x9a, 0xef, 0xb4, 0x2a, 0xe7, 0x15, 0x0f, 0x3c, 0x59, 0x65, + 0x4f, 0xa3, 0x80, 0x92, 0x3c, 0x03, 0x85, 0xc5, 0x96, 0x9f, 0x4e, 0x1c, 0x1d, 0x80, 0xed, 0x96, + 0x1c, 0x14, 0x8d, 0x61, 0x19, 0xd1, 0x02, 0x5d, 0x17, 0x6e, 0x69, 0x48, 0x64, 0xd1, 0x75, 0x99, + 0x68, 0x81, 0xae, 0xab, 0xff, 0x38, 0xe7, 0x3b, 0x1a, 0xcd, 0x99, 0xae, 0x57, 0xb3, 0x1e, 0xea, + 0x2d, 0x33, 0xe8, 0x08, 0x72, 0x13, 0xc6, 0x42, 0xa4, 0xf4, 0x80, 0x10, 0x5d, 0x81, 0x75, 0xc4, + 0x34, 0x5a, 0xa6, 0xeb, 0xf1, 0xac, 0x37, 0x4f, 0x49, 0x49, 0xa8, 0xa3, 0xc5, 0xc8, 0x65, 0x49, + 0xf9, 0xa5, 0xcd, 0x8e, 0xfc, 0x2a, 0x4d, 0x38, 0x8d, 0x8d, 0xcc, 0x9b, 0xae, 0x6b, 0x5a, 0xab, + 0x72, 0x6a, 0x71, 0xdc, 0x76, 0xb5, 0x39, 0xbc, 0x11, 0x4f, 0xf6, 0x1e, 0x29, 0xa0, 0xfe, 0xdb, + 0x1c, 0x5c, 0x60, 0x9c, 0x30, 0x22, 0x56, 0xe2, 0xc3, 0xf6, 0x35, 0x80, 0xdb, 0x3d, 0x24, 0x25, + 0x46, 0xf5, 0xd3, 0xc9, 0x57, 0xae, 0x31, 0xc2, 0x18, 0xf7, 0x1e, 0xb2, 0xdf, 0xd3, 0xf3, 0xbf, + 0xe7, 0xe6, 0xf9, 0xa6, 0xfa, 0xb6, 0x69, 0x19, 0xe4, 0x3c, 0x9c, 0xb9, 0x53, 0x9f, 0xd6, 0x1a, + 0xb7, 0x6b, 0x0b, 0xd5, 0xc6, 0x9d, 0x85, 0xfa, 0xd2, 0xf4, 0x54, 0x6d, 0xa6, 0x36, 0x5d, 0x1d, + 0xef, 0x23, 0xa7, 0xe0, 0x44, 0x88, 0x9a, 0xbd, 0x33, 0x5f, 0x59, 0x18, 0xcf, 0x91, 0x93, 0x30, + 0x1a, 0x02, 0x27, 0x17, 0x97, 0xc7, 0xf3, 0xcf, 0x7d, 0x14, 0x86, 0xf1, 0xed, 0x8f, 0x50, 0xd7, + 0x11, 0x18, 0x5c, 0x9c, 0xac, 0x4f, 0x6b, 0x77, 0x91, 0x09, 0x40, 0xa9, 0x3a, 0xbd, 0xc0, 0x18, + 0xe6, 0x9e, 0xfb, 0x7f, 0x73, 0x00, 0xf5, 0x99, 0xe5, 0x25, 0x41, 0x38, 0x0c, 0x03, 0xb5, 0x85, + 0xbb, 0x95, 0xb9, 0x1a, 0xa3, 0x1b, 0x84, 0xe2, 0xe2, 0xd2, 0x34, 0xab, 0x61, 0x08, 0xfa, 0xa7, + 0xe6, 0x16, 0xeb, 0xd3, 0xe3, 0x79, 0x06, 0xd4, 0xa6, 0x2b, 0xd5, 0xf1, 0x02, 0x03, 0xde, 0xd3, + 0x6a, 0xcb, 0xd3, 0xe3, 0x45, 0xf6, 0xe7, 0x5c, 0x7d, 0xb9, 0xb2, 0x3c, 0xde, 0xcf, 0xfe, 0x9c, + 0xc1, 0x3f, 0x4b, 0x8c, 0x59, 0x7d, 0x7a, 0x19, 0x7f, 0x0c, 0xb0, 0x26, 0xcc, 0xf8, 0xbf, 0x06, + 0x19, 0x8a, 0xb1, 0xae, 0xd6, 0xb4, 0xf1, 0x21, 0xf6, 0x83, 0xb1, 0x64, 0x3f, 0x80, 0x35, 0x4e, + 0x9b, 0x9e, 0x5f, 0xbc, 0x3b, 0x3d, 0x3e, 0xcc, 0x78, 0xcd, 0xdf, 0x66, 0xe0, 0x11, 0xf6, 0xa7, + 0x36, 0xcf, 0xfe, 0x1c, 0x65, 0x9c, 0xb4, 0xe9, 0xca, 0xdc, 0x52, 0x65, 0x79, 0x76, 0x7c, 0x8c, + 0xb5, 0x07, 0x79, 0x9e, 0xe0, 0x25, 0x17, 0x2a, 0xf3, 0xd3, 0xe3, 0xe3, 0x82, 0xa6, 0x3a, 0x57, + 0x5b, 0xb8, 0x3d, 0x7e, 0x12, 0x1b, 0xf2, 0xee, 0x3c, 0xfe, 0x20, 0xac, 0x00, 0xfe, 0x75, 0xea, + 0xb9, 0xef, 0x83, 0xd2, 0x62, 0x1d, 0xf7, 0xdb, 0xe7, 0xe0, 0xd4, 0x62, 0xbd, 0xb1, 0xfc, 0xee, + 0xd2, 0x74, 0x4c, 0xde, 0x27, 0x61, 0xd4, 0x47, 0xcc, 0xd5, 0x16, 0xee, 0x7c, 0x9a, 0x4b, 0xdb, + 0x07, 0xcd, 0x57, 0xa6, 0x16, 0xeb, 0xe3, 0x79, 0xd6, 0x2b, 0x3e, 0xe8, 0x5e, 0x6d, 0xa1, 0xba, + 0x78, 0xaf, 0x3e, 0x5e, 0x78, 0xee, 0x21, 0x8c, 0x54, 0x29, 0x9b, 0xd8, 0x16, 0x1d, 0x73, 0xd5, + 0xb4, 0xc8, 0x25, 0x38, 0x5f, 0x9d, 0xbe, 0x5b, 0x9b, 0x9a, 0x6e, 0x2c, 0x6a, 0xb5, 0x9b, 0xb5, + 0x85, 0x58, 0x4d, 0x67, 0xe0, 0x64, 0x14, 0x5d, 0x59, 0xaa, 0x8d, 0xe7, 0xc8, 0x59, 0x20, 0x51, + 0xf0, 0xad, 0xca, 0xfc, 0xcc, 0x78, 0x9e, 0x28, 0x70, 0x3a, 0x0a, 0xaf, 0x2d, 0x2c, 0xdf, 0x59, + 0x98, 0x1e, 0x2f, 0x3c, 0xf7, 0xd7, 0x73, 0x70, 0x26, 0x35, 0xd1, 0x0d, 0x51, 0xe1, 0xc9, 0xe9, + 0xb9, 0x4a, 0x7d, 0xb9, 0x36, 0x55, 0x9f, 0xae, 0x68, 0x53, 0xb3, 0x8d, 0xa9, 0xca, 0xf2, 0xf4, + 0xcd, 0x45, 0xed, 0xdd, 0xc6, 0xcd, 0xe9, 0x85, 0x69, 0xad, 0x32, 0x37, 0xde, 0x47, 0x9e, 0x81, + 0x72, 0x06, 0x4d, 0x7d, 0x7a, 0xea, 0x8e, 0x56, 0x5b, 0x7e, 0x77, 0x3c, 0x47, 0x9e, 0x86, 0x4b, + 0x99, 0x44, 0xec, 0xf7, 0x78, 0x9e, 0x3c, 0x09, 0x17, 0xb2, 0x48, 0xde, 0x99, 0x1b, 0x2f, 0x3c, + 0xf7, 0x53, 0x39, 0x20, 0xc9, 0x4c, 0x25, 0xe4, 0x29, 0xb8, 0xc8, 0xf4, 0xa2, 0x91, 0xdd, 0xc0, + 0xa7, 0xe1, 0x52, 0x2a, 0x85, 0xd4, 0xbc, 0x32, 0x3c, 0x91, 0x41, 0x22, 0x1a, 0x77, 0x11, 0x94, + 0x74, 0x02, 0x6c, 0xda, 0x2f, 0xe7, 0xe0, 0x4c, 0xaa, 0xaf, 0x3d, 0xb9, 0x02, 0x1f, 0xa9, 0x54, + 0xe7, 0x59, 0xdf, 0x4c, 0x2d, 0xd7, 0x16, 0x17, 0xea, 0x8d, 0xf9, 0x99, 0x4a, 0x83, 0x69, 0xdf, + 0x9d, 0x7a, 0xac, 0x37, 0x2f, 0x83, 0xda, 0x83, 0x72, 0x6a, 0xb6, 0xb2, 0x70, 0x93, 0x0d, 0x3f, + 0xf2, 0x11, 0x78, 0x2a, 0x93, 0x6e, 0x7a, 0xa1, 0x32, 0x39, 0x37, 0x5d, 0x1d, 0xcf, 0x93, 0x67, + 0xe1, 0xe9, 0x4c, 0xaa, 0x6a, 0xad, 0xce, 0xc9, 0x0a, 0xcf, 0xe9, 0x91, 0xed, 0x18, 0xfb, 0xca, + 0xa9, 0xc5, 0x85, 0xe5, 0xca, 0xd4, 0x72, 0x9a, 0x66, 0x9f, 0x87, 0x33, 0x11, 0xec, 0xe4, 0x9d, + 0x7a, 0x6d, 0x61, 0xba, 0x5e, 0x1f, 0xcf, 0x25, 0x50, 0x81, 0x68, 0xf3, 0x93, 0xd5, 0x6f, 0xff, + 0x2f, 0x4f, 0xf6, 0x7d, 0xfb, 0x0f, 0x9f, 0xcc, 0xfd, 0xfe, 0x1f, 0x3e, 0x99, 0xfb, 0x17, 0x7f, + 0xf8, 0x64, 0xee, 0x33, 0xd7, 0x77, 0x93, 0xe4, 0x86, 0xcf, 0x84, 0x2b, 0x25, 0xb4, 0x88, 0x5f, + 0xfa, 0x77, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x72, 0x45, 0x6f, 0x6b, 0x94, 0x01, 0x00, } func (m *Metadata) Marshal() (dAtA []byte, err error) { @@ -20586,6 +20654,70 @@ func (m *AccessRequestCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *AccessRequestExpire) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AccessRequestExpire) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AccessRequestExpire) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.ResourceExpiry != nil { + n130, err130 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.ResourceExpiry, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.ResourceExpiry):]) + if err130 != nil { + return 0, err130 + } + i -= n130 + i = encodeVarintEvents(dAtA, i, uint64(n130)) + i-- + dAtA[i] = 0x22 + } + if len(m.RequestID) > 0 { + i -= len(m.RequestID) + copy(dAtA[i:], m.RequestID) + i = encodeVarintEvents(dAtA, i, uint64(len(m.RequestID))) + i-- + dAtA[i] = 0x1a + } + { + size, err := m.ResourceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *ResourceID) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -21137,62 +21269,62 @@ func (m *SFTPAttributes) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if m.ModificationTime != nil { - n156, err156 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.ModificationTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.ModificationTime):]) - if err156 != nil { - return 0, err156 + n159, err159 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.ModificationTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.ModificationTime):]) + if err159 != nil { + return 0, err159 } - i -= n156 - i = encodeVarintEvents(dAtA, i, uint64(n156)) + i -= n159 + i = encodeVarintEvents(dAtA, i, uint64(n159)) i-- dAtA[i] = 0x32 } if m.AccessTime != nil { - n157, err157 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.AccessTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.AccessTime):]) - if err157 != nil { - return 0, err157 + n160, err160 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.AccessTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.AccessTime):]) + if err160 != nil { + return 0, err160 } - i -= n157 - i = encodeVarintEvents(dAtA, i, uint64(n157)) + i -= n160 + i = encodeVarintEvents(dAtA, i, uint64(n160)) i-- dAtA[i] = 0x2a } if m.Permissions != nil { - n158, err158 := github_com_gogo_protobuf_types.StdUInt32MarshalTo(*m.Permissions, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt32(*m.Permissions):]) - if err158 != nil { - return 0, err158 + n161, err161 := github_com_gogo_protobuf_types.StdUInt32MarshalTo(*m.Permissions, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt32(*m.Permissions):]) + if err161 != nil { + return 0, err161 } - i -= n158 - i = encodeVarintEvents(dAtA, i, uint64(n158)) + i -= n161 + i = encodeVarintEvents(dAtA, i, uint64(n161)) i-- dAtA[i] = 0x22 } if m.GID != nil { - n159, err159 := github_com_gogo_protobuf_types.StdUInt32MarshalTo(*m.GID, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt32(*m.GID):]) - if err159 != nil { - return 0, err159 + n162, err162 := github_com_gogo_protobuf_types.StdUInt32MarshalTo(*m.GID, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt32(*m.GID):]) + if err162 != nil { + return 0, err162 } - i -= n159 - i = encodeVarintEvents(dAtA, i, uint64(n159)) + i -= n162 + i = encodeVarintEvents(dAtA, i, uint64(n162)) i-- dAtA[i] = 0x1a } if m.UID != nil { - n160, err160 := github_com_gogo_protobuf_types.StdUInt32MarshalTo(*m.UID, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt32(*m.UID):]) - if err160 != nil { - return 0, err160 + n163, err163 := github_com_gogo_protobuf_types.StdUInt32MarshalTo(*m.UID, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt32(*m.UID):]) + if err163 != nil { + return 0, err163 } - i -= n160 - i = encodeVarintEvents(dAtA, i, uint64(n160)) + i -= n163 + i = encodeVarintEvents(dAtA, i, uint64(n163)) i-- dAtA[i] = 0x12 } if m.FileSize != nil { - n161, err161 := github_com_gogo_protobuf_types.StdUInt64MarshalTo(*m.FileSize, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt64(*m.FileSize):]) - if err161 != nil { - return 0, err161 + n164, err164 := github_com_gogo_protobuf_types.StdUInt64MarshalTo(*m.FileSize, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdUInt64(*m.FileSize):]) + if err164 != nil { + return 0, err164 } - i -= n161 - i = encodeVarintEvents(dAtA, i, uint64(n161)) + i -= n164 + i = encodeVarintEvents(dAtA, i, uint64(n164)) i-- dAtA[i] = 0xa } @@ -24100,6 +24232,16 @@ func (m *DatabaseSessionStart) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.ClientMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a if m.PostgresPID != 0 { i = encodeVarintEvents(dAtA, i, uint64(m.PostgresPID)) i-- @@ -25271,20 +25413,20 @@ func (m *DatabaseSessionEnd) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n360, err360 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.EndTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.EndTime):]) - if err360 != nil { - return 0, err360 + n364, err364 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.EndTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.EndTime):]) + if err364 != nil { + return 0, err364 } - i -= n360 - i = encodeVarintEvents(dAtA, i, uint64(n360)) + i -= n364 + i = encodeVarintEvents(dAtA, i, uint64(n364)) i-- dAtA[i] = 0x32 - n361, err361 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTime):]) - if err361 != nil { - return 0, err361 + n365, err365 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTime):]) + if err365 != nil { + return 0, err365 } - i -= n361 - i = encodeVarintEvents(dAtA, i, uint64(n361)) + i -= n365 + i = encodeVarintEvents(dAtA, i, uint64(n365)) i-- dAtA[i] = 0x2a { @@ -25951,20 +26093,20 @@ func (m *WindowsDesktopSessionEnd) MarshalToSizedBuffer(dAtA []byte) (int, error i-- dAtA[i] = 0x5a } - n394, err394 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.EndTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.EndTime):]) - if err394 != nil { - return 0, err394 + n398, err398 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.EndTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.EndTime):]) + if err398 != nil { + return 0, err398 } - i -= n394 - i = encodeVarintEvents(dAtA, i, uint64(n394)) + i -= n398 + i = encodeVarintEvents(dAtA, i, uint64(n398)) i-- dAtA[i] = 0x52 - n395, err395 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTime):]) - if err395 != nil { - return 0, err395 + n399, err399 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTime):]) + if err399 != nil { + return 0, err399 } - i -= n395 - i = encodeVarintEvents(dAtA, i, uint64(n395)) + i -= n399 + i = encodeVarintEvents(dAtA, i, uint64(n399)) i-- dAtA[i] = 0x4a if len(m.DesktopLabels) > 0 { @@ -26298,12 +26440,12 @@ func (m *InstanceJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { } i-- dAtA[i] = 0x52 - n409, err409 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.TokenExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.TokenExpires):]) - if err409 != nil { - return 0, err409 + n413, err413 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.TokenExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.TokenExpires):]) + if err413 != nil { + return 0, err413 } - i -= n409 - i = encodeVarintEvents(dAtA, i, uint64(n409)) + i -= n413 + i = encodeVarintEvents(dAtA, i, uint64(n413)) i-- dAtA[i] = 0x4a if m.Attributes != nil { @@ -32091,6 +32233,29 @@ func (m *OneOf_UserLoginAccessListInvalid) MarshalToSizedBuffer(dAtA []byte) (in } return len(dAtA) - i, nil } +func (m *OneOf_AccessRequestExpire) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OneOf_AccessRequestExpire) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.AccessRequestExpire != nil { + { + size, err := m.AccessRequestExpire.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xc + i-- + dAtA[i] = 0xba + } + return len(dAtA) - i, nil +} func (m *StreamStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -32115,12 +32280,12 @@ func (m *StreamStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n665, err665 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastUploadTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastUploadTime):]) - if err665 != nil { - return 0, err665 + n670, err670 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastUploadTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastUploadTime):]) + if err670 != nil { + return 0, err670 } - i -= n665 - i = encodeVarintEvents(dAtA, i, uint64(n665)) + i -= n670 + i = encodeVarintEvents(dAtA, i, uint64(n670)) i-- dAtA[i] = 0x1a if m.LastEventIndex != 0 { @@ -32279,12 +32444,12 @@ func (m *Identity) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xc2 } } - n669, err669 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.PreviousIdentityExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.PreviousIdentityExpires):]) - if err669 != nil { - return 0, err669 + n674, err674 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.PreviousIdentityExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.PreviousIdentityExpires):]) + if err674 != nil { + return 0, err674 } - i -= n669 - i = encodeVarintEvents(dAtA, i, uint64(n669)) + i -= n674 + i = encodeVarintEvents(dAtA, i, uint64(n674)) i-- dAtA[i] = 0x1 i-- @@ -32432,12 +32597,12 @@ func (m *Identity) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x4a } - n673, err673 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err673 != nil { - return 0, err673 + n678, err678 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err678 != nil { + return 0, err678 } - i -= n673 - i = encodeVarintEvents(dAtA, i, uint64(n673)) + i -= n678 + i = encodeVarintEvents(dAtA, i, uint64(n678)) i-- dAtA[i] = 0x42 if len(m.KubernetesUsers) > 0 { @@ -41233,6 +41398,30 @@ func (m *AccessRequestCreate) Size() (n int) { return n } +func (m *AccessRequestExpire) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Metadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.ResourceMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = len(m.RequestID) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.ResourceExpiry != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.ResourceExpiry) + n += 1 + l + sovEvents(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *ResourceID) Size() (n int) { if m == nil { return 0 @@ -42449,6 +42638,8 @@ func (m *DatabaseSessionStart) Size() (n int) { if m.PostgresPID != 0 { n += 1 + sovEvents(uint64(m.PostgresPID)) } + l = m.ClientMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -46001,6 +46192,18 @@ func (m *OneOf_UserLoginAccessListInvalid) Size() (n int) { } return n } +func (m *OneOf_AccessRequestExpire) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AccessRequestExpire != nil { + l = m.AccessRequestExpire.Size() + n += 2 + l + sovEvents(uint64(l)) + } + return n +} func (m *StreamStatus) Size() (n int) { if m == nil { return 0 @@ -60201,6 +60404,191 @@ func (m *AccessRequestCreate) Unmarshal(dAtA []byte) error { } return nil } +func (m *AccessRequestExpire) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AccessRequestExpire: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccessRequestExpire: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceExpiry", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceExpiry == nil { + m.ResourceExpiry = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.ResourceExpiry, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ResourceID) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -70750,6 +71138,39 @@ func (m *DatabaseSessionStart) Unmarshal(dAtA []byte) error { break } } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ClientMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -87759,6 +88180,41 @@ func (m *OneOf) Unmarshal(dAtA []byte) error { } m.Event = &OneOf_UserLoginAccessListInvalid{v} iNdEx = postIndex + case 199: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccessRequestExpire", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AccessRequestExpire{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Event = &OneOf_AccessRequestExpire{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) diff --git a/api/types/events/oneof.go b/api/types/events/oneof.go index 05849d37d4f73..27eb0f5451ffc 100644 --- a/api/types/events/oneof.go +++ b/api/types/events/oneof.go @@ -136,6 +136,10 @@ func ToOneOf(in AuditEvent) (*OneOf, error) { out.Event = &OneOf_AccessRequestCreate{ AccessRequestCreate: e, } + case *AccessRequestExpire: + out.Event = &OneOf_AccessRequestExpire{ + AccessRequestExpire: e, + } case *AccessRequestResourceSearch: out.Event = &OneOf_AccessRequestResourceSearch{ AccessRequestResourceSearch: e, diff --git a/api/types/session_tracker.go b/api/types/session_tracker.go index db07ea2578db5..2892db170085c 100644 --- a/api/types/session_tracker.go +++ b/api/types/session_tracker.go @@ -39,6 +39,7 @@ const ( DatabaseSessionKind SessionKind = "db" AppSessionKind SessionKind = "app" WindowsDesktopSessionKind SessionKind = "desktop" + UnknownSessionKind SessionKind = "" ) // SessionParticipantMode is the mode that determines what you can do when you join a session. diff --git a/api/types/trustedcluster.go b/api/types/trustedcluster.go index 27d8129f70cfe..17d397525a7df 100644 --- a/api/types/trustedcluster.go +++ b/api/types/trustedcluster.go @@ -29,8 +29,8 @@ import ( // TrustedCluster holds information needed for a cluster that can not be directly // accessed (maybe be behind firewall without any open ports) to join a parent cluster. type TrustedCluster interface { - // Resource provides common resource properties - Resource + // ResourceWithOrigin provides common resource properties + ResourceWithOrigin // SetMetadata sets object metadata SetMetadata(meta Metadata) // GetEnabled returns the state of the TrustedCluster. @@ -184,6 +184,16 @@ func (c *TrustedClusterV2) SetName(e string) { c.Metadata.Name = e } +// Origin returns the origin value of the resource. +func (c *TrustedClusterV2) Origin() string { + return c.Metadata.Origin() +} + +// SetOrigin sets the origin value of the resource. +func (c *TrustedClusterV2) SetOrigin(origin string) { + c.Metadata.SetOrigin(origin) +} + // GetEnabled returns the state of the TrustedCluster. func (c *TrustedClusterV2) GetEnabled() bool { return c.Spec.Enabled @@ -252,14 +262,6 @@ func (c *TrustedClusterV2) CanChangeStateTo(t TrustedCluster) error { if !slices.Equal(c.GetRoles(), t.GetRoles()) { return immutableFieldErr("roles") } - - if c.GetEnabled() == t.GetEnabled() && c.GetRoleMap().IsEqual(t.GetRoleMap()) { - if t.GetEnabled() { - return trace.AlreadyExists("leaf cluster is already enabled, this update would have no effect") - } - return trace.AlreadyExists("leaf cluster is already disabled, this update would have no effect") - } - return nil } diff --git a/api/types/types.pb.go b/api/types/types.pb.go index 10387e25bd555..a49edaacc6d5b 100644 --- a/api/types/types.pb.go +++ b/api/types/types.pb.go @@ -6524,7 +6524,8 @@ type AuthPreferenceSpecV2 struct { // Type is the type of authentication. Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"type"` // SecondFactor is the type of mult-factor. - SecondFactor github_com_gravitational_teleport_api_constants.SecondFactorType `protobuf:"bytes,2,opt,name=SecondFactor,proto3,casttype=github.com/gravitational/teleport/api/constants.SecondFactorType" json:"second_factor,omitempty"` + // Deprecated: Prefer using SecondFactors instead. + SecondFactor github_com_gravitational_teleport_api_constants.SecondFactorType `protobuf:"bytes,2,opt,name=SecondFactor,proto3,casttype=github.com/gravitational/teleport/api/constants.SecondFactorType" json:"second_factor,omitempty"` // Deprecated: Do not use. // ConnectorName is the name of the OIDC or SAML connector. If this value is // not set the first connector in the backend will be used. ConnectorName string `protobuf:"bytes,3,opt,name=ConnectorName,proto3" json:"connector_name,omitempty"` @@ -6572,7 +6573,9 @@ type AuthPreferenceSpecV2 struct { // If unspecified, the current default value is "legacy". // 1 is "legacy", 2 is "balanced-v1", 3 is "fips-v1", 4 is "hsm-v1". SignatureAlgorithmSuite SignatureAlgorithmSuite `protobuf:"varint,20,opt,name=signature_algorithm_suite,json=signatureAlgorithmSuite,proto3,enum=types.SignatureAlgorithmSuite" json:"signature_algorithm_suite,omitempty"` - // SecondFactors is a list of supported second factor types. + // SecondFactors is a list of supported multi-factor types. + // 1 is "otp", 2 is "webauthn", 3 is "sso", + // If unspecified, the current default value is [1], or ["otp"]. SecondFactors []SecondFactorType `protobuf:"varint,21,rep,packed,name=SecondFactors,proto3,enum=types.SecondFactorType" json:"second_factors,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -7576,7 +7579,9 @@ type AccessRequestSpecV3 struct { // This field is only populated when the request is in the PROMOTED state. AccessList *PromotedAccessList `protobuf:"bytes,20,opt,name=accessList,proto3" json:"access_list,omitempty"` // AssumeStartTime is the time the requested roles can be assumed. - AssumeStartTime *time.Time `protobuf:"bytes,21,opt,name=AssumeStartTime,proto3,stdtime" json:"assume_start_time,omitempty"` + AssumeStartTime *time.Time `protobuf:"bytes,21,opt,name=AssumeStartTime,proto3,stdtime" json:"assume_start_time,omitempty"` + // ResourceExpiry is the time at which the access request resource will expire. + ResourceExpiry *time.Time `protobuf:"bytes,22,opt,name=ResourceExpiry,proto3,stdtime" json:"expiry,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -22076,15 +22081,15 @@ func init() { func init() { proto.RegisterFile("teleport/legacy/types/types.proto", fileDescriptor_9198ee693835762e) } var fileDescriptor_9198ee693835762e = []byte{ - // 30542 bytes of a gzipped FileDescriptorProto + // 30566 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x70, 0x1c, 0x49, 0x7a, 0x20, 0x36, 0xdd, 0x8d, 0x47, 0xe3, 0xc3, 0xab, 0x91, 0x00, 0x49, 0x10, 0xf3, 0x68, 0x4e, 0xcd, 0x0c, 0x87, 0x9c, 0x9d, 0x21, 0x97, 0xe0, 0x0e, 0x77, 0x67, 0xe7, 0xb5, 0x8d, 0x6e, 0x90, 0x68, 0x12, 0x00, 0x31, 0xd5, 0x00, 0xb1, 0xa3, 0x7d, 0xd4, 0x16, 0xba, 0x13, 0x40, 0x0d, 0xba, - 0xbb, 0x7a, 0xab, 0xaa, 0x09, 0x42, 0x7b, 0xb2, 0xde, 0x27, 0x2b, 0x64, 0x3d, 0x4f, 0x3a, 0xed, - 0x39, 0x74, 0xb2, 0x42, 0xbe, 0xf3, 0x29, 0xce, 0x21, 0xc5, 0x59, 0xb2, 0xec, 0xb3, 0x2f, 0x2c, - 0x4b, 0x17, 0x67, 0x59, 0x56, 0x5c, 0x9c, 0x14, 0xf6, 0xd9, 0x0e, 0xaf, 0xcf, 0x90, 0x65, 0xf9, - 0x87, 0x03, 0x11, 0x8e, 0x90, 0x7c, 0x11, 0x8e, 0xf0, 0x3a, 0x74, 0x77, 0x91, 0x5f, 0x66, 0x56, + 0xbb, 0x7a, 0xab, 0xaa, 0x09, 0x42, 0x7b, 0xf2, 0xe9, 0xb5, 0x27, 0x2b, 0x64, 0x3d, 0x4f, 0x3a, + 0xed, 0x39, 0x74, 0xf2, 0x85, 0x7c, 0xe7, 0x53, 0x9c, 0x43, 0x8a, 0xb3, 0x64, 0xd9, 0x67, 0x5f, + 0x58, 0x96, 0x1c, 0xb2, 0x2c, 0x2b, 0x2e, 0x4e, 0x8a, 0xf3, 0xd9, 0x0e, 0xaf, 0xcf, 0x90, 0x65, + 0xf9, 0x17, 0x22, 0x1c, 0x21, 0xf9, 0x22, 0x1c, 0xe1, 0x75, 0xe8, 0xce, 0x91, 0x5f, 0x66, 0x56, 0x65, 0x56, 0x55, 0x37, 0x1a, 0x43, 0x8e, 0x4e, 0xdc, 0xd0, 0x1f, 0x12, 0xfd, 0xe5, 0xf7, 0x7d, 0x59, 0xf9, 0xfe, 0xf2, 0xcb, 0xef, 0x01, 0x2f, 0x06, 0xb4, 0x49, 0x3b, 0xae, 0x17, 0x5c, 0x6f, 0xd2, 0x3d, 0xbb, 0x7e, 0x74, 0x3d, 0x38, 0xea, 0x50, 0x9f, 0xff, 0x7b, 0xad, 0xe3, 0xb9, 0x81, @@ -22132,150 +22137,150 @@ var fileDescriptor_9198ee693835762e = []byte{ 0x0d, 0x33, 0x42, 0x60, 0xc3, 0x50, 0x0b, 0x6c, 0x2f, 0xa0, 0x8d, 0xf9, 0xe1, 0x81, 0x86, 0x81, 0xad, 0xcf, 0x19, 0x9f, 0x93, 0xc4, 0x87, 0x41, 0x70, 0x22, 0x2b, 0x30, 0x7e, 0xc7, 0xb3, 0xeb, 0x74, 0x83, 0x7a, 0x8e, 0xdb, 0xc0, 0xf1, 0xcd, 0x2d, 0x5d, 0x3e, 0x39, 0x2e, 0x9e, 0xdf, 0x63, - 0x60, 0xab, 0x83, 0xf0, 0x88, 0xfa, 0xdb, 0xc7, 0xc5, 0x7c, 0x45, 0x6c, 0xb5, 0xa6, 0x4a, 0x4a, + 0x60, 0xab, 0x83, 0xf0, 0x88, 0xfa, 0x3b, 0xc7, 0xc5, 0x7c, 0x45, 0x6c, 0xb5, 0xa6, 0x4a, 0x4a, 0xbe, 0xc6, 0x06, 0xc7, 0x0f, 0xb0, 0x6b, 0x69, 0x63, 0x7e, 0xf4, 0xd4, 0x4f, 0x34, 0xc4, 0x27, 0x9e, 0x6f, 0xda, 0x7e, 0x60, 0x79, 0x9c, 0x2e, 0xf6, 0x9d, 0x2a, 0x4b, 0x72, 0x1f, 0xf2, 0xb5, 0xfa, 0x3e, 0x6d, 0x74, 0x9b, 0x14, 0xa7, 0xcc, 0xf8, 0xe2, 0x05, 0x31, 0xa9, 0xe5, 0x78, 0xca, - 0xe2, 0xa5, 0x05, 0xc1, 0x9b, 0xf8, 0x02, 0xa2, 0xce, 0x27, 0x89, 0xf5, 0xf9, 0xfc, 0x37, 0x7f, - 0xa9, 0xf8, 0xcc, 0xf7, 0xfd, 0x8b, 0x4b, 0xcf, 0x18, 0xff, 0x79, 0x16, 0x0a, 0x71, 0x26, 0x64, - 0x17, 0x26, 0xb7, 0x3a, 0x0d, 0x3b, 0xa0, 0xe5, 0xa6, 0x43, 0xdb, 0x81, 0x8f, 0x93, 0xa4, 0x7f, - 0x9b, 0x5e, 0x16, 0xf5, 0xce, 0x77, 0x91, 0xd0, 0xaa, 0x73, 0xca, 0x58, 0xab, 0x74, 0xb6, 0x51, - 0x3d, 0x35, 0xdc, 0xc0, 0x7d, 0x9c, 0x61, 0x67, 0xab, 0x87, 0x6f, 0xfd, 0x3d, 0xea, 0x11, 0x6c, - 0xc5, 0x04, 0x6a, 0x37, 0x76, 0x8e, 0x70, 0x66, 0x0e, 0x3e, 0x81, 0x18, 0x49, 0xca, 0x04, 0x62, - 0x60, 0xe3, 0xff, 0xcc, 0xc0, 0x94, 0x49, 0x7d, 0xb7, 0xeb, 0xd5, 0xe9, 0x0a, 0xb5, 0x1b, 0xd4, - 0x63, 0xd3, 0xff, 0x9e, 0xd3, 0x6e, 0x88, 0x35, 0x85, 0xd3, 0xff, 0xc0, 0x69, 0xab, 0x5b, 0x37, - 0x96, 0x93, 0x4f, 0xc3, 0x68, 0xad, 0xbb, 0x83, 0xa8, 0xd9, 0x68, 0x07, 0xf0, 0xbb, 0x3b, 0x56, - 0x0c, 0x5d, 0xa2, 0x91, 0xeb, 0x30, 0xfa, 0x80, 0x7a, 0x7e, 0xb4, 0x1b, 0xe2, 0xd1, 0xf0, 0x90, - 0x83, 0x54, 0x02, 0x81, 0x45, 0xee, 0x44, 0x3b, 0xb2, 0x38, 0xd4, 0xa6, 0x63, 0xfb, 0x60, 0x34, - 0x55, 0x5a, 0x02, 0xa2, 0x4e, 0x15, 0x89, 0x65, 0xfc, 0x74, 0x16, 0x0a, 0x15, 0x3b, 0xb0, 0x77, - 0x6c, 0x5f, 0xf4, 0xe7, 0x83, 0x9b, 0x6c, 0x8f, 0x57, 0x1a, 0x8a, 0x7b, 0x3c, 0xfb, 0xf2, 0x8f, - 0xdd, 0xbc, 0x57, 0xe2, 0xcd, 0x1b, 0x67, 0x27, 0xac, 0x68, 0x5e, 0xd4, 0xa8, 0x77, 0x4f, 0x6f, - 0x54, 0x41, 0x34, 0x2a, 0x2f, 0x1b, 0x15, 0x35, 0x85, 0xbc, 0x0b, 0x43, 0xb5, 0x0e, 0xad, 0x8b, - 0x4d, 0x44, 0x9e, 0x0b, 0x7a, 0xe3, 0x18, 0xc2, 0x83, 0x9b, 0x4b, 0x13, 0x82, 0xcd, 0x90, 0xdf, - 0xa1, 0x75, 0x13, 0xc9, 0x94, 0x45, 0xf3, 0x0f, 0x73, 0x30, 0x97, 0x46, 0xa6, 0xb6, 0x63, 0xa4, - 0x4f, 0x3b, 0xae, 0x40, 0x9e, 0x1d, 0xe1, 0xec, 0x58, 0xc4, 0xed, 0x62, 0x6c, 0x69, 0x82, 0x7d, - 0xf2, 0xbe, 0x80, 0x99, 0x61, 0x29, 0x79, 0x29, 0x94, 0x08, 0xf2, 0x11, 0x3f, 0x21, 0x11, 0x48, - 0x39, 0x80, 0x8d, 0xb5, 0x5c, 0xc2, 0x28, 0x38, 0x44, 0xdd, 0x22, 0xc1, 0xd1, 0x58, 0x7b, 0x02, - 0xa2, 0x1d, 0x33, 0xf2, 0x50, 0x58, 0x86, 0xbc, 0x6c, 0xd6, 0xfc, 0x04, 0x32, 0x9a, 0x89, 0x75, - 0xd2, 0x83, 0x9b, 0x7c, 0x30, 0x1b, 0xe2, 0xb7, 0xca, 0x46, 0xe2, 0x90, 0x9b, 0x90, 0xdf, 0xf0, - 0xdc, 0x47, 0x47, 0xd5, 0x8a, 0x3f, 0x3f, 0x79, 0x29, 0x77, 0x65, 0x6c, 0xe9, 0xc2, 0xc9, 0x71, - 0x71, 0xb6, 0xc3, 0x60, 0x96, 0xd3, 0x50, 0x4f, 0xda, 0x10, 0xf1, 0xee, 0x50, 0x3e, 0x53, 0xc8, - 0xde, 0x1d, 0xca, 0x67, 0x0b, 0x39, 0x2e, 0x5e, 0xdc, 0x1d, 0xca, 0x0f, 0x15, 0x86, 0xef, 0x0e, - 0xe5, 0x87, 0x51, 0xe0, 0x18, 0x2b, 0xc0, 0xdd, 0xa1, 0xfc, 0x78, 0x61, 0x42, 0x3b, 0xed, 0x91, - 0x41, 0xe0, 0xd6, 0xdd, 0xa6, 0x99, 0xdb, 0x32, 0xab, 0xe6, 0x48, 0xb9, 0x54, 0xa6, 0x5e, 0x60, - 0xe6, 0x4a, 0xdb, 0x35, 0x73, 0xb2, 0x72, 0xd4, 0xb6, 0x5b, 0x4e, 0x9d, 0x1f, 0x9d, 0x66, 0xee, - 0x4e, 0x79, 0xc3, 0x28, 0xc1, 0x54, 0xd4, 0x96, 0x55, 0xc7, 0x0f, 0xc8, 0x75, 0x18, 0x93, 0x10, - 0xb6, 0xd1, 0xe5, 0x52, 0x5b, 0x6d, 0x46, 0x38, 0xc6, 0xef, 0x66, 0x01, 0xa2, 0x92, 0xa7, 0x74, - 0x2d, 0x7c, 0x56, 0x5b, 0x0b, 0xe7, 0xe2, 0x6b, 0xa1, 0xe7, 0x2a, 0x20, 0xef, 0xc3, 0x08, 0x13, - 0x0b, 0xba, 0x52, 0x24, 0xba, 0x10, 0x27, 0xc5, 0xc2, 0x07, 0x37, 0x97, 0xa6, 0x04, 0xf1, 0x88, - 0x8f, 0x10, 0x53, 0x90, 0x29, 0xcb, 0xe8, 0x17, 0x47, 0xa3, 0xc1, 0x10, 0x0b, 0xe8, 0x0a, 0x84, - 0x03, 0x2a, 0x3a, 0x14, 0x57, 0x46, 0x47, 0x0e, 0x72, 0x58, 0x4a, 0x2e, 0x02, 0x1b, 0x70, 0xd1, - 0xa9, 0xa3, 0x27, 0xc7, 0xc5, 0x5c, 0xd7, 0x73, 0x70, 0x12, 0x90, 0xeb, 0x20, 0xa6, 0x81, 0xe8, - 0x40, 0x36, 0xfb, 0x66, 0xea, 0xb6, 0x55, 0xa7, 0x5e, 0x10, 0xf5, 0xf8, 0x7c, 0x46, 0xce, 0x16, - 0xd2, 0x01, 0x7d, 0xaa, 0xcc, 0x0f, 0xe1, 0x34, 0xb8, 0x92, 0xda, 0x2b, 0xd7, 0x34, 0x54, 0x2e, - 0x46, 0x5e, 0x92, 0xa7, 0x52, 0x83, 0x97, 0x59, 0x09, 0x91, 0x52, 0xaf, 0x80, 0xdc, 0x04, 0x36, - 0x43, 0x45, 0xef, 0x83, 0xa8, 0xa7, 0xb4, 0x5d, 0x5b, 0x3a, 0x27, 0x38, 0x4d, 0xda, 0x87, 0x2a, - 0x39, 0xc3, 0x26, 0x6f, 0x03, 0x9b, 0xc2, 0xa2, 0xdf, 0x89, 0x20, 0xba, 0x53, 0xde, 0x28, 0x37, - 0xdd, 0x6e, 0xa3, 0xf6, 0xc1, 0x6a, 0x44, 0xbc, 0x57, 0xef, 0xa8, 0xc4, 0x77, 0xca, 0x1b, 0xe4, - 0x6d, 0x18, 0x2e, 0x7d, 0x77, 0xd7, 0xa3, 0x42, 0x3e, 0x99, 0x90, 0x75, 0x32, 0xd8, 0xd2, 0x05, - 0x41, 0x38, 0x6d, 0xb3, 0x9f, 0xaa, 0x5c, 0x87, 0xe5, 0xac, 0xe6, 0xcd, 0xd5, 0x9a, 0x90, 0x3d, - 0x48, 0xac, 0x5b, 0x36, 0x57, 0x95, 0xcf, 0x0e, 0xb4, 0x56, 0x33, 0x2a, 0x72, 0x1d, 0xb2, 0xa5, - 0x0a, 0xde, 0x88, 0xc6, 0x17, 0xc7, 0x64, 0xb5, 0x95, 0xa5, 0x39, 0x41, 0x32, 0x61, 0xab, 0xcb, - 0x20, 0x5b, 0xaa, 0x90, 0x25, 0x18, 0x5e, 0x3b, 0xaa, 0x7d, 0xb0, 0x2a, 0x36, 0xb3, 0x59, 0x39, - 0xaf, 0x19, 0xec, 0x3e, 0x2e, 0x7b, 0x3f, 0xfa, 0xe2, 0xd6, 0x91, 0xff, 0xf5, 0xa6, 0xfa, 0xc5, - 0x88, 0x46, 0x36, 0x60, 0xac, 0xd4, 0x68, 0x39, 0xed, 0x2d, 0x9f, 0x7a, 0xf3, 0xe3, 0xc8, 0x67, - 0x3e, 0xf6, 0xdd, 0x61, 0xf9, 0xd2, 0xfc, 0xc9, 0x71, 0x71, 0xce, 0x66, 0x3f, 0xad, 0xae, 0x4f, - 0x3d, 0x85, 0x5b, 0xc4, 0x84, 0x6c, 0x00, 0xac, 0xb9, 0xed, 0x3d, 0xb7, 0x14, 0x34, 0x6d, 0x3f, - 0xb6, 0x3d, 0x46, 0x05, 0xa1, 0xf8, 0x70, 0xae, 0xc5, 0x60, 0x96, 0xcd, 0x80, 0x0a, 0x43, 0x85, - 0x07, 0xb9, 0x0d, 0x23, 0xf7, 0x3d, 0xbb, 0xde, 0xa4, 0xf3, 0x93, 0xc8, 0x6d, 0x4e, 0x70, 0xe3, - 0x40, 0xd9, 0xd2, 0x79, 0xc1, 0xb0, 0xe0, 0x22, 0x58, 0xbd, 0xa6, 0x70, 0xc4, 0x85, 0x6d, 0x20, - 0xc9, 0x39, 0x99, 0x72, 0x49, 0xf8, 0x94, 0x7a, 0x49, 0x88, 0x16, 0x7d, 0xd9, 0x6d, 0xb5, 0xec, - 0x76, 0x03, 0x69, 0x1f, 0x2c, 0x2a, 0x77, 0x07, 0xe3, 0xeb, 0x30, 0x93, 0xe8, 0xac, 0x53, 0xee, - 0x77, 0xef, 0xc1, 0x74, 0x85, 0xee, 0xda, 0xdd, 0x66, 0x10, 0x9e, 0x24, 0x7c, 0x89, 0xe2, 0x4d, - 0xab, 0xc1, 0x8b, 0x2c, 0x79, 0x7c, 0x98, 0x71, 0x64, 0xe3, 0x5d, 0x98, 0xd4, 0x9a, 0xcf, 0xae, - 0x0a, 0xa5, 0x6e, 0xc3, 0x09, 0x70, 0x20, 0x33, 0xd1, 0x55, 0xc1, 0x66, 0x40, 0x1c, 0x2e, 0x33, - 0x42, 0x30, 0xfe, 0x8e, 0x2a, 0xad, 0x88, 0x9d, 0x88, 0x5d, 0xab, 0xc5, 0x7e, 0x90, 0x89, 0x64, - 0xa7, 0xc4, 0x7e, 0x10, 0xee, 0x06, 0x57, 0xf9, 0xda, 0xcc, 0x26, 0xd6, 0xe6, 0xb8, 0x18, 0x89, - 0x9c, 0x7d, 0xe8, 0xf3, 0x15, 0x19, 0xce, 0xd4, 0xdc, 0xc7, 0x9f, 0xa9, 0xef, 0xc3, 0xc4, 0x9a, - 0xdd, 0xb6, 0xf7, 0x68, 0x83, 0xb5, 0x80, 0xef, 0x3d, 0x63, 0x4b, 0xcf, 0x9e, 0x1c, 0x17, 0x2f, - 0xb4, 0x38, 0x1c, 0x5b, 0xa9, 0x4e, 0x22, 0x8d, 0x80, 0xdc, 0x90, 0x2b, 0x7b, 0x38, 0x65, 0x65, - 0x4f, 0x8a, 0xda, 0x87, 0x71, 0x65, 0x8b, 0xf5, 0x6c, 0xfc, 0xf6, 0x18, 0xb6, 0x91, 0xbc, 0x0e, - 0x23, 0x26, 0xdd, 0x63, 0x47, 0x4d, 0x26, 0x1a, 0x24, 0x0f, 0x21, 0x6a, 0xc7, 0x70, 0x1c, 0x94, - 0x33, 0x68, 0xc3, 0xdf, 0x77, 0x76, 0x03, 0xd1, 0x3b, 0xa1, 0x9c, 0x21, 0xc0, 0x8a, 0x9c, 0x21, - 0x20, 0xfa, 0x75, 0x96, 0xc3, 0xd8, 0xee, 0x67, 0x56, 0x6a, 0xa2, 0xd3, 0x64, 0x0f, 0x9b, 0x15, - 0x65, 0x1b, 0xf1, 0x34, 0x29, 0x81, 0x61, 0x93, 0x5b, 0x30, 0x56, 0xaa, 0xd7, 0xdd, 0xae, 0x72, - 0x67, 0xe4, 0xeb, 0x96, 0x03, 0x75, 0x15, 0x49, 0x84, 0x4a, 0x6a, 0x30, 0xbe, 0xcc, 0x2e, 0x5a, - 0x4e, 0xd9, 0xae, 0xef, 0xcb, 0x4e, 0x92, 0x7b, 0x98, 0x52, 0x12, 0xad, 0x5c, 0x8a, 0xc0, 0x3a, - 0x03, 0xaa, 0x4a, 0x06, 0x05, 0x97, 0x6c, 0xc2, 0x78, 0x8d, 0xd6, 0x3d, 0x1a, 0xd4, 0x02, 0xd7, - 0xa3, 0xb1, 0x2d, 0x59, 0x29, 0x59, 0x7a, 0x41, 0xde, 0xf5, 0x7c, 0x04, 0x5a, 0x3e, 0x83, 0xaa, - 0x5c, 0x15, 0x64, 0x2e, 0xb4, 0xb7, 0x5c, 0xef, 0xa8, 0xb2, 0x24, 0xb6, 0xe9, 0xe8, 0x4c, 0xe7, - 0x60, 0x55, 0x68, 0x67, 0x90, 0xc6, 0x8e, 0x2e, 0xb4, 0x73, 0x2c, 0x1c, 0xa9, 0x4a, 0x0d, 0x65, - 0x2b, 0xb1, 0x69, 0x4f, 0x47, 0xbd, 0x8c, 0x60, 0x65, 0xa4, 0x1a, 0x3e, 0x4a, 0x66, 0xda, 0x48, - 0x09, 0x2c, 0xd2, 0x01, 0x22, 0x47, 0x8d, 0x0b, 0xba, 0x4d, 0xea, 0xfb, 0x62, 0x2f, 0xbf, 0x18, - 0x1b, 0xfc, 0x08, 0x61, 0xe9, 0x15, 0xc1, 0xfc, 0x79, 0x39, 0x0d, 0xc4, 0x3d, 0x8d, 0x15, 0x2a, - 0xf5, 0xa4, 0xf0, 0x26, 0x6f, 0x01, 0x2c, 0x3f, 0x0a, 0xa8, 0xd7, 0xb6, 0x9b, 0xa1, 0x1e, 0x0c, - 0x55, 0x3f, 0x54, 0x40, 0xf5, 0x81, 0x56, 0x90, 0x49, 0x19, 0x26, 0x4b, 0xbe, 0xdf, 0x6d, 0x51, - 0xd3, 0x6d, 0xd2, 0x92, 0xb9, 0x8e, 0xfb, 0xfe, 0xd8, 0xd2, 0xf3, 0x27, 0xc7, 0xc5, 0x8b, 0x36, - 0x16, 0x58, 0x9e, 0xdb, 0xa4, 0x96, 0xed, 0xa9, 0xb3, 0x5b, 0xa7, 0x21, 0xf7, 0x01, 0xee, 0x77, - 0x68, 0xbb, 0x46, 0x6d, 0xaf, 0xbe, 0x1f, 0xdb, 0xe6, 0xa3, 0x82, 0xa5, 0xe7, 0x44, 0x0b, 0xe7, - 0xdc, 0x0e, 0x6d, 0xfb, 0x08, 0x53, 0xbf, 0x2a, 0xc2, 0x24, 0xdb, 0x30, 0x5d, 0x2d, 0xad, 0x6d, - 0xb8, 0x4d, 0xa7, 0x7e, 0x24, 0x24, 0xa7, 0x29, 0xd4, 0x0e, 0x9e, 0x17, 0x5c, 0x63, 0xa5, 0x7c, - 0x7b, 0x72, 0xec, 0x96, 0xd5, 0x41, 0xa8, 0x25, 0xe4, 0xa7, 0x38, 0x17, 0xf2, 0x21, 0x9b, 0x83, - 0x3e, 0x13, 0x06, 0x37, 0xed, 0x3d, 0x7f, 0x7e, 0x5a, 0xd3, 0x76, 0x95, 0xb6, 0x6b, 0xd7, 0x94, - 0x52, 0x2e, 0xa6, 0x2c, 0xf0, 0x89, 0x88, 0x50, 0x2b, 0xb0, 0xf7, 0x7c, 0x7d, 0x22, 0x86, 0xd8, - 0xe4, 0x2e, 0x40, 0xc5, 0xad, 0x77, 0x5b, 0xb4, 0x1d, 0x54, 0x96, 0xe6, 0x0b, 0xfa, 0x55, 0x20, - 0x2c, 0x88, 0xb6, 0xb6, 0x86, 0x5b, 0xd7, 0x66, 0xa2, 0x42, 0xbd, 0xf0, 0x1e, 0x14, 0xe2, 0x1f, - 0x72, 0x46, 0x05, 0xd6, 0x64, 0x61, 0x4a, 0x69, 0xfd, 0xf2, 0x23, 0xc7, 0x0f, 0x7c, 0xe3, 0x1b, - 0xda, 0x0a, 0x64, 0xbb, 0xc3, 0x3d, 0x7a, 0xb4, 0xe1, 0xd1, 0x5d, 0xe7, 0x91, 0xd8, 0xcc, 0x70, - 0x77, 0x38, 0xa0, 0x47, 0x56, 0x07, 0xa1, 0xea, 0xee, 0x10, 0xa2, 0x92, 0xcf, 0x40, 0xfe, 0xde, - 0x5a, 0xed, 0x1e, 0x3d, 0xaa, 0x56, 0xc4, 0x41, 0xc5, 0xc9, 0x5a, 0xbe, 0xc5, 0x48, 0xb5, 0xb9, - 0x16, 0x62, 0x1a, 0x4b, 0xd1, 0x4e, 0xc8, 0x6a, 0x2e, 0x37, 0xbb, 0x7e, 0x40, 0xbd, 0x6a, 0x45, - 0xad, 0xb9, 0xce, 0x81, 0xb1, 0x7d, 0x29, 0x44, 0x35, 0xfe, 0x75, 0x16, 0x77, 0x41, 0x36, 0xe1, - 0xab, 0x6d, 0x3f, 0xb0, 0xdb, 0x75, 0x1a, 0x32, 0xc0, 0x09, 0xef, 0x08, 0x68, 0x6c, 0xc2, 0x47, - 0xc8, 0x7a, 0xd5, 0xd9, 0x81, 0xab, 0x66, 0x55, 0x4a, 0xcd, 0x45, 0xb5, 0xa2, 0xaa, 0x57, 0x3d, - 0x01, 0x8d, 0x55, 0x19, 0x21, 0x93, 0xcb, 0x30, 0x5a, 0x2d, 0xad, 0x95, 0xba, 0xc1, 0x3e, 0xee, - 0xc1, 0x79, 0x2e, 0x9f, 0xb3, 0xd9, 0x6a, 0x77, 0x83, 0x7d, 0x53, 0x16, 0x92, 0xeb, 0x78, 0xef, - 0x69, 0xd3, 0x80, 0xab, 0x61, 0xc5, 0xa1, 0xeb, 0x73, 0x50, 0xec, 0xda, 0xc3, 0x40, 0xe4, 0x35, - 0x18, 0x7e, 0xb0, 0x51, 0xae, 0x56, 0xc4, 0xc5, 0x19, 0x4f, 0xa2, 0x87, 0x9d, 0xba, 0xfe, 0x25, - 0x1c, 0x85, 0x2c, 0xc3, 0x54, 0x8d, 0xd6, 0xbb, 0x9e, 0x13, 0x1c, 0xdd, 0xf1, 0xdc, 0x6e, 0xc7, - 0x9f, 0x1f, 0xc5, 0x3a, 0x70, 0xa5, 0xfb, 0xa2, 0xc4, 0xda, 0xc3, 0x22, 0x85, 0x3a, 0x46, 0x64, - 0xfc, 0x4e, 0x26, 0xda, 0x26, 0xc9, 0x65, 0x4d, 0xac, 0x41, 0xdd, 0x0d, 0x13, 0x6b, 0x54, 0xdd, - 0x0d, 0x0a, 0x38, 0x26, 0x90, 0x72, 0xd7, 0x0f, 0xdc, 0xd6, 0x72, 0xbb, 0xd1, 0x71, 0x9d, 0x76, - 0x80, 0x54, 0xbc, 0xf3, 0x8d, 0x93, 0xe3, 0xe2, 0x0b, 0x75, 0x2c, 0xb5, 0xa8, 0x28, 0xb6, 0x62, - 0x5c, 0x52, 0xa8, 0x1f, 0x63, 0x3c, 0x8c, 0xdf, 0xcf, 0x6a, 0xc7, 0x1b, 0xfb, 0x3c, 0x93, 0x76, - 0x9a, 0x4e, 0x1d, 0x6f, 0xf4, 0xd8, 0xd0, 0x70, 0x56, 0xe1, 0xe7, 0x79, 0x51, 0x29, 0xef, 0x21, - 0x9d, 0x77, 0x0a, 0x35, 0xf9, 0x02, 0x4c, 0x30, 0x49, 0x43, 0xfc, 0xf4, 0xe7, 0xb3, 0xd8, 0xd9, - 0xcf, 0xa1, 0x16, 0xce, 0xa7, 0x5e, 0xc8, 0x46, 0x13, 0x51, 0x54, 0x0a, 0xd2, 0x80, 0xf9, 0x4d, - 0xcf, 0x6e, 0xfb, 0x4e, 0xb0, 0xdc, 0xae, 0x7b, 0x47, 0x28, 0x19, 0x2d, 0xb7, 0xed, 0x9d, 0x26, - 0x6d, 0x60, 0x73, 0xf3, 0x4b, 0x57, 0x4e, 0x8e, 0x8b, 0x2f, 0x07, 0x1c, 0xc7, 0xa2, 0x21, 0x92, - 0x45, 0x39, 0x96, 0xc2, 0xb9, 0x27, 0x27, 0x26, 0x49, 0xc9, 0x6e, 0xc5, 0x47, 0x18, 0x2e, 0x24, - 0xa0, 0x24, 0x15, 0x8e, 0x06, 0xdb, 0xc3, 0xd4, 0xcf, 0x54, 0x09, 0x8c, 0xff, 0x37, 0x13, 0x1d, - 0xc0, 0xe4, 0x1d, 0x18, 0x17, 0x2b, 0x46, 0x99, 0x17, 0xb8, 0x83, 0xca, 0xe5, 0x15, 0x1b, 0x59, - 0x15, 0x9d, 0xdd, 0xfb, 0x4b, 0xe5, 0x55, 0x65, 0x6e, 0xe0, 0xbd, 0xdf, 0xae, 0x37, 0xe3, 0x54, - 0x12, 0x8d, 0x4d, 0x82, 0xcd, 0xd5, 0x9a, 0xde, 0x2b, 0x38, 0x09, 0x82, 0xa6, 0x9f, 0xd2, 0x0d, - 0x0a, 0xf2, 0xe3, 0x37, 0xfc, 0x7f, 0xce, 0xa4, 0x9d, 0xf3, 0x64, 0x09, 0x26, 0xb7, 0x5d, 0xef, - 0x00, 0xc7, 0x57, 0xe9, 0x04, 0x1c, 0xf9, 0x43, 0x59, 0x10, 0x6f, 0x90, 0x4e, 0xa2, 0x7e, 0x9b, - 0xd2, 0x1b, 0xfa, 0xb7, 0xc5, 0x38, 0x68, 0x04, 0x6c, 0x1c, 0x42, 0x8e, 0xe1, 0xea, 0xc0, 0x71, - 0x88, 0x3e, 0x41, 0x9b, 0xc2, 0x2a, 0xba, 0xf1, 0x5f, 0x65, 0xd4, 0xf3, 0x9c, 0x75, 0x72, 0xc5, - 0x6d, 0xd9, 0x4e, 0x5b, 0x69, 0x0e, 0x7f, 0x58, 0x42, 0x68, 0xfc, 0x4b, 0x14, 0x64, 0x72, 0x13, - 0xf2, 0xfc, 0x57, 0xb8, 0xd7, 0xa2, 0x56, 0x4b, 0x10, 0xea, 0x07, 0x85, 0x44, 0x4c, 0x8c, 0x4c, - 0xee, 0xac, 0x23, 0xf3, 0xdb, 0x19, 0xf5, 0x28, 0xfe, 0xb8, 0x87, 0x4d, 0xec, 0x90, 0xc9, 0x9e, - 0xe5, 0x90, 0x79, 0xec, 0x26, 0x7c, 0x5f, 0x06, 0xc6, 0x15, 0x2d, 0x05, 0x6b, 0xc3, 0x86, 0xe7, - 0x7e, 0x44, 0xeb, 0x81, 0xde, 0x86, 0x0e, 0x07, 0xc6, 0xda, 0x10, 0xa2, 0x3e, 0x46, 0x1b, 0x8c, - 0x3f, 0xcb, 0x88, 0x3b, 0xd2, 0xc0, 0xdb, 0xbc, 0xbe, 0x25, 0x67, 0xcf, 0x72, 0x44, 0x7e, 0x01, - 0x86, 0x4d, 0xda, 0x70, 0x7c, 0x71, 0xbf, 0x99, 0x51, 0xef, 0x63, 0x58, 0x10, 0xc9, 0x4d, 0x1e, - 0xfb, 0xa9, 0x9e, 0x6f, 0x58, 0xce, 0x04, 0xd9, 0xaa, 0x7f, 0xbb, 0x49, 0x1f, 0x39, 0x7c, 0x31, - 0x8a, 0xa3, 0x16, 0x8f, 0x37, 0xc7, 0xb7, 0x76, 0x59, 0x89, 0x90, 0xa8, 0xd5, 0x85, 0xa7, 0xd1, - 0x18, 0x1f, 0x02, 0x44, 0x55, 0x92, 0x7b, 0x50, 0x10, 0xb3, 0xc1, 0x69, 0xef, 0x71, 0x41, 0x4a, - 0xf4, 0x41, 0xf1, 0xe4, 0xb8, 0xf8, 0x6c, 0x3d, 0x2c, 0x13, 0x52, 0xa7, 0xc2, 0x37, 0x41, 0x68, - 0xfc, 0xbd, 0x2c, 0x64, 0x4b, 0x38, 0x20, 0xf7, 0xe8, 0x51, 0x60, 0xef, 0xdc, 0x76, 0x9a, 0xda, - 0x62, 0x3a, 0x40, 0xa8, 0xb5, 0xeb, 0x68, 0xea, 0x0a, 0x05, 0x99, 0x2d, 0xa6, 0x7b, 0xde, 0xce, - 0x9b, 0x48, 0xa8, 0x2c, 0xa6, 0x03, 0x6f, 0xe7, 0xcd, 0x38, 0x59, 0x88, 0x48, 0x0c, 0x18, 0xe1, - 0x0b, 0x4b, 0xcc, 0x41, 0x38, 0x39, 0x2e, 0x8e, 0xf0, 0xf5, 0x67, 0x8a, 0x12, 0x72, 0x11, 0x72, - 0xb5, 0x8d, 0x75, 0xb1, 0x03, 0xa2, 0x5a, 0xd0, 0xef, 0xb4, 0x4d, 0x06, 0x63, 0x75, 0xae, 0x56, - 0x4a, 0x1b, 0xa8, 0x08, 0x18, 0x8e, 0xea, 0x6c, 0x36, 0xec, 0x4e, 0x5c, 0x15, 0x10, 0x22, 0x92, - 0x77, 0x61, 0xfc, 0x5e, 0xa5, 0xbc, 0xe2, 0xfa, 0x7c, 0xf7, 0x1a, 0x89, 0x26, 0xff, 0x41, 0xa3, - 0x6e, 0xa1, 0x26, 0x3e, 0x7e, 0x0c, 0x28, 0xf8, 0xc6, 0x0f, 0x67, 0x61, 0x5c, 0xd1, 0x93, 0x91, + 0xe2, 0xa5, 0x05, 0xc1, 0x9b, 0xf8, 0x02, 0xa2, 0xce, 0x27, 0x89, 0xf5, 0xf9, 0xfc, 0xb7, 0xfe, + 0x6e, 0xf1, 0x99, 0xef, 0xff, 0x97, 0x97, 0x9e, 0x31, 0xfe, 0xf3, 0x2c, 0x14, 0xe2, 0x4c, 0xc8, + 0x2e, 0x4c, 0x6e, 0x75, 0x1a, 0x76, 0x40, 0xcb, 0x4d, 0x87, 0xb6, 0x03, 0x1f, 0x27, 0x49, 0xff, + 0x36, 0xbd, 0x2c, 0xea, 0x9d, 0xef, 0x22, 0xa1, 0x55, 0xe7, 0x94, 0xb1, 0x56, 0xe9, 0x6c, 0xa3, + 0x7a, 0x6a, 0xb8, 0x81, 0xfb, 0x38, 0xc3, 0xce, 0x56, 0x0f, 0xdf, 0xfa, 0x7b, 0xd4, 0x23, 0xd8, + 0x8a, 0x09, 0xd4, 0x6e, 0xec, 0x1c, 0xe1, 0xcc, 0x1c, 0x7c, 0x02, 0x31, 0x92, 0x94, 0x09, 0xc4, + 0xc0, 0xc6, 0xff, 0x99, 0x81, 0x29, 0x93, 0xfa, 0x6e, 0xd7, 0xab, 0xd3, 0x15, 0x6a, 0x37, 0xa8, + 0xc7, 0xa6, 0xff, 0x3d, 0xa7, 0xdd, 0x10, 0x6b, 0x0a, 0xa7, 0xff, 0x81, 0xd3, 0x56, 0xb7, 0x6e, + 0x2c, 0x27, 0x9f, 0x86, 0xd1, 0x5a, 0x77, 0x07, 0x51, 0xb3, 0xd1, 0x0e, 0xe0, 0x77, 0x77, 0xac, + 0x18, 0xba, 0x44, 0x23, 0xd7, 0x61, 0xf4, 0x01, 0xf5, 0xfc, 0x68, 0x37, 0xc4, 0xa3, 0xe1, 0x21, + 0x07, 0xa9, 0x04, 0x02, 0x8b, 0xdc, 0x89, 0x76, 0x64, 0x71, 0xa8, 0x4d, 0xc7, 0xf6, 0xc1, 0x68, + 0xaa, 0xb4, 0x04, 0x44, 0x9d, 0x2a, 0x12, 0xcb, 0xf8, 0xe9, 0x2c, 0x14, 0x2a, 0x76, 0x60, 0xef, + 0xd8, 0xbe, 0xe8, 0xcf, 0x07, 0x37, 0xd9, 0x1e, 0xaf, 0x34, 0x14, 0xf7, 0x78, 0xf6, 0xe5, 0x1f, + 0xbb, 0x79, 0xaf, 0xc4, 0x9b, 0x37, 0xce, 0x4e, 0x58, 0xd1, 0xbc, 0xa8, 0x51, 0xef, 0x9e, 0xde, + 0xa8, 0x82, 0x68, 0x54, 0x5e, 0x36, 0x2a, 0x6a, 0x0a, 0x79, 0x17, 0x86, 0x6a, 0x1d, 0x5a, 0x17, + 0x9b, 0x88, 0x3c, 0x17, 0xf4, 0xc6, 0x31, 0x84, 0x07, 0x37, 0x97, 0x26, 0x04, 0x9b, 0x21, 0xbf, + 0x43, 0xeb, 0x26, 0x92, 0x29, 0x8b, 0xe6, 0x1f, 0xe7, 0x60, 0x2e, 0x8d, 0x4c, 0x6d, 0xc7, 0x48, + 0x9f, 0x76, 0x5c, 0x81, 0x3c, 0x3b, 0xc2, 0xd9, 0xb1, 0x88, 0xdb, 0xc5, 0xd8, 0xd2, 0x04, 0xfb, + 0xe4, 0x7d, 0x01, 0x33, 0xc3, 0x52, 0xf2, 0x52, 0x28, 0x11, 0xe4, 0x23, 0x7e, 0x42, 0x22, 0x90, + 0x72, 0x00, 0x1b, 0x6b, 0xb9, 0x84, 0x51, 0x70, 0x88, 0xba, 0x45, 0x82, 0xa3, 0xb1, 0xf6, 0x04, + 0x44, 0x3b, 0x66, 0xe4, 0xa1, 0xb0, 0x0c, 0x79, 0xd9, 0xac, 0xf9, 0x09, 0x64, 0x34, 0x13, 0xeb, + 0xa4, 0x07, 0x37, 0xf9, 0x60, 0x36, 0xc4, 0x6f, 0x95, 0x8d, 0xc4, 0x21, 0x37, 0x21, 0xbf, 0xe1, + 0xb9, 0x8f, 0x8e, 0xaa, 0x15, 0x7f, 0x7e, 0xf2, 0x52, 0xee, 0xca, 0xd8, 0xd2, 0x85, 0x93, 0xe3, + 0xe2, 0x6c, 0x87, 0xc1, 0x2c, 0xa7, 0xa1, 0x9e, 0xb4, 0x21, 0xe2, 0xdd, 0xa1, 0x7c, 0xa6, 0x90, + 0xbd, 0x3b, 0x94, 0xcf, 0x16, 0x72, 0x5c, 0xbc, 0xb8, 0x3b, 0x94, 0x1f, 0x2a, 0x0c, 0xdf, 0x1d, + 0xca, 0x0f, 0xa3, 0xc0, 0x31, 0x56, 0x80, 0xbb, 0x43, 0xf9, 0xf1, 0xc2, 0x84, 0x76, 0xda, 0x23, + 0x83, 0xc0, 0xad, 0xbb, 0x4d, 0x33, 0xb7, 0x65, 0x56, 0xcd, 0x91, 0x72, 0xa9, 0x4c, 0xbd, 0xc0, + 0xcc, 0x95, 0xb6, 0x6b, 0xe6, 0x64, 0xe5, 0xa8, 0x6d, 0xb7, 0x9c, 0x3a, 0x3f, 0x3a, 0xcd, 0xdc, + 0x9d, 0xf2, 0x86, 0x51, 0x82, 0xa9, 0xa8, 0x2d, 0xab, 0x8e, 0x1f, 0x90, 0xeb, 0x30, 0x26, 0x21, + 0x6c, 0xa3, 0xcb, 0xa5, 0xb6, 0xda, 0x8c, 0x70, 0x8c, 0xdf, 0xcd, 0x02, 0x44, 0x25, 0x4f, 0xe9, + 0x5a, 0xf8, 0xac, 0xb6, 0x16, 0xce, 0xc5, 0xd7, 0x42, 0xcf, 0x55, 0x40, 0xde, 0x87, 0x11, 0x26, + 0x16, 0x74, 0xa5, 0x48, 0x74, 0x21, 0x4e, 0x8a, 0x85, 0x0f, 0x6e, 0x2e, 0x4d, 0x09, 0xe2, 0x11, + 0x1f, 0x21, 0xa6, 0x20, 0x53, 0x96, 0xd1, 0x2f, 0x8e, 0x46, 0x83, 0x21, 0x16, 0xd0, 0x15, 0x08, + 0x07, 0x54, 0x74, 0x28, 0xae, 0x8c, 0x8e, 0x1c, 0xe4, 0xb0, 0x94, 0x5c, 0x04, 0x36, 0xe0, 0xa2, + 0x53, 0x47, 0x4f, 0x8e, 0x8b, 0xb9, 0xae, 0xe7, 0xe0, 0x24, 0x20, 0xd7, 0x41, 0x4c, 0x03, 0xd1, + 0x81, 0x6c, 0xf6, 0xcd, 0xd4, 0x6d, 0xab, 0x4e, 0xbd, 0x20, 0xea, 0xf1, 0xf9, 0x8c, 0x9c, 0x2d, + 0xa4, 0x03, 0xfa, 0x54, 0x99, 0x1f, 0xc2, 0x69, 0x70, 0x25, 0xb5, 0x57, 0xae, 0x69, 0xa8, 0x5c, + 0x8c, 0xbc, 0x24, 0x4f, 0xa5, 0x06, 0x2f, 0xb3, 0x12, 0x22, 0xa5, 0x5e, 0x01, 0xb9, 0x09, 0x6c, + 0x86, 0x8a, 0xde, 0x07, 0x51, 0x4f, 0x69, 0xbb, 0xb6, 0x74, 0x4e, 0x70, 0x9a, 0xb4, 0x0f, 0x55, + 0x72, 0x86, 0x4d, 0xde, 0x06, 0x36, 0x85, 0x45, 0xbf, 0x13, 0x41, 0x74, 0xa7, 0xbc, 0x51, 0x6e, + 0xba, 0xdd, 0x46, 0xed, 0x83, 0xd5, 0x88, 0x78, 0xaf, 0xde, 0x51, 0x89, 0xef, 0x94, 0x37, 0xc8, + 0xdb, 0x30, 0x5c, 0xfa, 0xde, 0xae, 0x47, 0x85, 0x7c, 0x32, 0x21, 0xeb, 0x64, 0xb0, 0xa5, 0x0b, + 0x82, 0x70, 0xda, 0x66, 0x3f, 0x55, 0xb9, 0x0e, 0xcb, 0x59, 0xcd, 0x9b, 0xab, 0x35, 0x21, 0x7b, + 0x90, 0x58, 0xb7, 0x6c, 0xae, 0x2a, 0x9f, 0x1d, 0x68, 0xad, 0x66, 0x54, 0xe4, 0x3a, 0x64, 0x4b, + 0x15, 0xbc, 0x11, 0x8d, 0x2f, 0x8e, 0xc9, 0x6a, 0x2b, 0x4b, 0x73, 0x82, 0x64, 0xc2, 0x56, 0x97, + 0x41, 0xb6, 0x54, 0x21, 0x4b, 0x30, 0xbc, 0x76, 0x54, 0xfb, 0x60, 0x55, 0x6c, 0x66, 0xb3, 0x72, + 0x5e, 0x33, 0xd8, 0x7d, 0x5c, 0xf6, 0x7e, 0xf4, 0xc5, 0xad, 0x23, 0xff, 0xeb, 0x4d, 0xf5, 0x8b, + 0x11, 0x8d, 0x6c, 0xc0, 0x58, 0xa9, 0xd1, 0x72, 0xda, 0x5b, 0x3e, 0xf5, 0xe6, 0xc7, 0x91, 0xcf, + 0x7c, 0xec, 0xbb, 0xc3, 0xf2, 0xa5, 0xf9, 0x93, 0xe3, 0xe2, 0x9c, 0xcd, 0x7e, 0x5a, 0x5d, 0x9f, + 0x7a, 0x0a, 0xb7, 0x88, 0x09, 0xd9, 0x00, 0x58, 0x73, 0xdb, 0x7b, 0x6e, 0x29, 0x68, 0xda, 0x7e, + 0x6c, 0x7b, 0x8c, 0x0a, 0x42, 0xf1, 0xe1, 0x5c, 0x8b, 0xc1, 0x2c, 0x9b, 0x01, 0x15, 0x86, 0x0a, + 0x0f, 0x72, 0x1b, 0x46, 0xee, 0x7b, 0x76, 0xbd, 0x49, 0xe7, 0x27, 0x91, 0xdb, 0x9c, 0xe0, 0xc6, + 0x81, 0xb2, 0xa5, 0xf3, 0x82, 0x61, 0xc1, 0x45, 0xb0, 0x7a, 0x4d, 0xe1, 0x88, 0x0b, 0xdb, 0x40, + 0x92, 0x73, 0x32, 0xe5, 0x92, 0xf0, 0x29, 0xf5, 0x92, 0x10, 0x2d, 0xfa, 0xb2, 0xdb, 0x6a, 0xd9, + 0xed, 0x06, 0xd2, 0x3e, 0x58, 0x54, 0xee, 0x0e, 0xc6, 0xd7, 0x61, 0x26, 0xd1, 0x59, 0xa7, 0xdc, + 0xef, 0xde, 0x83, 0xe9, 0x0a, 0xdd, 0xb5, 0xbb, 0xcd, 0x20, 0x3c, 0x49, 0xf8, 0x12, 0xc5, 0x9b, + 0x56, 0x83, 0x17, 0x59, 0xf2, 0xf8, 0x30, 0xe3, 0xc8, 0xc6, 0xbb, 0x30, 0xa9, 0x35, 0x9f, 0x5d, + 0x15, 0x4a, 0xdd, 0x86, 0x13, 0xe0, 0x40, 0x66, 0xa2, 0xab, 0x82, 0xcd, 0x80, 0x38, 0x5c, 0x66, + 0x84, 0x60, 0xfc, 0x3d, 0x55, 0x5a, 0x11, 0x3b, 0x11, 0xbb, 0x56, 0x8b, 0xfd, 0x20, 0x13, 0xc9, + 0x4e, 0x89, 0xfd, 0x20, 0xdc, 0x0d, 0xae, 0xf2, 0xb5, 0x99, 0x4d, 0xac, 0xcd, 0x71, 0x31, 0x12, + 0x39, 0xfb, 0xd0, 0xe7, 0x2b, 0x32, 0x9c, 0xa9, 0xb9, 0x8f, 0x3f, 0x53, 0xdf, 0x87, 0x89, 0x35, + 0xbb, 0x6d, 0xef, 0xd1, 0x06, 0x6b, 0x01, 0xdf, 0x7b, 0xc6, 0x96, 0x9e, 0x3d, 0x39, 0x2e, 0x5e, + 0x68, 0x71, 0x38, 0xb6, 0x52, 0x9d, 0x44, 0x1a, 0x01, 0xb9, 0x21, 0x57, 0xf6, 0x70, 0xca, 0xca, + 0x9e, 0x14, 0xb5, 0x0f, 0xe3, 0xca, 0x16, 0xeb, 0xd9, 0xf8, 0xad, 0x31, 0x6c, 0x23, 0x79, 0x1d, + 0x46, 0x4c, 0xba, 0xc7, 0x8e, 0x9a, 0x4c, 0x34, 0x48, 0x1e, 0x42, 0xd4, 0x8e, 0xe1, 0x38, 0x28, + 0x67, 0xd0, 0x86, 0xbf, 0xef, 0xec, 0x06, 0xa2, 0x77, 0x42, 0x39, 0x43, 0x80, 0x15, 0x39, 0x43, + 0x40, 0xf4, 0xeb, 0x2c, 0x87, 0xb1, 0xdd, 0xcf, 0xac, 0xd4, 0x44, 0xa7, 0xc9, 0x1e, 0x36, 0x2b, + 0xca, 0x36, 0xe2, 0x69, 0x52, 0x02, 0xc3, 0x26, 0xb7, 0x60, 0xac, 0x54, 0xaf, 0xbb, 0x5d, 0xe5, + 0xce, 0xc8, 0xd7, 0x2d, 0x07, 0xea, 0x2a, 0x92, 0x08, 0x95, 0xd4, 0x60, 0x7c, 0x99, 0x5d, 0xb4, + 0x9c, 0xb2, 0x5d, 0xdf, 0x97, 0x9d, 0x24, 0xf7, 0x30, 0xa5, 0x24, 0x5a, 0xb9, 0x14, 0x81, 0x75, + 0x06, 0x54, 0x95, 0x0c, 0x0a, 0x2e, 0xd9, 0x84, 0xf1, 0x1a, 0xad, 0x7b, 0x34, 0xa8, 0x05, 0xae, + 0x47, 0x63, 0x5b, 0xb2, 0x52, 0xb2, 0xf4, 0x82, 0xbc, 0xeb, 0xf9, 0x08, 0xb4, 0x7c, 0x06, 0x55, + 0xb9, 0x2a, 0xc8, 0x5c, 0x68, 0x6f, 0xb9, 0xde, 0x51, 0x65, 0x49, 0x6c, 0xd3, 0xd1, 0x99, 0xce, + 0xc1, 0xaa, 0xd0, 0xce, 0x20, 0x8d, 0x1d, 0x5d, 0x68, 0xe7, 0x58, 0x38, 0x52, 0x95, 0x1a, 0xca, + 0x56, 0x62, 0xd3, 0x9e, 0x8e, 0x7a, 0x19, 0xc1, 0xca, 0x48, 0x35, 0x7c, 0x94, 0xcc, 0xb4, 0x91, + 0x12, 0x58, 0xa4, 0x03, 0x44, 0x8e, 0x1a, 0x17, 0x74, 0x9b, 0xd4, 0xf7, 0xc5, 0x5e, 0x7e, 0x31, + 0x36, 0xf8, 0x11, 0xc2, 0xd2, 0x2b, 0x82, 0xf9, 0xf3, 0x72, 0x1a, 0x88, 0x7b, 0x1a, 0x2b, 0x54, + 0xea, 0x49, 0xe1, 0x4d, 0xde, 0x02, 0x58, 0x7e, 0x14, 0x50, 0xaf, 0x6d, 0x37, 0x43, 0x3d, 0x18, + 0xaa, 0x7e, 0xa8, 0x80, 0xea, 0x03, 0xad, 0x20, 0x93, 0x32, 0x4c, 0x96, 0x7c, 0xbf, 0xdb, 0xa2, + 0xa6, 0xdb, 0xa4, 0x25, 0x73, 0x1d, 0xf7, 0xfd, 0xb1, 0xa5, 0xe7, 0x4f, 0x8e, 0x8b, 0x17, 0x6d, + 0x2c, 0xb0, 0x3c, 0xb7, 0x49, 0x2d, 0xdb, 0x53, 0x67, 0xb7, 0x4e, 0x43, 0xee, 0x03, 0xdc, 0xef, + 0xd0, 0x76, 0x8d, 0xda, 0x5e, 0x7d, 0x3f, 0xb6, 0xcd, 0x47, 0x05, 0x4b, 0xcf, 0x89, 0x16, 0xce, + 0xb9, 0x1d, 0xda, 0xf6, 0x11, 0xa6, 0x7e, 0x55, 0x84, 0x49, 0xb6, 0x61, 0xba, 0x5a, 0x5a, 0xdb, + 0x70, 0x9b, 0x4e, 0xfd, 0x48, 0x48, 0x4e, 0x53, 0xa8, 0x1d, 0x3c, 0x2f, 0xb8, 0xc6, 0x4a, 0xf9, + 0xf6, 0xe4, 0xd8, 0x2d, 0xab, 0x83, 0x50, 0x4b, 0xc8, 0x4f, 0x71, 0x2e, 0xe4, 0x43, 0x36, 0x07, + 0x7d, 0x26, 0x0c, 0x6e, 0xda, 0x7b, 0xfe, 0xfc, 0xb4, 0xa6, 0xed, 0x2a, 0x6d, 0xd7, 0xae, 0x29, + 0xa5, 0x5c, 0x4c, 0x59, 0xe0, 0x13, 0x11, 0xa1, 0x56, 0x60, 0xef, 0xf9, 0xfa, 0x44, 0x0c, 0xb1, + 0xc9, 0x5d, 0x80, 0x8a, 0x5b, 0xef, 0xb6, 0x68, 0x3b, 0xa8, 0x2c, 0xcd, 0x17, 0xf4, 0xab, 0x40, + 0x58, 0x10, 0x6d, 0x6d, 0x0d, 0xb7, 0xae, 0xcd, 0x44, 0x85, 0x7a, 0xe1, 0x3d, 0x28, 0xc4, 0x3f, + 0xe4, 0x8c, 0x0a, 0xac, 0xc9, 0xc2, 0x94, 0xd2, 0xfa, 0xe5, 0x47, 0x8e, 0x1f, 0xf8, 0xc6, 0x37, + 0xb4, 0x15, 0xc8, 0x76, 0x87, 0x7b, 0xf4, 0x68, 0xc3, 0xa3, 0xbb, 0xce, 0x23, 0xb1, 0x99, 0xe1, + 0xee, 0x70, 0x40, 0x8f, 0xac, 0x0e, 0x42, 0xd5, 0xdd, 0x21, 0x44, 0x25, 0x9f, 0x81, 0xfc, 0xbd, + 0xb5, 0xda, 0x3d, 0x7a, 0x54, 0xad, 0x88, 0x83, 0x8a, 0x93, 0xb5, 0x7c, 0x8b, 0x91, 0x6a, 0x73, + 0x2d, 0xc4, 0x34, 0x96, 0xa2, 0x9d, 0x90, 0xd5, 0x5c, 0x6e, 0x76, 0xfd, 0x80, 0x7a, 0xd5, 0x8a, + 0x5a, 0x73, 0x9d, 0x03, 0x63, 0xfb, 0x52, 0x88, 0x6a, 0xfc, 0x9b, 0x2c, 0xee, 0x82, 0x6c, 0xc2, + 0x57, 0xdb, 0x7e, 0x60, 0xb7, 0xeb, 0x34, 0x64, 0x80, 0x13, 0xde, 0x11, 0xd0, 0xd8, 0x84, 0x8f, + 0x90, 0xf5, 0xaa, 0xb3, 0x03, 0x57, 0xcd, 0xaa, 0x94, 0x9a, 0x8b, 0x6a, 0x45, 0x55, 0xaf, 0x7a, + 0x02, 0x1a, 0xab, 0x32, 0x42, 0x26, 0x97, 0x61, 0xb4, 0x5a, 0x5a, 0x2b, 0x75, 0x83, 0x7d, 0xdc, + 0x83, 0xf3, 0x5c, 0x3e, 0x67, 0xb3, 0xd5, 0xee, 0x06, 0xfb, 0xa6, 0x2c, 0x24, 0xd7, 0xf1, 0xde, + 0xd3, 0xa6, 0x01, 0x57, 0xc3, 0x8a, 0x43, 0xd7, 0xe7, 0xa0, 0xd8, 0xb5, 0x87, 0x81, 0xc8, 0x6b, + 0x30, 0xfc, 0x60, 0xa3, 0x5c, 0xad, 0x88, 0x8b, 0x33, 0x9e, 0x44, 0x0f, 0x3b, 0x75, 0xfd, 0x4b, + 0x38, 0x0a, 0x59, 0x86, 0xa9, 0x1a, 0xad, 0x77, 0x3d, 0x27, 0x38, 0xba, 0xe3, 0xb9, 0xdd, 0x8e, + 0x3f, 0x3f, 0x8a, 0x75, 0xe0, 0x4a, 0xf7, 0x45, 0x89, 0xb5, 0x87, 0x45, 0x0a, 0x75, 0x8c, 0xc8, + 0xf8, 0xed, 0x4c, 0xb4, 0x4d, 0x92, 0xcb, 0x9a, 0x58, 0x83, 0xba, 0x1b, 0x26, 0xd6, 0xa8, 0xba, + 0x1b, 0x14, 0x70, 0x4c, 0x20, 0xe5, 0xae, 0x1f, 0xb8, 0xad, 0xe5, 0x76, 0xa3, 0xe3, 0x3a, 0xed, + 0x00, 0xa9, 0x78, 0xe7, 0x1b, 0x27, 0xc7, 0xc5, 0x17, 0xea, 0x58, 0x6a, 0x51, 0x51, 0x6c, 0xc5, + 0xb8, 0xa4, 0x50, 0x3f, 0xc6, 0x78, 0x18, 0xbf, 0x9f, 0xd5, 0x8e, 0x37, 0xf6, 0x79, 0x26, 0xed, + 0x34, 0x9d, 0x3a, 0xde, 0xe8, 0xb1, 0xa1, 0xe1, 0xac, 0xc2, 0xcf, 0xf3, 0xa2, 0x52, 0xde, 0x43, + 0x3a, 0xef, 0x14, 0x6a, 0xf2, 0x05, 0x98, 0x60, 0x92, 0x86, 0xf8, 0xe9, 0xcf, 0x67, 0xb1, 0xb3, + 0x9f, 0x43, 0x2d, 0x9c, 0x4f, 0xbd, 0x90, 0x8d, 0x26, 0xa2, 0xa8, 0x14, 0xa4, 0x01, 0xf3, 0x9b, + 0x9e, 0xdd, 0xf6, 0x9d, 0x60, 0xb9, 0x5d, 0xf7, 0x8e, 0x50, 0x32, 0x5a, 0x6e, 0xdb, 0x3b, 0x4d, + 0xda, 0xc0, 0xe6, 0xe6, 0x97, 0xae, 0x9c, 0x1c, 0x17, 0x5f, 0x0e, 0x38, 0x8e, 0x45, 0x43, 0x24, + 0x8b, 0x72, 0x2c, 0x85, 0x73, 0x4f, 0x4e, 0x4c, 0x92, 0x92, 0xdd, 0x8a, 0x8f, 0x30, 0x5c, 0x48, + 0x40, 0x49, 0x2a, 0x1c, 0x0d, 0xb6, 0x87, 0xa9, 0x9f, 0xa9, 0x12, 0x18, 0xff, 0x4f, 0x26, 0x3a, + 0x80, 0xc9, 0x3b, 0x30, 0x2e, 0x56, 0x8c, 0x32, 0x2f, 0x70, 0x07, 0x95, 0xcb, 0x2b, 0x36, 0xb2, + 0x2a, 0x3a, 0xbb, 0xf7, 0x97, 0xca, 0xab, 0xca, 0xdc, 0xc0, 0x7b, 0xbf, 0x5d, 0x6f, 0xc6, 0xa9, + 0x24, 0x1a, 0x9b, 0x04, 0x9b, 0xab, 0x35, 0xbd, 0x57, 0x70, 0x12, 0x04, 0x4d, 0x3f, 0xa5, 0x1b, + 0x14, 0xe4, 0xc7, 0x6f, 0xf8, 0xff, 0x9c, 0x49, 0x3b, 0xe7, 0xc9, 0x12, 0x4c, 0x6e, 0xbb, 0xde, + 0x01, 0x8e, 0xaf, 0xd2, 0x09, 0x38, 0xf2, 0x87, 0xb2, 0x20, 0xde, 0x20, 0x9d, 0x44, 0xfd, 0x36, + 0xa5, 0x37, 0xf4, 0x6f, 0x8b, 0x71, 0xd0, 0x08, 0xd8, 0x38, 0x84, 0x1c, 0xc3, 0xd5, 0x81, 0xe3, + 0x10, 0x7d, 0x82, 0x36, 0x85, 0x55, 0x74, 0xe3, 0xbf, 0xca, 0xa8, 0xe7, 0x39, 0xeb, 0xe4, 0x8a, + 0xdb, 0xb2, 0x9d, 0xb6, 0xd2, 0x1c, 0xfe, 0xb0, 0x84, 0xd0, 0xf8, 0x97, 0x28, 0xc8, 0xe4, 0x26, + 0xe4, 0xf9, 0xaf, 0x70, 0xaf, 0x45, 0xad, 0x96, 0x20, 0xd4, 0x0f, 0x0a, 0x89, 0x98, 0x18, 0x99, + 0xdc, 0x59, 0x47, 0xe6, 0xb7, 0x32, 0xea, 0x51, 0xfc, 0x71, 0x0f, 0x9b, 0xd8, 0x21, 0x93, 0x3d, + 0xcb, 0x21, 0xf3, 0xd8, 0x4d, 0xf8, 0xfe, 0x0c, 0x8c, 0x2b, 0x5a, 0x0a, 0xd6, 0x86, 0x0d, 0xcf, + 0xfd, 0x88, 0xd6, 0x03, 0xbd, 0x0d, 0x1d, 0x0e, 0x8c, 0xb5, 0x21, 0x44, 0x7d, 0x8c, 0x36, 0x18, + 0x7f, 0x96, 0x11, 0x77, 0xa4, 0x81, 0xb7, 0x79, 0x7d, 0x4b, 0xce, 0x9e, 0xe5, 0x88, 0xfc, 0x02, + 0x0c, 0x9b, 0xb4, 0xe1, 0xf8, 0xe2, 0x7e, 0x33, 0xa3, 0xde, 0xc7, 0xb0, 0x20, 0x92, 0x9b, 0x3c, + 0xf6, 0x53, 0x3d, 0xdf, 0xb0, 0x9c, 0x09, 0xb2, 0x55, 0xff, 0x76, 0x93, 0x3e, 0x72, 0xf8, 0x62, + 0x14, 0x47, 0x2d, 0x1e, 0x6f, 0x8e, 0x6f, 0xed, 0xb2, 0x12, 0x21, 0x51, 0xab, 0x0b, 0x4f, 0xa3, + 0x31, 0x3e, 0x04, 0x88, 0xaa, 0x24, 0xf7, 0xa0, 0x20, 0x66, 0x83, 0xd3, 0xde, 0xe3, 0x82, 0x94, + 0xe8, 0x83, 0xe2, 0xc9, 0x71, 0xf1, 0xd9, 0x7a, 0x58, 0x26, 0xa4, 0x4e, 0x85, 0x6f, 0x82, 0xd0, + 0xf8, 0x07, 0x59, 0xc8, 0x96, 0x70, 0x40, 0xee, 0xd1, 0xa3, 0xc0, 0xde, 0xb9, 0xed, 0x34, 0xb5, + 0xc5, 0x74, 0x80, 0x50, 0x6b, 0xd7, 0xd1, 0xd4, 0x15, 0x0a, 0x32, 0x5b, 0x4c, 0xf7, 0xbc, 0x9d, + 0x37, 0x91, 0x50, 0x59, 0x4c, 0x07, 0xde, 0xce, 0x9b, 0x71, 0xb2, 0x10, 0x91, 0x18, 0x30, 0xc2, + 0x17, 0x96, 0x98, 0x83, 0x70, 0x72, 0x5c, 0x1c, 0xe1, 0xeb, 0xcf, 0x14, 0x25, 0xe4, 0x22, 0xe4, + 0x6a, 0x1b, 0xeb, 0x62, 0x07, 0x44, 0xb5, 0xa0, 0xdf, 0x69, 0x9b, 0x0c, 0xc6, 0xea, 0x5c, 0xad, + 0x94, 0x36, 0x50, 0x11, 0x30, 0x1c, 0xd5, 0xd9, 0x6c, 0xd8, 0x9d, 0xb8, 0x2a, 0x20, 0x44, 0x24, + 0xef, 0xc2, 0xf8, 0xbd, 0x4a, 0x79, 0xc5, 0xf5, 0xf9, 0xee, 0x35, 0x12, 0x4d, 0xfe, 0x83, 0x46, + 0xdd, 0x42, 0x4d, 0x7c, 0xfc, 0x18, 0x50, 0xf0, 0x8d, 0x6f, 0x66, 0x61, 0x5c, 0xd1, 0x93, 0x91, 0xcf, 0x88, 0x07, 0xd2, 0x8c, 0x76, 0x03, 0x50, 0x30, 0x58, 0x29, 0x57, 0xaa, 0xb4, 0xdc, 0x06, 0x15, 0xcf, 0xa5, 0x91, 0x02, 0x23, 0x3b, 0x88, 0x02, 0xe3, 0x2d, 0x00, 0x3e, 0x07, 0xf0, 0x93, 0x15, 0x71, 0x42, 0xb1, 0x93, 0x50, 0xc7, 0x25, 0x42, 0x26, 0x0f, 0x60, 0x76, 0xd3, 0xeb, 0xfa, @@ -22283,7 +22288,7 @@ var fileDescriptor_9198ee693835762e = []byte{ 0x17, 0x2f, 0xa1, 0x71, 0x87, 0xe5, 0x63, 0x39, 0x7e, 0x80, 0xd5, 0x71, 0x5d, 0x55, 0xad, 0x91, 0xc6, 0xc0, 0x30, 0x61, 0x42, 0x55, 0x8a, 0xb0, 0x93, 0x45, 0x3c, 0x26, 0x09, 0x55, 0xb7, 0x72, 0xb2, 0x88, 0xaf, 0x4c, 0x3e, 0x6e, 0xe9, 0x24, 0xc6, 0x67, 0x54, 0x85, 0xdc, 0xa0, 0x0b, 0xdb, - 0xf8, 0x81, 0x4c, 0xb4, 0x8d, 0x3c, 0xb8, 0x41, 0xde, 0x86, 0x11, 0xfe, 0x78, 0x27, 0xde, 0x38, + 0xf8, 0xc1, 0x4c, 0xb4, 0x8d, 0x3c, 0xb8, 0x41, 0xde, 0x86, 0x11, 0xfe, 0x78, 0x27, 0xde, 0x38, 0xcf, 0x85, 0x97, 0x5a, 0xf5, 0x65, 0x8f, 0x6b, 0xc2, 0xff, 0x90, 0x3f, 0xf0, 0x3f, 0x63, 0x0a, 0x92, 0x50, 0x89, 0xae, 0xeb, 0xd3, 0x24, 0x77, 0x54, 0x17, 0xdf, 0x48, 0x53, 0xa2, 0x1b, 0x3f, 0x3e, 0x0c, 0x53, 0x3a, 0x9a, 0xfa, 0xc2, 0x97, 0x19, 0xe8, 0x85, 0xef, 0x0b, 0x90, 0x67, 0xfd, @@ -22299,1693 +22304,1695 @@ var fileDescriptor_9198ee693835762e = []byte{ 0xc4, 0x61, 0x72, 0x94, 0xf3, 0x91, 0x4c, 0xae, 0xb2, 0x4b, 0x99, 0xf7, 0xbd, 0x58, 0x90, 0x3b, 0x30, 0xcd, 0x3a, 0x64, 0x8d, 0xda, 0x7e, 0x97, 0x1b, 0x58, 0x09, 0xd5, 0xcc, 0xf3, 0x52, 0x13, 0xc5, 0x57, 0x61, 0xd3, 0xad, 0x1f, 0x28, 0x48, 0x66, 0x9c, 0xca, 0x38, 0xce, 0xc2, 0xf9, 0x74, - 0x5c, 0xf2, 0xbd, 0x70, 0x4e, 0xf4, 0x4b, 0x93, 0x7a, 0x0a, 0xce, 0x00, 0x36, 0x01, 0x6f, 0x88, - 0xfe, 0x7e, 0xb1, 0x1e, 0x32, 0x08, 0x37, 0x0e, 0xc6, 0x22, 0x36, 0xb8, 0xe9, 0xf5, 0x90, 0xaf, - 0xc1, 0xb8, 0x5a, 0x6d, 0x76, 0x70, 0xf3, 0x8a, 0x3e, 0x75, 0xa9, 0x2c, 0x89, 0x0d, 0xd3, 0x26, - 0xfd, 0x7a, 0x97, 0xfa, 0x81, 0x34, 0xf0, 0x10, 0x47, 0xf7, 0xc5, 0x44, 0x2d, 0x12, 0x21, 0xd4, - 0xff, 0x14, 0x3c, 0x4e, 0x69, 0x49, 0x33, 0xbc, 0x6f, 0x32, 0xf6, 0x71, 0x7e, 0xc6, 0xb7, 0xb3, - 0x70, 0xa1, 0xc7, 0xb4, 0x64, 0x3b, 0x17, 0x0a, 0x56, 0xca, 0xce, 0x15, 0x93, 0xa7, 0xb8, 0x75, - 0xd8, 0x25, 0xc8, 0x0a, 0x51, 0x64, 0x68, 0xa9, 0x70, 0x72, 0x5c, 0x9c, 0xd0, 0x56, 0x5c, 0xb6, - 0x5a, 0x21, 0x77, 0x61, 0x88, 0x75, 0xc3, 0x00, 0x46, 0x0e, 0x52, 0xfb, 0x37, 0x15, 0x38, 0xea, - 0x42, 0xc7, 0xbe, 0x41, 0x1e, 0xe4, 0x33, 0x90, 0xdb, 0xdc, 0x5c, 0xc5, 0x55, 0x9e, 0xc3, 0x59, - 0x3a, 0x19, 0x04, 0x4d, 0x6d, 0x53, 0x99, 0x64, 0xb4, 0x61, 0x8f, 0x98, 0x0c, 0x9d, 0x7c, 0x31, - 0x66, 0x7c, 0xf5, 0x5a, 0xff, 0x25, 0x39, 0xb8, 0x2d, 0xd6, 0x63, 0x98, 0x40, 0x19, 0xbf, 0x90, - 0x8d, 0x76, 0xdb, 0xdb, 0x4e, 0x33, 0xa0, 0x1e, 0x59, 0xe0, 0x9b, 0x67, 0x24, 0x46, 0x9b, 0xe1, - 0x6f, 0x32, 0x1f, 0xed, 0xc4, 0x9c, 0x55, 0xb8, 0xe5, 0xbe, 0xa6, 0x6c, 0xb9, 0x39, 0xdc, 0x72, - 0xa7, 0x7a, 0x6e, 0xae, 0xaf, 0xa5, 0xec, 0x20, 0xb8, 0x65, 0xa6, 0xec, 0x12, 0x2f, 0xc3, 0xe4, - 0xba, 0xbb, 0xfc, 0x28, 0x08, 0x11, 0xd9, 0x56, 0x99, 0x37, 0x75, 0x20, 0xe3, 0x78, 0xbf, 0xd9, - 0xa0, 0xde, 0xe6, 0xbe, 0xdd, 0xd6, 0xac, 0x0c, 0xcc, 0x04, 0x9c, 0xe1, 0xae, 0xd3, 0x43, 0x1d, - 0x77, 0x94, 0xe3, 0xc6, 0xe1, 0xc6, 0xf7, 0x67, 0x65, 0x67, 0x3c, 0x58, 0x7c, 0x4a, 0x5f, 0xb3, - 0xdf, 0xd4, 0x5e, 0xb3, 0x67, 0x43, 0x3d, 0x7c, 0x68, 0x9a, 0xb1, 0x78, 0x8a, 0x45, 0xc7, 0xdf, - 0x19, 0x81, 0x09, 0x15, 0x9d, 0xf5, 0x43, 0xa9, 0xd1, 0xf0, 0xd4, 0x7e, 0xb0, 0x1b, 0x0d, 0xcf, - 0x44, 0xa8, 0x66, 0xc0, 0x91, 0xeb, 0x6b, 0xc0, 0xf1, 0x55, 0x18, 0x2b, 0xb7, 0x1a, 0xda, 0xb3, - 0xb2, 0x91, 0xf2, 0x79, 0xd7, 0x42, 0x24, 0xbe, 0x16, 0x42, 0xf5, 0x72, 0xbd, 0xd5, 0x48, 0x3e, - 0x26, 0x47, 0x2c, 0x35, 0xdb, 0x8f, 0xe1, 0xc7, 0xb1, 0xfd, 0xb8, 0x05, 0x63, 0x5b, 0x3e, 0xdd, - 0xec, 0xb6, 0xdb, 0xb4, 0x89, 0xd3, 0x2a, 0xcf, 0x6f, 0x65, 0x5d, 0x9f, 0x5a, 0x01, 0x42, 0xd5, - 0x0f, 0x08, 0x51, 0xd5, 0x01, 0x1e, 0xed, 0x33, 0xc0, 0x37, 0x21, 0xbf, 0x41, 0xa9, 0x87, 0x7d, - 0x3a, 0x1e, 0x09, 0xdf, 0x1d, 0x4a, 0x3d, 0x8b, 0x75, 0xac, 0x66, 0x13, 0x22, 0x10, 0x35, 0x43, - 0x92, 0x89, 0x01, 0x0d, 0x49, 0xc8, 0x8b, 0x30, 0xd1, 0xe9, 0xee, 0x34, 0x9d, 0x3a, 0xf2, 0x15, - 0x16, 0x28, 0xe6, 0x38, 0x87, 0x31, 0xb6, 0x3e, 0xf9, 0x22, 0x4c, 0xe2, 0x6d, 0x34, 0x9c, 0x72, - 0x53, 0xda, 0xfb, 0xab, 0x56, 0xc6, 0x65, 0xd2, 0x3a, 0x03, 0x59, 0x29, 0x86, 0x52, 0x3a, 0x23, - 0x72, 0x17, 0x46, 0xf7, 0x9c, 0xc0, 0xda, 0xef, 0xee, 0xcc, 0x4f, 0x6b, 0x56, 0x46, 0x77, 0x9c, - 0x60, 0xa5, 0xbb, 0xc3, 0x87, 0x3c, 0x64, 0x8d, 0x3b, 0xde, 0x9e, 0x13, 0xec, 0x77, 0x55, 0xe5, - 0xf9, 0xc8, 0x1e, 0xe2, 0x2e, 0xd4, 0x60, 0x4a, 0x9f, 0x15, 0x4f, 0xe0, 0x49, 0x37, 0x34, 0xb0, - 0xc9, 0x17, 0xc6, 0xee, 0x0e, 0xe5, 0xa1, 0x30, 0xce, 0x4d, 0x6b, 0x4c, 0xd8, 0x08, 0xfb, 0xc7, - 0x24, 0xf7, 0xba, 0x3b, 0xd4, 0x6b, 0xd3, 0x80, 0xfa, 0xe2, 0xea, 0xe7, 0x9b, 0x43, 0xa5, 0x4e, - 0xc7, 0x37, 0xfe, 0xd3, 0x2c, 0x8c, 0x96, 0xb6, 0x6b, 0xd5, 0xf6, 0xae, 0x8b, 0x0f, 0xb3, 0xe1, - 0x7b, 0x9c, 0xfa, 0x30, 0x1b, 0xbe, 0xc7, 0xa9, 0xaf, 0x70, 0xd7, 0x53, 0x2e, 0xef, 0x68, 0xbb, - 0xad, 0x5c, 0xde, 0x35, 0xb5, 0x43, 0xf4, 0x34, 0x99, 0x1b, 0xe0, 0x69, 0x32, 0xd4, 0x1e, 0x0f, - 0x9d, 0xae, 0x3d, 0x7e, 0x1b, 0xc6, 0xab, 0xed, 0x80, 0xee, 0x79, 0xd1, 0xaa, 0x09, 0x15, 0x09, - 0x21, 0x58, 0xbd, 0xd0, 0x29, 0xd8, 0x6c, 0x4a, 0x72, 0x8d, 0x75, 0xa8, 0xa9, 0xc6, 0x29, 0xc9, - 0x15, 0xdb, 0x31, 0x2d, 0x90, 0x44, 0x34, 0x2a, 0xb1, 0xf9, 0x26, 0xcd, 0x3f, 0xb8, 0x08, 0x35, - 0x15, 0x3d, 0xd9, 0xb0, 0x8e, 0x5d, 0x9a, 0x49, 0x37, 0xff, 0x30, 0xfe, 0x46, 0x06, 0xe6, 0xd2, - 0xa6, 0x11, 0x79, 0x0f, 0x26, 0x5c, 0x6f, 0xcf, 0x6e, 0x3b, 0xdf, 0xcd, 0x5b, 0xa4, 0xa8, 0x2a, - 0x55, 0xb8, 0xaa, 0xa0, 0x51, 0xe1, 0xac, 0x43, 0x94, 0x96, 0xeb, 0x9a, 0x95, 0xd4, 0x0e, 0x51, - 0xc0, 0xc6, 0x8f, 0x64, 0x61, 0xbc, 0xd4, 0xe9, 0x3c, 0xe5, 0xa6, 0x81, 0x9f, 0xd3, 0x0e, 0x10, - 0x79, 0x2f, 0x0f, 0xdb, 0x35, 0x90, 0x55, 0xe0, 0xaf, 0x65, 0x61, 0x3a, 0x46, 0xa1, 0x7e, 0x7d, - 0x66, 0x40, 0x83, 0xc0, 0xec, 0x80, 0x06, 0x81, 0xb9, 0xc1, 0x0c, 0x02, 0x87, 0x1e, 0xe7, 0x50, - 0x78, 0x15, 0x72, 0xa5, 0x4e, 0x27, 0x6e, 0x58, 0xd0, 0xe9, 0x3c, 0xb8, 0xc9, 0x75, 0x2b, 0x76, - 0xa7, 0x63, 0x32, 0x0c, 0x6d, 0xa7, 0x1e, 0x19, 0x70, 0xa7, 0x36, 0xde, 0x80, 0x31, 0xe4, 0x85, - 0x66, 0x78, 0x97, 0x00, 0xb7, 0x18, 0x61, 0x81, 0xa7, 0xd5, 0x25, 0x36, 0x9f, 0xff, 0x3f, 0x03, - 0xc3, 0xf8, 0xfb, 0x29, 0x9d, 0x63, 0x8b, 0xda, 0x1c, 0x2b, 0x28, 0x73, 0x6c, 0x90, 0xd9, 0xf5, - 0xf7, 0x73, 0x00, 0xe5, 0xfb, 0x66, 0x8d, 0xab, 0xe0, 0xc8, 0x6d, 0x98, 0xb6, 0x9b, 0x4d, 0xf7, - 0x90, 0x36, 0x2c, 0xd7, 0x73, 0xf6, 0x9c, 0x36, 0xef, 0x39, 0xf9, 0xda, 0xad, 0x17, 0xa9, 0x6f, - 0x60, 0xa2, 0xe8, 0x3e, 0x2f, 0x51, 0xf9, 0xb4, 0x68, 0xb0, 0xef, 0x36, 0xa4, 0x32, 0x41, 0xe3, - 0x23, 0x8a, 0x52, 0xf8, 0xac, 0xf1, 0x12, 0x95, 0xcf, 0x3e, 0x2a, 0x47, 0xa4, 0x84, 0xac, 0xf1, - 0x11, 0x45, 0x29, 0x7c, 0xb8, 0x46, 0xc5, 0x27, 0xab, 0x30, 0x83, 0x10, 0xab, 0xee, 0xd1, 0x06, - 0x6d, 0x07, 0x8e, 0xdd, 0xf4, 0x85, 0xfa, 0x09, 0x15, 0x95, 0x89, 0x42, 0xf5, 0xfa, 0x8d, 0x85, - 0xe5, 0xa8, 0x8c, 0x5c, 0x83, 0xd1, 0x96, 0xfd, 0xc8, 0xb2, 0xf7, 0xb8, 0xdd, 0xc7, 0x24, 0x57, - 0x57, 0x08, 0x90, 0x7a, 0x8c, 0xb4, 0xec, 0x47, 0xa5, 0x3d, 0xca, 0x5a, 0x41, 0x1f, 0x75, 0x5c, - 0x5f, 0x69, 0xc5, 0x48, 0xd4, 0x8a, 0x58, 0x91, 0xda, 0x0a, 0x51, 0x24, 0x5a, 0x61, 0xfc, 0x6a, - 0x06, 0x9e, 0xad, 0xe2, 0x57, 0x04, 0x47, 0x65, 0xda, 0x0e, 0xa8, 0xb7, 0x41, 0xbd, 0x96, 0x83, - 0xaf, 0xe0, 0x35, 0x1a, 0x90, 0x97, 0x20, 0x57, 0x32, 0xd7, 0xc5, 0xfc, 0xe5, 0xfb, 0xbd, 0x66, - 0x93, 0xc0, 0x4a, 0x43, 0x8d, 0x56, 0xf6, 0x14, 0x55, 0x75, 0x09, 0x26, 0x4a, 0xbe, 0xef, 0xec, - 0xb5, 0x5b, 0xdc, 0x9f, 0x22, 0xa7, 0x59, 0x3d, 0x08, 0x78, 0xe2, 0x8d, 0x45, 0x25, 0x31, 0xfe, - 0xb3, 0x0c, 0xcc, 0x94, 0x3a, 0x1d, 0xfd, 0x93, 0x75, 0x8b, 0x9b, 0xcc, 0xe0, 0x16, 0x37, 0x0e, - 0x4c, 0x69, 0xcd, 0xe5, 0x53, 0x2a, 0x12, 0x7c, 0xfb, 0xf4, 0x0c, 0xff, 0xec, 0x4e, 0x08, 0xb2, - 0x7c, 0xfd, 0xb9, 0x38, 0xc6, 0xd8, 0xf8, 0x0f, 0x46, 0x71, 0x0f, 0x11, 0xbb, 0xad, 0xb0, 0x09, - 0xcd, 0xa4, 0xd8, 0x84, 0xbe, 0x05, 0x8a, 0x84, 0xa3, 0x1e, 0x71, 0x8a, 0xac, 0xa8, 0xea, 0x82, - 0x22, 0x64, 0x72, 0x10, 0xb7, 0x0e, 0xcd, 0x61, 0x6b, 0x5e, 0x8a, 0x2f, 0xe0, 0x27, 0x62, 0x18, - 0xba, 0x02, 0xa4, 0xda, 0xc6, 0x27, 0x6c, 0x5a, 0x3b, 0x70, 0x3a, 0x0f, 0xa8, 0xe7, 0xec, 0x1e, - 0x89, 0x05, 0x80, 0x9d, 0xef, 0x88, 0x52, 0xcb, 0x3f, 0x70, 0x3a, 0xd6, 0x43, 0x2c, 0x37, 0x53, - 0x68, 0xc8, 0xfb, 0x30, 0x6a, 0xd2, 0x43, 0xcf, 0x09, 0xa4, 0xcd, 0xd3, 0x54, 0xa8, 0xda, 0x44, - 0x28, 0x5f, 0x0b, 0x1e, 0xff, 0xa1, 0xee, 0x8a, 0xa2, 0x9c, 0x2c, 0x72, 0x21, 0x85, 0xdb, 0x36, - 0x4d, 0x46, 0xad, 0x2d, 0x6d, 0xd7, 0x7a, 0xc9, 0x28, 0xe4, 0x2a, 0x0c, 0xa3, 0xa4, 0x23, 0xee, - 0x02, 0xe8, 0x2b, 0x84, 0xb2, 0xb3, 0x2a, 0x86, 0x21, 0x06, 0x79, 0x01, 0x20, 0x7c, 0x23, 0xf6, - 0xe7, 0xf3, 0x28, 0xa5, 0x2b, 0x90, 0xb8, 0x98, 0x36, 0x76, 0x26, 0x31, 0x6d, 0x15, 0x0a, 0x26, - 0x77, 0x3b, 0x6c, 0x94, 0x3a, 0xf8, 0x10, 0xe9, 0xcf, 0x03, 0xae, 0xe4, 0x4b, 0x27, 0xc7, 0xc5, - 0xe7, 0x84, 0x4b, 0x62, 0xc3, 0xb2, 0x3b, 0xfc, 0xfd, 0x52, 0xdb, 0x46, 0xe2, 0x94, 0xe4, 0x2d, - 0x18, 0x62, 0x5b, 0xaf, 0xb0, 0x23, 0x95, 0x0f, 0x3a, 0xd1, 0x6e, 0xcc, 0x17, 0x67, 0xdd, 0xd5, - 0xf6, 0x04, 0x24, 0x21, 0x16, 0x4c, 0xe9, 0xd3, 0x5d, 0x98, 0x14, 0xcd, 0x47, 0xfd, 0xa9, 0x97, - 0x8b, 0x57, 0x1e, 0x01, 0xb3, 0xea, 0x08, 0x54, 0x57, 0x40, 0x6c, 0x91, 0x2e, 0x43, 0x7e, 0xb3, - 0xbc, 0xb1, 0xe1, 0x7a, 0x01, 0xbf, 0xea, 0x44, 0x27, 0x0b, 0x83, 0x99, 0x76, 0x7b, 0x8f, 0xf2, - 0xb3, 0x38, 0xa8, 0x77, 0xac, 0x0e, 0x43, 0x53, 0xcf, 0x62, 0x49, 0xfa, 0xc9, 0xd9, 0x90, 0xfe, - 0x5a, 0x16, 0x5e, 0x0a, 0xa5, 0xa2, 0xfb, 0x5e, 0xad, 0xb4, 0xb6, 0x5a, 0x6d, 0x6c, 0x08, 0x35, - 0xc9, 0x86, 0xe7, 0x3e, 0x74, 0x1a, 0xd4, 0x7b, 0x70, 0xe3, 0x94, 0x33, 0x7d, 0x95, 0x2f, 0x73, - 0xfe, 0x1a, 0x96, 0xd5, 0xac, 0xed, 0x14, 0xe1, 0x53, 0x6c, 0x4f, 0x9d, 0x4e, 0xe2, 0x71, 0x6c, - 0xe5, 0x19, 0x33, 0x62, 0x40, 0x7e, 0x20, 0x03, 0xe7, 0xd3, 0x3f, 0x44, 0xa8, 0xce, 0x8a, 0xf2, - 0x8a, 0xde, 0xe3, 0x6b, 0x97, 0x5e, 0x3d, 0x39, 0x2e, 0xbe, 0xe4, 0xdb, 0xad, 0xa6, 0xe5, 0x34, - 0x78, 0x6d, 0x4e, 0x9d, 0x5a, 0x1d, 0x81, 0xa0, 0xd5, 0xdb, 0xa3, 0xa6, 0xcf, 0x83, 0x3c, 0xda, - 0xe7, 0x33, 0x4b, 0x00, 0x79, 0xf9, 0xe0, 0x60, 0xfc, 0x66, 0x06, 0x94, 0x25, 0x98, 0x37, 0x69, - 0xc3, 0xf1, 0x68, 0x3d, 0x10, 0xc7, 0xbb, 0xf0, 0x15, 0xe4, 0xb0, 0x98, 0x71, 0x25, 0xc2, 0xc8, - 0x7b, 0x30, 0x2a, 0x8e, 0x21, 0xb1, 0xed, 0xca, 0xa5, 0x2b, 0x9e, 0x32, 0xb8, 0x53, 0x69, 0xe2, - 0x08, 0x93, 0x44, 0x6c, 0xd7, 0xbf, 0xbb, 0xbd, 0x59, 0x6e, 0xda, 0x4e, 0xcb, 0x17, 0x67, 0x09, - 0x76, 0xeb, 0x47, 0x87, 0x81, 0x55, 0x47, 0xa8, 0xba, 0xeb, 0x87, 0xa8, 0xc6, 0x1d, 0xf9, 0x92, - 0x72, 0x8a, 0x85, 0x70, 0x11, 0x86, 0x1f, 0x44, 0x7a, 0xba, 0xa5, 0xb1, 0x93, 0xe3, 0x22, 0x9f, - 0x2e, 0x26, 0x87, 0x1b, 0x14, 0xc6, 0xc2, 0xa9, 0xcb, 0x78, 0xb1, 0x1f, 0xc8, 0x6b, 0x92, 0xf3, - 0x62, 0x93, 0xd8, 0x44, 0x28, 0x13, 0xf5, 0x96, 0xdb, 0x0d, 0x44, 0xc8, 0x22, 0x02, 0x76, 0x0f, - 0x6d, 0x37, 0x70, 0xa6, 0xab, 0xad, 0x13, 0x68, 0x8a, 0x40, 0xf5, 0x63, 0x19, 0x98, 0xd2, 0xa7, - 0x2d, 0xb9, 0x06, 0x23, 0xc2, 0x1d, 0x30, 0x83, 0x6a, 0x4f, 0xc6, 0x6d, 0x84, 0x3b, 0x02, 0x6a, - 0xee, 0x7f, 0x02, 0x8b, 0xc9, 0x8d, 0x82, 0x83, 0x10, 0x9a, 0x50, 0x6e, 0xac, 0x73, 0x90, 0x29, - 0xcb, 0x88, 0xc1, 0xae, 0xb2, 0x7e, 0xb7, 0x19, 0xa8, 0xef, 0x96, 0x1e, 0x42, 0x4c, 0x51, 0x62, - 0x94, 0x61, 0x84, 0x6f, 0xad, 0x31, 0x03, 0xc8, 0xcc, 0x19, 0x0c, 0x20, 0x8d, 0xe3, 0x0c, 0x40, - 0xad, 0xb6, 0x72, 0x8f, 0x1e, 0x6d, 0xd8, 0x0e, 0x9e, 0xdf, 0xfc, 0x18, 0xbb, 0x27, 0xd6, 0xf0, - 0x84, 0x78, 0x68, 0xe7, 0x47, 0xde, 0x01, 0x3d, 0xd2, 0x1e, 0xda, 0x25, 0x2a, 0x9e, 0x95, 0x9e, - 0xf3, 0xd0, 0x0e, 0x28, 0x23, 0xcc, 0x22, 0x21, 0x3f, 0x2b, 0x39, 0x34, 0x46, 0xa9, 0x20, 0x93, - 0xaf, 0xc0, 0x54, 0xf4, 0x2b, 0x34, 0x17, 0x98, 0x0a, 0xf7, 0x09, 0xbd, 0x70, 0xe9, 0x85, 0x93, - 0xe3, 0xe2, 0x82, 0xc2, 0x35, 0x6e, 0x48, 0x10, 0x63, 0x66, 0xfc, 0x72, 0x06, 0x8d, 0x64, 0x64, - 0x03, 0x2f, 0xc3, 0x50, 0x68, 0xd6, 0x3d, 0x21, 0x36, 0x61, 0xfd, 0x49, 0x14, 0xcb, 0x99, 0xb8, - 0x15, 0xb5, 0x04, 0x8f, 0x2e, 0xbd, 0x05, 0xac, 0x94, 0xdc, 0x81, 0xd1, 0x81, 0xbe, 0x19, 0xa7, - 0x58, 0xca, 0xb7, 0x4a, 0x6a, 0x1c, 0x85, 0xbb, 0xdb, 0x9b, 0xdf, 0xb9, 0xa3, 0xf0, 0x93, 0x59, - 0x98, 0x66, 0xfd, 0x5a, 0xea, 0x06, 0xfb, 0xae, 0xe7, 0x04, 0x47, 0x4f, 0xad, 0xde, 0xf8, 0x1d, - 0xed, 0x4a, 0xb6, 0x20, 0x0f, 0x33, 0xb5, 0x6d, 0x03, 0xa9, 0x8f, 0xff, 0xe9, 0x30, 0xcc, 0xa6, - 0x50, 0x91, 0xd7, 0xb5, 0xa7, 0x9d, 0x79, 0xe9, 0xee, 0xff, 0xed, 0xe3, 0xe2, 0x84, 0x44, 0xdf, - 0x8c, 0xdc, 0xff, 0x17, 0x75, 0x8b, 0x33, 0xde, 0x53, 0xf8, 0xd2, 0xa3, 0x5a, 0x9c, 0xe9, 0x76, - 0x66, 0x57, 0x61, 0xd8, 0x74, 0x9b, 0x54, 0x5a, 0x59, 0xa2, 0xc0, 0xe5, 0x31, 0x80, 0x66, 0x55, - 0xc2, 0x00, 0x64, 0x05, 0x46, 0xd9, 0x1f, 0x6b, 0x76, 0x47, 0xbc, 0x97, 0x92, 0x50, 0x29, 0x80, - 0xd0, 0x8e, 0xd3, 0xde, 0x53, 0xf5, 0x02, 0x4d, 0x6a, 0xb5, 0xec, 0x8e, 0x26, 0x19, 0x72, 0x44, - 0x4d, 0xbf, 0x90, 0xef, 0xad, 0x5f, 0xc8, 0x9c, 0xaa, 0x5f, 0xd8, 0x05, 0xa8, 0x39, 0x7b, 0x6d, - 0xa7, 0xbd, 0x57, 0x6a, 0xee, 0x89, 0xa0, 0x09, 0x57, 0x7b, 0x8f, 0xc2, 0xb5, 0x08, 0x19, 0x27, - 0xee, 0xb3, 0x68, 0xd4, 0xc0, 0x61, 0x96, 0xdd, 0xdc, 0xd3, 0x9c, 0xbb, 0x14, 0xce, 0x64, 0x1d, - 0xa0, 0x54, 0x0f, 0x9c, 0x87, 0x6c, 0x0a, 0xfb, 0x42, 0x8c, 0x93, 0x9f, 0x5c, 0x2e, 0xdd, 0xa3, - 0x47, 0x78, 0xf5, 0x90, 0xcf, 0xc3, 0x36, 0xa2, 0xb2, 0x95, 0xa0, 0x79, 0xee, 0x44, 0x1c, 0x48, - 0x07, 0xce, 0x95, 0x1a, 0x0d, 0x87, 0xb5, 0xc1, 0x6e, 0x6e, 0xf2, 0x70, 0x17, 0xc8, 0x7a, 0x22, - 0x9d, 0xf5, 0x55, 0xf9, 0x12, 0x6a, 0x87, 0x54, 0x96, 0x8c, 0x92, 0x11, 0xab, 0x26, 0x9d, 0xb1, - 0x51, 0x83, 0x29, 0xbd, 0xf1, 0x7a, 0xb0, 0x87, 0x09, 0xc8, 0x9b, 0xb5, 0x92, 0x55, 0x5b, 0x29, - 0xdd, 0x28, 0x64, 0x48, 0x01, 0x26, 0xc4, 0xaf, 0x45, 0x6b, 0xf1, 0xcd, 0x5b, 0x85, 0xac, 0x06, - 0x79, 0xf3, 0xc6, 0x62, 0x21, 0xb7, 0x90, 0x9d, 0xcf, 0xc4, 0xfc, 0x2c, 0x47, 0x0b, 0x79, 0xae, - 0x12, 0x36, 0x7e, 0x3d, 0x03, 0x79, 0xf9, 0xed, 0xe4, 0x16, 0xe4, 0x6a, 0xb5, 0x95, 0x98, 0x67, - 0x64, 0x74, 0xca, 0xf0, 0xfd, 0xd4, 0xf7, 0x55, 0xf3, 0x77, 0x46, 0xc0, 0xe8, 0x36, 0x57, 0x6b, - 0x42, 0x06, 0x91, 0x74, 0xd1, 0xe6, 0xcd, 0xe9, 0x52, 0xdc, 0xc5, 0x6e, 0x41, 0xee, 0xee, 0xf6, - 0xa6, 0xb8, 0x64, 0x49, 0xba, 0x68, 0x3f, 0xe5, 0x74, 0x1f, 0x1d, 0xaa, 0xbb, 0x3c, 0x23, 0x30, - 0x4c, 0x18, 0x57, 0x26, 0x32, 0x3f, 0x74, 0x5b, 0x6e, 0x18, 0xe1, 0x40, 0x1c, 0xba, 0x0c, 0x62, - 0x8a, 0x12, 0x26, 0x8a, 0xac, 0xba, 0x75, 0xbb, 0x29, 0x4e, 0x6f, 0x14, 0x45, 0x9a, 0x0c, 0x60, - 0x72, 0xb8, 0xf1, 0x3b, 0x19, 0x28, 0xa0, 0xc0, 0x86, 0xe6, 0xeb, 0xee, 0x01, 0x6d, 0x3f, 0xb8, - 0x41, 0xde, 0x90, 0x4b, 0x2e, 0x13, 0x2a, 0xba, 0x86, 0x71, 0xc9, 0xc5, 0xde, 0x02, 0xc5, 0xb2, - 0x53, 0x82, 0x48, 0x64, 0x07, 0x77, 0x3e, 0x3f, 0x25, 0x88, 0x44, 0x11, 0x86, 0xf1, 0x73, 0xc4, - 0xe6, 0x88, 0x5f, 0x1e, 0x30, 0x80, 0xc9, 0xe1, 0xca, 0xde, 0xf4, 0xd3, 0xd9, 0x44, 0x1b, 0x16, - 0xbf, 0xa3, 0x1c, 0xb8, 0xf5, 0xc6, 0x0d, 0xb4, 0x5f, 0x7f, 0x08, 0x73, 0xf1, 0x2e, 0x41, 0x25, - 0x64, 0x09, 0xa6, 0x75, 0xb8, 0xd4, 0x47, 0x5e, 0x48, 0xad, 0xeb, 0xc1, 0xa2, 0x19, 0xc7, 0x37, - 0xfe, 0x8f, 0x0c, 0x8c, 0xe1, 0x9f, 0x66, 0xb7, 0x89, 0x66, 0x84, 0xa5, 0xed, 0x9a, 0x50, 0x8d, - 0xa8, 0xc2, 0x9c, 0x7d, 0xe8, 0x5b, 0x42, 0x8f, 0xa2, 0xed, 0x31, 0x21, 0xb2, 0x20, 0xe5, 0xef, - 0x1b, 0x52, 0x29, 0x17, 0x92, 0xf2, 0x87, 0x10, 0x3f, 0x46, 0x2a, 0x90, 0xd1, 0xf8, 0x78, 0xbb, - 0xc6, 0xa6, 0x9f, 0x6a, 0xd7, 0x83, 0x74, 0x6e, 0x53, 0x37, 0x3e, 0xe6, 0x68, 0x68, 0xd6, 0xb3, - 0x5d, 0x2b, 0x99, 0xeb, 0x9a, 0x59, 0x0f, 0xfb, 0x46, 0x4d, 0x2f, 0x25, 0x90, 0x8c, 0x5f, 0x18, - 0x8f, 0x77, 0xa0, 0x38, 0xf0, 0xce, 0xb8, 0x36, 0xde, 0x86, 0xe1, 0x52, 0xb3, 0xe9, 0x1e, 0x8a, - 0x5d, 0x42, 0xde, 0x5c, 0xc3, 0xfe, 0xe3, 0xe7, 0x19, 0xaa, 0xf5, 0x34, 0xa7, 0x54, 0x06, 0x20, - 0x65, 0x18, 0x2b, 0x6d, 0xd7, 0xaa, 0xd5, 0xca, 0xe6, 0x26, 0x77, 0xc0, 0xcb, 0x2d, 0xbd, 0x22, - 0xfb, 0xc7, 0x71, 0x1a, 0x56, 0xdc, 0x5e, 0x21, 0x92, 0xdf, 0x23, 0x3a, 0xf2, 0x2e, 0xc0, 0x5d, - 0xd7, 0x69, 0x73, 0x35, 0xa6, 0x68, 0x3c, 0xbb, 0x81, 0x8f, 0x7f, 0xe4, 0x3a, 0x6d, 0xa1, 0xf7, - 0x64, 0xdf, 0x1e, 0x21, 0x99, 0xca, 0xdf, 0xac, 0xa7, 0x97, 0x5c, 0x6e, 0x1a, 0x38, 0x1c, 0xf5, - 0xf4, 0x8e, 0x9b, 0xd0, 0xb7, 0x49, 0x34, 0xd2, 0x82, 0xe9, 0x5a, 0x77, 0x6f, 0x8f, 0xb2, 0x9d, - 0x5d, 0xe8, 0x93, 0x46, 0xc4, 0x55, 0x3a, 0x0c, 0x7b, 0xc4, 0xef, 0x23, 0xec, 0x32, 0xe4, 0x2f, - 0xbd, 0xce, 0x26, 0xf2, 0xb7, 0x8e, 0x8b, 0xc2, 0x0e, 0x82, 0x89, 0x6a, 0xbe, 0xa4, 0x4f, 0x6a, - 0x93, 0xe2, 0xbc, 0xc9, 0x7d, 0x18, 0xe1, 0x6f, 0x46, 0xc2, 0xa1, 0xec, 0xc5, 0x3e, 0x8b, 0x86, - 0x23, 0xf6, 0x7a, 0x95, 0xe4, 0xa5, 0x64, 0x1b, 0xf2, 0x65, 0xc7, 0xab, 0x37, 0x69, 0xb9, 0x2a, - 0xce, 0xfe, 0x97, 0xfa, 0xb0, 0x94, 0xa8, 0xbc, 0x5f, 0xea, 0xf8, 0xab, 0xee, 0xa8, 0xb2, 0x80, - 0xc4, 0x20, 0x7f, 0x23, 0x03, 0xcf, 0x86, 0x5f, 0x5f, 0xda, 0xa3, 0xed, 0x60, 0xcd, 0x0e, 0xea, - 0xfb, 0xd4, 0x13, 0xbd, 0x34, 0xd6, 0xaf, 0x97, 0x3e, 0x9f, 0xe8, 0xa5, 0x2b, 0x51, 0x2f, 0xd9, - 0x8c, 0x99, 0xd5, 0xe2, 0xdc, 0x92, 0x7d, 0xd6, 0xaf, 0x56, 0x62, 0x01, 0x44, 0xaf, 0xa1, 0xc2, - 0x21, 0xf9, 0x95, 0x3e, 0x0d, 0x8e, 0x90, 0x85, 0x23, 0x51, 0xf8, 0x5b, 0xb3, 0x84, 0x0d, 0xa1, - 0xe4, 0x9e, 0xf4, 0xde, 0xe4, 0x52, 0xc9, 0xa5, 0x3e, 0xbc, 0xb9, 0x47, 0xe7, 0x6c, 0x1f, 0x3f, - 0x6d, 0x3e, 0xda, 0xab, 0xf6, 0x8e, 0x10, 0x44, 0x4e, 0x19, 0xed, 0x55, 0x3b, 0x1a, 0xed, 0xa6, - 0x1d, 0x1f, 0xed, 0x55, 0x7b, 0x87, 0x94, 0xb9, 0xcb, 0x39, 0xf7, 0x4f, 0x7e, 0xa1, 0x1f, 0xb7, - 0xf2, 0x06, 0x3f, 0x99, 0x53, 0x5c, 0xcf, 0xbf, 0x04, 0x63, 0xb5, 0x8e, 0x5d, 0xa7, 0x4d, 0x67, - 0x37, 0x10, 0x4f, 0xed, 0x2f, 0xf7, 0x61, 0x15, 0xe2, 0x8a, 0xa7, 0x55, 0xf9, 0x53, 0xbd, 0x26, - 0x85, 0x38, 0xec, 0x0b, 0x37, 0x37, 0xd6, 0xc4, 0x6b, 0x7b, 0xbf, 0x2f, 0xdc, 0xdc, 0x58, 0x13, - 0x32, 0x47, 0xa7, 0xa5, 0xc9, 0x1c, 0x1b, 0x6b, 0xa4, 0x03, 0x53, 0x9b, 0xd4, 0xf3, 0xec, 0x5d, - 0xd7, 0x6b, 0x71, 0xfd, 0x25, 0xf7, 0x79, 0xbb, 0xda, 0x8f, 0x9f, 0x46, 0xc0, 0xd5, 0x76, 0x81, - 0x84, 0x59, 0x71, 0xa5, 0x67, 0x8c, 0x3f, 0xeb, 0x93, 0x25, 0x27, 0xd8, 0xe9, 0xd6, 0x0f, 0x68, - 0x30, 0x3f, 0x73, 0x6a, 0x9f, 0x84, 0xb8, 0xbc, 0x4f, 0x76, 0xe4, 0x4f, 0xb5, 0x4f, 0x42, 0x1c, - 0xe3, 0x1f, 0xe5, 0xe0, 0x42, 0x8f, 0x2e, 0x20, 0xeb, 0x72, 0xcb, 0xcd, 0x68, 0x5a, 0xec, 0x1e, - 0xe8, 0xd7, 0x4e, 0xdd, 0x85, 0x57, 0xa1, 0xb0, 0x7c, 0x0f, 0x65, 0x75, 0xfe, 0x90, 0x53, 0x2e, - 0xc9, 0xc3, 0x0a, 0x35, 0xad, 0xf4, 0x00, 0x6d, 0x84, 0xe5, 0x03, 0x50, 0x5d, 0x73, 0x86, 0x4f, - 0x50, 0x2e, 0x7c, 0x7f, 0x16, 0x86, 0xf0, 0xe0, 0x8c, 0x85, 0x00, 0xcb, 0x9c, 0x29, 0x04, 0xd8, - 0x17, 0x60, 0x62, 0xf9, 0x1e, 0xbf, 0x49, 0xaf, 0xd8, 0xfe, 0xbe, 0xd8, 0xd6, 0xd1, 0x90, 0x83, - 0x1e, 0x58, 0xe2, 0xe2, 0xbd, 0x6f, 0x6b, 0x32, 0xab, 0x46, 0x41, 0xb6, 0x60, 0x96, 0x7f, 0x9b, - 0xb3, 0xeb, 0xd4, 0x79, 0x24, 0x21, 0xc7, 0x6e, 0x8a, 0x3d, 0xfe, 0xa5, 0x93, 0xe3, 0x62, 0x91, - 0x1e, 0xa0, 0xf5, 0xb3, 0x28, 0xb7, 0x7c, 0x44, 0x50, 0xcd, 0xa0, 0x53, 0xe8, 0xd5, 0xf0, 0x26, - 0xe6, 0x18, 0x56, 0xc8, 0x6a, 0x63, 0x75, 0x33, 0x5c, 0x8e, 0x64, 0xfc, 0xe9, 0x30, 0x2c, 0xf4, - 0xde, 0x9e, 0xc9, 0x07, 0xfa, 0x00, 0x5e, 0x3e, 0x75, 0x43, 0x3f, 0x7d, 0x0c, 0xbf, 0x08, 0x73, - 0xcb, 0xed, 0x80, 0x7a, 0x1d, 0xcf, 0x91, 0x01, 0x6d, 0x56, 0x5c, 0x5f, 0x5a, 0x9b, 0xa3, 0xd9, - 0x37, 0x0d, 0xcb, 0x85, 0x6e, 0x15, 0x6d, 0xdf, 0x15, 0x56, 0xa9, 0x1c, 0xc8, 0x32, 0x4c, 0x29, - 0xf0, 0x66, 0x77, 0x4f, 0x7d, 0x9d, 0x52, 0x79, 0x36, 0xbb, 0xaa, 0x29, 0x6e, 0x8c, 0x08, 0x2d, - 0xda, 0xd9, 0x95, 0xb1, 0x7e, 0x77, 0xfb, 0x5e, 0x4d, 0x0c, 0x27, 0xb7, 0x68, 0x47, 0xa8, 0xf5, - 0xd1, 0xe1, 0x81, 0xb6, 0xbf, 0x46, 0xc8, 0x0b, 0xbf, 0x9c, 0x13, 0x33, 0xea, 0x25, 0xc8, 0xd5, - 0xba, 0x3b, 0xea, 0x9b, 0x9b, 0xaf, 0x1d, 0x70, 0xac, 0x94, 0x7c, 0x0e, 0xc0, 0xa4, 0x1d, 0xd7, - 0x77, 0x02, 0xd7, 0x3b, 0x52, 0x5d, 0x2a, 0xbd, 0x10, 0xaa, 0x7b, 0x7d, 0x48, 0x28, 0x59, 0x81, - 0xe9, 0xe8, 0xd7, 0xfd, 0xc3, 0xb6, 0xd0, 0x25, 0x8f, 0x71, 0xed, 0x4a, 0x44, 0x6e, 0xb9, 0xac, - 0x4c, 0x3d, 0xb2, 0x63, 0x64, 0x64, 0x11, 0xf2, 0xdb, 0xae, 0x77, 0xb0, 0xcb, 0xc6, 0x78, 0x28, - 0x12, 0x2a, 0x0e, 0x05, 0x4c, 0x3d, 0x3c, 0x25, 0x1e, 0x5b, 0x2e, 0xcb, 0xed, 0x87, 0x8e, 0xe7, - 0xe2, 0x8b, 0x9e, 0x6a, 0xd3, 0x42, 0x23, 0xb0, 0xe6, 0xcc, 0x1e, 0x81, 0xc9, 0x55, 0x18, 0x2e, - 0xd5, 0x03, 0xd7, 0x13, 0x06, 0x2d, 0x7c, 0xa6, 0x30, 0x80, 0x36, 0x53, 0x18, 0x80, 0x75, 0xa2, - 0x49, 0x77, 0xc5, 0xeb, 0x0e, 0x76, 0xa2, 0x47, 0x77, 0x35, 0x4f, 0x7d, 0xba, 0xcb, 0x84, 0x22, - 0x93, 0xee, 0xa2, 0xe2, 0x43, 0x0b, 0x70, 0xb7, 0x9b, 0x50, 0x99, 0x09, 0x34, 0xe3, 0xf7, 0xc6, - 0x7a, 0x4e, 0x79, 0x76, 0x0a, 0x9d, 0x6d, 0xca, 0xaf, 0xda, 0x03, 0x4c, 0xf9, 0xd7, 0x43, 0x5f, - 0x12, 0x35, 0x3c, 0x05, 0x42, 0xd4, 0x63, 0x90, 0xe3, 0x2c, 0xfc, 0x4a, 0xfe, 0x2c, 0x93, 0x48, - 0x74, 0x52, 0x76, 0xd0, 0x4e, 0xca, 0x0d, 0xd4, 0x49, 0x64, 0x09, 0x26, 0xc3, 0x10, 0x89, 0x1b, - 0x76, 0xa0, 0x6d, 0x6b, 0x61, 0x5c, 0x4b, 0xab, 0x63, 0x07, 0xea, 0xb6, 0xa6, 0x93, 0x90, 0x77, - 0x60, 0x5c, 0x38, 0x54, 0x21, 0x87, 0xe1, 0xc8, 0x52, 0x48, 0x7a, 0x5f, 0xc5, 0xe8, 0x55, 0x74, - 0xb6, 0x9a, 0x37, 0x9c, 0x0e, 0x6d, 0x3a, 0x6d, 0x5a, 0xc3, 0xc7, 0x0a, 0x31, 0x63, 0xf8, 0xa3, - 0xad, 0x28, 0xb1, 0xf8, 0x3b, 0x86, 0xa6, 0x3f, 0xd4, 0x88, 0xe2, 0x93, 0x75, 0xf4, 0x4c, 0x93, - 0x95, 0xdb, 0x29, 0x7a, 0xab, 0xee, 0x9e, 0x23, 0x6d, 0xe8, 0xa5, 0x9d, 0xa2, 0x67, 0x35, 0x19, - 0x34, 0x66, 0xa7, 0xc8, 0x51, 0xd9, 0x0d, 0x87, 0xfd, 0xa8, 0x56, 0xc4, 0x4b, 0x22, 0xde, 0x70, - 0x90, 0x48, 0x77, 0x5c, 0xe0, 0x48, 0xb2, 0x9a, 0xe5, 0x96, 0xed, 0x34, 0x45, 0x14, 0x82, 0xa8, - 0x1a, 0xca, 0xa0, 0xf1, 0x6a, 0x10, 0x95, 0xd4, 0x61, 0xc2, 0xa4, 0xbb, 0x1b, 0x9e, 0x1b, 0xd0, - 0x7a, 0x40, 0x1b, 0x42, 0xaa, 0x93, 0x17, 0x9b, 0x25, 0xd7, 0xe5, 0x12, 0x2b, 0xda, 0xc6, 0x67, - 0xbe, 0x75, 0x5c, 0x04, 0x06, 0xe2, 0x5e, 0x31, 0x27, 0xc7, 0xc5, 0x0b, 0x6c, 0xfc, 0x3b, 0x92, - 0x58, 0x3d, 0x9d, 0x54, 0xa6, 0xe4, 0x1b, 0x6c, 0xbf, 0x0e, 0xbb, 0x24, 0xaa, 0x6c, 0xa2, 0x47, - 0x65, 0x6f, 0xa6, 0x56, 0x56, 0x54, 0x7a, 0x3b, 0xb5, 0xd2, 0xd4, 0x4a, 0xc8, 0xbb, 0x30, 0x5e, - 0xae, 0x96, 0xdd, 0xf6, 0xae, 0xb3, 0x57, 0x5b, 0x29, 0xa1, 0x68, 0x28, 0x3c, 0xa2, 0xea, 0x8e, - 0x55, 0x47, 0xb8, 0xe5, 0xef, 0xdb, 0x9a, 0x63, 0x6c, 0x84, 0x4f, 0xee, 0xc0, 0x94, 0xfc, 0x69, - 0xd2, 0xdd, 0x2d, 0xb3, 0x8a, 0x12, 0xa1, 0x74, 0x43, 0x0b, 0x39, 0xb0, 0x8e, 0xe8, 0x7a, 0xea, - 0x4d, 0x21, 0x46, 0xc6, 0x26, 0x63, 0x85, 0x76, 0x9a, 0xee, 0x11, 0xfb, 0xbc, 0x4d, 0x87, 0x7a, - 0x28, 0x03, 0x8a, 0xc9, 0xd8, 0x08, 0x4b, 0xac, 0xc0, 0xd1, 0xdf, 0x4f, 0x75, 0x22, 0xb2, 0x0e, - 0x33, 0x62, 0x8a, 0x3f, 0x70, 0x7c, 0x67, 0xc7, 0x69, 0x3a, 0xc1, 0x11, 0x4a, 0x7f, 0x42, 0x80, - 0x91, 0xeb, 0xe2, 0x61, 0x58, 0xaa, 0x30, 0x4b, 0x92, 0x1a, 0xbf, 0x9e, 0x85, 0xe7, 0xfa, 0xdd, - 0x84, 0x48, 0x4d, 0xdf, 0xcc, 0xae, 0x0c, 0x70, 0x7b, 0x3a, 0x7d, 0x3b, 0x5b, 0x86, 0xa9, 0xfb, - 0x8a, 0x49, 0x5f, 0x68, 0x62, 0x89, 0x9d, 0xa1, 0x1a, 0xfb, 0xe9, 0xb3, 0x3d, 0x46, 0xb4, 0xf0, - 0x50, 0x6c, 0x73, 0x1f, 0xd7, 0x45, 0xf3, 0x16, 0x8c, 0x95, 0xdd, 0x76, 0x40, 0x1f, 0x05, 0xb1, - 0x80, 0x04, 0x1c, 0x18, 0x77, 0x4f, 0x95, 0xa8, 0xc6, 0xbf, 0xce, 0xc2, 0xf3, 0x7d, 0xaf, 0x02, - 0x64, 0x53, 0xef, 0xb5, 0xab, 0x83, 0xdc, 0x1f, 0x4e, 0xef, 0xb6, 0xc5, 0x84, 0xdd, 0xdd, 0xa9, - 0x1e, 0x50, 0x0b, 0xff, 0x43, 0x46, 0x74, 0xd2, 0xa7, 0x61, 0x14, 0xab, 0x0a, 0xbb, 0x88, 0x6b, - 0xc9, 0x70, 0x17, 0x76, 0x74, 0x2d, 0x19, 0x47, 0x23, 0x37, 0x21, 0x5f, 0xb6, 0x9b, 0x4d, 0x25, - 0x5c, 0x03, 0x4a, 0xf3, 0x75, 0x84, 0xc5, 0x8c, 0x47, 0x25, 0x22, 0x93, 0x7d, 0xf8, 0xdf, 0xca, - 0x59, 0x81, 0x9b, 0xa5, 0x20, 0x8b, 0x1d, 0x17, 0x0a, 0x32, 0x06, 0x79, 0xad, 0xbb, 0xa1, 0x43, - 0x38, 0x0f, 0xf2, 0xca, 0x00, 0x5a, 0x90, 0x57, 0x06, 0x30, 0x7e, 0x23, 0x07, 0x2f, 0xf4, 0xbf, - 0xcf, 0x92, 0x2d, 0x7d, 0x08, 0x5e, 0x1b, 0xe8, 0x16, 0x7c, 0xfa, 0x18, 0xc8, 0x90, 0xc9, 0xbc, - 0x43, 0xae, 0x24, 0xdd, 0x5f, 0xbe, 0x7d, 0x5c, 0x54, 0x2c, 0x92, 0xef, 0xba, 0x4e, 0x5b, 0x79, - 0x33, 0xf9, 0xba, 0x26, 0x19, 0xf2, 0xd7, 0xfb, 0x5b, 0x83, 0x7d, 0x59, 0x44, 0xc7, 0xf7, 0x95, - 0x41, 0x25, 0xca, 0xcf, 0x43, 0x21, 0x4e, 0x4a, 0x2e, 0xc3, 0x10, 0x7e, 0x80, 0xe2, 0xc3, 0x13, - 0xe3, 0x80, 0xe5, 0x0b, 0x6b, 0x62, 0xee, 0x60, 0x04, 0x0b, 0xb4, 0x07, 0xd0, 0x75, 0x83, 0x22, - 0x82, 0x05, 0x37, 0x27, 0x48, 0xea, 0x07, 0x63, 0x44, 0xc6, 0x9f, 0x67, 0xe0, 0x62, 0x4f, 0x4d, - 0x01, 0xd9, 0xd0, 0x07, 0xec, 0x95, 0xd3, 0x54, 0x0b, 0xa7, 0x8e, 0xd5, 0xc2, 0x8f, 0xcb, 0xb9, - 0xff, 0x1e, 0x4c, 0xd4, 0xba, 0x3b, 0xf1, 0xfb, 0x19, 0x8f, 0x2f, 0xa3, 0xc0, 0xd5, 0x13, 0x4c, - 0xc5, 0x67, 0xed, 0x97, 0x06, 0x0f, 0xc2, 0x00, 0x48, 0xb1, 0x3a, 0x0c, 0x5d, 0xac, 0x93, 0x11, - 0x3c, 0x74, 0x22, 0xe3, 0xd7, 0xb2, 0xe9, 0x17, 0xdd, 0x3b, 0xe5, 0x8d, 0xb3, 0x5c, 0x74, 0xef, - 0x94, 0x37, 0x4e, 0x6f, 0xfb, 0x3f, 0x96, 0x6d, 0xc7, 0x87, 0x59, 0xb1, 0xe3, 0x49, 0x45, 0xa7, - 0x78, 0x98, 0x95, 0xbb, 0xa3, 0xaf, 0x3f, 0xcc, 0x4a, 0x64, 0xf2, 0x26, 0x8c, 0xad, 0xba, 0x3c, - 0xb8, 0x86, 0x6c, 0x31, 0xf7, 0x41, 0x96, 0x40, 0x75, 0x7b, 0x0c, 0x31, 0xd9, 0xdd, 0x42, 0x1f, - 0x78, 0x69, 0x5c, 0x89, 0x77, 0x8b, 0xd8, 0x74, 0xd1, 0xd5, 0x81, 0x3a, 0x99, 0xf1, 0x9f, 0x0c, - 0x83, 0x71, 0xba, 0x32, 0x83, 0x7c, 0xa8, 0xf7, 0xdd, 0xb5, 0x81, 0xd5, 0x20, 0x03, 0x6d, 0xb9, - 0xa5, 0x6e, 0xc3, 0xa1, 0xed, 0xba, 0x1e, 0x19, 0x43, 0xc0, 0xd4, 0x2d, 0x50, 0xe2, 0x7d, 0x1c, - 0x47, 0xd5, 0x85, 0xff, 0x36, 0x17, 0x2d, 0xb5, 0xd8, 0xd1, 0x98, 0xf9, 0x18, 0x47, 0x23, 0xb9, - 0x07, 0x05, 0x15, 0xa2, 0xbc, 0xd0, 0xa2, 0xe4, 0xa2, 0x31, 0x8a, 0x7d, 0x54, 0x82, 0x50, 0x3f, - 0x5f, 0x73, 0x83, 0x9f, 0xaf, 0x91, 0xf8, 0x8e, 0xf5, 0x0f, 0x25, 0xc5, 0xf7, 0xb8, 0x33, 0xba, - 0x82, 0x2e, 0x23, 0x69, 0xf8, 0xe2, 0xd0, 0x1a, 0xd6, 0x23, 0x69, 0xa4, 0x1c, 0x5c, 0x2a, 0xba, - 0x0c, 0x06, 0x82, 0x3f, 0x15, 0x5f, 0xf8, 0x30, 0x18, 0x08, 0xa7, 0x4f, 0x0b, 0x06, 0x12, 0x92, - 0xb0, 0x03, 0xd0, 0xec, 0xb6, 0x79, 0x34, 0xf1, 0xd1, 0xe8, 0x00, 0xf4, 0xba, 0x6d, 0x2b, 0x1e, - 0x51, 0x3c, 0x44, 0x34, 0xfe, 0xe1, 0x50, 0xba, 0x70, 0x10, 0xea, 0xbb, 0xce, 0x22, 0x1c, 0x84, - 0x44, 0x9f, 0xcc, 0x4c, 0xdd, 0x82, 0x59, 0x69, 0x9f, 0x27, 0x0d, 0xbd, 0xb6, 0xcc, 0x55, 0x31, - 0xc4, 0xa8, 0x37, 0x0a, 0x2d, 0xfb, 0xa4, 0xb1, 0x98, 0xd5, 0xf5, 0x34, 0xbd, 0x51, 0x0a, 0xfd, - 0xc2, 0x6f, 0x4a, 0xb5, 0x98, 0x3a, 0x08, 0x5b, 0x5b, 0xe1, 0x5c, 0x8e, 0x0d, 0x42, 0xb7, 0xab, - 0x0d, 0xa3, 0x4e, 0xc2, 0xf7, 0x5e, 0xa9, 0x72, 0x40, 0x26, 0x8a, 0xac, 0xa8, 0x28, 0x2a, 0x62, - 0x5c, 0x62, 0x44, 0x64, 0x0f, 0x2e, 0x46, 0xa2, 0xb4, 0x72, 0x53, 0x40, 0x8e, 0xbc, 0xc1, 0x57, - 0x4f, 0x8e, 0x8b, 0xaf, 0x28, 0xa2, 0xb8, 0x7a, 0xe1, 0x88, 0x71, 0xef, 0xcd, 0x8b, 0xed, 0xb7, - 0x4b, 0x9e, 0xdd, 0xae, 0xef, 0x2b, 0x73, 0x1e, 0xf7, 0xdb, 0x1d, 0x84, 0x26, 0xc2, 0x19, 0x44, - 0xc8, 0xc6, 0x8f, 0x67, 0x61, 0x8a, 0x9f, 0xd5, 0xfc, 0x75, 0xee, 0xa9, 0x7d, 0xf9, 0x7c, 0x5b, - 0x7b, 0xf9, 0x94, 0x91, 0xf7, 0xd4, 0xa6, 0x0d, 0xf4, 0xee, 0xb9, 0x0f, 0x24, 0x49, 0x43, 0x4c, - 0x98, 0x50, 0xa1, 0xfd, 0x9f, 0x3c, 0x6f, 0x44, 0x41, 0x1a, 0x85, 0xa8, 0x84, 0xef, 0xce, 0xbe, - 0xa9, 0xf1, 0x30, 0x7e, 0x2c, 0x0b, 0x93, 0x8a, 0x9d, 0xca, 0x53, 0xdb, 0xf1, 0x9f, 0xd7, 0x3a, - 0x7e, 0x3e, 0xf4, 0x10, 0x0c, 0x5b, 0x36, 0x50, 0xbf, 0x77, 0x61, 0x26, 0x41, 0x12, 0x37, 0xf7, - 0xc9, 0x0c, 0x62, 0xee, 0xf3, 0x7a, 0x32, 0xe2, 0x1b, 0x4f, 0x9c, 0x10, 0xc6, 0xff, 0x51, 0x43, - 0xcc, 0xfd, 0x64, 0x16, 0xe6, 0xc4, 0x2f, 0x0c, 0x91, 0xca, 0x85, 0xd5, 0xa7, 0x76, 0x2c, 0x4a, - 0xda, 0x58, 0x14, 0xf5, 0xb1, 0x50, 0x1a, 0xd8, 0x7b, 0x48, 0x8c, 0x1f, 0x02, 0x98, 0xef, 0x45, - 0x30, 0xb0, 0x23, 0x7e, 0xe4, 0x9a, 0x98, 0x1d, 0xc0, 0x35, 0x71, 0x15, 0x0a, 0x58, 0x95, 0x08, - 0x82, 0xe8, 0x6f, 0x99, 0x55, 0xd1, 0x49, 0xa8, 0x5f, 0xe0, 0x71, 0x6c, 0x45, 0x50, 0x46, 0x3f, - 0xa6, 0xf3, 0x48, 0x50, 0x92, 0x5f, 0xce, 0xc0, 0x14, 0x02, 0x97, 0x1f, 0xd2, 0x76, 0x80, 0xcc, - 0x86, 0x84, 0xcf, 0x5a, 0xf8, 0x30, 0x5a, 0x0b, 0x3c, 0xa7, 0xbd, 0x27, 0x5e, 0x46, 0x77, 0xc4, - 0xcb, 0xe8, 0x3b, 0xfc, 0x45, 0xf7, 0x5a, 0xdd, 0x6d, 0x5d, 0xdf, 0xf3, 0xec, 0x87, 0x0e, 0x37, - 0xc1, 0xb2, 0x9b, 0xd7, 0xa3, 0x7c, 0x3f, 0x1d, 0x27, 0x96, 0x89, 0x47, 0xb0, 0xc2, 0x57, 0x67, - 0xfe, 0xa1, 0x14, 0xab, 0x8d, 0xab, 0x66, 0xf4, 0x2f, 0x22, 0xdf, 0x05, 0x17, 0x78, 0x68, 0x32, - 0x76, 0xc3, 0x77, 0xda, 0x5d, 0xb7, 0xeb, 0x2f, 0xd9, 0xf5, 0x03, 0x26, 0xe6, 0x73, 0xcf, 0x62, - 0x6c, 0x79, 0x3d, 0x2c, 0xb4, 0x76, 0x78, 0xa9, 0x16, 0xf3, 0x22, 0x9d, 0x01, 0x59, 0x81, 0x19, - 0x5e, 0x54, 0xea, 0x06, 0x6e, 0xad, 0x6e, 0x37, 0x9d, 0xf6, 0x1e, 0xca, 0x12, 0x79, 0x2e, 0xca, - 0xd8, 0xdd, 0xc0, 0xb5, 0x7c, 0x0e, 0x57, 0x35, 0x35, 0x09, 0x22, 0x52, 0x85, 0x69, 0x93, 0xda, - 0x8d, 0x35, 0xfb, 0x51, 0xd9, 0xee, 0xd8, 0x75, 0x27, 0xe0, 0xb1, 0x52, 0x73, 0x5c, 0xa0, 0xf3, - 0xa8, 0xdd, 0xb0, 0x5a, 0xf6, 0x23, 0xab, 0x2e, 0x0a, 0x75, 0x95, 0xbd, 0x46, 0x17, 0xb2, 0x72, - 0xda, 0x21, 0xab, 0xb1, 0x38, 0x2b, 0xa7, 0xdd, 0x9b, 0x55, 0x44, 0x27, 0x59, 0x6d, 0xda, 0xde, - 0x1e, 0x0d, 0xb8, 0xa1, 0x34, 0x5c, 0xca, 0x5c, 0xc9, 0x28, 0xac, 0x02, 0x2c, 0xb3, 0xd0, 0x68, - 0x3a, 0xce, 0x4a, 0xa1, 0x63, 0x33, 0x6f, 0xdb, 0x73, 0x02, 0xaa, 0xb6, 0x70, 0x1c, 0x3f, 0x0b, - 0xfb, 0x1f, 0x4d, 0xcc, 0x7b, 0x35, 0x31, 0x41, 0x19, 0x71, 0x53, 0x1a, 0x39, 0x91, 0xe0, 0x96, - 0xde, 0xca, 0x04, 0x65, 0xc8, 0x4d, 0x6d, 0xe7, 0x24, 0xb6, 0x53, 0xe1, 0xd6, 0xa3, 0xa1, 0x09, - 0x4a, 0xb2, 0xce, 0x3a, 0x2d, 0x60, 0x72, 0x93, 0xdb, 0x16, 0x16, 0xdc, 0x53, 0xf8, 0x69, 0x2f, - 0x0b, 0x33, 0xc4, 0x82, 0x27, 0x8b, 0xad, 0x14, 0x7b, 0xee, 0x38, 0x31, 0xf9, 0x6b, 0x30, 0xbd, - 0xe5, 0xd3, 0xdb, 0xd5, 0x8d, 0x9a, 0x8c, 0x64, 0x86, 0xca, 0xc5, 0xa9, 0xc5, 0x1b, 0xa7, 0x6c, - 0x3a, 0xd7, 0x54, 0x1a, 0x4c, 0x9f, 0xc3, 0xc7, 0xad, 0xeb, 0x53, 0x6b, 0xd7, 0xe9, 0xf8, 0x61, - 0x58, 0x48, 0x75, 0xdc, 0x62, 0x55, 0x19, 0x2b, 0x30, 0x93, 0x60, 0x43, 0xa6, 0x00, 0x18, 0xd0, - 0xda, 0x5a, 0xaf, 0x2d, 0x6f, 0x16, 0x9e, 0x21, 0x05, 0x98, 0xc0, 0xdf, 0xcb, 0xeb, 0xa5, 0xa5, - 0xd5, 0xe5, 0x4a, 0x21, 0x43, 0x66, 0x60, 0x12, 0x21, 0x95, 0x6a, 0x8d, 0x83, 0xb2, 0x3c, 0x79, - 0x82, 0x59, 0xe0, 0x4b, 0x37, 0x60, 0x0b, 0x00, 0xcf, 0x14, 0xe3, 0x6f, 0x65, 0xe1, 0xa2, 0x3c, - 0x56, 0x68, 0xc0, 0x04, 0x47, 0xa7, 0xbd, 0xf7, 0x94, 0x9f, 0x0e, 0xb7, 0xb5, 0xd3, 0xe1, 0xe5, - 0xd8, 0x49, 0x1d, 0x6b, 0x65, 0x9f, 0x23, 0xe2, 0xb7, 0xc7, 0xe0, 0xf9, 0xbe, 0x54, 0xe4, 0x03, - 0x76, 0x9a, 0x3b, 0xb4, 0x1d, 0x54, 0x1b, 0x4d, 0xba, 0xe9, 0xb4, 0xa8, 0xdb, 0x0d, 0x84, 0xc7, - 0xc0, 0x4b, 0xa8, 0xcf, 0xc3, 0x42, 0xcb, 0x69, 0x34, 0xa9, 0x15, 0xf0, 0x62, 0x6d, 0xba, 0x25, - 0xa9, 0x19, 0xcb, 0x30, 0x95, 0x57, 0xb5, 0x1d, 0x50, 0xef, 0x21, 0x5a, 0x25, 0x86, 0x2c, 0x0f, - 0x28, 0xed, 0x58, 0x36, 0x2b, 0xb5, 0x1c, 0x51, 0xac, 0xb3, 0x4c, 0x50, 0x93, 0xdb, 0x0a, 0xcb, - 0x32, 0xbb, 0xfd, 0xaf, 0xd9, 0x8f, 0x84, 0x99, 0x94, 0x88, 0x8c, 0x1b, 0xb2, 0xe4, 0xee, 0x7c, - 0x2d, 0xfb, 0x91, 0x99, 0x24, 0x21, 0x5f, 0x81, 0x73, 0xe2, 0x00, 0x12, 0xc1, 0x5b, 0x64, 0x8b, - 0x79, 0x68, 0x98, 0x57, 0x4f, 0x8e, 0x8b, 0x17, 0x64, 0x4c, 0x61, 0x19, 0x58, 0x29, 0xad, 0xd5, - 0xe9, 0x5c, 0xc8, 0x26, 0x3b, 0x90, 0x63, 0xdd, 0xb1, 0x46, 0x7d, 0x5f, 0xfa, 0x6c, 0x8a, 0x9b, - 0xb1, 0xda, 0x99, 0x56, 0x8b, 0x97, 0x9b, 0x3d, 0x29, 0xc9, 0x0a, 0x4c, 0x6d, 0xd3, 0x1d, 0x75, - 0x7c, 0x46, 0xc2, 0xad, 0xaa, 0x70, 0x48, 0x77, 0x7a, 0x0f, 0x4e, 0x8c, 0x8e, 0x38, 0xf8, 0x3e, - 0xf0, 0xe8, 0x68, 0xd5, 0xf1, 0x03, 0xda, 0xa6, 0x1e, 0x86, 0x6f, 0x1b, 0xc5, 0xcd, 0x60, 0x3e, - 0x92, 0x90, 0xf5, 0xf2, 0xa5, 0x17, 0x4f, 0x8e, 0x8b, 0xcf, 0x73, 0xe7, 0xe7, 0xa6, 0x80, 0x5b, - 0xb1, 0x44, 0x58, 0x49, 0xae, 0xe4, 0x6b, 0x30, 0x6d, 0xba, 0xdd, 0xc0, 0x69, 0xef, 0xd5, 0x02, - 0xcf, 0x0e, 0xe8, 0x1e, 0x3f, 0x90, 0xa2, 0x38, 0x71, 0xb1, 0x52, 0xf1, 0xb4, 0xcc, 0x81, 0x96, - 0x2f, 0xa0, 0xda, 0x89, 0xa0, 0x13, 0x90, 0xaf, 0xc2, 0x14, 0x0f, 0xdb, 0x11, 0x56, 0x30, 0xa6, - 0x25, 0xf1, 0xd0, 0x0b, 0x1f, 0xdc, 0x10, 0x56, 0x2d, 0x08, 0x4d, 0xab, 0x20, 0xc6, 0x8d, 0x7c, - 0x49, 0x74, 0xd6, 0x86, 0xd3, 0xde, 0x0b, 0xa7, 0x31, 0x60, 0xcf, 0xbf, 0x11, 0x75, 0x49, 0x87, - 0x7d, 0xae, 0x9c, 0xc6, 0x3d, 0x4c, 0xf4, 0x92, 0x7c, 0x48, 0x00, 0xcf, 0x97, 0x7c, 0xdf, 0xf1, - 0x03, 0xe1, 0x57, 0xb3, 0xfc, 0x88, 0xd6, 0xbb, 0x0c, 0x99, 0x5d, 0x6f, 0xa9, 0xc7, 0xed, 0xba, - 0x87, 0x97, 0xae, 0x9d, 0x1c, 0x17, 0x5f, 0xb3, 0x11, 0xd1, 0x12, 0xae, 0x38, 0x16, 0x95, 0xa8, - 0xd6, 0x21, 0xc7, 0x55, 0xda, 0xd0, 0x9f, 0x29, 0xf9, 0x2a, 0x9c, 0x2f, 0xdb, 0x3e, 0xad, 0xb6, - 0x7d, 0xda, 0xf6, 0x9d, 0xc0, 0x79, 0x48, 0x45, 0xa7, 0xe2, 0xe1, 0x97, 0xc7, 0x94, 0x61, 0x46, - 0xdd, 0xf6, 0xd9, 0xc2, 0x0c, 0x51, 0x2c, 0x31, 0x28, 0x4a, 0x35, 0x3d, 0xb8, 0x10, 0x13, 0xa6, - 0x6a, 0xb5, 0x95, 0x8a, 0x63, 0x87, 0xeb, 0x6a, 0x12, 0xfb, 0xeb, 0x35, 0x54, 0xed, 0xf9, 0xfb, - 0x56, 0xc3, 0xb1, 0xc3, 0x05, 0xd5, 0xa3, 0xb3, 0x62, 0x1c, 0x8c, 0xe3, 0x0c, 0x14, 0xe2, 0x43, - 0x49, 0xbe, 0x08, 0x63, 0xdc, 0xbe, 0x8d, 0xfa, 0xfb, 0x22, 0xf2, 0x84, 0x34, 0x97, 0x0a, 0xe1, - 0x3a, 0x91, 0x70, 0xa7, 0xe3, 0xd6, 0x73, 0x54, 0xb5, 0x96, 0x41, 0x77, 0x3a, 0x49, 0x44, 0x1a, - 0x30, 0xc1, 0x47, 0x8b, 0x62, 0x90, 0x48, 0x61, 0xe6, 0xfc, 0xa2, 0xba, 0x3a, 0x44, 0x51, 0x8c, - 0x3f, 0xbe, 0x1a, 0x8a, 0x39, 0xc1, 0x11, 0xb4, 0x2a, 0x34, 0xae, 0x4b, 0x00, 0x79, 0x49, 0x68, - 0x5c, 0x84, 0x0b, 0x3d, 0xbe, 0xd9, 0x78, 0x88, 0x96, 0x04, 0x3d, 0x6a, 0x24, 0x5f, 0x84, 0x39, - 0x24, 0x2c, 0xbb, 0xed, 0x36, 0xad, 0x07, 0xb8, 0x1d, 0x49, 0xed, 0x7b, 0x8e, 0x5b, 0xba, 0xf0, - 0xf6, 0xd6, 0x43, 0x04, 0x2b, 0xae, 0x84, 0x4f, 0xe5, 0x60, 0xfc, 0x7c, 0x16, 0xe6, 0xc5, 0x0e, - 0x67, 0xd2, 0xba, 0xeb, 0x35, 0x9e, 0xfe, 0x13, 0x75, 0x59, 0x3b, 0x51, 0x5f, 0x0a, 0xc3, 0x16, - 0xa5, 0x35, 0xb2, 0xcf, 0x81, 0xfa, 0x6b, 0x19, 0x78, 0xae, 0x1f, 0x11, 0xeb, 0x9d, 0x30, 0x28, - 0xe6, 0x58, 0x22, 0xf8, 0x65, 0x07, 0x66, 0x71, 0x40, 0xcb, 0xfb, 0xb4, 0x7e, 0xe0, 0xaf, 0xb8, - 0x7e, 0x80, 0x9e, 0x16, 0xd9, 0x1e, 0x6f, 0xdd, 0xaf, 0xa7, 0xbe, 0x75, 0x9f, 0xe7, 0xb3, 0xac, - 0x8e, 0x3c, 0x78, 0xd8, 0xce, 0x03, 0x7a, 0xe4, 0x9b, 0x69, 0xac, 0xd1, 0x62, 0xbe, 0xd4, 0x0d, - 0xf6, 0x37, 0x3c, 0xba, 0x4b, 0x3d, 0xda, 0xae, 0xd3, 0xef, 0x30, 0x8b, 0x79, 0xbd, 0x71, 0x03, - 0x69, 0x30, 0xfe, 0xf1, 0x24, 0xcc, 0xa5, 0x91, 0xb1, 0x7e, 0x51, 0x2e, 0xcd, 0xf1, 0x8c, 0xa6, - 0x3f, 0x98, 0x81, 0x89, 0x1a, 0xad, 0xbb, 0xed, 0xc6, 0x6d, 0xb4, 0x28, 0x12, 0xbd, 0x63, 0x71, - 0xa1, 0x81, 0xc1, 0xad, 0xdd, 0x98, 0xa9, 0xd1, 0xb7, 0x8f, 0x8b, 0x5f, 0x18, 0xec, 0xae, 0x5a, - 0x77, 0x31, 0x5c, 0x50, 0x80, 0x19, 0x37, 0xc2, 0x2a, 0xf0, 0x71, 0x50, 0xab, 0x94, 0x2c, 0xc1, - 0xa4, 0x58, 0xae, 0xae, 0x1a, 0x13, 0x95, 0x47, 0x76, 0x92, 0x05, 0x09, 0xd5, 0xb5, 0x46, 0x42, - 0x6e, 0x42, 0x6e, 0x6b, 0xf1, 0xb6, 0x18, 0x03, 0x99, 0xb3, 0x64, 0x6b, 0xf1, 0x36, 0xaa, 0xc3, - 0xd8, 0x15, 0x63, 0xb2, 0xbb, 0xa8, 0x19, 0xf9, 0x6c, 0x2d, 0xde, 0x26, 0xdf, 0x0b, 0xe7, 0x2a, - 0x8e, 0x2f, 0xaa, 0xe0, 0xbe, 0x1b, 0x0d, 0xf4, 0x58, 0x1c, 0xe9, 0x31, 0x7b, 0x3f, 0x9b, 0x3a, - 0x7b, 0x5f, 0x6c, 0x84, 0x4c, 0x2c, 0xee, 0x18, 0xd2, 0x88, 0xc7, 0x7e, 0x4d, 0xaf, 0x87, 0x7c, - 0x04, 0x53, 0xa8, 0xcc, 0x46, 0x77, 0x16, 0x8c, 0xda, 0x3f, 0xda, 0xa3, 0xe6, 0x4f, 0xa7, 0xd6, - 0xbc, 0xc0, 0xe3, 0x6d, 0xa0, 0x53, 0x0c, 0x46, 0xf8, 0xd7, 0x6e, 0xfd, 0x1a, 0x67, 0x72, 0x17, - 0xa6, 0x85, 0xf8, 0x75, 0x7f, 0x77, 0x73, 0x9f, 0x56, 0xec, 0x23, 0x61, 0x9f, 0x83, 0x37, 0x3a, - 0x21, 0xb3, 0x59, 0xee, 0xae, 0x15, 0xec, 0x53, 0xab, 0x61, 0x6b, 0x82, 0x4a, 0x8c, 0x90, 0x7c, - 0x03, 0xc6, 0x57, 0xdd, 0x3a, 0x93, 0xbc, 0x71, 0x67, 0xe0, 0x26, 0x3b, 0x1f, 0x62, 0xce, 0x4c, - 0x0e, 0x8e, 0x89, 0x53, 0xdf, 0x3e, 0x2e, 0xbe, 0x7d, 0xd6, 0x49, 0xa3, 0x54, 0x60, 0xaa, 0xb5, - 0x91, 0x32, 0xe4, 0xb7, 0xe9, 0x0e, 0x6b, 0x6d, 0x3c, 0x9f, 0x9e, 0x04, 0x0b, 0x8b, 0x3c, 0xf1, - 0x4b, 0xb3, 0xc8, 0x13, 0x30, 0xe2, 0xc1, 0x0c, 0xf6, 0xcf, 0x86, 0xed, 0xfb, 0x87, 0xae, 0xd7, - 0xc0, 0xc4, 0x29, 0xbd, 0xac, 0x81, 0x16, 0x53, 0x3b, 0xff, 0x39, 0xde, 0xf9, 0x1d, 0x85, 0x83, - 0x2a, 0x40, 0x26, 0xd8, 0x93, 0xaf, 0xc1, 0x94, 0x88, 0x5d, 0xb0, 0x76, 0xbb, 0x84, 0xab, 0x72, - 0x42, 0xf3, 0xfb, 0xd4, 0x0b, 0xb9, 0x94, 0x2a, 0x42, 0x21, 0x48, 0x0d, 0x94, 0xd5, 0xda, 0xb5, - 0x75, 0xa5, 0xbf, 0x4a, 0x42, 0x36, 0x60, 0xbc, 0x82, 0x59, 0x9d, 0xd1, 0x37, 0x4d, 0xd8, 0x85, - 0x87, 0x09, 0xc1, 0xa2, 0x12, 0xae, 0x8b, 0x11, 0x09, 0xa0, 0xd1, 0xd3, 0x4d, 0xb7, 0xd5, 0x0d, - 0x11, 0xc9, 0x2d, 0xc8, 0x55, 0x2b, 0x1b, 0xc2, 0x2c, 0x7c, 0x26, 0x8c, 0x10, 0xb2, 0x21, 0xd3, - 0x27, 0xa1, 0xfd, 0x9c, 0xd3, 0xd0, 0x8c, 0xca, 0xab, 0x95, 0x0d, 0xb2, 0x0b, 0x93, 0xd8, 0x01, - 0x2b, 0xd4, 0xe6, 0x7d, 0x3b, 0xdd, 0xa3, 0x6f, 0xaf, 0xa5, 0xf6, 0xed, 0x3c, 0xef, 0xdb, 0x7d, - 0x41, 0xad, 0xe5, 0x83, 0x51, 0xd9, 0x32, 0x91, 0x56, 0xe4, 0xa8, 0x92, 0x59, 0x4c, 0x36, 0x57, - 0xd1, 0x3e, 0x48, 0x88, 0xb4, 0x32, 0xa5, 0x55, 0x98, 0x56, 0xa5, 0xa7, 0xd7, 0x49, 0x92, 0x0f, - 0xf9, 0x3c, 0x0c, 0xdd, 0x3f, 0x08, 0x6c, 0x61, 0x00, 0x2e, 0xfb, 0x91, 0x81, 0x64, 0xf3, 0x51, - 0x0b, 0xe9, 0x1e, 0x68, 0x31, 0xe7, 0x90, 0x86, 0x0d, 0xc5, 0x8a, 0xed, 0x35, 0x0e, 0x6d, 0x0f, - 0x1d, 0x84, 0x67, 0x35, 0x16, 0x4a, 0x09, 0x1f, 0x8a, 0x7d, 0x01, 0x88, 0x79, 0x0d, 0xab, 0x2c, - 0xc8, 0x77, 0xc1, 0x45, 0xdf, 0xd9, 0x6b, 0xdb, 0x41, 0xd7, 0xa3, 0x96, 0xdd, 0xdc, 0x73, 0x3d, - 0x27, 0xd8, 0x6f, 0x59, 0x7e, 0xd7, 0x09, 0xe8, 0xfc, 0x9c, 0x96, 0xd1, 0xba, 0x26, 0xf1, 0x4a, - 0x12, 0xad, 0xc6, 0xb0, 0xcc, 0x0b, 0x7e, 0x7a, 0x01, 0xf9, 0x12, 0x4c, 0xaa, 0x5b, 0xb2, 0x3f, - 0x7f, 0xee, 0x52, 0xee, 0xca, 0x54, 0x78, 0xf1, 0x88, 0x6f, 0xe0, 0x32, 0x12, 0xb4, 0x72, 0x42, - 0xf8, 0x7a, 0x24, 0x68, 0x85, 0x57, 0x98, 0x23, 0x92, 0x14, 0x66, 0xcd, 0x19, 0x31, 0x63, 0x45, - 0x2f, 0xaf, 0xdd, 0x2e, 0x99, 0xa3, 0x1b, 0xd5, 0x07, 0xb5, 0xa6, 0x1b, 0x18, 0xff, 0x45, 0x06, - 0x37, 0x71, 0xf2, 0x1a, 0x06, 0x92, 0x0a, 0x5f, 0xcf, 0x50, 0x7f, 0x6b, 0x77, 0x62, 0x69, 0x04, - 0x38, 0x0a, 0x79, 0x1d, 0x46, 0x6e, 0xdb, 0x75, 0x19, 0xc4, 0x46, 0x20, 0xef, 0x22, 0x44, 0x55, - 0xf6, 0x72, 0x1c, 0x26, 0x5f, 0xf2, 0xc9, 0x5d, 0x8a, 0x92, 0xa5, 0x97, 0x4b, 0xf2, 0xb9, 0x1e, - 0xe5, 0x4b, 0xb1, 0x28, 0x94, 0x6c, 0xea, 0x31, 0xab, 0xf8, 0x54, 0x0e, 0xc6, 0x9f, 0x66, 0xa2, - 0x5d, 0x89, 0xbc, 0x0a, 0x43, 0xe6, 0x46, 0xf8, 0xfd, 0xdc, 0xe9, 0x37, 0xf6, 0xf9, 0x88, 0x40, - 0xbe, 0x04, 0xe7, 0x14, 0x3e, 0x09, 0x13, 0xfd, 0x57, 0xd0, 0x27, 0x55, 0xf9, 0x92, 0x74, 0x3b, - 0xfd, 0x74, 0x1e, 0x28, 0x4c, 0x47, 0x05, 0x15, 0xda, 0x76, 0x38, 0x6f, 0xa5, 0xb1, 0x2a, 0xef, - 0x06, 0x22, 0xc4, 0x1b, 0x9b, 0xc6, 0x81, 0xbb, 0xa4, 0x1a, 0xbf, 0x95, 0xd1, 0x76, 0x9b, 0x30, - 0xbb, 0x74, 0xe6, 0x94, 0xec, 0xd2, 0x6f, 0x01, 0x94, 0xba, 0x81, 0xbb, 0xdc, 0xf6, 0xdc, 0x26, - 0xd7, 0xa2, 0x88, 0x4c, 0x1a, 0xa8, 0x1b, 0xa6, 0x08, 0xd6, 0x3c, 0xe7, 0x42, 0xe4, 0x54, 0x6f, - 0x86, 0xdc, 0xc7, 0xf5, 0x66, 0x30, 0x7e, 0x3f, 0xa3, 0xad, 0x51, 0x26, 0x25, 0x8a, 0xa9, 0xa8, - 0x5a, 0x8c, 0x75, 0x9c, 0x87, 0x96, 0xdf, 0x74, 0xb5, 0x70, 0x15, 0x02, 0x8d, 0xfc, 0xbb, 0x19, - 0x38, 0xcf, 0xdd, 0x02, 0xd6, 0xbb, 0xad, 0x1d, 0xea, 0x3d, 0xb0, 0x9b, 0x4e, 0x23, 0x0a, 0xd3, - 0x17, 0x99, 0x0f, 0x2a, 0xd5, 0xa4, 0xe3, 0xf3, 0x8b, 0x2a, 0x77, 0x53, 0xb0, 0xda, 0x58, 0x68, - 0x3d, 0x0c, 0x4b, 0xd5, 0x8b, 0x6a, 0x3a, 0xbd, 0xf1, 0xeb, 0x19, 0x78, 0xf1, 0xd4, 0x5a, 0xc8, - 0x75, 0x18, 0x95, 0x29, 0x4c, 0x32, 0xd8, 0xf1, 0x68, 0x67, 0x9b, 0x4c, 0x5f, 0x22, 0xb1, 0xc8, - 0x97, 0xe1, 0x9c, 0xca, 0x6a, 0xd3, 0xb3, 0x1d, 0x35, 0x51, 0x48, 0xca, 0x57, 0x07, 0x0c, 0x25, - 0x2e, 0xad, 0xa5, 0x33, 0x31, 0xfe, 0xbf, 0x8c, 0x92, 0x6f, 0xfe, 0x29, 0x95, 0xe1, 0x6f, 0x69, - 0x32, 0xbc, 0x0c, 0x52, 0x1a, 0xb6, 0x8a, 0x95, 0xa5, 0xde, 0xbb, 0xa6, 0x15, 0x7b, 0x71, 0x04, - 0xfc, 0x48, 0x16, 0xc6, 0xb7, 0x7c, 0xea, 0xf1, 0x87, 0xdc, 0xef, 0xac, 0x50, 0x8d, 0x61, 0xbb, - 0x06, 0x0a, 0xa6, 0xf7, 0xc7, 0x19, 0x54, 0xf0, 0xab, 0x14, 0xac, 0x37, 0x94, 0x1c, 0x93, 0xd8, - 0x1b, 0x98, 0x5d, 0x12, 0xa1, 0x3c, 0xb4, 0xd8, 0xaa, 0x9e, 0x6e, 0x16, 0x73, 0x0e, 0xaf, 0x92, - 0x2f, 0xc0, 0xf0, 0x16, 0xaa, 0x2b, 0xf5, 0x20, 0x1b, 0x21, 0x7f, 0x2c, 0xe4, 0x9b, 0x74, 0xd7, - 0xd7, 0xe3, 0xce, 0x71, 0x42, 0x52, 0x83, 0xd1, 0xb2, 0x47, 0x31, 0x7b, 0xfc, 0xd0, 0xe0, 0x2e, - 0xe2, 0x75, 0x4e, 0x12, 0x77, 0x11, 0x17, 0x9c, 0x8c, 0x9f, 0xcb, 0x02, 0x89, 0xda, 0x88, 0xa9, - 0xd2, 0xfc, 0xa7, 0x76, 0xd0, 0xdf, 0xd7, 0x06, 0xfd, 0xf9, 0xc4, 0xa0, 0xf3, 0xe6, 0x0d, 0x34, - 0xf6, 0xbf, 0x93, 0x81, 0xf3, 0xe9, 0x84, 0xe4, 0x25, 0x18, 0xb9, 0xbf, 0xb9, 0x21, 0xe3, 0xb4, - 0x88, 0xa6, 0xb8, 0x1d, 0xd4, 0x15, 0x98, 0xa2, 0x88, 0xbc, 0x01, 0x23, 0x1f, 0x98, 0x65, 0x76, - 0x0e, 0x29, 0xc9, 0x38, 0xbe, 0xee, 0x59, 0x75, 0xfd, 0x28, 0x12, 0x48, 0xea, 0xd8, 0xe6, 0x9e, - 0xd8, 0xd8, 0xfe, 0x64, 0x16, 0xa6, 0x4b, 0xf5, 0x3a, 0xf5, 0x7d, 0x11, 0x68, 0xfe, 0xa9, 0x1d, - 0xd8, 0xf4, 0x08, 0x2c, 0x5a, 0xdb, 0x06, 0x1a, 0xd5, 0xdf, 0xcd, 0xc0, 0x39, 0x49, 0xf5, 0xd0, - 0xa1, 0x87, 0x9b, 0xfb, 0x1e, 0xf5, 0xf7, 0xdd, 0x66, 0x63, 0xe0, 0x8c, 0x3f, 0x4c, 0xd0, 0xc3, - 0xe0, 0xf0, 0xea, 0xab, 0xfe, 0x2e, 0x42, 0x34, 0x41, 0x8f, 0x07, 0x90, 0xbf, 0x0e, 0xa3, 0xa5, - 0x4e, 0xc7, 0x73, 0x1f, 0xf2, 0x65, 0x2f, 0x22, 0x4b, 0xda, 0x1c, 0xa4, 0x79, 0xd8, 0x73, 0x10, - 0xfb, 0x8c, 0x0a, 0x6d, 0xf3, 0x50, 0x7e, 0x93, 0xfc, 0x33, 0x1a, 0xb4, 0xad, 0xca, 0xe2, 0x58, - 0x6e, 0xd4, 0x80, 0x6c, 0x78, 0x6e, 0xcb, 0x0d, 0x68, 0x83, 0xb7, 0x07, 0x03, 0x13, 0x9c, 0x1a, - 0x52, 0x6b, 0xd3, 0x09, 0x9a, 0x5a, 0x48, 0xad, 0x80, 0x01, 0x4c, 0x0e, 0x37, 0xfe, 0x9f, 0x61, - 0x98, 0x50, 0x7b, 0x87, 0x18, 0x3c, 0x8d, 0x87, 0xeb, 0xa9, 0xd1, 0x31, 0x6c, 0x84, 0x98, 0xa2, - 0x24, 0x0a, 0x2d, 0x93, 0x3d, 0x35, 0xb4, 0xcc, 0x36, 0x4c, 0x6e, 0x78, 0x2e, 0x86, 0xc0, 0xc4, - 0xd7, 0x4a, 0xb1, 0x15, 0xce, 0x2a, 0xf7, 0x4e, 0x36, 0x90, 0xf8, 0x1e, 0x8a, 0x92, 0x7d, 0x47, - 0x60, 0x63, 0x72, 0x4b, 0x4d, 0xeb, 0xa2, 0xf1, 0xe1, 0xa6, 0x16, 0xb6, 0x2f, 0xe2, 0xd8, 0x86, - 0xa6, 0x16, 0x0c, 0xa2, 0x9b, 0x5a, 0x30, 0x88, 0xba, 0xd6, 0x86, 0x9f, 0xd4, 0x5a, 0x23, 0x3f, - 0x97, 0x81, 0xf1, 0x52, 0xbb, 0x2d, 0x42, 0xd6, 0x9c, 0xe2, 0xad, 0xff, 0x65, 0x61, 0x6d, 0xf1, - 0xf6, 0xc7, 0xb2, 0xb6, 0x40, 0xb9, 0xc5, 0x47, 0x49, 0x35, 0xaa, 0x50, 0xbd, 0xad, 0x29, 0xdf, - 0x41, 0xde, 0x86, 0x42, 0x38, 0xc9, 0xab, 0xed, 0x06, 0x7d, 0x44, 0x79, 0x1a, 0xc4, 0x49, 0x11, - 0x57, 0x5b, 0x95, 0x4c, 0xe3, 0x88, 0x64, 0x13, 0xc0, 0x0e, 0x67, 0x57, 0x2c, 0x9f, 0x6b, 0x72, - 0xfa, 0x09, 0xe9, 0x19, 0x7f, 0xe3, 0x83, 0x96, 0x2a, 0x3d, 0x47, 0x7c, 0x48, 0x0b, 0xa6, 0x79, - 0x32, 0xd5, 0x5a, 0x60, 0x7b, 0x01, 0xa6, 0xa2, 0x80, 0x53, 0xc7, 0xe1, 0x55, 0xa1, 0x3f, 0x7b, - 0x56, 0xa4, 0x68, 0xf5, 0x19, 0xad, 0x95, 0x92, 0x97, 0x22, 0xce, 0x9b, 0x47, 0x31, 0x37, 0x2f, - 0x24, 0xbf, 0x97, 0x4f, 0xfa, 0x9f, 0xcc, 0xc0, 0x79, 0x75, 0xd2, 0xd7, 0xba, 0x3b, 0x22, 0x74, - 0x28, 0xb9, 0x06, 0x63, 0x62, 0x4e, 0x86, 0x97, 0xa8, 0x64, 0x46, 0x8d, 0x08, 0x85, 0x2c, 0xb3, - 0x69, 0xc8, 0x78, 0x08, 0xa9, 0x7b, 0x36, 0xb6, 0x4f, 0xb1, 0xa2, 0x28, 0x51, 0xb7, 0x87, 0xbf, - 0xf5, 0xf9, 0xc9, 0x20, 0xc6, 0x7b, 0x30, 0xa3, 0x8f, 0x44, 0x8d, 0x06, 0xe4, 0x2a, 0x8c, 0xca, - 0xe1, 0xcb, 0xa4, 0x0f, 0x9f, 0x2c, 0x37, 0xb6, 0x81, 0x24, 0xe8, 0x7d, 0x34, 0x8b, 0x62, 0xf7, - 0x53, 0x6e, 0xb6, 0x27, 0x1f, 0x25, 0x13, 0x88, 0x4b, 0xb3, 0xe2, 0xfb, 0xc6, 0x35, 0xb7, 0x04, - 0x0c, 0xa3, 0xfa, 0xa7, 0x53, 0x30, 0x9b, 0xb2, 0xe7, 0x9e, 0x22, 0x13, 0x15, 0xf5, 0x0d, 0x62, - 0x2c, 0x0c, 0xf6, 0x21, 0xb7, 0x85, 0xf7, 0x60, 0xf8, 0xd4, 0xed, 0x80, 0x3b, 0xa5, 0xc4, 0x76, - 0x01, 0x4e, 0xf6, 0x89, 0xc8, 0x45, 0x6a, 0x3c, 0x9e, 0xe1, 0x27, 0x16, 0x8f, 0x67, 0x09, 0x26, - 0x45, 0xab, 0xc4, 0x76, 0xa5, 0x18, 0x47, 0xcb, 0x0c, 0x31, 0x89, 0x6d, 0x4b, 0x27, 0xe1, 0x3c, - 0x7c, 0xb7, 0xf9, 0x90, 0x0a, 0x1e, 0xa3, 0x2a, 0x0f, 0x2c, 0x48, 0xe5, 0xa1, 0x90, 0x90, 0xff, - 0x18, 0x13, 0x39, 0x22, 0x44, 0xdd, 0xb3, 0xf2, 0xfd, 0xf6, 0xac, 0xc6, 0x93, 0xd9, 0xb3, 0x9e, - 0x97, 0xdf, 0x98, 0xbe, 0x77, 0xa5, 0x7c, 0x16, 0xf9, 0x95, 0x0c, 0xcc, 0xf0, 0xa0, 0x30, 0xea, - 0xc7, 0xf6, 0x0d, 0xf4, 0x51, 0x7f, 0x32, 0x1f, 0xfb, 0x9c, 0xc8, 0x0d, 0x94, 0xfe, 0xad, 0xc9, - 0x8f, 0x22, 0xdf, 0x05, 0x10, 0xae, 0x28, 0x1e, 0x4a, 0x76, 0x7c, 0xf1, 0xb9, 0x94, 0x5d, 0x20, - 0x44, 0x8a, 0x52, 0x78, 0x04, 0x21, 0x9d, 0x96, 0xbe, 0x33, 0x84, 0x92, 0xef, 0x85, 0x39, 0xb6, - 0x5e, 0x42, 0x88, 0x08, 0x61, 0x35, 0x3f, 0x8e, 0xb5, 0x7c, 0xa6, 0xb7, 0x4c, 0x74, 0x2d, 0x8d, - 0x8c, 0x07, 0x1e, 0x8e, 0x32, 0xa9, 0x07, 0x6a, 0xb4, 0x8b, 0xd4, 0x8a, 0x30, 0x32, 0x1c, 0x7e, - 0x3d, 0x4f, 0xb3, 0xd1, 0x63, 0x7f, 0xbb, 0x28, 0xd7, 0x02, 0xdf, 0xdf, 0x7c, 0xdd, 0x47, 0x19, - 0x41, 0xe4, 0x03, 0x20, 0x61, 0x34, 0x15, 0x0e, 0xa3, 0x32, 0x05, 0x07, 0x57, 0x37, 0x47, 0x51, - 0x59, 0x3c, 0x59, 0xac, 0x4e, 0x92, 0x24, 0x31, 0xa1, 0x30, 0x27, 0x1a, 0xcd, 0xa0, 0x32, 0xcb, - 0xa2, 0x3f, 0x3f, 0xa5, 0x05, 0x08, 0x8b, 0x4a, 0xa2, 0x94, 0xeb, 0x4a, 0xaa, 0x46, 0x4d, 0xe5, - 0x94, 0xc6, 0x8e, 0xdc, 0x82, 0x31, 0x74, 0x14, 0x5e, 0x91, 0xc6, 0x5e, 0xc2, 0xf0, 0x04, 0x5d, - 0x8a, 0xad, 0x7d, 0xdd, 0x64, 0x2b, 0x42, 0x65, 0xd7, 0x81, 0x8a, 0x77, 0x64, 0x76, 0xdb, 0xa8, - 0x14, 0x16, 0xfa, 0x8e, 0x86, 0x77, 0x64, 0x79, 0x5d, 0xdd, 0x93, 0x1c, 0x91, 0xc8, 0xd7, 0x60, - 0x7c, 0xcd, 0x7e, 0x14, 0xe6, 0x99, 0x9a, 0x19, 0x3c, 0x9b, 0x55, 0xcb, 0x7e, 0x14, 0x26, 0x99, - 0x8a, 0x67, 0xb3, 0x52, 0x58, 0x92, 0xaf, 0x00, 0x28, 0x9a, 0x6a, 0x72, 0x6a, 0x05, 0x2f, 0xca, - 0xb0, 0x77, 0xa9, 0x1a, 0x6c, 0xe4, 0xaf, 0x30, 0x8c, 0x49, 0x0e, 0x73, 0x9f, 0x9c, 0xe4, 0x70, - 0xee, 0x93, 0x93, 0x1c, 0x16, 0x76, 0xe0, 0x62, 0xcf, 0xa5, 0x93, 0x12, 0xf4, 0xf8, 0xba, 0x1e, - 0xf4, 0xf8, 0x62, 0xaf, 0x23, 0xd6, 0xd7, 0x33, 0xad, 0xcc, 0x16, 0xe6, 0x7a, 0x4b, 0x27, 0xdf, - 0xca, 0xc6, 0x8e, 0x5c, 0x71, 0xb1, 0xe0, 0x59, 0xbe, 0x7a, 0xc9, 0x24, 0x59, 0x4c, 0xc1, 0xcd, - 0x0f, 0x65, 0x25, 0x2e, 0x3c, 0x3b, 0x94, 0xd5, 0x43, 0x1d, 0x8f, 0xe7, 0xc7, 0x3d, 0x7d, 0xdf, - 0x81, 0x29, 0x9e, 0x35, 0xf7, 0x1e, 0x3d, 0x3a, 0x74, 0xbd, 0x06, 0xcf, 0x5f, 0x24, 0x64, 0xf0, - 0x44, 0xca, 0xfb, 0x18, 0x2e, 0xa9, 0x48, 0xdf, 0xd3, 0x61, 0xac, 0xfd, 0x62, 0xea, 0x2e, 0xc6, - 0x10, 0xfa, 0xb9, 0xa5, 0x92, 0x37, 0x43, 0x41, 0x8d, 0x7a, 0x6a, 0xbe, 0x15, 0x4f, 0x02, 0x53, - 0xe4, 0x35, 0xea, 0x19, 0xff, 0x22, 0x07, 0x84, 0xd7, 0x54, 0xb6, 0x3b, 0x36, 0x7a, 0x66, 0x3b, - 0x18, 0x6b, 0xa9, 0x20, 0x70, 0xec, 0x9d, 0x26, 0x55, 0x03, 0x95, 0x09, 0xe3, 0xda, 0xb0, 0xcc, - 0x8a, 0x5f, 0x74, 0x12, 0x84, 0x3d, 0xb6, 0xba, 0xec, 0xe3, 0x6c, 0x75, 0x5f, 0x83, 0x67, 0x4b, - 0x1d, 0x4c, 0xbf, 0x2d, 0x6b, 0xb9, 0xed, 0x7a, 0x72, 0x93, 0xd2, 0x7c, 0xfe, 0xec, 0x10, 0x2d, - 0xf1, 0xa5, 0xfd, 0x58, 0x28, 0x72, 0x0a, 0x9b, 0x97, 0x9d, 0x40, 0x8d, 0x21, 0x21, 0xe5, 0x94, - 0x0e, 0x96, 0xa4, 0xc8, 0x29, 0x9c, 0x44, 0xf2, 0x70, 0x3c, 0x29, 0xa7, 0x60, 0xb6, 0xb2, 0x88, - 0x87, 0xe3, 0xd1, 0x1e, 0xb2, 0x4e, 0x48, 0x42, 0xde, 0x81, 0xf1, 0x52, 0x37, 0x70, 0x05, 0x63, - 0x61, 0x15, 0x1e, 0xd9, 0x6f, 0x8b, 0x4f, 0xd1, 0xae, 0x3e, 0x11, 0xba, 0xf1, 0x27, 0x39, 0xb8, - 0x98, 0x1c, 0x5e, 0x51, 0x1a, 0xae, 0x8f, 0xcc, 0x29, 0xeb, 0x23, 0x6d, 0x36, 0x64, 0xa3, 0x4c, - 0x13, 0x4f, 0x62, 0x36, 0xf0, 0x2c, 0xde, 0x1f, 0x73, 0x36, 0xd4, 0x60, 0x5c, 0x3d, 0xef, 0x86, - 0x3e, 0xee, 0x79, 0xa7, 0x72, 0x61, 0x97, 0x7a, 0x1e, 0x3a, 0x63, 0x38, 0x7a, 0x3a, 0x8a, 0x47, - 0xcd, 0xe0, 0x18, 0xe4, 0xdf, 0x81, 0x4b, 0x7c, 0x4f, 0x8a, 0x37, 0x76, 0xe9, 0x48, 0x72, 0x14, - 0x03, 0xb7, 0x78, 0x72, 0x5c, 0xbc, 0xc6, 0x55, 0x25, 0x56, 0xa2, 0xdb, 0xac, 0x9d, 0x23, 0x4b, - 0x7e, 0x99, 0x52, 0xc9, 0xa9, 0xbc, 0x8d, 0x32, 0x5c, 0x14, 0xa5, 0x91, 0xd3, 0xb6, 0x2c, 0x64, - 0x83, 0x7c, 0x10, 0x69, 0xbb, 0x70, 0x90, 0x63, 0x8a, 0x2c, 0x2c, 0xc7, 0xfc, 0xdf, 0x4a, 0x6e, - 0xe6, 0x37, 0xd2, 0x7c, 0x6e, 0x78, 0xd4, 0x6e, 0x0e, 0xd6, 0xdd, 0x6d, 0xa4, 0x4e, 0x2d, 0x9b, - 0xaa, 0x53, 0x93, 0x4a, 0x99, 0x5c, 0xaa, 0x52, 0xa6, 0x02, 0xd3, 0xb5, 0xee, 0x8e, 0xac, 0x3b, - 0xee, 0xaf, 0xe9, 0x77, 0x77, 0xd2, 0x7a, 0x25, 0x4e, 0x62, 0xfc, 0x68, 0x16, 0x26, 0x36, 0x9a, - 0xdd, 0x3d, 0xa7, 0x5d, 0xb1, 0x03, 0xfb, 0xa9, 0x55, 0xf3, 0xbd, 0xa5, 0xa9, 0xf9, 0x42, 0xd7, - 0xb2, 0xb0, 0x61, 0x03, 0xe9, 0xf8, 0x7e, 0x36, 0x03, 0xd3, 0x11, 0x09, 0x3f, 0xac, 0x57, 0x60, - 0x88, 0xfd, 0x10, 0x97, 0xdf, 0x4b, 0x09, 0xc6, 0x3c, 0xcd, 0x64, 0xf8, 0x97, 0x50, 0xbc, 0xe9, - 0x39, 0xdc, 0x90, 0xc3, 0xc2, 0x67, 0x61, 0x2c, 0x62, 0x7b, 0x96, 0xf4, 0x92, 0xbf, 0x91, 0x81, - 0x42, 0xbc, 0x25, 0xe4, 0x1e, 0x8c, 0x32, 0x4e, 0x0e, 0x95, 0xf7, 0xf2, 0x97, 0x7b, 0xb4, 0xf9, - 0x9a, 0x40, 0xe3, 0x9f, 0x87, 0x9d, 0x4f, 0x39, 0xc4, 0x94, 0x1c, 0x16, 0x4c, 0x98, 0x50, 0xb1, - 0x52, 0xbe, 0xee, 0x75, 0x5d, 0x42, 0x39, 0x9f, 0xde, 0x0f, 0x5a, 0x52, 0x4c, 0xed, 0xab, 0x85, - 0xf0, 0x71, 0x59, 0x9b, 0x5c, 0xa9, 0xab, 0x0a, 0x27, 0xcd, 0x62, 0x94, 0xaf, 0x40, 0x9d, 0x67, - 0x29, 0x13, 0x3a, 0xc4, 0x23, 0xaf, 0xc3, 0x08, 0xaf, 0x4f, 0x4d, 0xe8, 0xd6, 0x41, 0x88, 0x2a, - 0x27, 0x73, 0x1c, 0xe3, 0x6f, 0xe7, 0xe0, 0x7c, 0xf4, 0x79, 0x5b, 0x9d, 0x86, 0x1d, 0xd0, 0x0d, - 0xdb, 0xb3, 0x5b, 0xfe, 0x29, 0x2b, 0xe0, 0x4a, 0xe2, 0xd3, 0x30, 0x95, 0x96, 0xfc, 0x34, 0xe5, - 0x83, 0x8c, 0xd8, 0x07, 0xa1, 0x0e, 0x94, 0x7f, 0x90, 0xfc, 0x0c, 0x72, 0x0f, 0x72, 0x35, 0x1a, - 0x88, 0xbd, 0xf7, 0x72, 0xa2, 0x57, 0xd5, 0xef, 0xba, 0x56, 0xa3, 0x01, 0x1f, 0x44, 0x1e, 0x17, - 0x4a, 0x0b, 0xce, 0xc7, 0xb8, 0x90, 0x6d, 0x18, 0x59, 0x7e, 0xd4, 0xa1, 0xf5, 0x40, 0x24, 0x47, - 0xbd, 0xda, 0x9f, 0x1f, 0xc7, 0x55, 0x72, 0xa3, 0x52, 0x04, 0xa8, 0x9d, 0xc5, 0x51, 0x16, 0x6e, - 0x41, 0x5e, 0x56, 0x7e, 0x96, 0x99, 0xbb, 0xf0, 0x16, 0x8c, 0x2b, 0x95, 0x9c, 0x69, 0xd2, 0xff, - 0x22, 0xdb, 0x57, 0xdd, 0xa6, 0xcc, 0xa7, 0xba, 0x9c, 0x90, 0x15, 0x95, 0x6c, 0x54, 0x5c, 0x56, - 0xb4, 0x0e, 0x44, 0x51, 0x1f, 0xa1, 0xb1, 0x0a, 0xd3, 0xb5, 0x03, 0xa7, 0x13, 0x05, 0x8a, 0xd5, - 0x4e, 0x64, 0xcc, 0x78, 0x23, 0x2e, 0xee, 0xf1, 0x13, 0x39, 0x4e, 0x67, 0xfc, 0x79, 0x06, 0x46, - 0xd8, 0x5f, 0x0f, 0x6e, 0x3d, 0xa5, 0x5b, 0xe6, 0x4d, 0x6d, 0xcb, 0x9c, 0x51, 0x62, 0xb5, 0xe3, - 0xc6, 0x71, 0xeb, 0x94, 0xcd, 0xf2, 0x58, 0x0c, 0x10, 0x47, 0x26, 0x77, 0x60, 0x54, 0x98, 0x14, - 0x09, 0xdb, 0x6f, 0x35, 0xf8, 0xbb, 0x34, 0x36, 0x0a, 0x6f, 0xf8, 0x6e, 0x27, 0xae, 0x12, 0x91, - 0xd4, 0x4c, 0xae, 0x97, 0x21, 0x7b, 0xb5, 0x7c, 0xe9, 0x2e, 0x3a, 0xeb, 0xf1, 0xd0, 0xe5, 0xfe, - 0xd2, 0x05, 0xc1, 0xa9, 0x97, 0x6f, 0x7d, 0x49, 0xbc, 0x86, 0xe4, 0xfa, 0x31, 0x39, 0x2f, 0x93, - 0x14, 0xa7, 0x3e, 0x94, 0xb4, 0xe0, 0x7c, 0xad, 0xb6, 0x82, 0xe6, 0x87, 0x1b, 0xae, 0x17, 0xdc, - 0x76, 0xbd, 0x43, 0x1b, 0x6d, 0x8b, 0x51, 0xc3, 0xa7, 0xd8, 0x20, 0xa4, 0x19, 0x85, 0xbd, 0x9a, - 0x6a, 0x14, 0xd6, 0xc7, 0x4e, 0xc1, 0x68, 0xc3, 0x85, 0x5a, 0x6d, 0x85, 0x07, 0x0e, 0xff, 0x8b, - 0xa8, 0xef, 0x37, 0x32, 0x30, 0x53, 0xab, 0xad, 0xc4, 0xaa, 0x5a, 0x95, 0x11, 0xcb, 0x33, 0x7a, - 0x9e, 0xef, 0xd4, 0x8e, 0xc0, 0x51, 0xc8, 0x70, 0x09, 0xaf, 0xae, 0x05, 0xa7, 0xe4, 0x4c, 0xc8, - 0x46, 0x18, 0x23, 0x3d, 0xab, 0xf9, 0x03, 0xf4, 0x68, 0x28, 0x6a, 0xb8, 0x85, 0x37, 0x1d, 0x2b, - 0xd5, 0x35, 0xdc, 0x0c, 0x62, 0xfc, 0x37, 0xe7, 0x79, 0x14, 0x76, 0x39, 0x5b, 0xde, 0x85, 0x09, - 0x41, 0x8f, 0x46, 0xf3, 0xc2, 0x26, 0xe4, 0x22, 0xdb, 0x20, 0x77, 0x39, 0x9c, 0x47, 0xe7, 0xfd, - 0xf6, 0x71, 0x71, 0x88, 0x75, 0x8d, 0xa9, 0xa1, 0x93, 0xfb, 0x30, 0xb9, 0x66, 0x3f, 0x52, 0xd4, - 0x19, 0xdc, 0x25, 0xea, 0x2a, 0xdb, 0x55, 0x5a, 0xf6, 0xa3, 0x01, 0x8c, 0xee, 0x74, 0x7a, 0x72, - 0x00, 0x53, 0x7a, 0x9b, 0xc4, 0x0c, 0x4c, 0x8e, 0xd8, 0x8d, 0xd4, 0x11, 0xbb, 0xd8, 0x71, 0xbd, - 0xc0, 0xda, 0x0d, 0xc9, 0xb5, 0x8c, 0x03, 0x31, 0xd6, 0xe4, 0x5d, 0x98, 0x51, 0x42, 0x80, 0xde, - 0x76, 0xbd, 0x96, 0x2d, 0x2f, 0x5c, 0xa8, 0xe3, 0x47, 0x5b, 0xa2, 0x5d, 0x04, 0x9b, 0x49, 0x4c, - 0xf2, 0xa5, 0x34, 0x37, 0xb3, 0xe1, 0xc8, 0xf2, 0x30, 0xc5, 0xcd, 0xac, 0x97, 0xe5, 0x61, 0xd2, - 0xe1, 0x6c, 0xaf, 0x9f, 0x65, 0x72, 0x9e, 0xb7, 0x7e, 0x20, 0xcb, 0xe3, 0x70, 0xe4, 0x7a, 0x58, - 0x20, 0x2f, 0x42, 0x6e, 0x69, 0xe3, 0x36, 0xbe, 0x4c, 0x49, 0x23, 0xaa, 0xf6, 0xbe, 0xdd, 0xae, - 0xe3, 0x45, 0x48, 0x78, 0x03, 0xa8, 0x07, 0xe5, 0xd2, 0xc6, 0x6d, 0x62, 0xc3, 0x2c, 0xe6, 0x79, - 0x0b, 0xbe, 0x78, 0xe3, 0x86, 0x32, 0x54, 0x79, 0xfc, 0xb4, 0xeb, 0xe2, 0xd3, 0x8a, 0x98, 0x25, - 0x2e, 0xb0, 0x1e, 0xdd, 0xb8, 0x91, 0x3a, 0x20, 0xe1, 0x87, 0xa5, 0xf1, 0x62, 0x07, 0xd6, 0x9a, - 0xfd, 0x28, 0x72, 0xe2, 0xf0, 0x85, 0xc3, 0xee, 0xf3, 0x72, 0x6a, 0x45, 0x0e, 0x20, 0xda, 0x81, - 0xa5, 0x13, 0xb1, 0x7b, 0x6c, 0x34, 0xc1, 0x7c, 0xe1, 0xea, 0xb4, 0x20, 0xd5, 0x75, 0xd2, 0xab, - 0x5b, 0xbd, 0x8c, 0x29, 0xe8, 0x64, 0x2b, 0xbc, 0x8d, 0xf3, 0xdb, 0xac, 0xc8, 0x8c, 0x7c, 0x5d, - 0xbd, 0x8d, 0x73, 0x25, 0x99, 0xd6, 0xac, 0xe9, 0x50, 0x85, 0xc3, 0xbd, 0x5a, 0x4c, 0x9d, 0x4b, - 0xf2, 0x92, 0x3f, 0x71, 0xf6, 0x4b, 0x3e, 0x85, 0xa1, 0x55, 0xb7, 0x7e, 0x20, 0x82, 0xf3, 0x7d, - 0xc0, 0x76, 0x61, 0x3d, 0x8d, 0xfe, 0xe3, 0x5a, 0x5c, 0x23, 0x7b, 0xb2, 0xce, 0x3e, 0x95, 0xcd, - 0x02, 0xd1, 0x27, 0xc2, 0x8a, 0x77, 0x2e, 0xbc, 0xe5, 0x2a, 0x65, 0x5c, 0x1e, 0xe5, 0x93, 0x46, - 0x76, 0xad, 0xa9, 0x93, 0x13, 0x0a, 0x85, 0x0a, 0xf5, 0x0f, 0x02, 0xb7, 0x53, 0x6e, 0x3a, 0x9d, - 0x1d, 0xd7, 0xf6, 0x64, 0x28, 0xe7, 0x81, 0xf7, 0xe4, 0x06, 0xa7, 0xb7, 0xea, 0x92, 0x81, 0x99, - 0x60, 0x49, 0xbe, 0x04, 0x53, 0x6c, 0x72, 0x2f, 0x3f, 0x0a, 0x68, 0x9b, 0x8f, 0xfc, 0x0c, 0x4a, - 0x74, 0x73, 0x4a, 0xee, 0x92, 0xb0, 0x90, 0xcf, 0x29, 0x5c, 0xec, 0x34, 0x24, 0xd0, 0x02, 0x1b, - 0x6a, 0xac, 0x48, 0x03, 0xe6, 0xd7, 0xec, 0x47, 0x4a, 0x0e, 0x66, 0x65, 0x92, 0x12, 0x9c, 0x60, - 0x57, 0x4e, 0x8e, 0x8b, 0x2f, 0xb3, 0x09, 0x16, 0x45, 0x17, 0xef, 0x31, 0x5f, 0x7b, 0x72, 0x22, - 0xdf, 0x80, 0x0b, 0xa2, 0x59, 0x15, 0xcc, 0x1b, 0xe6, 0x7a, 0x47, 0xb5, 0x7d, 0x1b, 0xfd, 0xb7, - 0x66, 0x7b, 0x74, 0xd8, 0xf5, 0xf4, 0x2d, 0x51, 0x76, 0x58, 0x43, 0xf2, 0xb1, 0x7c, 0xce, 0xc8, - 0xec, 0x55, 0x03, 0xf9, 0x08, 0xa6, 0xf8, 0x73, 0xdc, 0x8a, 0xeb, 0x07, 0xa8, 0xac, 0x99, 0x3b, - 0x9b, 0x5b, 0x02, 0x7f, 0xe3, 0xe3, 0x8e, 0x3c, 0x31, 0xe5, 0x4e, 0x8c, 0x33, 0x79, 0x1b, 0xc6, - 0x37, 0x9c, 0x36, 0x0f, 0x3d, 0x5a, 0xdd, 0x40, 0xb5, 0xb2, 0x38, 0x81, 0x3a, 0x4e, 0xdb, 0x92, - 0x1a, 0x93, 0x4e, 0xb8, 0x5d, 0xa8, 0xd8, 0x64, 0x1b, 0xc6, 0x6b, 0xb5, 0x95, 0xdb, 0x0e, 0x93, - 0x4b, 0x3a, 0x47, 0xf3, 0xe7, 0x7b, 0x7c, 0xe5, 0x4b, 0xa9, 0x5f, 0x39, 0xe9, 0xfb, 0xfb, 0xd6, - 0xae, 0xd3, 0xa4, 0x56, 0xdd, 0xed, 0x1c, 0x99, 0x2a, 0xa7, 0x14, 0x53, 0xfd, 0x0b, 0x4f, 0xd8, - 0x54, 0xbf, 0x0a, 0xd3, 0x8a, 0xf1, 0x2c, 0x1a, 0xce, 0xce, 0x47, 0xf1, 0xaa, 0x54, 0xd3, 0xfc, - 0xb8, 0x6b, 0x6a, 0x9c, 0x4e, 0xda, 0xe8, 0x5f, 0x3c, 0xab, 0x8d, 0xbe, 0x03, 0x33, 0x7c, 0x30, - 0xc4, 0x3c, 0xc0, 0x91, 0x5e, 0xe8, 0xd1, 0x87, 0x57, 0x53, 0xfb, 0x70, 0x56, 0x8c, 0xb4, 0x9c, - 0x64, 0xf8, 0xfc, 0x9c, 0xe4, 0x4a, 0x76, 0x81, 0x08, 0xa0, 0x1d, 0xd8, 0x3b, 0xb6, 0x4f, 0xb1, - 0xae, 0x67, 0x7b, 0xd4, 0xf5, 0x72, 0x6a, 0x5d, 0x53, 0xb2, 0xae, 0x1d, 0x5e, 0x4d, 0x0a, 0x47, - 0xd2, 0x96, 0xf5, 0xc8, 0xf9, 0x85, 0x1d, 0xfb, 0x9c, 0xa6, 0xe3, 0x4e, 0x22, 0xf0, 0xd0, 0x4f, - 0xf1, 0x49, 0x1b, 0xef, 0xf7, 0x14, 0xce, 0xe4, 0x11, 0x9c, 0x4f, 0x7e, 0x05, 0xd6, 0xf9, 0x3c, - 0xd6, 0xf9, 0xbc, 0x56, 0x67, 0x1c, 0x89, 0xcf, 0x1b, 0xbd, 0x59, 0xf1, 0x5a, 0x7b, 0xf0, 0x27, - 0x3f, 0x94, 0x81, 0x0b, 0x6b, 0xb7, 0x4b, 0x98, 0x4d, 0xd4, 0xe1, 0x91, 0xe8, 0x42, 0x97, 0xde, - 0x17, 0xc4, 0x3b, 0x48, 0xfc, 0x6d, 0x46, 0x4a, 0x1c, 0xb8, 0x55, 0x30, 0xd1, 0xfd, 0xa5, 0xd6, - 0xae, 0xcd, 0x93, 0x94, 0x0a, 0x16, 0x29, 0x7e, 0xbf, 0xdf, 0xfc, 0xa3, 0x62, 0xc6, 0xec, 0x55, - 0x15, 0x69, 0xc2, 0x82, 0xde, 0x2d, 0xd2, 0x8b, 0x62, 0x9f, 0x36, 0x9b, 0xf3, 0x45, 0x9c, 0xd1, - 0xaf, 0x9f, 0x1c, 0x17, 0xaf, 0x24, 0x7a, 0x37, 0xf4, 0xcc, 0x60, 0x98, 0x4a, 0x83, 0xfb, 0xf0, - 0x23, 0xad, 0x14, 0xa1, 0x7b, 0xfe, 0x92, 0x16, 0xfb, 0x27, 0x51, 0xbe, 0xf4, 0x8a, 0x90, 0x48, - 0x9e, 0x67, 0xeb, 0xbd, 0xa7, 0x80, 0x68, 0x26, 0x39, 0xdf, 0x1d, 0xca, 0x4f, 0x16, 0xa6, 0x52, - 0x5c, 0x16, 0x8c, 0xdf, 0xce, 0xc6, 0x0e, 0x46, 0x52, 0x85, 0x51, 0x31, 0xdf, 0x7b, 0x5e, 0x32, - 0x9e, 0x4f, 0x9d, 0xd5, 0xa3, 0x62, 0xe9, 0x98, 0x92, 0x9e, 0x1c, 0x32, 0x56, 0xd8, 0x68, 0x71, - 0xe3, 0xfd, 0x0a, 0x3f, 0xf7, 0x10, 0xa4, 0x9d, 0xf0, 0x95, 0xb3, 0x3b, 0xe2, 0xe9, 0x7e, 0x9e, - 0x78, 0xd4, 0xcb, 0xda, 0xc8, 0x01, 0x4f, 0x25, 0x95, 0x0b, 0xbd, 0xb9, 0xf4, 0xbc, 0x51, 0x4f, - 0xac, 0x42, 0x56, 0x8b, 0xf1, 0x5b, 0x19, 0x98, 0xd4, 0x4e, 0x56, 0x72, 0x4b, 0x71, 0x55, 0x8c, - 0xbc, 0xf7, 0x35, 0x1c, 0xdc, 0x6c, 0xe3, 0x4e, 0x8c, 0xb7, 0x84, 0xdf, 0x41, 0xb6, 0x37, 0x1d, - 0x2e, 0xb6, 0xb8, 0xe7, 0x6a, 0x7f, 0xfd, 0x70, 0x98, 0x07, 0x73, 0xa8, 0x47, 0x1e, 0xcc, 0xbf, - 0x57, 0x84, 0x29, 0xfd, 0x46, 0x4c, 0x5e, 0x87, 0x11, 0xd4, 0xcd, 0x4b, 0xf5, 0x0a, 0xaa, 0x85, - 0x50, 0x7d, 0xaf, 0x39, 0xa3, 0x70, 0x1c, 0xf2, 0x0a, 0x40, 0x68, 0x00, 0x2e, 0x5f, 0xa6, 0x86, - 0x4f, 0x8e, 0x8b, 0x99, 0x37, 0x4c, 0xa5, 0x80, 0x7c, 0x15, 0x60, 0xdd, 0x6d, 0xd0, 0x30, 0xb9, - 0x71, 0x1f, 0xeb, 0x8b, 0x57, 0x13, 0x69, 0x56, 0xce, 0xb5, 0xdd, 0x06, 0x4d, 0xe6, 0x54, 0x51, - 0x38, 0x92, 0xcf, 0xc3, 0xb0, 0xd9, 0x6d, 0x52, 0xf9, 0x82, 0x31, 0x2e, 0x4f, 0xb8, 0x6e, 0x93, - 0x46, 0x7a, 0x02, 0xaf, 0x1b, 0x37, 0x2c, 0x64, 0x00, 0xf2, 0x3e, 0x4f, 0xbf, 0x22, 0x62, 0x84, - 0x0e, 0x47, 0x6f, 0x75, 0x8a, 0xe4, 0x93, 0x88, 0x12, 0xaa, 0x90, 0x90, 0xfb, 0x30, 0xaa, 0x3e, - 0x32, 0x29, 0x3e, 0xef, 0xea, 0x43, 0xa4, 0xa2, 0x74, 0x10, 0x59, 0x91, 0xe3, 0xef, 0x4f, 0x92, - 0x0b, 0x79, 0x07, 0xc6, 0x18, 0x7b, 0xb6, 0x73, 0xf8, 0xe2, 0x56, 0x83, 0x2f, 0x72, 0xca, 0x07, - 0xb1, 0xdd, 0x47, 0x8b, 0xe4, 0x19, 0x12, 0x90, 0x2f, 0x61, 0x1e, 0x5b, 0xd1, 0xd5, 0x7d, 0xad, - 0x72, 0x2e, 0x27, 0xba, 0x1a, 0x13, 0xdb, 0x26, 0x7a, 0x3a, 0xe2, 0x47, 0xf6, 0xc2, 0x90, 0x6b, - 0x83, 0xa4, 0xcc, 0x79, 0x2d, 0x51, 0xc1, 0xbc, 0x8c, 0x22, 0x96, 0x4c, 0x52, 0xad, 0xf1, 0x25, - 0x1d, 0x28, 0x44, 0x42, 0xa5, 0xa8, 0x0b, 0xfa, 0xd5, 0xf5, 0x46, 0xa2, 0x2e, 0x75, 0x00, 0x13, - 0xd5, 0x25, 0xb8, 0x93, 0x06, 0x4c, 0xc9, 0x03, 0x4a, 0xd4, 0x37, 0xde, 0xaf, 0xbe, 0x57, 0x12, - 0xf5, 0xcd, 0x36, 0x76, 0x92, 0xf5, 0xc4, 0x78, 0x92, 0x77, 0x60, 0x52, 0x42, 0x78, 0xca, 0xe8, - 0x89, 0x28, 0xe7, 0x6e, 0x63, 0x27, 0x91, 0x28, 0x5a, 0x47, 0x56, 0xa9, 0xf9, 0xec, 0x98, 0xd4, - 0xa8, 0xe3, 0xb3, 0x42, 0x47, 0x26, 0x1f, 0xc2, 0x78, 0xb5, 0xc5, 0x1a, 0xe2, 0xb6, 0xed, 0x80, - 0x0a, 0x7f, 0x48, 0x69, 0x61, 0xa4, 0x94, 0x28, 0x53, 0x95, 0x27, 0xc3, 0x8e, 0x8a, 0xb4, 0x64, - 0xd8, 0x11, 0x98, 0x75, 0x1e, 0x7f, 0x55, 0x14, 0x73, 0x58, 0xfa, 0x4a, 0x3e, 0x9f, 0x62, 0xe5, - 0xa3, 0xb0, 0x17, 0xf1, 0x20, 0x19, 0x54, 0xbe, 0xea, 0xc5, 0x62, 0xf1, 0xaa, 0x3c, 0xc9, 0xbb, - 0x30, 0x2e, 0xb2, 0x89, 0x95, 0xcc, 0x75, 0x7f, 0xbe, 0x80, 0x8d, 0xc7, 0x08, 0x0f, 0x32, 0xf1, - 0x98, 0x65, 0x7b, 0x31, 0x73, 0xd6, 0x08, 0x9f, 0x7c, 0x11, 0xe6, 0xb6, 0x9d, 0x76, 0xc3, 0x3d, - 0xf4, 0xc5, 0x31, 0x25, 0x36, 0xba, 0x99, 0xc8, 0x99, 0xec, 0x90, 0x97, 0x87, 0xb2, 0x60, 0x62, - 0xe3, 0x4b, 0xe5, 0x40, 0xbe, 0x27, 0xc1, 0x99, 0xcf, 0x20, 0xd2, 0x6f, 0x06, 0x2d, 0x26, 0x66, - 0x50, 0xb2, 0xfa, 0xf8, 0x74, 0x4a, 0xad, 0x86, 0xb8, 0x40, 0xf4, 0xf3, 0xfd, 0xae, 0xeb, 0xb4, - 0xe7, 0x67, 0x71, 0x2f, 0x7c, 0x36, 0x1e, 0x53, 0x01, 0xf1, 0x44, 0x52, 0x71, 0xe3, 0xe4, 0xb8, - 0xf8, 0x42, 0x5c, 0xe6, 0xff, 0xc8, 0xd5, 0x9e, 0x4b, 0x52, 0x58, 0x93, 0x0f, 0x61, 0x82, 0xfd, - 0x1f, 0x2a, 0x25, 0xe6, 0x34, 0xbb, 0x50, 0x05, 0x53, 0xd4, 0x83, 0x63, 0x84, 0xe9, 0xce, 0x52, - 0xf4, 0x15, 0x1a, 0x2b, 0xf2, 0x16, 0x00, 0x13, 0x9b, 0xc4, 0x76, 0x7c, 0x2e, 0x0a, 0x7d, 0x8c, - 0x52, 0x57, 0x72, 0x23, 0x8e, 0x90, 0xc9, 0x3b, 0x30, 0xce, 0x7e, 0xd5, 0xba, 0x0d, 0x97, 0xad, - 0x8d, 0xf3, 0x48, 0xcb, 0x5d, 0x53, 0x19, 0xad, 0xcf, 0xe1, 0x9a, 0x6b, 0x6a, 0x84, 0x4e, 0x56, - 0x60, 0x1a, 0x43, 0x54, 0x8b, 0xe0, 0xa8, 0x0e, 0xf5, 0xe7, 0x2f, 0x28, 0xd6, 0x10, 0xac, 0xc8, - 0x72, 0xc2, 0x32, 0xf5, 0x2e, 0x13, 0x23, 0x23, 0x3e, 0xcc, 0x26, 0x9f, 0x93, 0xfd, 0xf9, 0x79, - 0xec, 0x24, 0x29, 0xc1, 0x27, 0x31, 0xf8, 0x7e, 0xcc, 0x46, 0x44, 0xd9, 0xb8, 0xe4, 0xa3, 0x92, - 0x5a, 0x61, 0x1a, 0x77, 0x62, 0x02, 0xb9, 0x53, 0xde, 0x88, 0xc7, 0x70, 0xbe, 0x88, 0x2d, 0xc0, - 0x61, 0xde, 0xab, 0x47, 0x59, 0xc4, 0x53, 0xe2, 0x38, 0xa7, 0x50, 0x93, 0xef, 0x86, 0x73, 0x72, - 0x07, 0x11, 0x45, 0x62, 0x5e, 0x2f, 0x9c, 0x71, 0x27, 0x6e, 0xec, 0x84, 0x55, 0x27, 0xa6, 0x74, - 0x7a, 0x15, 0xc4, 0x86, 0x71, 0x1c, 0x56, 0x51, 0xe3, 0xb3, 0xfd, 0x6a, 0xbc, 0x92, 0xa8, 0xf1, - 0x3c, 0x4e, 0x94, 0x64, 0x65, 0x2a, 0x4f, 0xb2, 0x04, 0x93, 0x62, 0x1d, 0x89, 0xd9, 0xf6, 0x1c, - 0xf6, 0x16, 0x2a, 0xb1, 0xe4, 0x0a, 0x4c, 0x4c, 0x38, 0x9d, 0x44, 0xdd, 0x91, 0xf9, 0x63, 0xd2, - 0xf3, 0xda, 0x8e, 0x1c, 0x7f, 0x43, 0xd2, 0x91, 0xd9, 0x8e, 0x14, 0x49, 0x31, 0xcb, 0x8f, 0x3a, - 0x9e, 0x50, 0x51, 0xbd, 0x10, 0x65, 0x45, 0x52, 0x84, 0x1f, 0x8b, 0x86, 0x18, 0xea, 0x96, 0x90, - 0xc6, 0x81, 0x6c, 0xc1, 0x6c, 0x78, 0x6a, 0x2b, 0x8c, 0x8b, 0x51, 0x94, 0xe0, 0xe8, 0xa8, 0x4f, - 0xe7, 0x9b, 0x46, 0x4f, 0x6c, 0xb8, 0xa0, 0x9d, 0xd3, 0x0a, 0xeb, 0x4b, 0xc8, 0x1a, 0xb3, 0xd6, - 0xeb, 0x87, 0x7c, 0x3a, 0xfb, 0x5e, 0x7c, 0xc8, 0x47, 0xb0, 0x10, 0x3f, 0x9b, 0x95, 0x5a, 0x5e, - 0xc4, 0x5a, 0x5e, 0x3b, 0x39, 0x2e, 0x5e, 0x4e, 0x1c, 0xef, 0xe9, 0x15, 0xf5, 0xe1, 0x46, 0xbe, - 0x0a, 0xf3, 0xfa, 0xf9, 0xac, 0xd4, 0x64, 0x60, 0x4d, 0xb8, 0x74, 0xc2, 0x83, 0x3d, 0xbd, 0x86, - 0x9e, 0x3c, 0x48, 0x00, 0xc5, 0xd4, 0xd9, 0xad, 0x54, 0xf3, 0x52, 0xd4, 0xa0, 0xc4, 0x2a, 0x49, - 0xaf, 0xee, 0x34, 0x96, 0xe4, 0x10, 0x5e, 0x48, 0x3b, 0x26, 0x94, 0x4a, 0x5f, 0x0e, 0x95, 0xc0, - 0x9f, 0x4a, 0x3f, 0x72, 0xd2, 0x6b, 0x3e, 0x85, 0x2d, 0xf9, 0x12, 0x9c, 0x53, 0xd6, 0x97, 0x52, - 0xdf, 0x2b, 0x58, 0x1f, 0xba, 0x82, 0xab, 0x0b, 0x33, 0xbd, 0x96, 0x74, 0x1e, 0xa4, 0x05, 0xb3, - 0xb2, 0xe1, 0xa8, 0x6d, 0x17, 0x47, 0xcf, 0x65, 0x6d, 0x57, 0x4d, 0x62, 0x2c, 0x5d, 0x12, 0xbb, - 0xea, 0x7c, 0x63, 0xc7, 0xea, 0x44, 0x84, 0xea, 0x4c, 0x4f, 0xe1, 0x4b, 0x56, 0x60, 0xa4, 0xb6, - 0x51, 0xbd, 0x7d, 0x7b, 0x79, 0xfe, 0x55, 0xac, 0x41, 0xfa, 0x8d, 0x71, 0xa0, 0x76, 0x69, 0x12, - 0xe6, 0x8a, 0x1d, 0x67, 0x77, 0x57, 0x7b, 0xb0, 0xe2, 0xa8, 0xe4, 0x7b, 0xd0, 0x50, 0x90, 0xed, - 0xa8, 0x25, 0xdf, 0x77, 0xf6, 0x30, 0xea, 0xb4, 0x3f, 0xff, 0x9a, 0xf6, 0xde, 0x2f, 0x23, 0x72, - 0x97, 0x31, 0x61, 0x59, 0x02, 0x9d, 0x4b, 0x9b, 0xec, 0xfe, 0x2f, 0x76, 0x6e, 0xcb, 0x8e, 0x58, - 0xa9, 0x9b, 0x78, 0xb2, 0x22, 0xd6, 0x6f, 0x7b, 0x4e, 0x60, 0xed, 0x77, 0xb5, 0xe6, 0xcf, 0x7f, - 0x4a, 0x8b, 0xc0, 0xcc, 0xd3, 0xb8, 0x29, 0xbd, 0xf6, 0xb2, 0xa8, 0xf0, 0x39, 0x7e, 0x5b, 0xee, - 0xd1, 0x73, 0x33, 0x7b, 0x31, 0x3a, 0x9f, 0xfc, 0x60, 0x06, 0xce, 0x6f, 0xbb, 0xde, 0x41, 0xd3, - 0xb5, 0x1b, 0xb2, 0x55, 0x62, 0x0f, 0x7f, 0xbd, 0xdf, 0x1e, 0xfe, 0x99, 0xc4, 0x1e, 0x6e, 0x1c, - 0x0a, 0x36, 0x56, 0x18, 0xd0, 0x3c, 0xb1, 0x9f, 0xf7, 0xa8, 0x8a, 0x7c, 0x0f, 0x5c, 0x4a, 0x2f, - 0x51, 0x26, 0xe5, 0x1b, 0x38, 0x29, 0x6f, 0x9c, 0x1c, 0x17, 0xdf, 0xe8, 0x55, 0x53, 0xfa, 0x04, - 0x3d, 0x95, 0xf5, 0xdd, 0xa1, 0xfc, 0x95, 0xc2, 0xd5, 0xbb, 0x43, 0xf9, 0xab, 0x85, 0xd7, 0xcc, - 0xe7, 0x6a, 0xa5, 0xb5, 0xd5, 0x6a, 0x43, 0x1e, 0xae, 0x32, 0xe6, 0x3a, 0xa7, 0x31, 0x2f, 0xf7, - 0x2b, 0x8d, 0x38, 0x1a, 0x7f, 0x33, 0x03, 0xc5, 0x53, 0x26, 0x09, 0x3b, 0xcf, 0xa2, 0x91, 0xa8, - 0xd1, 0x40, 0x8d, 0xdc, 0x1e, 0x8d, 0x9f, 0xa5, 0x9b, 0x8d, 0xe8, 0x24, 0xe8, 0x74, 0x28, 0xd2, - 0x85, 0x28, 0xbe, 0xa7, 0xc9, 0x34, 0x21, 0x12, 0xcb, 0x58, 0x85, 0x42, 0x7c, 0xf2, 0x90, 0xcf, - 0xc1, 0xa4, 0x9a, 0xac, 0x40, 0xaa, 0x12, 0x78, 0xa0, 0x11, 0x6f, 0x4f, 0x3b, 0x10, 0x35, 0x44, - 0xe3, 0x17, 0x33, 0x30, 0x9b, 0xb2, 0xc2, 0xc8, 0x65, 0x18, 0xc2, 0x6c, 0x62, 0x8a, 0xd5, 0x50, - 0x2c, 0x8b, 0x18, 0x96, 0x93, 0x4f, 0xc3, 0x68, 0x65, 0xbd, 0x56, 0x2b, 0xad, 0x4b, 0x65, 0x04, - 0x3f, 0x88, 0xdb, 0xbe, 0xe5, 0xdb, 0xba, 0xb1, 0x81, 0x40, 0x23, 0x6f, 0xc0, 0x48, 0x75, 0x03, - 0x09, 0xb8, 0xed, 0x2b, 0xb6, 0xd7, 0xe9, 0xc4, 0xf1, 0x05, 0x92, 0xf1, 0xe3, 0x19, 0x20, 0xc9, - 0xed, 0x82, 0xdc, 0x80, 0x71, 0x75, 0x53, 0xe2, 0xed, 0xc5, 0x17, 0x58, 0x65, 0xe1, 0x98, 0x2a, - 0x0e, 0xa9, 0xc0, 0x30, 0xe6, 0x81, 0x0d, 0xad, 0x1c, 0x52, 0x97, 0xc5, 0x85, 0xc4, 0xb2, 0x18, - 0xc6, 0x2c, 0xb3, 0x26, 0x27, 0x36, 0x7e, 0x37, 0x03, 0x24, 0xdd, 0x76, 0x71, 0x20, 0x2b, 0xab, - 0x37, 0x95, 0xd8, 0x05, 0x6a, 0xbe, 0xa0, 0x30, 0xd9, 0x9b, 0xaa, 0x06, 0x88, 0xa2, 0x1c, 0x5c, - 0xd6, 0xd4, 0x4e, 0xbd, 0x1d, 0x5e, 0xaf, 0xc2, 0xf0, 0x03, 0xea, 0xed, 0x48, 0xb3, 0x6e, 0x34, - 0x05, 0x7d, 0xc8, 0x00, 0xaa, 0x1a, 0x06, 0x31, 0x8c, 0x3f, 0xc9, 0xc0, 0x5c, 0xda, 0x1d, 0xe5, - 0x14, 0xbf, 0x54, 0x23, 0xe6, 0x52, 0x8b, 0x16, 0x56, 0xdc, 0x4e, 0x34, 0x74, 0xa4, 0x2d, 0xc2, - 0x30, 0x6b, 0xac, 0x1c, 0x61, 0x54, 0x83, 0xb1, 0xde, 0xf0, 0x4d, 0x0e, 0x67, 0x08, 0x3c, 0x46, - 0xdf, 0x10, 0x86, 0x77, 0x44, 0x04, 0x9c, 0xdd, 0x26, 0x87, 0x33, 0x84, 0x35, 0xb7, 0x41, 0xa5, - 0x7a, 0x08, 0x11, 0x5a, 0x0c, 0x60, 0x72, 0x38, 0xb9, 0x0c, 0xa3, 0xf7, 0xdb, 0xab, 0xd4, 0x7e, - 0x28, 0x73, 0x56, 0xa0, 0x45, 0x98, 0xdb, 0xb6, 0x9a, 0x0c, 0x66, 0xca, 0x42, 0xe3, 0x67, 0x33, - 0x30, 0x93, 0xb8, 0x1e, 0x9d, 0xee, 0x7a, 0xdb, 0xdf, 0x07, 0x6e, 0x90, 0xf6, 0xf1, 0xcf, 0x1f, - 0x4a, 0xff, 0x7c, 0xe3, 0xbf, 0x1b, 0x81, 0x0b, 0x3d, 0xb4, 0x55, 0x91, 0x8f, 0x6e, 0xe6, 0x54, - 0x1f, 0xdd, 0x2f, 0xc3, 0x64, 0xb9, 0x69, 0x3b, 0x2d, 0x7f, 0xd3, 0x8d, 0xbe, 0x38, 0x72, 0xf5, - 0xc1, 0x32, 0xe1, 0x07, 0x11, 0xfa, 0x84, 0x5c, 0xac, 0x23, 0x85, 0x15, 0xb8, 0x49, 0x61, 0x59, - 0x63, 0x96, 0xf0, 0x92, 0xcd, 0xfd, 0x25, 0xf1, 0x92, 0xd5, 0xfd, 0xb6, 0x86, 0x9e, 0xa8, 0xdf, - 0x56, 0xba, 0xcd, 0xf7, 0xf0, 0xe3, 0x78, 0x00, 0x94, 0x61, 0x92, 0x9b, 0xc4, 0x95, 0x7c, 0x3e, - 0x48, 0x23, 0x09, 0x33, 0x3a, 0xdb, 0x4f, 0x8e, 0x85, 0x46, 0x43, 0x56, 0x74, 0x1f, 0xa3, 0x51, - 0x7c, 0x33, 0xbe, 0xdc, 0xdb, 0x87, 0x48, 0xb3, 0x15, 0xd1, 0x7c, 0x89, 0xbe, 0x01, 0x73, 0x69, - 0xd7, 0xdd, 0xf9, 0xbc, 0x66, 0x6d, 0xdb, 0xd3, 0x4a, 0x7b, 0xf0, 0x4b, 0xf3, 0x41, 0xea, 0xa5, - 0x59, 0xfa, 0x7e, 0x8f, 0x69, 0x21, 0x9d, 0x7b, 0xac, 0x05, 0x8e, 0xdb, 0xdf, 0x43, 0xdc, 0xf8, - 0x32, 0x3c, 0xdf, 0x97, 0x9c, 0xbc, 0xad, 0xc5, 0x18, 0x7a, 0x35, 0x19, 0x63, 0xe8, 0xdb, 0xc7, - 0xc5, 0x19, 0xcd, 0x6f, 0x73, 0x2d, 0x54, 0xf8, 0x1b, 0x3f, 0x9b, 0xd5, 0x3d, 0x8e, 0xff, 0x32, - 0x2e, 0xd4, 0xab, 0x30, 0xbc, 0xbd, 0x4f, 0x3d, 0x79, 0x3c, 0xe0, 0x87, 0x1c, 0x32, 0x80, 0xfa, - 0x21, 0x88, 0x41, 0x6e, 0xc3, 0xd4, 0x06, 0x9f, 0xb8, 0x72, 0x36, 0x0e, 0x45, 0x3a, 0x97, 0x8e, - 0xd0, 0x0c, 0xa6, 0x4c, 0xc7, 0x18, 0x95, 0x71, 0x27, 0xd6, 0xe9, 0x22, 0x42, 0x12, 0xf7, 0x8c, - 0xe2, 0x02, 0xc4, 0x54, 0xe4, 0x0b, 0x16, 0x6d, 0xb6, 0x66, 0x0c, 0x6a, 0xec, 0xc2, 0x0b, 0x7d, - 0x19, 0xb1, 0x73, 0x1b, 0x3a, 0xe1, 0xaf, 0x98, 0xe5, 0x75, 0x5f, 0x52, 0x53, 0xa1, 0x33, 0xbe, - 0x01, 0x13, 0x6a, 0x2f, 0xe3, 0x11, 0xc4, 0x7e, 0x8b, 0x59, 0xc1, 0x8f, 0x20, 0x06, 0x30, 0x39, - 0x3c, 0x7a, 0xcb, 0xc9, 0xa6, 0xbf, 0xe5, 0x44, 0xc3, 0x9f, 0x3b, 0x6d, 0xf8, 0x59, 0xe5, 0xb8, - 0xc3, 0x29, 0x95, 0xe3, 0x6f, 0xb5, 0x72, 0x0c, 0x81, 0x64, 0x72, 0xf8, 0x13, 0xad, 0xfc, 0x9f, - 0xc8, 0x24, 0x67, 0xe8, 0x78, 0x25, 0x97, 0x7b, 0x26, 0xca, 0x54, 0x96, 0xb6, 0x7a, 0x23, 0xcc, - 0x48, 0xa6, 0xc8, 0x9e, 0x26, 0x53, 0x9c, 0x65, 0x22, 0xa2, 0xdc, 0xcb, 0x87, 0x74, 0x28, 0x92, - 0x03, 0xed, 0x84, 0xb5, 0x8b, 0xc4, 0x32, 0xbe, 0x99, 0x81, 0x73, 0xa9, 0x3a, 0x73, 0x56, 0x2b, - 0x57, 0xce, 0x2b, 0xeb, 0x30, 0xae, 0x99, 0xe7, 0x18, 0x67, 0x89, 0x7f, 0x31, 0x78, 0x5b, 0x8c, - 0x17, 0x61, 0x2c, 0x7c, 0xb1, 0x25, 0x73, 0x72, 0xe8, 0xd0, 0x2e, 0x52, 0x3e, 0xfc, 0xd5, 0x00, - 0xd8, 0x17, 0x3c, 0x51, 0xd3, 0x6a, 0xe3, 0x9f, 0x64, 0x79, 0x02, 0xdc, 0xa7, 0x36, 0x94, 0x6d, - 0xba, 0x3d, 0x34, 0x6b, 0x52, 0xef, 0x00, 0xb6, 0x64, 0x19, 0x46, 0x6a, 0x81, 0x1d, 0x74, 0x65, - 0xd8, 0x8e, 0x59, 0x95, 0x0c, 0x0b, 0x1e, 0x2c, 0x46, 0x81, 0x1b, 0x7c, 0x84, 0x68, 0x5a, 0x02, - 0x84, 0x28, 0x66, 0xd5, 0x7f, 0x90, 0x81, 0x09, 0x95, 0x98, 0x7c, 0x08, 0x53, 0x32, 0x40, 0x27, - 0x0f, 0x66, 0x22, 0x9e, 0x97, 0xa5, 0x29, 0x98, 0x0c, 0xd0, 0xa9, 0x06, 0x3f, 0xd1, 0xf0, 0xd5, - 0xad, 0xba, 0xa3, 0x22, 0x93, 0x06, 0x90, 0xd6, 0xae, 0x6d, 0x1d, 0x52, 0xfb, 0x80, 0xfa, 0x81, - 0xc5, 0x4d, 0x76, 0xc4, 0x2b, 0xb4, 0x64, 0xbf, 0x76, 0xbb, 0xc4, 0xad, 0x75, 0xd8, 0x48, 0x88, - 0x48, 0xab, 0x09, 0x1a, 0xf5, 0x69, 0xad, 0xb5, 0x6b, 0x6f, 0xf3, 0x42, 0x4e, 0x67, 0xfc, 0xe9, - 0x08, 0x9f, 0x6e, 0x22, 0x9e, 0xef, 0x0e, 0x4c, 0xdd, 0xaf, 0x56, 0xca, 0x8a, 0xa2, 0x5d, 0x4f, - 0x07, 0xb5, 0xfc, 0x28, 0xa0, 0x5e, 0xdb, 0x6e, 0xca, 0xfb, 0x6e, 0x74, 0x04, 0xb9, 0x4e, 0xa3, - 0x9e, 0xae, 0x84, 0x8f, 0x71, 0x64, 0x75, 0xf0, 0x9b, 0x75, 0x58, 0x47, 0x76, 0xc0, 0x3a, 0x7c, - 0xbb, 0xd5, 0xec, 0x51, 0x87, 0xce, 0x91, 0xec, 0xe3, 0xd5, 0x77, 0xbf, 0xbb, 0xa3, 0xd4, 0x92, - 0xeb, 0x5f, 0xcb, 0x4b, 0xa2, 0x96, 0x67, 0x85, 0x5a, 0x25, 0xb5, 0x9e, 0x04, 0xd7, 0x68, 0x9f, - 0x18, 0x3a, 0x75, 0x9f, 0xf8, 0xeb, 0x19, 0x18, 0xe1, 0xe2, 0xab, 0x98, 0xc6, 0x3d, 0x04, 0xe4, - 0xed, 0x27, 0x23, 0x20, 0x17, 0xf0, 0x9c, 0xd0, 0x26, 0x34, 0x2f, 0x23, 0x95, 0xd8, 0xba, 0x90, - 0xde, 0x00, 0xf8, 0x64, 0xc6, 0x4b, 0x4e, 0x5f, 0x16, 0xa4, 0x1a, 0x85, 0xd2, 0x18, 0x3d, 0xd5, - 0x5b, 0x5b, 0x86, 0x1f, 0x19, 0x15, 0xa1, 0x34, 0xf4, 0x00, 0x1a, 0xab, 0x30, 0x26, 0x02, 0x74, - 0x2c, 0x1d, 0x89, 0x87, 0xf1, 0x82, 0x66, 0xda, 0xd4, 0x58, 0x3a, 0x8a, 0x44, 0x73, 0x11, 0xe2, - 0xc3, 0xda, 0x39, 0xd2, 0xf2, 0x09, 0x4b, 0x44, 0x72, 0x9f, 0xe7, 0xd9, 0xe4, 0x11, 0x8f, 0xf5, - 0x14, 0x07, 0x21, 0x5c, 0x84, 0xfe, 0x92, 0x5e, 0xfe, 0x29, 0x01, 0x8e, 0x23, 0x1e, 0x64, 0x15, - 0x0a, 0x68, 0x0e, 0x47, 0x1b, 0x7c, 0xd5, 0x54, 0x2b, 0x3c, 0x08, 0x84, 0x30, 0x69, 0x0e, 0x78, - 0x99, 0x58, 0x6e, 0x31, 0xff, 0xcb, 0x04, 0xa5, 0xf1, 0x33, 0x59, 0x28, 0xc4, 0x67, 0x1f, 0x79, - 0x07, 0xc6, 0xc3, 0x88, 0xd3, 0xa1, 0x07, 0x38, 0x3e, 0x90, 0x45, 0x21, 0xaa, 0xf5, 0xec, 0x8c, - 0x0a, 0x3a, 0x59, 0x84, 0x3c, 0x5b, 0xc4, 0xf1, 0x4c, 0xc6, 0x5d, 0x01, 0x53, 0x3d, 0xb2, 0x24, - 0x1e, 0xa9, 0xc1, 0x2c, 0x5b, 0x34, 0x35, 0xa7, 0xbd, 0xd7, 0xa4, 0xab, 0xee, 0x9e, 0xdb, 0x0d, - 0xa2, 0x64, 0x85, 0xfc, 0x02, 0x63, 0xb7, 0x9a, 0x5a, 0xb1, 0x9e, 0xaa, 0x30, 0x85, 0x5a, 0xc9, - 0xb3, 0x3e, 0x34, 0x40, 0x9e, 0x75, 0x65, 0x67, 0xfd, 0xa3, 0x2c, 0x8c, 0x2b, 0xd3, 0x8f, 0x5c, - 0x85, 0x7c, 0xd5, 0x5f, 0x75, 0xeb, 0x07, 0x61, 0x28, 0xc9, 0xc9, 0x93, 0xe3, 0xe2, 0x98, 0xe3, - 0x5b, 0x4d, 0x04, 0x9a, 0x61, 0x31, 0x59, 0x82, 0x49, 0xfe, 0x97, 0x4c, 0x1c, 0x92, 0x8d, 0x74, - 0x6b, 0x1c, 0x59, 0xa6, 0x0c, 0x51, 0x37, 0x5b, 0x8d, 0x84, 0x7c, 0x05, 0x80, 0x03, 0x30, 0xf8, - 0x40, 0x6e, 0xf0, 0xb0, 0x09, 0xa2, 0x82, 0x94, 0xb0, 0x03, 0x0a, 0x43, 0xf2, 0x35, 0x1e, 0xd0, - 0x5a, 0x2e, 0x97, 0xa1, 0xc1, 0xe3, 0x3e, 0x30, 0xfe, 0x56, 0x7a, 0xf8, 0x19, 0x95, 0xa5, 0xc8, - 0xf5, 0xb3, 0x60, 0xd2, 0xba, 0xfb, 0x90, 0x7a, 0x47, 0xa5, 0x00, 0x11, 0x15, 0x0c, 0xe3, 0x7f, - 0xc9, 0x28, 0x8b, 0x8c, 0xac, 0x63, 0xae, 0x6e, 0x3e, 0x81, 0x84, 0x49, 0x59, 0x78, 0xc5, 0x90, - 0x70, 0x93, 0xee, 0x2e, 0x3d, 0x2b, 0xac, 0xdb, 0x66, 0xc3, 0x69, 0x18, 0xcb, 0xe1, 0xcd, 0x81, - 0xe4, 0x0b, 0x30, 0x84, 0x5d, 0x97, 0x3d, 0xb5, 0x69, 0xf2, 0x94, 0x1f, 0x62, 0x7d, 0x86, 0x0d, - 0x41, 0x4a, 0xf2, 0x69, 0xe1, 0xb8, 0xcd, 0x3b, 0x7f, 0x4a, 0x39, 0xaa, 0xd9, 0x77, 0x84, 0xc7, - 0x7b, 0x14, 0x81, 0x48, 0x99, 0x3d, 0x7f, 0x33, 0x0b, 0x85, 0xf8, 0xd2, 0x26, 0xef, 0xc3, 0x84, - 0x3c, 0x7e, 0x57, 0x6c, 0x91, 0xf5, 0x62, 0x42, 0x64, 0x9d, 0x90, 0x67, 0xf0, 0xbe, 0xad, 0x9a, - 0xa0, 0x99, 0x1a, 0x01, 0x93, 0x85, 0x36, 0x45, 0x44, 0x40, 0x65, 0x51, 0x05, 0x6e, 0xd0, 0x89, - 0xc5, 0x51, 0x96, 0x68, 0xe4, 0x4d, 0xc8, 0xad, 0xdd, 0x2e, 0x09, 0x07, 0xbf, 0x42, 0xfc, 0x90, - 0xe6, 0x96, 0xb2, 0xba, 0xdd, 0x2e, 0xc3, 0x27, 0xab, 0x4a, 0xc8, 0xf1, 0x11, 0xcd, 0xdc, 0x50, - 0x82, 0xc3, 0xc6, 0x9d, 0x1e, 0x7b, 0xfc, 0xee, 0x50, 0x3e, 0x57, 0x18, 0x12, 0x41, 0x74, 0xff, - 0xfb, 0x1c, 0x8c, 0x85, 0xf5, 0x13, 0xa2, 0xba, 0x4d, 0x73, 0x17, 0x69, 0x72, 0x11, 0xf2, 0x52, - 0xba, 0x13, 0x7e, 0x7e, 0xa3, 0xbe, 0x90, 0xec, 0xe6, 0x41, 0x8a, 0x71, 0x7c, 0x57, 0x30, 0xe5, - 0x4f, 0x72, 0x03, 0x42, 0x19, 0xad, 0x97, 0x30, 0x37, 0xc4, 0x06, 0xcc, 0x0c, 0xd1, 0xc8, 0x14, - 0x64, 0x1d, 0x1e, 0x98, 0x6d, 0xcc, 0xcc, 0x3a, 0x0d, 0xf2, 0x3e, 0xe4, 0xed, 0x46, 0x83, 0x36, - 0x2c, 0x5b, 0xda, 0x66, 0xf5, 0x9b, 0x34, 0x79, 0xc6, 0x8d, 0x9f, 0x19, 0x48, 0x55, 0x0a, 0x48, - 0x09, 0xc6, 0x9a, 0x36, 0xb7, 0xf6, 0x6c, 0x0c, 0x70, 0x00, 0x45, 0x1c, 0xf2, 0x8c, 0x6c, 0xcb, - 0xa7, 0x0d, 0xf2, 0x2a, 0x0c, 0xb1, 0xd1, 0x14, 0x27, 0x8e, 0x14, 0x2a, 0xd9, 0x60, 0xf2, 0x0e, - 0x5b, 0x79, 0xc6, 0x44, 0x04, 0xf2, 0x32, 0xe4, 0xba, 0x8b, 0xbb, 0xe2, 0x2c, 0x29, 0x44, 0xe1, - 0xff, 0x43, 0x34, 0x56, 0x4c, 0x6e, 0x42, 0xfe, 0x50, 0x8f, 0x1c, 0x7f, 0x2e, 0x36, 0x8c, 0x21, - 0x7e, 0x88, 0x48, 0x5e, 0x85, 0x9c, 0xef, 0xbb, 0xc2, 0xa0, 0x69, 0x36, 0xb4, 0x32, 0xbd, 0x1f, - 0x8e, 0x1a, 0xe3, 0xee, 0xfb, 0xee, 0x52, 0x1e, 0x46, 0xf8, 0x01, 0x63, 0xbc, 0x00, 0x10, 0x7d, - 0x63, 0xd2, 0x6f, 0xd3, 0xf8, 0x0a, 0x8c, 0x85, 0xdf, 0x46, 0x9e, 0x07, 0x38, 0xa0, 0x47, 0xd6, - 0xbe, 0xdd, 0x6e, 0x34, 0xb9, 0x74, 0x3a, 0x61, 0x8e, 0x1d, 0xd0, 0xa3, 0x15, 0x04, 0x90, 0x0b, - 0x30, 0xda, 0x61, 0xc3, 0x2f, 0xe6, 0xf8, 0x84, 0x39, 0xd2, 0xe9, 0xee, 0xb0, 0xa9, 0x3c, 0x0f, - 0xa3, 0xa8, 0x67, 0x15, 0x2b, 0x72, 0xd2, 0x94, 0x3f, 0x8d, 0x3f, 0xcb, 0x61, 0x7a, 0x25, 0xa5, - 0x41, 0xe4, 0x25, 0x98, 0xac, 0x7b, 0x14, 0xcf, 0x32, 0x9b, 0x49, 0x68, 0xa2, 0x9e, 0x89, 0x08, - 0x58, 0x6d, 0x90, 0xcb, 0x30, 0xdd, 0xe9, 0xee, 0x34, 0x9d, 0x3a, 0xab, 0xcd, 0xaa, 0xef, 0x88, - 0x7c, 0x10, 0x13, 0xe6, 0x24, 0x07, 0xdf, 0xa3, 0x47, 0xe5, 0x1d, 0x8c, 0x3c, 0x58, 0x50, 0x03, - 0x47, 0x07, 0x61, 0xe2, 0x7b, 0x73, 0x5a, 0x81, 0xa3, 0x6d, 0xe6, 0x79, 0x18, 0xb1, 0xed, 0xbd, - 0xae, 0xc3, 0x23, 0x84, 0x4d, 0x98, 0xe2, 0x17, 0xf9, 0x14, 0xcc, 0x44, 0xb1, 0xcc, 0x65, 0x33, - 0x86, 0xb1, 0x19, 0x85, 0xb0, 0xa0, 0xcc, 0xe1, 0xe4, 0x0d, 0x20, 0x6a, 0x7d, 0xee, 0xce, 0x47, - 0xb4, 0xce, 0xe7, 0xe4, 0x84, 0x39, 0xa3, 0x94, 0xdc, 0xc7, 0x02, 0xf2, 0x22, 0x4c, 0x78, 0xd4, - 0x47, 0xe9, 0x10, 0xbb, 0x0d, 0xb3, 0x0f, 0x9a, 0xe3, 0x12, 0xc6, 0xfa, 0xee, 0x0a, 0x14, 0x94, - 0xee, 0xc0, 0xd8, 0xdc, 0x3c, 0x19, 0x82, 0x39, 0x15, 0xc1, 0xcd, 0x4e, 0xb5, 0x41, 0xbe, 0x08, - 0x0b, 0x0a, 0x26, 0x4f, 0x84, 0x68, 0xd1, 0xa6, 0xb3, 0xe7, 0xec, 0x34, 0xa9, 0x98, 0x6f, 0xc9, - 0x59, 0x1d, 0x5e, 0x21, 0xcd, 0xf9, 0x88, 0x9a, 0xa7, 0x48, 0x5c, 0x16, 0xb4, 0x64, 0x15, 0xe6, - 0x62, 0x9c, 0x69, 0xc3, 0xea, 0x76, 0x7a, 0x86, 0xe4, 0x8b, 0x78, 0x12, 0x9d, 0x27, 0x6d, 0x6c, - 0x75, 0x8c, 0x6f, 0xc0, 0x84, 0x3a, 0x27, 0x59, 0x27, 0xa8, 0x72, 0x89, 0x98, 0x7d, 0xe3, 0x21, - 0xac, 0xca, 0xee, 0x85, 0x53, 0x11, 0x4a, 0x10, 0xe6, 0xf8, 0x37, 0x27, 0x43, 0x28, 0x0e, 0xe1, - 0x8b, 0x30, 0xd1, 0x70, 0xfc, 0x4e, 0xd3, 0x3e, 0xb2, 0xa2, 0x0c, 0xdf, 0xe6, 0xb8, 0x80, 0xa1, - 0xe2, 0x67, 0x09, 0x66, 0x12, 0xfb, 0xa0, 0x22, 0x69, 0xf0, 0x7d, 0xbd, 0xbf, 0xa4, 0x61, 0xb4, - 0x61, 0x42, 0x3d, 0xd7, 0x4e, 0x49, 0x5c, 0x72, 0x1e, 0xc3, 0xf0, 0xf0, 0x4d, 0x7f, 0xe4, 0xe4, - 0xb8, 0x98, 0x75, 0x1a, 0x18, 0x7c, 0xe7, 0x0a, 0xe4, 0xa5, 0xc4, 0x26, 0x04, 0x25, 0x7c, 0x4c, - 0x90, 0x4f, 0x93, 0x66, 0x58, 0x6a, 0xbc, 0x0a, 0xa3, 0xe2, 0xe8, 0xea, 0xff, 0x84, 0x60, 0xfc, - 0x70, 0x16, 0xa6, 0x4d, 0xca, 0x36, 0x56, 0xca, 0xb3, 0x15, 0x3d, 0xb5, 0x57, 0xf4, 0xf4, 0x60, - 0xae, 0x5a, 0xdb, 0xfa, 0xe4, 0x09, 0xfa, 0xd5, 0x0c, 0xcc, 0xa6, 0xe0, 0x7e, 0xac, 0x3c, 0xb9, - 0xb7, 0x60, 0xac, 0xe2, 0xd8, 0xcd, 0x52, 0xa3, 0x11, 0xc6, 0xe4, 0x41, 0x39, 0x1f, 0x93, 0x69, - 0xd9, 0x0c, 0xaa, 0x0a, 0x31, 0x21, 0x2a, 0x79, 0x4d, 0x4c, 0x8a, 0x28, 0xcb, 0x3c, 0x4e, 0x8a, - 0x6f, 0x1f, 0x17, 0x81, 0x7f, 0xd3, 0x66, 0x38, 0x45, 0x30, 0xc0, 0x32, 0x07, 0x46, 0x7e, 0x55, - 0x4f, 0xed, 0xd0, 0xa5, 0x07, 0x58, 0x8e, 0x37, 0x6f, 0xa0, 0x54, 0x41, 0x3f, 0x91, 0x85, 0xf3, - 0xe9, 0x84, 0x1f, 0x37, 0xe5, 0x31, 0x26, 0x69, 0x52, 0x82, 0xc2, 0x63, 0xca, 0x63, 0x9e, 0xd1, - 0x09, 0xf1, 0x23, 0x04, 0xb2, 0x0b, 0x93, 0xab, 0xb6, 0x1f, 0xac, 0x50, 0xdb, 0x0b, 0x76, 0xa8, - 0x1d, 0x0c, 0x20, 0xc9, 0x4b, 0x6b, 0x8a, 0x79, 0x14, 0x26, 0xf6, 0x25, 0x65, 0x4c, 0xd6, 0xd6, - 0xd9, 0x86, 0x13, 0x65, 0x68, 0x80, 0x89, 0xf2, 0x75, 0x98, 0xae, 0xd1, 0x96, 0xdd, 0xd9, 0x77, - 0x3d, 0x19, 0x2f, 0xe1, 0x1a, 0x4c, 0x86, 0xa0, 0xd4, 0xd9, 0xa2, 0x17, 0x6b, 0xf8, 0x4a, 0x47, - 0x44, 0x5b, 0x89, 0x5e, 0x6c, 0xfc, 0xad, 0x2c, 0x5c, 0x28, 0xd5, 0x85, 0x69, 0xa8, 0x28, 0x90, - 0x16, 0xec, 0x9f, 0x70, 0xdd, 0xe4, 0x3a, 0x8c, 0xad, 0xd9, 0x8f, 0x56, 0xa9, 0xed, 0x53, 0x5f, - 0x24, 0x9c, 0xe4, 0x62, 0xaf, 0xfd, 0x28, 0x7a, 0xfc, 0x31, 0x23, 0x1c, 0x55, 0x8d, 0x30, 0xf4, - 0x98, 0x6a, 0x04, 0x03, 0x46, 0x56, 0xdc, 0x66, 0x43, 0x9c, 0xf5, 0xe2, 0xc5, 0x79, 0x1f, 0x21, - 0xa6, 0x28, 0x31, 0xfe, 0x24, 0x03, 0x53, 0xe1, 0x17, 0xe3, 0x27, 0x7c, 0xe2, 0x5d, 0x72, 0x19, - 0x46, 0xb1, 0xa2, 0x30, 0x33, 0x3e, 0x1e, 0x1a, 0x4d, 0x8a, 0x69, 0x03, 0x1b, 0xa6, 0x2c, 0x54, - 0x7b, 0x62, 0xf8, 0xf1, 0x7a, 0xc2, 0xf8, 0xfb, 0xf8, 0x98, 0xad, 0xb6, 0x92, 0x9d, 0x44, 0xca, - 0x87, 0x64, 0x06, 0xfc, 0x90, 0xec, 0x13, 0x1b, 0x92, 0x5c, 0xcf, 0x21, 0xf9, 0x91, 0x2c, 0x8c, - 0x87, 0x1f, 0xfb, 0x1d, 0x96, 0x99, 0x20, 0x6c, 0xd7, 0x40, 0x31, 0x8e, 0x6a, 0xca, 0x5e, 0x21, - 0x42, 0x09, 0x7d, 0x01, 0x46, 0xc4, 0x62, 0xca, 0xc4, 0x2c, 0xb9, 0x63, 0xa3, 0xbb, 0x34, 0x25, - 0x58, 0x8f, 0xe0, 0x80, 0xfa, 0xa6, 0xa0, 0xc3, 0x20, 0x52, 0xdb, 0x74, 0x47, 0xd8, 0x36, 0x3c, - 0xb5, 0x67, 0x54, 0x7a, 0x10, 0xa9, 0xa8, 0x61, 0x03, 0x9d, 0x4e, 0xff, 0x2c, 0x0f, 0x85, 0x38, - 0xc9, 0xe9, 0xb9, 0x1f, 0x36, 0xba, 0x3b, 0xfc, 0xaa, 0xc2, 0x73, 0x3f, 0x74, 0xba, 0x3b, 0x26, - 0x83, 0xa1, 0xe9, 0x93, 0xe7, 0x3c, 0xc4, 0x56, 0x4f, 0x08, 0xd3, 0x27, 0xcf, 0x79, 0xa8, 0x99, - 0x3e, 0x79, 0xce, 0x43, 0x54, 0x24, 0xac, 0xd6, 0x30, 0xc0, 0x02, 0xde, 0x53, 0x84, 0x22, 0xa1, - 0xe9, 0xc7, 0xf3, 0xb8, 0x49, 0x34, 0x76, 0x54, 0x2e, 0x51, 0xdb, 0x13, 0x79, 0x0a, 0xc4, 0x76, - 0x86, 0x47, 0xe5, 0x0e, 0x82, 0xad, 0x80, 0xc1, 0x4d, 0x15, 0x89, 0x34, 0x81, 0x28, 0x3f, 0xe5, - 0x02, 0x3e, 0xfd, 0x6e, 0x2d, 0xad, 0x30, 0xe7, 0x54, 0xd6, 0x96, 0xba, 0x9a, 0x53, 0xf8, 0x3e, - 0x49, 0xed, 0xef, 0x86, 0x08, 0xbe, 0x8a, 0x0a, 0xa4, 0xfc, 0xa9, 0xcc, 0x64, 0x60, 0x18, 0xe0, - 0xc1, 0x59, 0x43, 0x35, 0x52, 0xc4, 0x84, 0xbc, 0x07, 0xe3, 0x6a, 0xd8, 0x0c, 0x1e, 0xdc, 0xe1, - 0x39, 0x1e, 0x4f, 0xb3, 0x47, 0xe6, 0x5f, 0x95, 0x80, 0xec, 0xc0, 0x85, 0xb2, 0xdb, 0xf6, 0xbb, - 0x2d, 0x19, 0xb9, 0x33, 0x8a, 0x17, 0x0e, 0x38, 0x14, 0xe8, 0x83, 0x5f, 0x17, 0x28, 0x22, 0x4a, - 0x83, 0x74, 0x93, 0xd1, 0x2f, 0x20, 0xbd, 0x18, 0x91, 0x4d, 0x18, 0x47, 0x0d, 0xaa, 0x30, 0x79, - 0x1c, 0xd7, 0xb7, 0x8d, 0xa8, 0xa4, 0xc2, 0x16, 0x06, 0x8f, 0x1a, 0x67, 0xb7, 0x9a, 0xd2, 0x4b, - 0x43, 0xd5, 0x04, 0x2b, 0xc8, 0xe4, 0x2b, 0x30, 0xc5, 0xaf, 0x68, 0xdb, 0x74, 0x87, 0xcf, 0x9d, - 0x09, 0x4d, 0x13, 0xa1, 0x17, 0xf2, 0xc7, 0x7c, 0xa1, 0xb7, 0x3e, 0xa4, 0x3b, 0x7c, 0xec, 0x35, - 0x1f, 0x29, 0x0d, 0x9f, 0x6c, 0xc1, 0xec, 0x8a, 0xed, 0x73, 0xa0, 0x12, 0xff, 0x60, 0x12, 0x35, - 0xb4, 0x68, 0xbb, 0xbe, 0x6f, 0xfb, 0x52, 0x11, 0x9e, 0x1a, 0xef, 0x20, 0x8d, 0x9e, 0xfc, 0x70, - 0x06, 0xe6, 0x35, 0x3d, 0xb9, 0xb0, 0x33, 0x6b, 0xd1, 0x76, 0x80, 0xce, 0x50, 0x53, 0x8b, 0x45, - 0x29, 0x94, 0xf6, 0x40, 0xe3, 0x43, 0x12, 0x53, 0xc5, 0x7b, 0x51, 0xb9, 0x6a, 0x14, 0xde, 0x8b, - 0x87, 0x58, 0xa8, 0xb8, 0xa6, 0xa7, 0xf5, 0x85, 0x1a, 0x5b, 0xd7, 0x12, 0xcd, 0xb8, 0x15, 0xef, - 0x6f, 0xa1, 0xe8, 0xca, 0x84, 0x8a, 0xae, 0x39, 0x18, 0xc6, 0x5e, 0x95, 0x51, 0xb4, 0xf0, 0x87, - 0xf1, 0x69, 0x75, 0x1f, 0x12, 0x62, 0x61, 0xdf, 0x7d, 0xc8, 0xf8, 0x1f, 0x47, 0x60, 0x3a, 0x36, - 0x2d, 0xc4, 0x3d, 0x35, 0x93, 0xb8, 0xa7, 0xd6, 0x00, 0xb8, 0xaa, 0x77, 0x40, 0x9d, 0xac, 0x74, - 0xc4, 0x1c, 0x17, 0x6e, 0xd4, 0xe1, 0x9a, 0x52, 0xd8, 0x30, 0xa6, 0x7c, 0xc5, 0x0e, 0xa8, 0x23, - 0x0f, 0x99, 0xf2, 0x45, 0xaf, 0x30, 0x8d, 0xd8, 0x90, 0x22, 0x0c, 0x63, 0xfc, 0x5c, 0xd5, 0x0f, - 0xd6, 0x61, 0x00, 0x93, 0xc3, 0xc9, 0x4b, 0x30, 0xc2, 0x84, 0xa8, 0x6a, 0x45, 0x6c, 0x82, 0x78, - 0xb6, 0x30, 0x29, 0x8b, 0x49, 0x2c, 0xa2, 0x88, 0xdc, 0x82, 0x09, 0xfe, 0x97, 0x08, 0xb3, 0x33, - 0xa2, 0x1b, 0x3f, 0x5a, 0x4e, 0x43, 0x46, 0xda, 0xd1, 0xf0, 0xd8, 0xed, 0xa2, 0xd6, 0x45, 0xb5, - 0x4e, 0xb5, 0x22, 0x02, 0xae, 0xe3, 0xed, 0xc2, 0xe7, 0x40, 0x56, 0x45, 0x84, 0xc0, 0x64, 0x19, - 0xe1, 0x8d, 0x92, 0xc7, 0x3b, 0x25, 0xca, 0x32, 0xdc, 0x0b, 0xc5, 0x14, 0x25, 0xe4, 0x2a, 0x7f, - 0x89, 0x41, 0xb1, 0x90, 0xe7, 0xad, 0xc4, 0x77, 0x0b, 0x54, 0x4c, 0xa0, 0x6c, 0x18, 0x16, 0xb3, - 0xca, 0xd9, 0xdf, 0xcb, 0x2d, 0xdb, 0x69, 0x8a, 0x6d, 0x05, 0x2b, 0x47, 0x5c, 0xca, 0xa0, 0x66, - 0x84, 0x40, 0xde, 0x81, 0x29, 0xf6, 0xa3, 0xec, 0xb6, 0x5a, 0x6e, 0x1b, 0xd9, 0x8f, 0x47, 0x81, - 0xf4, 0x90, 0xa4, 0x8e, 0x45, 0xbc, 0x96, 0x18, 0x2e, 0x3b, 0x4f, 0xf0, 0x95, 0xb7, 0xcb, 0xdf, - 0x88, 0x26, 0xa2, 0xf3, 0x04, 0x49, 0x7d, 0x0e, 0x37, 0x55, 0x24, 0xf2, 0x16, 0x4c, 0xb2, 0x9f, - 0x77, 0x9c, 0x87, 0x94, 0x57, 0x38, 0x19, 0x99, 0x37, 0x20, 0xd5, 0x1e, 0x2b, 0xe1, 0xf5, 0xe9, - 0x98, 0xe4, 0x03, 0x38, 0x87, 0x9c, 0xea, 0x6e, 0x87, 0x36, 0x4a, 0xbb, 0xbb, 0x4e, 0xd3, 0xe1, - 0xd6, 0x68, 0x3c, 0xa0, 0x0c, 0xea, 0xe0, 0x79, 0xc5, 0x88, 0x61, 0xd9, 0x11, 0x8a, 0x99, 0x4e, - 0x49, 0xb6, 0xa1, 0x50, 0xee, 0xfa, 0x81, 0xdb, 0x2a, 0x05, 0x81, 0xe7, 0xec, 0x74, 0x03, 0xea, - 0xcf, 0x4f, 0x6b, 0x61, 0x57, 0xd8, 0xe2, 0x08, 0x0b, 0xb9, 0x3e, 0xa8, 0x8e, 0x14, 0x96, 0x1d, - 0x92, 0x98, 0x09, 0x26, 0xc6, 0x3f, 0xcf, 0xc0, 0xa4, 0x46, 0x4a, 0xde, 0x84, 0x89, 0xdb, 0x9e, - 0x43, 0xdb, 0x8d, 0xe6, 0x91, 0x72, 0x51, 0xc5, 0x5b, 0xcc, 0xae, 0x80, 0xf3, 0x56, 0x6b, 0x68, - 0xa1, 0x9e, 0x27, 0x9b, 0x6a, 0x2a, 0x7a, 0x9d, 0xbb, 0x63, 0x8b, 0x09, 0x9a, 0x8b, 0xe2, 0x40, - 0xe1, 0x04, 0x15, 0xb3, 0x53, 0x41, 0x21, 0xef, 0xc2, 0x08, 0x7f, 0x0f, 0x16, 0x76, 0x8b, 0x17, - 0xd3, 0x9a, 0xc9, 0x5d, 0xff, 0x71, 0x22, 0xa2, 0xd1, 0x8f, 0x6f, 0x0a, 0x22, 0xe3, 0xe7, 0x32, - 0x40, 0x92, 0xa8, 0xa7, 0xe8, 0xbd, 0x4e, 0x35, 0x26, 0xfa, 0x42, 0xb8, 0x1a, 0x73, 0x9a, 0xce, - 0x9c, 0xd5, 0xc4, 0x0b, 0x78, 0xc7, 0x8b, 0x55, 0xa7, 0x2a, 0xe2, 0x78, 0xb1, 0xf1, 0x43, 0x59, - 0x80, 0x08, 0x9b, 0x7c, 0x8e, 0xa7, 0x29, 0xfb, 0xa0, 0x6b, 0x37, 0x9d, 0x5d, 0x47, 0x8f, 0xdb, - 0x8b, 0x4c, 0xbe, 0x2e, 0x4b, 0x4c, 0x1d, 0x91, 0xbc, 0x0f, 0xd3, 0xb5, 0x0d, 0x9d, 0x56, 0x31, - 0x8b, 0xf7, 0x3b, 0x56, 0x8c, 0x3c, 0x8e, 0x8d, 0xf6, 0xc9, 0xea, 0x68, 0x70, 0xfb, 0x64, 0x3e, - 0x10, 0xa2, 0x84, 0x6d, 0x2c, 0xb5, 0x0d, 0x61, 0xf9, 0xdf, 0x08, 0x5f, 0x35, 0xf1, 0xeb, 0xfc, - 0x8e, 0xd5, 0x11, 0x2e, 0x01, 0x6c, 0x9f, 0xd0, 0xf0, 0xa2, 0x8e, 0x1c, 0xee, 0xe1, 0xde, 0xff, - 0xf3, 0xa8, 0xf6, 0x6b, 0xb9, 0x01, 0x15, 0xda, 0x8e, 0xa7, 0xf6, 0xde, 0x13, 0x19, 0x13, 0x0c, - 0x6b, 0x5e, 0xcb, 0x5a, 0xeb, 0x84, 0xc1, 0xcc, 0xcd, 0xe8, 0x92, 0xc2, 0xcd, 0x0a, 0x52, 0x6c, - 0x6c, 0xfe, 0x6e, 0x06, 0xce, 0xa5, 0xd2, 0x92, 0x6b, 0x00, 0x91, 0x4e, 0x49, 0xf4, 0x12, 0xee, - 0x98, 0x51, 0xf4, 0x23, 0x53, 0xc1, 0x20, 0x5f, 0x8e, 0x6b, 0x83, 0x4e, 0x3f, 0x08, 0x17, 0x64, - 0xd0, 0x41, 0x5d, 0x1b, 0x94, 0xa2, 0x03, 0x32, 0x7e, 0x35, 0x07, 0x33, 0x4a, 0x70, 0x25, 0xfe, - 0xad, 0xa7, 0xd8, 0x8b, 0x1f, 0xc0, 0x04, 0x6b, 0x8d, 0x53, 0x17, 0x6e, 0x37, 0xdc, 0xf0, 0xe5, - 0xb5, 0x84, 0xdf, 0xa9, 0xe0, 0x76, 0x4d, 0x45, 0xe6, 0xa1, 0x40, 0x71, 0xeb, 0xc4, 0x07, 0x89, - 0x7a, 0xd2, 0xe5, 0x46, 0x63, 0x4e, 0x7c, 0x98, 0xac, 0x1c, 0xb5, 0xed, 0x56, 0x58, 0x1b, 0x37, - 0x80, 0xf9, 0x54, 0xcf, 0xda, 0x34, 0x6c, 0x5e, 0x5d, 0xe4, 0xa1, 0xc5, 0xcb, 0x52, 0x82, 0x03, - 0x68, 0x54, 0x0b, 0xef, 0xc3, 0x4c, 0xe2, 0xa3, 0xcf, 0x14, 0x95, 0x74, 0x1b, 0x48, 0xf2, 0x3b, - 0x52, 0x38, 0x7c, 0x4a, 0x8f, 0x79, 0x7b, 0x2e, 0x7c, 0xbc, 0x6e, 0xb5, 0xec, 0x76, 0x83, 0x9b, - 0xd3, 0x2c, 0xaa, 0x31, 0x4b, 0x7f, 0x3e, 0xab, 0xfa, 0xfe, 0x3e, 0xed, 0xab, 0xee, 0x0b, 0xda, - 0x6d, 0xf8, 0x85, 0x5e, 0x63, 0x3a, 0x90, 0xd6, 0xe1, 0x5b, 0x39, 0xb8, 0xd0, 0x83, 0x92, 0x1c, - 0xc5, 0x27, 0x11, 0xd7, 0x42, 0xdc, 0xe8, 0x5f, 0xe1, 0x93, 0x98, 0x4a, 0xe4, 0x73, 0x3c, 0xfa, - 0x47, 0xdd, 0x6d, 0xef, 0x3a, 0x7b, 0xe2, 0xfe, 0x8d, 0x6a, 0xfc, 0x83, 0x10, 0x1a, 0x0f, 0xfb, - 0xc1, 0xa1, 0xe4, 0x7d, 0x18, 0x46, 0xc7, 0xef, 0x58, 0x78, 0x47, 0x86, 0x81, 0x70, 0x25, 0x40, - 0x29, 0xfb, 0xa9, 0x05, 0x28, 0x65, 0x00, 0xf2, 0x59, 0xc8, 0x95, 0xb6, 0x6b, 0x62, 0x5c, 0xa6, - 0x54, 0xf2, 0xed, 0x5a, 0x94, 0x5c, 0xc5, 0xd6, 0xb2, 0xa0, 0x30, 0x0a, 0x46, 0x78, 0xa7, 0xbc, - 0x21, 0x46, 0x45, 0x25, 0xbc, 0x53, 0xde, 0x88, 0x08, 0xf7, 0xea, 0x5a, 0xb0, 0xac, 0x3b, 0xe5, - 0x8d, 0x4f, 0x6e, 0xda, 0xff, 0x7b, 0x59, 0x1e, 0xb2, 0x84, 0x37, 0xec, 0x7d, 0x98, 0xd0, 0x62, - 0x92, 0x67, 0x22, 0x79, 0x2c, 0x8c, 0x1f, 0x1f, 0xb3, 0x18, 0xd2, 0x08, 0x64, 0x9a, 0x22, 0xf6, - 0x1b, 0x25, 0x5e, 0xd5, 0xd8, 0x26, 0xe4, 0x80, 0x32, 0x71, 0x3c, 0x4d, 0x51, 0x48, 0x42, 0x6e, - 0x42, 0x7e, 0x93, 0xb6, 0xed, 0x76, 0x10, 0x2a, 0x44, 0xd1, 0xb8, 0x38, 0x40, 0x98, 0x2e, 0x35, - 0x84, 0x88, 0x68, 0x08, 0xdb, 0xdd, 0xf1, 0xeb, 0x9e, 0x83, 0xa1, 0x8d, 0xc2, 0xb3, 0x98, 0x1b, - 0xc2, 0x2a, 0x25, 0x3a, 0x83, 0x18, 0x91, 0xf1, 0xf3, 0x19, 0x18, 0x15, 0x03, 0xc9, 0xd3, 0xcb, - 0xed, 0x45, 0x67, 0x89, 0x70, 0x1e, 0xd8, 0x73, 0xe2, 0xce, 0x03, 0x7b, 0x3c, 0x7e, 0xd0, 0x98, - 0x70, 0xac, 0x0b, 0x9f, 0x06, 0x71, 0x36, 0x4a, 0xb7, 0x4f, 0x3d, 0x7b, 0x58, 0x88, 0x3a, 0xa8, - 0x43, 0x96, 0xf1, 0xb7, 0xc5, 0x97, 0xdd, 0x29, 0x6f, 0x90, 0x45, 0xc8, 0xaf, 0xba, 0x3c, 0x14, - 0x96, 0x9a, 0x2b, 0xb8, 0x29, 0x60, 0x6a, 0x07, 0x49, 0x3c, 0xf6, 0x7d, 0x1b, 0x9e, 0x2b, 0xee, - 0x32, 0xca, 0xf7, 0x75, 0x38, 0x30, 0xf6, 0x7d, 0x21, 0xea, 0xc0, 0xdf, 0x47, 0x53, 0x36, 0x89, - 0x07, 0x37, 0x31, 0x7f, 0xcb, 0x5d, 0xd5, 0xd1, 0x4d, 0x14, 0xc9, 0x9d, 0x62, 0xa1, 0xd7, 0x4e, - 0xf1, 0xe0, 0xa6, 0x99, 0x42, 0x85, 0xef, 0x6a, 0x11, 0xb8, 0x46, 0xbd, 0x87, 0x4f, 0xf1, 0x2e, - 0x9d, 0xfe, 0xae, 0x16, 0x6f, 0xde, 0x40, 0x9b, 0xf4, 0x1f, 0x64, 0xe1, 0x7c, 0x3a, 0xa1, 0xda, - 0x96, 0x4c, 0x9f, 0xb6, 0x5c, 0x81, 0xfc, 0x8a, 0xeb, 0x07, 0x8a, 0x91, 0x20, 0xaa, 0xff, 0xf7, - 0x05, 0xcc, 0x0c, 0x4b, 0xd9, 0x9d, 0x9b, 0xfd, 0x1d, 0x2e, 0x4f, 0xe4, 0x87, 0x81, 0x3a, 0xd8, - 0x9d, 0x9b, 0x17, 0x91, 0x3b, 0x90, 0x37, 0x85, 0xa3, 0x55, 0xac, 0x6b, 0x24, 0x38, 0x94, 0xa6, - 0x88, 0x27, 0x20, 0x5a, 0x68, 0x78, 0x01, 0x23, 0x25, 0x18, 0x15, 0xa3, 0x1f, 0x7b, 0x3a, 0x4e, - 0x99, 0x32, 0x7a, 0xb6, 0x06, 0x49, 0xc7, 0x76, 0x14, 0x7c, 0x04, 0xac, 0x56, 0xa4, 0xcf, 0x14, - 0xee, 0x28, 0xfc, 0x91, 0x50, 0xb7, 0xc7, 0x0c, 0x11, 0x8d, 0x1f, 0xce, 0x02, 0x48, 0xad, 0xcd, - 0x53, 0x3b, 0xc3, 0x3e, 0xab, 0xcd, 0x30, 0xc5, 0xde, 0x68, 0xf0, 0x74, 0xc8, 0xf7, 0xd1, 0x9c, - 0x67, 0xf0, 0x64, 0xc8, 0x45, 0x18, 0xde, 0x8c, 0x14, 0x5a, 0xc2, 0x25, 0x05, 0xd5, 0xd1, 0x1c, - 0x6e, 0xec, 0xc0, 0xdc, 0x1d, 0x1a, 0x44, 0xea, 0x2d, 0xf9, 0xf4, 0xd8, 0x9f, 0xed, 0xeb, 0x30, - 0x26, 0xf0, 0xc3, 0xfd, 0x8b, 0xeb, 0x62, 0x44, 0xec, 0x1b, 0xd4, 0xc5, 0x48, 0x04, 0xb6, 0x1b, - 0x55, 0x68, 0x93, 0x06, 0xf4, 0x93, 0xad, 0xa6, 0x06, 0x84, 0x37, 0x05, 0x5b, 0x36, 0x58, 0x0d, - 0xa7, 0xf6, 0xcf, 0x03, 0x38, 0x17, 0x7e, 0xfb, 0x93, 0xe4, 0x7b, 0x9d, 0x5d, 0x29, 0x45, 0xa2, - 0x83, 0x88, 0x63, 0x1f, 0xdb, 0x93, 0x47, 0xb0, 0x20, 0x09, 0xb6, 0x9d, 0xd0, 0x70, 0x72, 0x20, - 0x5a, 0xf2, 0x0e, 0x8c, 0x2b, 0x34, 0x22, 0x50, 0x3f, 0xaa, 0xa9, 0x0f, 0x9d, 0x60, 0xdf, 0xf2, - 0x39, 0x5c, 0x55, 0x53, 0x2b, 0xe8, 0xc6, 0x97, 0xe0, 0xd9, 0xd0, 0x6d, 0x28, 0xa5, 0xea, 0x18, - 0xf3, 0xcc, 0xd9, 0x98, 0xaf, 0x47, 0xcd, 0xaa, 0xb6, 0x43, 0xcf, 0x68, 0xc9, 0x9b, 0xa8, 0xcd, - 0x12, 0x8d, 0x79, 0x2e, 0xe1, 0x6b, 0xad, 0xb8, 0x54, 0x1b, 0x6f, 0x2b, 0x1f, 0x9b, 0xc2, 0x50, - 0x23, 0xce, 0xc4, 0x89, 0x7f, 0x38, 0x0b, 0xd3, 0xf7, 0xab, 0x95, 0x72, 0x68, 0x7d, 0xf4, 0x1d, - 0x96, 0xac, 0x59, 0x6b, 0x5b, 0xef, 0xfd, 0xc6, 0xd8, 0x82, 0xd9, 0x58, 0x37, 0xa0, 0xe8, 0xf0, - 0x1e, 0x77, 0x38, 0x09, 0xc1, 0x52, 0x6c, 0x38, 0x9f, 0xc6, 0xfe, 0xc1, 0x4d, 0x33, 0x86, 0x6d, - 0xfc, 0x97, 0x10, 0xe3, 0x2b, 0xb6, 0xb0, 0xd7, 0x61, 0xac, 0xea, 0xfb, 0x5d, 0xea, 0x6d, 0x99, - 0xab, 0xaa, 0xaa, 0xc0, 0x41, 0xa0, 0xd5, 0xf5, 0x9a, 0x66, 0x84, 0x40, 0xae, 0x42, 0x5e, 0x04, - 0x49, 0x97, 0x7b, 0x02, 0x6a, 0x6d, 0xc3, 0x18, 0xeb, 0x66, 0x58, 0x4c, 0xde, 0x84, 0x09, 0xfe, - 0x37, 0x9f, 0x6d, 0xa2, 0xc3, 0x51, 0x39, 0x28, 0xd0, 0xf9, 0xec, 0x34, 0x35, 0x34, 0xf2, 0x1a, - 0xe4, 0x4a, 0x65, 0x53, 0xa8, 0x83, 0x84, 0xdc, 0xe8, 0x59, 0x5c, 0x67, 0xa7, 0x5d, 0x22, 0xca, - 0x26, 0x93, 0xfe, 0x64, 0xb0, 0x09, 0xa1, 0xc9, 0xc6, 0x19, 0x20, 0xb5, 0x4d, 0xb1, 0xc3, 0x0c, - 0x61, 0xe4, 0x3a, 0x8c, 0x56, 0xb8, 0xc9, 0x9c, 0xd0, 0x63, 0xf3, 0x4c, 0x84, 0x1c, 0xa4, 0x05, - 0x57, 0xe0, 0x20, 0x72, 0x55, 0x66, 0x68, 0xcb, 0x47, 0x7e, 0x2b, 0x3d, 0xd2, 0xb0, 0xbd, 0x0e, - 0x23, 0x22, 0x94, 0xf8, 0x98, 0x92, 0xbb, 0x25, 0x1e, 0x42, 0x5c, 0xe0, 0x24, 0x1d, 0x58, 0xe1, - 0x49, 0x3a, 0xb0, 0xee, 0xc0, 0x85, 0x3b, 0xa8, 0xbd, 0xd1, 0x03, 0x62, 0x6d, 0x99, 0x55, 0xa1, - 0x0f, 0xc7, 0x67, 0x20, 0xae, 0xe0, 0x89, 0xc7, 0xd4, 0xb2, 0xba, 0x9e, 0x9a, 0x58, 0xb7, 0x17, - 0x23, 0xf2, 0x45, 0x98, 0x4b, 0x2b, 0x12, 0x5a, 0x73, 0x0c, 0xfd, 0x94, 0x5e, 0x81, 0x1a, 0xfa, - 0x29, 0x8d, 0x03, 0x59, 0x85, 0x02, 0x87, 0x97, 0x1a, 0x2d, 0xa7, 0xcd, 0x35, 0xff, 0x5c, 0xab, - 0x8e, 0x8e, 0x24, 0x82, 0xab, 0xcd, 0x0a, 0xf9, 0x0b, 0x80, 0xe6, 0x7a, 0x14, 0xa3, 0x24, 0x3f, - 0x95, 0x61, 0xb7, 0x39, 0x1e, 0x78, 0x7b, 0xcb, 0x5c, 0xf5, 0x45, 0xd8, 0xc0, 0xf3, 0x91, 0x57, - 0x51, 0x2d, 0xf0, 0x9c, 0xf6, 0x9e, 0x70, 0x2b, 0xda, 0x14, 0x6e, 0x45, 0xef, 0x7c, 0x2c, 0xb7, - 0x22, 0xce, 0xca, 0x3f, 0x39, 0x2e, 0x4e, 0x78, 0xa2, 0x4e, 0x5c, 0x45, 0xda, 0x17, 0xb0, 0xae, - 0x43, 0xdf, 0xda, 0xad, 0x36, 0x0f, 0xfb, 0x4b, 0x1b, 0xbc, 0x91, 0xd3, 0xb8, 0x83, 0x63, 0xd7, - 0x61, 0x4e, 0x10, 0xab, 0x1b, 0x22, 0x24, 0x1a, 0x9a, 0xca, 0x81, 0x5d, 0x3c, 0xa5, 0xeb, 0x0a, - 0xf7, 0xc6, 0x2d, 0x44, 0x17, 0x4f, 0xe9, 0xe7, 0x62, 0xe1, 0x34, 0x52, 0x27, 0x8f, 0x46, 0x42, - 0xae, 0xc3, 0xc8, 0x9a, 0xfd, 0xa8, 0xb4, 0x47, 0x45, 0xe6, 0xcd, 0x49, 0xb9, 0xfd, 0x21, 0x70, - 0x29, 0xff, 0x87, 0xdc, 0xd7, 0xe1, 0x19, 0x53, 0xa0, 0x91, 0xef, 0xcb, 0xc0, 0x79, 0xbe, 0x8c, - 0x65, 0x2b, 0x6b, 0x34, 0x08, 0x58, 0x3f, 0x88, 0xf8, 0x81, 0x97, 0x22, 0x83, 0xed, 0x74, 0x3c, - 0xf4, 0xbc, 0x37, 0xc4, 0xce, 0x10, 0x76, 0x9c, 0x2f, 0x4a, 0xb5, 0x40, 0xcc, 0xa9, 0xf4, 0x64, - 0x13, 0xc6, 0xd7, 0x6e, 0x97, 0xc2, 0x6a, 0x79, 0x74, 0xf6, 0x62, 0xda, 0xee, 0xa8, 0xa0, 0xa5, - 0x79, 0x1a, 0xa8, 0x6c, 0x84, 0x77, 0xc0, 0x67, 0x65, 0x7f, 0x90, 0x37, 0x54, 0x57, 0xd4, 0x1c, - 0x4a, 0xcf, 0xa3, 0x2d, 0xfb, 0x91, 0x65, 0xef, 0x51, 0xed, 0x95, 0x5c, 0x68, 0xaf, 0x7f, 0x36, - 0x03, 0x17, 0x7b, 0x36, 0x99, 0xdc, 0x82, 0x0b, 0x36, 0x77, 0xb0, 0xb6, 0xf6, 0x83, 0xa0, 0xe3, - 0x5b, 0xf2, 0x8a, 0x21, 0x9c, 0x57, 0xcd, 0x73, 0xa2, 0x78, 0x85, 0x95, 0xca, 0x5b, 0x87, 0x4f, - 0xde, 0x87, 0xe7, 0x9c, 0xb6, 0x4f, 0xeb, 0x5d, 0x8f, 0x5a, 0x92, 0x41, 0xdd, 0x69, 0x78, 0x96, - 0x67, 0xb7, 0xf7, 0xa4, 0x27, 0xae, 0x79, 0x51, 0xe2, 0x08, 0x27, 0xee, 0xb2, 0xd3, 0xf0, 0x4c, - 0x44, 0x30, 0xfe, 0x79, 0x06, 0xe6, 0x7b, 0x75, 0x09, 0x99, 0x87, 0x51, 0xaa, 0xe4, 0x69, 0xc9, - 0x9b, 0xf2, 0x27, 0x79, 0x16, 0xa2, 0x9d, 0x5e, 0x9c, 0xfe, 0xf9, 0xba, 0xc8, 0x99, 0x81, 0xa6, - 0xed, 0xea, 0xbe, 0x2e, 0x0c, 0x94, 0x27, 0xea, 0xea, 0xee, 0xfe, 0x3c, 0x40, 0xb4, 0x9d, 0x73, - 0xc5, 0x84, 0x39, 0x66, 0xd7, 0x3d, 0xbe, 0xf2, 0xc8, 0x79, 0x18, 0xe1, 0xdb, 0xa5, 0xf0, 0x7f, - 0x10, 0xbf, 0xd8, 0xb9, 0x2d, 0x3a, 0x19, 0xf7, 0xf9, 0xdc, 0xd2, 0x84, 0xd6, 0xd9, 0x23, 0x2d, - 0x1c, 0x1c, 0xe3, 0xa7, 0x27, 0xb9, 0x08, 0x51, 0xea, 0x06, 0xfb, 0x52, 0xe8, 0x58, 0x4c, 0xf3, - 0x17, 0xe3, 0xb6, 0x94, 0x8a, 0x5d, 0xb6, 0xee, 0x25, 0x26, 0xdf, 0x7e, 0xb2, 0xa9, 0x6f, 0x3f, - 0xaf, 0xc3, 0x58, 0x79, 0x9f, 0xd6, 0x0f, 0x42, 0x27, 0x9c, 0xbc, 0x50, 0xae, 0x33, 0x20, 0x0f, - 0x89, 0x1e, 0x21, 0x90, 0xeb, 0x00, 0xe8, 0xa6, 0xca, 0x25, 0x52, 0x25, 0xad, 0x09, 0x7a, 0xb5, - 0x0a, 0xf3, 0x14, 0x05, 0x05, 0xd9, 0xd7, 0xcc, 0xdb, 0xaa, 0x3d, 0x0b, 0x67, 0xef, 0x7b, 0xbb, - 0x02, 0x3d, 0x42, 0x60, 0xcd, 0x53, 0xf6, 0x15, 0x71, 0x0a, 0x16, 0x12, 0x9b, 0x8f, 0x8a, 0x44, - 0xae, 0xc1, 0xd8, 0x86, 0x74, 0x24, 0xc0, 0x43, 0x70, 0x02, 0x29, 0x20, 0x72, 0x3a, 0x98, 0xcf, - 0x98, 0x11, 0x0a, 0xf9, 0x2c, 0x8c, 0x96, 0xa9, 0x17, 0x6c, 0x6e, 0xae, 0xa2, 0xd1, 0x09, 0xcf, - 0xfe, 0x91, 0xc7, 0x4c, 0x0d, 0x41, 0xd0, 0xfc, 0xf6, 0x71, 0x71, 0x32, 0x70, 0x5a, 0x34, 0x8c, - 0x6a, 0x6e, 0x4a, 0x6c, 0xb2, 0x04, 0x05, 0xfe, 0x2c, 0x1e, 0xdd, 0x3d, 0xf0, 0x64, 0xcc, 0xf3, - 0x73, 0x5a, 0xbc, 0xa1, 0x1f, 0xd2, 0x9d, 0x30, 0x4f, 0x45, 0x02, 0x9f, 0x2c, 0xcb, 0xf4, 0x2e, - 0x6a, 0x33, 0x21, 0x52, 0x86, 0xc5, 0x77, 0x0c, 0xd6, 0xda, 0x24, 0x05, 0x29, 0xc1, 0x64, 0xd9, - 0x6d, 0x75, 0xec, 0xc0, 0xc1, 0x3c, 0x98, 0x47, 0xe2, 0x10, 0x44, 0x85, 0x5e, 0x5d, 0x2d, 0xd0, - 0x4e, 0x54, 0xb5, 0x80, 0xdc, 0x86, 0x29, 0xd3, 0xed, 0xb2, 0x61, 0x92, 0xb7, 0x70, 0x7e, 0xce, - 0xa1, 0x69, 0x88, 0xc7, 0x4a, 0xd8, 0xb1, 0x2c, 0xae, 0xdc, 0x5a, 0x04, 0x58, 0x8d, 0x8a, 0xac, - 0xa7, 0x3c, 0x87, 0xa8, 0x87, 0x9b, 0x9a, 0xad, 0x22, 0xc1, 0x2c, 0xe5, 0x25, 0xe5, 0x26, 0x8c, - 0xd7, 0x6a, 0xf7, 0x37, 0xa9, 0x1f, 0xdc, 0x6e, 0xba, 0x87, 0x78, 0xb6, 0xe5, 0x45, 0x72, 0x35, - 0xdf, 0xb5, 0x02, 0xea, 0x07, 0xd6, 0x6e, 0xd3, 0x3d, 0x34, 0x55, 0x2c, 0xf2, 0x55, 0xd6, 0x1f, - 0x8a, 0x24, 0x28, 0x62, 0xdd, 0xf6, 0x13, 0x56, 0xf1, 0x04, 0x89, 0x16, 0x0d, 0x13, 0x59, 0xf5, - 0xce, 0x52, 0xd0, 0xd1, 0xa7, 0xcc, 0x73, 0x1f, 0x1d, 0x95, 0x1a, 0x0d, 0x8f, 0xfa, 0xbe, 0x38, - 0x84, 0xb8, 0x4f, 0x19, 0x2a, 0x1b, 0x6c, 0x5e, 0xa0, 0xf9, 0x94, 0x29, 0x04, 0xe4, 0x47, 0x33, - 0x70, 0x4e, 0xf5, 0x36, 0xc1, 0xe5, 0x82, 0x66, 0x2e, 0xfc, 0x48, 0x7a, 0xe3, 0x9a, 0x3c, 0x84, - 0xaf, 0x29, 0x68, 0xd7, 0x1e, 0xde, 0xb8, 0x56, 0x8a, 0x7e, 0xd6, 0x24, 0x11, 0xc6, 0xed, 0x2b, - 0xa6, 0xf2, 0xd3, 0x72, 0x13, 0xcd, 0xd9, 0x29, 0xc4, 0xa4, 0xcc, 0x24, 0x35, 0x36, 0xa3, 0xd0, - 0x70, 0xaa, 0xba, 0x81, 0x67, 0x9a, 0xd0, 0xa8, 0x8a, 0xf9, 0xc7, 0x4d, 0xac, 0x9c, 0x8e, 0x2e, - 0x90, 0x29, 0x34, 0xa4, 0x0a, 0xd3, 0x1c, 0xc0, 0xb6, 0x05, 0x9e, 0xe6, 0x69, 0x36, 0x4a, 0x34, - 0x21, 0xd8, 0xe0, 0x5b, 0x3f, 0xa6, 0x7a, 0x52, 0x83, 0xb3, 0xc6, 0xe8, 0xc8, 0xfb, 0x30, 0x85, - 0x31, 0xf4, 0xa3, 0xf5, 0x3a, 0x87, 0xab, 0x18, 0x63, 0xcc, 0x8a, 0x92, 0x98, 0xe7, 0xdd, 0x84, - 0xef, 0xef, 0x47, 0x2b, 0xfa, 0x7d, 0x98, 0x42, 0x5b, 0x9d, 0x88, 0xc1, 0xb9, 0x88, 0x81, 0x28, - 0x89, 0x33, 0x08, 0x9a, 0x7e, 0xc4, 0xe0, 0x67, 0x32, 0x70, 0x91, 0x55, 0x94, 0x3e, 0x42, 0xe7, - 0x3f, 0xce, 0x08, 0x61, 0xd4, 0xcd, 0x9e, 0x3c, 0x55, 0x71, 0xd4, 0xf7, 0xf7, 0xd3, 0x38, 0xe0, - 0x47, 0xb1, 0x8f, 0x4f, 0xff, 0xa8, 0x0b, 0x1f, 0xfb, 0xa3, 0x7a, 0xf2, 0x54, 0x3f, 0x2a, 0x68, - 0xfa, 0x69, 0x1c, 0xf0, 0x5a, 0x5b, 0x2b, 0xad, 0xad, 0x46, 0x77, 0xb3, 0xef, 0x2c, 0xb7, 0x15, - 0xad, 0x6d, 0x7d, 0xdc, 0x56, 0xb6, 0xb8, 0x17, 0xb5, 0xd2, 0x0d, 0xf2, 0x5a, 0xab, 0x81, 0xe3, - 0xd7, 0xda, 0x18, 0x8d, 0x19, 0xc3, 0x36, 0x7e, 0x19, 0x62, 0x7c, 0x85, 0xa9, 0xaa, 0x01, 0x23, - 0xfc, 0xd6, 0x2a, 0x3a, 0x19, 0x6d, 0x16, 0xf8, 0x9d, 0xd6, 0x14, 0x25, 0xe4, 0x22, 0xe4, 0x6a, - 0xb5, 0xfb, 0xa2, 0x93, 0xd1, 0x60, 0xd5, 0xf7, 0x5d, 0x93, 0xc1, 0xd8, 0x08, 0xa1, 0x15, 0xaa, - 0x92, 0x93, 0x80, 0x9d, 0x77, 0x26, 0x42, 0x59, 0x7f, 0xcb, 0x3b, 0xe4, 0x50, 0xd4, 0xdf, 0xe2, - 0x0e, 0x19, 0xdd, 0x1c, 0xcb, 0x30, 0x5f, 0xf2, 0x7d, 0xea, 0xb1, 0x09, 0x21, 0x8c, 0x1b, 0x3d, - 0x71, 0xcf, 0x11, 0x07, 0x3b, 0x56, 0x6a, 0xd7, 0x7d, 0xb3, 0x27, 0x22, 0xb9, 0x02, 0xf9, 0x52, - 0xb7, 0xe1, 0xd0, 0x76, 0x5d, 0x0b, 0xcb, 0x66, 0x0b, 0x98, 0x19, 0x96, 0x92, 0x0f, 0xe0, 0x5c, - 0x2c, 0x02, 0xa3, 0xe8, 0x81, 0xd1, 0x68, 0xef, 0x95, 0xf7, 0xb0, 0xc8, 0x20, 0x83, 0x77, 0x49, - 0x3a, 0x25, 0x29, 0x41, 0x61, 0x19, 0xdd, 0xb4, 0x2a, 0x94, 0xbf, 0x0d, 0xb9, 0x1e, 0xf7, 0xcf, - 0xe3, 0xb7, 0x66, 0x11, 0x67, 0xb2, 0x11, 0x16, 0x9a, 0x09, 0x74, 0x72, 0x0f, 0x66, 0xe3, 0x30, - 0x76, 0x82, 0xf3, 0x0b, 0x32, 0xee, 0x37, 0x09, 0x2e, 0x78, 0x86, 0xa7, 0x51, 0x91, 0x1d, 0x98, - 0x89, 0x0c, 0x92, 0xf4, 0x6b, 0xb3, 0xb4, 0x73, 0x0e, 0xcb, 0xe5, 0xd5, 0xf9, 0x59, 0x31, 0x19, - 0x67, 0x23, 0xe3, 0xa6, 0xf0, 0xfa, 0x6c, 0x26, 0xd9, 0x91, 0x06, 0x4c, 0xd5, 0x9c, 0xbd, 0xb6, - 0xd3, 0xde, 0xbb, 0x47, 0x8f, 0x36, 0x6c, 0xc7, 0x13, 0x16, 0xa7, 0xd2, 0x9e, 0xbc, 0xe4, 0x1f, - 0xb5, 0x5a, 0x34, 0xf0, 0x70, 0x23, 0x64, 0xe5, 0xe8, 0x83, 0xce, 0xae, 0x43, 0x0b, 0x3e, 0xa7, - 0x43, 0xb7, 0xcd, 0x8e, 0xed, 0x68, 0x42, 0x80, 0xce, 0x53, 0x53, 0x5d, 0x4c, 0x0c, 0xa8, 0xba, - 0x68, 0xc2, 0xcc, 0x72, 0xbb, 0xee, 0x1d, 0xe1, 0x13, 0x9d, 0xfc, 0xb8, 0xc9, 0x53, 0x3e, 0xee, - 0x65, 0xf1, 0x71, 0xcf, 0xd9, 0x72, 0x86, 0xa5, 0x7d, 0x5e, 0x92, 0x31, 0xa9, 0xc1, 0x0c, 0x5e, - 0x1c, 0xaa, 0x95, 0x8d, 0x6a, 0xdb, 0x09, 0x1c, 0x3b, 0xa0, 0x0d, 0x21, 0x5c, 0x84, 0x99, 0x5c, - 0xf8, 0x15, 0xd5, 0x69, 0x74, 0x2c, 0x47, 0xa2, 0xa8, 0x4c, 0x13, 0xf4, 0xfd, 0xee, 0x89, 0xd3, - 0x7f, 0x41, 0xf7, 0xc4, 0x2a, 0x4c, 0xc7, 0x43, 0x39, 0x14, 0xa2, 0x73, 0xd8, 0xc7, 0x22, 0x76, - 0x9c, 0xbb, 0x5d, 0x14, 0x26, 0xb5, 0xe4, 0xa9, 0xb1, 0x20, 0x0e, 0xb1, 0x2b, 0xe7, 0x8c, 0x76, - 0xe5, 0xd4, 0x76, 0xa5, 0x33, 0x5c, 0x39, 0xc9, 0x06, 0xc0, 0x6d, 0xd7, 0xab, 0xd3, 0x12, 0xfa, - 0x47, 0x13, 0x2d, 0xdf, 0x15, 0x63, 0x1a, 0x15, 0xf2, 0xf5, 0xb3, 0xcb, 0x7e, 0x5b, 0x71, 0x37, - 0x77, 0x85, 0x87, 0xf1, 0x63, 0x59, 0x98, 0xef, 0xf5, 0x39, 0x7d, 0xae, 0x7b, 0x9f, 0x82, 0xe4, - 0x0a, 0x17, 0xd7, 0xbe, 0x02, 0x8d, 0xaf, 0xf3, 0x45, 0x48, 0x5f, 0xc8, 0xe2, 0x1a, 0x38, 0x1b, - 0x27, 0xd8, 0xf2, 0x9a, 0xe4, 0x16, 0x8c, 0x2b, 0x1f, 0x8f, 0x7b, 0x69, 0xaf, 0xa6, 0x9a, 0xb0, - 0x1b, 0xfe, 0xcd, 0xae, 0x89, 0x7c, 0xdf, 0x92, 0xd7, 0x44, 0xfe, 0x8b, 0x14, 0xb8, 0x8b, 0xf8, - 0x08, 0xb7, 0x02, 0xf0, 0x7d, 0x97, 0x10, 0xc0, 0x7d, 0x9b, 0x6f, 0x81, 0x26, 0xfe, 0x6d, 0xfc, - 0xe6, 0x04, 0x3f, 0x91, 0xd5, 0x5b, 0x62, 0x2f, 0xfb, 0xe0, 0xd8, 0xed, 0x31, 0x7b, 0x96, 0xdb, - 0x63, 0xee, 0xf4, 0xdb, 0xe3, 0xd0, 0x69, 0xb7, 0xc7, 0xd8, 0xf5, 0x6e, 0xf8, 0xcc, 0xd7, 0xbb, - 0x91, 0x33, 0x5d, 0xef, 0x46, 0xcf, 0x74, 0xbd, 0xd3, 0x6e, 0xaa, 0xf9, 0xd3, 0x6e, 0xaa, 0x7f, - 0x75, 0x19, 0x7c, 0x5a, 0x2f, 0x83, 0x69, 0x22, 0xde, 0x99, 0x2e, 0x83, 0x3f, 0xd2, 0xf3, 0x2e, - 0x57, 0xf8, 0x38, 0x42, 0xf9, 0x4b, 0x03, 0xdc, 0xe5, 0x06, 0xbd, 0xc9, 0xcd, 0x3c, 0x99, 0x9b, - 0x1c, 0x79, 0x62, 0x37, 0xb9, 0xd9, 0xc7, 0xbd, 0xc9, 0xcd, 0x3d, 0xc9, 0x9b, 0xdc, 0xb9, 0xbf, - 0x8c, 0x37, 0xb9, 0xf3, 0xff, 0x76, 0x6e, 0x72, 0x7f, 0x0d, 0x0a, 0x71, 0xe1, 0xf2, 0xf4, 0xa8, - 0xc7, 0x4f, 0x2c, 0xe4, 0x24, 0x13, 0x7d, 0xe3, 0xc2, 0x1d, 0xb9, 0x0e, 0xb0, 0xe1, 0x39, 0x0f, - 0xed, 0x80, 0xde, 0x93, 0xd6, 0x6f, 0x22, 0x62, 0x37, 0x87, 0xb2, 0x91, 0x37, 0x15, 0x94, 0xf0, - 0x5e, 0x93, 0x4d, 0xbb, 0xd7, 0x18, 0x3f, 0x9a, 0x85, 0x19, 0x1e, 0xb7, 0xed, 0xe9, 0x7f, 0x84, - 0x7d, 0x4f, 0xbb, 0xad, 0x3e, 0x17, 0xe5, 0x08, 0x50, 0x5b, 0xd7, 0xe7, 0x19, 0xf6, 0x2b, 0x70, - 0x2e, 0xd1, 0x15, 0x78, 0x63, 0xad, 0xc8, 0x88, 0x79, 0x89, 0x3b, 0xeb, 0x7c, 0x7a, 0x25, 0x0f, - 0x6e, 0x9a, 0x09, 0x0a, 0xe3, 0xcf, 0x86, 0x12, 0xfc, 0xc5, 0x83, 0xac, 0xfa, 0xc4, 0x9a, 0x39, - 0xdb, 0x13, 0x6b, 0x76, 0xb0, 0x27, 0xd6, 0x98, 0x50, 0x91, 0x1b, 0x44, 0xa8, 0xf8, 0x00, 0x26, - 0x37, 0xa9, 0xdd, 0xf2, 0x37, 0x5d, 0x91, 0x70, 0x8a, 0xfb, 0x5a, 0xc8, 0x80, 0x78, 0xac, 0x4c, - 0x5e, 0xb8, 0x42, 0x9b, 0xd1, 0x80, 0x11, 0xb0, 0x63, 0x90, 0x67, 0xa0, 0x32, 0x75, 0x0e, 0xea, - 0x2d, 0x7a, 0xb8, 0xcf, 0x2d, 0xba, 0x06, 0x13, 0x82, 0x2e, 0x0a, 0xf5, 0x1c, 0x5d, 0xf7, 0x58, - 0x11, 0xc2, 0x65, 0xed, 0x61, 0x36, 0xfc, 0xb0, 0x76, 0x7e, 0xd3, 0xd3, 0x98, 0xb0, 0x2e, 0x58, - 0x6e, 0x37, 0x3a, 0xae, 0xd3, 0xc6, 0x2e, 0x18, 0x8d, 0xba, 0x80, 0x0a, 0x30, 0xef, 0x02, 0x05, - 0x89, 0xbc, 0x03, 0x53, 0xa5, 0x8d, 0xaa, 0x4a, 0x96, 0x8f, 0x5e, 0x79, 0xed, 0x8e, 0x63, 0x69, - 0xa4, 0x31, 0xdc, 0x7e, 0x37, 0x9f, 0xb1, 0xbf, 0x98, 0x9b, 0x8f, 0xf1, 0x4f, 0x27, 0xe5, 0xf2, - 0xfe, 0x64, 0x1f, 0x48, 0xf4, 0x27, 0x8f, 0xdc, 0x19, 0x9f, 0x3c, 0x86, 0x4e, 0x13, 0x24, 0x35, - 0xf9, 0x76, 0xf8, 0x4c, 0xf2, 0xed, 0xc8, 0x63, 0x3f, 0x5f, 0x8c, 0x9e, 0x51, 0x62, 0x8d, 0xad, - 0xb5, 0xfc, 0x20, 0x6b, 0x2d, 0x55, 0xca, 0x1d, 0x7b, 0x7c, 0x29, 0x17, 0xce, 0x2c, 0xe5, 0xd6, - 0x22, 0xdf, 0xe5, 0xf1, 0x53, 0x5d, 0x42, 0x9e, 0x17, 0x5a, 0x81, 0x99, 0xf4, 0x28, 0x7c, 0xa1, - 0x17, 0xf3, 0x77, 0x94, 0xe8, 0xfc, 0xb5, 0x74, 0xd1, 0xb9, 0xff, 0x79, 0x73, 0x26, 0xe1, 0xf9, - 0x47, 0x9f, 0xac, 0xf0, 0xfc, 0x64, 0x1f, 0x42, 0xfe, 0x4a, 0x7c, 0xfe, 0x2b, 0xf1, 0x79, 0x30, - 0xf1, 0x99, 0xdc, 0x07, 0x62, 0x77, 0x83, 0x7d, 0xda, 0x0e, 0x9c, 0x3a, 0x46, 0xa5, 0x65, 0x43, - 0x8c, 0xaf, 0x32, 0x62, 0xbd, 0x26, 0x4b, 0xd5, 0xf5, 0xaa, 0x95, 0xa2, 0x9f, 0xb7, 0x87, 0xeb, - 0x75, 0xdb, 0xf6, 0xda, 0xa8, 0xc7, 0xba, 0x0e, 0xa3, 0x32, 0xae, 0x69, 0x26, 0x52, 0x51, 0x27, - 0x03, 0x9a, 0x4a, 0x2c, 0xb2, 0x08, 0x79, 0x49, 0xac, 0x26, 0xda, 0x39, 0x14, 0x30, 0x2d, 0x64, - 0xa4, 0x80, 0x19, 0xff, 0xd1, 0x90, 0x3c, 0x13, 0xd8, 0x27, 0x6c, 0xd8, 0x9e, 0xdd, 0xc2, 0x1c, - 0x7c, 0xe1, 0x92, 0x55, 0x6e, 0x03, 0xb1, 0x55, 0x1e, 0xf3, 0x15, 0xd0, 0x49, 0x3e, 0x56, 0x60, - 0xda, 0x28, 0xcd, 0x71, 0x6e, 0x80, 0x34, 0xc7, 0x6f, 0x69, 0x39, 0x82, 0x87, 0xa2, 0xa4, 0x94, - 0x6c, 0x9f, 0xec, 0x9f, 0x1d, 0xf8, 0x96, 0x9a, 0xcc, 0x77, 0x38, 0x0a, 0x13, 0x86, 0x94, 0x7d, - 0xd2, 0xf8, 0x86, 0xd7, 0x9b, 0x91, 0xb3, 0x84, 0x7c, 0x1e, 0xfd, 0xb7, 0x1a, 0xf2, 0x79, 0x19, - 0x40, 0x9c, 0xdd, 0x91, 0xbd, 0xc3, 0x2b, 0xb8, 0x9d, 0x08, 0xbb, 0xe7, 0x20, 0x68, 0xf6, 0xc8, - 0x09, 0xa2, 0x10, 0x1a, 0xbf, 0x4f, 0x60, 0xa6, 0x56, 0xbb, 0x5f, 0x71, 0xec, 0xbd, 0xb6, 0xeb, - 0x07, 0x4e, 0xbd, 0xda, 0xde, 0x75, 0x99, 0x6c, 0x1f, 0x9e, 0x2f, 0x4a, 0xb0, 0xde, 0xe8, 0x6c, - 0x09, 0x8b, 0xd9, 0xdd, 0x71, 0xd9, 0xf3, 0xa4, 0xc2, 0x95, 0xdf, 0x1d, 0x29, 0x03, 0x98, 0x1c, - 0xce, 0xc4, 0xe7, 0x5a, 0x17, 0x43, 0x65, 0x08, 0x23, 0x14, 0x14, 0x9f, 0x7d, 0x0e, 0x32, 0x65, - 0x19, 0xa1, 0xc9, 0x09, 0x2b, 0xae, 0x53, 0x17, 0xb4, 0xc0, 0xd1, 0x51, 0x31, 0x5f, 0x8d, 0x42, - 0xba, 0xc1, 0x7d, 0xb8, 0x83, 0x70, 0xd5, 0xc4, 0x2e, 0xb1, 0x06, 0x8e, 0xe0, 0x9c, 0xe6, 0x44, - 0x3d, 0xe8, 0xeb, 0xcc, 0x6b, 0x42, 0x5c, 0x37, 0x30, 0x66, 0x47, 0xca, 0x13, 0x8d, 0x9a, 0x54, - 0x2f, 0xb5, 0x06, 0x76, 0x40, 0x3e, 0x9f, 0x5a, 0x12, 0xae, 0xee, 0x71, 0x2d, 0x78, 0xb7, 0xb2, - 0x69, 0xf0, 0xf4, 0x81, 0xbd, 0xaa, 0xb6, 0x52, 0xb6, 0x82, 0xfe, 0x35, 0x91, 0x7f, 0x94, 0x81, - 0x0b, 0x1a, 0x46, 0xb8, 0xff, 0xf9, 0x61, 0x7c, 0x91, 0xd4, 0x79, 0xfd, 0xd1, 0x93, 0x99, 0xd7, - 0x2f, 0xe9, 0x6d, 0x89, 0x76, 0x68, 0xb5, 0x0d, 0xbd, 0xbe, 0x90, 0x3c, 0x84, 0x19, 0x2c, 0x92, - 0x2f, 0x45, 0x6c, 0xce, 0x8a, 0x07, 0xa6, 0xb9, 0xe8, 0xb3, 0x79, 0x60, 0x00, 0x4c, 0x01, 0xbf, - 0xf8, 0xad, 0xe3, 0xe2, 0xa4, 0x86, 0x2e, 0xc3, 0x61, 0x5b, 0xd1, 0x73, 0x93, 0xd3, 0xde, 0x75, - 0xb5, 0xfc, 0xfe, 0xf1, 0x2a, 0xc8, 0x7f, 0x9d, 0xe1, 0xef, 0x13, 0xbc, 0x19, 0xb7, 0x3d, 0xb7, - 0x15, 0x96, 0x4b, 0x5b, 0xcd, 0x1e, 0xdd, 0xd6, 0x7c, 0x32, 0xdd, 0xf6, 0x0a, 0x7e, 0x32, 0xdf, - 0x13, 0xac, 0x5d, 0xcf, 0x6d, 0x45, 0x9f, 0xaf, 0x76, 0x5c, 0xcf, 0x8f, 0x24, 0xdf, 0x9f, 0x81, - 0x8b, 0x9a, 0x9a, 0x54, 0xcd, 0x4d, 0x22, 0xc2, 0x2f, 0xcc, 0x86, 0x81, 0x59, 0xa2, 0xa2, 0xa5, - 0x6b, 0x62, 0xfe, 0x5f, 0xc6, 0x2f, 0x50, 0xe2, 0x80, 0x32, 0x24, 0xab, 0xc5, 0xb1, 0x94, 0x4f, - 0xe8, 0x5d, 0x0b, 0x71, 0x60, 0x06, 0xcd, 0x76, 0x34, 0x9b, 0xe2, 0xb9, 0xde, 0x36, 0xc5, 0x61, - 0xd6, 0x21, 0xcc, 0x48, 0xd0, 0xdb, 0xb0, 0x38, 0xc9, 0x95, 0x7c, 0x0f, 0x5c, 0x4c, 0x00, 0xc3, - 0xd5, 0x76, 0xae, 0xe7, 0x6a, 0xfb, 0xd4, 0xc9, 0x71, 0xf1, 0xd5, 0xb4, 0xda, 0xd2, 0x56, 0x5a, - 0xef, 0x1a, 0x88, 0x0d, 0x10, 0x15, 0x0a, 0x79, 0x26, 0x7d, 0x82, 0x7e, 0x4a, 0xcc, 0x0f, 0x05, - 0x9f, 0xed, 0xe5, 0xca, 0x37, 0xa8, 0x47, 0x5e, 0x84, 0x44, 0x28, 0x4c, 0x28, 0xd9, 0x18, 0x8e, - 0x84, 0xf5, 0x48, 0x8f, 0x4a, 0xbe, 0x75, 0x5c, 0xd4, 0xb0, 0xd9, 0x0d, 0x4b, 0x4d, 0xf3, 0xa0, - 0x89, 0x8f, 0x2a, 0x22, 0xf9, 0x8d, 0x0c, 0xcc, 0x31, 0x40, 0x34, 0xa9, 0x44, 0xa3, 0xe6, 0xfb, - 0xcd, 0xfa, 0xfd, 0x27, 0x33, 0xeb, 0x5f, 0xc4, 0x6f, 0x54, 0x67, 0x7d, 0xa2, 0x4b, 0x52, 0x3f, - 0x0e, 0x67, 0xbb, 0x66, 0x21, 0xa6, 0xcd, 0xf6, 0x8b, 0x03, 0xcc, 0x76, 0x3e, 0x00, 0xa7, 0xcf, - 0xf6, 0x9e, 0xb5, 0x90, 0x4d, 0x98, 0x10, 0x97, 0x2b, 0xde, 0x61, 0x2f, 0x68, 0x71, 0xa1, 0xd5, - 0x22, 0x7e, 0xe3, 0x15, 0xc9, 0x2a, 0x12, 0x2d, 0xd4, 0xb8, 0x90, 0x36, 0xcc, 0xf2, 0xdf, 0xba, - 0xb2, 0xab, 0xd8, 0x53, 0xd9, 0x75, 0x45, 0xb4, 0xe8, 0x92, 0xe0, 0x1f, 0xd3, 0x79, 0xa9, 0xf1, - 0x9c, 0x52, 0x18, 0x93, 0x0e, 0x10, 0x0d, 0xcc, 0x17, 0xed, 0xa5, 0xfe, 0x2a, 0xae, 0x57, 0x45, - 0x9d, 0xc5, 0x78, 0x9d, 0xf1, 0x95, 0x9b, 0xc2, 0x9b, 0xd8, 0x30, 0x2d, 0xa0, 0xee, 0x01, 0xe5, - 0x3b, 0xfc, 0x8b, 0x5a, 0x44, 0xad, 0x58, 0x29, 0xbf, 0x95, 0xc9, 0x9a, 0x30, 0xe2, 0x59, 0x6c, - 0x43, 0x8f, 0xf3, 0x23, 0xf7, 0x61, 0xa6, 0xd4, 0xe9, 0x34, 0x1d, 0xda, 0xc0, 0x56, 0x9a, 0x5d, - 0xd6, 0x26, 0x23, 0xca, 0xf7, 0x66, 0xf3, 0x42, 0x71, 0x55, 0xf4, 0xba, 0xb1, 0xed, 0x26, 0x41, - 0x6b, 0xfc, 0x48, 0x26, 0xf1, 0xd1, 0xe4, 0x75, 0x18, 0xc3, 0x1f, 0x4a, 0x90, 0x16, 0xd4, 0x19, - 0xf1, 0x4f, 0x44, 0x6d, 0x54, 0x84, 0xc0, 0x84, 0x25, 0x35, 0x50, 0x63, 0x8e, 0x0b, 0x4b, 0x42, - 0x51, 0x11, 0xa9, 0x26, 0x8a, 0xd2, 0xd7, 0x23, 0x17, 0x09, 0x5d, 0xe8, 0xeb, 0x21, 0x3c, 0x3c, - 0x8c, 0x7f, 0x90, 0xd5, 0xa7, 0x1d, 0xb9, 0xa2, 0xc8, 0xed, 0x4a, 0xa8, 0x48, 0x29, 0xb7, 0x2b, - 0xd2, 0xfa, 0xdf, 0xcd, 0xc0, 0xec, 0x7d, 0x25, 0x51, 0xe8, 0xa6, 0x8b, 0xe3, 0xd2, 0x3f, 0x75, - 0xe6, 0x93, 0x4a, 0x01, 0xa8, 0x66, 0x28, 0x65, 0x33, 0x05, 0xa7, 0x8c, 0x99, 0xf6, 0x3d, 0xe8, - 0x3d, 0x87, 0x1f, 0xa6, 0x64, 0x62, 0xe4, 0xe8, 0x1c, 0x7e, 0xc6, 0xd4, 0x15, 0xc6, 0x4f, 0x64, - 0x61, 0x5c, 0x59, 0x31, 0xe4, 0x33, 0x30, 0xa1, 0x56, 0xab, 0x2a, 0x1c, 0xd5, 0xaf, 0x34, 0x35, - 0x2c, 0xd4, 0x38, 0x52, 0xbb, 0xa5, 0x69, 0x1c, 0xd9, 0xba, 0x40, 0xe8, 0x19, 0x6f, 0x42, 0xef, - 0xa7, 0xdc, 0x84, 0x70, 0x96, 0x2b, 0x1a, 0xa3, 0xbe, 0xf7, 0xa1, 0x77, 0x92, 0xf7, 0x21, 0x54, - 0x5e, 0x29, 0xf4, 0xbd, 0x6f, 0x45, 0xc6, 0x4f, 0x67, 0xa0, 0x10, 0x5f, 0xd3, 0x9f, 0x48, 0xaf, - 0x9c, 0xe1, 0x75, 0xe9, 0xc7, 0xb3, 0x61, 0xe6, 0x16, 0xe9, 0x42, 0xfc, 0xb4, 0x9a, 0x29, 0xbe, - 0xab, 0x3d, 0xfc, 0x3c, 0xab, 0x47, 0xc3, 0x53, 0x83, 0x6f, 0xa4, 0x87, 0xc0, 0x1c, 0xfa, 0xe6, - 0x2f, 0x15, 0x9f, 0x31, 0x3e, 0x84, 0xb9, 0x78, 0x77, 0xe0, 0xe3, 0x4f, 0x09, 0xa6, 0x75, 0x78, - 0x3c, 0xef, 0x53, 0x9c, 0xca, 0x8c, 0xe3, 0x1b, 0x7f, 0x98, 0x8d, 0xf3, 0x16, 0x26, 0x8b, 0x6c, - 0x8f, 0x52, 0x0d, 0x71, 0xc4, 0x1e, 0xc5, 0x41, 0xa6, 0x2c, 0x3b, 0x4b, 0xbe, 0xb5, 0xd0, 0x11, - 0x36, 0x97, 0xee, 0x08, 0x4b, 0x6e, 0xc5, 0xac, 0xb4, 0x95, 0xa8, 0x4d, 0x87, 0x74, 0xc7, 0x8a, - 0x2c, 0xb5, 0x63, 0xc6, 0xd9, 0x65, 0x98, 0xd3, 0x42, 0x90, 0x4b, 0xfa, 0xe1, 0x48, 0xd7, 0x1f, - 0x60, 0x01, 0x27, 0x4e, 0x45, 0x26, 0x2b, 0x30, 0xca, 0x3e, 0x73, 0xcd, 0xee, 0x88, 0x37, 0x1d, - 0x12, 0xba, 0xc5, 0x37, 0xc3, 0xfb, 0xa1, 0xe2, 0x19, 0xdf, 0xa4, 0x4c, 0x42, 0x50, 0x27, 0x96, - 0x40, 0x34, 0xfe, 0x65, 0x86, 0xad, 0xff, 0xfa, 0xc1, 0x77, 0x58, 0xd2, 0x36, 0xd6, 0xa4, 0x3e, - 0x16, 0xb5, 0x7f, 0x9c, 0xe5, 0xb9, 0x78, 0xc4, 0xf4, 0x79, 0x0b, 0x46, 0x36, 0x6d, 0x6f, 0x4f, - 0xa4, 0xcc, 0xd6, 0xb9, 0xf0, 0x82, 0x28, 0xa6, 0x54, 0x80, 0xbf, 0x4d, 0x41, 0xa0, 0xaa, 0xce, - 0xb2, 0x03, 0xa9, 0xce, 0x94, 0x77, 0x81, 0xdc, 0x13, 0x7b, 0x17, 0xf8, 0xae, 0x30, 0xed, 0x4e, - 0x29, 0x18, 0x20, 0xc2, 0xf5, 0xa5, 0x78, 0x96, 0xab, 0x44, 0x2c, 0xf2, 0x88, 0x1d, 0xb9, 0xa5, - 0xe6, 0xcd, 0x52, 0x7c, 0x4b, 0x4f, 0xc9, 0x90, 0x65, 0xfc, 0x71, 0x8e, 0xf7, 0xb1, 0xe8, 0xa8, - 0xcb, 0x9a, 0xdf, 0x39, 0xae, 0x93, 0x98, 0x9e, 0x92, 0x7b, 0xa0, 0x5f, 0x86, 0x21, 0x36, 0x37, - 0x45, 0x6f, 0x22, 0x1e, 0x9b, 0xbf, 0x2a, 0x1e, 0x2b, 0x67, 0x6b, 0x19, 0xcf, 0x24, 0x35, 0x21, - 0x22, 0x1e, 0x5b, 0xea, 0x5a, 0x46, 0x0c, 0x72, 0x05, 0x86, 0xd6, 0xdd, 0x86, 0x8c, 0xa4, 0x3e, - 0x87, 0xd1, 0x47, 0xb4, 0x8c, 0xab, 0xf3, 0x19, 0x13, 0x31, 0x58, 0x5b, 0xc3, 0xfc, 0x13, 0x6a, - 0x5b, 0x5b, 0xbb, 0x76, 0x32, 0xd1, 0x9d, 0x92, 0xf4, 0x66, 0x19, 0xa6, 0xb6, 0x9d, 0x76, 0xc3, - 0x3d, 0xf4, 0x2b, 0xd4, 0x3f, 0x08, 0xdc, 0x8e, 0xb0, 0x37, 0x46, 0xed, 0xfe, 0x21, 0x2f, 0xb1, - 0x1a, 0xbc, 0x48, 0x7d, 0x96, 0xd1, 0x89, 0xc8, 0x12, 0x4c, 0x6a, 0x11, 0x5c, 0xc5, 0xe3, 0x2a, - 0x6a, 0x43, 0xf5, 0xf8, 0xaf, 0xaa, 0x36, 0x54, 0x23, 0x61, 0xe7, 0xb9, 0xf8, 0x7e, 0xe5, 0x89, - 0x35, 0xf1, 0xed, 0x02, 0x87, 0xdc, 0x84, 0x3c, 0x0f, 0xf3, 0x51, 0xad, 0xa8, 0xcf, 0x64, 0x3e, - 0xc2, 0x62, 0x61, 0x72, 0x24, 0xa2, 0x12, 0xd6, 0xe1, 0xd3, 0x50, 0x10, 0x5b, 0x52, 0x94, 0xab, - 0xfd, 0x39, 0x18, 0x2a, 0x57, 0x2b, 0xa6, 0xba, 0x8d, 0xd4, 0x9d, 0x86, 0x67, 0x22, 0x14, 0xbd, - 0xfa, 0xd6, 0x69, 0x70, 0xe8, 0x7a, 0x07, 0x26, 0xf5, 0x03, 0xcf, 0xe1, 0xf9, 0x34, 0x71, 0x21, - 0x7e, 0x86, 0xbc, 0x03, 0xc3, 0x68, 0xf8, 0x1a, 0x3b, 0x19, 0xe2, 0x75, 0x2c, 0x4d, 0x8a, 0x09, - 0x3c, 0x8c, 0x56, 0xb4, 0x26, 0x27, 0x22, 0x6f, 0xc1, 0x50, 0x85, 0xb6, 0x8f, 0x62, 0xa9, 0xfe, - 0x12, 0xc4, 0xe1, 0x86, 0xd0, 0xa0, 0xed, 0x23, 0x13, 0x49, 0x8c, 0x9f, 0xce, 0xc2, 0xb9, 0x94, - 0xcf, 0x7a, 0xf0, 0x99, 0xa7, 0x74, 0x57, 0x5c, 0xd2, 0x76, 0x45, 0xf9, 0x3e, 0xde, 0xb3, 0xe3, - 0x53, 0x37, 0xc9, 0x5f, 0xc8, 0xc0, 0x05, 0x7d, 0x82, 0x0a, 0x4b, 0xf7, 0x07, 0x37, 0xc9, 0xdb, - 0x30, 0xb2, 0x42, 0xed, 0x06, 0x95, 0x79, 0xbd, 0xce, 0x85, 0x01, 0xf9, 0x78, 0x0c, 0x03, 0x5e, - 0xc8, 0xd9, 0x46, 0x1e, 0xaf, 0x1c, 0x4a, 0x2a, 0xe2, 0xe3, 0xb8, 0xf8, 0x6e, 0xc8, 0x78, 0x22, - 0x69, 0x55, 0xf5, 0xb1, 0x32, 0xf9, 0x56, 0x06, 0x9e, 0xed, 0x43, 0xc3, 0x06, 0x8e, 0x0d, 0xbd, - 0x3a, 0x70, 0x78, 0xa2, 0x22, 0x94, 0xbc, 0x07, 0xd3, 0x9b, 0x42, 0xfc, 0x97, 0xc3, 0x91, 0x8d, - 0xd6, 0x8b, 0xbc, 0x19, 0x58, 0x72, 0x5c, 0xe2, 0xc8, 0x5a, 0xa0, 0x9b, 0x5c, 0xdf, 0x40, 0x37, - 0x6a, 0xdc, 0x98, 0xa1, 0x41, 0xe3, 0xc6, 0x7c, 0x08, 0x73, 0x7a, 0xdb, 0x44, 0xf8, 0xde, 0x28, - 0x6a, 0x4e, 0xa6, 0x77, 0xd4, 0x9c, 0xbe, 0x41, 0x42, 0x8d, 0x9f, 0xc8, 0x40, 0x41, 0xe7, 0xfd, - 0xb8, 0xe3, 0xf9, 0xae, 0x36, 0x9e, 0xcf, 0xa6, 0x8f, 0x67, 0xef, 0x81, 0xfc, 0xbf, 0x33, 0xf1, - 0xc6, 0x0e, 0x34, 0x82, 0x06, 0x8c, 0x54, 0xdc, 0x96, 0xed, 0xb4, 0xd5, 0xd4, 0xff, 0x0d, 0x84, - 0x98, 0xa2, 0x64, 0xb0, 0x20, 0x43, 0x97, 0x60, 0x78, 0xdd, 0x6d, 0x97, 0x2a, 0xc2, 0xa4, 0x18, - 0xf9, 0xb4, 0xdd, 0xb6, 0x65, 0x37, 0x4c, 0x5e, 0x40, 0x56, 0x01, 0x6a, 0x75, 0x8f, 0xd2, 0x76, - 0xcd, 0xf9, 0x6e, 0x1a, 0x93, 0x34, 0x58, 0x0f, 0x35, 0xbb, 0xb8, 0xb1, 0xf0, 0xa7, 0x53, 0x44, - 0xb4, 0x7c, 0xe7, 0xbb, 0xd5, 0xfd, 0x56, 0xa1, 0xc7, 0x75, 0x25, 0xe2, 0xb0, 0xc5, 0xc6, 0xe1, - 0xc6, 0x27, 0xb1, 0xae, 0x52, 0xab, 0xc2, 0x1e, 0xbe, 0x91, 0x3a, 0x1c, 0x7f, 0x90, 0x81, 0x67, - 0xfb, 0xd0, 0x3c, 0x81, 0x51, 0xf9, 0x8b, 0xee, 0x70, 0x0a, 0x10, 0x11, 0x61, 0x26, 0x65, 0xa7, - 0x11, 0xf0, 0x5c, 0x7d, 0x93, 0x22, 0x93, 0x32, 0x03, 0x68, 0x99, 0x94, 0x19, 0x80, 0x9d, 0xa5, - 0x2b, 0xd4, 0xd9, 0xdb, 0xe7, 0xe6, 0x61, 0x93, 0x7c, 0x6f, 0xd8, 0x47, 0x88, 0x7a, 0x96, 0x72, - 0x1c, 0xe3, 0x5f, 0x0d, 0xc3, 0x45, 0x93, 0xee, 0x39, 0xec, 0x5e, 0xb2, 0xe5, 0x3b, 0xed, 0x3d, - 0x2d, 0xee, 0x8e, 0x11, 0x5b, 0xb9, 0x22, 0x49, 0x05, 0x83, 0x84, 0x33, 0xf1, 0x2a, 0xe4, 0x99, - 0x18, 0xa2, 0x2c, 0x5e, 0x7c, 0xe3, 0x62, 0xc2, 0x8a, 0x08, 0xec, 0x2c, 0x8b, 0xc9, 0x6b, 0x42, - 0x4c, 0x52, 0xd2, 0x08, 0x31, 0x31, 0xe9, 0xdb, 0xc7, 0x45, 0xa8, 0x1d, 0xf9, 0x01, 0xc5, 0x2b, - 0xb2, 0x10, 0x95, 0xc2, 0xbb, 0xcc, 0x50, 0x8f, 0xbb, 0xcc, 0x1a, 0xcc, 0x95, 0x1a, 0xfc, 0x74, - 0xb4, 0x9b, 0x1b, 0x9e, 0xd3, 0xae, 0x3b, 0x1d, 0xbb, 0x29, 0xef, 0xe7, 0xd8, 0xcb, 0x76, 0x58, - 0x6e, 0x75, 0x42, 0x04, 0x33, 0x95, 0x8c, 0x35, 0xa3, 0xb2, 0x5e, 0xc3, 0xf0, 0x34, 0xe2, 0xf9, - 0x12, 0x9b, 0xd1, 0x68, 0xfb, 0xd8, 0x0a, 0xdf, 0x0c, 0x8b, 0xf1, 0x16, 0x85, 0x06, 0x01, 0x9b, - 0xab, 0xb5, 0xc8, 0xa5, 0x9a, 0x67, 0x39, 0xe0, 0x86, 0x05, 0x41, 0xd3, 0x47, 0x53, 0x4c, 0x0d, - 0x2f, 0xa2, 0xab, 0xd5, 0x56, 0x18, 0x5d, 0x3e, 0x41, 0xe7, 0xfb, 0xfb, 0x2a, 0x1d, 0xc7, 0x23, - 0xd7, 0xd9, 0x54, 0x68, 0xb9, 0x01, 0xc5, 0x29, 0x3c, 0x16, 0xdd, 0xb9, 0x3c, 0x84, 0xf2, 0x3b, - 0x97, 0x82, 0x42, 0xde, 0x81, 0xd9, 0xe5, 0xf2, 0xa2, 0x54, 0x3a, 0x57, 0xdc, 0x7a, 0x17, 0x0d, - 0x01, 0x00, 0xeb, 0xc3, 0x31, 0xa4, 0xf5, 0x45, 0xb6, 0x9b, 0xa4, 0xa1, 0x91, 0xcb, 0x30, 0x5a, - 0xad, 0xf0, 0xbe, 0x1f, 0x57, 0x53, 0x79, 0x09, 0xcb, 0x2c, 0x59, 0x48, 0xee, 0x47, 0x97, 0x82, - 0x89, 0x53, 0xa5, 0xf7, 0x8b, 0x03, 0x5c, 0x08, 0xde, 0x82, 0xc9, 0x25, 0x37, 0xa8, 0xb6, 0xfd, - 0xc0, 0x6e, 0xd7, 0x69, 0xb5, 0xa2, 0xc6, 0xd5, 0xde, 0x71, 0x03, 0xcb, 0x11, 0x25, 0xec, 0xcb, - 0x75, 0x4c, 0xf2, 0x39, 0x24, 0xbd, 0x43, 0xdb, 0xd4, 0x8b, 0xe2, 0x69, 0x0f, 0xf3, 0xbe, 0x65, - 0xa4, 0x7b, 0x61, 0x89, 0xa9, 0x23, 0x8a, 0x34, 0x63, 0x3c, 0x39, 0x68, 0xd9, 0x6d, 0x50, 0x9f, - 0xef, 0x16, 0xdf, 0x41, 0x69, 0xc6, 0x94, 0xb6, 0xf5, 0xd9, 0x41, 0xff, 0x7d, 0x4c, 0x33, 0x96, - 0xc0, 0x25, 0x9f, 0x83, 0x61, 0xfc, 0x29, 0xa4, 0xdb, 0xd9, 0x14, 0xb6, 0x91, 0x64, 0x5b, 0x67, - 0x98, 0x26, 0x27, 0x20, 0x55, 0x18, 0x15, 0x17, 0xab, 0xb3, 0x24, 0xcb, 0x11, 0x37, 0x34, 0x3e, - 0x33, 0x04, 0xbd, 0xd1, 0x80, 0x09, 0xb5, 0x42, 0xb6, 0x22, 0x56, 0x6c, 0x7f, 0x9f, 0x36, 0xd8, - 0x2f, 0x91, 0xe7, 0x0e, 0x57, 0xc4, 0x3e, 0x42, 0x2d, 0xf6, 0x1d, 0xa6, 0x82, 0xc2, 0xce, 0xd4, - 0xaa, 0xbf, 0xe5, 0x8b, 0x4f, 0x11, 0xaa, 0x16, 0x07, 0xd5, 0x76, 0x0d, 0x53, 0x14, 0x19, 0xdf, - 0x05, 0x73, 0xeb, 0xdd, 0x66, 0xd3, 0xde, 0x69, 0x52, 0x99, 0x07, 0x05, 0x13, 0x8e, 0x2f, 0xc1, - 0x70, 0x4d, 0x49, 0x61, 0x1e, 0xe6, 0xa2, 0x54, 0x70, 0xd0, 0x08, 0x36, 0x83, 0xa1, 0x82, 0x62, - 0xc9, 0xcb, 0x39, 0xa9, 0xf1, 0x7b, 0x19, 0x98, 0x93, 0xe6, 0x02, 0x9e, 0x5d, 0x3f, 0x08, 0xf3, - 0xd8, 0x5f, 0xd6, 0xe6, 0x1a, 0x4e, 0xd8, 0xd8, 0x34, 0xe2, 0xb3, 0xee, 0xae, 0xfc, 0x08, 0x5d, - 0x60, 0x49, 0xfb, 0xe0, 0xd3, 0x3e, 0x86, 0xbc, 0x03, 0xe3, 0xe2, 0x78, 0x54, 0x02, 0x5c, 0x62, - 0x14, 0x31, 0x71, 0xdd, 0x8b, 0x1b, 0xaf, 0xa8, 0xe8, 0x28, 0x8b, 0xe9, 0x4d, 0x79, 0x5c, 0x19, - 0x20, 0x5d, 0x16, 0xd3, 0xeb, 0xe8, 0x33, 0x75, 0x7f, 0x7b, 0x3c, 0xde, 0xb7, 0x62, 0xee, 0xde, - 0x52, 0x43, 0xda, 0x65, 0xa2, 0x9b, 0x71, 0x14, 0xd2, 0x4e, 0xbd, 0x19, 0x87, 0xa8, 0xe1, 0x98, - 0x64, 0x4f, 0x19, 0x93, 0xf7, 0xe4, 0x98, 0xe4, 0x7a, 0x4f, 0x8c, 0xd9, 0x3e, 0xe3, 0x50, 0x8b, - 0x56, 0xc8, 0xd0, 0x40, 0x6a, 0x95, 0x67, 0x30, 0x76, 0x3f, 0x27, 0x89, 0xef, 0xa2, 0x82, 0x93, - 0xaa, 0xab, 0x19, 0x1e, 0x9c, 0xe9, 0x29, 0x5b, 0xf3, 0xe7, 0x61, 0xa2, 0x14, 0x04, 0x76, 0x7d, - 0x9f, 0x36, 0x2a, 0x6c, 0x7b, 0x52, 0xa2, 0x6f, 0xd9, 0x02, 0xae, 0xbe, 0xb1, 0xa9, 0xb8, 0x3c, - 0x9a, 0xac, 0xed, 0x0b, 0x63, 0xda, 0x30, 0x9a, 0x2c, 0x83, 0xe8, 0xd1, 0x64, 0x19, 0x84, 0x5c, - 0x87, 0xd1, 0x6a, 0xfb, 0xa1, 0xc3, 0xfa, 0x84, 0x07, 0xe0, 0x42, 0xdd, 0x94, 0xc3, 0x41, 0xea, - 0xe6, 0x2a, 0xb0, 0xc8, 0x5b, 0xca, 0xa5, 0x66, 0x2c, 0x52, 0x60, 0x70, 0x95, 0x57, 0x18, 0x61, - 0x47, 0xbd, 0xb0, 0x84, 0xb7, 0x9c, 0x5b, 0x30, 0x2a, 0x35, 0x99, 0x10, 0x29, 0x2d, 0x04, 0x65, - 0x32, 0x60, 0x85, 0x44, 0xc6, 0x9c, 0xe4, 0x4a, 0xbe, 0xbe, 0x71, 0x25, 0x27, 0xb9, 0x92, 0xaf, - 0x4f, 0xcb, 0x49, 0xae, 0x64, 0xee, 0x0b, 0x95, 0x40, 0x13, 0xa7, 0x2a, 0x81, 0x1e, 0xc0, 0xc4, - 0x86, 0xed, 0x05, 0x0e, 0x93, 0x51, 0xda, 0x81, 0x3f, 0x3f, 0xa9, 0xe9, 0x4d, 0x95, 0xa2, 0xa5, - 0x17, 0x64, 0x5e, 0xec, 0x8e, 0x82, 0xaf, 0x27, 0x70, 0x8e, 0xe0, 0xe9, 0xa6, 0xb4, 0x53, 0x8f, - 0x63, 0x4a, 0x8b, 0x9d, 0x8a, 0xba, 0xb2, 0xe9, 0x48, 0x23, 0x83, 0x97, 0x96, 0x98, 0xc2, 0x2c, - 0x44, 0x24, 0x5f, 0x86, 0x09, 0xf6, 0xf7, 0x86, 0xdb, 0x74, 0xea, 0x0e, 0xf5, 0xe7, 0x0b, 0xd8, - 0xb8, 0x17, 0x52, 0x57, 0x3f, 0x22, 0x1d, 0xd5, 0x68, 0xc0, 0x17, 0x30, 0x32, 0x8e, 0x2b, 0xc1, - 0x35, 0x6e, 0xe4, 0x7d, 0x98, 0x60, 0xb3, 0x6f, 0xc7, 0xf6, 0xb9, 0x68, 0x3a, 0x13, 0x19, 0x43, - 0x37, 0x04, 0x3c, 0x11, 0xd0, 0x59, 0x25, 0x60, 0xc7, 0x7c, 0xa9, 0xc3, 0x37, 0x48, 0xa2, 0xcc, - 0xf6, 0x4e, 0x62, 0x73, 0x94, 0x68, 0xe4, 0x0b, 0x30, 0x51, 0xea, 0x74, 0xa2, 0x1d, 0x67, 0x56, - 0x51, 0x84, 0x75, 0x3a, 0x56, 0xea, 0xae, 0xa3, 0x51, 0xc4, 0x37, 0xe6, 0xb9, 0x33, 0x6d, 0xcc, - 0xe4, 0x8d, 0x50, 0x5a, 0x3f, 0x17, 0x69, 0x75, 0xc5, 0xc5, 0x51, 0x13, 0xfd, 0xb9, 0xe0, 0x5e, - 0x86, 0x49, 0xae, 0xe6, 0x94, 0xd2, 0xcc, 0xf9, 0xc4, 0xea, 0x49, 0x11, 0x6a, 0x74, 0x1a, 0xb2, - 0x0c, 0x53, 0xdc, 0xdb, 0xbb, 0x29, 0x22, 0x6d, 0xcf, 0x5f, 0xc0, 0x55, 0x8b, 0x5c, 0xb8, 0x93, - 0x78, 0x13, 0x13, 0xb0, 0xd8, 0x1a, 0x97, 0x18, 0x91, 0xf1, 0x27, 0x19, 0xb8, 0xd0, 0x63, 0xc4, - 0xc3, 0x38, 0xcc, 0x99, 0xfe, 0x71, 0x98, 0xd9, 0xce, 0xa1, 0x6b, 0x45, 0xb0, 0xfd, 0x42, 0xca, - 0x52, 0xc7, 0x4b, 0xca, 0x5b, 0x2e, 0x10, 0x91, 0xe3, 0x48, 0x54, 0x7d, 0xd7, 0x45, 0xd5, 0x6c, - 0x2e, 0x79, 0x08, 0x09, 0x3c, 0xfe, 0x51, 0x4b, 0xc6, 0xc9, 0x71, 0xf1, 0x05, 0x91, 0x42, 0x29, - 0x1c, 0xd6, 0x8f, 0x5c, 0x6d, 0x05, 0xa7, 0xb0, 0x36, 0x8e, 0x33, 0x30, 0xae, 0xac, 0x43, 0x72, - 0x49, 0xf1, 0x42, 0x2e, 0xf0, 0x24, 0x5c, 0x0a, 0x87, 0x2c, 0x3f, 0x89, 0x70, 0x51, 0x65, 0x4f, - 0x57, 0x40, 0xaf, 0x31, 0x51, 0x48, 0x89, 0x55, 0xdd, 0xd2, 0xb4, 0xc5, 0x26, 0x96, 0x63, 0x3a, - 0x7f, 0xdb, 0x0f, 0x4a, 0xf5, 0xc0, 0x79, 0x48, 0x07, 0x38, 0x74, 0xa2, 0x74, 0xfe, 0xb6, 0x1f, - 0x58, 0x36, 0x92, 0x25, 0xd2, 0xf9, 0x87, 0x0c, 0x8d, 0x1f, 0xc8, 0x00, 0x6c, 0x55, 0xcb, 0x18, - 0x6c, 0xfe, 0x71, 0x85, 0x82, 0xf4, 0x00, 0xbe, 0x92, 0x7b, 0x1f, 0x71, 0xe0, 0x7f, 0xca, 0xc0, - 0x94, 0x8e, 0x46, 0xde, 0x83, 0xe9, 0x5a, 0xdd, 0x73, 0x9b, 0xcd, 0x1d, 0xbb, 0x7e, 0xb0, 0xea, - 0xb4, 0x29, 0x0f, 0x9d, 0x3a, 0xcc, 0xcf, 0x22, 0x3f, 0x2c, 0xb2, 0x9a, 0xac, 0xcc, 0x8c, 0x23, - 0x93, 0x1f, 0xcc, 0xc0, 0x64, 0x6d, 0xdf, 0x3d, 0x0c, 0xa3, 0x9d, 0x8a, 0x01, 0xf9, 0x0a, 0x5b, - 0xdb, 0xfe, 0xbe, 0x7b, 0x18, 0x65, 0xf0, 0xd4, 0x6c, 0x45, 0xdf, 0x1d, 0xec, 0x19, 0xbf, 0xee, - 0xe2, 0x4d, 0x26, 0xf0, 0xaf, 0x69, 0x95, 0x98, 0x7a, 0x9d, 0xc6, 0x9f, 0x67, 0x60, 0x1c, 0xef, - 0x3c, 0xcd, 0x26, 0xca, 0x5c, 0xdf, 0x49, 0xe9, 0x20, 0xc3, 0x76, 0xf5, 0x19, 0xd8, 0x37, 0x61, - 0x3a, 0x86, 0x46, 0x0c, 0x18, 0xa9, 0x61, 0x80, 0x01, 0x55, 0x41, 0xc1, 0x43, 0x0e, 0x98, 0xa2, - 0xc4, 0x58, 0x56, 0xc8, 0x1e, 0xdc, 0xc0, 0x67, 0xdd, 0x45, 0x00, 0x47, 0x82, 0xe4, 0xcd, 0x86, - 0xc4, 0xbf, 0xe4, 0xc1, 0x0d, 0x53, 0xc1, 0x32, 0xd6, 0x61, 0xa4, 0xe6, 0x7a, 0xc1, 0xd2, 0x11, - 0xbf, 0x4c, 0x54, 0xa8, 0x5f, 0x57, 0xdf, 0x6d, 0x1d, 0x7c, 0x2b, 0xa9, 0x9b, 0xa2, 0x88, 0x14, - 0x61, 0xf8, 0xb6, 0x43, 0x9b, 0x0d, 0xd5, 0x9e, 0x77, 0x97, 0x01, 0x4c, 0x0e, 0x67, 0x17, 0xae, - 0xf3, 0x51, 0x4e, 0x96, 0xc8, 0x70, 0xf8, 0x71, 0xd7, 0x4d, 0x59, 0xeb, 0xdf, 0x17, 0xc3, 0x3c, - 0x08, 0xc9, 0x9a, 0xfa, 0x74, 0xf5, 0x3f, 0xc8, 0xc0, 0x42, 0x6f, 0x12, 0xd5, 0x16, 0x39, 0xd3, - 0xc7, 0x16, 0xf9, 0x95, 0xf8, 0x3b, 0x23, 0xa2, 0x89, 0x77, 0xc6, 0xe8, 0x75, 0xb1, 0x82, 0xa6, - 0xe0, 0x75, 0x2a, 0x13, 0xb1, 0x5c, 0xea, 0xf3, 0xcd, 0x88, 0xc8, 0x87, 0x39, 0x40, 0x1a, 0x53, - 0xd0, 0x1a, 0xbf, 0x35, 0x04, 0x17, 0x7b, 0x52, 0x90, 0x15, 0x25, 0xbd, 0xd3, 0x54, 0x98, 0x58, - 0xa6, 0x27, 0xfe, 0x35, 0xfc, 0x17, 0xad, 0xfd, 0xe2, 0xde, 0x6e, 0xf7, 0xc3, 0xb4, 0x3e, 0x59, - 0xe4, 0xf5, 0xa9, 0x53, 0x79, 0x71, 0x74, 0x64, 0x06, 0xc9, 0x0c, 0x3f, 0xe8, 0x17, 0x49, 0x03, - 0xdb, 0x69, 0xfa, 0xea, 0xb2, 0x6b, 0x70, 0x90, 0x29, 0xcb, 0x22, 0x03, 0xf1, 0xa1, 0x74, 0x03, - 0x71, 0xe3, 0x5f, 0x65, 0x60, 0x2c, 0xfc, 0x6c, 0xb2, 0x00, 0xe7, 0x37, 0xcd, 0x52, 0x79, 0xd9, - 0xda, 0xfc, 0x70, 0x63, 0xd9, 0xda, 0x5a, 0xaf, 0x6d, 0x2c, 0x97, 0xab, 0xb7, 0xab, 0xcb, 0x95, - 0xc2, 0x33, 0x64, 0x06, 0x26, 0xb7, 0xd6, 0xef, 0xad, 0xdf, 0xdf, 0x5e, 0xb7, 0x96, 0x4d, 0xf3, - 0xbe, 0x59, 0xc8, 0x90, 0x49, 0x18, 0x33, 0x97, 0x4a, 0x65, 0x6b, 0xfd, 0x7e, 0x65, 0xb9, 0x90, - 0x25, 0x05, 0x98, 0x28, 0xdf, 0x5f, 0x5f, 0x5f, 0x2e, 0x6f, 0x56, 0x1f, 0x54, 0x37, 0x3f, 0x2c, - 0xe4, 0x08, 0x81, 0x29, 0x44, 0xd8, 0x30, 0xab, 0xeb, 0xe5, 0xea, 0x46, 0x69, 0xb5, 0x30, 0xc4, - 0x60, 0x0c, 0x5f, 0x81, 0x0d, 0x87, 0x8c, 0xee, 0x6d, 0x2d, 0x2d, 0x17, 0x46, 0x18, 0x0a, 0xfb, - 0x4b, 0x41, 0x19, 0x65, 0xd5, 0x23, 0x4a, 0xa5, 0xb4, 0x59, 0x5a, 0x2a, 0xd5, 0x96, 0x0b, 0x79, - 0x72, 0x01, 0x66, 0x35, 0x90, 0xb5, 0x7a, 0xff, 0x4e, 0x75, 0xbd, 0x30, 0x46, 0xe6, 0xa0, 0x10, - 0xc2, 0x2a, 0x4b, 0xd6, 0x56, 0x6d, 0xd9, 0x2c, 0x40, 0x1c, 0xba, 0x5e, 0x5a, 0x5b, 0x2e, 0x8c, - 0x1b, 0xef, 0x72, 0x3f, 0x44, 0xde, 0xd5, 0xe4, 0x3c, 0x90, 0xda, 0x66, 0x69, 0x73, 0xab, 0x16, - 0x6b, 0xfc, 0x38, 0x8c, 0xd6, 0xb6, 0xca, 0xe5, 0xe5, 0x5a, 0xad, 0x90, 0x21, 0x00, 0x23, 0xb7, - 0x4b, 0xd5, 0xd5, 0xe5, 0x4a, 0x21, 0x6b, 0xfc, 0x54, 0x06, 0x66, 0xa4, 0x04, 0x28, 0x1f, 0x8d, - 0x1e, 0x73, 0x2d, 0xbe, 0xa7, 0x5d, 0x6c, 0xa5, 0x93, 0x58, 0xac, 0x92, 0x3e, 0xcb, 0xf0, 0x17, - 0x32, 0x70, 0x2e, 0x15, 0x9b, 0x7c, 0x08, 0x05, 0xf9, 0x05, 0x6b, 0x76, 0x50, 0xdf, 0x8f, 0xf6, - 0xb1, 0x17, 0x62, 0xb5, 0xc4, 0xd0, 0xb8, 0x5a, 0x33, 0x4a, 0x38, 0x9d, 0x60, 0x33, 0x78, 0x3a, - 0x04, 0xe3, 0x9b, 0x19, 0xb8, 0xd0, 0xa3, 0x1a, 0x52, 0x86, 0x91, 0x30, 0x31, 0x4e, 0x1f, 0x83, - 0xb7, 0xb9, 0x6f, 0x1d, 0x17, 0x05, 0x22, 0x66, 0xe8, 0xc5, 0xbf, 0xcc, 0x91, 0x30, 0xd3, 0x0d, - 0xa6, 0x9b, 0xe1, 0xdd, 0x77, 0x31, 0xd6, 0xf3, 0xa2, 0xa6, 0xd2, 0x76, 0x6d, 0x69, 0x5c, 0xf4, - 0x5d, 0xce, 0x3e, 0xf4, 0x31, 0xdf, 0x8c, 0xf1, 0xb3, 0x19, 0x26, 0xdc, 0xc5, 0x11, 0x99, 0xcc, - 0x5b, 0xf2, 0xfd, 0x6e, 0x8b, 0x9a, 0x6e, 0x93, 0x96, 0xcc, 0x75, 0x71, 0x6c, 0xa0, 0xb4, 0x6a, - 0x63, 0x01, 0x5e, 0x2b, 0x2c, 0xdb, 0x6b, 0x6b, 0xaf, 0xd5, 0x2a, 0x0d, 0x79, 0x0b, 0x60, 0xf9, - 0x51, 0x40, 0xbd, 0xb6, 0xdd, 0x0c, 0x63, 0xc4, 0xf0, 0xc8, 0x56, 0x02, 0xaa, 0xcb, 0xdb, 0x0a, - 0xb2, 0xf1, 0xd7, 0x33, 0x30, 0x21, 0x2e, 0x4d, 0xa5, 0x26, 0xf5, 0x82, 0xc7, 0x9b, 0x5e, 0x6f, - 0x69, 0xd3, 0x2b, 0xf4, 0xef, 0x50, 0xf8, 0xb3, 0xe2, 0xd4, 0x99, 0xf5, 0xcf, 0x32, 0x50, 0x88, - 0x23, 0x92, 0xf7, 0x20, 0x5f, 0xa3, 0x0f, 0xa9, 0xe7, 0x04, 0x47, 0x62, 0xa3, 0x94, 0x29, 0x04, - 0x39, 0x8e, 0x28, 0xe3, 0xf3, 0xc1, 0x17, 0xbf, 0xcc, 0x90, 0x66, 0xd0, 0xfd, 0x5e, 0x51, 0x7b, - 0xe4, 0x9e, 0x94, 0xda, 0xc3, 0xf8, 0xdf, 0xb3, 0x70, 0xe1, 0x0e, 0x0d, 0xd4, 0x36, 0x85, 0xe6, - 0x05, 0x9f, 0x1e, 0xac, 0x5d, 0x4a, 0x4b, 0xe6, 0x61, 0x14, 0x8b, 0xe4, 0xf8, 0x9a, 0xf2, 0x27, - 0x59, 0x0a, 0xe7, 0x75, 0x4e, 0xcb, 0x51, 0xd6, 0xa3, 0xee, 0x6b, 0x4a, 0xd6, 0xa2, 0x70, 0x5a, - 0x5f, 0x86, 0x29, 0x0c, 0xcb, 0xdf, 0x65, 0xcb, 0x81, 0x36, 0x84, 0xfa, 0x27, 0x6f, 0xc6, 0xa0, - 0xe4, 0x35, 0x28, 0x30, 0x48, 0xa9, 0x7e, 0xd0, 0x76, 0x0f, 0x9b, 0xb4, 0xb1, 0x47, 0x1b, 0x78, - 0xac, 0xe7, 0xcd, 0x04, 0x5c, 0xf2, 0xdc, 0x6a, 0xf3, 0xab, 0x1b, 0x6d, 0xa0, 0x8e, 0x46, 0xf0, - 0x8c, 0xa0, 0x0b, 0x6f, 0xc1, 0xf8, 0xc7, 0xcc, 0x40, 0x66, 0xfc, 0xaf, 0x19, 0x98, 0xc3, 0xc6, - 0x29, 0x15, 0xcb, 0xec, 0xb0, 0xb2, 0xb7, 0x94, 0xa4, 0x3c, 0x36, 0x03, 0xe9, 0x4b, 0x21, 0xec, - 0xc5, 0x48, 0x27, 0x94, 0x1d, 0x40, 0x27, 0x54, 0x3b, 0x4b, 0x26, 0xfc, 0x01, 0x55, 0x5a, 0x77, - 0x87, 0xf2, 0xb9, 0xc2, 0x50, 0x34, 0xe4, 0xc6, 0x0f, 0x66, 0x61, 0xd4, 0xa4, 0x98, 0x22, 0x9c, - 0x5c, 0x86, 0xd1, 0x75, 0x37, 0xa0, 0xfe, 0x9a, 0x96, 0x0f, 0xbe, 0xcd, 0x40, 0x56, 0xab, 0x61, - 0xca, 0x42, 0x36, 0xe1, 0x37, 0x3c, 0xb7, 0xd1, 0xad, 0x07, 0xea, 0x84, 0xef, 0x70, 0x90, 0x29, - 0xcb, 0xc8, 0xeb, 0x30, 0x26, 0x38, 0x87, 0x8f, 0xba, 0x68, 0xbb, 0xec, 0xd1, 0x30, 0xc5, 0x7c, - 0x84, 0x80, 0x32, 0x2d, 0x17, 0x30, 0x86, 0x14, 0x99, 0x36, 0x21, 0x33, 0x48, 0x51, 0x7d, 0xb8, - 0x8f, 0xa8, 0xfe, 0x69, 0x18, 0x29, 0xf9, 0x3e, 0x0d, 0x64, 0x14, 0x85, 0x89, 0x30, 0x6c, 0x9c, - 0x4f, 0x03, 0xce, 0xd8, 0xc6, 0x72, 0x53, 0xe0, 0x19, 0xff, 0x32, 0x0b, 0xc3, 0xf8, 0x27, 0x3e, - 0x99, 0x7a, 0xf5, 0x7d, 0xed, 0xc9, 0xd4, 0xab, 0xef, 0x9b, 0x08, 0x25, 0x37, 0x50, 0x53, 0x21, - 0xf3, 0x47, 0x89, 0xd6, 0xa3, 0x0a, 0xbe, 0x11, 0x81, 0x4d, 0x15, 0x27, 0x7c, 0xe1, 0xcf, 0xa5, - 0xc6, 0x4e, 0x39, 0x0f, 0xd9, 0xfb, 0x35, 0xd1, 0x62, 0x8c, 0xc8, 0xe5, 0xfa, 0x66, 0xf6, 0x7e, - 0x0d, 0x7b, 0x63, 0xa5, 0xb4, 0xf8, 0xe6, 0x2d, 0xd1, 0x50, 0xde, 0x1b, 0xfb, 0xf6, 0xe2, 0x9b, - 0xb7, 0x4c, 0x51, 0xc2, 0xfa, 0x17, 0xbf, 0x19, 0x1f, 0x5e, 0xb9, 0xcf, 0x3f, 0xf6, 0x2f, 0xb6, - 0x0d, 0x1f, 0x59, 0xcd, 0x08, 0x81, 0x2c, 0xc2, 0xb8, 0x88, 0x35, 0x81, 0xf8, 0x4a, 0x2c, 0x08, - 0x11, 0x8b, 0x82, 0x53, 0xa8, 0x48, 0xfc, 0x09, 0x4e, 0x0c, 0x90, 0xcc, 0x72, 0x2b, 0x9e, 0xe0, - 0xe4, 0x10, 0xfa, 0xa6, 0x82, 0xc2, 0x3e, 0x89, 0xbf, 0xe1, 0x45, 0xbe, 0xfc, 0x53, 0x4a, 0xd0, - 0x02, 0x4c, 0xb3, 0x10, 0x22, 0x18, 0xbf, 0x92, 0x85, 0xfc, 0x46, 0xb3, 0xbb, 0xe7, 0xb4, 0x1f, - 0xdc, 0x20, 0x04, 0xf0, 0x1a, 0x27, 0xf3, 0x70, 0xb0, 0xbf, 0xc9, 0x45, 0xc8, 0xcb, 0x9b, 0x9b, - 0xdc, 0x90, 0x7c, 0x71, 0x6b, 0x9b, 0x07, 0x39, 0xee, 0x22, 0xf4, 0x9a, 0xfc, 0x49, 0x6e, 0x40, - 0x78, 0xff, 0xea, 0x75, 0x51, 0x1b, 0x62, 0x8b, 0xc5, 0x0c, 0xd1, 0xc8, 0x1b, 0x80, 0x87, 0x84, - 0xb8, 0x3c, 0x48, 0x85, 0x36, 0xff, 0x34, 0x21, 0xa7, 0x70, 0x12, 0x44, 0x23, 0x37, 0x41, 0x4c, - 0x4c, 0x91, 0x4d, 0xfd, 0x9c, 0x4e, 0xc0, 0xf3, 0x53, 0x4a, 0x12, 0x81, 0x4a, 0xde, 0x81, 0xf1, - 0xba, 0x47, 0xf1, 0xd5, 0xd1, 0x6e, 0x46, 0x49, 0xd2, 0x55, 0xca, 0x72, 0x54, 0xfe, 0xe0, 0x86, - 0xa9, 0xa2, 0x1b, 0xff, 0x5b, 0x1e, 0x26, 0xd4, 0xef, 0x21, 0x26, 0xcc, 0xfa, 0x4d, 0x76, 0x77, - 0x17, 0xc6, 0x66, 0x1d, 0x2c, 0x14, 0xc7, 0xe9, 0x25, 0xfd, 0x83, 0x18, 0x1e, 0xb7, 0x3c, 0x93, - 0x41, 0x32, 0x56, 0x9e, 0x31, 0x67, 0xfc, 0x08, 0xcc, 0xf1, 0x48, 0x09, 0xf2, 0x6e, 0xc7, 0xdf, - 0xa3, 0x6d, 0x47, 0xbe, 0xb7, 0xbc, 0xa4, 0x31, 0xba, 0x2f, 0x0a, 0x13, 0xbc, 0x42, 0x32, 0xf2, - 0x26, 0x8c, 0xb8, 0x1d, 0xda, 0xb6, 0x1d, 0x71, 0xc6, 0x3d, 0x1b, 0x63, 0x40, 0xdb, 0xa5, 0xaa, - 0x42, 0x28, 0x90, 0xc9, 0x75, 0x18, 0x72, 0x0f, 0xc2, 0xf1, 0xba, 0xa8, 0x13, 0x1d, 0x04, 0xb6, - 0x42, 0x82, 0x88, 0x8c, 0xe0, 0x23, 0xbb, 0xb5, 0x2b, 0x46, 0x4c, 0x27, 0xb8, 0x6b, 0xb7, 0x76, - 0x55, 0x02, 0x86, 0x48, 0xde, 0x07, 0xe8, 0xd8, 0x7b, 0xd4, 0xb3, 0x1a, 0xdd, 0xe0, 0x48, 0x8c, - 0xdb, 0x0b, 0x1a, 0xd9, 0x06, 0x2b, 0xae, 0x74, 0x83, 0x23, 0x85, 0x76, 0xac, 0x23, 0x81, 0xa4, - 0x04, 0xd0, 0xb2, 0x83, 0x80, 0x7a, 0x2d, 0x57, 0x58, 0xfb, 0x45, 0x41, 0x10, 0x39, 0x83, 0xb5, - 0xb0, 0x58, 0xe1, 0xa0, 0x10, 0xe1, 0x47, 0x3b, 0x9e, 0x2d, 0x72, 0xda, 0xc7, 0x3e, 0xda, 0xf1, - 0xb4, 0x56, 0x32, 0x44, 0xf2, 0x39, 0x18, 0x6d, 0x38, 0x7e, 0xdd, 0xf5, 0x1a, 0x22, 0x7a, 0xca, - 0x73, 0x1a, 0x4d, 0x85, 0x97, 0x29, 0x64, 0x12, 0x9d, 0x7d, 0xad, 0x08, 0x82, 0xba, 0xee, 0x1e, - 0xa2, 0x9a, 0x3f, 0xfe, 0xb5, 0xb5, 0xb0, 0x58, 0xfd, 0xda, 0x88, 0x88, 0x0d, 0xe5, 0x9e, 0x13, - 0x34, 0xed, 0x1d, 0xf1, 0xce, 0xad, 0x0f, 0xe5, 0x1d, 0x2c, 0x52, 0x87, 0x92, 0x23, 0x93, 0xb7, - 0x20, 0x4f, 0xdb, 0x81, 0x67, 0x5b, 0x4e, 0x43, 0x38, 0x55, 0xea, 0x1f, 0xcd, 0x0e, 0x60, 0xbb, - 0x5a, 0x51, 0x3f, 0x1a, 0xf1, 0xab, 0x0d, 0xd6, 0x3f, 0x7e, 0xdd, 0x69, 0x09, 0x5f, 0x48, 0xbd, - 0x7f, 0x6a, 0xe5, 0xea, 0x9a, 0xda, 0x3f, 0x0c, 0x91, 0xbc, 0x07, 0xa3, 0x6c, 0xfd, 0x36, 0xdc, - 0x3d, 0x11, 0x90, 0xc2, 0xd0, 0xfb, 0x87, 0x97, 0x25, 0xa6, 0xab, 0x24, 0x62, 0x0b, 0xd9, 0x3e, - 0xf4, 0x2d, 0xa7, 0x2e, 0x82, 0x4c, 0xe8, 0xcb, 0xb1, 0xb4, 0x5d, 0xab, 0x96, 0x15, 0xb2, 0x61, - 0xfb, 0xd0, 0xaf, 0xd6, 0xc9, 0x22, 0x0c, 0x63, 0x8a, 0x0a, 0x11, 0x08, 0x53, 0xa7, 0xc1, 0xe4, - 0x14, 0x2a, 0x0d, 0xa2, 0xb2, 0x81, 0x6c, 0xf9, 0xe8, 0x5e, 0x22, 0x12, 0x45, 0xe8, 0x7d, 0xb2, - 0x56, 0x43, 0x9f, 0x13, 0xf5, 0x13, 0x05, 0x3a, 0xfb, 0xc4, 0x36, 0x0d, 0x2c, 0xe7, 0xeb, 0x22, - 0xd5, 0x83, 0x5e, 0xdd, 0x3a, 0x0d, 0xaa, 0x1f, 0xa8, 0xd5, 0xb5, 0x69, 0x50, 0xfd, 0x3a, 0x79, - 0x01, 0x20, 0x7a, 0xfa, 0xe7, 0x0f, 0x35, 0xa6, 0x02, 0xf9, 0xfc, 0xd0, 0xff, 0xf5, 0x4b, 0xc5, - 0xcc, 0x12, 0xfc, 0x1b, 0xf6, 0xbe, 0xad, 0xc7, 0x91, 0xe3, 0x3a, 0x78, 0x9b, 0xe4, 0xcc, 0x70, - 0x0e, 0xe7, 0xd2, 0x53, 0x3b, 0xbb, 0x33, 0x9a, 0xbd, 0x69, 0x5b, 0xbb, 0xeb, 0x5d, 0xca, 0x92, - 0xb5, 0xab, 0x4f, 0x96, 0x64, 0x5b, 0x96, 0x7b, 0x38, 0x3d, 0x43, 0xee, 0xf2, 0xe6, 0x6e, 0x72, - 0xd6, 0xeb, 0xb5, 0xdd, 0xee, 0x25, 0x7b, 0x66, 0xda, 0xe2, 0x90, 0x34, 0x9b, 0xd4, 0x7a, 0x8c, - 0x0f, 0xf8, 0x6c, 0x7c, 0x80, 0x0d, 0x7c, 0x5f, 0x12, 0x27, 0x4e, 0x82, 0x08, 0x7e, 0xf1, 0x43, - 0x84, 0x20, 0x0f, 0xf9, 0x01, 0x41, 0x9c, 0x17, 0xbf, 0x19, 0x30, 0x0c, 0x18, 0xc8, 0x9b, 0x13, - 0x08, 0x89, 0x80, 0x04, 0xc8, 0xe5, 0x2d, 0x48, 0x1e, 0x0c, 0x04, 0x08, 0xea, 0x54, 0x55, 0x77, - 0xf5, 0x85, 0xdc, 0x59, 0x4b, 0x4a, 0x62, 0xc0, 0x4f, 0x33, 0x3c, 0x75, 0xea, 0x74, 0x5d, 0x4f, - 0x9d, 0x3a, 0x75, 0x2e, 0x90, 0x17, 0x61, 0x75, 0xb4, 0x2a, 0x3c, 0x33, 0x95, 0x59, 0x90, 0x5b, - 0xa0, 0x1e, 0x38, 0x5c, 0x55, 0xd8, 0x39, 0x72, 0xfa, 0x7d, 0xb7, 0xc7, 0xd9, 0xf4, 0xaa, 0x80, - 0x97, 0x18, 0x98, 0x51, 0xd6, 0xde, 0x84, 0xf5, 0xb4, 0x55, 0x42, 0xae, 0xc2, 0x92, 0x1c, 0x41, - 0x88, 0x13, 0x29, 0x38, 0x43, 0x4f, 0xc4, 0x10, 0xe2, 0x04, 0x7e, 0xa4, 0xc0, 0xc5, 0x59, 0x3c, - 0x87, 0x6c, 0x41, 0x7e, 0x38, 0xf2, 0x06, 0x28, 0xdb, 0xf2, 0x14, 0x0d, 0xe2, 0x37, 0x66, 0x5f, - 0x40, 0x21, 0x6c, 0xec, 0x1c, 0x72, 0xaf, 0x10, 0x73, 0x11, 0x21, 0x2d, 0xe7, 0xd0, 0x27, 0xcf, - 0xc3, 0x5a, 0xd7, 0x3d, 0x70, 0x26, 0xbd, 0xb1, 0xed, 0x77, 0x8e, 0xdc, 0x2e, 0xfa, 0x6d, 0xa1, - 0xb5, 0x9f, 0xa9, 0xf2, 0x02, 0x4b, 0xc0, 0x13, 0x2d, 0x9e, 0x9b, 0xd2, 0xe2, 0xbb, 0xb9, 0xbc, - 0xa2, 0x66, 0x4c, 0x34, 0xaf, 0xd2, 0xbe, 0x95, 0x81, 0xcd, 0x69, 0x9b, 0x8c, 0xbc, 0x91, 0x36, - 0x06, 0xec, 0xb5, 0x43, 0x86, 0xcb, 0xaf, 0x1d, 0xd2, 0xd7, 0xc8, 0x1d, 0x08, 0xbc, 0xae, 0x9e, - 0x14, 0x41, 0x41, 0xc0, 0x68, 0x9d, 0xa1, 0xe3, 0xfb, 0x8f, 0x29, 0x1f, 0xc9, 0x4a, 0x51, 0x78, - 0x39, 0x4c, 0xae, 0x23, 0x60, 0xe4, 0x55, 0x80, 0x4e, 0x6f, 0xe0, 0xbb, 0x68, 0x54, 0xc0, 0x05, - 0x14, 0x66, 0x4b, 0x1e, 0x40, 0xe5, 0x57, 0x64, 0x84, 0x96, 0x06, 0x5d, 0x97, 0x4f, 0xa0, 0x03, - 0x1b, 0x53, 0xb8, 0x2a, 0x9d, 0x9e, 0x30, 0xa5, 0xbd, 0x48, 0x90, 0x35, 0x09, 0x12, 0xdb, 0xc7, - 0x47, 0x3c, 0x33, 0x6d, 0x8d, 0x9c, 0x00, 0x49, 0xb2, 0x4e, 0x4a, 0x9d, 0x5b, 0x44, 0x4f, 0x46, - 0x01, 0x75, 0x06, 0x69, 0x8f, 0x7a, 0xe4, 0x0a, 0x14, 0x44, 0x02, 0x4c, 0x7a, 0x01, 0x60, 0xc4, - 0x81, 0x83, 0xee, 0xb9, 0xb8, 0x78, 0x30, 0xcc, 0x2a, 0xfa, 0xd6, 0x71, 0xd1, 0x62, 0x11, 0x21, - 0xad, 0x93, 0xa1, 0xe8, 0xdd, 0x45, 0xb1, 0xbe, 0xa3, 0x07, 0x1a, 0x2f, 0xfd, 0x43, 0x45, 0x4c, - 0x7f, 0xf2, 0x44, 0x78, 0x52, 0xfb, 0x08, 0xa0, 0x6b, 0x13, 0x6f, 0x18, 0xfe, 0x4f, 0x45, 0x1d, - 0xb1, 0xeb, 0xb8, 0xa8, 0xc3, 0x7f, 0x92, 0x1b, 0xb0, 0x3a, 0x62, 0xc6, 0xaf, 0xe3, 0x01, 0x1f, - 0x4f, 0x96, 0x6c, 0x64, 0x99, 0x81, 0x5b, 0x03, 0x1c, 0x53, 0xde, 0xae, 0xbb, 0xc1, 0x80, 0x49, - 0x07, 0x24, 0x79, 0x11, 0x16, 0xe9, 0x01, 0x89, 0xe1, 0x79, 0x62, 0x3e, 0x15, 0x88, 0x87, 0xe2, - 0x86, 0x99, 0xff, 0x1a, 0xff, 0x9f, 0xd3, 0x7a, 0x27, 0x23, 0x88, 0xc9, 0xc7, 0x33, 0xd9, 0x80, - 0x85, 0xc1, 0xe8, 0x50, 0xea, 0xda, 0xfc, 0x60, 0x74, 0x48, 0xfb, 0x75, 0x13, 0x54, 0xe6, 0xe2, - 0xc3, 0x42, 0x2d, 0xf8, 0x27, 0x7d, 0x76, 0x7f, 0xcf, 0x9b, 0x2b, 0x0c, 0x8e, 0x59, 0xfe, 0x4f, - 0xfa, 0x1d, 0x8a, 0xe9, 0xfb, 0x03, 0x5b, 0x8e, 0xca, 0xc5, 0xbb, 0xbd, 0xe2, 0xfb, 0x83, 0x30, - 0x3c, 0x57, 0x97, 0x6c, 0xc3, 0x32, 0xa5, 0x13, 0xc4, 0x06, 0xe3, 0xd2, 0xc3, 0xa5, 0xa4, 0xf4, - 0x70, 0xd2, 0xef, 0x88, 0x26, 0x9a, 0x4b, 0xbe, 0xf4, 0x8b, 0xdc, 0x03, 0x55, 0x12, 0xb3, 0xd0, - 0xe7, 0x33, 0x66, 0x88, 0x1d, 0x92, 0x91, 0xc4, 0xb3, 0x4a, 0xff, 0x60, 0x60, 0xae, 0x76, 0xa2, - 0x00, 0x3e, 0x34, 0x3f, 0x54, 0x04, 0x2f, 0x4d, 0xa9, 0x44, 0x34, 0x58, 0x3e, 0x72, 0x7c, 0xdb, - 0xf7, 0x8f, 0x99, 0x61, 0x19, 0x8f, 0x46, 0x5c, 0x38, 0x72, 0x7c, 0xcb, 0x3f, 0x16, 0xd9, 0x4e, - 0xce, 0x51, 0x9c, 0x81, 0x33, 0x19, 0x1f, 0xd9, 0xb2, 0xd0, 0xc8, 0x46, 0xec, 0xec, 0x91, 0xe3, - 0x37, 0x68, 0x99, 0x44, 0x9b, 0x5c, 0x83, 0x15, 0xa4, 0xdb, 0xf1, 0x04, 0x61, 0x0c, 0x97, 0x61, - 0x2e, 0x51, 0xc2, 0x1d, 0x8f, 0x51, 0xe6, 0x2d, 0xfc, 0xc7, 0x0c, 0x9c, 0x4f, 0x1f, 0x1d, 0x5c, - 0x9e, 0x74, 0x4c, 0xd1, 0xb1, 0x8f, 0xb7, 0x6d, 0x91, 0x42, 0x58, 0xa8, 0x93, 0xb4, 0xc9, 0xc9, - 0xa4, 0x4e, 0x4e, 0x11, 0xd6, 0x90, 0x10, 0x17, 0x4f, 0x7b, 0x9e, 0x3f, 0xe6, 0x11, 0x3c, 0xcc, - 0x55, 0x5a, 0xc0, 0xf8, 0x79, 0x95, 0x82, 0xc9, 0x75, 0x58, 0x11, 0x1c, 0x79, 0xf0, 0xb8, 0x4f, - 0x3f, 0xcc, 0xd8, 0xf1, 0x32, 0x87, 0x36, 0x10, 0x48, 0xce, 0xc1, 0xbc, 0x33, 0x1c, 0xd2, 0x4f, - 0x32, 0x2e, 0x3c, 0xe7, 0x0c, 0x87, 0x2c, 0x23, 0x0f, 0xba, 0x31, 0xda, 0x07, 0x68, 0x5a, 0xc4, - 0xed, 0x18, 0xcd, 0x25, 0x04, 0x32, 0x73, 0x23, 0x9f, 0xee, 0x7b, 0x5a, 0x57, 0xa0, 0x2c, 0x20, - 0x0a, 0x38, 0xc3, 0x00, 0xe1, 0x19, 0xc8, 0x8b, 0x47, 0x6e, 0xe6, 0x8d, 0x61, 0x2e, 0x38, 0xfc, - 0x81, 0xfb, 0x15, 0xd8, 0xe8, 0x7a, 0x3e, 0x2e, 0x5e, 0xd6, 0xa5, 0xe1, 0x90, 0x3b, 0x4e, 0xb2, - 0xc8, 0xbe, 0xe6, 0x3a, 0x2f, 0xa6, 0x23, 0xa9, 0x0f, 0x87, 0xcc, 0x7d, 0x92, 0x8f, 0xf5, 0x6b, - 0xb0, 0xca, 0xc5, 0x34, 0x7e, 0x44, 0x62, 0x5b, 0xf8, 0x06, 0xa6, 0xf7, 0x27, 0x9e, 0x03, 0x09, - 0x38, 0xa8, 0xd2, 0x15, 0x35, 0xff, 0x46, 0x81, 0x73, 0xa9, 0x72, 0x1e, 0xf9, 0x2a, 0x30, 0x3f, - 0xb1, 0xf1, 0xc0, 0x1e, 0xb9, 0x1d, 0x6f, 0xe8, 0x61, 0xe0, 0x0d, 0xa6, 0x07, 0xbd, 0x33, 0x4b, - 0x42, 0x44, 0x9f, 0xb3, 0xd6, 0xc0, 0x0c, 0x2a, 0x31, 0x05, 0x8d, 0x3a, 0x8a, 0x81, 0xb7, 0x1e, - 0xc2, 0xb9, 0x54, 0xd4, 0x14, 0xc5, 0xc9, 0xc7, 0xa3, 0x19, 0xa8, 0xc5, 0xcb, 0x56, 0xac, 0xd3, - 0x92, 0x42, 0x85, 0x77, 0xef, 0xc7, 0x41, 0xf7, 0x62, 0x12, 0x21, 0x31, 0xe2, 0xfb, 0x3a, 0xed, - 0x52, 0x23, 0x2a, 0x4d, 0xdf, 0xda, 0x0f, 0xe1, 0x1c, 0x5f, 0x7c, 0x87, 0x23, 0x67, 0x78, 0x14, - 0x92, 0x63, 0x0d, 0xfd, 0x58, 0x1a, 0x39, 0xb6, 0x2a, 0xf7, 0x28, 0x7e, 0x40, 0xf5, 0xac, 0x93, - 0x04, 0xf2, 0x3e, 0x7c, 0x3b, 0x23, 0xb6, 0x7a, 0x4a, 0x73, 0x52, 0x96, 0xb5, 0x92, 0xb6, 0xac, - 0x4f, 0xbf, 0xa7, 0xea, 0x40, 0x64, 0x66, 0xc5, 0x54, 0xa5, 0xdc, 0x0a, 0x4b, 0x08, 0xf7, 0xbc, - 0x21, 0x12, 0x6b, 0xb0, 0x58, 0x06, 0xd0, 0xb5, 0x4e, 0x1c, 0x44, 0x2e, 0xc0, 0x62, 0x90, 0x64, - 0x9b, 0x1f, 0x1c, 0x79, 0x06, 0xa8, 0x74, 0xc9, 0xb3, 0xb0, 0xc4, 0xe4, 0xf8, 0xc8, 0x9e, 0x03, - 0x84, 0xe9, 0x74, 0xe3, 0x89, 0x31, 0x50, 0xe0, 0xd9, 0x27, 0x8d, 0x21, 0xb9, 0x0f, 0xe7, 0xd1, - 0x16, 0xc4, 0x1f, 0x04, 0xd3, 0x60, 0x77, 0x9c, 0xce, 0x91, 0xcb, 0x57, 0xad, 0x96, 0x3a, 0x19, - 0xc3, 0xa1, 0x65, 0x35, 0xa4, 0x79, 0x18, 0x0e, 0x2d, 0x7f, 0x20, 0x7e, 0x97, 0x68, 0x75, 0xde, - 0x86, 0x2e, 0x5c, 0x98, 0x51, 0x53, 0x62, 0x1c, 0x8a, 0xcc, 0x38, 0x6e, 0x82, 0x7a, 0xe0, 0x76, - 0xa9, 0x4c, 0xec, 0x76, 0xb1, 0x69, 0x6f, 0xdf, 0x61, 0x69, 0xe5, 0xcd, 0x95, 0x00, 0x6e, 0xf9, - 0x83, 0xfd, 0x3b, 0xfc, 0x2b, 0xc7, 0xe2, 0xc8, 0x93, 0xef, 0x22, 0xe4, 0x45, 0x38, 0x1b, 0x0b, - 0x6a, 0x12, 0x7a, 0xc9, 0x9b, 0x6b, 0xb4, 0x28, 0x1a, 0x02, 0xeb, 0x2a, 0x2c, 0x89, 0x55, 0x31, - 0x0a, 0x9c, 0xe7, 0xcc, 0x02, 0x87, 0xd1, 0x5d, 0xc7, 0x3f, 0x37, 0x11, 0x9d, 0x4a, 0xbd, 0xc6, - 0x9c, 0x42, 0x96, 0x26, 0x2f, 0x00, 0x09, 0xe4, 0xf6, 0x80, 0x51, 0xf0, 0x0f, 0xae, 0x89, 0x92, - 0x60, 0x87, 0xf3, 0xcf, 0xfe, 0x65, 0x06, 0xce, 0xa6, 0xdc, 0x7f, 0xe8, 0x25, 0xc0, 0xeb, 0x8f, - 0xdd, 0x43, 0x76, 0x85, 0x90, 0x3b, 0xb9, 0x2a, 0xc1, 0xb9, 0x52, 0x6b, 0x9e, 0xa5, 0x4d, 0xe7, - 0xdf, 0xe2, 0xbf, 0x28, 0xf3, 0x70, 0x46, 0x42, 0x5f, 0x43, 0xff, 0x25, 0x15, 0x58, 0xc3, 0x5c, - 0x10, 0xbe, 0x37, 0xc0, 0x94, 0x12, 0x28, 0x84, 0xe4, 0x22, 0x37, 0x24, 0x6c, 0x45, 0x53, 0x42, - 0xa2, 0x52, 0x88, 0xa9, 0x0e, 0x63, 0x10, 0xf2, 0x69, 0xd8, 0x92, 0xce, 0x1a, 0x3b, 0xb6, 0xf3, - 0xd0, 0x3c, 0xde, 0xdc, 0x70, 0x82, 0x53, 0x67, 0x27, 0xb2, 0x07, 0xb7, 0xe1, 0x32, 0x4e, 0xa2, - 0xd7, 0x1d, 0xda, 0x89, 0xe4, 0x21, 0xd8, 0x55, 0x16, 0x6d, 0x7f, 0x8b, 0x62, 0x55, 0xba, 0xc3, - 0x58, 0x1e, 0x11, 0xda, 0x6b, 0x3e, 0x7c, 0x0f, 0xe1, 0x5c, 0x6a, 0x8b, 0xe9, 0x01, 0x83, 0xd6, - 0x57, 0xa1, 0x6c, 0xb4, 0x40, 0x7f, 0x53, 0xe1, 0xe8, 0x2a, 0x2c, 0x3d, 0x72, 0x9d, 0x91, 0x3b, - 0xe2, 0x27, 0x37, 0x5f, 0x12, 0x0c, 0x26, 0x1f, 0xdc, 0xdd, 0xe8, 0xd4, 0x70, 0x45, 0x13, 0xa9, - 0xc1, 0x59, 0x76, 0x02, 0x7a, 0xc7, 0x28, 0x0c, 0x72, 0xe5, 0x94, 0x12, 0x11, 0x87, 0xb0, 0x0a, - 0x1e, 0x4d, 0x15, 0xc4, 0x62, 0xb5, 0xcd, 0xb5, 0xc3, 0x38, 0x88, 0xee, 0xe8, 0xf3, 0xe9, 0xd8, - 0x64, 0x1b, 0x0a, 0x8c, 0x38, 0xbb, 0x16, 0xb0, 0x57, 0x85, 0xab, 0x33, 0xbf, 0x50, 0x42, 0xa3, - 0x64, 0x3f, 0xf8, 0x9f, 0x9e, 0xd7, 0xf8, 0x80, 0x6b, 0x1f, 0xcb, 0x8f, 0x26, 0xe6, 0x12, 0x02, - 0xf9, 0x63, 0x89, 0xf6, 0x57, 0x8a, 0xe8, 0x6a, 0xe4, 0x46, 0x4d, 0x97, 0x96, 0xef, 0xf6, 0xc5, - 0xc3, 0xd1, 0xa2, 0xc9, 0x7f, 0x3d, 0xe5, 0x52, 0x27, 0xaf, 0xc2, 0x12, 0x25, 0x7b, 0x38, 0xe9, - 0xb3, 0x25, 0x97, 0x8d, 0x04, 0xf3, 0xa9, 0xb1, 0x22, 0x3a, 0x6d, 0xe5, 0x33, 0x66, 0xe1, 0x38, - 0xfc, 0x49, 0xa5, 0x65, 0xff, 0x78, 0x3c, 0x94, 0x17, 0xaa, 0xd0, 0x2e, 0x5a, 0xb5, 0x56, 0x93, - 0x57, 0xc9, 0x53, 0x9c, 0x50, 0x5a, 0xde, 0x9e, 0x67, 0xfa, 0x45, 0xed, 0x79, 0x28, 0x48, 0xb4, - 0x69, 0x67, 0x98, 0xbb, 0x8d, 0xe8, 0x0c, 0xfb, 0xc5, 0x27, 0xfb, 0x11, 0xe4, 0x05, 0x49, 0x7a, - 0x2d, 0x38, 0x1a, 0xf8, 0x62, 0x93, 0xe3, 0xff, 0x14, 0x46, 0x47, 0x19, 0x3b, 0x39, 0x67, 0xe2, - 0xff, 0x78, 0x96, 0x8c, 0x1d, 0x7a, 0x1f, 0xe8, 0xf9, 0xf6, 0x10, 0xcd, 0xb6, 0x02, 0xe1, 0x99, - 0xc2, 0x5b, 0x3d, 0x9f, 0x19, 0x73, 0xf1, 0x6f, 0xfc, 0x79, 0x70, 0x08, 0xc7, 0x54, 0x10, 0xd3, - 0x78, 0x66, 0xe4, 0xc8, 0xc8, 0x24, 0x8f, 0x0c, 0x16, 0xa4, 0x85, 0xd7, 0x64, 0x5f, 0x06, 0x84, - 0xe1, 0x91, 0x21, 0x71, 0x86, 0x5c, 0x84, 0x33, 0x48, 0x77, 0xf2, 0x70, 0xf6, 0xd8, 0x89, 0x23, - 0xee, 0xe4, 0x71, 0x3e, 0xf5, 0x6e, 0xb0, 0x42, 0x22, 0x4a, 0x10, 0x2a, 0x3c, 0x33, 0xc1, 0x99, - 0xa7, 0xf6, 0x8d, 0x31, 0xc8, 0xb3, 0x58, 0xc8, 0x12, 0xff, 0x04, 0x8c, 0xf2, 0xc9, 0x77, 0x4e, - 0xf2, 0x12, 0xac, 0x07, 0xc9, 0x28, 0xfd, 0xb7, 0xbc, 0xa1, 0x8d, 0xe9, 0x48, 0x4f, 0xb8, 0x48, - 0x4b, 0x44, 0x99, 0xf5, 0x96, 0x37, 0xdc, 0xc7, 0x12, 0xde, 0xcc, 0x3f, 0xc9, 0x08, 0x4d, 0xc6, - 0xf6, 0x60, 0x30, 0xf6, 0xc7, 0x23, 0x67, 0x18, 0x51, 0xf3, 0x92, 0x63, 0x78, 0x06, 0x9b, 0x74, - 0x07, 0xd3, 0x83, 0x0c, 0x46, 0x22, 0x7c, 0x49, 0xb0, 0xc1, 0x0a, 0x77, 0x3e, 0x11, 0xbd, 0x8a, - 0xe8, 0x14, 0x5b, 0x97, 0x91, 0xe9, 0xbe, 0x92, 0xa8, 0x96, 0xcf, 0x98, 0x1b, 0x8c, 0x66, 0x02, - 0x8b, 0x94, 0x53, 0x78, 0x4d, 0x5c, 0xcf, 0xbb, 0x1d, 0x32, 0x9e, 0x28, 0x55, 0x99, 0x25, 0x91, - 0xcf, 0xc2, 0xa2, 0xd7, 0x95, 0xb3, 0x60, 0xc6, 0x35, 0x8c, 0x95, 0x2e, 0x8b, 0xc4, 0x1d, 0xd2, - 0xa0, 0x5b, 0xc3, 0xe3, 0xd0, 0xed, 0xe5, 0x88, 0x42, 0x5c, 0xdb, 0x16, 0x97, 0xe6, 0x64, 0x35, - 0xb2, 0x02, 0x99, 0x60, 0x21, 0x66, 0xbc, 0x2e, 0xe3, 0x02, 0x61, 0x2c, 0x70, 0x93, 0xff, 0xd2, - 0xfe, 0x37, 0xdc, 0x3c, 0xed, 0x18, 0x51, 0x8e, 0x31, 0x65, 0xc0, 0x17, 0x59, 0x18, 0xce, 0xe8, - 0xb8, 0x5d, 0x05, 0x39, 0x94, 0xb1, 0x27, 0x96, 0x88, 0x80, 0xb5, 0x47, 0x9e, 0xf6, 0x2f, 0x59, - 0x58, 0x89, 0x3e, 0x01, 0x90, 0xe7, 0x21, 0x27, 0x31, 0xca, 0x8d, 0x94, 0x77, 0x02, 0x64, 0x8f, - 0x88, 0x74, 0x2a, 0xc6, 0x48, 0xee, 0xc2, 0x0a, 0x1a, 0x25, 0xa2, 0x84, 0x3c, 0xf6, 0xf8, 0xc3, - 0xd2, 0xec, 0xb7, 0xc1, 0xfc, 0x4f, 0xde, 0xbb, 0x72, 0x06, 0x9f, 0x01, 0x97, 0x68, 0x5d, 0x2a, - 0xa4, 0xd2, 0x42, 0x49, 0xc3, 0x9b, 0x9b, 0xae, 0xe1, 0xe5, 0x5d, 0x99, 0xa2, 0xe1, 0x9d, 0x9b, - 0xa1, 0xe1, 0x0d, 0x6b, 0xca, 0x1a, 0x5e, 0xd4, 0xf3, 0x2f, 0x4c, 0xd3, 0xf3, 0x87, 0x75, 0x98, - 0x9e, 0x3f, 0xd4, 0xd0, 0xe6, 0xa7, 0x6a, 0x68, 0xc3, 0x3a, 0x5c, 0x43, 0x1b, 0xea, 0x4c, 0x17, - 0xa7, 0xea, 0x4c, 0xa5, 0x4a, 0x4c, 0x67, 0x7a, 0x8d, 0x0f, 0xec, 0xc8, 0x79, 0x6c, 0xe3, 0x88, - 0xf3, 0x23, 0x1f, 0x87, 0xcc, 0x74, 0x1e, 0xa3, 0xb5, 0xd1, 0xf6, 0x22, 0x08, 0x13, 0x25, 0xed, - 0x47, 0x31, 0x06, 0x24, 0xe6, 0xfc, 0x3a, 0xac, 0xb0, 0x73, 0x98, 0x87, 0x77, 0x65, 0x07, 0xf1, - 0xb2, 0xb9, 0x2c, 0xa0, 0xec, 0x2a, 0xfd, 0x31, 0x58, 0x0d, 0xd0, 0xf8, 0x6d, 0x12, 0x3d, 0x17, - 0xcd, 0xa0, 0x36, 0x0f, 0xc3, 0x23, 0xd3, 0x1b, 0xf1, 0x40, 0x37, 0x11, 0x7a, 0x2c, 0x0a, 0xca, - 0x0b, 0x40, 0x42, 0xb4, 0xc0, 0x60, 0x33, 0x87, 0xa8, 0x6b, 0x01, 0x6a, 0x60, 0x55, 0xf9, 0xfb, - 0x4a, 0x4c, 0x47, 0xfb, 0x51, 0x35, 0xff, 0x79, 0x08, 0xbe, 0x6e, 0x73, 0x3d, 0x9b, 0xe8, 0x81, - 0x2a, 0x0a, 0x9a, 0x1c, 0xae, 0x1d, 0xc6, 0xef, 0x84, 0x1f, 0x51, 0xab, 0xb4, 0x1f, 0x67, 0x23, - 0xfa, 0x2b, 0xf1, 0x19, 0x2a, 0xdf, 0xf8, 0x03, 0x9b, 0x4f, 0x31, 0x67, 0xbf, 0x57, 0xa7, 0x2c, - 0x53, 0x6e, 0xa2, 0x66, 0x59, 0x0d, 0x13, 0x7c, 0x7f, 0x20, 0x2c, 0xd6, 0x6c, 0x76, 0xd7, 0x61, - 0x12, 0x19, 0x6e, 0x53, 0x41, 0x8e, 0xf1, 0xda, 0xe2, 0x6c, 0x72, 0x42, 0x81, 0x40, 0x77, 0x29, - 0xde, 0x79, 0x82, 0x5f, 0xe2, 0x03, 0x6d, 0x40, 0x75, 0xaf, 0x1f, 0x25, 0x9e, 0x4d, 0xb9, 0xd5, - 0x26, 0x88, 0xe3, 0x28, 0x21, 0x65, 0x75, 0x22, 0xfe, 0x15, 0x64, 0x0d, 0x58, 0x42, 0xed, 0x91, - 0x20, 0x98, 0x4b, 0x79, 0x51, 0x49, 0x76, 0xbe, 0x54, 0xa9, 0x99, 0x05, 0x5a, 0x4f, 0x90, 0x39, - 0x82, 0x67, 0x64, 0x9d, 0x4f, 0xb4, 0x91, 0x73, 0x22, 0x28, 0xf3, 0xcc, 0x11, 0x08, 0x55, 0x43, - 0xd8, 0xd4, 0xf3, 0x4e, 0x14, 0xc0, 0xd1, 0xb4, 0x23, 0xd8, 0x9a, 0x3e, 0x25, 0x33, 0x12, 0x7e, - 0x85, 0xa2, 0x4d, 0x46, 0x16, 0x6d, 0x64, 0x0d, 0x50, 0x36, 0xa2, 0x01, 0xd2, 0xfe, 0x38, 0x0b, - 0xcf, 0x9d, 0x62, 0xba, 0x66, 0x7c, 0xf3, 0x73, 0x51, 0xc1, 0x39, 0x13, 0xb9, 0xb3, 0x53, 0xa2, - 0xfc, 0x4c, 0x38, 0xe9, 0x77, 0xa6, 0x88, 0xcd, 0x5f, 0x85, 0x55, 0xc6, 0xf8, 0x99, 0x95, 0xe9, - 0xc1, 0xa4, 0x77, 0x0a, 0xce, 0x7f, 0x41, 0xb8, 0xc4, 0xc5, 0xaa, 0xe2, 0x61, 0x80, 0xfc, 0xce, - 0x0a, 0x60, 0xa4, 0x05, 0x05, 0x44, 0x3b, 0x70, 0xbc, 0xde, 0xa9, 0x7c, 0xb3, 0x84, 0xc3, 0x9d, - 0x5c, 0x8d, 0x19, 0xc7, 0x53, 0xc0, 0x2e, 0xfe, 0x26, 0x37, 0x60, 0xb5, 0x3f, 0x39, 0xa6, 0x22, - 0x21, 0x5b, 0x0b, 0xdc, 0x98, 0x67, 0xce, 0x5c, 0xee, 0x4f, 0x8e, 0xf5, 0xe1, 0x10, 0xa7, 0x14, - 0xad, 0x7e, 0xd6, 0x28, 0x1e, 0xdb, 0xb5, 0x02, 0x73, 0x1e, 0x31, 0x29, 0x01, 0xb6, 0x6f, 0x39, - 0xee, 0x3a, 0x30, 0x1b, 0x50, 0x9e, 0xf0, 0x8c, 0xfd, 0xd0, 0xfe, 0x3d, 0x23, 0x34, 0x11, 0xd3, - 0xd7, 0xfd, 0x6f, 0xa6, 0x28, 0x65, 0x8a, 0x6e, 0x82, 0x4a, 0x87, 0x3e, 0x64, 0x2a, 0xc1, 0x1c, - 0xad, 0xf4, 0x27, 0xc7, 0xc1, 0xd8, 0xc9, 0x03, 0x3f, 0x2f, 0x0f, 0xfc, 0xab, 0x42, 0x53, 0x91, - 0xca, 0x1e, 0xa6, 0x0f, 0x39, 0x95, 0x98, 0x6e, 0x9c, 0x8e, 0x09, 0xfc, 0x66, 0xde, 0x52, 0xe6, - 0x2d, 0xa6, 0xd4, 0x9e, 0x4b, 0x28, 0xb5, 0x53, 0xf6, 0xde, 0x7c, 0xda, 0xde, 0x4b, 0xa8, 0xd0, - 0x17, 0x52, 0x54, 0xe8, 0xa9, 0x1b, 0x34, 0xff, 0x84, 0x0d, 0xba, 0x28, 0xaf, 0x93, 0x7f, 0x08, - 0x54, 0x4b, 0xd1, 0x2b, 0xd0, 0x43, 0x38, 0x2b, 0xae, 0x40, 0xec, 0xe4, 0x08, 0x5f, 0x46, 0x0a, - 0x77, 0x6e, 0xa5, 0x5d, 0x7e, 0x10, 0x2d, 0xe5, 0x82, 0xb2, 0xc6, 0xaf, 0x3d, 0x61, 0xf9, 0xff, - 0x9c, 0x0b, 0x0f, 0x79, 0x00, 0xe7, 0x31, 0x5d, 0x40, 0x47, 0x7e, 0xd3, 0xb1, 0x47, 0xee, 0x01, - 0x5f, 0x0f, 0x57, 0x13, 0xd7, 0x03, 0xaf, 0x23, 0x35, 0xc7, 0x74, 0x0f, 0xca, 0x67, 0xcc, 0x75, - 0x3f, 0x05, 0x1e, 0xbf, 0x4b, 0xfd, 0x99, 0x02, 0xda, 0x93, 0xc7, 0x0b, 0xaf, 0xbd, 0xf1, 0x01, - 0xa7, 0xd7, 0x5e, 0x69, 0xf4, 0x9e, 0x83, 0xe5, 0x91, 0x7b, 0x30, 0x72, 0xfd, 0xa3, 0x88, 0x6e, - 0x6a, 0x89, 0x03, 0xc5, 0xc0, 0x88, 0x18, 0xa3, 0x4f, 0x75, 0x19, 0x11, 0x95, 0xb4, 0xdd, 0xe0, - 0x8a, 0x9c, 0x3a, 0x0f, 0x74, 0x35, 0xc9, 0x0d, 0x64, 0x3f, 0xee, 0xe6, 0xf2, 0x19, 0x35, 0x6b, - 0xf2, 0x48, 0xa8, 0x07, 0x5e, 0xcf, 0xd5, 0xfe, 0x42, 0x11, 0x12, 0x41, 0xda, 0xe0, 0x91, 0x87, - 0x92, 0x6d, 0x76, 0x36, 0x21, 0x86, 0xa4, 0x55, 0x91, 0xcd, 0x58, 0x79, 0xb4, 0x4d, 0x04, 0x44, - 0xa2, 0x6d, 0x22, 0xe4, 0x03, 0x18, 0x98, 0x72, 0x45, 0xc1, 0xeb, 0xc2, 0xc0, 0x8b, 0xf2, 0xbc, - 0xfd, 0xdb, 0xe4, 0x16, 0x2c, 0x30, 0x9b, 0x2e, 0xd1, 0xdc, 0xd5, 0x48, 0x73, 0xf7, 0x6f, 0x9b, - 0xa2, 0x5c, 0x7b, 0x27, 0x78, 0x71, 0x4c, 0x74, 0x62, 0xff, 0x36, 0x79, 0xf5, 0x74, 0xb6, 0xd6, - 0x79, 0x61, 0x6b, 0x1d, 0xd8, 0x59, 0xbf, 0x16, 0xb1, 0xb3, 0xbe, 0x36, 0x7b, 0xb4, 0xf8, 0x3b, - 0x31, 0x8b, 0x2e, 0x19, 0x46, 0x1d, 0xfb, 0x45, 0x06, 0x2e, 0xcd, 0xac, 0x41, 0x2e, 0x42, 0x5e, - 0x6f, 0x56, 0x5a, 0xe1, 0xfc, 0xd2, 0x3d, 0x23, 0x20, 0x64, 0x0f, 0x16, 0xb7, 0x1d, 0xdf, 0xeb, - 0xd0, 0x65, 0x9c, 0xfa, 0x70, 0x93, 0x20, 0x1b, 0xa0, 0x97, 0xcf, 0x98, 0x61, 0x5d, 0x62, 0xc3, - 0x1a, 0xee, 0x85, 0x48, 0x26, 0xb1, 0x6c, 0x8a, 0x7a, 0x25, 0x41, 0x30, 0x51, 0x8d, 0xf2, 0x99, - 0x04, 0x90, 0x3c, 0x02, 0x62, 0x59, 0xe5, 0x92, 0x3b, 0x1a, 0x73, 0xb5, 0xc3, 0xd8, 0x0b, 0x0c, - 0x77, 0x5f, 0x7a, 0xc2, 0xd8, 0x25, 0xea, 0x95, 0xcf, 0x98, 0x29, 0xd4, 0xe2, 0xdb, 0xfc, 0x6d, - 0x21, 0xef, 0x4c, 0x1f, 0x84, 0xa7, 0x88, 0xdc, 0x7b, 0x13, 0xf2, 0x4d, 0x61, 0x25, 0x22, 0x39, - 0x40, 0x08, 0x8b, 0x10, 0x33, 0x28, 0xd5, 0x7e, 0x4b, 0x11, 0x7a, 0x96, 0x27, 0x0f, 0x96, 0x94, - 0xe8, 0xad, 0x3b, 0x3b, 0xd1, 0x5b, 0xf7, 0x57, 0x4c, 0xf4, 0xa6, 0x79, 0x70, 0xeb, 0xd4, 0x03, - 0x4b, 0x3e, 0x03, 0x2a, 0xe6, 0xc4, 0x72, 0xa4, 0x49, 0x62, 0xfb, 0x6b, 0x2d, 0x08, 0xe5, 0x5e, - 0xe6, 0x89, 0x07, 0xcd, 0xd5, 0x4e, 0xb4, 0xb6, 0xf6, 0xa7, 0x3c, 0x84, 0x7f, 0xa5, 0xdb, 0x8c, - 0x3d, 0x01, 0x7c, 0x50, 0x9f, 0x19, 0x23, 0xb2, 0xd9, 0x9e, 0x93, 0x72, 0x92, 0x26, 0xbf, 0x35, - 0xdd, 0x75, 0x46, 0xda, 0x79, 0x7f, 0x94, 0x85, 0x8b, 0xb3, 0xaa, 0xa7, 0x66, 0x3d, 0x57, 0x9e, - 0x2e, 0xeb, 0xf9, 0x2d, 0xc8, 0x33, 0x58, 0xe0, 0x10, 0x82, 0x73, 0xcb, 0xab, 0xd2, 0xb9, 0x15, - 0xc5, 0xe4, 0x39, 0x98, 0xd7, 0x4b, 0x56, 0x98, 0x88, 0x0f, 0x2d, 0xb7, 0x9d, 0x8e, 0x8f, 0x36, - 0xc1, 0xbc, 0x88, 0x7c, 0x25, 0x99, 0x7b, 0x92, 0x67, 0xe0, 0xbb, 0x20, 0x0d, 0x48, 0x22, 0xbb, - 0x06, 0xb6, 0x37, 0xcc, 0x06, 0xc1, 0x03, 0xac, 0x9b, 0xc9, 0x3c, 0x96, 0x1a, 0xcc, 0x37, 0x47, - 0xae, 0xef, 0x8e, 0x65, 0xab, 0xea, 0x21, 0x42, 0x4c, 0x5e, 0xc2, 0x6d, 0x9e, 0x9d, 0x13, 0x16, - 0xe2, 0x62, 0x5e, 0x0e, 0x3b, 0x84, 0x46, 0xd2, 0x14, 0x6c, 0x4a, 0x28, 0xb4, 0x42, 0xd5, 0x99, - 0xf4, 0x3b, 0x47, 0x6d, 0xb3, 0xca, 0x25, 0x27, 0x56, 0xa1, 0x87, 0x50, 0xda, 0x41, 0xdf, 0x94, - 0x50, 0xb4, 0xef, 0x2a, 0xb0, 0x9e, 0xd6, 0x0f, 0x72, 0x11, 0x72, 0xfd, 0xd4, 0x34, 0x9b, 0x7d, - 0xe6, 0x99, 0x5f, 0xa0, 0x7f, 0xed, 0x83, 0xc1, 0xe8, 0xd8, 0x19, 0xcb, 0xb6, 0xe7, 0x12, 0xd8, - 0x04, 0xfa, 0x63, 0x17, 0xff, 0x27, 0x57, 0xc4, 0x91, 0x93, 0x4d, 0x24, 0xe6, 0xc4, 0x3f, 0x9a, - 0x0e, 0x50, 0xe9, 0x36, 0x1b, 0x43, 0x96, 0xdd, 0xe1, 0x65, 0xc8, 0xd1, 0x66, 0xc5, 0x56, 0x2f, - 0x5d, 0x3f, 0x7a, 0xad, 0xca, 0x91, 0x58, 0xab, 0x7c, 0xe7, 0xb8, 0x67, 0x22, 0xb2, 0x76, 0x1f, - 0x56, 0xa2, 0x18, 0xc4, 0x88, 0x06, 0xf8, 0x2d, 0xdc, 0x51, 0x39, 0xa5, 0xed, 0xc1, 0x80, 0xf9, - 0x3f, 0x6d, 0x3f, 0xf3, 0x8b, 0xf7, 0xae, 0x00, 0xfd, 0xc9, 0xea, 0xa4, 0x05, 0x00, 0xd6, 0xbe, - 0x97, 0x81, 0xf5, 0x30, 0xe4, 0x82, 0xd8, 0x43, 0xbf, 0xb6, 0xfe, 0xbf, 0x7a, 0xc4, 0x3f, 0x55, - 0xc8, 0x8d, 0xc9, 0x0e, 0xce, 0x70, 0x8b, 0xdb, 0x83, 0xcd, 0x69, 0xf8, 0xe4, 0x79, 0x58, 0xc4, - 0x28, 0x5d, 0x43, 0xa7, 0xe3, 0xca, 0x6c, 0xb6, 0x2f, 0x80, 0x66, 0x58, 0xae, 0xfd, 0x4c, 0x81, - 0x2d, 0xee, 0xb5, 0x53, 0x73, 0xbc, 0x3e, 0xbe, 0xdf, 0x74, 0xdc, 0x0f, 0xc7, 0x7f, 0x7d, 0x2f, - 0xc2, 0xc7, 0xae, 0x47, 0x9d, 0xb3, 0x12, 0x5f, 0x9b, 0xde, 0x5b, 0x72, 0x0b, 0x23, 0xcf, 0x71, - 0xfb, 0x86, 0x1c, 0x8b, 0x17, 0xd2, 0xa7, 0x00, 0x39, 0x5e, 0x08, 0x62, 0x68, 0xff, 0x07, 0x2e, - 0xcf, 0xfe, 0x00, 0xf9, 0x32, 0x2c, 0x63, 0x2a, 0xb5, 0xf6, 0xf0, 0x70, 0xe4, 0x74, 0x5d, 0xa1, - 0xd9, 0x13, 0x0a, 0x68, 0xb9, 0x8c, 0x05, 0xd2, 0xe3, 0xf1, 0x2b, 0x0e, 0x31, 0x49, 0x1b, 0xaf, - 0x14, 0x71, 0x8d, 0x93, 0xa9, 0x69, 0xdf, 0x52, 0x80, 0x24, 0x69, 0x90, 0x4f, 0xc2, 0x52, 0xbb, - 0x55, 0xb2, 0xc6, 0xce, 0x68, 0x5c, 0x1e, 0x4c, 0x46, 0x3c, 0x8a, 0x1d, 0x0b, 0x67, 0x30, 0xee, - 0xd8, 0xec, 0xa5, 0xee, 0x68, 0x30, 0x19, 0x99, 0x11, 0x3c, 0x4c, 0xd9, 0xe5, 0xba, 0x6f, 0x75, - 0x9d, 0x93, 0x68, 0xca, 0x2e, 0x0e, 0x8b, 0xa4, 0xec, 0xe2, 0x30, 0xed, 0x5d, 0x05, 0x2e, 0x08, - 0xb3, 0xd5, 0x6e, 0x4a, 0x5b, 0x4a, 0x18, 0xb4, 0x67, 0x24, 0xc2, 0x26, 0xcf, 0x92, 0xd0, 0xd7, - 0x44, 0x5c, 0x2b, 0x6c, 0x20, 0x8a, 0xea, 0xac, 0x2e, 0xf9, 0x1c, 0xe4, 0xac, 0xf1, 0x60, 0x78, - 0x8a, 0xc0, 0x56, 0x6a, 0x30, 0xa3, 0xe3, 0xc1, 0x10, 0x49, 0x60, 0x4d, 0xcd, 0x85, 0x75, 0xb9, - 0x71, 0xa2, 0xc5, 0xa4, 0x06, 0x0b, 0x3c, 0x82, 0x61, 0xcc, 0x22, 0x64, 0x46, 0x9f, 0xb6, 0x57, - 0x45, 0xf4, 0x2c, 0x1e, 0xb6, 0xd7, 0x14, 0x34, 0xb4, 0xdf, 0x51, 0xa0, 0x40, 0x05, 0x1b, 0xbc, - 0x94, 0x7e, 0xd0, 0x25, 0x1d, 0x95, 0x83, 0x85, 0x81, 0x53, 0x40, 0xfe, 0x54, 0xa7, 0xf1, 0x2b, - 0xb0, 0x1a, 0xab, 0x40, 0x34, 0x8c, 0x9b, 0xd2, 0xf3, 0x3a, 0x0e, 0xcb, 0x00, 0xc4, 0x8c, 0x83, - 0x22, 0x30, 0xed, 0xff, 0x29, 0xb0, 0xde, 0x78, 0x6b, 0xec, 0xb0, 0x07, 0x75, 0x73, 0xd2, 0x13, - 0xfb, 0x9d, 0x0a, 0x6b, 0xc2, 0xfe, 0x99, 0xc5, 0x74, 0x60, 0xc2, 0x1a, 0x87, 0x99, 0x41, 0x29, - 0x29, 0x43, 0x9e, 0x9f, 0x2f, 0x3e, 0x8f, 0xb6, 0x7b, 0x59, 0xd2, 0x8d, 0x84, 0x84, 0x39, 0x12, - 0xed, 0x09, 0xb2, 0x30, 0x5e, 0xc7, 0x0c, 0x6a, 0x6b, 0xff, 0xaa, 0xc0, 0xc6, 0x94, 0x3a, 0xe4, - 0x0d, 0x98, 0x43, 0x7f, 0x53, 0x3e, 0x7b, 0x17, 0xa7, 0x7c, 0x62, 0xdc, 0x39, 0xda, 0xbf, 0xcd, - 0x0e, 0xa2, 0x63, 0xfa, 0xc3, 0x64, 0xb5, 0xc8, 0x43, 0x58, 0xd4, 0xbb, 0x5d, 0x7e, 0x3b, 0xcb, - 0x44, 0x6e, 0x67, 0x53, 0xbe, 0xf8, 0x62, 0x80, 0xcf, 0x6e, 0x67, 0xcc, 0xf3, 0xa9, 0xdb, 0xb5, - 0xb9, 0x2f, 0x6d, 0x48, 0x6f, 0xeb, 0x33, 0xb0, 0x12, 0x45, 0x7e, 0x2a, 0xf7, 0xbf, 0x77, 0x14, - 0x50, 0xa3, 0x6d, 0xf8, 0x68, 0xe2, 0x7e, 0xa5, 0x4d, 0xf3, 0x13, 0x16, 0xd5, 0xef, 0x65, 0xe0, - 0x5c, 0xea, 0x08, 0x93, 0x17, 0x60, 0x5e, 0x1f, 0x0e, 0x2b, 0x3b, 0x7c, 0x55, 0x71, 0x09, 0x09, - 0x95, 0xde, 0x91, 0xcb, 0x2b, 0x43, 0x22, 0x2f, 0x43, 0x9e, 0xd9, 0x6d, 0xec, 0x08, 0x86, 0x83, - 0x81, 0x8c, 0xb8, 0x51, 0x49, 0x34, 0xee, 0xad, 0x40, 0x24, 0xbb, 0xb0, 0xc2, 0x43, 0x00, 0x99, - 0xee, 0xa1, 0xfb, 0x8d, 0x20, 0x01, 0x03, 0xe6, 0x88, 0x10, 0x9a, 0x74, 0x7b, 0xc4, 0xca, 0xe4, - 0x20, 0x38, 0xd1, 0x5a, 0xa4, 0x0a, 0x2a, 0xd2, 0x94, 0x29, 0xb1, 0xe0, 0xbb, 0x18, 0x94, 0x89, - 0x35, 0x62, 0x0a, 0xad, 0x44, 0xcd, 0x60, 0xba, 0x74, 0xdf, 0xf7, 0x0e, 0xfb, 0xc7, 0x6e, 0x7f, - 0xfc, 0xd1, 0x4d, 0x57, 0xf8, 0x8d, 0x53, 0x4d, 0xd7, 0x1f, 0xe4, 0xd8, 0x66, 0x8e, 0x57, 0xa3, - 0x12, 0x8d, 0x14, 0x6f, 0x1d, 0x25, 0x1a, 0x7a, 0x3f, 0xe3, 0x41, 0x6e, 0x76, 0x60, 0x81, 0x05, - 0x1f, 0x12, 0x3b, 0xe3, 0x52, 0x6a, 0x13, 0x18, 0xce, 0xfe, 0x6d, 0x26, 0xbe, 0x30, 0xc7, 0x57, - 0xdf, 0x14, 0x55, 0xc9, 0x3e, 0x14, 0x4a, 0x3d, 0xd7, 0xe9, 0x4f, 0x86, 0xad, 0xd3, 0x3d, 0x1a, - 0x6f, 0xf2, 0xbe, 0x2c, 0x75, 0x58, 0x35, 0x7c, 0x6c, 0x46, 0x4e, 0x2e, 0x13, 0x22, 0xad, 0xc0, - 0x17, 0x2e, 0x87, 0x8a, 0xd7, 0x97, 0x66, 0x8c, 0x4f, 0x1c, 0x88, 0xf5, 0xa2, 0x8e, 0x9e, 0xdc, - 0x59, 0xce, 0x86, 0x95, 0xaa, 0xe3, 0x8f, 0x5b, 0x23, 0xa7, 0xef, 0x63, 0xd0, 0xd2, 0x53, 0x04, - 0x75, 0xbb, 0x20, 0x12, 0x72, 0xa3, 0xca, 0x74, 0x1c, 0x54, 0x65, 0x0a, 0xd9, 0x28, 0x39, 0x2a, - 0x2f, 0xed, 0x7a, 0x7d, 0xa7, 0xe7, 0x7d, 0x53, 0xb8, 0x0c, 0x33, 0x79, 0xe9, 0x40, 0x00, 0xcd, - 0xb0, 0x5c, 0xfb, 0x52, 0x62, 0xde, 0x58, 0x2b, 0x0b, 0xb0, 0xc0, 0x03, 0x4a, 0xb0, 0x00, 0x0b, - 0x4d, 0xa3, 0xbe, 0x53, 0xa9, 0xef, 0xa9, 0x0a, 0x59, 0x01, 0x68, 0x9a, 0x8d, 0x92, 0x61, 0x59, - 0xf4, 0x77, 0x86, 0xfe, 0xe6, 0xd1, 0x17, 0x76, 0xdb, 0x55, 0x35, 0x2b, 0x05, 0x60, 0xc8, 0x69, - 0x3f, 0x55, 0xe0, 0x7c, 0xfa, 0x54, 0x92, 0x16, 0x60, 0x08, 0x0e, 0x6e, 0x3e, 0xf0, 0xc9, 0x99, - 0xf3, 0x9e, 0x0a, 0x8e, 0x87, 0xf2, 0x18, 0xb3, 0x10, 0x11, 0x19, 0xf1, 0xf6, 0xc5, 0x7c, 0x4e, - 0xbd, 0xae, 0x99, 0xf1, 0xba, 0x5a, 0x09, 0x36, 0xa7, 0xd1, 0x88, 0x76, 0x75, 0x15, 0x0a, 0x7a, - 0xb3, 0x59, 0xad, 0x94, 0xf4, 0x56, 0xa5, 0x51, 0x57, 0x15, 0xb2, 0x08, 0x73, 0x7b, 0x66, 0xa3, - 0xdd, 0x54, 0x33, 0xda, 0xf7, 0x15, 0x58, 0xae, 0x84, 0xf6, 0x80, 0x1f, 0x74, 0xf3, 0x7d, 0x2a, - 0xb2, 0xf9, 0x36, 0x83, 0x60, 0x35, 0xc1, 0x07, 0x4e, 0xb5, 0xf3, 0xde, 0xcf, 0xc0, 0x5a, 0xa2, - 0x0e, 0xb1, 0x60, 0x41, 0xbf, 0x6f, 0x35, 0x2a, 0x3b, 0x25, 0xde, 0xb2, 0x2b, 0xa1, 0x21, 0x1b, - 0xa6, 0x2f, 0x4b, 0x7c, 0x85, 0x39, 0x78, 0x3f, 0xf6, 0xed, 0x81, 0xd7, 0x95, 0x72, 0x19, 0x97, - 0xcf, 0x98, 0x82, 0x12, 0x9e, 0x64, 0xdf, 0x9c, 0x8c, 0x5c, 0x24, 0x9b, 0x89, 0xe8, 0x75, 0x03, - 0x78, 0x92, 0x30, 0x7a, 0xd6, 0x38, 0xb4, 0x3c, 0x49, 0x3a, 0xa4, 0x47, 0xea, 0x30, 0xbf, 0xe7, - 0x8d, 0xcb, 0x93, 0x47, 0x7c, 0xff, 0x5e, 0x0e, 0x93, 0x59, 0x95, 0x27, 0x8f, 0x92, 0x64, 0x51, - 0x65, 0xc9, 0x82, 0x31, 0x45, 0x48, 0x72, 0x2a, 0x71, 0x9f, 0xd4, 0xdc, 0x53, 0xf9, 0xa4, 0x6e, - 0x2f, 0x43, 0x81, 0xdf, 0xa1, 0xf0, 0x7a, 0xf2, 0x63, 0x05, 0x36, 0xa7, 0x8d, 0x1c, 0xbd, 0x96, - 0x45, 0x63, 0x4f, 0x9c, 0x0f, 0xb2, 0x9d, 0x44, 0x83, 0x4e, 0x08, 0x34, 0xf2, 0x26, 0x14, 0x98, - 0x95, 0x96, 0xf5, 0x72, 0xdb, 0xac, 0xf0, 0xe5, 0x7a, 0xe9, 0x9f, 0xde, 0xbb, 0xb2, 0xc1, 0x0d, - 0xbb, 0xfc, 0x97, 0xed, 0xc9, 0xc8, 0x8b, 0x64, 0x86, 0x90, 0x6b, 0x50, 0x29, 0xda, 0x99, 0x74, - 0x3d, 0x57, 0xdc, 0x21, 0x84, 0x7f, 0x3e, 0x87, 0xc9, 0x67, 0x9a, 0x80, 0x69, 0xdf, 0x51, 0x60, - 0x6b, 0xfa, 0x34, 0xd1, 0x73, 0xb2, 0xc5, 0x8c, 0xdd, 0x84, 0x87, 0x3c, 0x9e, 0x93, 0x81, 0x45, - 0x9c, 0x4c, 0x53, 0x20, 0xd2, 0x4a, 0x5c, 0xc3, 0x25, 0x94, 0x24, 0x72, 0x7a, 0xf3, 0x68, 0x25, - 0x81, 0xa8, 0x3d, 0x80, 0x8d, 0x29, 0x93, 0x4a, 0x3e, 0x9b, 0x9a, 0x43, 0x09, 0x1d, 0xc8, 0xe4, - 0x1c, 0x4a, 0x91, 0x64, 0x7c, 0x12, 0x5c, 0xfb, 0xe7, 0x0c, 0x9c, 0xa7, 0xbb, 0xab, 0xe7, 0xfa, - 0xbe, 0x1e, 0xa6, 0x1b, 0xa6, 0x5c, 0xf1, 0x55, 0x98, 0x3f, 0x7a, 0x3a, 0x55, 0x31, 0x43, 0x27, - 0x04, 0xf0, 0xc4, 0x12, 0x6e, 0x4b, 0xf4, 0x7f, 0x72, 0x15, 0xe4, 0x5c, 0xf5, 0x59, 0x8c, 0x56, - 0x9b, 0xd9, 0x54, 0xcc, 0xc5, 0x61, 0x90, 0x56, 0xfa, 0x35, 0x98, 0x43, 0x7d, 0x0a, 0x3f, 0x3b, - 0x84, 0xcc, 0x9f, 0xde, 0x3a, 0xd4, 0xb6, 0x98, 0xac, 0x02, 0xf9, 0x04, 0x40, 0x98, 0xe8, 0x83, - 0x1f, 0x0e, 0x42, 0xcf, 0x10, 0xe4, 0xfa, 0x30, 0x17, 0x8f, 0x0f, 0x1c, 0x9e, 0x3d, 0xa3, 0x08, - 0x6b, 0x62, 0xc4, 0x87, 0x22, 0xc8, 0x25, 0x7f, 0xc5, 0x5c, 0x65, 0x05, 0x95, 0xa1, 0x08, 0x74, - 0x79, 0x2d, 0x91, 0x6f, 0x1b, 0x63, 0x5d, 0xc7, 0x92, 0x6a, 0x5f, 0x4b, 0x24, 0xd5, 0xce, 0x33, - 0x2c, 0x39, 0x73, 0xb6, 0xf6, 0xf7, 0x19, 0x58, 0xbc, 0x4f, 0xa5, 0x32, 0xd4, 0x35, 0xcc, 0xd6, - 0x5d, 0xdc, 0x81, 0x42, 0x75, 0xe0, 0xf0, 0xe7, 0x22, 0xee, 0xed, 0xc3, 0x5c, 0xf4, 0x7b, 0x03, - 0x47, 0xbc, 0x3c, 0xf9, 0xa6, 0x8c, 0xf4, 0x84, 0xf0, 0x02, 0x77, 0x61, 0x9e, 0x3d, 0xdf, 0x71, - 0x35, 0x9a, 0x90, 0xcb, 0x83, 0x16, 0xbd, 0xc8, 0x8a, 0xa5, 0x17, 0x0e, 0xf6, 0x04, 0x28, 0x0b, - 0x89, 0x3c, 0x64, 0xaf, 0xa4, 0x59, 0x99, 0x3b, 0x9d, 0x66, 0x45, 0x0a, 0x4d, 0x38, 0x7f, 0x9a, - 0xd0, 0x84, 0x5b, 0xaf, 0x43, 0x41, 0x6a, 0xcf, 0x53, 0x89, 0xe9, 0xdf, 0xce, 0xc0, 0x32, 0xf6, - 0x2a, 0xb0, 0xe5, 0xf9, 0xf5, 0xd4, 0x13, 0x7d, 0x2a, 0xa2, 0x27, 0xda, 0x94, 0xe7, 0x8b, 0xf5, - 0x6c, 0x86, 0x82, 0xe8, 0x2e, 0xac, 0x25, 0x10, 0xc9, 0x2b, 0x30, 0x47, 0x9b, 0x2f, 0xee, 0xd5, - 0x6a, 0x7c, 0x05, 0x84, 0x61, 0xac, 0x69, 0xc7, 0x7d, 0x93, 0x61, 0x6b, 0xff, 0xa6, 0xc0, 0x12, - 0xcf, 0x22, 0xd3, 0x3f, 0x18, 0x3c, 0x71, 0x38, 0x6f, 0xc4, 0x87, 0x93, 0x05, 0xcb, 0xe1, 0xc3, - 0xf9, 0x5f, 0x3d, 0x88, 0xaf, 0x47, 0x06, 0x71, 0x23, 0x08, 0x6a, 0x29, 0xba, 0x33, 0x63, 0x0c, - 0x7f, 0x84, 0x61, 0x9e, 0xa3, 0x88, 0xe4, 0x2b, 0xb0, 0x58, 0x77, 0x1f, 0x47, 0xae, 0xa7, 0x37, - 0xa6, 0x10, 0x7d, 0x31, 0x40, 0x64, 0x7b, 0x0a, 0x4f, 0xf6, 0xbe, 0xfb, 0xd8, 0x4e, 0xbc, 0x1c, - 0x86, 0x24, 0xe9, 0x0d, 0x35, 0x5a, 0xed, 0x69, 0x96, 0x3e, 0x77, 0x3d, 0xc6, 0xf8, 0x4f, 0xdf, - 0xcd, 0x02, 0x84, 0x5e, 0x9b, 0x74, 0x03, 0x46, 0x8c, 0x26, 0x84, 0x66, 0x1f, 0x41, 0xf2, 0x1a, - 0x17, 0xb6, 0x14, 0x37, 0xb8, 0x06, 0x3a, 0x33, 0x3d, 0xe8, 0x28, 0xea, 0xa2, 0x4b, 0xdc, 0x4d, - 0xb0, 0xeb, 0xf6, 0x1c, 0xc6, 0xdb, 0xb3, 0xdb, 0xd7, 0x30, 0xc6, 0x74, 0x00, 0x9d, 0x92, 0x3d, - 0x1c, 0x9d, 0x09, 0x77, 0x28, 0x42, 0xc2, 0x13, 0x3a, 0xf7, 0x74, 0x9e, 0xd0, 0x4d, 0x58, 0xf4, - 0xfa, 0x6f, 0xbb, 0xfd, 0xf1, 0x60, 0x74, 0x82, 0x6a, 0xf7, 0x50, 0x9f, 0x47, 0x87, 0xa0, 0x22, - 0xca, 0xd8, 0x3c, 0xe0, 0x99, 0x1b, 0xe0, 0xcb, 0xd3, 0x10, 0x00, 0x03, 0x4f, 0xee, 0x39, 0x75, - 0xfe, 0x6e, 0x2e, 0x3f, 0xaf, 0x2e, 0xdc, 0xcd, 0xe5, 0xf3, 0xea, 0xe2, 0xdd, 0x5c, 0x7e, 0x51, - 0x05, 0x53, 0x7a, 0x33, 0x0b, 0xde, 0xc4, 0xa4, 0x67, 0xac, 0xe8, 0x13, 0x95, 0xf6, 0xcb, 0x0c, - 0x90, 0x64, 0x33, 0xc8, 0xa7, 0xa0, 0xc0, 0x18, 0xac, 0x3d, 0xf2, 0xbf, 0xce, 0x1d, 0x41, 0x58, - 0x14, 0x2d, 0x09, 0x2c, 0x47, 0xd1, 0x62, 0x60, 0xd3, 0xff, 0x7a, 0x8f, 0x7c, 0x19, 0xce, 0xe2, - 0xf0, 0x0e, 0xdd, 0x91, 0x37, 0xe8, 0xda, 0x18, 0xf2, 0xd8, 0xe9, 0xf1, 0x4c, 0x9f, 0x2f, 0x60, - 0x4a, 0xea, 0x64, 0xf1, 0x94, 0x69, 0x40, 0xe7, 0xcc, 0x26, 0x62, 0x36, 0x19, 0x22, 0x69, 0x81, - 0x2a, 0xd7, 0x3f, 0x98, 0xf4, 0x7a, 0x7c, 0x66, 0x8b, 0xf4, 0x46, 0x1f, 0x2f, 0x9b, 0x42, 0x78, - 0x25, 0x24, 0xbc, 0x3b, 0xe9, 0xf5, 0xc8, 0xab, 0x00, 0x83, 0xbe, 0x7d, 0xec, 0xf9, 0x3e, 0x7b, - 0xcc, 0x09, 0xfc, 0xc8, 0x43, 0xa8, 0x3c, 0x19, 0x83, 0x7e, 0x8d, 0x01, 0xc9, 0xff, 0x02, 0x0c, - 0xbe, 0x81, 0x51, 0x69, 0x98, 0x35, 0x12, 0x4f, 0xc6, 0x23, 0x80, 0x51, 0xb7, 0xf5, 0x43, 0xd7, - 0xf2, 0xbe, 0x29, 0x9c, 0x70, 0xbe, 0x08, 0x6b, 0xdc, 0x5e, 0xfa, 0xbe, 0x37, 0x3e, 0xe2, 0x57, - 0x89, 0x0f, 0x72, 0x0f, 0x91, 0xee, 0x12, 0x7f, 0x9d, 0x03, 0xd0, 0xef, 0x5b, 0x22, 0xe0, 0xdb, - 0x2d, 0x98, 0xa3, 0x17, 0x24, 0xa1, 0x68, 0x41, 0x35, 0x35, 0xd2, 0x95, 0xd5, 0xd4, 0x88, 0x41, - 0x77, 0xa3, 0x89, 0xee, 0x0e, 0x42, 0xc9, 0x82, 0xbb, 0x91, 0x79, 0x40, 0x44, 0x02, 0x6e, 0x73, - 0x2c, 0x52, 0x05, 0x08, 0x43, 0xb0, 0x71, 0x91, 0x7f, 0x2d, 0x8c, 0x65, 0xc4, 0x0b, 0x78, 0xd2, - 0x8f, 0x30, 0x8c, 0x9b, 0xbc, 0x7c, 0x42, 0x34, 0x72, 0x0f, 0x72, 0x2d, 0x27, 0xf0, 0x92, 0x9e, - 0x12, 0x98, 0xee, 0x59, 0x9e, 0x89, 0x35, 0x0c, 0x4e, 0xb7, 0x32, 0x76, 0x22, 0x09, 0xab, 0x91, - 0x08, 0x31, 0x60, 0x9e, 0x67, 0xd9, 0x9f, 0x12, 0xd0, 0x94, 0x27, 0xd9, 0xe7, 0x61, 0xcc, 0x11, - 0x28, 0xcb, 0x14, 0x3c, 0x9f, 0xfe, 0x1d, 0xc8, 0x5a, 0x56, 0x8d, 0x87, 0x63, 0x59, 0x0e, 0xaf, - 0x5f, 0x96, 0x55, 0x63, 0xef, 0xbe, 0xbe, 0x7f, 0x2c, 0x55, 0xa3, 0xc8, 0xe4, 0xd3, 0x50, 0x90, - 0x84, 0x62, 0x1e, 0xc8, 0x08, 0xc7, 0x40, 0xf2, 0x43, 0x93, 0x99, 0x86, 0x84, 0x4d, 0xaa, 0xa0, - 0xde, 0x9b, 0x3c, 0x72, 0xf5, 0xe1, 0x10, 0x1d, 0x54, 0xdf, 0x76, 0x47, 0x4c, 0x6c, 0xcb, 0x87, - 0x11, 0xc0, 0xd1, 0x7b, 0xa5, 0x2b, 0x4a, 0x65, 0x65, 0x53, 0xbc, 0x26, 0x69, 0xc2, 0x9a, 0xe5, - 0x8e, 0x27, 0x43, 0x66, 0x5f, 0xb3, 0x3b, 0x18, 0xd1, 0xfb, 0x0d, 0x0b, 0x7b, 0x84, 0xc1, 0x92, - 0x7d, 0x5a, 0x28, 0x8c, 0x9a, 0x0e, 0x06, 0xa3, 0xd8, 0x5d, 0x27, 0x59, 0x59, 0x73, 0xe5, 0x29, - 0xa7, 0xa7, 0x6a, 0xf4, 0xd6, 0x84, 0xa7, 0xaa, 0xb8, 0x35, 0x85, 0x77, 0xa5, 0x4f, 0xa4, 0x84, - 0xe6, 0xc3, 0x97, 0x41, 0x29, 0x34, 0x5f, 0x24, 0x20, 0xdf, 0xbb, 0x39, 0x29, 0x3a, 0x2c, 0x9f, - 0x8b, 0x37, 0x00, 0xee, 0x0e, 0xbc, 0x7e, 0xcd, 0x1d, 0x1f, 0x0d, 0xba, 0x52, 0x84, 0xc0, 0xc2, - 0xd7, 0x06, 0x5e, 0xdf, 0x3e, 0x46, 0xf0, 0x2f, 0xdf, 0xbb, 0x22, 0x21, 0x99, 0xd2, 0xff, 0xe4, - 0xe3, 0xb0, 0x48, 0x7f, 0xb5, 0x42, 0x2b, 0x21, 0xa6, 0x93, 0xc5, 0xda, 0x2c, 0x87, 0x4a, 0x88, - 0x40, 0x5e, 0xc7, 0xac, 0x41, 0xde, 0x70, 0x2c, 0x09, 0xaf, 0x22, 0x45, 0x90, 0x37, 0x1c, 0xc7, - 0x03, 0x7e, 0x4b, 0xc8, 0xa4, 0x1c, 0x34, 0x5d, 0x24, 0xfa, 0xe2, 0xc9, 0x89, 0x50, 0xf1, 0xc8, - 0xd7, 0x9a, 0x2d, 0x22, 0x0d, 0xcb, 0x19, 0x9c, 0x63, 0xd5, 0xb0, 0x11, 0x56, 0x79, 0x87, 0xbd, - 0x14, 0x71, 0xa1, 0x96, 0x35, 0xc2, 0x3f, 0xea, 0xda, 0x1d, 0x04, 0x47, 0x1a, 0x11, 0x20, 0x93, - 0x6d, 0x58, 0x65, 0x32, 0x7e, 0x90, 0x30, 0x94, 0x8b, 0xb8, 0xc8, 0xdb, 0xc2, 0x8c, 0xa2, 0xf2, - 0xe7, 0x63, 0x15, 0xc8, 0x2e, 0xcc, 0xe1, 0x5d, 0x93, 0x7b, 0x43, 0x5c, 0x90, 0xd5, 0x04, 0xf1, - 0x7d, 0x84, 0x7c, 0x05, 0x15, 0x04, 0x32, 0x5f, 0x41, 0x54, 0xf2, 0x05, 0x00, 0xa3, 0x3f, 0x1a, - 0xf4, 0x7a, 0x18, 0x0b, 0x3b, 0x8f, 0x57, 0xa9, 0x4b, 0xd1, 0xfd, 0x88, 0x54, 0x42, 0x24, 0x1e, - 0xb7, 0x11, 0x7f, 0xdb, 0xb1, 0x88, 0xd9, 0x12, 0x2d, 0xad, 0x02, 0xf3, 0x6c, 0x33, 0x62, 0x5c, - 0x79, 0x9e, 0x29, 0x47, 0x8a, 0x4a, 0xce, 0xe2, 0xca, 0x73, 0x78, 0x32, 0xae, 0xbc, 0x54, 0x41, - 0xbb, 0x07, 0xeb, 0x69, 0x1d, 0x8b, 0xdc, 0x8e, 0x95, 0xd3, 0xde, 0x8e, 0x7f, 0x98, 0x85, 0x25, - 0xa4, 0x26, 0xb8, 0xb0, 0x0e, 0xcb, 0xd6, 0xe4, 0x51, 0x10, 0x74, 0x4d, 0x70, 0x63, 0x6c, 0x9f, - 0x2f, 0x17, 0xc8, 0x6f, 0x78, 0x91, 0x1a, 0xc4, 0x80, 0x15, 0x71, 0x12, 0xec, 0x09, 0xcf, 0x81, - 0x20, 0xa4, 0xbb, 0x70, 0xa8, 0x48, 0x26, 0x4c, 0x8e, 0x55, 0x0a, 0xcf, 0x83, 0xec, 0xd3, 0x9c, - 0x07, 0xb9, 0x53, 0x9d, 0x07, 0x0f, 0x61, 0x49, 0x7c, 0x0d, 0x39, 0xf9, 0xdc, 0x07, 0xe3, 0xe4, - 0x11, 0x62, 0xa4, 0x1a, 0x70, 0xf4, 0xf9, 0x99, 0x1c, 0x1d, 0x1f, 0x46, 0xc5, 0x2e, 0x1b, 0x22, - 0x2c, 0xc9, 0xd8, 0x31, 0xa3, 0xe8, 0x5e, 0xa9, 0xf9, 0x2b, 0x9c, 0x92, 0xaf, 0xc0, 0x62, 0x75, - 0x20, 0xde, 0xc4, 0xa4, 0xc7, 0x88, 0x9e, 0x00, 0xca, 0xe2, 0x42, 0x80, 0x19, 0x9c, 0x6e, 0xd9, - 0x0f, 0xe3, 0x74, 0x7b, 0x1d, 0x80, 0xbb, 0xa4, 0x84, 0x99, 0x00, 0x71, 0xcb, 0x88, 0xd8, 0x31, - 0xd1, 0x37, 0x11, 0x09, 0x99, 0x72, 0x27, 0x6e, 0x6e, 0xa3, 0x77, 0x3a, 0x83, 0x49, 0x7f, 0x1c, - 0x49, 0x9d, 0x2d, 0x7c, 0x8b, 0x1d, 0x5e, 0x26, 0xb3, 0x87, 0x58, 0xb5, 0x0f, 0x77, 0x42, 0xc8, - 0xe7, 0x03, 0xe3, 0xc7, 0x85, 0x59, 0x23, 0xa4, 0x25, 0x46, 0x68, 0xaa, 0xc9, 0xa3, 0xf6, 0x53, - 0x45, 0xce, 0xa7, 0xf1, 0x2b, 0x4c, 0xf5, 0x6b, 0x00, 0x81, 0x51, 0x82, 0x98, 0x6b, 0x76, 0x5f, - 0x0a, 0xa0, 0xf2, 0x28, 0x87, 0xb8, 0x52, 0x6f, 0xb2, 0x1f, 0x56, 0x6f, 0x5a, 0x50, 0x68, 0xbc, - 0x35, 0x76, 0x42, 0x2b, 0x16, 0xb0, 0x02, 0x49, 0x16, 0x39, 0x53, 0x76, 0xfb, 0x3a, 0x9e, 0x0d, - 0xa1, 0x1c, 0x3c, 0x45, 0x04, 0x96, 0x2a, 0x6a, 0xff, 0xa1, 0xc0, 0xaa, 0x1c, 0x10, 0xe1, 0xa4, - 0xdf, 0x21, 0x9f, 0x65, 0xe1, 0x7d, 0x95, 0xc8, 0x95, 0x45, 0x42, 0xa2, 0x2c, 0xf7, 0xa4, 0xdf, - 0x61, 0x02, 0x90, 0xf3, 0x58, 0x6e, 0x2c, 0xad, 0x48, 0x1e, 0xc1, 0x52, 0x73, 0xd0, 0xeb, 0x51, - 0xb1, 0x66, 0xf4, 0x36, 0xbf, 0x00, 0x50, 0x42, 0xf1, 0xa7, 0x11, 0xd1, 0xa0, 0xed, 0xe7, 0xf8, - 0x3d, 0x77, 0x63, 0x48, 0xf9, 0xbd, 0xc7, 0xeb, 0x85, 0x64, 0xdf, 0x41, 0xd7, 0x40, 0x99, 0x66, - 0x78, 0x36, 0x45, 0xf3, 0x42, 0xc8, 0xad, 0xa4, 0xc5, 0xd8, 0xce, 0x19, 0x67, 0x93, 0xf6, 0x73, - 0x05, 0x48, 0xb2, 0x6b, 0x32, 0xeb, 0x53, 0xfe, 0x1b, 0x44, 0xe1, 0x98, 0x08, 0x99, 0x7b, 0x1a, - 0x11, 0x52, 0xfb, 0x81, 0x02, 0xeb, 0x69, 0xe3, 0x40, 0x4f, 0x10, 0xf9, 0x48, 0x09, 0x0e, 0x34, - 0x3c, 0x41, 0xe4, 0x53, 0x28, 0x7a, 0xac, 0xc5, 0x2a, 0xc5, 0x1b, 0x97, 0x79, 0x9a, 0xc6, 0x15, - 0x7f, 0x57, 0x81, 0xd5, 0x8a, 0x5e, 0xe3, 0x69, 0x44, 0xd8, 0x33, 0xd5, 0x55, 0xb8, 0x54, 0xd1, - 0x6b, 0x76, 0xb3, 0x51, 0xad, 0x94, 0x1e, 0xd8, 0xa9, 0xd1, 0xc1, 0x2f, 0xc1, 0x33, 0x49, 0x94, - 0xf0, 0x39, 0xeb, 0x22, 0x6c, 0x26, 0x8b, 0x45, 0x04, 0xf1, 0xf4, 0xca, 0x22, 0xd8, 0x78, 0xb6, - 0xf8, 0x26, 0xac, 0x8a, 0x68, 0xd9, 0xad, 0xaa, 0x85, 0xf9, 0x38, 0x56, 0xa1, 0xb0, 0x6f, 0x98, - 0x95, 0xdd, 0x07, 0xf6, 0x6e, 0xbb, 0x5a, 0x55, 0xcf, 0x90, 0x65, 0x58, 0xe4, 0x80, 0x92, 0xae, - 0x2a, 0x64, 0x09, 0xf2, 0x95, 0xba, 0x65, 0x94, 0xda, 0xa6, 0xa1, 0x66, 0x8a, 0x6f, 0xc2, 0x4a, - 0x73, 0xe4, 0xbd, 0xed, 0x8c, 0xdd, 0x7b, 0xee, 0x09, 0xbe, 0x46, 0x2d, 0x40, 0xd6, 0xd4, 0xef, - 0xab, 0x67, 0x08, 0xc0, 0x7c, 0xf3, 0x5e, 0xc9, 0xba, 0x7d, 0x5b, 0x55, 0x48, 0x01, 0x16, 0xf6, - 0x4a, 0x4d, 0xfb, 0x5e, 0xcd, 0x52, 0x33, 0xf4, 0x87, 0x7e, 0xdf, 0xc2, 0x1f, 0xd9, 0xe2, 0x4b, - 0xb0, 0x86, 0x52, 0x57, 0xd5, 0xf3, 0xc7, 0x6e, 0xdf, 0x1d, 0x61, 0x1b, 0x96, 0x20, 0x6f, 0xb9, - 0x94, 0x5d, 0x8e, 0x5d, 0xd6, 0x80, 0xda, 0xa4, 0x37, 0xf6, 0x86, 0x3d, 0xf7, 0x1b, 0xaa, 0x52, - 0x7c, 0x1d, 0x56, 0xcd, 0xc1, 0x64, 0xec, 0xf5, 0x0f, 0xad, 0x31, 0xc5, 0x38, 0x3c, 0x21, 0xe7, - 0x60, 0xad, 0x5d, 0xd7, 0x6b, 0xdb, 0x95, 0xbd, 0x76, 0xa3, 0x6d, 0xd9, 0x35, 0xbd, 0x55, 0x2a, - 0xb3, 0xb7, 0xb0, 0x5a, 0xc3, 0x6a, 0xd9, 0xa6, 0x51, 0x32, 0xea, 0x2d, 0x55, 0x29, 0x7e, 0x0f, - 0x15, 0x48, 0x9d, 0x41, 0xbf, 0xbb, 0xeb, 0x74, 0xc6, 0x83, 0x11, 0x36, 0x58, 0x83, 0xcb, 0x96, - 0x51, 0x6a, 0xd4, 0x77, 0xec, 0x5d, 0xbd, 0xd4, 0x6a, 0x98, 0x69, 0xe1, 0xe9, 0xb7, 0xe0, 0x7c, - 0x0a, 0x4e, 0xa3, 0xd5, 0x54, 0x15, 0x72, 0x05, 0x2e, 0xa4, 0x94, 0xdd, 0x37, 0xb6, 0xf5, 0x76, - 0xab, 0x5c, 0x57, 0x33, 0x53, 0x2a, 0x5b, 0x56, 0x43, 0xcd, 0x16, 0xff, 0xbf, 0x02, 0x2b, 0x6d, - 0x9f, 0xdb, 0xd5, 0xb7, 0xd1, 0x8b, 0xf8, 0x59, 0xb8, 0xd8, 0xb6, 0x0c, 0xd3, 0x6e, 0x35, 0xee, - 0x19, 0x75, 0xbb, 0x6d, 0xe9, 0x7b, 0xf1, 0xd6, 0x5c, 0x81, 0x0b, 0x12, 0x86, 0x69, 0x94, 0x1a, - 0xfb, 0x86, 0x69, 0x37, 0x75, 0xcb, 0xba, 0xdf, 0x30, 0x77, 0x54, 0x85, 0x7e, 0x31, 0x05, 0xa1, - 0xb6, 0xab, 0xb3, 0xd6, 0x44, 0xca, 0xea, 0xc6, 0x7d, 0xbd, 0x6a, 0x6f, 0x37, 0x5a, 0x6a, 0xb6, - 0x58, 0xa3, 0x42, 0x0c, 0x06, 0x89, 0x66, 0xe6, 0x93, 0x79, 0xc8, 0xd5, 0x1b, 0x75, 0x23, 0xfe, - 0x82, 0xba, 0x04, 0x79, 0xbd, 0xd9, 0x34, 0x1b, 0xfb, 0xb8, 0xc4, 0x00, 0xe6, 0x77, 0x8c, 0x3a, - 0x6d, 0x59, 0x96, 0x96, 0x34, 0xcd, 0x46, 0xad, 0xd1, 0x32, 0x76, 0xd4, 0x5c, 0xd1, 0x14, 0xfc, - 0x45, 0x10, 0xed, 0x0c, 0xd8, 0x73, 0xe5, 0x8e, 0xb1, 0xab, 0xb7, 0xab, 0x2d, 0x3e, 0x45, 0x0f, - 0x6c, 0xd3, 0xf8, 0x7c, 0xdb, 0xb0, 0x5a, 0x96, 0xaa, 0x10, 0x15, 0x96, 0xea, 0x86, 0xb1, 0x63, - 0xd9, 0xa6, 0xb1, 0x5f, 0x31, 0xee, 0xab, 0x19, 0x4a, 0x93, 0xfd, 0x4f, 0xbf, 0x50, 0x7c, 0x57, - 0x01, 0xc2, 0x02, 0x6c, 0x8b, 0xac, 0x4d, 0xb8, 0x62, 0x2e, 0xc3, 0x56, 0x99, 0x4e, 0x35, 0x76, - 0xad, 0xd6, 0xd8, 0x89, 0x0f, 0xd9, 0x79, 0x20, 0xb1, 0xf2, 0xc6, 0xee, 0xae, 0xaa, 0x90, 0x0b, - 0x70, 0x36, 0x06, 0xdf, 0x31, 0x1b, 0x4d, 0x35, 0xb3, 0x95, 0xc9, 0x2b, 0x64, 0x23, 0x51, 0x78, - 0xcf, 0x30, 0x9a, 0x6a, 0x96, 0x4e, 0x51, 0xac, 0x40, 0x6c, 0x09, 0x56, 0x3d, 0x57, 0xfc, 0x8e, - 0x02, 0xe7, 0x59, 0x33, 0xc5, 0xfe, 0x0a, 0x9a, 0x7a, 0x11, 0x36, 0x79, 0xda, 0x80, 0xb4, 0x86, - 0xae, 0x83, 0x1a, 0x29, 0x65, 0xcd, 0x3c, 0x07, 0x6b, 0x11, 0x28, 0xb6, 0x23, 0x43, 0xb9, 0x47, - 0x04, 0xbc, 0x6d, 0x58, 0x2d, 0xdb, 0xd8, 0xdd, 0x6d, 0x98, 0x2d, 0xd6, 0x90, 0x6c, 0x51, 0x83, - 0xb5, 0x92, 0x3b, 0x1a, 0xd3, 0xfb, 0x65, 0xdf, 0xf7, 0x06, 0x7d, 0x6c, 0xc2, 0x32, 0x2c, 0x1a, - 0x5f, 0x68, 0x19, 0x75, 0xab, 0xd2, 0xa8, 0xab, 0x67, 0x8a, 0x17, 0x63, 0x38, 0x62, 0x1f, 0x5b, - 0x56, 0x59, 0x3d, 0x53, 0x74, 0x60, 0x59, 0x58, 0x97, 0xb3, 0x55, 0x71, 0x19, 0xb6, 0xc4, 0x5a, - 0x43, 0x8e, 0x12, 0xef, 0xc2, 0x26, 0xac, 0x27, 0xcb, 0x8d, 0x96, 0xaa, 0xd0, 0x59, 0x88, 0x95, - 0x50, 0x78, 0xa6, 0xf8, 0x7f, 0x15, 0x58, 0x0e, 0x5e, 0x86, 0x50, 0x17, 0x7d, 0x05, 0x2e, 0xd4, - 0x76, 0x75, 0x7b, 0xc7, 0xd8, 0xaf, 0x94, 0x0c, 0xfb, 0x5e, 0xa5, 0xbe, 0x13, 0xfb, 0xc8, 0x33, - 0x70, 0x2e, 0x05, 0x01, 0xbf, 0xb2, 0x09, 0xeb, 0xf1, 0xa2, 0x16, 0xdd, 0xaa, 0x19, 0x3a, 0xf4, - 0xf1, 0x92, 0x60, 0x9f, 0x66, 0x8b, 0xfb, 0xb0, 0x62, 0xe9, 0xb5, 0xea, 0xee, 0x60, 0xd4, 0x71, - 0xf5, 0xc9, 0xf8, 0xa8, 0x4f, 0x2e, 0xc0, 0xc6, 0x6e, 0xc3, 0x2c, 0x19, 0x36, 0xa2, 0xc4, 0x5a, - 0x70, 0x16, 0x56, 0xe5, 0xc2, 0x07, 0x06, 0x5d, 0xbe, 0x04, 0x56, 0x64, 0x60, 0xbd, 0xa1, 0x66, - 0x8a, 0x5f, 0x82, 0xa5, 0x48, 0xf2, 0xc6, 0x0d, 0x38, 0x2b, 0xff, 0x6e, 0xba, 0xfd, 0xae, 0xd7, - 0x3f, 0x54, 0xcf, 0xc4, 0x0b, 0xcc, 0x49, 0xbf, 0x4f, 0x0b, 0x70, 0x3f, 0xcb, 0x05, 0x2d, 0x77, - 0x74, 0xec, 0xf5, 0x9d, 0xb1, 0xdb, 0x55, 0x33, 0xc5, 0x17, 0x61, 0x39, 0x12, 0x32, 0x9e, 0x4e, - 0x5c, 0xb5, 0xc1, 0x19, 0x70, 0xcd, 0xd8, 0xa9, 0xb4, 0x6b, 0xea, 0x1c, 0xdd, 0xc9, 0xe5, 0xca, - 0x5e, 0x59, 0x85, 0xe2, 0xf7, 0x15, 0x7a, 0x99, 0xc2, 0x44, 0x50, 0xb5, 0x5d, 0x5d, 0x4c, 0x35, - 0x5d, 0x66, 0x2c, 0x11, 0x85, 0x61, 0x59, 0xcc, 0x70, 0xe0, 0x22, 0x6c, 0xf2, 0x1f, 0xb6, 0x5e, - 0xdf, 0xb1, 0xcb, 0xba, 0xb9, 0x73, 0x5f, 0x37, 0xe9, 0xda, 0x7b, 0xa0, 0x66, 0x70, 0x43, 0x49, - 0x10, 0xbb, 0xd5, 0x68, 0x97, 0xca, 0x6a, 0x96, 0xae, 0xdf, 0x08, 0xbc, 0x59, 0xa9, 0xab, 0x39, - 0xdc, 0x9e, 0x09, 0x6c, 0x24, 0x4b, 0xcb, 0xe7, 0x8a, 0xef, 0x2b, 0xb0, 0x61, 0x79, 0x87, 0x7d, - 0x67, 0x3c, 0x19, 0xb9, 0x7a, 0xef, 0x70, 0x30, 0xf2, 0xc6, 0x47, 0xc7, 0xd6, 0xc4, 0x1b, 0xbb, - 0xe4, 0x16, 0x5c, 0xb7, 0x2a, 0x7b, 0x75, 0xbd, 0x45, 0xb7, 0x97, 0x5e, 0xdd, 0x6b, 0x98, 0x95, - 0x56, 0xb9, 0x66, 0x5b, 0xed, 0x4a, 0x62, 0xe5, 0x5d, 0x83, 0x67, 0xa7, 0xa3, 0x56, 0x8d, 0x3d, - 0xbd, 0xf4, 0x40, 0x55, 0x66, 0x13, 0xdc, 0xd6, 0xab, 0x7a, 0xbd, 0x64, 0xec, 0xd8, 0xfb, 0xb7, - 0xd5, 0x0c, 0xb9, 0x0e, 0x57, 0xa7, 0xa3, 0xee, 0x56, 0x9a, 0x16, 0x45, 0xcb, 0xce, 0xfe, 0x6e, - 0xd9, 0xaa, 0x51, 0xac, 0x5c, 0xf1, 0x07, 0x0a, 0x6c, 0x4e, 0x0b, 0x01, 0x46, 0x6e, 0x80, 0x66, - 0xd4, 0x5b, 0xa6, 0x5e, 0xd9, 0xb1, 0x4b, 0xa6, 0xb1, 0x63, 0xd4, 0x5b, 0x15, 0xbd, 0x6a, 0xd9, - 0x56, 0xa3, 0x4d, 0x57, 0x53, 0x68, 0xdf, 0xf1, 0x1c, 0x5c, 0x99, 0x81, 0xd7, 0xa8, 0xec, 0x94, - 0x54, 0x85, 0xdc, 0x86, 0x17, 0x66, 0x20, 0x59, 0x0f, 0xac, 0x96, 0x51, 0x93, 0x4b, 0xd4, 0x4c, - 0xb1, 0x04, 0x5b, 0xd3, 0x63, 0x04, 0x51, 0x36, 0x1d, 0x1d, 0xe9, 0x3c, 0xe4, 0x76, 0xe8, 0xc9, - 0x10, 0xc9, 0x57, 0x52, 0xf4, 0x40, 0x8d, 0xc7, 0xcf, 0x48, 0x18, 0xe2, 0x98, 0xed, 0x7a, 0x9d, - 0x1d, 0x23, 0xab, 0x50, 0x68, 0xb4, 0xca, 0x86, 0xc9, 0x33, 0xbe, 0x60, 0x8a, 0x97, 0x76, 0x9d, - 0x6e, 0x9c, 0x86, 0x59, 0xf9, 0x22, 0x9e, 0x27, 0x9b, 0xb0, 0x6e, 0x55, 0xf5, 0xd2, 0x3d, 0xbb, - 0xde, 0x68, 0xd9, 0x95, 0xba, 0x5d, 0x2a, 0xeb, 0xf5, 0xba, 0x51, 0x55, 0x01, 0x07, 0x73, 0x9a, - 0x03, 0x29, 0xf9, 0x38, 0xdc, 0x6c, 0xdc, 0x6b, 0xe9, 0x76, 0xb3, 0xda, 0xde, 0xab, 0xd4, 0x6d, - 0xeb, 0x41, 0xbd, 0x24, 0x64, 0x9f, 0x52, 0x92, 0xe5, 0xde, 0x84, 0x6b, 0x33, 0xb1, 0xc3, 0xdc, - 0x2c, 0x37, 0x40, 0x9b, 0x89, 0xc9, 0x3b, 0x52, 0xfc, 0x99, 0x02, 0x17, 0x66, 0x3c, 0x94, 0x93, - 0x17, 0xe0, 0x56, 0xd9, 0xd0, 0x77, 0xaa, 0x86, 0x65, 0x21, 0xa3, 0xa0, 0xd3, 0xc0, 0x0c, 0x76, - 0x52, 0x19, 0xea, 0x2d, 0xb8, 0x3e, 0x1b, 0x3d, 0x3c, 0x9a, 0x6f, 0xc2, 0xb5, 0xd9, 0xa8, 0xfc, - 0xa8, 0xce, 0x90, 0x22, 0xdc, 0x98, 0x8d, 0x19, 0x1c, 0xf1, 0xd9, 0xe2, 0x6f, 0x2b, 0x70, 0x3e, - 0x5d, 0x5b, 0x45, 0xdb, 0x56, 0xa9, 0x5b, 0x2d, 0xbd, 0x5a, 0xb5, 0x9b, 0xba, 0xa9, 0xd7, 0x6c, - 0xa3, 0x6e, 0x36, 0xaa, 0xd5, 0xb4, 0xa3, 0xed, 0x1a, 0x3c, 0x3b, 0x1d, 0xd5, 0x2a, 0x99, 0x95, - 0x26, 0xe5, 0xde, 0x1a, 0x5c, 0x9e, 0x8e, 0x65, 0x54, 0x4a, 0x86, 0x9a, 0xd9, 0x7e, 0xe3, 0x27, - 0x7f, 0x77, 0xf9, 0xcc, 0x4f, 0xde, 0xbf, 0xac, 0xfc, 0xfc, 0xfd, 0xcb, 0xca, 0xdf, 0xbe, 0x7f, - 0x59, 0xf9, 0xe2, 0xf3, 0xa7, 0x4b, 0x6b, 0x86, 0x97, 0x92, 0x47, 0xf3, 0x78, 0x0d, 0x7b, 0xf9, - 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xe6, 0xfe, 0x0d, 0xe2, 0xc1, 0x01, 0x00, + 0x5c, 0xf2, 0xd7, 0xe1, 0x9c, 0xe8, 0x97, 0x26, 0xf5, 0x14, 0x9c, 0x01, 0x6c, 0x02, 0xde, 0x10, + 0xfd, 0xfd, 0x62, 0x3d, 0x64, 0x10, 0x6e, 0x1c, 0x8c, 0x45, 0x6c, 0x70, 0xd3, 0xeb, 0x21, 0x5f, + 0x83, 0x71, 0xb5, 0xda, 0xec, 0xe0, 0xe6, 0x15, 0x7d, 0xea, 0x52, 0x59, 0x12, 0x1b, 0xa6, 0x4d, + 0xfa, 0xf5, 0x2e, 0xf5, 0x03, 0x69, 0xe0, 0x21, 0x8e, 0xee, 0x8b, 0x89, 0x5a, 0x24, 0x42, 0xa8, + 0xff, 0x29, 0x78, 0x9c, 0xd2, 0x92, 0x66, 0x78, 0xdf, 0x62, 0xec, 0xe3, 0xfc, 0x8c, 0xef, 0x64, + 0xe1, 0x42, 0x8f, 0x69, 0xc9, 0x76, 0x2e, 0x14, 0xac, 0x94, 0x9d, 0x2b, 0x26, 0x4f, 0x71, 0xeb, + 0xb0, 0x4b, 0x90, 0x15, 0xa2, 0xc8, 0xd0, 0x52, 0xe1, 0xe4, 0xb8, 0x38, 0xa1, 0xad, 0xb8, 0x6c, + 0xb5, 0x42, 0xee, 0xc2, 0x10, 0xeb, 0x86, 0x01, 0x8c, 0x1c, 0xa4, 0xf6, 0x6f, 0x2a, 0x70, 0xd4, + 0x85, 0x8e, 0x7d, 0x83, 0x3c, 0xc8, 0x67, 0x20, 0xb7, 0xb9, 0xb9, 0x8a, 0xab, 0x3c, 0x87, 0xb3, + 0x74, 0x32, 0x08, 0x9a, 0xda, 0xa6, 0x32, 0xc9, 0x68, 0xc3, 0x1e, 0x31, 0x19, 0x3a, 0xf9, 0x62, + 0xcc, 0xf8, 0xea, 0xb5, 0xfe, 0x4b, 0x72, 0x70, 0x5b, 0xac, 0xc7, 0x30, 0x81, 0x32, 0x7e, 0x21, + 0x1b, 0xed, 0xb6, 0xb7, 0x9d, 0x66, 0x40, 0x3d, 0xb2, 0xc0, 0x37, 0xcf, 0x48, 0x8c, 0x36, 0xc3, + 0xdf, 0x64, 0x3e, 0xda, 0x89, 0x39, 0xab, 0x70, 0xcb, 0x7d, 0x4d, 0xd9, 0x72, 0x73, 0xb8, 0xe5, + 0x4e, 0xf5, 0xdc, 0x5c, 0x5f, 0x4b, 0xd9, 0x41, 0x70, 0xcb, 0x4c, 0xd9, 0x25, 0x5e, 0x86, 0xc9, + 0x75, 0x77, 0xf9, 0x51, 0x10, 0x22, 0xb2, 0xad, 0x32, 0x6f, 0xea, 0x40, 0xc6, 0xf1, 0x7e, 0xb3, + 0x41, 0xbd, 0xcd, 0x7d, 0xbb, 0xad, 0x59, 0x19, 0x98, 0x09, 0x38, 0xc3, 0x5d, 0xa7, 0x87, 0x3a, + 0xee, 0x28, 0xc7, 0x8d, 0xc3, 0x8d, 0x1f, 0xc8, 0xca, 0xce, 0x78, 0xb0, 0xf8, 0x94, 0xbe, 0x66, + 0xbf, 0xa9, 0xbd, 0x66, 0xcf, 0x86, 0x7a, 0xf8, 0xd0, 0x34, 0x63, 0xf1, 0x14, 0x8b, 0x8e, 0xbf, + 0x37, 0x02, 0x13, 0x2a, 0x3a, 0xeb, 0x87, 0x52, 0xa3, 0xe1, 0xa9, 0xfd, 0x60, 0x37, 0x1a, 0x9e, + 0x89, 0x50, 0xcd, 0x80, 0x23, 0xd7, 0xd7, 0x80, 0xe3, 0xab, 0x30, 0x56, 0x6e, 0x35, 0xb4, 0x67, + 0x65, 0x23, 0xe5, 0xf3, 0xae, 0x85, 0x48, 0x7c, 0x2d, 0x84, 0xea, 0xe5, 0x7a, 0xab, 0x91, 0x7c, + 0x4c, 0x8e, 0x58, 0x6a, 0xb6, 0x1f, 0xc3, 0x8f, 0x63, 0xfb, 0x71, 0x0b, 0xc6, 0xb6, 0x7c, 0xba, + 0xd9, 0x6d, 0xb7, 0x69, 0x13, 0xa7, 0x55, 0x9e, 0xdf, 0xca, 0xba, 0x3e, 0xb5, 0x02, 0x84, 0xaa, + 0x1f, 0x10, 0xa2, 0xaa, 0x03, 0x3c, 0xda, 0x67, 0x80, 0x6f, 0x42, 0x7e, 0x83, 0x52, 0x0f, 0xfb, + 0x74, 0x3c, 0x12, 0xbe, 0x3b, 0x94, 0x7a, 0x16, 0xeb, 0x58, 0xcd, 0x26, 0x44, 0x20, 0x6a, 0x86, + 0x24, 0x13, 0x03, 0x1a, 0x92, 0x90, 0x17, 0x61, 0xa2, 0xd3, 0xdd, 0x69, 0x3a, 0x75, 0xe4, 0x2b, + 0x2c, 0x50, 0xcc, 0x71, 0x0e, 0x63, 0x6c, 0x7d, 0xf2, 0x45, 0x98, 0xc4, 0xdb, 0x68, 0x38, 0xe5, + 0xa6, 0xb4, 0xf7, 0x57, 0xad, 0x8c, 0xcb, 0xa4, 0x75, 0x06, 0xb2, 0x52, 0x0c, 0xa5, 0x74, 0x46, + 0xe4, 0x2e, 0x8c, 0xee, 0x39, 0x81, 0xb5, 0xdf, 0xdd, 0x99, 0x9f, 0xd6, 0xac, 0x8c, 0xee, 0x38, + 0xc1, 0x4a, 0x77, 0x87, 0x0f, 0x79, 0xc8, 0x1a, 0x77, 0xbc, 0x3d, 0x27, 0xd8, 0xef, 0xaa, 0xca, + 0xf3, 0x91, 0x3d, 0xc4, 0x5d, 0xa8, 0xc1, 0x94, 0x3e, 0x2b, 0x9e, 0xc0, 0x93, 0x6e, 0x68, 0x60, + 0x93, 0x2f, 0x8c, 0xdd, 0x1d, 0xca, 0x43, 0x61, 0x9c, 0x9b, 0xd6, 0x98, 0xb0, 0x11, 0xf6, 0x8f, + 0x49, 0xee, 0x75, 0x77, 0xa8, 0xd7, 0xa6, 0x01, 0xf5, 0xc5, 0xd5, 0xcf, 0x37, 0x87, 0x4a, 0x9d, + 0x8e, 0x6f, 0xfc, 0xa7, 0x59, 0x18, 0x2d, 0x6d, 0xd7, 0xaa, 0xed, 0x5d, 0x17, 0x1f, 0x66, 0xc3, + 0xf7, 0x38, 0xf5, 0x61, 0x36, 0x7c, 0x8f, 0x53, 0x5f, 0xe1, 0xae, 0xa7, 0x5c, 0xde, 0xd1, 0x76, + 0x5b, 0xb9, 0xbc, 0x6b, 0x6a, 0x87, 0xe8, 0x69, 0x32, 0x37, 0xc0, 0xd3, 0x64, 0xa8, 0x3d, 0x1e, + 0x3a, 0x5d, 0x7b, 0xfc, 0x36, 0x8c, 0x57, 0xdb, 0x01, 0xdd, 0xf3, 0xa2, 0x55, 0x13, 0x2a, 0x12, + 0x42, 0xb0, 0x7a, 0xa1, 0x53, 0xb0, 0xd9, 0x94, 0xe4, 0x1a, 0xeb, 0x50, 0x53, 0x8d, 0x53, 0x92, + 0x2b, 0xb6, 0x63, 0x5a, 0x20, 0x89, 0x68, 0x54, 0x62, 0xf3, 0x4d, 0x9a, 0x7f, 0x70, 0x11, 0x6a, + 0x2a, 0x7a, 0xb2, 0x61, 0x1d, 0xbb, 0x34, 0x93, 0x6e, 0xfe, 0x61, 0xfc, 0xcd, 0x0c, 0xcc, 0xa5, + 0x4d, 0x23, 0xf2, 0x1e, 0x4c, 0xb8, 0xde, 0x9e, 0xdd, 0x76, 0xbe, 0x97, 0xb7, 0x48, 0x51, 0x55, + 0xaa, 0x70, 0x55, 0x41, 0xa3, 0xc2, 0x59, 0x87, 0x28, 0x2d, 0xd7, 0x35, 0x2b, 0xa9, 0x1d, 0xa2, + 0x80, 0x8d, 0x1f, 0xc9, 0xc2, 0x78, 0xa9, 0xd3, 0x79, 0xca, 0x4d, 0x03, 0x3f, 0xa7, 0x1d, 0x20, + 0xf2, 0x5e, 0x1e, 0xb6, 0x6b, 0x20, 0xab, 0xc0, 0x5f, 0xcd, 0xc2, 0x74, 0x8c, 0x42, 0xfd, 0xfa, + 0xcc, 0x80, 0x06, 0x81, 0xd9, 0x01, 0x0d, 0x02, 0x73, 0x83, 0x19, 0x04, 0x0e, 0x3d, 0xce, 0xa1, + 0xf0, 0x2a, 0xe4, 0x4a, 0x9d, 0x4e, 0xdc, 0xb0, 0xa0, 0xd3, 0x79, 0x70, 0x93, 0xeb, 0x56, 0xec, + 0x4e, 0xc7, 0x64, 0x18, 0xda, 0x4e, 0x3d, 0x32, 0xe0, 0x4e, 0x6d, 0xbc, 0x01, 0x63, 0xc8, 0x0b, + 0xcd, 0xf0, 0x2e, 0x01, 0x6e, 0x31, 0xc2, 0x02, 0x4f, 0xab, 0x4b, 0x6c, 0x3e, 0xff, 0x5f, 0x06, + 0x86, 0xf1, 0xf7, 0x53, 0x3a, 0xc7, 0x16, 0xb5, 0x39, 0x56, 0x50, 0xe6, 0xd8, 0x20, 0xb3, 0xeb, + 0x1f, 0xe6, 0x00, 0xca, 0xf7, 0xcd, 0x1a, 0x57, 0xc1, 0x91, 0xdb, 0x30, 0x6d, 0x37, 0x9b, 0xee, + 0x21, 0x6d, 0x58, 0xae, 0xe7, 0xec, 0x39, 0x6d, 0xde, 0x73, 0xf2, 0xb5, 0x5b, 0x2f, 0x52, 0xdf, + 0xc0, 0x44, 0xd1, 0x7d, 0x5e, 0xa2, 0xf2, 0x69, 0xd1, 0x60, 0xdf, 0x6d, 0x48, 0x65, 0x82, 0xc6, + 0x47, 0x14, 0xa5, 0xf0, 0x59, 0xe3, 0x25, 0x2a, 0x9f, 0x7d, 0x54, 0x8e, 0x48, 0x09, 0x59, 0xe3, + 0x23, 0x8a, 0x52, 0xf8, 0x70, 0x8d, 0x8a, 0x4f, 0x56, 0x61, 0x06, 0x21, 0x56, 0xdd, 0xa3, 0x0d, + 0xda, 0x0e, 0x1c, 0xbb, 0xe9, 0x0b, 0xf5, 0x13, 0x2a, 0x2a, 0x13, 0x85, 0xea, 0xf5, 0x1b, 0x0b, + 0xcb, 0x51, 0x19, 0xb9, 0x06, 0xa3, 0x2d, 0xfb, 0x91, 0x65, 0xef, 0x71, 0xbb, 0x8f, 0x49, 0xae, + 0xae, 0x10, 0x20, 0xf5, 0x18, 0x69, 0xd9, 0x8f, 0x4a, 0x7b, 0x94, 0xb5, 0x82, 0x3e, 0xea, 0xb8, + 0xbe, 0xd2, 0x8a, 0x91, 0xa8, 0x15, 0xb1, 0x22, 0xb5, 0x15, 0xa2, 0x48, 0xb4, 0xc2, 0xf8, 0x95, + 0x0c, 0x3c, 0x5b, 0xc5, 0xaf, 0x08, 0x8e, 0xca, 0xb4, 0x1d, 0x50, 0x6f, 0x83, 0x7a, 0x2d, 0x07, + 0x5f, 0xc1, 0x6b, 0x34, 0x20, 0x2f, 0x41, 0xae, 0x64, 0xae, 0x8b, 0xf9, 0xcb, 0xf7, 0x7b, 0xcd, + 0x26, 0x81, 0x95, 0x86, 0x1a, 0xad, 0xec, 0x29, 0xaa, 0xea, 0x12, 0x4c, 0x94, 0x7c, 0xdf, 0xd9, + 0x6b, 0xb7, 0xb8, 0x3f, 0x45, 0x4e, 0xb3, 0x7a, 0x10, 0xf0, 0xc4, 0x1b, 0x8b, 0x4a, 0x62, 0xfc, + 0x67, 0x19, 0x98, 0x29, 0x75, 0x3a, 0xfa, 0x27, 0xeb, 0x16, 0x37, 0x99, 0xc1, 0x2d, 0x6e, 0x1c, + 0x98, 0xd2, 0x9a, 0xcb, 0xa7, 0x54, 0x24, 0xf8, 0xf6, 0xe9, 0x19, 0xfe, 0xd9, 0x9d, 0x10, 0x64, + 0xf9, 0xfa, 0x73, 0x71, 0x8c, 0xb1, 0xf1, 0x1f, 0x8c, 0xe2, 0x1e, 0x22, 0x76, 0x5b, 0x61, 0x13, + 0x9a, 0x49, 0xb1, 0x09, 0x7d, 0x0b, 0x14, 0x09, 0x47, 0x3d, 0xe2, 0x14, 0x59, 0x51, 0xd5, 0x05, + 0x45, 0xc8, 0xe4, 0x20, 0x6e, 0x1d, 0x9a, 0xc3, 0xd6, 0xbc, 0x14, 0x5f, 0xc0, 0x4f, 0xc4, 0x30, + 0x74, 0x05, 0x48, 0xb5, 0x8d, 0x4f, 0xd8, 0xb4, 0x76, 0xe0, 0x74, 0x1e, 0x50, 0xcf, 0xd9, 0x3d, + 0x12, 0x0b, 0x00, 0x3b, 0xdf, 0x11, 0xa5, 0x96, 0x7f, 0xe0, 0x74, 0xac, 0x87, 0x58, 0x6e, 0xa6, + 0xd0, 0x90, 0xf7, 0x61, 0xd4, 0xa4, 0x87, 0x9e, 0x13, 0x48, 0x9b, 0xa7, 0xa9, 0x50, 0xb5, 0x89, + 0x50, 0xbe, 0x16, 0x3c, 0xfe, 0x43, 0xdd, 0x15, 0x45, 0x39, 0x59, 0xe4, 0x42, 0x0a, 0xb7, 0x6d, + 0x9a, 0x8c, 0x5a, 0x5b, 0xda, 0xae, 0xf5, 0x92, 0x51, 0xc8, 0x55, 0x18, 0x46, 0x49, 0x47, 0xdc, + 0x05, 0xd0, 0x57, 0x08, 0x65, 0x67, 0x55, 0x0c, 0x43, 0x0c, 0xf2, 0x02, 0x40, 0xf8, 0x46, 0xec, + 0xcf, 0xe7, 0x51, 0x4a, 0x57, 0x20, 0x71, 0x31, 0x6d, 0xec, 0x4c, 0x62, 0xda, 0x2a, 0x14, 0x4c, + 0xee, 0x76, 0xd8, 0x28, 0x75, 0xf0, 0x21, 0xd2, 0x9f, 0x07, 0x5c, 0xc9, 0x97, 0x4e, 0x8e, 0x8b, + 0xcf, 0x09, 0x97, 0xc4, 0x86, 0x65, 0x77, 0xf8, 0xfb, 0xa5, 0xb6, 0x8d, 0xc4, 0x29, 0xc9, 0x5b, + 0x30, 0xc4, 0xb6, 0x5e, 0x61, 0x47, 0x2a, 0x1f, 0x74, 0xa2, 0xdd, 0x98, 0x2f, 0xce, 0xba, 0xab, + 0xed, 0x09, 0x48, 0x42, 0x2c, 0x98, 0xd2, 0xa7, 0xbb, 0x30, 0x29, 0x9a, 0x8f, 0xfa, 0x53, 0x2f, + 0x17, 0xaf, 0x3c, 0x02, 0x66, 0xd5, 0x11, 0xa8, 0xae, 0x80, 0xd8, 0x22, 0x5d, 0x86, 0xfc, 0x66, + 0x79, 0x63, 0xc3, 0xf5, 0x02, 0x7e, 0xd5, 0x89, 0x4e, 0x16, 0x06, 0x33, 0xed, 0xf6, 0x1e, 0xe5, + 0x67, 0x71, 0x50, 0xef, 0x58, 0x1d, 0x86, 0xa6, 0x9e, 0xc5, 0x92, 0xf4, 0x93, 0xb3, 0x21, 0xfd, + 0xd5, 0x2c, 0xbc, 0x14, 0x4a, 0x45, 0xf7, 0xbd, 0x5a, 0x69, 0x6d, 0xb5, 0xda, 0xd8, 0x10, 0x6a, + 0x92, 0x0d, 0xcf, 0x7d, 0xe8, 0x34, 0xa8, 0xf7, 0xe0, 0xc6, 0x29, 0x67, 0xfa, 0x2a, 0x5f, 0xe6, + 0xfc, 0x35, 0x2c, 0xab, 0x59, 0xdb, 0x29, 0xc2, 0xa7, 0xd8, 0x9e, 0x3a, 0x9d, 0xc4, 0xe3, 0xd8, + 0xca, 0x33, 0x66, 0xc4, 0x80, 0xfc, 0x60, 0x06, 0xce, 0xa7, 0x7f, 0x88, 0x50, 0x9d, 0x15, 0xe5, + 0x15, 0xbd, 0xc7, 0xd7, 0x2e, 0xbd, 0x7a, 0x72, 0x5c, 0x7c, 0xc9, 0xb7, 0x5b, 0x4d, 0xcb, 0x69, + 0xf0, 0xda, 0x9c, 0x3a, 0xb5, 0x3a, 0x02, 0x41, 0xab, 0xb7, 0x47, 0x4d, 0x9f, 0x07, 0x79, 0xb4, + 0xcf, 0x67, 0x96, 0x00, 0xf2, 0xf2, 0xc1, 0xc1, 0xf8, 0x8d, 0x0c, 0x28, 0x4b, 0x30, 0x6f, 0xd2, + 0x86, 0xe3, 0xd1, 0x7a, 0x20, 0x8e, 0x77, 0xe1, 0x2b, 0xc8, 0x61, 0x31, 0xe3, 0x4a, 0x84, 0x91, + 0xf7, 0x60, 0x54, 0x1c, 0x43, 0x62, 0xdb, 0x95, 0x4b, 0x57, 0x3c, 0x65, 0x70, 0xa7, 0xd2, 0xc4, + 0x11, 0x26, 0x89, 0xd8, 0xae, 0x7f, 0x77, 0x7b, 0xb3, 0xdc, 0xb4, 0x9d, 0x96, 0x2f, 0xce, 0x12, + 0xec, 0xd6, 0x8f, 0x0e, 0x03, 0xab, 0x8e, 0x50, 0x75, 0xd7, 0x0f, 0x51, 0x8d, 0x3b, 0xf2, 0x25, + 0xe5, 0x14, 0x0b, 0xe1, 0x22, 0x0c, 0x3f, 0x88, 0xf4, 0x74, 0x4b, 0x63, 0x27, 0xc7, 0x45, 0x3e, + 0x5d, 0x4c, 0x0e, 0x37, 0x28, 0x8c, 0x85, 0x53, 0x97, 0xf1, 0x62, 0x3f, 0x90, 0xd7, 0x24, 0xe7, + 0xc5, 0x26, 0xb1, 0x89, 0x50, 0x26, 0xea, 0x2d, 0xb7, 0x1b, 0x88, 0x90, 0x45, 0x04, 0xec, 0x1e, + 0xda, 0x6e, 0xe0, 0x4c, 0x57, 0x5b, 0x27, 0xd0, 0x14, 0x81, 0xea, 0xc7, 0x32, 0x30, 0xa5, 0x4f, + 0x5b, 0x72, 0x0d, 0x46, 0x84, 0x3b, 0x60, 0x06, 0xd5, 0x9e, 0x8c, 0xdb, 0x08, 0x77, 0x04, 0xd4, + 0xdc, 0xff, 0x04, 0x16, 0x93, 0x1b, 0x05, 0x07, 0x21, 0x34, 0xa1, 0xdc, 0x58, 0xe7, 0x20, 0x53, + 0x96, 0x11, 0x83, 0x5d, 0x65, 0xfd, 0x6e, 0x33, 0x50, 0xdf, 0x2d, 0x3d, 0x84, 0x98, 0xa2, 0xc4, + 0x28, 0xc3, 0x08, 0xdf, 0x5a, 0x63, 0x06, 0x90, 0x99, 0x33, 0x18, 0x40, 0x1a, 0xc7, 0x19, 0x80, + 0x5a, 0x6d, 0xe5, 0x1e, 0x3d, 0xda, 0xb0, 0x1d, 0x3c, 0xbf, 0xf9, 0x31, 0x76, 0x4f, 0xac, 0xe1, + 0x09, 0xf1, 0xd0, 0xce, 0x8f, 0xbc, 0x03, 0x7a, 0xa4, 0x3d, 0xb4, 0x4b, 0x54, 0x3c, 0x2b, 0x3d, + 0xe7, 0xa1, 0x1d, 0x50, 0x46, 0x98, 0x45, 0x42, 0x7e, 0x56, 0x72, 0x68, 0x8c, 0x52, 0x41, 0x26, + 0x5f, 0x81, 0xa9, 0xe8, 0x57, 0x68, 0x2e, 0x30, 0x15, 0xee, 0x13, 0x7a, 0xe1, 0xd2, 0x0b, 0x27, + 0xc7, 0xc5, 0x05, 0x85, 0x6b, 0xdc, 0x90, 0x20, 0xc6, 0xcc, 0xf8, 0xa5, 0x0c, 0x1a, 0xc9, 0xc8, + 0x06, 0x5e, 0x86, 0xa1, 0xd0, 0xac, 0x7b, 0x42, 0x6c, 0xc2, 0xfa, 0x93, 0x28, 0x96, 0x33, 0x71, + 0x2b, 0x6a, 0x09, 0x1e, 0x5d, 0x7a, 0x0b, 0x58, 0x29, 0xb9, 0x03, 0xa3, 0x03, 0x7d, 0x33, 0x4e, + 0xb1, 0x94, 0x6f, 0x95, 0xd4, 0x38, 0x0a, 0x77, 0xb7, 0x37, 0xbf, 0x7b, 0x47, 0xe1, 0x27, 0xb3, + 0x30, 0xcd, 0xfa, 0xb5, 0xd4, 0x0d, 0xf6, 0x5d, 0xcf, 0x09, 0x8e, 0x9e, 0x5a, 0xbd, 0xf1, 0x3b, + 0xda, 0x95, 0x6c, 0x41, 0x1e, 0x66, 0x6a, 0xdb, 0x06, 0x52, 0x1f, 0xff, 0xd3, 0x61, 0x98, 0x4d, + 0xa1, 0x22, 0xaf, 0x6b, 0x4f, 0x3b, 0xf3, 0xd2, 0xdd, 0xff, 0x3b, 0xc7, 0xc5, 0x09, 0x89, 0xbe, + 0x19, 0xb9, 0xff, 0x2f, 0xea, 0x16, 0x67, 0xbc, 0xa7, 0xf0, 0xa5, 0x47, 0xb5, 0x38, 0xd3, 0xed, + 0xcc, 0xae, 0xc2, 0xb0, 0xe9, 0x36, 0xa9, 0xb4, 0xb2, 0x44, 0x81, 0xcb, 0x63, 0x00, 0xcd, 0xaa, + 0x84, 0x01, 0xc8, 0x0a, 0x8c, 0xb2, 0x3f, 0xd6, 0xec, 0x8e, 0x78, 0x2f, 0x25, 0xa1, 0x52, 0x00, + 0xa1, 0x1d, 0xa7, 0xbd, 0xa7, 0xea, 0x05, 0x9a, 0xd4, 0x6a, 0xd9, 0x1d, 0x4d, 0x32, 0xe4, 0x88, + 0x9a, 0x7e, 0x21, 0xdf, 0x5b, 0xbf, 0x90, 0x39, 0x55, 0xbf, 0xb0, 0x0b, 0x50, 0x73, 0xf6, 0xda, + 0x4e, 0x7b, 0xaf, 0xd4, 0xdc, 0x13, 0x41, 0x13, 0xae, 0xf6, 0x1e, 0x85, 0x6b, 0x11, 0x32, 0x4e, + 0xdc, 0x67, 0xd1, 0xa8, 0x81, 0xc3, 0x2c, 0xbb, 0xb9, 0xa7, 0x39, 0x77, 0x29, 0x9c, 0xc9, 0x3a, + 0x40, 0xa9, 0x1e, 0x38, 0x0f, 0xd9, 0x14, 0xf6, 0x85, 0x18, 0x27, 0x3f, 0xb9, 0x5c, 0xba, 0x47, + 0x8f, 0xf0, 0xea, 0x21, 0x9f, 0x87, 0x6d, 0x44, 0x65, 0x2b, 0x41, 0xf3, 0xdc, 0x89, 0x38, 0x90, + 0x0e, 0x9c, 0x2b, 0x35, 0x1a, 0x0e, 0x6b, 0x83, 0xdd, 0xdc, 0xe4, 0xe1, 0x2e, 0x90, 0xf5, 0x44, + 0x3a, 0xeb, 0xab, 0xf2, 0x25, 0xd4, 0x0e, 0xa9, 0x2c, 0x19, 0x25, 0x23, 0x56, 0x4d, 0x3a, 0x63, + 0xa3, 0x06, 0x53, 0x7a, 0xe3, 0xf5, 0x60, 0x0f, 0x13, 0x90, 0x37, 0x6b, 0x25, 0xab, 0xb6, 0x52, + 0xba, 0x51, 0xc8, 0x90, 0x02, 0x4c, 0x88, 0x5f, 0x8b, 0xd6, 0xe2, 0x9b, 0xb7, 0x0a, 0x59, 0x0d, + 0xf2, 0xe6, 0x8d, 0xc5, 0x42, 0x6e, 0x21, 0x3b, 0x9f, 0x89, 0xf9, 0x59, 0x8e, 0x16, 0xf2, 0x5c, + 0x25, 0x6c, 0xfc, 0x5a, 0x06, 0xf2, 0xf2, 0xdb, 0xc9, 0x2d, 0xc8, 0xd5, 0x6a, 0x2b, 0x31, 0xcf, + 0xc8, 0xe8, 0x94, 0xe1, 0xfb, 0xa9, 0xef, 0xab, 0xe6, 0xef, 0x8c, 0x80, 0xd1, 0x6d, 0xae, 0xd6, + 0x84, 0x0c, 0x22, 0xe9, 0xa2, 0xcd, 0x9b, 0xd3, 0xa5, 0xb8, 0x8b, 0xdd, 0x82, 0xdc, 0xdd, 0xed, + 0x4d, 0x71, 0xc9, 0x92, 0x74, 0xd1, 0x7e, 0xca, 0xe9, 0x3e, 0x3a, 0x54, 0x77, 0x79, 0x46, 0x60, + 0x98, 0x30, 0xae, 0x4c, 0x64, 0x7e, 0xe8, 0xb6, 0xdc, 0x30, 0xc2, 0x81, 0x38, 0x74, 0x19, 0xc4, + 0x14, 0x25, 0x4c, 0x14, 0x59, 0x75, 0xeb, 0x76, 0x53, 0x9c, 0xde, 0x28, 0x8a, 0x34, 0x19, 0xc0, + 0xe4, 0x70, 0xe3, 0xb7, 0x33, 0x50, 0x40, 0x81, 0x0d, 0xcd, 0xd7, 0xdd, 0x03, 0xda, 0x7e, 0x70, + 0x83, 0xbc, 0x21, 0x97, 0x5c, 0x26, 0x54, 0x74, 0x0d, 0xe3, 0x92, 0x8b, 0xbd, 0x05, 0x8a, 0x65, + 0xa7, 0x04, 0x91, 0xc8, 0x0e, 0xee, 0x7c, 0x7e, 0x4a, 0x10, 0x89, 0x22, 0x0c, 0xe3, 0xe7, 0x88, + 0xcd, 0x11, 0xbf, 0x3c, 0x60, 0x00, 0x93, 0xc3, 0x95, 0xbd, 0xe9, 0xa7, 0xb3, 0x89, 0x36, 0x2c, + 0x7e, 0x57, 0x39, 0x70, 0xeb, 0x8d, 0x1b, 0x68, 0xbf, 0xfe, 0x10, 0xe6, 0xe2, 0x5d, 0x82, 0x4a, + 0xc8, 0x12, 0x4c, 0xeb, 0x70, 0xa9, 0x8f, 0xbc, 0x90, 0x5a, 0xd7, 0x83, 0x45, 0x33, 0x8e, 0x6f, + 0xfc, 0x1f, 0x19, 0x18, 0xc3, 0x3f, 0xcd, 0x6e, 0x13, 0xcd, 0x08, 0x4b, 0xdb, 0x35, 0xa1, 0x1a, + 0x51, 0x85, 0x39, 0xfb, 0xd0, 0xb7, 0x84, 0x1e, 0x45, 0xdb, 0x63, 0x42, 0x64, 0x41, 0xca, 0xdf, + 0x37, 0xa4, 0x52, 0x2e, 0x24, 0xe5, 0x0f, 0x21, 0x7e, 0x8c, 0x54, 0x20, 0xa3, 0xf1, 0xf1, 0x76, + 0x8d, 0x4d, 0x3f, 0xd5, 0xae, 0x07, 0xe9, 0xdc, 0xa6, 0x6e, 0x7c, 0xcc, 0xd1, 0xd0, 0xac, 0x67, + 0xbb, 0x56, 0x32, 0xd7, 0x35, 0xb3, 0x1e, 0xf6, 0x8d, 0x9a, 0x5e, 0x4a, 0x20, 0x19, 0xbf, 0x30, + 0x1e, 0xef, 0x40, 0x71, 0xe0, 0x9d, 0x71, 0x6d, 0xbc, 0x0d, 0xc3, 0xa5, 0x66, 0xd3, 0x3d, 0x14, + 0xbb, 0x84, 0xbc, 0xb9, 0x86, 0xfd, 0xc7, 0xcf, 0x33, 0x54, 0xeb, 0x69, 0x4e, 0xa9, 0x0c, 0x40, + 0xca, 0x30, 0x56, 0xda, 0xae, 0x55, 0xab, 0x95, 0xcd, 0x4d, 0xee, 0x80, 0x97, 0x5b, 0x7a, 0x45, + 0xf6, 0x8f, 0xe3, 0x34, 0xac, 0xb8, 0xbd, 0x42, 0x24, 0xbf, 0x47, 0x74, 0xe4, 0x5d, 0x80, 0xbb, + 0xae, 0xd3, 0xe6, 0x6a, 0x4c, 0xd1, 0x78, 0x76, 0x03, 0x1f, 0xff, 0xc8, 0x75, 0xda, 0x42, 0xef, + 0xc9, 0xbe, 0x3d, 0x42, 0x32, 0x95, 0xbf, 0x59, 0x4f, 0x2f, 0xb9, 0xdc, 0x34, 0x70, 0x38, 0xea, + 0xe9, 0x1d, 0x37, 0xa1, 0x6f, 0x93, 0x68, 0xa4, 0x05, 0xd3, 0xb5, 0xee, 0xde, 0x1e, 0x65, 0x3b, + 0xbb, 0xd0, 0x27, 0x8d, 0x88, 0xab, 0x74, 0x18, 0xf6, 0x88, 0xdf, 0x47, 0xd8, 0x65, 0xc8, 0x5f, + 0x7a, 0x9d, 0x4d, 0xe4, 0x6f, 0x1f, 0x17, 0x85, 0x1d, 0x04, 0x13, 0xd5, 0x7c, 0x49, 0x9f, 0xd4, + 0x26, 0xc5, 0x79, 0x93, 0xfb, 0x30, 0xc2, 0xdf, 0x8c, 0x84, 0x43, 0xd9, 0x8b, 0x7d, 0x16, 0x0d, + 0x47, 0xec, 0xf5, 0x2a, 0xc9, 0x4b, 0xc9, 0x36, 0xe4, 0xcb, 0x8e, 0x57, 0x6f, 0xd2, 0x72, 0x55, + 0x9c, 0xfd, 0x2f, 0xf5, 0x61, 0x29, 0x51, 0x79, 0xbf, 0xd4, 0xf1, 0x57, 0xdd, 0x51, 0x65, 0x01, + 0x89, 0x41, 0xfe, 0x66, 0x06, 0x9e, 0x0d, 0xbf, 0xbe, 0xb4, 0x47, 0xdb, 0xc1, 0x9a, 0x1d, 0xd4, + 0xf7, 0xa9, 0x27, 0x7a, 0x69, 0xac, 0x5f, 0x2f, 0x7d, 0x3e, 0xd1, 0x4b, 0x57, 0xa2, 0x5e, 0xb2, + 0x19, 0x33, 0xab, 0xc5, 0xb9, 0x25, 0xfb, 0xac, 0x5f, 0xad, 0xc4, 0x02, 0x88, 0x5e, 0x43, 0x85, + 0x43, 0xf2, 0x2b, 0x7d, 0x1a, 0x1c, 0x21, 0x0b, 0x47, 0xa2, 0xf0, 0xb7, 0x66, 0x09, 0x1b, 0x42, + 0xc9, 0x3d, 0xe9, 0xbd, 0xc9, 0xa5, 0x92, 0x4b, 0x7d, 0x78, 0x73, 0x8f, 0xce, 0xd9, 0x3e, 0x7e, + 0xda, 0x7c, 0xb4, 0x57, 0xed, 0x1d, 0x21, 0x88, 0x9c, 0x32, 0xda, 0xab, 0x76, 0x34, 0xda, 0x4d, + 0x3b, 0x3e, 0xda, 0xab, 0xf6, 0x0e, 0x29, 0x73, 0x97, 0x73, 0xee, 0x9f, 0xfc, 0x42, 0x3f, 0x6e, + 0xe5, 0x0d, 0x7e, 0x32, 0xa7, 0xb8, 0x9e, 0x7f, 0x09, 0xc6, 0x6a, 0x1d, 0xbb, 0x4e, 0x9b, 0xce, + 0x6e, 0x20, 0x9e, 0xda, 0x5f, 0xee, 0xc3, 0x2a, 0xc4, 0x15, 0x4f, 0xab, 0xf2, 0xa7, 0x7a, 0x4d, + 0x0a, 0x71, 0xd8, 0x17, 0x6e, 0x6e, 0xac, 0x89, 0xd7, 0xf6, 0x7e, 0x5f, 0xb8, 0xb9, 0xb1, 0x26, + 0x64, 0x8e, 0x4e, 0x4b, 0x93, 0x39, 0x36, 0xd6, 0x48, 0x07, 0xa6, 0x36, 0xa9, 0xe7, 0xd9, 0xbb, + 0xae, 0xd7, 0xe2, 0xfa, 0x4b, 0xee, 0xf3, 0x76, 0xb5, 0x1f, 0x3f, 0x8d, 0x80, 0xab, 0xed, 0x02, + 0x09, 0xb3, 0xe2, 0x4a, 0xcf, 0x18, 0x7f, 0xd6, 0x27, 0x4b, 0x4e, 0xb0, 0xd3, 0xad, 0x1f, 0xd0, + 0x60, 0x7e, 0xe6, 0xd4, 0x3e, 0x09, 0x71, 0x79, 0x9f, 0xec, 0xc8, 0x9f, 0x6a, 0x9f, 0x84, 0x38, + 0xc6, 0x3f, 0xc9, 0xc1, 0x85, 0x1e, 0x5d, 0x40, 0xd6, 0xe5, 0x96, 0x9b, 0xd1, 0xb4, 0xd8, 0x3d, + 0xd0, 0xaf, 0x9d, 0xba, 0x0b, 0xaf, 0x42, 0x61, 0xf9, 0x1e, 0xca, 0xea, 0xfc, 0x21, 0xa7, 0x5c, + 0x92, 0x87, 0x15, 0x6a, 0x5a, 0xe9, 0x01, 0xda, 0x08, 0xcb, 0x07, 0xa0, 0xba, 0xe6, 0x0c, 0x9f, + 0xa0, 0x5c, 0xf8, 0x81, 0x2c, 0x0c, 0xe1, 0xc1, 0x19, 0x0b, 0x01, 0x96, 0x39, 0x53, 0x08, 0xb0, + 0x2f, 0xc0, 0xc4, 0xf2, 0x3d, 0x7e, 0x93, 0x5e, 0xb1, 0xfd, 0x7d, 0xb1, 0xad, 0xa3, 0x21, 0x07, + 0x3d, 0xb0, 0xc4, 0xc5, 0x7b, 0xdf, 0xd6, 0x64, 0x56, 0x8d, 0x82, 0x6c, 0xc1, 0x2c, 0xff, 0x36, + 0x67, 0xd7, 0xa9, 0xf3, 0x48, 0x42, 0x8e, 0xdd, 0x14, 0x7b, 0xfc, 0x4b, 0x27, 0xc7, 0xc5, 0x22, + 0x3d, 0x40, 0xeb, 0x67, 0x51, 0x6e, 0xf9, 0x88, 0xa0, 0x9a, 0x41, 0xa7, 0xd0, 0xab, 0xe1, 0x4d, + 0xcc, 0x31, 0xac, 0x90, 0xd5, 0xc6, 0xea, 0x66, 0xb8, 0x1c, 0xc9, 0xf8, 0xd3, 0x61, 0x58, 0xe8, + 0xbd, 0x3d, 0x93, 0x0f, 0xf4, 0x01, 0xbc, 0x7c, 0xea, 0x86, 0x7e, 0xfa, 0x18, 0x7e, 0x11, 0xe6, + 0x96, 0xdb, 0x01, 0xf5, 0x3a, 0x9e, 0x23, 0x03, 0xda, 0xac, 0xb8, 0xbe, 0xb4, 0x36, 0x47, 0xb3, + 0x6f, 0x1a, 0x96, 0x0b, 0xdd, 0x2a, 0xda, 0xbe, 0x2b, 0xac, 0x52, 0x39, 0x90, 0x65, 0x98, 0x52, + 0xe0, 0xcd, 0xee, 0x9e, 0xfa, 0x3a, 0xa5, 0xf2, 0x6c, 0x76, 0x55, 0x53, 0xdc, 0x18, 0x11, 0x5a, + 0xb4, 0xb3, 0x2b, 0x63, 0xfd, 0xee, 0xf6, 0xbd, 0x9a, 0x18, 0x4e, 0x6e, 0xd1, 0x8e, 0x50, 0xeb, + 0xa3, 0xc3, 0x03, 0x6d, 0x7f, 0x8d, 0x90, 0x17, 0x7e, 0x29, 0x27, 0x66, 0xd4, 0x4b, 0x90, 0xab, + 0x75, 0x77, 0xd4, 0x37, 0x37, 0x5f, 0x3b, 0xe0, 0x58, 0x29, 0xf9, 0x1c, 0x80, 0x49, 0x3b, 0xae, + 0xef, 0x04, 0xae, 0x77, 0xa4, 0xba, 0x54, 0x7a, 0x21, 0x54, 0xf7, 0xfa, 0x90, 0x50, 0xb2, 0x02, + 0xd3, 0xd1, 0xaf, 0xfb, 0x87, 0x6d, 0xa1, 0x4b, 0x1e, 0xe3, 0xda, 0x95, 0x88, 0xdc, 0x72, 0x59, + 0x99, 0x7a, 0x64, 0xc7, 0xc8, 0xc8, 0x22, 0xe4, 0xb7, 0x5d, 0xef, 0x60, 0x97, 0x8d, 0xf1, 0x50, + 0x24, 0x54, 0x1c, 0x0a, 0x98, 0x7a, 0x78, 0x4a, 0x3c, 0xb6, 0x5c, 0x96, 0xdb, 0x0f, 0x1d, 0xcf, + 0xc5, 0x17, 0x3d, 0xd5, 0xa6, 0x85, 0x46, 0x60, 0xcd, 0x99, 0x3d, 0x02, 0x93, 0xab, 0x30, 0x5c, + 0xaa, 0x07, 0xae, 0x27, 0x0c, 0x5a, 0xf8, 0x4c, 0x61, 0x00, 0x6d, 0xa6, 0x30, 0x00, 0xeb, 0x44, + 0x93, 0xee, 0x8a, 0xd7, 0x1d, 0xec, 0x44, 0x8f, 0xee, 0x6a, 0x9e, 0xfa, 0x74, 0x97, 0x09, 0x45, + 0x26, 0xdd, 0x45, 0xc5, 0x87, 0x16, 0xe0, 0x6e, 0x37, 0xa1, 0x32, 0x13, 0x68, 0xc6, 0xef, 0x8d, + 0xf5, 0x9c, 0xf2, 0xec, 0x14, 0x3a, 0xdb, 0x94, 0x5f, 0xb5, 0x07, 0x98, 0xf2, 0xaf, 0x87, 0xbe, + 0x24, 0x6a, 0x78, 0x0a, 0x84, 0xa8, 0xc7, 0x20, 0xc7, 0x59, 0xf8, 0xe5, 0xfc, 0x59, 0x26, 0x91, + 0xe8, 0xa4, 0xec, 0xa0, 0x9d, 0x94, 0x1b, 0xa8, 0x93, 0xc8, 0x12, 0x4c, 0x86, 0x21, 0x12, 0x37, + 0xec, 0x40, 0xdb, 0xd6, 0xc2, 0xb8, 0x96, 0x56, 0xc7, 0x0e, 0xd4, 0x6d, 0x4d, 0x27, 0x21, 0xef, + 0xc0, 0xb8, 0x70, 0xa8, 0x42, 0x0e, 0xc3, 0x91, 0xa5, 0x90, 0xf4, 0xbe, 0x8a, 0xd1, 0xab, 0xe8, + 0x6c, 0x35, 0x6f, 0x38, 0x1d, 0xda, 0x74, 0xda, 0xb4, 0x86, 0x8f, 0x15, 0x62, 0xc6, 0xf0, 0x47, + 0x5b, 0x51, 0x62, 0xf1, 0x77, 0x0c, 0x4d, 0x7f, 0xa8, 0x11, 0xc5, 0x27, 0xeb, 0xe8, 0x99, 0x26, + 0x2b, 0xb7, 0x53, 0xf4, 0x56, 0xdd, 0x3d, 0x47, 0xda, 0xd0, 0x4b, 0x3b, 0x45, 0xcf, 0x6a, 0x32, + 0x68, 0xcc, 0x4e, 0x91, 0xa3, 0xb2, 0x1b, 0x0e, 0xfb, 0x51, 0xad, 0x88, 0x97, 0x44, 0xbc, 0xe1, + 0x20, 0x91, 0xee, 0xb8, 0xc0, 0x91, 0x64, 0x35, 0xcb, 0x2d, 0xdb, 0x69, 0x8a, 0x28, 0x04, 0x51, + 0x35, 0x94, 0x41, 0xe3, 0xd5, 0x20, 0x2a, 0xa9, 0xc3, 0x84, 0x49, 0x77, 0x37, 0x3c, 0x37, 0xa0, + 0xf5, 0x80, 0x36, 0x84, 0x54, 0x27, 0x2f, 0x36, 0x4b, 0xae, 0xcb, 0x25, 0x56, 0xb4, 0x8d, 0xcf, + 0x7c, 0xfb, 0xb8, 0x08, 0x0c, 0xc4, 0xbd, 0x62, 0x4e, 0x8e, 0x8b, 0x17, 0xd8, 0xf8, 0x77, 0x24, + 0xb1, 0x7a, 0x3a, 0xa9, 0x4c, 0xc9, 0x37, 0xd8, 0x7e, 0x1d, 0x76, 0x49, 0x54, 0xd9, 0x44, 0x8f, + 0xca, 0xde, 0x4c, 0xad, 0xac, 0xa8, 0xf4, 0x76, 0x6a, 0xa5, 0xa9, 0x95, 0x90, 0x77, 0x61, 0xbc, + 0x5c, 0x2d, 0xbb, 0xed, 0x5d, 0x67, 0xaf, 0xb6, 0x52, 0x42, 0xd1, 0x50, 0x78, 0x44, 0xd5, 0x1d, + 0xab, 0x8e, 0x70, 0xcb, 0xdf, 0xb7, 0x35, 0xc7, 0xd8, 0x08, 0x9f, 0xdc, 0x81, 0x29, 0xf9, 0xd3, + 0xa4, 0xbb, 0x5b, 0x66, 0x15, 0x25, 0x42, 0xe9, 0x86, 0x16, 0x72, 0x60, 0x1d, 0xd1, 0xf5, 0xd4, + 0x9b, 0x42, 0x8c, 0x8c, 0x4d, 0xc6, 0x0a, 0xed, 0x34, 0xdd, 0x23, 0xf6, 0x79, 0x9b, 0x0e, 0xf5, + 0x50, 0x06, 0x14, 0x93, 0xb1, 0x11, 0x96, 0x58, 0x81, 0xa3, 0xbf, 0x9f, 0xea, 0x44, 0x64, 0x1d, + 0x66, 0xc4, 0x14, 0x7f, 0xe0, 0xf8, 0xce, 0x8e, 0xd3, 0x74, 0x82, 0x23, 0x94, 0xfe, 0x84, 0x00, + 0x23, 0xd7, 0xc5, 0xc3, 0xb0, 0x54, 0x61, 0x96, 0x24, 0x35, 0x7e, 0x2d, 0x0b, 0xcf, 0xf5, 0xbb, + 0x09, 0x91, 0x9a, 0xbe, 0x99, 0x5d, 0x19, 0xe0, 0xf6, 0x74, 0xfa, 0x76, 0xb6, 0x0c, 0x53, 0xf7, + 0x15, 0x93, 0xbe, 0xd0, 0xc4, 0x12, 0x3b, 0x43, 0x35, 0xf6, 0xd3, 0x67, 0x7b, 0x8c, 0x68, 0xe1, + 0xa1, 0xd8, 0xe6, 0x3e, 0xae, 0x8b, 0xe6, 0x2d, 0x18, 0x2b, 0xbb, 0xed, 0x80, 0x3e, 0x0a, 0x62, + 0x01, 0x09, 0x38, 0x30, 0xee, 0x9e, 0x2a, 0x51, 0x8d, 0x7f, 0x93, 0x85, 0xe7, 0xfb, 0x5e, 0x05, + 0xc8, 0xa6, 0xde, 0x6b, 0x57, 0x07, 0xb9, 0x3f, 0x9c, 0xde, 0x6d, 0x8b, 0x09, 0xbb, 0xbb, 0x53, + 0x3d, 0xa0, 0x16, 0xfe, 0x87, 0x8c, 0xe8, 0xa4, 0x4f, 0xc3, 0x28, 0x56, 0x15, 0x76, 0x11, 0xd7, + 0x92, 0xe1, 0x2e, 0xec, 0xe8, 0x5a, 0x32, 0x8e, 0x46, 0x6e, 0x42, 0xbe, 0x6c, 0x37, 0x9b, 0x4a, + 0xb8, 0x06, 0x94, 0xe6, 0xeb, 0x08, 0x8b, 0x19, 0x8f, 0x4a, 0x44, 0x26, 0xfb, 0xf0, 0xbf, 0x95, + 0xb3, 0x02, 0x37, 0x4b, 0x41, 0x16, 0x3b, 0x2e, 0x14, 0x64, 0x0c, 0xf2, 0x5a, 0x77, 0x43, 0x87, + 0x70, 0x1e, 0xe4, 0x95, 0x01, 0xb4, 0x20, 0xaf, 0x0c, 0x60, 0xfc, 0x7a, 0x0e, 0x5e, 0xe8, 0x7f, + 0x9f, 0x25, 0x5b, 0xfa, 0x10, 0xbc, 0x36, 0xd0, 0x2d, 0xf8, 0xf4, 0x31, 0x90, 0x21, 0x93, 0x79, + 0x87, 0x5c, 0x49, 0xba, 0xbf, 0x7c, 0xe7, 0xb8, 0xa8, 0x58, 0x24, 0xdf, 0x75, 0x9d, 0xb6, 0xf2, + 0x66, 0xf2, 0x75, 0x4d, 0x32, 0xe4, 0xaf, 0xf7, 0xb7, 0x06, 0xfb, 0xb2, 0x88, 0x8e, 0xef, 0x2b, + 0x83, 0x4a, 0x94, 0x9f, 0x87, 0x42, 0x9c, 0x94, 0x5c, 0x86, 0x21, 0xfc, 0x00, 0xc5, 0x87, 0x27, + 0xc6, 0x01, 0xcb, 0x17, 0xd6, 0xc4, 0xdc, 0xc1, 0x08, 0x16, 0x68, 0x0f, 0xa0, 0xeb, 0x06, 0x45, + 0x04, 0x0b, 0x6e, 0x4e, 0x90, 0xd4, 0x0f, 0xc6, 0x88, 0x8c, 0x3f, 0xcf, 0xc0, 0xc5, 0x9e, 0x9a, + 0x02, 0xb2, 0xa1, 0x0f, 0xd8, 0x2b, 0xa7, 0xa9, 0x16, 0x4e, 0x1d, 0xab, 0x85, 0x1f, 0x97, 0x73, + 0xff, 0x3d, 0x98, 0xa8, 0x75, 0x77, 0xe2, 0xf7, 0x33, 0x1e, 0x5f, 0x46, 0x81, 0xab, 0x27, 0x98, + 0x8a, 0xcf, 0xda, 0x2f, 0x0d, 0x1e, 0x84, 0x01, 0x90, 0x62, 0x75, 0x18, 0xba, 0x58, 0x27, 0x23, + 0x78, 0xe8, 0x44, 0xc6, 0xaf, 0x66, 0xd3, 0x2f, 0xba, 0x77, 0xca, 0x1b, 0x67, 0xb9, 0xe8, 0xde, + 0x29, 0x6f, 0x9c, 0xde, 0xf6, 0xff, 0x46, 0xb6, 0x1d, 0x1f, 0x66, 0xc5, 0x8e, 0x27, 0x15, 0x9d, + 0xe2, 0x61, 0x56, 0xee, 0x8e, 0xbe, 0xfe, 0x30, 0x2b, 0x91, 0xc9, 0x9b, 0x30, 0xb6, 0xea, 0xf2, + 0xe0, 0x1a, 0xb2, 0xc5, 0xdc, 0x07, 0x59, 0x02, 0xd5, 0xed, 0x31, 0xc4, 0x64, 0x77, 0x0b, 0x7d, + 0xe0, 0xa5, 0x71, 0x25, 0xde, 0x2d, 0x62, 0xd3, 0x45, 0x57, 0x07, 0xea, 0x64, 0xc6, 0x7f, 0x32, + 0x0c, 0xc6, 0xe9, 0xca, 0x0c, 0xf2, 0xa1, 0xde, 0x77, 0xd7, 0x06, 0x56, 0x83, 0x0c, 0xb4, 0xe5, + 0x96, 0xba, 0x0d, 0x87, 0xb6, 0xeb, 0x7a, 0x64, 0x0c, 0x01, 0x53, 0xb7, 0x40, 0x89, 0xf7, 0x71, + 0x1c, 0x55, 0x17, 0xfe, 0xbb, 0x5c, 0xb4, 0xd4, 0x62, 0x47, 0x63, 0xe6, 0x63, 0x1c, 0x8d, 0xe4, + 0x1e, 0x14, 0x54, 0x88, 0xf2, 0x42, 0x8b, 0x92, 0x8b, 0xc6, 0x28, 0xf6, 0x51, 0x09, 0x42, 0xfd, + 0x7c, 0xcd, 0x0d, 0x7e, 0xbe, 0x46, 0xe2, 0x3b, 0xd6, 0x3f, 0x94, 0x14, 0xdf, 0xe3, 0xce, 0xe8, + 0x0a, 0xba, 0x8c, 0xa4, 0xe1, 0x8b, 0x43, 0x6b, 0x58, 0x8f, 0xa4, 0x91, 0x72, 0x70, 0xa9, 0xe8, + 0x32, 0x18, 0x08, 0xfe, 0x54, 0x7c, 0xe1, 0xc3, 0x60, 0x20, 0x9c, 0x3e, 0x2d, 0x18, 0x48, 0x48, + 0xc2, 0x0e, 0x40, 0xb3, 0xdb, 0xe6, 0xd1, 0xc4, 0x47, 0xa3, 0x03, 0xd0, 0xeb, 0xb6, 0xad, 0x78, + 0x44, 0xf1, 0x10, 0xd1, 0xf8, 0xc7, 0x43, 0xe9, 0xc2, 0x41, 0xa8, 0xef, 0x3a, 0x8b, 0x70, 0x10, + 0x12, 0x7d, 0x32, 0x33, 0x75, 0x0b, 0x66, 0xa5, 0x7d, 0x9e, 0x34, 0xf4, 0xda, 0x32, 0x57, 0xc5, + 0x10, 0xa3, 0xde, 0x28, 0xb4, 0xec, 0x93, 0xc6, 0x62, 0x56, 0xd7, 0xd3, 0xf4, 0x46, 0x29, 0xf4, + 0x0b, 0xbf, 0x21, 0xd5, 0x62, 0xea, 0x20, 0x6c, 0x6d, 0x85, 0x73, 0x39, 0x36, 0x08, 0xdd, 0xae, + 0x36, 0x8c, 0x3a, 0x09, 0xdf, 0x7b, 0xa5, 0xca, 0x01, 0x99, 0x28, 0xb2, 0xa2, 0xa2, 0xa8, 0x88, + 0x71, 0x89, 0x11, 0x91, 0x3d, 0xb8, 0x18, 0x89, 0xd2, 0xca, 0x4d, 0x01, 0x39, 0xf2, 0x06, 0x5f, + 0x3d, 0x39, 0x2e, 0xbe, 0xa2, 0x88, 0xe2, 0xea, 0x85, 0x23, 0xc6, 0xbd, 0x37, 0x2f, 0xb6, 0xdf, + 0x2e, 0x79, 0x76, 0xbb, 0xbe, 0xaf, 0xcc, 0x79, 0xdc, 0x6f, 0x77, 0x10, 0x9a, 0x08, 0x67, 0x10, + 0x21, 0x1b, 0x3f, 0x9e, 0x85, 0x29, 0x7e, 0x56, 0xf3, 0xd7, 0xb9, 0xa7, 0xf6, 0xe5, 0xf3, 0x6d, + 0xed, 0xe5, 0x53, 0x46, 0xde, 0x53, 0x9b, 0x36, 0xd0, 0xbb, 0xe7, 0x3e, 0x90, 0x24, 0x0d, 0x31, + 0x61, 0x42, 0x85, 0xf6, 0x7f, 0xf2, 0xbc, 0x11, 0x05, 0x69, 0x14, 0xa2, 0x12, 0xbe, 0x3b, 0xfb, + 0xa6, 0xc6, 0xc3, 0xf8, 0xb1, 0x2c, 0x4c, 0x2a, 0x76, 0x2a, 0x4f, 0x6d, 0xc7, 0x7f, 0x5e, 0xeb, + 0xf8, 0xf9, 0xd0, 0x43, 0x30, 0x6c, 0xd9, 0x40, 0xfd, 0xde, 0x85, 0x99, 0x04, 0x49, 0xdc, 0xdc, + 0x27, 0x33, 0x88, 0xb9, 0xcf, 0xeb, 0xc9, 0x88, 0x6f, 0x3c, 0x71, 0x42, 0x18, 0xff, 0x47, 0x0d, + 0x31, 0xf7, 0x93, 0x59, 0x98, 0x13, 0xbf, 0x30, 0x44, 0x2a, 0x17, 0x56, 0x9f, 0xda, 0xb1, 0x28, + 0x69, 0x63, 0x51, 0xd4, 0xc7, 0x42, 0x69, 0x60, 0xef, 0x21, 0x31, 0x7e, 0x18, 0x60, 0xbe, 0x17, + 0xc1, 0xc0, 0x8e, 0xf8, 0x91, 0x6b, 0x62, 0x76, 0x00, 0xd7, 0xc4, 0x55, 0x28, 0x60, 0x55, 0x22, + 0x08, 0xa2, 0xbf, 0x65, 0x56, 0x45, 0x27, 0xa1, 0x7e, 0x81, 0xc7, 0xb1, 0x15, 0x41, 0x19, 0xfd, + 0x98, 0xce, 0x23, 0x41, 0x49, 0x7e, 0x29, 0x03, 0x53, 0x08, 0x5c, 0x7e, 0x48, 0xdb, 0x01, 0x32, + 0x1b, 0x12, 0x3e, 0x6b, 0xe1, 0xc3, 0x68, 0x2d, 0xf0, 0x9c, 0xf6, 0x9e, 0x78, 0x19, 0xdd, 0x11, + 0x2f, 0xa3, 0xef, 0xf0, 0x17, 0xdd, 0x6b, 0x75, 0xb7, 0x75, 0x7d, 0xcf, 0xb3, 0x1f, 0x3a, 0xdc, + 0x04, 0xcb, 0x6e, 0x5e, 0x8f, 0xf2, 0xfd, 0x74, 0x9c, 0x58, 0x26, 0x1e, 0xc1, 0x0a, 0x5f, 0x9d, + 0xf9, 0x87, 0x52, 0xac, 0x36, 0xae, 0x9a, 0xd1, 0xbf, 0x88, 0x7c, 0x0f, 0x5c, 0xe0, 0xa1, 0xc9, + 0xd8, 0x0d, 0xdf, 0x69, 0x77, 0xdd, 0xae, 0xbf, 0x64, 0xd7, 0x0f, 0x98, 0x98, 0xcf, 0x3d, 0x8b, + 0xb1, 0xe5, 0xf5, 0xb0, 0xd0, 0xda, 0xe1, 0xa5, 0x5a, 0xcc, 0x8b, 0x74, 0x06, 0x64, 0x05, 0x66, + 0x78, 0x51, 0xa9, 0x1b, 0xb8, 0xb5, 0xba, 0xdd, 0x74, 0xda, 0x7b, 0x28, 0x4b, 0xe4, 0xb9, 0x28, + 0x63, 0x77, 0x03, 0xd7, 0xf2, 0x39, 0x5c, 0xd5, 0xd4, 0x24, 0x88, 0x48, 0x15, 0xa6, 0x4d, 0x6a, + 0x37, 0xd6, 0xec, 0x47, 0x65, 0xbb, 0x63, 0xd7, 0x9d, 0x80, 0xc7, 0x4a, 0xcd, 0x71, 0x81, 0xce, + 0xa3, 0x76, 0xc3, 0x6a, 0xd9, 0x8f, 0xac, 0xba, 0x28, 0xd4, 0x55, 0xf6, 0x1a, 0x5d, 0xc8, 0xca, + 0x69, 0x87, 0xac, 0xc6, 0xe2, 0xac, 0x9c, 0x76, 0x6f, 0x56, 0x11, 0x9d, 0x64, 0xb5, 0x69, 0x7b, + 0x7b, 0x34, 0xe0, 0x86, 0xd2, 0x70, 0x29, 0x73, 0x25, 0xa3, 0xb0, 0x0a, 0xb0, 0xcc, 0x42, 0xa3, + 0xe9, 0x38, 0x2b, 0x85, 0x8e, 0xcd, 0xbc, 0x6d, 0xcf, 0x09, 0xa8, 0xda, 0xc2, 0x71, 0xfc, 0x2c, + 0xec, 0x7f, 0x34, 0x31, 0xef, 0xd5, 0xc4, 0x04, 0x65, 0xc4, 0x4d, 0x69, 0xe4, 0x44, 0x82, 0x5b, + 0x7a, 0x2b, 0x13, 0x94, 0x21, 0x37, 0xb5, 0x9d, 0x93, 0xd8, 0x4e, 0x85, 0x5b, 0x8f, 0x86, 0x26, + 0x28, 0xc9, 0x3a, 0xeb, 0xb4, 0x80, 0xc9, 0x4d, 0x6e, 0x5b, 0x58, 0x70, 0x4f, 0xe1, 0xa7, 0xbd, + 0x2c, 0xcc, 0x10, 0x0b, 0x9e, 0x2c, 0xb6, 0x52, 0xec, 0xb9, 0xe3, 0xc4, 0xe4, 0xaf, 0xc1, 0xf4, + 0x96, 0x4f, 0x6f, 0x57, 0x37, 0x6a, 0x32, 0x92, 0x19, 0x2a, 0x17, 0xa7, 0x16, 0x6f, 0x9c, 0xb2, + 0xe9, 0x5c, 0x53, 0x69, 0x30, 0x7d, 0x0e, 0x1f, 0xb7, 0xae, 0x4f, 0xad, 0x5d, 0xa7, 0xe3, 0x87, + 0x61, 0x21, 0xd5, 0x71, 0x8b, 0x55, 0x65, 0xac, 0xc0, 0x4c, 0x82, 0x0d, 0x99, 0x02, 0x60, 0x40, + 0x6b, 0x6b, 0xbd, 0xb6, 0xbc, 0x59, 0x78, 0x86, 0x14, 0x60, 0x02, 0x7f, 0x2f, 0xaf, 0x97, 0x96, + 0x56, 0x97, 0x2b, 0x85, 0x0c, 0x99, 0x81, 0x49, 0x84, 0x54, 0xaa, 0x35, 0x0e, 0xca, 0xf2, 0xe4, + 0x09, 0x66, 0x81, 0x2f, 0xdd, 0x80, 0x2d, 0x00, 0x3c, 0x53, 0x8c, 0xbf, 0x9d, 0x85, 0x8b, 0xf2, + 0x58, 0xa1, 0x01, 0x13, 0x1c, 0x9d, 0xf6, 0xde, 0x53, 0x7e, 0x3a, 0xdc, 0xd6, 0x4e, 0x87, 0x97, + 0x63, 0x27, 0x75, 0xac, 0x95, 0x7d, 0x8e, 0x88, 0xdf, 0x1a, 0x83, 0xe7, 0xfb, 0x52, 0x91, 0x0f, + 0xd8, 0x69, 0xee, 0xd0, 0x76, 0x50, 0x6d, 0x34, 0xe9, 0xa6, 0xd3, 0xa2, 0x6e, 0x37, 0x10, 0x1e, + 0x03, 0x2f, 0xa1, 0x3e, 0x0f, 0x0b, 0x2d, 0xa7, 0xd1, 0xa4, 0x56, 0xc0, 0x8b, 0xb5, 0xe9, 0x96, + 0xa4, 0x66, 0x2c, 0xc3, 0x54, 0x5e, 0xd5, 0x76, 0x40, 0xbd, 0x87, 0x68, 0x95, 0x18, 0xb2, 0x3c, + 0xa0, 0xb4, 0x63, 0xd9, 0xac, 0xd4, 0x72, 0x44, 0xb1, 0xce, 0x32, 0x41, 0x4d, 0x6e, 0x2b, 0x2c, + 0xcb, 0xec, 0xf6, 0xbf, 0x66, 0x3f, 0x12, 0x66, 0x52, 0x22, 0x32, 0x6e, 0xc8, 0x92, 0xbb, 0xf3, + 0xb5, 0xec, 0x47, 0x66, 0x92, 0x84, 0x7c, 0x05, 0xce, 0x89, 0x03, 0x48, 0x04, 0x6f, 0x91, 0x2d, + 0xe6, 0xa1, 0x61, 0x5e, 0x3d, 0x39, 0x2e, 0x5e, 0x90, 0x31, 0x85, 0x65, 0x60, 0xa5, 0xb4, 0x56, + 0xa7, 0x73, 0x21, 0x9b, 0xec, 0x40, 0x8e, 0x75, 0xc7, 0x1a, 0xf5, 0x7d, 0xe9, 0xb3, 0x29, 0x6e, + 0xc6, 0x6a, 0x67, 0x5a, 0x2d, 0x5e, 0x6e, 0xf6, 0xa4, 0x24, 0x2b, 0x30, 0xb5, 0x4d, 0x77, 0xd4, + 0xf1, 0x19, 0x09, 0xb7, 0xaa, 0xc2, 0x21, 0xdd, 0xe9, 0x3d, 0x38, 0x31, 0x3a, 0xe2, 0xe0, 0xfb, + 0xc0, 0xa3, 0xa3, 0x55, 0xc7, 0x0f, 0x68, 0x9b, 0x7a, 0x18, 0xbe, 0x6d, 0x14, 0x37, 0x83, 0xf9, + 0x48, 0x42, 0xd6, 0xcb, 0x97, 0x5e, 0x3c, 0x39, 0x2e, 0x3e, 0xcf, 0x9d, 0x9f, 0x9b, 0x02, 0x6e, + 0xc5, 0x12, 0x61, 0x25, 0xb9, 0x92, 0xaf, 0xc1, 0xb4, 0xe9, 0x76, 0x03, 0xa7, 0xbd, 0x57, 0x0b, + 0x3c, 0x3b, 0xa0, 0x7b, 0xfc, 0x40, 0x8a, 0xe2, 0xc4, 0xc5, 0x4a, 0xc5, 0xd3, 0x32, 0x07, 0x5a, + 0xbe, 0x80, 0x6a, 0x27, 0x82, 0x4e, 0x40, 0xbe, 0x0a, 0x53, 0x3c, 0x6c, 0x47, 0x58, 0xc1, 0x98, + 0x96, 0xc4, 0x43, 0x2f, 0x7c, 0x70, 0x43, 0x58, 0xb5, 0x20, 0x34, 0xad, 0x82, 0x18, 0x37, 0xf2, + 0x25, 0xd1, 0x59, 0x1b, 0x4e, 0x7b, 0x2f, 0x9c, 0xc6, 0x80, 0x3d, 0xff, 0x46, 0xd4, 0x25, 0x1d, + 0xf6, 0xb9, 0x72, 0x1a, 0xf7, 0x30, 0xd1, 0x4b, 0xf2, 0x21, 0x01, 0x3c, 0x5f, 0xf2, 0x7d, 0xc7, + 0x0f, 0x84, 0x5f, 0xcd, 0xf2, 0x23, 0x5a, 0xef, 0x32, 0x64, 0x76, 0xbd, 0xa5, 0x1e, 0xb7, 0xeb, + 0x1e, 0x5e, 0xba, 0x76, 0x72, 0x5c, 0x7c, 0xcd, 0x46, 0x44, 0x4b, 0xb8, 0xe2, 0x58, 0x54, 0xa2, + 0x5a, 0x87, 0x1c, 0x57, 0x69, 0x43, 0x7f, 0xa6, 0xe4, 0xab, 0x70, 0xbe, 0x6c, 0xfb, 0xb4, 0xda, + 0xf6, 0x69, 0xdb, 0x77, 0x02, 0xe7, 0x21, 0x15, 0x9d, 0x8a, 0x87, 0x5f, 0x1e, 0x53, 0x86, 0x19, + 0x75, 0xdb, 0x67, 0x0b, 0x33, 0x44, 0xb1, 0xc4, 0xa0, 0x28, 0xd5, 0xf4, 0xe0, 0x42, 0x4c, 0x98, + 0xaa, 0xd5, 0x56, 0x2a, 0x8e, 0x1d, 0xae, 0xab, 0x49, 0xec, 0xaf, 0xd7, 0x50, 0xb5, 0xe7, 0xef, + 0x5b, 0x0d, 0xc7, 0x0e, 0x17, 0x54, 0x8f, 0xce, 0x8a, 0x71, 0x30, 0x8e, 0x33, 0x50, 0x88, 0x0f, + 0x25, 0xf9, 0x22, 0x8c, 0x71, 0xfb, 0x36, 0xea, 0xef, 0x8b, 0xc8, 0x13, 0xd2, 0x5c, 0x2a, 0x84, + 0xeb, 0x44, 0xc2, 0x9d, 0x8e, 0x5b, 0xcf, 0x51, 0xd5, 0x5a, 0x06, 0xdd, 0xe9, 0x24, 0x11, 0x69, + 0xc0, 0x04, 0x1f, 0x2d, 0x8a, 0x41, 0x22, 0x85, 0x99, 0xf3, 0x8b, 0xea, 0xea, 0x10, 0x45, 0x31, + 0xfe, 0xf8, 0x6a, 0x28, 0xe6, 0x04, 0x47, 0xd0, 0xaa, 0xd0, 0xb8, 0x2e, 0x01, 0xe4, 0x25, 0xa1, + 0x71, 0x11, 0x2e, 0xf4, 0xf8, 0x66, 0xe3, 0x21, 0x5a, 0x12, 0xf4, 0xa8, 0x91, 0x7c, 0x11, 0xe6, + 0x90, 0xb0, 0xec, 0xb6, 0xdb, 0xb4, 0x1e, 0xe0, 0x76, 0x24, 0xb5, 0xef, 0x39, 0x6e, 0xe9, 0xc2, + 0xdb, 0x5b, 0x0f, 0x11, 0xac, 0xb8, 0x12, 0x3e, 0x95, 0x83, 0xf1, 0xf3, 0x59, 0x98, 0x17, 0x3b, + 0x9c, 0x49, 0xeb, 0xae, 0xd7, 0x78, 0xfa, 0x4f, 0xd4, 0x65, 0xed, 0x44, 0x7d, 0x29, 0x0c, 0x5b, + 0x94, 0xd6, 0xc8, 0x3e, 0x07, 0xea, 0xaf, 0x66, 0xe0, 0xb9, 0x7e, 0x44, 0xac, 0x77, 0xc2, 0xa0, + 0x98, 0x63, 0x89, 0xe0, 0x97, 0x1d, 0x98, 0xc5, 0x01, 0x2d, 0xef, 0xd3, 0xfa, 0x81, 0xbf, 0xe2, + 0xfa, 0x01, 0x7a, 0x5a, 0x64, 0x7b, 0xbc, 0x75, 0xbf, 0x9e, 0xfa, 0xd6, 0x7d, 0x9e, 0xcf, 0xb2, + 0x3a, 0xf2, 0xe0, 0x61, 0x3b, 0x0f, 0xe8, 0x91, 0x6f, 0xa6, 0xb1, 0x46, 0x8b, 0xf9, 0x52, 0x37, + 0xd8, 0xdf, 0xf0, 0xe8, 0x2e, 0xf5, 0x68, 0xbb, 0x4e, 0xbf, 0xcb, 0x2c, 0xe6, 0xf5, 0xc6, 0x0d, + 0xa4, 0xc1, 0xf8, 0x9d, 0x49, 0x98, 0x4b, 0x23, 0x63, 0xfd, 0xa2, 0x5c, 0x9a, 0xe3, 0x19, 0x4d, + 0xbf, 0x99, 0x81, 0x89, 0x1a, 0xad, 0xbb, 0xed, 0xc6, 0x6d, 0xb4, 0x28, 0x12, 0xbd, 0x63, 0x73, + 0xa1, 0x81, 0xc1, 0xad, 0xdd, 0x98, 0xa9, 0xd1, 0x77, 0x8e, 0x8b, 0x5f, 0x18, 0xec, 0xae, 0x5a, + 0x77, 0x31, 0x5c, 0x50, 0x80, 0x19, 0x37, 0xc2, 0x2a, 0xf8, 0xd7, 0x98, 0x5a, 0xb5, 0x64, 0x09, + 0x26, 0xc5, 0x82, 0x75, 0xd5, 0xa8, 0xa8, 0x3c, 0xb6, 0x93, 0x2c, 0x48, 0x28, 0xaf, 0x35, 0x12, + 0x72, 0x13, 0x72, 0x5b, 0x8b, 0xb7, 0xc5, 0x28, 0xc8, 0xac, 0x25, 0x5b, 0x8b, 0xb7, 0x51, 0x21, + 0xc6, 0x2e, 0x19, 0x93, 0xdd, 0x45, 0xcd, 0xcc, 0x67, 0x6b, 0xf1, 0x36, 0xf9, 0xeb, 0x70, 0xae, + 0xe2, 0xf8, 0xa2, 0x0a, 0xee, 0xbd, 0xd1, 0x40, 0x9f, 0xc5, 0x91, 0x1e, 0xf3, 0xf7, 0xb3, 0xa9, + 0xf3, 0xf7, 0xc5, 0x46, 0xc8, 0xc4, 0xe2, 0xae, 0x21, 0x8d, 0x78, 0xf4, 0xd7, 0xf4, 0x7a, 0xc8, + 0x47, 0x30, 0x85, 0xea, 0x6c, 0x74, 0x68, 0xc1, 0xb8, 0xfd, 0xa3, 0x3d, 0x6a, 0xfe, 0x74, 0x6a, + 0xcd, 0x0b, 0x3c, 0xe2, 0x06, 0xba, 0xc5, 0x60, 0x8c, 0x7f, 0xed, 0xde, 0xaf, 0x71, 0x26, 0x77, + 0x61, 0x5a, 0x08, 0x60, 0xf7, 0x77, 0x37, 0xf7, 0x69, 0xc5, 0x3e, 0x12, 0x16, 0x3a, 0x78, 0xa7, + 0x13, 0x52, 0x9b, 0xe5, 0xee, 0x5a, 0xc1, 0x3e, 0xb5, 0x1a, 0xb6, 0x26, 0xaa, 0xc4, 0x08, 0xc9, + 0x37, 0x60, 0x7c, 0xd5, 0xad, 0x33, 0xd9, 0x1b, 0xf7, 0x06, 0x6e, 0xb4, 0xf3, 0x21, 0x66, 0xcd, + 0xe4, 0xe0, 0x98, 0x40, 0xf5, 0x9d, 0xe3, 0xe2, 0xdb, 0x67, 0x9d, 0x36, 0x4a, 0x05, 0xa6, 0x5a, + 0x1b, 0x29, 0x43, 0x7e, 0x9b, 0xee, 0xb0, 0xd6, 0xc6, 0x33, 0xea, 0x49, 0xb0, 0xb0, 0xc9, 0x13, + 0xbf, 0x34, 0x9b, 0x3c, 0x01, 0x23, 0x1e, 0xcc, 0x60, 0xff, 0x6c, 0xd8, 0xbe, 0x7f, 0xe8, 0x7a, + 0x0d, 0x4c, 0x9d, 0xd2, 0xcb, 0x1e, 0x68, 0x31, 0xb5, 0xf3, 0x9f, 0xe3, 0x9d, 0xdf, 0x51, 0x38, + 0xa8, 0x22, 0x64, 0x82, 0x3d, 0xf9, 0x1a, 0x4c, 0x89, 0xe8, 0x05, 0x6b, 0xb7, 0x4b, 0xb8, 0x12, + 0x26, 0x34, 0xcf, 0x4f, 0xbd, 0x90, 0xcb, 0xa9, 0x22, 0x18, 0x82, 0xd4, 0x41, 0x59, 0xad, 0x5d, + 0x5b, 0x57, 0xfb, 0xab, 0x24, 0x64, 0x03, 0xc6, 0x2b, 0x98, 0xd7, 0x19, 0xbd, 0xd3, 0x84, 0x65, + 0x78, 0x98, 0x12, 0x2c, 0x2a, 0xe1, 0xda, 0x18, 0x91, 0x02, 0x1a, 0x7d, 0xdd, 0x74, 0x6b, 0xdd, + 0x10, 0x91, 0xdc, 0x82, 0x5c, 0xb5, 0xb2, 0x21, 0x0c, 0xc3, 0x67, 0xc2, 0x18, 0x21, 0x1b, 0x32, + 0x81, 0x12, 0x5a, 0xd0, 0x39, 0x0d, 0xcd, 0xac, 0xbc, 0x5a, 0xd9, 0x20, 0xbb, 0x30, 0x89, 0x1d, + 0xb0, 0x42, 0x6d, 0xde, 0xb7, 0xd3, 0x3d, 0xfa, 0xf6, 0x5a, 0x6a, 0xdf, 0xce, 0xf3, 0xbe, 0xdd, + 0x17, 0xd4, 0x5a, 0x46, 0x18, 0x95, 0x2d, 0x13, 0x6a, 0x45, 0x96, 0x2a, 0x99, 0xc7, 0x64, 0x73, + 0x15, 0x2d, 0x84, 0x84, 0x50, 0x2b, 0x93, 0x5a, 0x85, 0x89, 0x55, 0x7a, 0xfa, 0x9d, 0x24, 0xf9, + 0x90, 0xcf, 0xc3, 0xd0, 0xfd, 0x83, 0xc0, 0x16, 0x26, 0xe0, 0xb2, 0x1f, 0x19, 0x48, 0x36, 0x1f, + 0xf5, 0x90, 0xee, 0x81, 0x16, 0x75, 0x0e, 0x69, 0xd8, 0x50, 0xac, 0xd8, 0x5e, 0xe3, 0xd0, 0xf6, + 0xd0, 0x45, 0x78, 0x56, 0x63, 0xa1, 0x94, 0xf0, 0xa1, 0xd8, 0x17, 0x80, 0x98, 0xdf, 0xb0, 0xca, + 0x82, 0x7c, 0x0f, 0x5c, 0xf4, 0x9d, 0xbd, 0xb6, 0x1d, 0x74, 0x3d, 0x6a, 0xd9, 0xcd, 0x3d, 0xd7, + 0x73, 0x82, 0xfd, 0x96, 0xe5, 0x77, 0x9d, 0x80, 0xce, 0xcf, 0x69, 0x39, 0xad, 0x6b, 0x12, 0xaf, + 0x24, 0xd1, 0x6a, 0x0c, 0xcb, 0xbc, 0xe0, 0xa7, 0x17, 0x90, 0x2f, 0xc1, 0xa4, 0xba, 0x25, 0xfb, + 0xf3, 0xe7, 0x2e, 0xe5, 0xae, 0x4c, 0x85, 0x57, 0x8f, 0xf8, 0x16, 0x2e, 0x63, 0x41, 0x2b, 0x67, + 0x84, 0xaf, 0xc7, 0x82, 0x56, 0x78, 0x85, 0x59, 0x22, 0x49, 0x61, 0xd6, 0x9c, 0x11, 0x33, 0x56, + 0xf4, 0xf2, 0xda, 0xed, 0x92, 0x39, 0xba, 0x51, 0x7d, 0x50, 0x6b, 0xba, 0x81, 0xf1, 0x5f, 0x64, + 0x70, 0x13, 0x27, 0xaf, 0x61, 0x28, 0xa9, 0xf0, 0xfd, 0x0c, 0x35, 0xb8, 0x76, 0x27, 0x96, 0x48, + 0x80, 0xa3, 0x90, 0xd7, 0x61, 0xe4, 0xb6, 0x5d, 0x97, 0x61, 0x6c, 0x04, 0xf2, 0x2e, 0x42, 0x54, + 0x75, 0x2f, 0xc7, 0x61, 0x12, 0x26, 0x9f, 0xdc, 0xa5, 0x28, 0x5d, 0x7a, 0xb9, 0x24, 0x1f, 0xec, + 0x51, 0xc2, 0x14, 0x8b, 0x42, 0xc9, 0xa7, 0x1e, 0xb3, 0x8b, 0x4f, 0xe5, 0x60, 0xfc, 0x69, 0x26, + 0xda, 0x95, 0xc8, 0xab, 0x30, 0x64, 0x6e, 0x84, 0xdf, 0xcf, 0xdd, 0x7e, 0x63, 0x9f, 0x8f, 0x08, + 0xe4, 0x4b, 0x70, 0x4e, 0xe1, 0x93, 0x30, 0xd2, 0x7f, 0x05, 0xbd, 0x52, 0x95, 0x2f, 0x49, 0xb7, + 0xd4, 0x4f, 0xe7, 0x81, 0xe2, 0x74, 0x54, 0x50, 0xa1, 0x6d, 0x87, 0xf3, 0x56, 0x1a, 0xab, 0xf2, + 0x6e, 0x20, 0x42, 0xbc, 0xb1, 0x69, 0x1c, 0xb8, 0x53, 0xaa, 0xf1, 0x9b, 0x19, 0x6d, 0xb7, 0x09, + 0xf3, 0x4b, 0x67, 0x4e, 0xc9, 0x2f, 0xfd, 0x16, 0x40, 0xa9, 0x1b, 0xb8, 0xcb, 0x6d, 0xcf, 0x6d, + 0x72, 0x3d, 0x8a, 0xc8, 0xa5, 0x81, 0xda, 0x61, 0x8a, 0x60, 0xcd, 0x77, 0x2e, 0x44, 0x4e, 0xf5, + 0x67, 0xc8, 0x7d, 0x5c, 0x7f, 0x06, 0xe3, 0xf7, 0x33, 0xda, 0x1a, 0x65, 0x72, 0xa2, 0x98, 0x8a, + 0xaa, 0xcd, 0x58, 0xc7, 0x79, 0x68, 0xf9, 0x4d, 0x57, 0x0b, 0x58, 0x21, 0xd0, 0xc8, 0xbf, 0x9b, + 0x81, 0xf3, 0xdc, 0x31, 0x60, 0xbd, 0xdb, 0xda, 0xa1, 0xde, 0x03, 0xbb, 0xe9, 0x34, 0xa2, 0x40, + 0x7d, 0x91, 0x01, 0xa1, 0x52, 0x4d, 0x3a, 0x3e, 0xbf, 0xaa, 0x72, 0x47, 0x05, 0xab, 0x8d, 0x85, + 0xd6, 0xc3, 0xb0, 0x54, 0xbd, 0xaa, 0xa6, 0xd3, 0x1b, 0xbf, 0x96, 0x81, 0x17, 0x4f, 0xad, 0x85, + 0x5c, 0x87, 0x51, 0x99, 0xc4, 0x24, 0x83, 0x1d, 0x8f, 0x96, 0xb6, 0xc9, 0x04, 0x26, 0x12, 0x8b, + 0x7c, 0x19, 0xce, 0xa9, 0xac, 0x36, 0x3d, 0xdb, 0x51, 0x53, 0x85, 0xa4, 0x7c, 0x75, 0xc0, 0x50, + 0xe2, 0xd2, 0x5a, 0x3a, 0x13, 0xe3, 0xff, 0xcd, 0x28, 0x19, 0xe7, 0x9f, 0x52, 0x29, 0xfe, 0x96, + 0x26, 0xc5, 0xcb, 0x30, 0xa5, 0x61, 0xab, 0x58, 0x59, 0xea, 0xcd, 0x6b, 0x5a, 0xb1, 0x18, 0x47, + 0xc0, 0x8f, 0x64, 0x61, 0x7c, 0xcb, 0xa7, 0x1e, 0x7f, 0xca, 0xfd, 0xee, 0x0a, 0xd6, 0x18, 0xb6, + 0x6b, 0xa0, 0x70, 0x7a, 0x7f, 0x9c, 0x41, 0x15, 0xbf, 0x4a, 0xc1, 0x7a, 0x43, 0xc9, 0x32, 0x89, + 0xbd, 0x81, 0xf9, 0x25, 0x11, 0xca, 0x83, 0x8b, 0xad, 0xea, 0x09, 0x67, 0x31, 0xeb, 0xf0, 0x2a, + 0xf9, 0x02, 0x0c, 0x6f, 0xa1, 0xc2, 0x52, 0x0f, 0xb3, 0x11, 0xf2, 0xc7, 0x42, 0xbe, 0x49, 0x77, + 0x7d, 0x3d, 0xf2, 0x1c, 0x27, 0x24, 0x35, 0x18, 0x2d, 0x7b, 0x14, 0xf3, 0xc7, 0x0f, 0x0d, 0xee, + 0x24, 0x5e, 0xe7, 0x24, 0x71, 0x27, 0x71, 0xc1, 0xc9, 0xf8, 0xb9, 0x2c, 0x90, 0xa8, 0x8d, 0x98, + 0x2c, 0xcd, 0x7f, 0x6a, 0x07, 0xfd, 0x7d, 0x6d, 0xd0, 0x9f, 0x4f, 0x0c, 0x3a, 0x6f, 0xde, 0x40, + 0x63, 0xff, 0xdb, 0x19, 0x38, 0x9f, 0x4e, 0x48, 0x5e, 0x82, 0x91, 0xfb, 0x9b, 0x1b, 0x32, 0x52, + 0x8b, 0x68, 0x8a, 0xdb, 0x41, 0x6d, 0x81, 0x29, 0x8a, 0xc8, 0x1b, 0x30, 0xf2, 0x81, 0x59, 0x66, + 0xe7, 0x90, 0x92, 0x8e, 0xe3, 0xeb, 0x9e, 0x55, 0xd7, 0x8f, 0x22, 0x81, 0xa4, 0x8e, 0x6d, 0xee, + 0x89, 0x8d, 0xed, 0x4f, 0x66, 0x61, 0xba, 0x54, 0xaf, 0x53, 0xdf, 0x17, 0xa1, 0xe6, 0x9f, 0xda, + 0x81, 0x4d, 0x8f, 0xc1, 0xa2, 0xb5, 0x6d, 0xa0, 0x51, 0xfd, 0xdd, 0x0c, 0x9c, 0x93, 0x54, 0x0f, + 0x1d, 0x7a, 0xb8, 0xb9, 0xef, 0x51, 0x7f, 0xdf, 0x6d, 0x36, 0x06, 0xce, 0xf9, 0xc3, 0x04, 0x3d, + 0x0c, 0x0f, 0xaf, 0xbe, 0xeb, 0xef, 0x22, 0x44, 0x13, 0xf4, 0x78, 0x08, 0xf9, 0xeb, 0x30, 0x5a, + 0xea, 0x74, 0x3c, 0xf7, 0x21, 0x5f, 0xf6, 0x22, 0xb6, 0xa4, 0xcd, 0x41, 0x9a, 0x8f, 0x3d, 0x07, + 0xb1, 0xcf, 0xa8, 0xd0, 0x36, 0x0f, 0xe6, 0x37, 0xc9, 0x3f, 0xa3, 0x41, 0xdb, 0xaa, 0x2c, 0x8e, + 0xe5, 0x46, 0x0d, 0xc8, 0x86, 0xe7, 0xb6, 0xdc, 0x80, 0x36, 0x78, 0x7b, 0x30, 0x34, 0xc1, 0xa9, + 0x41, 0xb5, 0x36, 0x9d, 0xa0, 0xa9, 0x05, 0xd5, 0x0a, 0x18, 0xc0, 0xe4, 0x70, 0xe3, 0xff, 0x1e, + 0x86, 0x09, 0xb5, 0x77, 0x88, 0xc1, 0x13, 0x79, 0xb8, 0x9e, 0x1a, 0x1f, 0xc3, 0x46, 0x88, 0x29, + 0x4a, 0xa2, 0xe0, 0x32, 0xd9, 0x53, 0x83, 0xcb, 0x6c, 0xc3, 0xe4, 0x86, 0xe7, 0x62, 0x10, 0x4c, + 0x7c, 0xaf, 0x14, 0x5b, 0xe1, 0xac, 0x72, 0xef, 0x64, 0x03, 0x89, 0x2f, 0xa2, 0x28, 0xd9, 0x77, + 0x04, 0x36, 0xa6, 0xb7, 0xd4, 0xb4, 0x2e, 0x1a, 0x1f, 0x6e, 0x6c, 0x61, 0xfb, 0x22, 0x92, 0x6d, + 0x68, 0x6c, 0xc1, 0x20, 0xba, 0xb1, 0x05, 0x83, 0xa8, 0x6b, 0x6d, 0xf8, 0x49, 0xad, 0x35, 0xf2, + 0x73, 0x19, 0x18, 0x2f, 0xb5, 0xdb, 0x22, 0x68, 0xcd, 0x29, 0xfe, 0xfa, 0x5f, 0x16, 0xf6, 0x16, + 0x6f, 0x7f, 0x2c, 0x7b, 0x0b, 0x94, 0x5b, 0x7c, 0x94, 0x54, 0xa3, 0x0a, 0xd5, 0xdb, 0x9a, 0xf2, + 0x1d, 0xe4, 0x6d, 0x28, 0x84, 0x93, 0xbc, 0xda, 0x6e, 0xd0, 0x47, 0x94, 0x27, 0x42, 0x9c, 0x14, + 0x91, 0xb5, 0x55, 0xc9, 0x34, 0x8e, 0x48, 0x36, 0x01, 0xec, 0x70, 0x76, 0xc5, 0x32, 0xba, 0x26, + 0xa7, 0x9f, 0x90, 0x9e, 0xf1, 0x37, 0x3e, 0x69, 0xa9, 0xd2, 0x73, 0xc4, 0x87, 0xb4, 0x60, 0x9a, + 0xa7, 0x53, 0xad, 0x05, 0xb6, 0x17, 0x60, 0x32, 0x0a, 0x38, 0x75, 0x1c, 0x5e, 0x15, 0xfa, 0xb3, + 0x67, 0x45, 0x92, 0x56, 0x9f, 0xd1, 0x5a, 0x29, 0x99, 0x29, 0xe2, 0xbc, 0x79, 0x1c, 0x73, 0xf3, + 0x42, 0xf2, 0x7b, 0xf9, 0xa4, 0xff, 0xc9, 0x0c, 0x9c, 0x57, 0x27, 0x7d, 0xad, 0xbb, 0x23, 0x82, + 0x87, 0x92, 0x6b, 0x30, 0x26, 0xe6, 0x64, 0x78, 0x89, 0x4a, 0xe6, 0xd4, 0x88, 0x50, 0xc8, 0x32, + 0x9b, 0x86, 0x8c, 0x87, 0x90, 0xba, 0x67, 0x63, 0xfb, 0x14, 0x2b, 0x8a, 0x52, 0x75, 0x7b, 0xf8, + 0x5b, 0x9f, 0x9f, 0x0c, 0x62, 0xbc, 0x07, 0x33, 0xfa, 0x48, 0xd4, 0x68, 0x40, 0xae, 0xc2, 0xa8, + 0x1c, 0xbe, 0x4c, 0xfa, 0xf0, 0xc9, 0x72, 0x63, 0x1b, 0x48, 0x82, 0xde, 0x47, 0xc3, 0x28, 0x76, + 0x3f, 0xe5, 0x86, 0x7b, 0xf2, 0x59, 0x32, 0x81, 0xb8, 0x34, 0x2b, 0xbe, 0x6f, 0x5c, 0x73, 0x4c, + 0xc0, 0x40, 0xaa, 0xff, 0x7c, 0x1a, 0x66, 0x53, 0xf6, 0xdc, 0x53, 0x64, 0xa2, 0xa2, 0xbe, 0x41, + 0x8c, 0x85, 0xe1, 0x3e, 0xe4, 0xb6, 0xf0, 0x1e, 0x0c, 0x9f, 0xba, 0x1d, 0x70, 0xb7, 0x94, 0xd8, + 0x2e, 0xc0, 0xc9, 0x3e, 0x11, 0xb9, 0x48, 0x8d, 0xc8, 0x33, 0xfc, 0xc4, 0x22, 0xf2, 0x2c, 0xc1, + 0xa4, 0x68, 0x95, 0xd8, 0xae, 0x14, 0xf3, 0x68, 0x99, 0x23, 0x26, 0xb1, 0x6d, 0xe9, 0x24, 0x9c, + 0x87, 0xef, 0x36, 0x1f, 0x52, 0xc1, 0x63, 0x54, 0xe5, 0x81, 0x05, 0xa9, 0x3c, 0x14, 0x12, 0xf2, + 0x1f, 0x63, 0x2a, 0x47, 0x84, 0xa8, 0x7b, 0x56, 0xbe, 0xdf, 0x9e, 0xd5, 0x78, 0x32, 0x7b, 0xd6, + 0xf3, 0xf2, 0x1b, 0xd3, 0xf7, 0xae, 0x94, 0xcf, 0x22, 0xbf, 0x9c, 0x81, 0x19, 0x1e, 0x16, 0x46, + 0xfd, 0xd8, 0xbe, 0xa1, 0x3e, 0xea, 0x4f, 0xe6, 0x63, 0x9f, 0x13, 0xd9, 0x81, 0xd2, 0xbf, 0x35, + 0xf9, 0x51, 0xe4, 0x7b, 0x00, 0xc2, 0x15, 0xc5, 0x83, 0xc9, 0x8e, 0x2f, 0x3e, 0x97, 0xb2, 0x0b, + 0x84, 0x48, 0x51, 0x12, 0x8f, 0x20, 0xa4, 0xd3, 0x12, 0x78, 0x86, 0x50, 0xf2, 0xd7, 0x61, 0x8e, + 0xad, 0x97, 0x10, 0x22, 0x82, 0x58, 0xcd, 0x8f, 0x63, 0x2d, 0x9f, 0xe9, 0x2d, 0x13, 0x5d, 0x4b, + 0x23, 0xe3, 0xa1, 0x87, 0xa3, 0x5c, 0xea, 0x81, 0x1a, 0xef, 0x22, 0xb5, 0x22, 0x8c, 0x0d, 0x87, + 0x5f, 0xcf, 0x13, 0x6d, 0xf4, 0xd8, 0xdf, 0x2e, 0xca, 0xb5, 0xc0, 0xf7, 0x37, 0x5f, 0xf7, 0x52, + 0x46, 0x10, 0xf9, 0x00, 0x48, 0x18, 0x4f, 0x85, 0xc3, 0xa8, 0x4c, 0xc2, 0xc1, 0xd5, 0xcd, 0x51, + 0x5c, 0x16, 0x4f, 0x16, 0xab, 0x93, 0x24, 0x49, 0x4c, 0x28, 0xcc, 0x89, 0x46, 0x33, 0xa8, 0xcc, + 0xb3, 0xe8, 0xcf, 0x4f, 0x69, 0x21, 0xc2, 0xa2, 0x92, 0x28, 0xe9, 0xba, 0x92, 0xac, 0x51, 0x53, + 0x39, 0xa5, 0xb1, 0x23, 0xb7, 0x60, 0x0c, 0x5d, 0x85, 0x57, 0xa4, 0xb9, 0x97, 0x30, 0x3d, 0x41, + 0xa7, 0x62, 0x6b, 0x5f, 0x37, 0xda, 0x8a, 0x50, 0xd9, 0x75, 0xa0, 0xe2, 0x1d, 0x99, 0xdd, 0x36, + 0x2a, 0x85, 0x85, 0xbe, 0xa3, 0xe1, 0x1d, 0x59, 0x5e, 0x57, 0xf7, 0x25, 0x47, 0x24, 0xf2, 0x35, + 0x18, 0x5f, 0xb3, 0x1f, 0x85, 0x99, 0xa6, 0x66, 0x06, 0xcf, 0x67, 0xd5, 0xb2, 0x1f, 0x85, 0x69, + 0xa6, 0xe2, 0xf9, 0xac, 0x14, 0x96, 0xe4, 0x2b, 0x00, 0x8a, 0xa6, 0x9a, 0x9c, 0x5a, 0xc1, 0x8b, + 0x32, 0xf0, 0x5d, 0xaa, 0x06, 0x1b, 0xf9, 0x2b, 0x0c, 0x63, 0x92, 0xc3, 0xdc, 0x27, 0x27, 0x39, + 0x9c, 0xfb, 0xe4, 0x24, 0x07, 0xfe, 0x50, 0xc2, 0xc7, 0x1e, 0x77, 0xf0, 0xa3, 0xf9, 0xf3, 0xa7, + 0xd6, 0xf6, 0x9c, 0x34, 0x26, 0xc4, 0xa3, 0xe0, 0x28, 0x56, 0x45, 0x8c, 0xdf, 0xc2, 0x0e, 0x5c, + 0xec, 0xb9, 0x38, 0x53, 0x02, 0x2b, 0x5f, 0xd7, 0x03, 0x2b, 0x5f, 0xec, 0x75, 0x88, 0xfb, 0x7a, + 0x36, 0x97, 0xd9, 0xc2, 0x5c, 0x6f, 0xf9, 0xe7, 0xdb, 0xd9, 0xd8, 0xa1, 0x2e, 0xae, 0x2e, 0x3c, + 0x93, 0x58, 0x2f, 0xa9, 0x27, 0x8b, 0x69, 0xbe, 0xf9, 0xb1, 0xaf, 0xc4, 0x9e, 0x67, 0xc7, 0xbe, + 0x2a, 0x36, 0xa0, 0x00, 0xf0, 0xb8, 0xe7, 0xfb, 0x3b, 0x30, 0xc5, 0x33, 0xf3, 0xde, 0xa3, 0x47, + 0x87, 0xae, 0xd7, 0xe0, 0x39, 0x92, 0x84, 0x94, 0x9f, 0x48, 0xab, 0x1f, 0xc3, 0x25, 0x15, 0xe9, + 0xdf, 0x3a, 0x8c, 0xb5, 0x5f, 0x4c, 0xdd, 0x27, 0x19, 0x42, 0x3f, 0xd7, 0x57, 0xf2, 0x66, 0x28, + 0x0a, 0x52, 0x4f, 0xcd, 0xe9, 0xe2, 0x49, 0x60, 0x8a, 0x44, 0x48, 0x3d, 0xe3, 0x5f, 0xe6, 0x80, + 0xf0, 0x9a, 0xca, 0x76, 0xc7, 0x46, 0xef, 0x6f, 0x07, 0xe3, 0x39, 0x15, 0x04, 0x8e, 0xbd, 0xd3, + 0xa4, 0x6a, 0x30, 0x34, 0x61, 0xc0, 0x1b, 0x96, 0x59, 0xf1, 0xab, 0x54, 0x82, 0xb0, 0xc7, 0x66, + 0x9a, 0x7d, 0x9c, 0xcd, 0xf4, 0x6b, 0xf0, 0x6c, 0xa9, 0x83, 0x29, 0xbe, 0x65, 0x2d, 0xb7, 0x5d, + 0x4f, 0x4e, 0x5d, 0xcd, 0xaf, 0xd0, 0x0e, 0xd1, 0x12, 0x5f, 0xda, 0x8f, 0x85, 0x22, 0x09, 0xb1, + 0x79, 0xd9, 0x09, 0xd4, 0x38, 0x15, 0x52, 0x12, 0xea, 0x60, 0x49, 0x8a, 0x24, 0xc4, 0x49, 0x24, + 0x0f, 0xc7, 0x93, 0x92, 0x10, 0x66, 0x44, 0x8b, 0x78, 0x38, 0x1e, 0xed, 0x21, 0x4d, 0x85, 0x24, + 0xe4, 0x1d, 0x18, 0x2f, 0x75, 0x03, 0x57, 0x30, 0x16, 0x96, 0xe7, 0x91, 0x8d, 0xb8, 0xf8, 0x14, + 0xed, 0x72, 0x15, 0xa1, 0x1b, 0x7f, 0x92, 0x83, 0x8b, 0xc9, 0xe1, 0x15, 0xa5, 0xe1, 0xfa, 0xc8, + 0x9c, 0xb2, 0x3e, 0xd2, 0x66, 0x43, 0x36, 0xca, 0x66, 0xf1, 0x24, 0x66, 0x03, 0xcf, 0x14, 0xfe, + 0x31, 0x67, 0x43, 0x0d, 0xc6, 0xd5, 0x13, 0x75, 0xe8, 0xe3, 0x9e, 0xa8, 0x2a, 0x17, 0x72, 0x15, + 0x86, 0x79, 0x78, 0x8e, 0xe1, 0xe8, 0x71, 0x2a, 0x1e, 0x99, 0x83, 0x63, 0x90, 0x7f, 0x07, 0x2e, + 0xf1, 0x3d, 0x29, 0xde, 0xd8, 0xa5, 0x23, 0xc9, 0x51, 0x0c, 0xdc, 0xe2, 0xc9, 0x71, 0xf1, 0x1a, + 0x57, 0xc6, 0x58, 0x89, 0x6e, 0xb3, 0x76, 0x8e, 0x2c, 0xf9, 0x65, 0x4a, 0x25, 0xa7, 0xf2, 0x36, + 0xca, 0x70, 0x51, 0x94, 0x46, 0x8e, 0xe1, 0xb2, 0x90, 0x0d, 0xf2, 0x41, 0xa4, 0x4f, 0xc3, 0x41, + 0x8e, 0xa9, 0xca, 0xb0, 0x1c, 0x73, 0x8c, 0x2b, 0xf9, 0x9f, 0xdf, 0x48, 0xf3, 0xeb, 0xe1, 0x91, + 0xc1, 0x39, 0x58, 0x77, 0xe9, 0x91, 0x5a, 0xbb, 0x6c, 0xaa, 0xd6, 0x4e, 0xaa, 0x7d, 0x72, 0xa9, + 0x6a, 0x9f, 0x0a, 0x4c, 0xd7, 0xba, 0x3b, 0xb2, 0xee, 0xb8, 0x4f, 0xa8, 0xdf, 0xdd, 0x49, 0xeb, + 0x95, 0x38, 0x89, 0xf1, 0xa3, 0x59, 0x98, 0xd8, 0x68, 0x76, 0xf7, 0x9c, 0x76, 0xc5, 0x0e, 0xec, + 0xa7, 0x56, 0x91, 0xf8, 0x96, 0xa6, 0x48, 0x0c, 0xdd, 0xd7, 0xc2, 0x86, 0x0d, 0xa4, 0x45, 0xfc, + 0xd9, 0x0c, 0x4c, 0x47, 0x24, 0xfc, 0xb0, 0x5e, 0x81, 0x21, 0xf6, 0x43, 0x5c, 0xaf, 0x2f, 0x25, + 0x18, 0xf3, 0x54, 0x96, 0xe1, 0x5f, 0x42, 0xb5, 0xa7, 0xe7, 0x89, 0x43, 0x0e, 0x0b, 0x9f, 0x85, + 0xb1, 0x88, 0xed, 0x59, 0x52, 0x58, 0xfe, 0x7a, 0x06, 0x0a, 0xf1, 0x96, 0x90, 0x7b, 0x30, 0xca, + 0x38, 0x39, 0x54, 0xde, 0xfc, 0x5f, 0xee, 0xd1, 0xe6, 0x6b, 0x02, 0x8d, 0x7f, 0x1e, 0x76, 0x3e, + 0xe5, 0x10, 0x53, 0x72, 0x58, 0x30, 0x61, 0x42, 0xc5, 0x4a, 0xf9, 0xba, 0xd7, 0x75, 0x09, 0xe5, + 0x7c, 0x7a, 0x3f, 0x68, 0x89, 0x37, 0xb5, 0xaf, 0x16, 0xc2, 0xc7, 0x65, 0x6d, 0x72, 0xa5, 0xae, + 0x2a, 0x9c, 0x34, 0x8b, 0x51, 0x4e, 0x04, 0x75, 0x9e, 0xa5, 0x4c, 0xe8, 0x10, 0x8f, 0xbc, 0x0e, + 0x23, 0xbc, 0x3e, 0x35, 0x69, 0x5c, 0x07, 0x21, 0xaa, 0x24, 0xce, 0x71, 0x8c, 0xbf, 0x93, 0x83, + 0xf3, 0xd1, 0xe7, 0x6d, 0x75, 0x1a, 0x76, 0x40, 0x37, 0x6c, 0xcf, 0x6e, 0xf9, 0xa7, 0xac, 0x80, + 0x2b, 0x89, 0x4f, 0xc3, 0x74, 0x5d, 0xf2, 0xd3, 0x94, 0x0f, 0x32, 0x62, 0x1f, 0x84, 0x5a, 0x56, + 0xfe, 0x41, 0xf2, 0x33, 0xc8, 0x3d, 0xc8, 0xd5, 0x68, 0x20, 0xf6, 0xde, 0xcb, 0x89, 0x5e, 0x55, + 0xbf, 0xeb, 0x5a, 0x8d, 0x06, 0x7c, 0x10, 0x79, 0xec, 0x29, 0x2d, 0x00, 0x20, 0xe3, 0x42, 0xb6, + 0x61, 0x64, 0xf9, 0x51, 0x87, 0xd6, 0x03, 0x91, 0x80, 0xf5, 0x6a, 0x7f, 0x7e, 0x1c, 0x57, 0xc9, + 0xbf, 0x4a, 0x11, 0xa0, 0x76, 0x16, 0x47, 0x59, 0xb8, 0x05, 0x79, 0x59, 0xf9, 0x59, 0x66, 0xee, + 0xc2, 0x5b, 0x30, 0xae, 0x54, 0x72, 0xa6, 0x49, 0xff, 0x8b, 0x6c, 0x5f, 0x75, 0x9b, 0x32, 0x67, + 0xeb, 0x72, 0x42, 0x56, 0x54, 0x32, 0x5e, 0x71, 0x59, 0xd1, 0x3a, 0x10, 0x45, 0x7d, 0x84, 0xc6, + 0x2a, 0x4c, 0xd7, 0x0e, 0x9c, 0x4e, 0x14, 0x8c, 0x56, 0x3b, 0x91, 0x31, 0xab, 0x8e, 0x50, 0x0d, + 0xc4, 0x4f, 0xe4, 0x38, 0x9d, 0xf1, 0xe7, 0x19, 0x18, 0x61, 0x7f, 0x3d, 0xb8, 0xf5, 0x94, 0x6e, + 0x99, 0x37, 0xb5, 0x2d, 0x73, 0x46, 0x89, 0x07, 0x8f, 0x1b, 0xc7, 0xad, 0x53, 0x36, 0xcb, 0x63, + 0x31, 0x40, 0x1c, 0x99, 0xdc, 0x81, 0x51, 0x61, 0xb4, 0x24, 0xec, 0xcb, 0xd5, 0x00, 0xf3, 0xd2, + 0x9c, 0x29, 0xd4, 0x21, 0xb8, 0x9d, 0xb8, 0xd2, 0x45, 0x52, 0x33, 0xb9, 0x5e, 0x86, 0x05, 0xd6, + 0x72, 0xb2, 0xbb, 0xe8, 0x10, 0xc8, 0xc3, 0xa3, 0xfb, 0x4b, 0x17, 0x04, 0xa7, 0x5e, 0xfe, 0xfb, + 0x25, 0xf1, 0xde, 0x92, 0xeb, 0xc7, 0xe4, 0xbc, 0x4c, 0x84, 0x9c, 0xfa, 0x14, 0xd3, 0x82, 0xf3, + 0xb5, 0xda, 0x0a, 0x1a, 0x38, 0x6e, 0xb8, 0x5e, 0x70, 0xdb, 0xf5, 0x0e, 0x6d, 0xb4, 0x5f, 0x46, + 0x1d, 0xa2, 0x62, 0xe5, 0x90, 0x66, 0x76, 0xf6, 0x6a, 0xaa, 0xd9, 0x59, 0x1f, 0x4b, 0x08, 0xa3, + 0x0d, 0x17, 0x6a, 0xb5, 0x15, 0x1e, 0x9c, 0xfc, 0x2f, 0xa2, 0xbe, 0x5f, 0xcf, 0xc0, 0x4c, 0xad, + 0xb6, 0x12, 0xab, 0x6a, 0x55, 0x46, 0x45, 0xcf, 0xe8, 0xb9, 0xc4, 0x53, 0x3b, 0x02, 0x47, 0x21, + 0xc3, 0x25, 0xbc, 0xba, 0x16, 0x00, 0x93, 0x33, 0x21, 0x1b, 0x61, 0x1c, 0xf6, 0xac, 0xe6, 0x73, + 0xd0, 0xa3, 0xa1, 0xa8, 0x43, 0x17, 0x1e, 0x7b, 0xac, 0x54, 0xd7, 0xa1, 0x33, 0x88, 0xf1, 0xdf, + 0x9e, 0xe7, 0x91, 0xde, 0xe5, 0x6c, 0x79, 0x17, 0x26, 0x04, 0x3d, 0x1a, 0xe6, 0x0b, 0xab, 0x93, + 0x8b, 0x6c, 0x83, 0xdc, 0xe5, 0x70, 0x1e, 0x01, 0xf8, 0x3b, 0xc7, 0xc5, 0x21, 0xd6, 0x35, 0xa6, + 0x86, 0x4e, 0xee, 0xc3, 0xe4, 0x9a, 0xfd, 0x48, 0x51, 0x98, 0x70, 0xb7, 0xab, 0xab, 0x6c, 0x57, + 0x69, 0xd9, 0x8f, 0x06, 0x30, 0xeb, 0xd3, 0xe9, 0xc9, 0x01, 0x4c, 0xe9, 0x6d, 0x12, 0x33, 0x30, + 0x39, 0x62, 0x37, 0x52, 0x47, 0xec, 0x62, 0xc7, 0xf5, 0x02, 0x6b, 0x37, 0x24, 0xd7, 0xb2, 0x1a, + 0xc4, 0x58, 0x93, 0x77, 0x61, 0x46, 0x09, 0x33, 0x7a, 0xdb, 0xf5, 0x5a, 0xb6, 0xbc, 0x70, 0xe1, + 0x2b, 0x02, 0x5a, 0x2b, 0xed, 0x22, 0xd8, 0x4c, 0x62, 0x92, 0x2f, 0xa5, 0xb9, 0xb2, 0x0d, 0x47, + 0xb6, 0x8d, 0x29, 0xae, 0x6c, 0xbd, 0x6c, 0x1b, 0x93, 0x4e, 0x6d, 0x7b, 0xfd, 0x6c, 0x9f, 0xf3, + 0xbc, 0xf5, 0x03, 0xd9, 0x36, 0x87, 0x23, 0xd7, 0xc3, 0xc6, 0x79, 0x11, 0x72, 0x4b, 0x1b, 0xb7, + 0xf1, 0xed, 0x4b, 0x9a, 0x69, 0xb5, 0xf7, 0xed, 0x76, 0x1d, 0x2f, 0x42, 0xc2, 0xe3, 0x40, 0x3d, + 0x28, 0x97, 0x36, 0x6e, 0x13, 0x1b, 0x66, 0x31, 0x97, 0x5c, 0xf0, 0xc5, 0x1b, 0x37, 0x94, 0xa1, + 0xca, 0xe3, 0xa7, 0x5d, 0x17, 0x9f, 0x56, 0xc4, 0x4c, 0x74, 0x81, 0xf5, 0xe8, 0xc6, 0x8d, 0xd4, + 0x01, 0x09, 0x3f, 0x2c, 0x8d, 0x17, 0x3b, 0xb0, 0xd6, 0xec, 0x47, 0x91, 0xa3, 0x88, 0x2f, 0x9c, + 0x82, 0x9f, 0x97, 0x53, 0x2b, 0x72, 0x32, 0xd1, 0x0e, 0x2c, 0x9d, 0x88, 0xdd, 0x63, 0xa3, 0x09, + 0xe6, 0x0b, 0x77, 0xaa, 0x05, 0xa9, 0x10, 0x94, 0x9e, 0xe3, 0xea, 0x65, 0x4c, 0x41, 0x27, 0x5b, + 0xe1, 0x6d, 0x9c, 0xdf, 0x66, 0x45, 0xf6, 0xe5, 0xeb, 0xea, 0x6d, 0x9c, 0xab, 0xe1, 0xb4, 0x66, + 0x4d, 0x87, 0x2a, 0x1c, 0xee, 0x39, 0x63, 0xea, 0x5c, 0x92, 0x97, 0xfc, 0x89, 0xb3, 0x5f, 0xf2, + 0x29, 0x0c, 0xad, 0xba, 0xf5, 0x03, 0x11, 0x00, 0xf0, 0x03, 0xb6, 0x0b, 0xeb, 0xa9, 0xfa, 0x1f, + 0xd7, 0xa6, 0x1b, 0xd9, 0x93, 0x75, 0xf6, 0xa9, 0x6c, 0x16, 0x88, 0x3e, 0x11, 0x76, 0xc2, 0x73, + 0xe1, 0x2d, 0x57, 0x29, 0xe3, 0xf2, 0x28, 0x9f, 0x34, 0xb2, 0x6b, 0x4d, 0x9d, 0x9c, 0x50, 0x28, + 0x54, 0xa8, 0x7f, 0x10, 0xb8, 0x9d, 0x72, 0xd3, 0xe9, 0xec, 0xb8, 0xb6, 0x27, 0xc3, 0x45, 0x0f, + 0xbc, 0x27, 0x37, 0x38, 0xbd, 0x55, 0x97, 0x0c, 0xcc, 0x04, 0x4b, 0xf2, 0x25, 0x98, 0x62, 0x93, + 0x7b, 0xf9, 0x51, 0x40, 0xdb, 0x7c, 0xe4, 0x67, 0x50, 0xa2, 0x9b, 0x53, 0xf2, 0xa3, 0x84, 0x85, + 0x7c, 0x4e, 0xe1, 0x62, 0xa7, 0x21, 0x81, 0x16, 0x3c, 0x51, 0x63, 0x45, 0x1a, 0x30, 0xbf, 0x66, + 0x3f, 0x52, 0xf2, 0x3c, 0x2b, 0x93, 0x94, 0xe0, 0x04, 0xbb, 0x72, 0x72, 0x5c, 0x7c, 0x99, 0x4d, + 0xb0, 0x28, 0x82, 0x79, 0x8f, 0xf9, 0xda, 0x93, 0x13, 0xf9, 0x06, 0x5c, 0x10, 0xcd, 0xaa, 0x60, + 0x6e, 0x32, 0xd7, 0x3b, 0xaa, 0xed, 0xdb, 0xe8, 0x23, 0x36, 0xdb, 0xa3, 0xc3, 0xae, 0xa7, 0x6f, + 0x89, 0xb2, 0xc3, 0x1a, 0x92, 0x8f, 0xe5, 0x73, 0x46, 0x66, 0xaf, 0x1a, 0xc8, 0x47, 0x30, 0xc5, + 0x1f, 0xfc, 0x56, 0x5c, 0x3f, 0x40, 0x65, 0xcd, 0xdc, 0xd9, 0x1c, 0x1f, 0xf8, 0x2b, 0x22, 0x77, + 0x16, 0x8a, 0x29, 0x77, 0x62, 0x9c, 0xc9, 0xdb, 0x30, 0xbe, 0xe1, 0xb4, 0x79, 0x78, 0xd3, 0xea, + 0x06, 0x2a, 0xae, 0xc5, 0x09, 0xd4, 0x71, 0xda, 0x96, 0xd4, 0x98, 0x74, 0xc2, 0xed, 0x42, 0xc5, + 0x26, 0xdb, 0x30, 0x5e, 0xab, 0xad, 0xdc, 0x76, 0x98, 0x5c, 0xd2, 0x91, 0x7a, 0xe8, 0xe4, 0x57, + 0xbe, 0x94, 0xfa, 0x95, 0x93, 0xbe, 0xbf, 0x6f, 0xed, 0x3a, 0x4d, 0x6a, 0xd5, 0xdd, 0xce, 0x91, + 0xa9, 0x72, 0x4a, 0x71, 0x06, 0xb8, 0xf0, 0x84, 0x9d, 0x01, 0xaa, 0x30, 0xad, 0x98, 0xe7, 0xa2, + 0x69, 0xee, 0x7c, 0x14, 0x13, 0x4b, 0x35, 0xfe, 0x8f, 0xbb, 0xbf, 0xc6, 0xe9, 0xa4, 0x17, 0xc0, + 0xc5, 0xb3, 0x7a, 0x01, 0x38, 0x30, 0xc3, 0x07, 0x43, 0xcc, 0x03, 0x1c, 0xe9, 0x85, 0x1e, 0x7d, + 0x78, 0x35, 0xb5, 0x0f, 0x67, 0xc5, 0x48, 0xcb, 0x49, 0x86, 0x0f, 0xdc, 0x49, 0xae, 0x64, 0x17, + 0x88, 0x00, 0xda, 0x81, 0xbd, 0x63, 0xfb, 0x14, 0xeb, 0x7a, 0xb6, 0x47, 0x5d, 0x2f, 0xa7, 0xd6, + 0x35, 0x25, 0xeb, 0xda, 0xe1, 0xd5, 0xa4, 0x70, 0x24, 0x6d, 0x59, 0x8f, 0x9c, 0x5f, 0xd8, 0xb1, + 0xcf, 0x69, 0x3a, 0xee, 0x24, 0x02, 0x0f, 0x2f, 0x15, 0x9f, 0xb4, 0xf1, 0x7e, 0x4f, 0xe1, 0x4c, + 0x1e, 0xc1, 0xf9, 0xe4, 0x57, 0x60, 0x9d, 0xcf, 0x63, 0x9d, 0xcf, 0x6b, 0x75, 0xc6, 0x91, 0xf8, + 0xbc, 0xd1, 0x9b, 0x15, 0xaf, 0xb5, 0x07, 0x7f, 0xf2, 0xc3, 0x19, 0xb8, 0xb0, 0x76, 0xbb, 0x84, + 0x19, 0x4b, 0x1d, 0x1e, 0xed, 0x2e, 0x74, 0x1b, 0x7e, 0x41, 0xbc, 0x83, 0xc4, 0xdf, 0x63, 0xa4, + 0xc4, 0x81, 0x5b, 0x05, 0x13, 0xdd, 0x5f, 0x6a, 0xed, 0xda, 0x3c, 0x11, 0xaa, 0x60, 0x91, 0xe2, + 0x5b, 0xfc, 0xad, 0x3f, 0x2a, 0x66, 0xcc, 0x5e, 0x55, 0x91, 0x26, 0x2c, 0xe8, 0xdd, 0x22, 0xfd, + 0x34, 0xf6, 0x69, 0xb3, 0x39, 0x5f, 0xc4, 0x19, 0xfd, 0xfa, 0xc9, 0x71, 0xf1, 0x4a, 0xa2, 0x77, + 0x43, 0xdf, 0x0f, 0x86, 0xa9, 0x34, 0xb8, 0x0f, 0x3f, 0xd2, 0x4a, 0x11, 0xba, 0xe7, 0x2f, 0x69, + 0xf1, 0x85, 0x12, 0xe5, 0x4b, 0xaf, 0x08, 0x89, 0xe4, 0x79, 0xb6, 0xde, 0x7b, 0x0a, 0x88, 0x66, + 0x92, 0xf3, 0xdd, 0xa1, 0xfc, 0x64, 0x61, 0x2a, 0xc5, 0x29, 0xc2, 0xf8, 0xad, 0x6c, 0xec, 0x60, + 0x24, 0x55, 0x18, 0x15, 0xf3, 0xbd, 0xe7, 0x25, 0xe3, 0xf9, 0xd4, 0x59, 0x3d, 0x2a, 0x96, 0x8e, + 0x29, 0xe9, 0xc9, 0x21, 0x63, 0x85, 0x8d, 0x16, 0x37, 0xde, 0xaf, 0xf0, 0x73, 0x0f, 0x41, 0xda, + 0x09, 0x5f, 0x39, 0xbb, 0xb3, 0x9f, 0xee, 0x4b, 0x8a, 0x47, 0xbd, 0xac, 0x8d, 0x1c, 0xf0, 0x74, + 0x55, 0xb9, 0xd0, 0x5f, 0x4c, 0xcf, 0x4d, 0xf5, 0xc4, 0x2a, 0x64, 0xb5, 0x18, 0xbf, 0x99, 0x81, + 0x49, 0xed, 0x64, 0x25, 0xb7, 0x14, 0x77, 0xc8, 0x28, 0x42, 0x80, 0x86, 0x83, 0x9b, 0x6d, 0xdc, + 0x51, 0xf2, 0x96, 0xf0, 0x6c, 0xc8, 0xf6, 0xa6, 0xc3, 0xc5, 0x16, 0xf7, 0x8e, 0xed, 0xaf, 0x1f, + 0x0e, 0x73, 0x6d, 0x0e, 0xf5, 0xc8, 0xb5, 0xf9, 0x0f, 0x8a, 0x30, 0xa5, 0xdf, 0x88, 0xc9, 0xeb, + 0x30, 0x82, 0xba, 0x79, 0xa9, 0x5e, 0x41, 0xb5, 0x10, 0xaa, 0xef, 0x35, 0x77, 0x17, 0x8e, 0x43, + 0x5e, 0x01, 0x08, 0x4d, 0xcc, 0xe5, 0xcb, 0xd4, 0xf0, 0xc9, 0x71, 0x31, 0xf3, 0x86, 0xa9, 0x14, + 0x90, 0xaf, 0x02, 0xac, 0xbb, 0x0d, 0x1a, 0x26, 0x50, 0xee, 0x63, 0xdf, 0xf1, 0x6a, 0x22, 0x95, + 0xcb, 0xb9, 0xb6, 0xdb, 0xa0, 0xc9, 0xbc, 0x2d, 0x0a, 0x47, 0xf2, 0x79, 0x18, 0x36, 0xbb, 0x4d, + 0x2a, 0x5f, 0x30, 0xc6, 0xe5, 0x09, 0xd7, 0x6d, 0xd2, 0x48, 0x4f, 0xe0, 0x75, 0xe3, 0xa6, 0x8b, + 0x0c, 0x40, 0xde, 0xe7, 0x29, 0x5e, 0x44, 0x1c, 0xd2, 0xe1, 0xe8, 0xad, 0x4e, 0x91, 0x7c, 0x12, + 0x91, 0x48, 0x15, 0x12, 0x72, 0x1f, 0x46, 0xd5, 0x47, 0x26, 0xc5, 0xaf, 0x5e, 0x7d, 0x88, 0x54, + 0x94, 0x0e, 0x22, 0xf3, 0x72, 0xfc, 0xfd, 0x49, 0x72, 0x21, 0xef, 0xc0, 0x18, 0x63, 0xcf, 0x76, + 0x0e, 0x5f, 0xdc, 0x6a, 0xf0, 0x45, 0x4e, 0xf9, 0x20, 0xb6, 0xfb, 0x68, 0xd1, 0x42, 0x43, 0x02, + 0xf2, 0x25, 0xcc, 0x95, 0x2b, 0xba, 0xba, 0xaf, 0xdd, 0xcf, 0xe5, 0x44, 0x57, 0x63, 0xf2, 0xdc, + 0x44, 0x4f, 0x47, 0xfc, 0xc8, 0x5e, 0x18, 0xd6, 0x6d, 0x90, 0xb4, 0x3c, 0xaf, 0x25, 0x2a, 0x98, + 0x97, 0x91, 0xca, 0x92, 0x89, 0xb0, 0x35, 0xbe, 0xa4, 0x03, 0x85, 0x48, 0xa8, 0x14, 0x75, 0x41, + 0xbf, 0xba, 0xde, 0x48, 0xd4, 0xa5, 0x0e, 0x60, 0xa2, 0xba, 0x04, 0x77, 0xd2, 0x80, 0x29, 0x79, + 0x40, 0x89, 0xfa, 0xc6, 0xfb, 0xd5, 0xf7, 0x4a, 0xa2, 0xbe, 0xd9, 0xc6, 0x4e, 0xb2, 0x9e, 0x18, + 0x4f, 0xf2, 0x0e, 0x4c, 0x4a, 0x08, 0x4f, 0x4b, 0x3d, 0x11, 0xe5, 0xf5, 0x6d, 0xec, 0x24, 0x92, + 0x51, 0xeb, 0xc8, 0x2a, 0x35, 0x9f, 0x1d, 0x93, 0x1a, 0x75, 0x7c, 0x56, 0xe8, 0xc8, 0xe4, 0x43, + 0x18, 0xaf, 0xb6, 0x58, 0x43, 0xdc, 0xb6, 0x1d, 0x50, 0xe1, 0x71, 0x29, 0x6d, 0x98, 0x94, 0x12, + 0x65, 0xaa, 0xf2, 0x84, 0xdb, 0x51, 0x91, 0x96, 0x70, 0x3b, 0x02, 0xb3, 0xce, 0xe3, 0xaf, 0x8a, + 0x62, 0x0e, 0x4b, 0x6f, 0xcc, 0xe7, 0x53, 0xec, 0x88, 0x14, 0xf6, 0x22, 0xe6, 0x24, 0x83, 0xca, + 0x57, 0xbd, 0x58, 0xbc, 0x5f, 0x95, 0x27, 0x79, 0x17, 0xc6, 0x45, 0xc6, 0xb2, 0x92, 0xb9, 0xee, + 0xcf, 0x17, 0xb0, 0xf1, 0x18, 0x45, 0x42, 0x26, 0x37, 0xb3, 0x6c, 0x2f, 0x66, 0x30, 0x1b, 0xe1, + 0x93, 0x2f, 0xc2, 0xdc, 0xb6, 0xd3, 0x6e, 0xb8, 0x87, 0xbe, 0x38, 0xa6, 0xc4, 0x46, 0x37, 0x13, + 0xb9, 0xab, 0x1d, 0xf2, 0xf2, 0x50, 0x16, 0x4c, 0x6c, 0x7c, 0xa9, 0x1c, 0xc8, 0xf7, 0x25, 0x38, + 0xf3, 0x19, 0x44, 0xfa, 0xcd, 0xa0, 0xc5, 0xc4, 0x0c, 0x4a, 0x56, 0x1f, 0x9f, 0x4e, 0xa9, 0xd5, + 0x10, 0x17, 0x88, 0x7e, 0xbe, 0xdf, 0x75, 0x9d, 0xf6, 0xfc, 0x2c, 0xee, 0x85, 0xcf, 0xc6, 0xe3, + 0x36, 0x20, 0x9e, 0x48, 0x5c, 0x6e, 0x9c, 0x1c, 0x17, 0x5f, 0x88, 0xcb, 0xfc, 0x1f, 0xb9, 0xda, + 0x73, 0x49, 0x0a, 0x6b, 0xf2, 0x21, 0x4c, 0xb0, 0xff, 0x43, 0xa5, 0xc4, 0x9c, 0x66, 0x79, 0xaa, + 0x60, 0x8a, 0x7a, 0x70, 0x8c, 0x30, 0xa5, 0x5a, 0x8a, 0xbe, 0x42, 0x63, 0x45, 0xde, 0x02, 0x60, + 0x62, 0x93, 0xd8, 0x8e, 0xcf, 0x45, 0xe1, 0x95, 0x51, 0xea, 0x4a, 0x6e, 0xc4, 0x11, 0x32, 0x79, + 0x07, 0xc6, 0xd9, 0xaf, 0x5a, 0xb7, 0xe1, 0xb2, 0xb5, 0x71, 0x1e, 0x69, 0xb9, 0xf3, 0x2b, 0xa3, + 0xf5, 0x39, 0x5c, 0x73, 0x7e, 0x8d, 0xd0, 0xc9, 0x0a, 0x4c, 0x63, 0x18, 0x6c, 0x11, 0x80, 0xd5, + 0xa1, 0xfe, 0xfc, 0x05, 0xc5, 0x1a, 0x82, 0x15, 0x59, 0x4e, 0x58, 0xa6, 0xde, 0x65, 0x62, 0x64, + 0xc4, 0x87, 0xd9, 0xe4, 0x73, 0xb2, 0x3f, 0x3f, 0x8f, 0x9d, 0x24, 0x25, 0xf8, 0x24, 0x06, 0xdf, + 0x8f, 0xd9, 0x88, 0x28, 0x1b, 0x97, 0x7c, 0x54, 0x52, 0x2b, 0x4c, 0xe3, 0x4e, 0x4c, 0x20, 0x77, + 0xca, 0x1b, 0xf1, 0x38, 0xd1, 0x17, 0xb1, 0x05, 0x38, 0xcc, 0x7b, 0xf5, 0x28, 0x53, 0x79, 0x4a, + 0xac, 0xe8, 0x14, 0x6a, 0xf2, 0xbd, 0x70, 0x4e, 0xee, 0x20, 0xa2, 0x48, 0xcc, 0xeb, 0x85, 0x33, + 0xee, 0xc4, 0x8d, 0x9d, 0xb0, 0xea, 0xc4, 0x94, 0x4e, 0xaf, 0x82, 0xd8, 0x30, 0x8e, 0xc3, 0x2a, + 0x6a, 0x7c, 0xb6, 0x5f, 0x8d, 0x57, 0x12, 0x35, 0x9e, 0xc7, 0x89, 0x92, 0xac, 0x4c, 0xe5, 0x49, + 0x96, 0x60, 0x52, 0xac, 0x23, 0x31, 0xdb, 0x9e, 0xc3, 0xde, 0x42, 0x25, 0x96, 0x5c, 0x81, 0x89, + 0x09, 0xa7, 0x93, 0xa8, 0x3b, 0x32, 0x7f, 0x4c, 0x7a, 0x5e, 0xdb, 0x91, 0xe3, 0x6f, 0x48, 0x3a, + 0x32, 0xdb, 0x91, 0x22, 0x29, 0x66, 0xf9, 0x51, 0xc7, 0x13, 0x2a, 0xaa, 0x17, 0xa2, 0xcc, 0x4b, + 0x8a, 0xf0, 0x63, 0xd1, 0x10, 0x43, 0xdd, 0x12, 0xd2, 0x38, 0x90, 0x2d, 0x98, 0x0d, 0x4f, 0x6d, + 0x85, 0x71, 0x31, 0x8a, 0x44, 0x1c, 0x1d, 0xf5, 0xe9, 0x7c, 0xd3, 0xe8, 0x89, 0x0d, 0x17, 0xb4, + 0x73, 0x5a, 0x61, 0x7d, 0x09, 0x59, 0x63, 0x66, 0x7c, 0xfd, 0x90, 0x4f, 0x67, 0xdf, 0x8b, 0x0f, + 0xf9, 0x08, 0x16, 0xe2, 0x67, 0xb3, 0x52, 0xcb, 0x8b, 0x58, 0xcb, 0x6b, 0x27, 0xc7, 0xc5, 0xcb, + 0x89, 0xe3, 0x3d, 0xbd, 0xa2, 0x3e, 0xdc, 0xc8, 0x57, 0x61, 0x5e, 0x3f, 0x9f, 0x95, 0x9a, 0x0c, + 0xac, 0x09, 0x97, 0x4e, 0x78, 0xb0, 0xa7, 0xd7, 0xd0, 0x93, 0x07, 0x09, 0xa0, 0x98, 0x3a, 0xbb, + 0x95, 0x6a, 0x5e, 0x8a, 0x1a, 0x94, 0x58, 0x25, 0xe9, 0xd5, 0x9d, 0xc6, 0x92, 0x1c, 0xc2, 0x0b, + 0x69, 0xc7, 0x84, 0x52, 0xe9, 0xcb, 0xa1, 0x12, 0xf8, 0x53, 0xe9, 0x47, 0x4e, 0x7a, 0xcd, 0xa7, + 0xb0, 0x25, 0x5f, 0x82, 0x73, 0xca, 0xfa, 0x52, 0xea, 0x7b, 0x05, 0xeb, 0x43, 0x67, 0x73, 0x75, + 0x61, 0xa6, 0xd7, 0x92, 0xce, 0x83, 0xb4, 0x60, 0x56, 0x36, 0x1c, 0xb5, 0xed, 0xe2, 0xe8, 0xb9, + 0xac, 0xed, 0xaa, 0x49, 0x8c, 0xa5, 0x4b, 0x62, 0x57, 0x9d, 0x6f, 0xec, 0x58, 0x9d, 0x88, 0x50, + 0x9d, 0xe9, 0x29, 0x7c, 0xc9, 0x0a, 0x8c, 0xd4, 0x36, 0xaa, 0xb7, 0x6f, 0x2f, 0xcf, 0xbf, 0x8a, + 0x35, 0x48, 0xcf, 0x34, 0x0e, 0xd4, 0x2e, 0x4d, 0xc2, 0x5c, 0xb1, 0xe3, 0xec, 0xee, 0x6a, 0x0f, + 0x56, 0x1c, 0x95, 0x7c, 0x1f, 0x1a, 0x0a, 0xb2, 0x1d, 0xb5, 0xe4, 0xfb, 0xce, 0x1e, 0x46, 0xb6, + 0xf6, 0xe7, 0x5f, 0xd3, 0xde, 0xfb, 0x65, 0xd4, 0xef, 0x32, 0x26, 0x45, 0x4b, 0xa0, 0x73, 0x69, + 0x93, 0xdd, 0xff, 0xc5, 0xce, 0x6d, 0xd9, 0x11, 0x2b, 0x75, 0x13, 0x4f, 0x56, 0xc4, 0xfa, 0x6d, + 0xcf, 0x09, 0xac, 0xfd, 0xae, 0xd6, 0xfc, 0xf9, 0x4f, 0x69, 0x51, 0x9e, 0x79, 0xaa, 0x38, 0xa5, + 0xd7, 0x5e, 0x16, 0x15, 0x3e, 0xc7, 0x6f, 0xcb, 0x3d, 0x7a, 0x6e, 0x66, 0x2f, 0x46, 0xe7, 0x93, + 0x1f, 0xca, 0xc0, 0xf9, 0x6d, 0xd7, 0x3b, 0x68, 0xba, 0x76, 0x43, 0xb6, 0x4a, 0xec, 0xe1, 0xaf, + 0xf7, 0xdb, 0xc3, 0x3f, 0x93, 0xd8, 0xc3, 0x8d, 0x43, 0xc1, 0xc6, 0x0a, 0x83, 0xa6, 0x27, 0xf6, + 0xf3, 0x1e, 0x55, 0x91, 0xef, 0x83, 0x4b, 0xe9, 0x25, 0xca, 0xa4, 0x7c, 0x03, 0x27, 0xe5, 0x8d, + 0x93, 0xe3, 0xe2, 0x1b, 0xbd, 0x6a, 0x4a, 0x9f, 0xa0, 0xa7, 0xb2, 0xbe, 0x3b, 0x94, 0xbf, 0x52, + 0xb8, 0x7a, 0x77, 0x28, 0x7f, 0xb5, 0xf0, 0x9a, 0xf9, 0x5c, 0xad, 0xb4, 0xb6, 0x5a, 0x6d, 0xc8, + 0xc3, 0x55, 0xc6, 0x75, 0xe7, 0x34, 0xe6, 0xe5, 0x7e, 0xa5, 0x11, 0x47, 0xe3, 0x6f, 0x65, 0xa0, + 0x78, 0xca, 0x24, 0x61, 0xe7, 0x59, 0x34, 0x12, 0x35, 0x1a, 0xa8, 0xd1, 0xe1, 0xa3, 0xf1, 0xb3, + 0x74, 0xb3, 0x11, 0x9d, 0x04, 0xdd, 0x1a, 0x45, 0x4a, 0x12, 0xc5, 0xbb, 0x35, 0x99, 0x8a, 0x44, + 0x62, 0x19, 0xab, 0x50, 0x88, 0x4f, 0x1e, 0xf2, 0x39, 0x98, 0x54, 0x13, 0x22, 0x48, 0x55, 0x02, + 0x0f, 0x65, 0xe2, 0xed, 0x69, 0x07, 0xa2, 0x86, 0x68, 0xfc, 0x62, 0x06, 0x66, 0x53, 0x56, 0x18, + 0xb9, 0x0c, 0x43, 0x98, 0xb1, 0x4c, 0xb1, 0x1a, 0x8a, 0x65, 0x2a, 0xc3, 0x72, 0xf2, 0x69, 0x18, + 0xad, 0xac, 0xd7, 0x6a, 0xa5, 0x75, 0xa9, 0x8c, 0xe0, 0x07, 0x71, 0xdb, 0xb7, 0x7c, 0x5b, 0x37, + 0x36, 0x10, 0x68, 0xe4, 0x0d, 0x18, 0xa9, 0x6e, 0x20, 0x01, 0xb7, 0x7d, 0xc5, 0xf6, 0x3a, 0x9d, + 0x38, 0xbe, 0x40, 0x32, 0x7e, 0x3c, 0x03, 0x24, 0xb9, 0x5d, 0x90, 0x1b, 0x30, 0xae, 0x6e, 0x4a, + 0xbc, 0xbd, 0xf8, 0x02, 0xab, 0x2c, 0x1c, 0x53, 0xc5, 0x21, 0x15, 0x18, 0xc6, 0x5c, 0xb3, 0xa1, + 0x95, 0x43, 0xea, 0xb2, 0xb8, 0x90, 0x58, 0x16, 0xc3, 0x98, 0xc9, 0xd6, 0xe4, 0xc4, 0xc6, 0xef, + 0x66, 0x80, 0xa4, 0xdb, 0x2e, 0x0e, 0x64, 0x65, 0xf5, 0xa6, 0x12, 0x1d, 0x41, 0xcd, 0x49, 0x14, + 0x26, 0x94, 0x53, 0xd5, 0x00, 0x51, 0x1c, 0x85, 0xcb, 0x9a, 0xda, 0xa9, 0xb7, 0x4b, 0xed, 0x55, + 0x18, 0x7e, 0x40, 0xbd, 0x1d, 0x69, 0xd6, 0x8d, 0xa6, 0xa0, 0x0f, 0x19, 0x40, 0x55, 0xc3, 0x20, + 0x86, 0xf1, 0x27, 0x19, 0x98, 0x4b, 0xbb, 0xa3, 0x9c, 0xe2, 0xf9, 0x6a, 0xc4, 0x9c, 0x76, 0xd1, + 0xc2, 0x8a, 0xdb, 0x89, 0x86, 0xae, 0xba, 0x45, 0x18, 0x66, 0x8d, 0x95, 0x23, 0x8c, 0x6a, 0x30, + 0xd6, 0x1b, 0xbe, 0xc9, 0xe1, 0x0c, 0x81, 0xc7, 0x01, 0x1c, 0xc2, 0x10, 0x92, 0x88, 0x80, 0xb3, + 0xdb, 0xe4, 0x70, 0x86, 0xb0, 0xe6, 0x36, 0xa8, 0x54, 0x0f, 0x21, 0x42, 0x8b, 0x01, 0x4c, 0x0e, + 0x27, 0x97, 0x61, 0xf4, 0x7e, 0x7b, 0x95, 0xda, 0x0f, 0x65, 0x5e, 0x0c, 0xb4, 0x08, 0x73, 0xdb, + 0x56, 0x93, 0xc1, 0x4c, 0x59, 0x68, 0xfc, 0x6c, 0x06, 0x66, 0x12, 0xd7, 0xa3, 0xd3, 0x9d, 0x7b, + 0xfb, 0x7b, 0xd9, 0x0d, 0xd2, 0x3e, 0xfe, 0xf9, 0x43, 0xe9, 0x9f, 0x6f, 0xfc, 0xf7, 0x23, 0x70, + 0xa1, 0x87, 0xb6, 0x2a, 0xf2, 0x02, 0xce, 0x9c, 0xea, 0x05, 0xfc, 0x65, 0x98, 0x2c, 0x37, 0x6d, + 0xa7, 0xe5, 0x6f, 0xba, 0xd1, 0x17, 0x47, 0xce, 0x44, 0x58, 0x26, 0xfc, 0x20, 0x42, 0xaf, 0x93, + 0x8b, 0x75, 0xa4, 0xb0, 0x02, 0x37, 0x29, 0x2c, 0x6b, 0xcc, 0x12, 0x7e, 0xb8, 0xb9, 0xbf, 0x24, + 0x7e, 0xb8, 0xba, 0x67, 0xd8, 0xd0, 0x13, 0xf5, 0x0c, 0x4b, 0xb7, 0xf9, 0x1e, 0x7e, 0x1c, 0x0f, + 0x80, 0x32, 0x4c, 0x72, 0x93, 0xb8, 0x92, 0xcf, 0x07, 0x69, 0x24, 0x61, 0x46, 0x67, 0xfb, 0xc9, + 0xb1, 0xd0, 0x68, 0xc8, 0x8a, 0xee, 0xc5, 0x34, 0x8a, 0x6f, 0xc6, 0x97, 0x7b, 0x7b, 0x29, 0x69, + 0xb6, 0x22, 0x9a, 0xb7, 0xd2, 0x37, 0x60, 0x2e, 0xed, 0xba, 0x3b, 0x9f, 0xd7, 0xac, 0x6d, 0x7b, + 0x5a, 0x69, 0x0f, 0x7e, 0x69, 0x3e, 0x48, 0xbd, 0x34, 0x4b, 0xef, 0xf2, 0x31, 0x2d, 0x6c, 0x74, + 0x8f, 0xb5, 0xc0, 0x71, 0xfb, 0xfb, 0xa0, 0x1b, 0x5f, 0x86, 0xe7, 0xfb, 0x92, 0x93, 0xb7, 0xb5, + 0x28, 0x46, 0xaf, 0x26, 0xa3, 0x18, 0x7d, 0xe7, 0xb8, 0x38, 0xa3, 0x79, 0x86, 0xae, 0x85, 0x0a, + 0x7f, 0xe3, 0x67, 0xb3, 0xba, 0x4f, 0xf3, 0x5f, 0xc6, 0x85, 0x7a, 0x15, 0x86, 0xb7, 0xf7, 0xa9, + 0x27, 0x8f, 0x07, 0xfc, 0x90, 0x43, 0x06, 0x50, 0x3f, 0x04, 0x31, 0xc8, 0x6d, 0x98, 0xda, 0xe0, + 0x13, 0x57, 0xce, 0xc6, 0xa1, 0x48, 0xe7, 0xd2, 0x11, 0x9a, 0xc1, 0x94, 0xe9, 0x18, 0xa3, 0x32, + 0xee, 0xc4, 0x3a, 0x5d, 0xc4, 0x60, 0xe2, 0x9e, 0x51, 0x5c, 0x80, 0x98, 0x8a, 0xbc, 0xcd, 0xa2, + 0xcd, 0xd6, 0x8c, 0x41, 0x8d, 0x5d, 0x78, 0xa1, 0x2f, 0x23, 0x76, 0x6e, 0x43, 0x27, 0xfc, 0x15, + 0xb3, 0xbc, 0xee, 0x4b, 0x6a, 0x2a, 0x74, 0xc6, 0x37, 0x60, 0x42, 0xed, 0x65, 0x3c, 0x82, 0xd8, + 0x6f, 0x31, 0x2b, 0xf8, 0x11, 0xc4, 0x00, 0x26, 0x87, 0x47, 0x6f, 0x39, 0xd9, 0xf4, 0xb7, 0x9c, + 0x68, 0xf8, 0x73, 0xa7, 0x0d, 0x3f, 0xab, 0x1c, 0x77, 0x38, 0xa5, 0x72, 0xfc, 0xad, 0x56, 0x8e, + 0x41, 0x96, 0x4c, 0x0e, 0x7f, 0xa2, 0x95, 0xff, 0x8e, 0x4c, 0xa4, 0x86, 0x8e, 0x57, 0x72, 0xb9, + 0x67, 0xa2, 0x6c, 0x68, 0x69, 0xab, 0x37, 0xc2, 0x8c, 0x64, 0x8a, 0xec, 0x69, 0x32, 0xc5, 0x59, + 0x26, 0x22, 0xca, 0xbd, 0x7c, 0x48, 0x87, 0x22, 0x39, 0xd0, 0x4e, 0x58, 0xbb, 0x48, 0x2c, 0xe3, + 0x5b, 0x19, 0x38, 0x97, 0xaa, 0x33, 0x67, 0xb5, 0x72, 0xe5, 0xbc, 0xb2, 0x0e, 0xe3, 0x9a, 0x79, + 0x8e, 0x71, 0x96, 0x08, 0x1b, 0x83, 0xb7, 0xc5, 0x78, 0x11, 0xc6, 0xc2, 0x17, 0x5b, 0x32, 0x27, + 0x87, 0x0e, 0xed, 0x22, 0xe5, 0xc3, 0x5f, 0x0d, 0x80, 0x7d, 0xc1, 0x13, 0x35, 0xad, 0x36, 0x7e, + 0x27, 0xcb, 0x93, 0xec, 0x3e, 0xb5, 0xe1, 0x72, 0xd3, 0xed, 0xa1, 0x59, 0x93, 0x7a, 0x07, 0xc9, + 0x25, 0xcb, 0x30, 0x52, 0x0b, 0xec, 0xa0, 0x2b, 0x03, 0x83, 0xcc, 0xaa, 0x64, 0x58, 0xf0, 0x60, + 0x31, 0x0a, 0x0d, 0xe1, 0x23, 0x44, 0xd3, 0x12, 0x20, 0x44, 0x31, 0xab, 0xfe, 0x83, 0x0c, 0x4c, + 0xa8, 0xc4, 0xe4, 0x43, 0x98, 0x92, 0x21, 0x40, 0x79, 0xb8, 0x14, 0xf1, 0xbc, 0x2c, 0x4d, 0xc1, + 0x64, 0x08, 0x50, 0x35, 0xbc, 0x8a, 0x86, 0xaf, 0x6e, 0xd5, 0x1d, 0x15, 0x99, 0x34, 0x80, 0xb4, + 0x76, 0x6d, 0xeb, 0x90, 0xda, 0x07, 0xd4, 0x0f, 0x2c, 0x6e, 0xb2, 0x23, 0x5e, 0xa1, 0x25, 0xfb, + 0xb5, 0xdb, 0x25, 0x6e, 0xad, 0xc3, 0x46, 0x42, 0xc4, 0x72, 0x4d, 0xd0, 0xa8, 0x4f, 0x6b, 0xad, + 0x5d, 0x7b, 0x9b, 0x17, 0x72, 0x3a, 0xe3, 0x4f, 0x47, 0xf8, 0x74, 0x13, 0x31, 0x83, 0x77, 0x60, + 0xea, 0x7e, 0xb5, 0x52, 0x56, 0x14, 0xed, 0x7a, 0xca, 0xa9, 0xe5, 0x47, 0x01, 0xf5, 0xda, 0x76, + 0x53, 0xde, 0x77, 0xa3, 0x23, 0xc8, 0x75, 0x1a, 0xf5, 0x74, 0x25, 0x7c, 0x8c, 0x23, 0xab, 0x83, + 0xdf, 0xac, 0xc3, 0x3a, 0xb2, 0x03, 0xd6, 0xe1, 0xdb, 0xad, 0x66, 0x8f, 0x3a, 0x74, 0x8e, 0x64, + 0x1f, 0xaf, 0xbe, 0xfb, 0xdd, 0x1d, 0xa5, 0x96, 0x5c, 0xff, 0x5a, 0x5e, 0x12, 0xb5, 0x3c, 0x2b, + 0xd4, 0x2a, 0xa9, 0xf5, 0x24, 0xb8, 0x46, 0xfb, 0xc4, 0xd0, 0xa9, 0xfb, 0xc4, 0xdf, 0xc8, 0xc0, + 0x08, 0x17, 0x5f, 0xc5, 0x34, 0xee, 0x21, 0x20, 0x6f, 0x3f, 0x19, 0x01, 0xb9, 0x80, 0xe7, 0x84, + 0x36, 0xa1, 0x79, 0x19, 0xa9, 0xc4, 0xd6, 0x85, 0xf4, 0x06, 0xc0, 0x27, 0x33, 0x5e, 0x72, 0xfa, + 0xb2, 0x20, 0xd5, 0x28, 0x58, 0xc7, 0xe8, 0xa9, 0x1e, 0xda, 0x32, 0xc0, 0xc9, 0xa8, 0x08, 0xd6, + 0xa1, 0x87, 0xe8, 0x58, 0x85, 0x31, 0x11, 0x02, 0x64, 0xe9, 0x48, 0x3c, 0x8c, 0x17, 0x34, 0xd3, + 0xa6, 0xc6, 0xd2, 0x51, 0x24, 0x9a, 0x8b, 0x20, 0x22, 0xd6, 0xce, 0x91, 0x96, 0xb3, 0x58, 0x22, + 0x92, 0xfb, 0x3c, 0x97, 0x27, 0x8f, 0xa9, 0xac, 0xa7, 0x51, 0x08, 0xe1, 0x22, 0xb8, 0x98, 0x8c, + 0x23, 0x90, 0x12, 0x42, 0x39, 0xe2, 0x41, 0x56, 0xa1, 0x80, 0xe6, 0x70, 0xb4, 0xc1, 0x57, 0x4d, + 0xb5, 0xc2, 0xc3, 0x4c, 0x08, 0x93, 0xe6, 0x80, 0x97, 0x89, 0xe5, 0x16, 0xf3, 0xbf, 0x4c, 0x50, + 0x1a, 0x3f, 0x93, 0x85, 0x42, 0x7c, 0xf6, 0x91, 0x77, 0x60, 0x3c, 0x8c, 0x69, 0x1d, 0x7a, 0x80, + 0xe3, 0x03, 0x59, 0x14, 0x04, 0x5b, 0xcf, 0x00, 0xa9, 0xa0, 0x93, 0x45, 0xc8, 0xb3, 0x45, 0x1c, + 0xcf, 0x96, 0xdc, 0x15, 0x30, 0xd5, 0x23, 0x4b, 0xe2, 0x91, 0x1a, 0xcc, 0xb2, 0x45, 0x53, 0x73, + 0xda, 0x7b, 0x4d, 0xba, 0xea, 0xee, 0xb9, 0xdd, 0x20, 0x4a, 0x88, 0xc8, 0x2f, 0x30, 0x76, 0xab, + 0xa9, 0x15, 0xeb, 0xe9, 0x10, 0x53, 0xa8, 0x95, 0x5c, 0xee, 0x43, 0x03, 0xe4, 0x72, 0x57, 0x76, + 0xd6, 0x3f, 0xca, 0xc2, 0xb8, 0x32, 0xfd, 0xc8, 0x55, 0xc8, 0x57, 0xfd, 0x55, 0xb7, 0x7e, 0x10, + 0x06, 0xab, 0x9c, 0x3c, 0x39, 0x2e, 0x8e, 0x39, 0xbe, 0xd5, 0x44, 0xa0, 0x19, 0x16, 0x93, 0x25, + 0x98, 0xe4, 0x7f, 0xc9, 0xe4, 0x24, 0xd9, 0x48, 0xb7, 0xc6, 0x91, 0x65, 0x5a, 0x12, 0x75, 0xb3, + 0xd5, 0x48, 0xc8, 0x57, 0x00, 0x38, 0x00, 0xc3, 0x1b, 0xe4, 0x06, 0x0f, 0xcc, 0x20, 0x2a, 0x48, + 0x09, 0x6c, 0xa0, 0x30, 0x24, 0x5f, 0xe3, 0x21, 0xb3, 0xe5, 0x72, 0x19, 0x1a, 0x3c, 0xb2, 0x04, + 0xe3, 0x6f, 0xa5, 0x07, 0xb8, 0x51, 0x59, 0x8a, 0x7c, 0x42, 0x0b, 0x26, 0xad, 0xbb, 0x0f, 0xa9, + 0x77, 0x54, 0x0a, 0x10, 0x51, 0xc1, 0x30, 0xfe, 0x97, 0x8c, 0xb2, 0xc8, 0xc8, 0x3a, 0xe6, 0x03, + 0xe7, 0x13, 0x48, 0x98, 0x94, 0x85, 0x57, 0x0c, 0x09, 0x37, 0xe9, 0xee, 0xd2, 0xb3, 0xc2, 0xba, + 0x6d, 0x36, 0x9c, 0x86, 0xb1, 0x3c, 0xe1, 0x1c, 0x48, 0xbe, 0x00, 0x43, 0xd8, 0x75, 0xd9, 0x53, + 0x9b, 0x26, 0x4f, 0xf9, 0x21, 0xd6, 0x67, 0xd8, 0x10, 0xa4, 0x24, 0x9f, 0x16, 0x8e, 0xdb, 0xbc, + 0xf3, 0xa7, 0x94, 0xa3, 0x9a, 0x7d, 0x47, 0x78, 0xbc, 0x47, 0x31, 0x8e, 0x94, 0xd9, 0xf3, 0xb7, + 0xb2, 0x50, 0x88, 0x2f, 0x6d, 0xf2, 0x3e, 0x4c, 0xc8, 0xe3, 0x77, 0xc5, 0x16, 0x99, 0x35, 0x26, + 0x44, 0x66, 0x0b, 0x79, 0x06, 0xef, 0xdb, 0xaa, 0x09, 0x9a, 0xa9, 0x11, 0x30, 0x59, 0x68, 0x53, + 0xc4, 0x1c, 0x54, 0x16, 0x55, 0xe0, 0x06, 0x9d, 0x58, 0xa4, 0x66, 0x89, 0x46, 0xde, 0x84, 0xdc, + 0xda, 0xed, 0x92, 0x70, 0xf0, 0x2b, 0xc4, 0x0f, 0x69, 0x6e, 0x29, 0xab, 0xdb, 0xed, 0x32, 0x7c, + 0xb2, 0xaa, 0x04, 0x35, 0x1f, 0xd1, 0xcc, 0x0d, 0x25, 0x38, 0x6c, 0xdc, 0xe9, 0xd1, 0xcd, 0xef, + 0x0e, 0xe5, 0x73, 0x85, 0x21, 0x11, 0xa6, 0xf7, 0x9f, 0xe7, 0x60, 0x2c, 0xac, 0x9f, 0x10, 0xd5, + 0x6d, 0x9a, 0xbb, 0x48, 0x93, 0x8b, 0x90, 0x97, 0xd2, 0x9d, 0xf0, 0xf3, 0x1b, 0xf5, 0x85, 0x64, + 0x37, 0x0f, 0x52, 0x8c, 0xe3, 0xbb, 0x82, 0x29, 0x7f, 0x92, 0x1b, 0x10, 0xca, 0x68, 0xbd, 0x84, + 0xb9, 0x21, 0x36, 0x60, 0x66, 0x88, 0x46, 0xa6, 0x20, 0xeb, 0xf0, 0xd0, 0x6f, 0x63, 0x66, 0xd6, + 0x69, 0x90, 0xf7, 0x21, 0x6f, 0x37, 0x1a, 0xb4, 0x61, 0xd9, 0xd2, 0x36, 0xab, 0xdf, 0xa4, 0xc9, + 0x33, 0x6e, 0xfc, 0xcc, 0x40, 0xaa, 0x52, 0x40, 0x4a, 0x30, 0xd6, 0xb4, 0xb9, 0xb5, 0x67, 0x63, + 0x80, 0x03, 0x28, 0xe2, 0x90, 0x67, 0x64, 0x5b, 0x3e, 0x6d, 0x90, 0x57, 0x61, 0x88, 0x8d, 0xa6, + 0x38, 0x71, 0xa4, 0x50, 0xc9, 0x06, 0x93, 0x77, 0xd8, 0xca, 0x33, 0x26, 0x22, 0x90, 0x97, 0x21, + 0xd7, 0x5d, 0xdc, 0x15, 0x67, 0x49, 0x21, 0x4a, 0x30, 0x10, 0xa2, 0xb1, 0x62, 0x72, 0x13, 0xf2, + 0x87, 0x7a, 0x6c, 0xfa, 0x73, 0xb1, 0x61, 0x0c, 0xf1, 0x43, 0x44, 0xf2, 0x2a, 0xe4, 0x7c, 0xdf, + 0x15, 0x06, 0x4d, 0xb3, 0xa1, 0x95, 0xe9, 0xfd, 0x70, 0xd4, 0x18, 0x77, 0xdf, 0x77, 0x97, 0xf2, + 0x30, 0xc2, 0x0f, 0x18, 0xe3, 0x05, 0x80, 0xe8, 0x1b, 0x93, 0x7e, 0x9b, 0xc6, 0x57, 0x60, 0x2c, + 0xfc, 0x36, 0xf2, 0x3c, 0xc0, 0x01, 0x3d, 0xb2, 0xf6, 0xed, 0x76, 0xa3, 0xc9, 0xa5, 0xd3, 0x09, + 0x73, 0xec, 0x80, 0x1e, 0xad, 0x20, 0x80, 0x5c, 0x80, 0xd1, 0x0e, 0x1b, 0x7e, 0x31, 0xc7, 0x27, + 0xcc, 0x91, 0x4e, 0x77, 0x87, 0x4d, 0xe5, 0x79, 0x18, 0x45, 0x3d, 0xab, 0x58, 0x91, 0x93, 0xa6, + 0xfc, 0x69, 0xfc, 0x59, 0x0e, 0x53, 0x38, 0x29, 0x0d, 0x22, 0x2f, 0xc1, 0x64, 0xdd, 0xa3, 0x78, + 0x96, 0xd9, 0x4c, 0x42, 0x13, 0xf5, 0x4c, 0x44, 0xc0, 0x6a, 0x83, 0x5c, 0x86, 0xe9, 0x4e, 0x77, + 0xa7, 0xe9, 0xd4, 0x59, 0x6d, 0x56, 0x7d, 0x47, 0xe4, 0x9c, 0x98, 0x30, 0x27, 0x39, 0xf8, 0x1e, + 0x3d, 0x2a, 0xef, 0x60, 0x6c, 0xc3, 0x82, 0x1a, 0x9a, 0x3a, 0x08, 0x93, 0xeb, 0x9b, 0xd3, 0x0a, + 0x1c, 0x6d, 0x33, 0xcf, 0xc3, 0x88, 0x6d, 0xef, 0x75, 0x1d, 0x1e, 0x83, 0x6c, 0xc2, 0x14, 0xbf, + 0xc8, 0xa7, 0x60, 0x26, 0x8a, 0x96, 0x2e, 0x9b, 0x31, 0x8c, 0xcd, 0x28, 0x84, 0x05, 0x65, 0x0e, + 0x27, 0x6f, 0x00, 0x51, 0xeb, 0x73, 0x77, 0x3e, 0xa2, 0x75, 0x3e, 0x27, 0x27, 0xcc, 0x19, 0xa5, + 0xe4, 0x3e, 0x16, 0x90, 0x17, 0x61, 0xc2, 0xa3, 0x3e, 0x4a, 0x87, 0xd8, 0x6d, 0x98, 0xe1, 0xd0, + 0x1c, 0x97, 0x30, 0xd6, 0x77, 0x57, 0xa0, 0xa0, 0x74, 0x07, 0x46, 0xff, 0xe6, 0xe9, 0x16, 0xcc, + 0xa9, 0x08, 0x6e, 0x76, 0xaa, 0x0d, 0xf2, 0x45, 0x58, 0x50, 0x30, 0x79, 0xb2, 0x45, 0x8b, 0x36, + 0x9d, 0x3d, 0x67, 0xa7, 0x49, 0xc5, 0x7c, 0x4b, 0xce, 0xea, 0xf0, 0x0a, 0x69, 0xce, 0x47, 0xd4, + 0x3c, 0x0d, 0xe3, 0xb2, 0xa0, 0x25, 0xab, 0x30, 0x17, 0xe3, 0x4c, 0x1b, 0x56, 0xb7, 0xd3, 0x33, + 0xe8, 0x5f, 0xc4, 0x93, 0xe8, 0x3c, 0x69, 0x63, 0xab, 0x63, 0x7c, 0x03, 0x26, 0xd4, 0x39, 0xc9, + 0x3a, 0x41, 0x95, 0x4b, 0xc4, 0xec, 0x1b, 0x0f, 0x61, 0x55, 0x76, 0x2f, 0x9c, 0x8a, 0x50, 0x70, + 0x10, 0xf9, 0xf6, 0x32, 0x19, 0x42, 0x71, 0x08, 0x5f, 0x84, 0x89, 0x86, 0xe3, 0x77, 0x9a, 0xf6, + 0x91, 0x15, 0x65, 0x11, 0x37, 0xc7, 0x05, 0x0c, 0x15, 0x3f, 0x4b, 0x30, 0x93, 0xd8, 0x07, 0x15, + 0x49, 0x83, 0xef, 0xeb, 0xfd, 0x25, 0x0d, 0xa3, 0x0d, 0x13, 0xea, 0xb9, 0x76, 0x4a, 0x72, 0x94, + 0xf3, 0x18, 0x86, 0x87, 0x6f, 0xfa, 0x23, 0x27, 0xc7, 0xc5, 0xac, 0xd3, 0xc0, 0xe0, 0x3b, 0x57, + 0x20, 0x2f, 0x25, 0x36, 0x21, 0x28, 0xe1, 0x63, 0x82, 0x7c, 0x9a, 0x34, 0xc3, 0x52, 0xe3, 0x55, + 0x18, 0x15, 0x47, 0x57, 0xff, 0x27, 0x04, 0xe3, 0x9b, 0x59, 0x98, 0x36, 0x29, 0xdb, 0x58, 0x29, + 0xcf, 0x88, 0xf4, 0xd4, 0x5e, 0xd1, 0xd3, 0xc3, 0xc5, 0x6a, 0x6d, 0xeb, 0x93, 0x8b, 0xe8, 0x57, + 0x32, 0x30, 0x9b, 0x82, 0xfb, 0xb1, 0x72, 0xf1, 0xde, 0x82, 0xb1, 0x8a, 0x63, 0x37, 0x4b, 0x8d, + 0x46, 0x18, 0x93, 0x07, 0xe5, 0x7c, 0x4c, 0xd8, 0x65, 0x33, 0xa8, 0x2a, 0xc4, 0x84, 0xa8, 0xe4, + 0x35, 0x31, 0x29, 0xa2, 0x4c, 0xf6, 0x38, 0x29, 0xbe, 0x73, 0x5c, 0x04, 0xfe, 0x4d, 0x9b, 0xe1, + 0x14, 0xc1, 0x10, 0xce, 0x1c, 0x18, 0xf9, 0x55, 0x3d, 0xb5, 0x43, 0x97, 0x1e, 0xc2, 0x39, 0xde, + 0xbc, 0x81, 0xd2, 0x11, 0xfd, 0x44, 0x16, 0xce, 0xa7, 0x13, 0x7e, 0xdc, 0xb4, 0xca, 0x98, 0x08, + 0x4a, 0x09, 0x3b, 0x8f, 0x69, 0x95, 0x79, 0xd6, 0x28, 0xc4, 0x8f, 0x10, 0xc8, 0x2e, 0x4c, 0xae, + 0xda, 0x7e, 0xb0, 0x42, 0x6d, 0x2f, 0xd8, 0xa1, 0x76, 0x30, 0x80, 0x24, 0x2f, 0xad, 0x29, 0xe6, + 0x51, 0x98, 0xd8, 0x97, 0x94, 0x31, 0x59, 0x5b, 0x67, 0x1b, 0x4e, 0x94, 0xa1, 0x01, 0x26, 0xca, + 0xd7, 0x61, 0xba, 0x46, 0x5b, 0x76, 0x67, 0xdf, 0xf5, 0x64, 0xbc, 0x84, 0x6b, 0x30, 0x19, 0x82, + 0x52, 0x67, 0x8b, 0x5e, 0xac, 0xe1, 0x2b, 0x1d, 0x11, 0x6d, 0x25, 0x7a, 0xb1, 0xf1, 0xb7, 0xb3, + 0x70, 0xa1, 0x54, 0x17, 0xa6, 0xa1, 0xa2, 0x40, 0x5a, 0xb0, 0x7f, 0xc2, 0x75, 0x93, 0xeb, 0x30, + 0xb6, 0x66, 0x3f, 0x5a, 0xa5, 0xb6, 0x4f, 0x7d, 0x91, 0xd4, 0x92, 0x8b, 0xbd, 0xf6, 0xa3, 0xe8, + 0xf1, 0xc7, 0x8c, 0x70, 0x54, 0x35, 0xc2, 0xd0, 0x63, 0xaa, 0x11, 0x0c, 0x18, 0x59, 0x71, 0x9b, + 0x0d, 0x71, 0xd6, 0x8b, 0x17, 0xe7, 0x7d, 0x84, 0x98, 0xa2, 0xc4, 0xf8, 0x93, 0x0c, 0x4c, 0x85, + 0x5f, 0x8c, 0x9f, 0xf0, 0x89, 0x77, 0xc9, 0x65, 0x18, 0xc5, 0x8a, 0xc2, 0xec, 0xfb, 0x78, 0x68, + 0x34, 0x29, 0xa6, 0x26, 0x6c, 0x98, 0xb2, 0x50, 0xed, 0x89, 0xe1, 0xc7, 0xeb, 0x09, 0xe3, 0x1f, + 0xe2, 0x63, 0xb6, 0xda, 0x4a, 0x76, 0x12, 0x29, 0x1f, 0x92, 0x19, 0xf0, 0x43, 0xb2, 0x4f, 0x6c, + 0x48, 0x72, 0x3d, 0x87, 0xe4, 0x47, 0xb2, 0x30, 0x1e, 0x7e, 0xec, 0x77, 0x59, 0xee, 0x83, 0xb0, + 0x5d, 0x03, 0xc5, 0x38, 0xaa, 0x29, 0x7b, 0x85, 0x08, 0x25, 0xf4, 0x05, 0x18, 0x11, 0x8b, 0x29, + 0x13, 0xb3, 0xe4, 0x8e, 0x8d, 0xee, 0xd2, 0x94, 0x60, 0x3d, 0x82, 0x03, 0xea, 0x9b, 0x82, 0x0e, + 0x83, 0x48, 0x6d, 0xd3, 0x1d, 0x61, 0xdb, 0xf0, 0xd4, 0x9e, 0x51, 0xe9, 0x41, 0xa4, 0xa2, 0x86, + 0x0d, 0x74, 0x3a, 0xfd, 0xb3, 0x3c, 0x14, 0xe2, 0x24, 0xa7, 0x67, 0x97, 0xd8, 0xe8, 0xee, 0xf0, + 0xab, 0x0a, 0xcf, 0x2e, 0xd1, 0xe9, 0xee, 0x98, 0x0c, 0x86, 0xa6, 0x4f, 0x9e, 0xf3, 0x10, 0x5b, + 0x3d, 0x21, 0x4c, 0x9f, 0x3c, 0xe7, 0xa1, 0x66, 0xfa, 0xe4, 0x39, 0x0f, 0x51, 0x91, 0xb0, 0x5a, + 0xc3, 0x00, 0x0b, 0x78, 0x4f, 0x11, 0x8a, 0x84, 0xa6, 0x1f, 0xcf, 0x14, 0x27, 0xd1, 0xd8, 0x51, + 0xb9, 0x44, 0x6d, 0x4f, 0x64, 0x42, 0x10, 0xdb, 0x19, 0x1e, 0x95, 0x3b, 0x08, 0xb6, 0x02, 0x06, + 0x37, 0x55, 0x24, 0xd2, 0x04, 0xa2, 0xfc, 0x94, 0x0b, 0xf8, 0xf4, 0xbb, 0xb5, 0xb4, 0xc2, 0x9c, + 0x53, 0x59, 0x5b, 0xea, 0x6a, 0x4e, 0xe1, 0xfb, 0x24, 0xb5, 0xbf, 0x1b, 0x22, 0xbc, 0x2b, 0x2a, + 0x90, 0xf2, 0xa7, 0x32, 0x93, 0x81, 0x61, 0x80, 0x87, 0x7f, 0x0d, 0xd5, 0x48, 0x11, 0x13, 0xf2, + 0x1e, 0x8c, 0xab, 0x61, 0x33, 0x78, 0x70, 0x87, 0xe7, 0x78, 0x3c, 0xcd, 0x1e, 0xd9, 0x85, 0x55, + 0x02, 0xb2, 0x03, 0x17, 0xca, 0x6e, 0xdb, 0xef, 0xb6, 0x64, 0xe4, 0xce, 0x28, 0x22, 0x39, 0xe0, + 0x50, 0xa0, 0x0f, 0x7e, 0x5d, 0xa0, 0x88, 0x28, 0x0d, 0xd2, 0x4d, 0x46, 0xbf, 0x80, 0xf4, 0x62, + 0x44, 0x36, 0x61, 0x1c, 0x35, 0xa8, 0xc2, 0xe4, 0x71, 0x5c, 0xdf, 0x36, 0xa2, 0x92, 0x0a, 0x5b, + 0x18, 0x3c, 0x6a, 0x9c, 0xdd, 0x6a, 0x4a, 0x2f, 0x0d, 0x55, 0x13, 0xac, 0x20, 0x93, 0xaf, 0xc0, + 0x14, 0xbf, 0xa2, 0x6d, 0xd3, 0x1d, 0x3e, 0x77, 0x26, 0x34, 0x4d, 0x84, 0x5e, 0xc8, 0x1f, 0xf3, + 0x85, 0xde, 0xfa, 0x90, 0xee, 0xf0, 0xb1, 0xd7, 0x7c, 0xa4, 0x34, 0x7c, 0xb2, 0x05, 0xb3, 0x2b, + 0xb6, 0xcf, 0x81, 0x4a, 0xfc, 0x83, 0x49, 0xd4, 0xd0, 0xa2, 0xed, 0xfa, 0xbe, 0xed, 0x4b, 0x45, + 0x78, 0x6a, 0xbc, 0x83, 0x34, 0x7a, 0xf2, 0xcd, 0x0c, 0xcc, 0x6b, 0x7a, 0x72, 0x61, 0x67, 0xd6, + 0xa2, 0xed, 0x00, 0x9d, 0xa1, 0xa6, 0x16, 0x8b, 0x52, 0x28, 0xed, 0x81, 0xc6, 0x87, 0x24, 0xa6, + 0x8a, 0xf7, 0xa2, 0x72, 0xd5, 0x28, 0xbc, 0x17, 0x0f, 0xb1, 0x50, 0x71, 0x4d, 0x4f, 0xeb, 0x0b, + 0x35, 0xb6, 0xae, 0x25, 0x9a, 0x71, 0x2b, 0xde, 0xdf, 0x42, 0xd1, 0x95, 0x09, 0x15, 0x5d, 0x73, + 0x30, 0x8c, 0xbd, 0x2a, 0xa3, 0x68, 0xe1, 0x0f, 0xe3, 0xd3, 0xea, 0x3e, 0x24, 0xc4, 0xc2, 0xbe, + 0xfb, 0x90, 0xf1, 0x3f, 0x8e, 0xc0, 0x74, 0x6c, 0x5a, 0x88, 0x7b, 0x6a, 0x26, 0x71, 0x4f, 0xad, + 0x01, 0x70, 0x55, 0xef, 0x80, 0x3a, 0x59, 0xe9, 0x88, 0x39, 0x2e, 0xdc, 0xa8, 0xc3, 0x35, 0xa5, + 0xb0, 0x61, 0x4c, 0xf9, 0x8a, 0x1d, 0x50, 0x47, 0x1e, 0x32, 0xe5, 0x8b, 0x5e, 0x61, 0x1a, 0xb1, + 0x21, 0x45, 0x18, 0xc6, 0xf8, 0xb9, 0xaa, 0x1f, 0xac, 0xc3, 0x00, 0x26, 0x87, 0x93, 0x97, 0x60, + 0x84, 0x09, 0x51, 0xd5, 0x8a, 0xd8, 0x04, 0xf1, 0x6c, 0x61, 0x52, 0x16, 0x93, 0x58, 0x44, 0x11, + 0xb9, 0x05, 0x13, 0xfc, 0x2f, 0x11, 0x66, 0x67, 0x44, 0x37, 0x7e, 0xb4, 0x9c, 0x86, 0x8c, 0xb4, + 0xa3, 0xe1, 0xb1, 0xdb, 0x45, 0xad, 0x8b, 0x6a, 0x9d, 0x6a, 0x45, 0x84, 0x74, 0xc7, 0xdb, 0x85, + 0xcf, 0x81, 0xac, 0x8a, 0x08, 0x81, 0xc9, 0x32, 0xc2, 0x1b, 0x25, 0x8f, 0x77, 0x4a, 0x94, 0x65, + 0xb8, 0x17, 0x8a, 0x29, 0x4a, 0xc8, 0x55, 0xfe, 0x12, 0x83, 0x62, 0x21, 0xcf, 0x8c, 0x89, 0xef, + 0x16, 0xa8, 0x98, 0x40, 0xd9, 0x30, 0x2c, 0x66, 0x95, 0xb3, 0xbf, 0x97, 0x5b, 0xb6, 0xd3, 0x14, + 0xdb, 0x0a, 0x56, 0x8e, 0xb8, 0x94, 0x41, 0xcd, 0x08, 0x81, 0xbc, 0x03, 0x53, 0xec, 0x47, 0xd9, + 0x6d, 0xb5, 0xdc, 0x36, 0xb2, 0x1f, 0x8f, 0x02, 0xe9, 0x21, 0x49, 0x1d, 0x8b, 0x78, 0x2d, 0x31, + 0x5c, 0x76, 0x9e, 0xe0, 0x2b, 0x6f, 0x97, 0xbf, 0x11, 0x4d, 0x44, 0xe7, 0x09, 0x92, 0xfa, 0x1c, + 0x6e, 0xaa, 0x48, 0xe4, 0x2d, 0x98, 0x64, 0x3f, 0xef, 0x38, 0x0f, 0x29, 0xaf, 0x70, 0x32, 0x32, + 0x6f, 0x40, 0xaa, 0x3d, 0x56, 0xc2, 0xeb, 0xd3, 0x31, 0xc9, 0x07, 0x70, 0x0e, 0x39, 0xd5, 0xdd, + 0x0e, 0x6d, 0x94, 0x76, 0x77, 0x9d, 0xa6, 0xc3, 0xad, 0xd1, 0x78, 0x40, 0x19, 0xd4, 0xc1, 0xf3, + 0x8a, 0x11, 0xc3, 0xb2, 0x23, 0x14, 0x33, 0x9d, 0x92, 0x6c, 0x43, 0xa1, 0xdc, 0xf5, 0x03, 0xb7, + 0x55, 0x0a, 0x02, 0xcf, 0xd9, 0xe9, 0x06, 0xd4, 0x9f, 0x9f, 0xd6, 0xc2, 0xae, 0xb0, 0xc5, 0x11, + 0x16, 0x72, 0x7d, 0x50, 0x1d, 0x29, 0x2c, 0x3b, 0x24, 0x31, 0x13, 0x4c, 0x8c, 0x7f, 0x91, 0x81, + 0x49, 0x8d, 0x94, 0xbc, 0x09, 0x13, 0xb7, 0x3d, 0x87, 0xb6, 0x1b, 0xcd, 0x23, 0xe5, 0xa2, 0x8a, + 0xb7, 0x98, 0x5d, 0x01, 0xe7, 0xad, 0xd6, 0xd0, 0x42, 0x3d, 0x4f, 0x36, 0xd5, 0x54, 0xf4, 0x3a, + 0x77, 0xc7, 0x16, 0x13, 0x34, 0x17, 0xc5, 0x81, 0xc2, 0x09, 0x2a, 0x66, 0xa7, 0x82, 0x42, 0xde, + 0x85, 0x11, 0xfe, 0x1e, 0x2c, 0xec, 0x16, 0x2f, 0xa6, 0x35, 0x93, 0xbb, 0xfe, 0xe3, 0x44, 0x44, + 0xa3, 0x1f, 0xdf, 0x14, 0x44, 0xc6, 0xcf, 0x65, 0x80, 0x24, 0x51, 0x4f, 0xd1, 0x7b, 0x9d, 0x6a, + 0x4c, 0xf4, 0x85, 0x70, 0x35, 0xe6, 0x34, 0x9d, 0x39, 0xab, 0x89, 0x17, 0xf0, 0x8e, 0x17, 0xab, + 0x4e, 0x55, 0xc4, 0xf1, 0x62, 0xe3, 0x87, 0xb3, 0x00, 0x11, 0x36, 0xf9, 0x1c, 0x4f, 0x84, 0xf6, + 0x41, 0xd7, 0x6e, 0x3a, 0xbb, 0x8e, 0x1e, 0xb7, 0x17, 0x99, 0x7c, 0x5d, 0x96, 0x98, 0x3a, 0x22, + 0x79, 0x1f, 0xa6, 0x6b, 0x1b, 0x3a, 0xad, 0x62, 0x16, 0xef, 0x77, 0xac, 0x18, 0x79, 0x1c, 0x1b, + 0xed, 0x93, 0xd5, 0xd1, 0xe0, 0xf6, 0xc9, 0x7c, 0x20, 0x44, 0x09, 0xdb, 0x58, 0x6a, 0x1b, 0xc2, + 0xf2, 0xbf, 0x11, 0xbe, 0x6a, 0xe2, 0xd7, 0xf9, 0x1d, 0xab, 0x23, 0x5c, 0x02, 0xd8, 0x3e, 0xa1, + 0xe1, 0x45, 0x1d, 0x39, 0xdc, 0xc3, 0xbd, 0xff, 0xe7, 0x51, 0xed, 0xd7, 0x72, 0x03, 0x2a, 0xb4, + 0x1d, 0x4f, 0xed, 0xbd, 0x27, 0x32, 0x26, 0x18, 0xd6, 0xbc, 0x96, 0xb5, 0xd6, 0x09, 0x83, 0x99, + 0x9b, 0xd1, 0x25, 0x85, 0x9b, 0x15, 0xa4, 0xd8, 0xd8, 0xfc, 0xfd, 0x0c, 0x9c, 0x4b, 0xa5, 0x25, + 0xd7, 0x00, 0x22, 0x9d, 0x92, 0xe8, 0x25, 0xdc, 0x31, 0xa3, 0xe8, 0x47, 0xa6, 0x82, 0x41, 0xbe, + 0x1c, 0xd7, 0x06, 0x9d, 0x7e, 0x10, 0x2e, 0xc8, 0xa0, 0x83, 0xba, 0x36, 0x28, 0x45, 0x07, 0x64, + 0xfc, 0x4a, 0x0e, 0x66, 0x94, 0xe0, 0x4a, 0xfc, 0x5b, 0x4f, 0xb1, 0x17, 0x3f, 0x80, 0x09, 0xd6, + 0x1a, 0xa7, 0x2e, 0xdc, 0x6e, 0xb8, 0xe1, 0xcb, 0x6b, 0x09, 0xbf, 0x53, 0xc1, 0xed, 0x9a, 0x8a, + 0xcc, 0x43, 0x81, 0xe2, 0xd6, 0x89, 0x0f, 0x12, 0xf5, 0xa4, 0xcb, 0x8d, 0xc6, 0x9c, 0xf8, 0x30, + 0x59, 0x39, 0x6a, 0xdb, 0xad, 0xb0, 0x36, 0x6e, 0x00, 0xf3, 0xa9, 0x9e, 0xb5, 0x69, 0xd8, 0xbc, + 0xba, 0xc8, 0x43, 0x8b, 0x97, 0xa5, 0x04, 0x07, 0xd0, 0xa8, 0x16, 0xde, 0x87, 0x99, 0xc4, 0x47, + 0x9f, 0x29, 0x2a, 0xe9, 0x36, 0x90, 0xe4, 0x77, 0xa4, 0x70, 0xf8, 0x94, 0x1e, 0xf3, 0xf6, 0x5c, + 0xf8, 0x78, 0xdd, 0x6a, 0xd9, 0xed, 0x06, 0x37, 0xa7, 0x59, 0x54, 0x63, 0x96, 0xfe, 0x7c, 0x56, + 0xf5, 0xfd, 0x7d, 0xda, 0x57, 0xdd, 0x17, 0xb4, 0xdb, 0xf0, 0x0b, 0xbd, 0xc6, 0x74, 0x20, 0xad, + 0xc3, 0xb7, 0x73, 0x70, 0xa1, 0x07, 0x25, 0x39, 0x8a, 0x4f, 0x22, 0xae, 0x85, 0xb8, 0xd1, 0xbf, + 0xc2, 0x27, 0x31, 0x95, 0xc8, 0xe7, 0x78, 0xf4, 0x8f, 0xba, 0xdb, 0xde, 0x75, 0xf6, 0xc4, 0xfd, + 0x1b, 0xd5, 0xf8, 0x07, 0x21, 0x34, 0x1e, 0xf6, 0x83, 0x43, 0xc9, 0xfb, 0x30, 0x8c, 0x8e, 0xdf, + 0xb1, 0xf0, 0x8e, 0x0c, 0x03, 0xe1, 0x4a, 0x80, 0x52, 0xf6, 0x53, 0x0b, 0x50, 0xca, 0x00, 0xe4, + 0xb3, 0x90, 0x2b, 0x6d, 0xd7, 0xc4, 0xb8, 0x4c, 0xa9, 0xe4, 0xdb, 0xb5, 0x28, 0x7d, 0x8b, 0xad, + 0xe5, 0x59, 0x61, 0x14, 0x8c, 0xf0, 0x4e, 0x79, 0x43, 0x8c, 0x8a, 0x4a, 0x78, 0xa7, 0xbc, 0x11, + 0x11, 0xee, 0xd5, 0xb5, 0x60, 0x59, 0x77, 0xca, 0x1b, 0x9f, 0xdc, 0xb4, 0xff, 0xf7, 0xb2, 0x3c, + 0x64, 0x09, 0x6f, 0xd8, 0xfb, 0x30, 0xa1, 0xc5, 0x24, 0xcf, 0x44, 0xf2, 0x58, 0x18, 0x3f, 0x3e, + 0x66, 0x31, 0xa4, 0x11, 0xc8, 0x44, 0x48, 0xec, 0x37, 0x4a, 0xbc, 0xaa, 0xb1, 0x4d, 0xc8, 0x01, + 0x65, 0xe2, 0x78, 0x22, 0xa4, 0x90, 0x84, 0xdc, 0x84, 0xfc, 0x26, 0x6d, 0xdb, 0xed, 0x20, 0x54, + 0x88, 0xa2, 0x71, 0x71, 0x80, 0x30, 0x5d, 0x6a, 0x08, 0x11, 0xd1, 0x10, 0xb6, 0xbb, 0xe3, 0xd7, + 0x3d, 0x07, 0x43, 0x1b, 0x85, 0x67, 0x31, 0x37, 0x84, 0x55, 0x4a, 0x74, 0x06, 0x31, 0x22, 0xe3, + 0xe7, 0x33, 0x30, 0x2a, 0x06, 0x92, 0x27, 0xb0, 0xdb, 0x8b, 0xce, 0x12, 0xe1, 0x3c, 0xb0, 0xe7, + 0xc4, 0x9d, 0x07, 0xf6, 0x78, 0xfc, 0xa0, 0x31, 0xe1, 0x58, 0x17, 0x3e, 0x0d, 0xe2, 0x6c, 0x94, + 0x6e, 0x9f, 0x7a, 0x7e, 0xb2, 0x10, 0x75, 0x50, 0x87, 0x2c, 0xe3, 0xef, 0x88, 0x2f, 0xbb, 0x53, + 0xde, 0x20, 0x8b, 0x90, 0x5f, 0x75, 0x79, 0x28, 0x2c, 0x35, 0x1b, 0x71, 0x53, 0xc0, 0xd4, 0x0e, + 0x92, 0x78, 0xec, 0xfb, 0x36, 0x3c, 0x57, 0xdc, 0x65, 0x94, 0xef, 0xeb, 0x70, 0x60, 0xec, 0xfb, + 0x42, 0xd4, 0x81, 0xbf, 0x8f, 0xa6, 0x6c, 0x12, 0x0f, 0x6e, 0x62, 0x86, 0x98, 0xbb, 0xaa, 0xa3, + 0x9b, 0x28, 0x92, 0x3b, 0xc5, 0x42, 0xaf, 0x9d, 0xe2, 0xc1, 0x4d, 0x33, 0x85, 0x0a, 0xdf, 0xd5, + 0x22, 0x70, 0x8d, 0x7a, 0x0f, 0x9f, 0xe2, 0x5d, 0x3a, 0xfd, 0x5d, 0x2d, 0xde, 0xbc, 0x81, 0x36, + 0xe9, 0x3f, 0xc8, 0xc2, 0xf9, 0x74, 0x42, 0xb5, 0x2d, 0x99, 0x3e, 0x6d, 0xb9, 0x02, 0xf9, 0x15, + 0xd7, 0x0f, 0x14, 0x23, 0x41, 0x54, 0xff, 0xef, 0x0b, 0x98, 0x19, 0x96, 0xb2, 0x3b, 0x37, 0xfb, + 0x3b, 0x5c, 0x9e, 0xc8, 0x0f, 0x03, 0x75, 0xb0, 0x3b, 0x37, 0x2f, 0x22, 0x77, 0x20, 0x6f, 0x0a, + 0x47, 0xab, 0x58, 0xd7, 0x48, 0x70, 0x28, 0x4d, 0x11, 0x4f, 0x40, 0xb4, 0xd0, 0xf0, 0x02, 0x46, + 0x4a, 0x30, 0x2a, 0x46, 0x3f, 0xf6, 0x74, 0x9c, 0x32, 0x65, 0xf4, 0x6c, 0x0d, 0x92, 0x8e, 0xed, + 0x28, 0xf8, 0x08, 0x58, 0xad, 0x48, 0x9f, 0x29, 0xdc, 0x51, 0xf8, 0x23, 0xa1, 0x6e, 0x8f, 0x19, + 0x22, 0x1a, 0xdf, 0xcc, 0x02, 0x48, 0xad, 0xcd, 0x53, 0x3b, 0xc3, 0x3e, 0xab, 0xcd, 0x30, 0xc5, + 0xde, 0x68, 0xf0, 0x84, 0xcb, 0xf7, 0xd1, 0x9c, 0x67, 0xf0, 0x74, 0xcb, 0x45, 0x18, 0xde, 0x8c, + 0x14, 0x5a, 0xc2, 0x25, 0x05, 0xd5, 0xd1, 0x1c, 0x6e, 0xec, 0xc0, 0xdc, 0x1d, 0x1a, 0x44, 0xea, + 0x2d, 0xf9, 0xf4, 0xd8, 0x9f, 0xed, 0xeb, 0x30, 0x26, 0xf0, 0xc3, 0xfd, 0x8b, 0xeb, 0x62, 0x44, + 0xec, 0x1b, 0xd4, 0xc5, 0x48, 0x04, 0xb6, 0x1b, 0x55, 0x68, 0x93, 0x06, 0xf4, 0x93, 0xad, 0xa6, + 0x06, 0x84, 0x37, 0x05, 0x5b, 0x36, 0x58, 0x0d, 0xa7, 0xf6, 0xcf, 0x03, 0x38, 0x17, 0x7e, 0xfb, + 0x93, 0xe4, 0x7b, 0x9d, 0x5d, 0x29, 0x45, 0xa2, 0x83, 0x88, 0x63, 0x1f, 0xdb, 0x93, 0x47, 0xb0, + 0x20, 0x09, 0xb6, 0x9d, 0xd0, 0x70, 0x72, 0x20, 0x5a, 0xf2, 0x0e, 0x8c, 0x2b, 0x34, 0x22, 0x50, + 0x3f, 0xaa, 0xa9, 0x0f, 0x9d, 0x60, 0xdf, 0xf2, 0x39, 0x5c, 0x55, 0x53, 0x2b, 0xe8, 0xc6, 0x97, + 0xe0, 0xd9, 0xd0, 0x6d, 0x28, 0xa5, 0xea, 0x18, 0xf3, 0xcc, 0xd9, 0x98, 0xaf, 0x47, 0xcd, 0xaa, + 0xb6, 0x43, 0xcf, 0x68, 0xc9, 0x9b, 0xa8, 0xcd, 0x12, 0x8d, 0x79, 0x2e, 0xe1, 0x6b, 0xad, 0xb8, + 0x54, 0x1b, 0x6f, 0x2b, 0x1f, 0x9b, 0xc2, 0x50, 0x23, 0xce, 0xc4, 0x89, 0xbf, 0x99, 0x85, 0xe9, + 0xfb, 0xd5, 0x4a, 0x39, 0xb4, 0x3e, 0xfa, 0x2e, 0x4b, 0x07, 0xad, 0xb5, 0xad, 0xf7, 0x7e, 0x63, + 0x6c, 0xc1, 0x6c, 0xac, 0x1b, 0x50, 0x74, 0x78, 0x8f, 0x3b, 0x9c, 0x84, 0x60, 0x29, 0x36, 0x9c, + 0x4f, 0x63, 0xff, 0xe0, 0xa6, 0x19, 0xc3, 0x36, 0xfe, 0x4b, 0x88, 0xf1, 0x15, 0x5b, 0xd8, 0xeb, + 0x30, 0x56, 0xf5, 0xfd, 0x2e, 0xf5, 0xb6, 0xcc, 0x55, 0x55, 0x55, 0xe0, 0x20, 0xd0, 0xea, 0x7a, + 0x4d, 0x33, 0x42, 0x20, 0x57, 0x21, 0x2f, 0x82, 0xa4, 0xcb, 0x3d, 0x01, 0xb5, 0xb6, 0x61, 0x8c, + 0x75, 0x33, 0x2c, 0x26, 0x6f, 0xc2, 0x04, 0xff, 0x9b, 0xcf, 0x36, 0xd1, 0xe1, 0xa8, 0x1c, 0x14, + 0xe8, 0x7c, 0x76, 0x9a, 0x1a, 0x1a, 0x79, 0x0d, 0x72, 0xa5, 0xb2, 0x29, 0xd4, 0x41, 0x42, 0x6e, + 0xf4, 0x2c, 0xae, 0xb3, 0xd3, 0x2e, 0x11, 0x65, 0x93, 0x49, 0x7f, 0x32, 0xd8, 0x84, 0xd0, 0x64, + 0xe3, 0x0c, 0x90, 0xda, 0xa6, 0xd8, 0x61, 0x86, 0x30, 0x72, 0x1d, 0x46, 0x2b, 0xdc, 0x64, 0x4e, + 0xe8, 0xb1, 0x79, 0xae, 0x43, 0x0e, 0xd2, 0x82, 0x2b, 0x70, 0x10, 0xb9, 0x2a, 0x33, 0xb4, 0xe5, + 0x23, 0xbf, 0x95, 0x1e, 0x69, 0xd8, 0x5e, 0x87, 0x11, 0x11, 0x4a, 0x7c, 0x4c, 0xc9, 0xdd, 0x12, + 0x0f, 0x21, 0x2e, 0x70, 0x92, 0x0e, 0xac, 0xf0, 0x24, 0x1d, 0x58, 0x77, 0xe0, 0xc2, 0x1d, 0xd4, + 0xde, 0xe8, 0x01, 0xb1, 0xb6, 0xcc, 0xaa, 0xd0, 0x87, 0xe3, 0x33, 0x10, 0x57, 0xf0, 0xc4, 0x63, + 0x6a, 0x59, 0x5d, 0x4f, 0x4d, 0xdd, 0xdb, 0x8b, 0x11, 0xf9, 0x22, 0xcc, 0xa5, 0x15, 0x09, 0xad, + 0x39, 0x86, 0x7e, 0x4a, 0xaf, 0x40, 0x0d, 0xfd, 0x94, 0xc6, 0x81, 0xac, 0x42, 0x81, 0xc3, 0x4b, + 0x8d, 0x96, 0xd3, 0xe6, 0x9a, 0x7f, 0xae, 0x55, 0x47, 0x47, 0x12, 0xc1, 0xd5, 0x66, 0x85, 0xfc, + 0x05, 0x40, 0x73, 0x3d, 0x8a, 0x51, 0x92, 0x9f, 0xca, 0xb0, 0xdb, 0x1c, 0x0f, 0xbc, 0xbd, 0x65, + 0xae, 0xfa, 0x22, 0x6c, 0xe0, 0xf9, 0xc8, 0xab, 0xa8, 0x16, 0x78, 0x4e, 0x7b, 0x4f, 0xb8, 0x15, + 0x6d, 0x0a, 0xb7, 0xa2, 0x77, 0x3e, 0x96, 0x5b, 0x11, 0x67, 0xe5, 0x9f, 0x1c, 0x17, 0x27, 0x3c, + 0x51, 0x27, 0xae, 0x22, 0xed, 0x0b, 0x58, 0xd7, 0xa1, 0x6f, 0xed, 0x56, 0x9b, 0x87, 0xfd, 0xa5, + 0x0d, 0xde, 0xc8, 0x69, 0xdc, 0xc1, 0xb1, 0xeb, 0x30, 0x27, 0x88, 0xd5, 0x0d, 0x11, 0x12, 0x0d, + 0x4d, 0xe5, 0xc0, 0x2e, 0x9e, 0xd2, 0x75, 0x85, 0x7b, 0xe3, 0x16, 0xa2, 0x8b, 0xa7, 0xf4, 0x73, + 0xb1, 0x70, 0x1a, 0xa9, 0x93, 0x47, 0x23, 0x21, 0xd7, 0x61, 0x64, 0xcd, 0x7e, 0x54, 0xda, 0xa3, + 0x22, 0xb7, 0xe7, 0xa4, 0xdc, 0xfe, 0x10, 0xb8, 0x94, 0xff, 0x43, 0xee, 0xeb, 0xf0, 0x8c, 0x29, + 0xd0, 0xc8, 0xf7, 0x67, 0xe0, 0x3c, 0x5f, 0xc6, 0xb2, 0x95, 0x35, 0x1a, 0x04, 0xac, 0x1f, 0x44, + 0xfc, 0xc0, 0x4b, 0x91, 0xc1, 0x76, 0x3a, 0x1e, 0x7a, 0xde, 0x1b, 0x62, 0x67, 0x08, 0x3b, 0xce, + 0x17, 0xa5, 0x5a, 0x20, 0xe6, 0x54, 0x7a, 0xb2, 0x09, 0xe3, 0x6b, 0xb7, 0x4b, 0x61, 0xb5, 0x3c, + 0x3a, 0x7b, 0x31, 0x6d, 0x77, 0x54, 0xd0, 0xd2, 0x3c, 0x0d, 0x54, 0x36, 0xc2, 0x3b, 0xe0, 0xb3, + 0xb2, 0x3f, 0xc8, 0x1b, 0xaa, 0x2b, 0x6a, 0x0e, 0xa5, 0xe7, 0xd1, 0x96, 0xfd, 0xc8, 0xb2, 0xf7, + 0xa8, 0xf6, 0x4a, 0x2e, 0xb4, 0xd7, 0x3f, 0x9b, 0x81, 0x8b, 0x3d, 0x9b, 0x4c, 0x6e, 0xc1, 0x05, + 0x9b, 0x3b, 0x58, 0x5b, 0xfb, 0x41, 0xd0, 0xf1, 0x2d, 0x79, 0xc5, 0x10, 0xce, 0xab, 0xe6, 0x39, + 0x51, 0xbc, 0xc2, 0x4a, 0xe5, 0xad, 0xc3, 0x27, 0xef, 0xc3, 0x73, 0x4e, 0xdb, 0xa7, 0xf5, 0xae, + 0x47, 0x2d, 0xc9, 0xa0, 0xee, 0x34, 0x3c, 0xcb, 0xb3, 0xdb, 0x7b, 0xd2, 0x13, 0xd7, 0xbc, 0x28, + 0x71, 0x84, 0x13, 0x77, 0xd9, 0x69, 0x78, 0x26, 0x22, 0x18, 0xff, 0x22, 0x03, 0xf3, 0xbd, 0xba, + 0x84, 0xcc, 0xc3, 0x28, 0x55, 0xf2, 0xb4, 0xe4, 0x4d, 0xf9, 0x93, 0x3c, 0x0b, 0xd1, 0x4e, 0x2f, + 0x4e, 0xff, 0x7c, 0x5d, 0xe4, 0xcc, 0x40, 0xd3, 0x76, 0x75, 0x5f, 0x17, 0x06, 0xca, 0x13, 0x75, + 0x75, 0x77, 0x7f, 0x1e, 0x20, 0xda, 0xce, 0xb9, 0x62, 0xc2, 0x1c, 0xb3, 0xeb, 0x1e, 0x5f, 0x79, + 0xe4, 0x3c, 0x8c, 0xf0, 0xed, 0x52, 0xf8, 0x3f, 0x88, 0x5f, 0xec, 0xdc, 0x16, 0x9d, 0x8c, 0xfb, + 0x7c, 0x6e, 0x69, 0x42, 0xeb, 0xec, 0x91, 0x16, 0x0e, 0x8e, 0xf1, 0xd3, 0x93, 0x5c, 0x84, 0x28, + 0x75, 0x83, 0x7d, 0x29, 0x74, 0x2c, 0xa6, 0xf9, 0x8b, 0x71, 0x5b, 0x4a, 0xc5, 0x2e, 0x5b, 0xf7, + 0x12, 0x93, 0x6f, 0x3f, 0xd9, 0xd4, 0xb7, 0x9f, 0xd7, 0x61, 0xac, 0xbc, 0x4f, 0xeb, 0x07, 0xa1, + 0x13, 0x4e, 0x5e, 0x28, 0xd7, 0x19, 0x90, 0x87, 0x44, 0x8f, 0x10, 0xc8, 0x75, 0x00, 0x74, 0x53, + 0xe5, 0x12, 0xa9, 0x92, 0xd6, 0x04, 0xbd, 0x5a, 0x85, 0x79, 0x8a, 0x82, 0x82, 0xec, 0x6b, 0xe6, + 0x6d, 0xd5, 0x9e, 0x85, 0xb3, 0xf7, 0xbd, 0x5d, 0x81, 0x1e, 0x21, 0xb0, 0xe6, 0x29, 0xfb, 0x8a, + 0x38, 0x05, 0x0b, 0x89, 0xcd, 0x47, 0x45, 0x22, 0xd7, 0x60, 0x6c, 0x43, 0x3a, 0x12, 0xe0, 0x21, + 0x38, 0x81, 0x14, 0x10, 0x39, 0x1d, 0xcc, 0x67, 0xcc, 0x08, 0x85, 0x7c, 0x16, 0x46, 0xcb, 0xd4, + 0x0b, 0x36, 0x37, 0x57, 0xd1, 0xe8, 0x84, 0x67, 0xff, 0xc8, 0x63, 0xa6, 0x86, 0x20, 0x68, 0x7e, + 0xe7, 0xb8, 0x38, 0x19, 0x38, 0x2d, 0x1a, 0x46, 0x35, 0x37, 0x25, 0x36, 0x59, 0x82, 0x02, 0x7f, + 0x16, 0x8f, 0xee, 0x1e, 0x78, 0x32, 0xe6, 0xf9, 0x39, 0x2d, 0xde, 0xd0, 0x0f, 0xe9, 0x4e, 0x98, + 0xa7, 0x22, 0x81, 0x4f, 0x96, 0x65, 0x7a, 0x17, 0xb5, 0x99, 0x10, 0x29, 0xc3, 0xe2, 0x3b, 0x06, + 0x6b, 0x6d, 0x92, 0x82, 0x94, 0x60, 0xb2, 0xec, 0xb6, 0x3a, 0x76, 0xe0, 0x60, 0x1e, 0xcc, 0x23, + 0x71, 0x08, 0xa2, 0x42, 0xaf, 0xae, 0x16, 0x68, 0x27, 0xaa, 0x5a, 0x40, 0x6e, 0xc3, 0x94, 0xe9, + 0x76, 0xd9, 0x30, 0xc9, 0x5b, 0x38, 0x3f, 0xe7, 0xd0, 0x34, 0xc4, 0x63, 0x25, 0xec, 0x58, 0x16, + 0x57, 0x6e, 0x2d, 0x02, 0xac, 0x46, 0x45, 0xd6, 0x53, 0x9e, 0x43, 0xd4, 0xc3, 0x4d, 0xcd, 0x56, + 0x91, 0x60, 0x96, 0xf2, 0x92, 0x72, 0x13, 0xc6, 0x6b, 0xb5, 0xfb, 0x9b, 0xd4, 0x0f, 0x6e, 0x37, + 0xdd, 0x43, 0x3c, 0xdb, 0xf2, 0x22, 0xb9, 0x9a, 0xef, 0x5a, 0x01, 0xf5, 0x03, 0x6b, 0xb7, 0xe9, + 0x1e, 0x9a, 0x2a, 0x16, 0xf9, 0x2a, 0xeb, 0x0f, 0x45, 0x12, 0x14, 0xb1, 0x6e, 0xfb, 0x09, 0xab, + 0x78, 0x82, 0x44, 0x8b, 0x86, 0x89, 0xac, 0x7a, 0x67, 0x29, 0xe8, 0xe8, 0x53, 0xe6, 0xb9, 0x8f, + 0x8e, 0x4a, 0x8d, 0x86, 0x47, 0x7d, 0x5f, 0x1c, 0x42, 0xdc, 0xa7, 0x0c, 0x95, 0x0d, 0x36, 0x2f, + 0xd0, 0x7c, 0xca, 0x14, 0x02, 0xf2, 0xa3, 0x19, 0x38, 0xa7, 0x7a, 0x9b, 0xe0, 0x72, 0x41, 0x33, + 0x17, 0x7e, 0x24, 0xbd, 0x71, 0x4d, 0x1e, 0xc2, 0xd7, 0x14, 0xb4, 0x6b, 0x0f, 0x6f, 0x5c, 0x2b, + 0x45, 0x3f, 0x6b, 0x92, 0x08, 0xe3, 0xf6, 0x15, 0x53, 0xf9, 0x69, 0xb9, 0x89, 0xe6, 0xec, 0x14, + 0x62, 0x52, 0x66, 0x92, 0x1a, 0x9b, 0x51, 0x68, 0x38, 0x55, 0xdd, 0xc0, 0x33, 0x4d, 0x68, 0x54, + 0xc5, 0xfc, 0xe3, 0x26, 0x56, 0x4e, 0x47, 0x17, 0xc8, 0x14, 0x1a, 0x52, 0x85, 0x69, 0x0e, 0x60, + 0xdb, 0x02, 0x4f, 0xf3, 0x34, 0x1b, 0x25, 0x9a, 0x10, 0x6c, 0xf0, 0xad, 0x1f, 0x53, 0x3d, 0xa9, + 0xc1, 0x59, 0x63, 0x74, 0xe4, 0x7d, 0x98, 0xc2, 0x18, 0xfa, 0xd1, 0x7a, 0x9d, 0xc3, 0x55, 0x8c, + 0x31, 0x66, 0x45, 0x49, 0xcc, 0xf3, 0x6e, 0xc2, 0xf7, 0xf7, 0xa3, 0x15, 0xfd, 0x3e, 0x4c, 0xa1, + 0xad, 0x4e, 0xc4, 0xe0, 0x5c, 0xc4, 0x40, 0x94, 0xc4, 0x19, 0x04, 0x4d, 0x3f, 0x62, 0xf0, 0x33, + 0x19, 0xb8, 0xc8, 0x2a, 0x4a, 0x1f, 0xa1, 0xf3, 0x1f, 0x67, 0x84, 0x30, 0xea, 0x66, 0x4f, 0x9e, + 0xaa, 0x38, 0xea, 0xfb, 0xfb, 0x69, 0x1c, 0xf0, 0xa3, 0xd8, 0xc7, 0xa7, 0x7f, 0xd4, 0x85, 0x8f, + 0xfd, 0x51, 0x3d, 0x79, 0xaa, 0x1f, 0x15, 0x34, 0xfd, 0x34, 0x0e, 0x78, 0xad, 0xad, 0x95, 0xd6, + 0x56, 0xa3, 0xbb, 0xd9, 0x77, 0x97, 0xdb, 0x8a, 0xd6, 0xb6, 0x3e, 0x6e, 0x2b, 0x5b, 0xdc, 0x8b, + 0x5a, 0xe9, 0x06, 0x79, 0xad, 0xd5, 0xc0, 0xf1, 0x6b, 0x6d, 0x8c, 0xc6, 0x8c, 0x61, 0x1b, 0xbf, + 0x04, 0x31, 0xbe, 0xc2, 0x54, 0xd5, 0x80, 0x11, 0x7e, 0x6b, 0x15, 0x9d, 0x8c, 0x36, 0x0b, 0xfc, + 0x4e, 0x6b, 0x8a, 0x12, 0x72, 0x11, 0x72, 0xb5, 0xda, 0x7d, 0xd1, 0xc9, 0x68, 0xb0, 0xea, 0xfb, + 0xae, 0xc9, 0x60, 0x6c, 0x84, 0xd0, 0x0a, 0x55, 0xc9, 0x49, 0xc0, 0xce, 0x3b, 0x13, 0xa1, 0xac, + 0xbf, 0xe5, 0x1d, 0x72, 0x28, 0xea, 0x6f, 0x71, 0x87, 0x8c, 0x6e, 0x8e, 0x65, 0x98, 0x2f, 0xf9, + 0x3e, 0xf5, 0xd8, 0x84, 0x10, 0xc6, 0x8d, 0x9e, 0xb8, 0xe7, 0x88, 0x83, 0x1d, 0x2b, 0xb5, 0xeb, + 0xbe, 0xd9, 0x13, 0x91, 0x5c, 0x81, 0x7c, 0xa9, 0xdb, 0x70, 0x68, 0xbb, 0xae, 0x85, 0x65, 0xb3, + 0x05, 0xcc, 0x0c, 0x4b, 0xc9, 0x07, 0x70, 0x2e, 0x16, 0x81, 0x51, 0xf4, 0xc0, 0x68, 0xb4, 0xf7, + 0xca, 0x7b, 0x58, 0x64, 0x90, 0xc1, 0xbb, 0x24, 0x9d, 0x92, 0x94, 0xa0, 0xb0, 0x8c, 0x6e, 0x5a, + 0x15, 0xca, 0xdf, 0x86, 0x5c, 0x8f, 0xfb, 0xe7, 0xf1, 0x5b, 0xb3, 0x88, 0x33, 0xd9, 0x08, 0x0b, + 0xcd, 0x04, 0x3a, 0xb9, 0x07, 0xb3, 0x71, 0x18, 0x3b, 0xc1, 0xf9, 0x05, 0x19, 0xf7, 0x9b, 0x04, + 0x17, 0x3c, 0xc3, 0xd3, 0xa8, 0xc8, 0x0e, 0xcc, 0x44, 0x06, 0x49, 0xfa, 0xb5, 0x59, 0xda, 0x39, + 0x87, 0xe5, 0xf2, 0xea, 0xfc, 0xac, 0x98, 0x8c, 0xb3, 0x91, 0x71, 0x53, 0x78, 0x7d, 0x36, 0x93, + 0xec, 0x48, 0x03, 0xa6, 0x6a, 0xce, 0x5e, 0xdb, 0x69, 0xef, 0xdd, 0xa3, 0x47, 0x1b, 0xb6, 0xe3, + 0x09, 0x8b, 0x53, 0x69, 0x4f, 0x5e, 0xf2, 0x8f, 0x5a, 0x2d, 0x1a, 0x78, 0xb8, 0x11, 0xb2, 0x72, + 0xf4, 0x41, 0x67, 0xd7, 0xa1, 0x05, 0x9f, 0xd3, 0xa1, 0xdb, 0x66, 0xc7, 0x76, 0x34, 0x21, 0x40, + 0xe7, 0xa9, 0xa9, 0x2e, 0x26, 0x06, 0x54, 0x5d, 0x34, 0x61, 0x66, 0xb9, 0x5d, 0xf7, 0x8e, 0xf0, + 0x89, 0x4e, 0x7e, 0xdc, 0xe4, 0x29, 0x1f, 0xf7, 0xb2, 0xf8, 0xb8, 0xe7, 0x6c, 0x39, 0xc3, 0xd2, + 0x3e, 0x2f, 0xc9, 0x98, 0xd4, 0x60, 0x06, 0x2f, 0x0e, 0xd5, 0xca, 0x46, 0xb5, 0xed, 0x04, 0x8e, + 0x1d, 0xd0, 0x86, 0x10, 0x2e, 0xc2, 0x4c, 0x2e, 0xfc, 0x8a, 0xea, 0x34, 0x3a, 0x96, 0x23, 0x51, + 0x54, 0xa6, 0x09, 0xfa, 0x7e, 0xf7, 0xc4, 0xe9, 0xbf, 0xa0, 0x7b, 0x62, 0x15, 0xa6, 0xe3, 0xa1, + 0x1c, 0x0a, 0xd1, 0x39, 0xec, 0x63, 0x11, 0x3b, 0xce, 0xdd, 0x2e, 0x0a, 0x93, 0x5a, 0xf2, 0xd4, + 0x58, 0x10, 0x87, 0xd8, 0x95, 0x73, 0x46, 0xbb, 0x72, 0x6a, 0xbb, 0xd2, 0x19, 0xae, 0x9c, 0x64, + 0x03, 0xe0, 0xb6, 0xeb, 0xd5, 0x69, 0x09, 0xfd, 0xa3, 0x89, 0x96, 0xef, 0x8a, 0x31, 0x8d, 0x0a, + 0xf9, 0xfa, 0xd9, 0x65, 0xbf, 0xad, 0xb8, 0x9b, 0xbb, 0xc2, 0xc3, 0xf8, 0xb1, 0x2c, 0xcc, 0xf7, + 0xfa, 0x9c, 0x3e, 0xd7, 0xbd, 0x4f, 0x41, 0x72, 0x85, 0x8b, 0x6b, 0x5f, 0x81, 0xc6, 0xd7, 0xf9, + 0x22, 0xa4, 0x2f, 0x64, 0x71, 0x0d, 0x9c, 0x8d, 0x13, 0x6c, 0x79, 0x4d, 0x72, 0x0b, 0xc6, 0x95, + 0x8f, 0xc7, 0xbd, 0xb4, 0x57, 0x53, 0x4d, 0xd8, 0x0d, 0xff, 0x66, 0xd7, 0x44, 0xbe, 0x6f, 0xc9, + 0x6b, 0x22, 0xff, 0x45, 0x0a, 0xdc, 0x45, 0x7c, 0x84, 0x5b, 0x01, 0xf8, 0xbe, 0x4b, 0x08, 0xe0, + 0xbe, 0xcd, 0xb7, 0x40, 0x13, 0xff, 0x36, 0x7e, 0x63, 0x82, 0x9f, 0xc8, 0xea, 0x2d, 0xb1, 0x97, + 0x7d, 0x70, 0xec, 0xf6, 0x98, 0x3d, 0xcb, 0xed, 0x31, 0x77, 0xfa, 0xed, 0x71, 0xe8, 0xb4, 0xdb, + 0x63, 0xec, 0x7a, 0x37, 0x7c, 0xe6, 0xeb, 0xdd, 0xc8, 0x99, 0xae, 0x77, 0xa3, 0x67, 0xba, 0xde, + 0x69, 0x37, 0xd5, 0xfc, 0x69, 0x37, 0xd5, 0xbf, 0xba, 0x0c, 0x3e, 0xad, 0x97, 0xc1, 0x34, 0x11, + 0xef, 0x4c, 0x97, 0xc1, 0x1f, 0xe9, 0x79, 0x97, 0x2b, 0x7c, 0x1c, 0xa1, 0xfc, 0xa5, 0x01, 0xee, + 0x72, 0x83, 0xde, 0xe4, 0x66, 0x9e, 0xcc, 0x4d, 0x8e, 0x3c, 0xb1, 0x9b, 0xdc, 0xec, 0xe3, 0xde, + 0xe4, 0xe6, 0x9e, 0xe4, 0x4d, 0xee, 0xdc, 0x5f, 0xc6, 0x9b, 0xdc, 0xf9, 0x7f, 0x3b, 0x37, 0xb9, + 0xbf, 0x06, 0x85, 0xb8, 0x70, 0x79, 0x7a, 0xd4, 0xe3, 0x27, 0x16, 0x72, 0x92, 0x89, 0xbe, 0x71, + 0xe1, 0x8e, 0x5c, 0x07, 0xd8, 0xf0, 0x9c, 0x87, 0x76, 0x40, 0xef, 0x49, 0xeb, 0x37, 0x11, 0xb1, + 0x9b, 0x43, 0xd9, 0xc8, 0x9b, 0x0a, 0x4a, 0x78, 0xaf, 0xc9, 0xa6, 0xdd, 0x6b, 0x8c, 0x1f, 0xcd, + 0xc2, 0x0c, 0x8f, 0xdb, 0xf6, 0xf4, 0x3f, 0xc2, 0xbe, 0xa7, 0xdd, 0x56, 0x9f, 0x8b, 0x72, 0x04, + 0xa8, 0xad, 0xeb, 0xf3, 0x0c, 0xfb, 0x15, 0x38, 0x97, 0xe8, 0x0a, 0xbc, 0xb1, 0x56, 0x64, 0xc4, + 0xbc, 0xc4, 0x9d, 0x75, 0x3e, 0xbd, 0x92, 0x07, 0x37, 0xcd, 0x04, 0x85, 0xf1, 0x67, 0x43, 0x09, + 0xfe, 0xe2, 0x41, 0x56, 0x7d, 0x62, 0xcd, 0x9c, 0xed, 0x89, 0x35, 0x3b, 0xd8, 0x13, 0x6b, 0x4c, + 0xa8, 0xc8, 0x0d, 0x22, 0x54, 0x7c, 0x00, 0x93, 0x9b, 0xd4, 0x6e, 0xf9, 0x9b, 0xae, 0x48, 0x38, + 0xc5, 0x7d, 0x2d, 0x64, 0x40, 0x3c, 0x56, 0x26, 0x2f, 0x5c, 0xa1, 0xcd, 0x68, 0xc0, 0x08, 0xd8, + 0x31, 0xc8, 0x33, 0x50, 0x99, 0x3a, 0x07, 0xf5, 0x16, 0x3d, 0xdc, 0xe7, 0x16, 0x5d, 0x83, 0x09, + 0x41, 0x17, 0x85, 0x7a, 0x8e, 0xae, 0x7b, 0xac, 0x08, 0xe1, 0xb2, 0xf6, 0x30, 0x1b, 0x7e, 0x58, + 0x3b, 0xbf, 0xe9, 0x69, 0x4c, 0x58, 0x17, 0x2c, 0xb7, 0x1b, 0x1d, 0xd7, 0x69, 0x63, 0x17, 0x8c, + 0x46, 0x5d, 0x40, 0x05, 0x98, 0x77, 0x81, 0x82, 0x44, 0xde, 0x81, 0xa9, 0xd2, 0x46, 0x55, 0x25, + 0xcb, 0x47, 0xaf, 0xbc, 0x76, 0xc7, 0xb1, 0x34, 0xd2, 0x18, 0x6e, 0xbf, 0x9b, 0xcf, 0xd8, 0x5f, + 0xcc, 0xcd, 0xc7, 0xf8, 0xa7, 0x93, 0x72, 0x79, 0x7f, 0xb2, 0x0f, 0x24, 0xfa, 0x93, 0x47, 0xee, + 0x8c, 0x4f, 0x1e, 0x43, 0xa7, 0x09, 0x92, 0x9a, 0x7c, 0x3b, 0x7c, 0x26, 0xf9, 0x76, 0xe4, 0xb1, + 0x9f, 0x2f, 0x46, 0xcf, 0x28, 0xb1, 0xc6, 0xd6, 0x5a, 0x7e, 0x90, 0xb5, 0x96, 0x2a, 0xe5, 0x8e, + 0x3d, 0xbe, 0x94, 0x0b, 0x67, 0x96, 0x72, 0x6b, 0x91, 0xef, 0xf2, 0xf8, 0xa9, 0x2e, 0x21, 0xcf, + 0x0b, 0xad, 0xc0, 0x4c, 0x7a, 0x14, 0xbe, 0xd0, 0x8b, 0xf9, 0xbb, 0x4a, 0x74, 0xfe, 0x5a, 0xba, + 0xe8, 0xdc, 0xff, 0xbc, 0x39, 0x93, 0xf0, 0xfc, 0xa3, 0x4f, 0x56, 0x78, 0x7e, 0xb2, 0x0f, 0x21, + 0x7f, 0x25, 0x3e, 0xff, 0x95, 0xf8, 0x3c, 0x98, 0xf8, 0x4c, 0xee, 0x03, 0xb1, 0xbb, 0xc1, 0x3e, + 0x6d, 0x07, 0x4e, 0x1d, 0xa3, 0xd2, 0xb2, 0x21, 0xc6, 0x57, 0x19, 0xb1, 0x5e, 0x93, 0xa5, 0xea, + 0x7a, 0xd5, 0x4a, 0xd1, 0xcf, 0xdb, 0xc3, 0xf5, 0xba, 0x6d, 0x7b, 0x6d, 0xd4, 0x63, 0x5d, 0x87, + 0x51, 0x19, 0xd7, 0x34, 0x13, 0xa9, 0xa8, 0x93, 0x01, 0x4d, 0x25, 0x16, 0x59, 0x84, 0xbc, 0x24, + 0x56, 0x13, 0xed, 0x1c, 0x0a, 0x98, 0x16, 0x32, 0x52, 0xc0, 0x8c, 0xff, 0x68, 0x48, 0x9e, 0x09, + 0xec, 0x13, 0x36, 0x6c, 0xcf, 0x6e, 0x61, 0x0e, 0xbe, 0x70, 0xc9, 0x2a, 0xb7, 0x81, 0xd8, 0x2a, + 0x8f, 0xf9, 0x0a, 0xe8, 0x24, 0x1f, 0x2b, 0x30, 0x6d, 0x94, 0xe6, 0x38, 0x37, 0x40, 0x9a, 0xe3, + 0xb7, 0xb4, 0x1c, 0xc1, 0x43, 0x51, 0x52, 0x4a, 0xb6, 0x4f, 0xf6, 0xcf, 0x0e, 0x7c, 0x4b, 0x4d, + 0xe6, 0x3b, 0x1c, 0x85, 0x09, 0x43, 0xca, 0x3e, 0x69, 0x7c, 0xc3, 0xeb, 0xcd, 0xc8, 0x59, 0x42, + 0x3e, 0x8f, 0xfe, 0x5b, 0x0d, 0xf9, 0xbc, 0x0c, 0x20, 0xce, 0xee, 0xc8, 0xde, 0xe1, 0x15, 0xdc, + 0x4e, 0x84, 0xdd, 0x73, 0x10, 0x34, 0x7b, 0xe4, 0x04, 0x51, 0x08, 0x8d, 0xdf, 0x27, 0x30, 0x53, + 0xab, 0xdd, 0xaf, 0x38, 0xf6, 0x5e, 0xdb, 0xf5, 0x03, 0xa7, 0x5e, 0x6d, 0xef, 0xba, 0x4c, 0xb6, + 0x0f, 0xcf, 0x17, 0x25, 0x58, 0x6f, 0x74, 0xb6, 0x84, 0xc5, 0xec, 0xee, 0xb8, 0xec, 0x79, 0x52, + 0xe1, 0xca, 0xef, 0x8e, 0x94, 0x01, 0x4c, 0x0e, 0x67, 0xe2, 0x73, 0xad, 0x8b, 0xa1, 0x32, 0x84, + 0x11, 0x0a, 0x8a, 0xcf, 0x3e, 0x07, 0x99, 0xb2, 0x8c, 0xd0, 0xe4, 0x84, 0x15, 0xd7, 0xa9, 0x0b, + 0x5a, 0xe0, 0xe8, 0xa8, 0x98, 0xaf, 0x46, 0x21, 0xdd, 0xe0, 0x3e, 0xdc, 0x41, 0xb8, 0x6a, 0x62, + 0x97, 0x58, 0x03, 0x47, 0x70, 0x4e, 0x73, 0xa2, 0x1e, 0xf4, 0x75, 0xe6, 0x35, 0x21, 0xae, 0x1b, + 0x18, 0xb3, 0x23, 0xe5, 0x89, 0x46, 0x4d, 0xaa, 0x97, 0x5a, 0x03, 0x3b, 0x20, 0x9f, 0x4f, 0x2d, + 0x09, 0x57, 0xf7, 0xb8, 0x16, 0xbc, 0x5b, 0xd9, 0x34, 0x78, 0xfa, 0xc0, 0x5e, 0x55, 0x5b, 0x29, + 0x5b, 0x41, 0xff, 0x9a, 0xc8, 0x3f, 0xc9, 0xc0, 0x05, 0x0d, 0x23, 0xdc, 0xff, 0xfc, 0x30, 0xbe, + 0x48, 0xea, 0xbc, 0xfe, 0xe8, 0xc9, 0xcc, 0xeb, 0x97, 0xf4, 0xb6, 0x44, 0x3b, 0xb4, 0xda, 0x86, + 0x5e, 0x5f, 0x48, 0x1e, 0xc2, 0x0c, 0x16, 0xc9, 0x97, 0x22, 0x36, 0x67, 0xc5, 0x03, 0xd3, 0x5c, + 0xf4, 0xd9, 0x3c, 0x30, 0x00, 0xa6, 0x80, 0x5f, 0xfc, 0xf6, 0x71, 0x71, 0x52, 0x43, 0x97, 0xe1, + 0xb0, 0xad, 0xe8, 0xb9, 0xc9, 0x69, 0xef, 0xba, 0x5a, 0x7e, 0xff, 0x78, 0x15, 0xe4, 0xbf, 0xce, + 0xf0, 0xf7, 0x09, 0xde, 0x8c, 0xdb, 0x9e, 0xdb, 0x0a, 0xcb, 0xa5, 0xad, 0x66, 0x8f, 0x6e, 0x6b, + 0x3e, 0x99, 0x6e, 0x7b, 0x05, 0x3f, 0x99, 0xef, 0x09, 0xd6, 0xae, 0xe7, 0xb6, 0xa2, 0xcf, 0x57, + 0x3b, 0xae, 0xe7, 0x47, 0x92, 0x1f, 0xc8, 0xc0, 0x45, 0x4d, 0x4d, 0xaa, 0xe6, 0x26, 0x11, 0xe1, + 0x17, 0x66, 0xc3, 0xc0, 0x2c, 0x51, 0xd1, 0xd2, 0x35, 0x31, 0xff, 0x2f, 0xe3, 0x17, 0x28, 0x71, + 0x40, 0x19, 0x92, 0xd5, 0xe2, 0x58, 0xca, 0x27, 0xf4, 0xae, 0x85, 0x38, 0x30, 0x83, 0x66, 0x3b, + 0x9a, 0x4d, 0xf1, 0x5c, 0x6f, 0x9b, 0xe2, 0x30, 0xeb, 0x10, 0x66, 0x24, 0xe8, 0x6d, 0x58, 0x9c, + 0xe4, 0x4a, 0xbe, 0x0f, 0x2e, 0x26, 0x80, 0xe1, 0x6a, 0x3b, 0xd7, 0x73, 0xb5, 0x7d, 0xea, 0xe4, + 0xb8, 0xf8, 0x6a, 0x5a, 0x6d, 0x69, 0x2b, 0xad, 0x77, 0x0d, 0xc4, 0x06, 0x88, 0x0a, 0x85, 0x3c, + 0x93, 0x3e, 0x41, 0x3f, 0x25, 0xe6, 0x87, 0x82, 0xcf, 0xf6, 0x72, 0xe5, 0x1b, 0xd4, 0x23, 0x2f, + 0x42, 0x22, 0x14, 0x26, 0x94, 0x6c, 0x0c, 0x47, 0xc2, 0x7a, 0xa4, 0x47, 0x25, 0xdf, 0x3e, 0x2e, + 0x6a, 0xd8, 0xec, 0x86, 0xa5, 0xa6, 0x79, 0xd0, 0xc4, 0x47, 0x15, 0x91, 0xfc, 0x7a, 0x06, 0xe6, + 0x18, 0x20, 0x9a, 0x54, 0xa2, 0x51, 0xf3, 0xfd, 0x66, 0xfd, 0xfe, 0x93, 0x99, 0xf5, 0x2f, 0xe2, + 0x37, 0xaa, 0xb3, 0x3e, 0xd1, 0x25, 0xa9, 0x1f, 0x87, 0xb3, 0x5d, 0xb3, 0x10, 0xd3, 0x66, 0xfb, + 0xc5, 0x01, 0x66, 0x3b, 0x1f, 0x80, 0xd3, 0x67, 0x7b, 0xcf, 0x5a, 0xc8, 0x26, 0x4c, 0x88, 0xcb, + 0x15, 0xef, 0xb0, 0x17, 0xb4, 0xb8, 0xd0, 0x6a, 0x11, 0xbf, 0xf1, 0x8a, 0x64, 0x15, 0x89, 0x16, + 0x6a, 0x5c, 0x48, 0x1b, 0x66, 0xf9, 0x6f, 0x5d, 0xd9, 0x55, 0xec, 0xa9, 0xec, 0xba, 0x22, 0x5a, + 0x74, 0x49, 0xf0, 0x8f, 0xe9, 0xbc, 0xd4, 0x78, 0x4e, 0x29, 0x8c, 0x49, 0x07, 0x88, 0x06, 0xe6, + 0x8b, 0xf6, 0x52, 0x7f, 0x15, 0xd7, 0xab, 0xa2, 0xce, 0x62, 0xbc, 0xce, 0xf8, 0xca, 0x4d, 0xe1, + 0x4d, 0x6c, 0x98, 0x16, 0x50, 0xf7, 0x80, 0xf2, 0x1d, 0xfe, 0x45, 0x2d, 0xa2, 0x56, 0xac, 0x94, + 0xdf, 0xca, 0x64, 0x4d, 0x18, 0xf1, 0x2c, 0xb6, 0xa1, 0xc7, 0xf9, 0x91, 0xfb, 0x30, 0x53, 0xea, + 0x74, 0x9a, 0x0e, 0x6d, 0x60, 0x2b, 0xcd, 0x2e, 0x6b, 0x93, 0x11, 0xe5, 0x7b, 0xb3, 0x79, 0xa1, + 0xb8, 0x2a, 0x7a, 0xdd, 0xd8, 0x76, 0x93, 0xa0, 0x35, 0x7e, 0x24, 0x93, 0xf8, 0x68, 0xf2, 0x3a, + 0x8c, 0xe1, 0x0f, 0x25, 0x48, 0x0b, 0xea, 0x8c, 0xf8, 0x27, 0xa2, 0x36, 0x2a, 0x42, 0x60, 0xc2, + 0x92, 0x1a, 0xa8, 0x31, 0xc7, 0x85, 0x25, 0xa1, 0xa8, 0x88, 0x54, 0x13, 0x45, 0xe9, 0xeb, 0x91, + 0x8b, 0x84, 0x2e, 0xf4, 0xf5, 0x10, 0x1e, 0x1e, 0xc6, 0x3f, 0xca, 0xea, 0xd3, 0x8e, 0x5c, 0x51, + 0xe4, 0x76, 0x25, 0x54, 0xa4, 0x94, 0xdb, 0x15, 0x69, 0xfd, 0xef, 0x67, 0x60, 0xf6, 0xbe, 0x92, + 0x28, 0x74, 0xd3, 0xc5, 0x71, 0xe9, 0x9f, 0x3a, 0xf3, 0x49, 0xa5, 0x00, 0x54, 0x33, 0x94, 0xb2, + 0x99, 0x82, 0x53, 0xc6, 0x4c, 0xfb, 0x1e, 0xf4, 0x9e, 0xc3, 0x0f, 0x53, 0x32, 0x31, 0x72, 0x74, + 0x0e, 0x3f, 0x63, 0xea, 0x0a, 0xe3, 0x27, 0xb2, 0x30, 0xae, 0xac, 0x18, 0xf2, 0x19, 0x98, 0x50, + 0xab, 0x55, 0x15, 0x8e, 0xea, 0x57, 0x9a, 0x1a, 0x16, 0x6a, 0x1c, 0xa9, 0xdd, 0xd2, 0x34, 0x8e, + 0x6c, 0x5d, 0x20, 0xf4, 0x8c, 0x37, 0xa1, 0xf7, 0x53, 0x6e, 0x42, 0x38, 0xcb, 0x15, 0x8d, 0x51, + 0xdf, 0xfb, 0xd0, 0x3b, 0xc9, 0xfb, 0x10, 0x2a, 0xaf, 0x14, 0xfa, 0xde, 0xb7, 0x22, 0xe3, 0xa7, + 0x33, 0x50, 0x88, 0xaf, 0xe9, 0x4f, 0xa4, 0x57, 0xce, 0xf0, 0xba, 0xf4, 0xe3, 0xd9, 0x30, 0x73, + 0x8b, 0x74, 0x21, 0x7e, 0x5a, 0xcd, 0x14, 0xdf, 0xd5, 0x1e, 0x7e, 0x9e, 0xd5, 0xa3, 0xe1, 0xa9, + 0xc1, 0x37, 0xd2, 0x43, 0x60, 0x0e, 0x7d, 0xeb, 0xef, 0x16, 0x9f, 0x31, 0x3e, 0x84, 0xb9, 0x78, + 0x77, 0xe0, 0xe3, 0x4f, 0x09, 0xa6, 0x75, 0x78, 0x3c, 0xef, 0x53, 0x9c, 0xca, 0x8c, 0xe3, 0x1b, + 0x7f, 0x98, 0x8d, 0xf3, 0x16, 0x26, 0x8b, 0x6c, 0x8f, 0x52, 0x0d, 0x71, 0xc4, 0x1e, 0xc5, 0x41, + 0xa6, 0x2c, 0x3b, 0x4b, 0xbe, 0xb5, 0xd0, 0x11, 0x36, 0x97, 0xee, 0x08, 0x4b, 0x6e, 0xc5, 0xac, + 0xb4, 0x95, 0xa8, 0x4d, 0x87, 0x74, 0xc7, 0x8a, 0x2c, 0xb5, 0x63, 0xc6, 0xd9, 0x65, 0x98, 0xd3, + 0x42, 0x90, 0x4b, 0xfa, 0xe1, 0x48, 0xd7, 0x1f, 0x60, 0x01, 0x27, 0x4e, 0x45, 0x26, 0x2b, 0x30, + 0xca, 0x3e, 0x73, 0xcd, 0xee, 0x88, 0x37, 0x1d, 0x12, 0xba, 0xc5, 0x37, 0xc3, 0xfb, 0xa1, 0xe2, + 0x19, 0xdf, 0xa4, 0x4c, 0x42, 0x50, 0x27, 0x96, 0x40, 0x34, 0xfe, 0x55, 0x86, 0xad, 0xff, 0xfa, + 0xc1, 0x77, 0x59, 0xd2, 0x36, 0xd6, 0xa4, 0x3e, 0x16, 0xb5, 0x7f, 0x9c, 0xe5, 0xb9, 0x78, 0xc4, + 0xf4, 0x79, 0x0b, 0x46, 0x36, 0x6d, 0x6f, 0x4f, 0xa4, 0xcc, 0xd6, 0xb9, 0xf0, 0x82, 0x28, 0xa6, + 0x54, 0x80, 0xbf, 0x4d, 0x41, 0xa0, 0xaa, 0xce, 0xb2, 0x03, 0xa9, 0xce, 0x94, 0x77, 0x81, 0xdc, + 0x13, 0x7b, 0x17, 0xf8, 0x9e, 0x30, 0xed, 0x4e, 0x29, 0x18, 0x20, 0xc2, 0xf5, 0xa5, 0x78, 0x96, + 0xab, 0x44, 0x2c, 0xf2, 0x88, 0x1d, 0xb9, 0xa5, 0xe6, 0xcd, 0x52, 0x7c, 0x4b, 0x4f, 0xc9, 0x90, + 0x65, 0xfc, 0x71, 0x8e, 0xf7, 0xb1, 0xe8, 0xa8, 0xcb, 0x9a, 0xdf, 0x39, 0xae, 0x93, 0x98, 0x9e, + 0x92, 0x7b, 0xa0, 0x5f, 0x86, 0x21, 0x36, 0x37, 0x45, 0x6f, 0x22, 0x1e, 0x9b, 0xbf, 0x2a, 0x1e, + 0x2b, 0x67, 0x6b, 0x19, 0xcf, 0x24, 0x35, 0x21, 0x22, 0x1e, 0x5b, 0xea, 0x5a, 0x46, 0x0c, 0x72, + 0x05, 0x86, 0xd6, 0xdd, 0x86, 0x8c, 0xa4, 0x3e, 0x87, 0xd1, 0x47, 0xb4, 0x8c, 0xab, 0xf3, 0x19, + 0x13, 0x31, 0x58, 0x5b, 0xc3, 0xfc, 0x13, 0x6a, 0x5b, 0x5b, 0xbb, 0x76, 0x32, 0xd1, 0x9d, 0x92, + 0xf4, 0x66, 0x19, 0xa6, 0xb6, 0x9d, 0x76, 0xc3, 0x3d, 0xf4, 0x2b, 0xd4, 0x3f, 0x08, 0xdc, 0x8e, + 0xb0, 0x37, 0x46, 0xed, 0xfe, 0x21, 0x2f, 0xb1, 0x1a, 0xbc, 0x48, 0x7d, 0x96, 0xd1, 0x89, 0xc8, + 0x12, 0x4c, 0x6a, 0x11, 0x5c, 0xc5, 0xe3, 0x2a, 0x6a, 0x43, 0xf5, 0xf8, 0xaf, 0xaa, 0x36, 0x54, + 0x23, 0x61, 0xe7, 0xb9, 0xf8, 0x7e, 0xe5, 0x89, 0x35, 0xf1, 0xed, 0x02, 0x87, 0xdc, 0x84, 0x3c, + 0x0f, 0xf3, 0x51, 0xad, 0xa8, 0xcf, 0x64, 0x3e, 0xc2, 0x62, 0x61, 0x72, 0x24, 0xa2, 0x12, 0xd6, + 0xe1, 0xd3, 0x50, 0x10, 0x5b, 0x52, 0x94, 0xab, 0xfd, 0x39, 0x18, 0x2a, 0x57, 0x2b, 0xa6, 0xba, + 0x8d, 0xd4, 0x9d, 0x86, 0x67, 0x22, 0x14, 0xbd, 0xfa, 0xd6, 0x69, 0x70, 0xe8, 0x7a, 0x07, 0x26, + 0xf5, 0x03, 0xcf, 0xe1, 0xf9, 0x34, 0x71, 0x21, 0x7e, 0x86, 0xbc, 0x03, 0xc3, 0x68, 0xf8, 0x1a, + 0x3b, 0x19, 0xe2, 0x75, 0x2c, 0x4d, 0x8a, 0x09, 0x3c, 0x8c, 0x56, 0xb4, 0x26, 0x27, 0x22, 0x6f, + 0xc1, 0x50, 0x85, 0xb6, 0x8f, 0x62, 0xa9, 0xfe, 0x12, 0xc4, 0xe1, 0x86, 0xd0, 0xa0, 0xed, 0x23, + 0x13, 0x49, 0x8c, 0x9f, 0xce, 0xc2, 0xb9, 0x94, 0xcf, 0x7a, 0xf0, 0x99, 0xa7, 0x74, 0x57, 0x5c, + 0xd2, 0x76, 0x45, 0xf9, 0x3e, 0xde, 0xb3, 0xe3, 0x53, 0x37, 0xc9, 0x5f, 0xc8, 0xc0, 0x05, 0x7d, + 0x82, 0x0a, 0x4b, 0xf7, 0x07, 0x37, 0xc9, 0xdb, 0x30, 0xb2, 0x42, 0xed, 0x06, 0x95, 0x79, 0xbd, + 0xce, 0x85, 0x01, 0xf9, 0x78, 0x0c, 0x03, 0x5e, 0xc8, 0xd9, 0x46, 0x1e, 0xaf, 0x1c, 0x4a, 0x2a, + 0xe2, 0xe3, 0xb8, 0xf8, 0x6e, 0xc8, 0x78, 0x22, 0x69, 0x55, 0xf5, 0xb1, 0x32, 0xf9, 0x76, 0x06, + 0x9e, 0xed, 0x43, 0xc3, 0x06, 0x8e, 0x0d, 0xbd, 0x3a, 0x70, 0x78, 0xa2, 0x22, 0x94, 0xbc, 0x07, + 0xd3, 0x9b, 0x42, 0xfc, 0x97, 0xc3, 0x91, 0x8d, 0xd6, 0x8b, 0xbc, 0x19, 0x58, 0x72, 0x5c, 0xe2, + 0xc8, 0x5a, 0xa0, 0x9b, 0x5c, 0xdf, 0x40, 0x37, 0x6a, 0xdc, 0x98, 0xa1, 0x41, 0xe3, 0xc6, 0x7c, + 0x08, 0x73, 0x7a, 0xdb, 0x44, 0xf8, 0xde, 0x28, 0x6a, 0x4e, 0xa6, 0x77, 0xd4, 0x9c, 0xbe, 0x41, + 0x42, 0x8d, 0x9f, 0xc8, 0x40, 0x41, 0xe7, 0xfd, 0xb8, 0xe3, 0xf9, 0xae, 0x36, 0x9e, 0xcf, 0xa6, + 0x8f, 0x67, 0xef, 0x81, 0xfc, 0xbf, 0x32, 0xf1, 0xc6, 0x0e, 0x34, 0x82, 0x06, 0x8c, 0x54, 0xdc, + 0x96, 0xed, 0xb4, 0xd5, 0xd4, 0xff, 0x0d, 0x84, 0x98, 0xa2, 0x64, 0xb0, 0x20, 0x43, 0x97, 0x60, + 0x78, 0xdd, 0x6d, 0x97, 0x2a, 0xc2, 0xa4, 0x18, 0xf9, 0xb4, 0xdd, 0xb6, 0x65, 0x37, 0x4c, 0x5e, + 0x40, 0x56, 0x01, 0x6a, 0x75, 0x8f, 0xd2, 0x76, 0xcd, 0xf9, 0x5e, 0x1a, 0x93, 0x34, 0x58, 0x0f, + 0x35, 0xbb, 0xb8, 0xb1, 0xf0, 0xa7, 0x53, 0x44, 0xb4, 0x7c, 0xe7, 0x7b, 0xd5, 0xfd, 0x56, 0xa1, + 0xc7, 0x75, 0x25, 0xe2, 0xb0, 0xc5, 0xc6, 0xe1, 0xc6, 0x27, 0xb1, 0xae, 0x52, 0xab, 0xc2, 0x1e, + 0xbe, 0x91, 0x3a, 0x1c, 0x7f, 0x90, 0x81, 0x67, 0xfb, 0xd0, 0x3c, 0x81, 0x51, 0xf9, 0x8b, 0xee, + 0x70, 0x0a, 0x10, 0x11, 0x61, 0x26, 0x65, 0xa7, 0x11, 0xf0, 0x5c, 0x7d, 0x93, 0x22, 0x93, 0x32, + 0x03, 0x68, 0x99, 0x94, 0x19, 0x80, 0x9d, 0xa5, 0x2b, 0xd4, 0xd9, 0xdb, 0xe7, 0xe6, 0x61, 0x93, + 0x7c, 0x6f, 0xd8, 0x47, 0x88, 0x7a, 0x96, 0x72, 0x1c, 0xe3, 0x5f, 0x0f, 0xc3, 0x45, 0x93, 0xee, + 0x39, 0xec, 0x5e, 0xb2, 0xe5, 0x3b, 0xed, 0x3d, 0x2d, 0xee, 0x8e, 0x11, 0x5b, 0xb9, 0x22, 0x49, + 0x05, 0x83, 0x84, 0x33, 0xf1, 0x2a, 0xe4, 0x99, 0x18, 0xa2, 0x2c, 0x5e, 0x7c, 0xe3, 0x62, 0xc2, + 0x8a, 0x08, 0xec, 0x2c, 0x8b, 0xc9, 0x6b, 0x42, 0x4c, 0x52, 0xd2, 0x08, 0x31, 0x31, 0xe9, 0x3b, + 0xc7, 0x45, 0xa8, 0x1d, 0xf9, 0x01, 0xc5, 0x2b, 0xb2, 0x10, 0x95, 0xc2, 0xbb, 0xcc, 0x50, 0x8f, + 0xbb, 0xcc, 0x1a, 0xcc, 0x95, 0x1a, 0xfc, 0x74, 0xb4, 0x9b, 0x1b, 0x9e, 0xd3, 0xae, 0x3b, 0x1d, + 0xbb, 0x29, 0xef, 0xe7, 0xd8, 0xcb, 0x76, 0x58, 0x6e, 0x75, 0x42, 0x04, 0x33, 0x95, 0x8c, 0x35, + 0xa3, 0xb2, 0x5e, 0xc3, 0xf0, 0x34, 0xe2, 0xf9, 0x12, 0x9b, 0xd1, 0x68, 0xfb, 0xd8, 0x0a, 0xdf, + 0x0c, 0x8b, 0xf1, 0x16, 0x85, 0x06, 0x01, 0x9b, 0xab, 0xb5, 0xc8, 0xa5, 0x9a, 0x67, 0x39, 0xe0, + 0x86, 0x05, 0x41, 0xd3, 0x47, 0x53, 0x4c, 0x0d, 0x2f, 0xa2, 0xab, 0xd5, 0x56, 0x18, 0x5d, 0x3e, + 0x41, 0xe7, 0xfb, 0xfb, 0x2a, 0x1d, 0xc7, 0x23, 0xd7, 0xd9, 0x54, 0x68, 0xb9, 0x01, 0xc5, 0x29, + 0x3c, 0x16, 0xdd, 0xb9, 0x3c, 0x84, 0xf2, 0x3b, 0x97, 0x82, 0x42, 0xde, 0x81, 0xd9, 0xe5, 0xf2, + 0xa2, 0x54, 0x3a, 0x57, 0xdc, 0x7a, 0x17, 0x0d, 0x01, 0x00, 0xeb, 0xc3, 0x31, 0xa4, 0xf5, 0x45, + 0xb6, 0x9b, 0xa4, 0xa1, 0x91, 0xcb, 0x30, 0x5a, 0xad, 0xf0, 0xbe, 0x1f, 0x57, 0x53, 0x79, 0x09, + 0xcb, 0x2c, 0x59, 0x48, 0xee, 0x47, 0x97, 0x82, 0x89, 0x53, 0xa5, 0xf7, 0x8b, 0x03, 0x5c, 0x08, + 0xde, 0x82, 0xc9, 0x25, 0x37, 0xa8, 0xb6, 0xfd, 0xc0, 0x6e, 0xd7, 0x69, 0xb5, 0xa2, 0xc6, 0xd5, + 0xde, 0x71, 0x03, 0xcb, 0x11, 0x25, 0xec, 0xcb, 0x75, 0x4c, 0xf2, 0x39, 0x24, 0xbd, 0x43, 0xdb, + 0xd4, 0x8b, 0xe2, 0x69, 0x0f, 0xf3, 0xbe, 0x65, 0xa4, 0x7b, 0x61, 0x89, 0xa9, 0x23, 0x8a, 0x34, + 0x63, 0x3c, 0x39, 0x68, 0xd9, 0x6d, 0x50, 0x9f, 0xef, 0x16, 0xdf, 0x45, 0x69, 0xc6, 0x94, 0xb6, + 0xf5, 0xd9, 0x41, 0xff, 0x7d, 0x4c, 0x33, 0x96, 0xc0, 0x25, 0x9f, 0x83, 0x61, 0xfc, 0x29, 0xa4, + 0xdb, 0xd9, 0x14, 0xb6, 0x91, 0x64, 0x5b, 0x67, 0x98, 0x26, 0x27, 0x20, 0x55, 0x18, 0x15, 0x17, + 0xab, 0xb3, 0x24, 0xcb, 0x11, 0x37, 0x34, 0x3e, 0x33, 0x04, 0xbd, 0xd1, 0x80, 0x09, 0xb5, 0x42, + 0xb6, 0x22, 0x56, 0x6c, 0x7f, 0x9f, 0x36, 0xd8, 0x2f, 0x91, 0xe7, 0x0e, 0x57, 0xc4, 0x3e, 0x42, + 0x2d, 0xf6, 0x1d, 0xa6, 0x82, 0xc2, 0xce, 0xd4, 0xaa, 0xbf, 0xe5, 0x8b, 0x4f, 0x11, 0xaa, 0x16, + 0x07, 0xd5, 0x76, 0x0d, 0x53, 0x14, 0x19, 0xdf, 0x03, 0x73, 0xeb, 0xdd, 0x66, 0xd3, 0xde, 0x69, + 0x52, 0x99, 0x07, 0x05, 0x13, 0x8e, 0x2f, 0xc1, 0x70, 0x4d, 0x49, 0x61, 0x1e, 0xe6, 0xa2, 0x54, + 0x70, 0xd0, 0x08, 0x36, 0x83, 0xa1, 0x82, 0x62, 0xc9, 0xcb, 0x39, 0xa9, 0xf1, 0x7b, 0x19, 0x98, + 0x93, 0xe6, 0x02, 0x9e, 0x5d, 0x3f, 0x08, 0xf3, 0xd8, 0x5f, 0xd6, 0xe6, 0x1a, 0x4e, 0xd8, 0xd8, + 0x34, 0xe2, 0xb3, 0xee, 0xae, 0xfc, 0x08, 0x5d, 0x60, 0x49, 0xfb, 0xe0, 0xd3, 0x3e, 0x86, 0xbc, + 0x03, 0xe3, 0xe2, 0x78, 0x54, 0x02, 0x5c, 0x62, 0x14, 0x31, 0x71, 0xdd, 0x8b, 0x1b, 0xaf, 0xa8, + 0xe8, 0x28, 0x8b, 0xe9, 0x4d, 0x79, 0x5c, 0x19, 0x20, 0x5d, 0x16, 0xd3, 0xeb, 0xe8, 0x33, 0x75, + 0x7f, 0x6b, 0x3c, 0xde, 0xb7, 0x62, 0xee, 0xde, 0x52, 0x43, 0xda, 0x65, 0xa2, 0x9b, 0x71, 0x14, + 0xd2, 0x4e, 0xbd, 0x19, 0x87, 0xa8, 0xe1, 0x98, 0x64, 0x4f, 0x19, 0x93, 0xf7, 0xe4, 0x98, 0xe4, + 0x7a, 0x4f, 0x8c, 0xd9, 0x3e, 0xe3, 0x50, 0x8b, 0x56, 0xc8, 0xd0, 0x40, 0x6a, 0x95, 0x67, 0x30, + 0x76, 0x3f, 0x27, 0x89, 0xef, 0xa2, 0x82, 0x93, 0xaa, 0xab, 0x19, 0x1e, 0x9c, 0xe9, 0x29, 0x5b, + 0xf3, 0xe7, 0x61, 0xa2, 0x14, 0x04, 0x76, 0x7d, 0x9f, 0x36, 0x2a, 0x6c, 0x7b, 0x52, 0xa2, 0x6f, + 0xd9, 0x02, 0xae, 0xbe, 0xb1, 0xa9, 0xb8, 0x3c, 0x9a, 0xac, 0xed, 0x0b, 0x63, 0xda, 0x30, 0x9a, + 0x2c, 0x83, 0xe8, 0xd1, 0x64, 0x19, 0x84, 0x5c, 0x87, 0xd1, 0x6a, 0xfb, 0xa1, 0xc3, 0xfa, 0x84, + 0x07, 0xe0, 0x42, 0xdd, 0x94, 0xc3, 0x41, 0xea, 0xe6, 0x2a, 0xb0, 0xc8, 0x5b, 0xca, 0xa5, 0x66, + 0x2c, 0x52, 0x60, 0x70, 0x95, 0x57, 0x18, 0x61, 0x47, 0xbd, 0xb0, 0x84, 0xb7, 0x9c, 0x5b, 0x30, + 0x2a, 0x35, 0x99, 0x10, 0x29, 0x2d, 0x04, 0x65, 0x32, 0x60, 0x85, 0x44, 0xc6, 0x9c, 0xe4, 0x4a, + 0xbe, 0xbe, 0x71, 0x25, 0x27, 0xb9, 0x92, 0xaf, 0x4f, 0xcb, 0x49, 0xae, 0x64, 0xee, 0x0b, 0x95, + 0x40, 0x13, 0xa7, 0x2a, 0x81, 0x1e, 0xc0, 0xc4, 0x86, 0xed, 0x05, 0x0e, 0x93, 0x51, 0xda, 0x81, + 0x3f, 0x3f, 0xa9, 0xe9, 0x4d, 0x95, 0xa2, 0xa5, 0x17, 0x64, 0x5e, 0xec, 0x8e, 0x82, 0xaf, 0x27, + 0x70, 0x8e, 0xe0, 0xe9, 0xa6, 0xb4, 0x53, 0x8f, 0x63, 0x4a, 0x8b, 0x9d, 0x8a, 0xba, 0xb2, 0xe9, + 0x48, 0x23, 0x83, 0x97, 0x96, 0x98, 0xc2, 0x2c, 0x44, 0x24, 0x5f, 0x86, 0x09, 0xf6, 0xf7, 0x86, + 0xdb, 0x74, 0xea, 0x0e, 0xf5, 0xe7, 0x0b, 0xd8, 0xb8, 0x17, 0x52, 0x57, 0x3f, 0x22, 0x1d, 0xd5, + 0x68, 0xc0, 0x17, 0x30, 0x32, 0x8e, 0x2b, 0xc1, 0x35, 0x6e, 0xe4, 0x7d, 0x98, 0x60, 0xb3, 0x6f, + 0xc7, 0xf6, 0xb9, 0x68, 0x3a, 0x13, 0x19, 0x43, 0x37, 0x04, 0x3c, 0x11, 0xd0, 0x59, 0x25, 0x60, + 0xc7, 0x7c, 0xa9, 0xc3, 0x37, 0x48, 0xa2, 0xcc, 0xf6, 0x4e, 0x62, 0x73, 0x94, 0x68, 0xe4, 0x0b, + 0x30, 0x51, 0xea, 0x74, 0xa2, 0x1d, 0x67, 0x56, 0x51, 0x84, 0x75, 0x3a, 0x56, 0xea, 0xae, 0xa3, + 0x51, 0xc4, 0x37, 0xe6, 0xb9, 0x33, 0x6d, 0xcc, 0xe4, 0x8d, 0x50, 0x5a, 0x3f, 0x17, 0x69, 0x75, + 0xc5, 0xc5, 0x51, 0x13, 0xfd, 0xb9, 0xe0, 0x5e, 0x86, 0x49, 0xae, 0xe6, 0x94, 0xd2, 0xcc, 0xf9, + 0xc4, 0xea, 0x49, 0x11, 0x6a, 0x74, 0x1a, 0xb2, 0x0c, 0x53, 0xdc, 0xdb, 0xbb, 0x29, 0x22, 0x6d, + 0xcf, 0x5f, 0xc0, 0x55, 0x8b, 0x5c, 0xb8, 0x93, 0x78, 0x13, 0x13, 0xb0, 0xd8, 0x1a, 0x97, 0x18, + 0x91, 0xf1, 0x27, 0x19, 0xb8, 0xd0, 0x63, 0xc4, 0xc3, 0x38, 0xcc, 0x99, 0xfe, 0x71, 0x98, 0xd9, + 0xce, 0xa1, 0x6b, 0x45, 0xb0, 0xfd, 0x42, 0xca, 0x52, 0xc7, 0x4b, 0xca, 0x5b, 0x2e, 0x10, 0x91, + 0xe3, 0x48, 0x54, 0x7d, 0xd7, 0x45, 0xd5, 0x6c, 0x2e, 0x79, 0x08, 0x09, 0x3c, 0xfe, 0x51, 0x4b, + 0xc6, 0xc9, 0x71, 0xf1, 0x05, 0x91, 0x42, 0x29, 0x1c, 0xd6, 0x8f, 0x5c, 0x6d, 0x05, 0xa7, 0xb0, + 0x36, 0x8e, 0x33, 0x30, 0xae, 0xac, 0x43, 0x72, 0x49, 0xf1, 0x42, 0x2e, 0xf0, 0x24, 0x5c, 0x0a, + 0x87, 0x2c, 0x3f, 0x89, 0x70, 0x51, 0x65, 0x4f, 0x57, 0x40, 0xaf, 0x31, 0x51, 0x48, 0x89, 0x55, + 0xdd, 0xd2, 0xb4, 0xc5, 0x26, 0x96, 0x63, 0x3a, 0x7f, 0xdb, 0x0f, 0x4a, 0xf5, 0xc0, 0x79, 0x48, + 0x07, 0x38, 0x74, 0xa2, 0x74, 0xfe, 0xb6, 0x1f, 0x58, 0x36, 0x92, 0x25, 0xd2, 0xf9, 0x87, 0x0c, + 0x8d, 0x1f, 0xcc, 0x00, 0x6c, 0x55, 0xcb, 0x18, 0x6c, 0xfe, 0x71, 0x85, 0x82, 0xf4, 0x00, 0xbe, + 0x92, 0x7b, 0x1f, 0x71, 0xe0, 0x7f, 0xca, 0xc0, 0x94, 0x8e, 0x46, 0xde, 0x83, 0xe9, 0x5a, 0xdd, + 0x73, 0x9b, 0xcd, 0x1d, 0xbb, 0x7e, 0xb0, 0xea, 0xb4, 0x29, 0x0f, 0x9d, 0x3a, 0xcc, 0xcf, 0x22, + 0x3f, 0x2c, 0xb2, 0x9a, 0xac, 0xcc, 0x8c, 0x23, 0x93, 0x1f, 0xca, 0xc0, 0x64, 0x6d, 0xdf, 0x3d, + 0x0c, 0xa3, 0x9d, 0x8a, 0x01, 0xf9, 0x0a, 0x5b, 0xdb, 0xfe, 0xbe, 0x7b, 0x18, 0x65, 0xf0, 0xd4, + 0x6c, 0x45, 0xdf, 0x1d, 0xec, 0x19, 0xbf, 0xee, 0xe2, 0x4d, 0x26, 0xf0, 0xaf, 0x69, 0x95, 0x98, + 0x7a, 0x9d, 0xc6, 0x9f, 0x67, 0x60, 0x1c, 0xef, 0x3c, 0xcd, 0x26, 0xca, 0x5c, 0xdf, 0x4d, 0xe9, + 0x20, 0xc3, 0x76, 0xf5, 0x19, 0xd8, 0x37, 0x61, 0x3a, 0x86, 0x46, 0x0c, 0x18, 0xa9, 0x61, 0x80, + 0x01, 0x55, 0x41, 0xc1, 0x43, 0x0e, 0x98, 0xa2, 0xc4, 0x58, 0x56, 0xc8, 0x1e, 0xdc, 0xc0, 0x67, + 0xdd, 0x45, 0x00, 0x47, 0x82, 0xe4, 0xcd, 0x86, 0xc4, 0xbf, 0xe4, 0xc1, 0x0d, 0x53, 0xc1, 0x32, + 0xd6, 0x61, 0xa4, 0xe6, 0x7a, 0xc1, 0xd2, 0x11, 0xbf, 0x4c, 0x54, 0xa8, 0x5f, 0x57, 0xdf, 0x6d, + 0x1d, 0x7c, 0x2b, 0xa9, 0x9b, 0xa2, 0x88, 0x14, 0x61, 0xf8, 0xb6, 0x43, 0x9b, 0x0d, 0xd5, 0x9e, + 0x77, 0x97, 0x01, 0x4c, 0x0e, 0x67, 0x17, 0xae, 0xf3, 0x51, 0x4e, 0x96, 0xc8, 0x70, 0xf8, 0x71, + 0xd7, 0x4d, 0x59, 0xeb, 0xdf, 0x17, 0xc3, 0x3c, 0x08, 0xc9, 0x9a, 0xfa, 0x74, 0xf5, 0x3f, 0xca, + 0xc0, 0x42, 0x6f, 0x12, 0xd5, 0x16, 0x39, 0xd3, 0xc7, 0x16, 0xf9, 0x95, 0xf8, 0x3b, 0x23, 0xa2, + 0x89, 0x77, 0xc6, 0xe8, 0x75, 0xb1, 0x82, 0xa6, 0xe0, 0x75, 0x2a, 0x13, 0xb1, 0x5c, 0xea, 0xf3, + 0xcd, 0x88, 0xc8, 0x87, 0x39, 0x40, 0x1a, 0x53, 0xd0, 0x1a, 0xbf, 0x39, 0x04, 0x17, 0x7b, 0x52, + 0x90, 0x15, 0x25, 0xbd, 0xd3, 0x54, 0x98, 0x58, 0xa6, 0x27, 0xfe, 0x35, 0xfc, 0x17, 0xad, 0xfd, + 0xe2, 0xde, 0x6e, 0xf7, 0xc3, 0xb4, 0x3e, 0x59, 0xe4, 0xf5, 0xa9, 0x53, 0x79, 0x71, 0x74, 0x64, + 0x06, 0xc9, 0x0c, 0x3f, 0xe8, 0x17, 0x49, 0x03, 0xdb, 0x69, 0xfa, 0xea, 0xb2, 0x6b, 0x70, 0x90, + 0x29, 0xcb, 0x22, 0x03, 0xf1, 0xa1, 0x74, 0x03, 0x71, 0xe3, 0x5f, 0x67, 0x60, 0x2c, 0xfc, 0x6c, + 0xb2, 0x00, 0xe7, 0x37, 0xcd, 0x52, 0x79, 0xd9, 0xda, 0xfc, 0x70, 0x63, 0xd9, 0xda, 0x5a, 0xaf, + 0x6d, 0x2c, 0x97, 0xab, 0xb7, 0xab, 0xcb, 0x95, 0xc2, 0x33, 0x64, 0x06, 0x26, 0xb7, 0xd6, 0xef, + 0xad, 0xdf, 0xdf, 0x5e, 0xb7, 0x96, 0x4d, 0xf3, 0xbe, 0x59, 0xc8, 0x90, 0x49, 0x18, 0x33, 0x97, + 0x4a, 0x65, 0x6b, 0xfd, 0x7e, 0x65, 0xb9, 0x90, 0x25, 0x05, 0x98, 0x28, 0xdf, 0x5f, 0x5f, 0x5f, + 0x2e, 0x6f, 0x56, 0x1f, 0x54, 0x37, 0x3f, 0x2c, 0xe4, 0x08, 0x81, 0x29, 0x44, 0xd8, 0x30, 0xab, + 0xeb, 0xe5, 0xea, 0x46, 0x69, 0xb5, 0x30, 0xc4, 0x60, 0x0c, 0x5f, 0x81, 0x0d, 0x87, 0x8c, 0xee, + 0x6d, 0x2d, 0x2d, 0x17, 0x46, 0x18, 0x0a, 0xfb, 0x4b, 0x41, 0x19, 0x65, 0xd5, 0x23, 0x4a, 0xa5, + 0xb4, 0x59, 0x5a, 0x2a, 0xd5, 0x96, 0x0b, 0x79, 0x72, 0x01, 0x66, 0x35, 0x90, 0xb5, 0x7a, 0xff, + 0x4e, 0x75, 0xbd, 0x30, 0x46, 0xe6, 0xa0, 0x10, 0xc2, 0x2a, 0x4b, 0xd6, 0x56, 0x6d, 0xd9, 0x2c, + 0x40, 0x1c, 0xba, 0x5e, 0x5a, 0x5b, 0x2e, 0x8c, 0x1b, 0xef, 0x72, 0x3f, 0x44, 0xde, 0xd5, 0xe4, + 0x3c, 0x90, 0xda, 0x66, 0x69, 0x73, 0xab, 0x16, 0x6b, 0xfc, 0x38, 0x8c, 0xd6, 0xb6, 0xca, 0xe5, + 0xe5, 0x5a, 0xad, 0x90, 0x21, 0x00, 0x23, 0xb7, 0x4b, 0xd5, 0xd5, 0xe5, 0x4a, 0x21, 0x6b, 0xfc, + 0x54, 0x06, 0x66, 0xa4, 0x04, 0x28, 0x1f, 0x8d, 0x1e, 0x73, 0x2d, 0xbe, 0xa7, 0x5d, 0x6c, 0xa5, + 0x93, 0x58, 0xac, 0x92, 0x3e, 0xcb, 0xf0, 0x17, 0x32, 0x70, 0x2e, 0x15, 0x9b, 0x7c, 0x08, 0x05, + 0xf9, 0x05, 0x6b, 0x76, 0x50, 0xdf, 0x8f, 0xf6, 0xb1, 0x17, 0x62, 0xb5, 0xc4, 0xd0, 0xb8, 0x5a, + 0x33, 0x4a, 0x38, 0x9d, 0x60, 0x33, 0x78, 0x3a, 0x04, 0xe3, 0x5b, 0x19, 0xb8, 0xd0, 0xa3, 0x1a, + 0x52, 0x86, 0x91, 0x30, 0x31, 0x4e, 0x1f, 0x83, 0xb7, 0xb9, 0x6f, 0x1f, 0x17, 0x05, 0x22, 0x66, + 0xe8, 0xc5, 0xbf, 0xcc, 0x91, 0x30, 0xd3, 0x0d, 0xa6, 0x9b, 0xe1, 0xdd, 0x77, 0x31, 0xd6, 0xf3, + 0xa2, 0xa6, 0xd2, 0x76, 0x6d, 0x69, 0x5c, 0xf4, 0x5d, 0xce, 0x3e, 0xf4, 0x31, 0xdf, 0x8c, 0xf1, + 0xb3, 0x19, 0x26, 0xdc, 0xc5, 0x11, 0x99, 0xcc, 0x5b, 0xf2, 0xfd, 0x6e, 0x8b, 0x9a, 0x6e, 0x93, + 0x96, 0xcc, 0x75, 0x71, 0x6c, 0xa0, 0xb4, 0x6a, 0x63, 0x01, 0x5e, 0x2b, 0x2c, 0xdb, 0x6b, 0x6b, + 0xaf, 0xd5, 0x2a, 0x0d, 0x79, 0x0b, 0x60, 0xf9, 0x51, 0x40, 0xbd, 0xb6, 0xdd, 0x0c, 0x63, 0xc4, + 0xf0, 0xc8, 0x56, 0x02, 0xaa, 0xcb, 0xdb, 0x0a, 0xb2, 0xf1, 0x37, 0x32, 0x30, 0x21, 0x2e, 0x4d, + 0xa5, 0x26, 0xf5, 0x82, 0xc7, 0x9b, 0x5e, 0x6f, 0x69, 0xd3, 0x2b, 0xf4, 0xef, 0x50, 0xf8, 0xb3, + 0xe2, 0xd4, 0x99, 0xf5, 0xcf, 0x32, 0x50, 0x88, 0x23, 0x92, 0xf7, 0x20, 0x5f, 0xa3, 0x0f, 0xa9, + 0xe7, 0x04, 0x47, 0x62, 0xa3, 0x94, 0x29, 0x04, 0x39, 0x8e, 0x28, 0xe3, 0xf3, 0xc1, 0x17, 0xbf, + 0xcc, 0x90, 0x66, 0xd0, 0xfd, 0x5e, 0x51, 0x7b, 0xe4, 0x9e, 0x94, 0xda, 0xc3, 0xf8, 0xdf, 0xb3, + 0x70, 0xe1, 0x0e, 0x0d, 0xd4, 0x36, 0x85, 0xe6, 0x05, 0x9f, 0x1e, 0xac, 0x5d, 0x4a, 0x4b, 0xe6, + 0x61, 0x14, 0x8b, 0xe4, 0xf8, 0x9a, 0xf2, 0x27, 0x59, 0x0a, 0xe7, 0x75, 0x4e, 0xcb, 0x51, 0xd6, + 0xa3, 0xee, 0x6b, 0x4a, 0xd6, 0xa2, 0x70, 0x5a, 0x5f, 0x86, 0x29, 0x0c, 0xcb, 0xdf, 0x65, 0xcb, + 0x81, 0x36, 0x84, 0xfa, 0x27, 0x6f, 0xc6, 0xa0, 0xe4, 0x35, 0x28, 0x30, 0x48, 0xa9, 0x7e, 0xd0, + 0x76, 0x0f, 0x9b, 0xb4, 0xb1, 0x47, 0x1b, 0x78, 0xac, 0xe7, 0xcd, 0x04, 0x5c, 0xf2, 0xdc, 0x6a, + 0xf3, 0xab, 0x1b, 0x6d, 0xa0, 0x8e, 0x46, 0xf0, 0x8c, 0xa0, 0x0b, 0x6f, 0xc1, 0xf8, 0xc7, 0xcc, + 0x40, 0x66, 0xfc, 0xaf, 0x19, 0x98, 0xc3, 0xc6, 0x29, 0x15, 0xcb, 0xec, 0xb0, 0xb2, 0xb7, 0x94, + 0xa4, 0x3c, 0x36, 0x03, 0xe9, 0x4b, 0x21, 0xec, 0xc5, 0x48, 0x27, 0x94, 0x1d, 0x40, 0x27, 0x54, + 0x3b, 0x4b, 0x26, 0xfc, 0x01, 0x55, 0x5a, 0x77, 0x87, 0xf2, 0xb9, 0xc2, 0x50, 0x34, 0xe4, 0xc6, + 0x0f, 0x65, 0x61, 0xd4, 0xa4, 0x98, 0x22, 0x9c, 0x5c, 0x86, 0xd1, 0x75, 0x37, 0xa0, 0xfe, 0x9a, + 0x96, 0x0f, 0xbe, 0xcd, 0x40, 0x56, 0xab, 0x61, 0xca, 0x42, 0x36, 0xe1, 0x37, 0x3c, 0xb7, 0xd1, + 0xad, 0x07, 0xea, 0x84, 0xef, 0x70, 0x90, 0x29, 0xcb, 0xc8, 0xeb, 0x30, 0x26, 0x38, 0x87, 0x8f, + 0xba, 0x68, 0xbb, 0xec, 0xd1, 0x30, 0xc5, 0x7c, 0x84, 0x80, 0x32, 0x2d, 0x17, 0x30, 0x86, 0x14, + 0x99, 0x36, 0x21, 0x33, 0x48, 0x51, 0x7d, 0xb8, 0x8f, 0xa8, 0xfe, 0x69, 0x18, 0x29, 0xf9, 0x3e, + 0x0d, 0x64, 0x14, 0x85, 0x89, 0x30, 0x6c, 0x9c, 0x4f, 0x03, 0xce, 0xd8, 0xc6, 0x72, 0x53, 0xe0, + 0x19, 0xff, 0x2a, 0x0b, 0xc3, 0xf8, 0x27, 0x3e, 0x99, 0x7a, 0xf5, 0x7d, 0xed, 0xc9, 0xd4, 0xab, + 0xef, 0x9b, 0x08, 0x25, 0x37, 0x50, 0x53, 0x21, 0xf3, 0x47, 0x89, 0xd6, 0xa3, 0x0a, 0xbe, 0x11, + 0x81, 0x4d, 0x15, 0x27, 0x7c, 0xe1, 0xcf, 0xa5, 0xc6, 0x4e, 0x39, 0x0f, 0xd9, 0xfb, 0x35, 0xd1, + 0x62, 0x8c, 0xc8, 0xe5, 0xfa, 0x66, 0xf6, 0x7e, 0x0d, 0x7b, 0x63, 0xa5, 0xb4, 0xf8, 0xe6, 0x2d, + 0xd1, 0x50, 0xde, 0x1b, 0xfb, 0xf6, 0xe2, 0x9b, 0xb7, 0x4c, 0x51, 0xc2, 0xfa, 0x17, 0xbf, 0x19, + 0x1f, 0x5e, 0xb9, 0xcf, 0x3f, 0xf6, 0x2f, 0xb6, 0x0d, 0x1f, 0x59, 0xcd, 0x08, 0x81, 0x2c, 0xc2, + 0xb8, 0x88, 0x35, 0x81, 0xf8, 0x4a, 0x2c, 0x08, 0x11, 0x8b, 0x82, 0x53, 0xa8, 0x48, 0xfc, 0x09, + 0x4e, 0x0c, 0x90, 0xcc, 0x72, 0x2b, 0x9e, 0xe0, 0xe4, 0x10, 0xfa, 0xa6, 0x82, 0xc2, 0x3e, 0x89, + 0xbf, 0xe1, 0x45, 0xbe, 0xfc, 0x53, 0x4a, 0xd0, 0x02, 0x4c, 0xb3, 0x10, 0x22, 0x18, 0xbf, 0x9c, + 0x85, 0xfc, 0x46, 0xb3, 0xbb, 0xe7, 0xb4, 0x1f, 0xdc, 0x20, 0x04, 0xf0, 0x1a, 0x27, 0xf3, 0x70, + 0xb0, 0xbf, 0xc9, 0x45, 0xc8, 0xcb, 0x9b, 0x9b, 0xdc, 0x90, 0x7c, 0x71, 0x6b, 0x9b, 0x07, 0x39, + 0xee, 0x22, 0xf4, 0x9a, 0xfc, 0x49, 0x6e, 0x40, 0x78, 0xff, 0xea, 0x75, 0x51, 0x1b, 0x62, 0x8b, + 0xc5, 0x0c, 0xd1, 0xc8, 0x1b, 0x80, 0x87, 0x84, 0xb8, 0x3c, 0x48, 0x85, 0x36, 0xff, 0x34, 0x21, + 0xa7, 0x70, 0x12, 0x44, 0x23, 0x37, 0x41, 0x4c, 0x4c, 0x91, 0x4d, 0xfd, 0x9c, 0x4e, 0xc0, 0xf3, + 0x53, 0x4a, 0x12, 0x81, 0x4a, 0xde, 0x81, 0xf1, 0xba, 0x47, 0xf1, 0xd5, 0xd1, 0x6e, 0x46, 0x49, + 0xd2, 0x55, 0xca, 0x72, 0x54, 0xfe, 0xe0, 0x86, 0xa9, 0xa2, 0x1b, 0xff, 0x5b, 0x1e, 0x26, 0xd4, + 0xef, 0x21, 0x26, 0xcc, 0xfa, 0x4d, 0x76, 0x77, 0x17, 0xc6, 0x66, 0x1d, 0x2c, 0x14, 0xc7, 0xe9, + 0x25, 0xfd, 0x83, 0x18, 0x1e, 0xb7, 0x3c, 0x93, 0x41, 0x32, 0x56, 0x9e, 0x31, 0x67, 0xfc, 0x08, + 0xcc, 0xf1, 0x48, 0x09, 0xf2, 0x6e, 0xc7, 0xdf, 0xa3, 0x6d, 0x47, 0xbe, 0xb7, 0xbc, 0xa4, 0x31, + 0xba, 0x2f, 0x0a, 0x13, 0xbc, 0x42, 0x32, 0xf2, 0x26, 0x8c, 0xb8, 0x1d, 0xda, 0xb6, 0x1d, 0x71, + 0xc6, 0x3d, 0x1b, 0x63, 0x40, 0xdb, 0xa5, 0xaa, 0x42, 0x28, 0x90, 0xc9, 0x75, 0x18, 0x72, 0x0f, + 0xc2, 0xf1, 0xba, 0xa8, 0x13, 0x1d, 0x04, 0xb6, 0x42, 0x82, 0x88, 0x8c, 0xe0, 0x23, 0xbb, 0xb5, + 0x2b, 0x46, 0x4c, 0x27, 0xb8, 0x6b, 0xb7, 0x76, 0x55, 0x02, 0x86, 0x48, 0xde, 0x07, 0xe8, 0xd8, + 0x7b, 0xd4, 0xb3, 0x1a, 0xdd, 0xe0, 0x48, 0x8c, 0xdb, 0x0b, 0x1a, 0xd9, 0x06, 0x2b, 0xae, 0x74, + 0x83, 0x23, 0x85, 0x76, 0xac, 0x23, 0x81, 0xa4, 0x04, 0xd0, 0xb2, 0x83, 0x80, 0x7a, 0x2d, 0x57, + 0x58, 0xfb, 0x45, 0x41, 0x10, 0x39, 0x83, 0xb5, 0xb0, 0x58, 0xe1, 0xa0, 0x10, 0xe1, 0x47, 0x3b, + 0x9e, 0x2d, 0x72, 0xda, 0xc7, 0x3e, 0xda, 0xf1, 0xb4, 0x56, 0x32, 0x44, 0xf2, 0x39, 0x18, 0x6d, + 0x38, 0x7e, 0xdd, 0xf5, 0x1a, 0x22, 0x7a, 0xca, 0x73, 0x1a, 0x4d, 0x85, 0x97, 0x29, 0x64, 0x12, + 0x9d, 0x7d, 0xad, 0x08, 0x82, 0xba, 0xee, 0x1e, 0xa2, 0x9a, 0x3f, 0xfe, 0xb5, 0xb5, 0xb0, 0x58, + 0xfd, 0xda, 0x88, 0x88, 0x0d, 0xe5, 0x9e, 0x13, 0x34, 0xed, 0x1d, 0xf1, 0xce, 0xad, 0x0f, 0xe5, + 0x1d, 0x2c, 0x52, 0x87, 0x92, 0x23, 0x93, 0xb7, 0x20, 0x4f, 0xdb, 0x81, 0x67, 0x5b, 0x4e, 0x43, + 0x38, 0x55, 0xea, 0x1f, 0xcd, 0x0e, 0x60, 0xbb, 0x5a, 0x51, 0x3f, 0x1a, 0xf1, 0xab, 0x0d, 0xd6, + 0x3f, 0x7e, 0xdd, 0x69, 0x09, 0x5f, 0x48, 0xbd, 0x7f, 0x6a, 0xe5, 0xea, 0x9a, 0xda, 0x3f, 0x0c, + 0x91, 0xbc, 0x07, 0xa3, 0x6c, 0xfd, 0x36, 0xdc, 0x3d, 0x11, 0x90, 0xc2, 0xd0, 0xfb, 0x87, 0x97, + 0x25, 0xa6, 0xab, 0x24, 0x62, 0x0b, 0xd9, 0x3e, 0xf4, 0x2d, 0xa7, 0x2e, 0x82, 0x4c, 0xe8, 0xcb, + 0xb1, 0xb4, 0x5d, 0xab, 0x96, 0x15, 0xb2, 0x61, 0xfb, 0xd0, 0xaf, 0xd6, 0xc9, 0x22, 0x0c, 0x63, + 0x8a, 0x0a, 0x11, 0x08, 0x53, 0xa7, 0xc1, 0xe4, 0x14, 0x2a, 0x0d, 0xa2, 0xb2, 0x81, 0x6c, 0xf9, + 0xe8, 0x5e, 0x22, 0x12, 0x45, 0xe8, 0x7d, 0xb2, 0x56, 0x43, 0x9f, 0x13, 0xf5, 0x13, 0x05, 0x3a, + 0xfb, 0xc4, 0x36, 0x0d, 0x2c, 0xe7, 0xeb, 0x22, 0xd5, 0x83, 0x5e, 0xdd, 0x3a, 0x0d, 0xaa, 0x1f, + 0xa8, 0xd5, 0xb5, 0x69, 0x50, 0xfd, 0x3a, 0x79, 0xe1, 0xff, 0x67, 0xef, 0xdb, 0x7a, 0xdc, 0x48, + 0xae, 0x83, 0xd5, 0x24, 0x67, 0x86, 0x73, 0x38, 0x97, 0x9e, 0xd2, 0x48, 0x33, 0x3b, 0xd2, 0x4a, + 0xab, 0x5e, 0x49, 0x96, 0xb8, 0xde, 0xf5, 0x4a, 0xfb, 0xad, 0xf7, 0x62, 0xaf, 0xd7, 0x3d, 0x9c, + 0x9e, 0x21, 0x25, 0xde, 0xdc, 0x4d, 0x8e, 0x2c, 0xcb, 0x76, 0xbb, 0x45, 0xf6, 0xcc, 0xb4, 0x97, + 0x43, 0xd2, 0x6c, 0x72, 0xe5, 0x31, 0x3e, 0xe0, 0xb3, 0xf1, 0x01, 0x36, 0xf0, 0x7d, 0x49, 0x9c, + 0x38, 0x09, 0xb2, 0xf0, 0x8b, 0x03, 0x64, 0x11, 0xe4, 0x21, 0x3f, 0x20, 0x88, 0xf3, 0xe2, 0x37, + 0x03, 0x86, 0x01, 0x03, 0x79, 0x73, 0x82, 0x45, 0xb2, 0x40, 0x82, 0xdc, 0xde, 0x82, 0xe4, 0xc1, + 0x40, 0x80, 0xa0, 0x4e, 0x55, 0x75, 0x57, 0x5f, 0x48, 0x8d, 0xbc, 0xbb, 0x49, 0x0c, 0xf8, 0x69, + 0x86, 0xa7, 0x4e, 0x9d, 0xae, 0xeb, 0xa9, 0x53, 0xa7, 0xce, 0x05, 0x20, 0x7c, 0xfa, 0x67, 0x0f, + 0x35, 0xa6, 0x04, 0x79, 0x3d, 0xf7, 0x0f, 0x7f, 0x78, 0x59, 0xd9, 0x06, 0xc8, 0x8b, 0xb0, 0x3a, + 0x5a, 0x15, 0x9e, 0x9a, 0xca, 0x2c, 0xc8, 0x4d, 0x50, 0x0f, 0x1c, 0xae, 0x2a, 0xec, 0x1c, 0x39, + 0xfd, 0xbe, 0xdb, 0xe3, 0x6c, 0x7a, 0x55, 0xc0, 0x4b, 0x0c, 0xcc, 0x28, 0x6b, 0x6f, 0xc2, 0x7a, + 0xda, 0x2a, 0x21, 0x57, 0x60, 0x49, 0x8e, 0x20, 0xc4, 0x89, 0x14, 0x9c, 0xa1, 0x27, 0x62, 0x08, + 0x71, 0x02, 0x3f, 0x54, 0xe0, 0xe2, 0x2c, 0x9e, 0x43, 0xb6, 0x20, 0x3f, 0x1c, 0x79, 0x03, 0x94, + 0x6d, 0x79, 0x8a, 0x06, 0xf1, 0x1b, 0xb3, 0x2f, 0xa0, 0x10, 0x36, 0x76, 0x0e, 0xb9, 0x57, 0x88, + 0xb9, 0x88, 0x90, 0x96, 0x73, 0xe8, 0x93, 0xe7, 0x60, 0xad, 0xeb, 0x1e, 0x38, 0x93, 0xde, 0xd8, + 0xf6, 0x3b, 0x47, 0x6e, 0x17, 0xfd, 0xb6, 0xd0, 0xda, 0xcf, 0x54, 0x79, 0x81, 0x25, 0xe0, 0x89, + 0x16, 0xcf, 0x4d, 0x69, 0xf1, 0x9d, 0x5c, 0x5e, 0x51, 0x33, 0x26, 0x9a, 0x57, 0x69, 0xdf, 0xcc, + 0xc0, 0xe6, 0xb4, 0x4d, 0x46, 0xde, 0x48, 0x1b, 0x03, 0xf6, 0xda, 0x21, 0xc3, 0xe5, 0xd7, 0x0e, + 0xe9, 0x6b, 0xe4, 0x36, 0x04, 0x5e, 0x57, 0x8f, 0x8b, 0xa0, 0x20, 0x60, 0xb4, 0xce, 0xd0, 0xf1, + 0xfd, 0x47, 0x94, 0x8f, 0x64, 0xa5, 0x28, 0xbc, 0x1c, 0x26, 0xd7, 0x11, 0x30, 0xf2, 0x0a, 0x40, + 0xa7, 0x37, 0xf0, 0x5d, 0x34, 0x2a, 0xe0, 0x02, 0x0a, 0xb3, 0x25, 0x0f, 0xa0, 0xf2, 0x2b, 0x32, + 0x42, 0x4b, 0x83, 0xae, 0xcb, 0x27, 0xd0, 0x81, 0x8d, 0x29, 0x5c, 0x95, 0x4e, 0x4f, 0x98, 0xd2, + 0x5e, 0x24, 0xc8, 0x9a, 0x04, 0x89, 0xed, 0xe3, 0x23, 0x9e, 0x99, 0xb6, 0x46, 0x4e, 0x80, 0x24, + 0x59, 0x27, 0xa5, 0xce, 0x2d, 0xa2, 0x27, 0xa3, 0x80, 0x3a, 0x83, 0xb4, 0x47, 0x3d, 0x72, 0x19, + 0x0a, 0x22, 0x01, 0x26, 0xbd, 0x00, 0x30, 0xe2, 0xc0, 0x41, 0x77, 0x5d, 0x5c, 0x3c, 0x18, 0x66, + 0x15, 0x7d, 0xeb, 0xb8, 0x68, 0xb1, 0x88, 0x90, 0xd6, 0xc9, 0x50, 0xf4, 0xee, 0xa2, 0x58, 0xdf, + 0xd1, 0x03, 0x8d, 0x97, 0xfe, 0xbe, 0x22, 0xa6, 0x3f, 0x79, 0x22, 0x3c, 0xae, 0x7d, 0x04, 0xd0, + 0xb5, 0x89, 0x37, 0x0c, 0xff, 0xa7, 0xa2, 0x8e, 0xd8, 0x75, 0x5c, 0xd4, 0xe1, 0x3f, 0xc9, 0x75, + 0x58, 0x1d, 0x31, 0xe3, 0xd7, 0xf1, 0x80, 0x8f, 0x27, 0x4b, 0x36, 0xb2, 0xcc, 0xc0, 0xad, 0x01, + 0x8e, 0x29, 0x6f, 0xd7, 0x9d, 0x60, 0xc0, 0xa4, 0x03, 0x92, 0xbc, 0x00, 0x8b, 0xf4, 0x80, 0xc4, + 0xf0, 0x3c, 0x31, 0x9f, 0x0a, 0xc4, 0x43, 0x71, 0xc3, 0xcc, 0x7f, 0x95, 0xff, 0xcf, 0x69, 0xbd, + 0x93, 0x11, 0xc4, 0xe4, 0xe3, 0x99, 0x6c, 0xc0, 0xc2, 0x60, 0x74, 0x28, 0x75, 0x6d, 0x7e, 0x30, + 0x3a, 0xa4, 0xfd, 0xba, 0x01, 0x2a, 0x73, 0xf1, 0x61, 0xa1, 0x16, 0xfc, 0x93, 0x3e, 0xbb, 0xbf, + 0xe7, 0xcd, 0x15, 0x06, 0xc7, 0x2c, 0xff, 0x27, 0xfd, 0x0e, 0xc5, 0xf4, 0xfd, 0x81, 0x2d, 0x47, + 0xe5, 0xe2, 0xdd, 0x5e, 0xf1, 0xfd, 0x41, 0x18, 0x9e, 0xab, 0x4b, 0xb6, 0x61, 0x99, 0xd2, 0x09, + 0x62, 0x83, 0x71, 0xe9, 0xe1, 0xe9, 0xa4, 0xf4, 0x70, 0xd2, 0xef, 0x88, 0x26, 0x9a, 0x4b, 0xbe, + 0xf4, 0x8b, 0xdc, 0x05, 0x55, 0x12, 0xb3, 0xd0, 0xe7, 0x33, 0x66, 0x88, 0x1d, 0x92, 0x91, 0xc4, + 0xb3, 0x4a, 0xff, 0x60, 0x60, 0xae, 0x76, 0xa2, 0x00, 0x3e, 0x34, 0x3f, 0x50, 0x04, 0x2f, 0x4d, + 0xa9, 0x44, 0x34, 0x58, 0x3e, 0x72, 0x7c, 0xdb, 0xf7, 0x8f, 0x99, 0x61, 0x19, 0x8f, 0x46, 0x5c, + 0x38, 0x72, 0x7c, 0xcb, 0x3f, 0x16, 0xd9, 0x4e, 0xce, 0x51, 0x9c, 0x81, 0x33, 0x19, 0x1f, 0xd9, + 0xb2, 0xd0, 0xc8, 0x46, 0xec, 0xec, 0x91, 0xe3, 0x37, 0x68, 0x99, 0x44, 0x9b, 0x5c, 0x85, 0x15, + 0xa4, 0xdb, 0xf1, 0x04, 0x61, 0x0c, 0x97, 0x61, 0x2e, 0x51, 0xc2, 0x1d, 0x8f, 0x51, 0xe6, 0x2d, + 0xfc, 0xc7, 0x0c, 0x9c, 0x4f, 0x1f, 0x1d, 0x5c, 0x9e, 0x74, 0x4c, 0xd1, 0xb1, 0x8f, 0xb7, 0x6d, + 0x91, 0x42, 0x58, 0xa8, 0x93, 0xb4, 0xc9, 0xc9, 0xa4, 0x4e, 0x4e, 0x11, 0xd6, 0x90, 0x10, 0x17, + 0x4f, 0x7b, 0x9e, 0x3f, 0xe6, 0x11, 0x3c, 0xcc, 0x55, 0x5a, 0xc0, 0xf8, 0x79, 0x95, 0x82, 0xc9, + 0x35, 0x58, 0x11, 0x1c, 0x79, 0xf0, 0xa8, 0x4f, 0x3f, 0xcc, 0xd8, 0xf1, 0x32, 0x87, 0x36, 0x10, + 0x48, 0xce, 0xc1, 0xbc, 0x33, 0x1c, 0xd2, 0x4f, 0x32, 0x2e, 0x3c, 0xe7, 0x0c, 0x87, 0x2c, 0x23, + 0x0f, 0xba, 0x31, 0xda, 0x07, 0x68, 0x5a, 0xc4, 0xed, 0x18, 0xcd, 0x25, 0x04, 0x32, 0x73, 0x23, + 0x9f, 0xee, 0x7b, 0x5a, 0x57, 0xa0, 0x2c, 0x20, 0x0a, 0x38, 0xc3, 0x00, 0xe1, 0x29, 0xc8, 0x8b, + 0x47, 0x6e, 0xe6, 0x8d, 0x61, 0x2e, 0x38, 0xfc, 0x81, 0xfb, 0x65, 0xd8, 0xe8, 0x7a, 0x3e, 0x2e, + 0x5e, 0xd6, 0xa5, 0xe1, 0x90, 0x3b, 0x4e, 0xb2, 0xc8, 0xbe, 0xe6, 0x3a, 0x2f, 0xa6, 0x23, 0xa9, + 0x0f, 0x87, 0xcc, 0x7d, 0x92, 0x8f, 0xf5, 0xab, 0xb0, 0xca, 0xc5, 0x34, 0x7e, 0x44, 0x62, 0x5b, + 0xf8, 0x06, 0xa6, 0xf7, 0x27, 0x9e, 0x03, 0x09, 0x38, 0xa8, 0xd2, 0x15, 0x35, 0xff, 0x5a, 0x81, + 0x73, 0xa9, 0x72, 0x1e, 0xf9, 0x0a, 0x30, 0x3f, 0xb1, 0xf1, 0xc0, 0x1e, 0xb9, 0x1d, 0x6f, 0xe8, + 0x61, 0xe0, 0x0d, 0xa6, 0x07, 0xbd, 0x3d, 0x4b, 0x42, 0x44, 0x9f, 0xb3, 0xd6, 0xc0, 0x0c, 0x2a, + 0x31, 0x05, 0x8d, 0x3a, 0x8a, 0x81, 0xb7, 0x1e, 0xc0, 0xb9, 0x54, 0xd4, 0x14, 0xc5, 0xc9, 0xc7, + 0xa3, 0x19, 0xa8, 0xc5, 0xcb, 0x56, 0xac, 0xd3, 0x92, 0x42, 0x85, 0x77, 0xef, 0x47, 0x41, 0xf7, + 0x62, 0x12, 0x21, 0x31, 0xe2, 0xfb, 0x3a, 0xed, 0x52, 0x23, 0x2a, 0x4d, 0xdf, 0xda, 0x0f, 0xe0, + 0x1c, 0x5f, 0x7c, 0x87, 0x23, 0x67, 0x78, 0x14, 0x92, 0x63, 0x0d, 0xfd, 0x58, 0x1a, 0x39, 0xb6, + 0x2a, 0xf7, 0x28, 0x7e, 0x40, 0xf5, 0xac, 0x93, 0x04, 0xf2, 0x3e, 0x7c, 0x2b, 0x23, 0xb6, 0x7a, + 0x4a, 0x73, 0x52, 0x96, 0xb5, 0x92, 0xb6, 0xac, 0x4f, 0xbf, 0xa7, 0xea, 0x40, 0x64, 0x66, 0xc5, + 0x54, 0xa5, 0xdc, 0x0a, 0x4b, 0x08, 0xf7, 0xbc, 0x21, 0x12, 0x6b, 0xb0, 0x58, 0x06, 0xd0, 0xb5, + 0x4e, 0x1c, 0x44, 0x2e, 0xc0, 0x62, 0x90, 0x64, 0x9b, 0x1f, 0x1c, 0x79, 0x06, 0xa8, 0x74, 0xc9, + 0x33, 0xb0, 0xc4, 0xe4, 0xf8, 0xc8, 0x9e, 0x03, 0x84, 0xe9, 0x74, 0xe3, 0x89, 0x31, 0x50, 0xe0, + 0x99, 0xc7, 0x8d, 0x21, 0xb9, 0x07, 0xe7, 0xd1, 0x16, 0xc4, 0x1f, 0x04, 0xd3, 0x60, 0x77, 0x9c, + 0xce, 0x91, 0xcb, 0x57, 0xad, 0x96, 0x3a, 0x19, 0xc3, 0xa1, 0x65, 0x35, 0xa4, 0x79, 0x18, 0x0e, + 0x2d, 0x7f, 0x20, 0x7e, 0x97, 0x68, 0x75, 0xde, 0x86, 0x2e, 0x5c, 0x98, 0x51, 0x53, 0x62, 0x1c, + 0x8a, 0xcc, 0x38, 0x6e, 0x80, 0x7a, 0xe0, 0x76, 0xa9, 0x4c, 0xec, 0x76, 0xb1, 0x69, 0x6f, 0xdf, + 0x66, 0x69, 0xe5, 0xcd, 0x95, 0x00, 0x6e, 0xf9, 0x83, 0xfd, 0xdb, 0xfc, 0x2b, 0xc7, 0xe2, 0xc8, + 0x93, 0xef, 0x22, 0xe4, 0x05, 0x38, 0x1b, 0x0b, 0x6a, 0x12, 0x7a, 0xc9, 0x9b, 0x6b, 0xb4, 0x28, + 0x1a, 0x02, 0xeb, 0x0a, 0x2c, 0x89, 0x55, 0x31, 0x0a, 0x9c, 0xe7, 0xcc, 0x02, 0x87, 0xd1, 0x5d, + 0xc7, 0x3f, 0x37, 0x11, 0x9d, 0x4a, 0xbd, 0xc6, 0x9c, 0x42, 0x96, 0x26, 0xcf, 0x03, 0x09, 0xe4, + 0xf6, 0x80, 0x51, 0xf0, 0x0f, 0xae, 0x89, 0x92, 0x60, 0x87, 0xf3, 0xcf, 0xfe, 0x45, 0x06, 0xce, + 0xa6, 0xdc, 0x7f, 0xe8, 0x25, 0xc0, 0xeb, 0x8f, 0xdd, 0x43, 0x76, 0x85, 0x90, 0x3b, 0xb9, 0x2a, + 0xc1, 0xb9, 0x52, 0x6b, 0x9e, 0xa5, 0x4d, 0xe7, 0xdf, 0xe2, 0xbf, 0x28, 0xf3, 0x70, 0x46, 0x42, + 0x5f, 0x43, 0xff, 0x25, 0x15, 0x58, 0xc3, 0x5c, 0x10, 0xbe, 0x37, 0xc0, 0x94, 0x12, 0x28, 0x84, + 0xe4, 0x22, 0x37, 0x24, 0x6c, 0x45, 0x53, 0x42, 0xa2, 0x52, 0x88, 0xa9, 0x0e, 0x63, 0x10, 0xf2, + 0x29, 0xd8, 0x92, 0xce, 0x1a, 0x3b, 0xb6, 0xf3, 0xd0, 0x3c, 0xde, 0xdc, 0x70, 0x82, 0x53, 0x67, + 0x27, 0xb2, 0x07, 0xb7, 0xe1, 0x12, 0x4e, 0xa2, 0xd7, 0x1d, 0xda, 0x89, 0xe4, 0x21, 0xd8, 0x55, + 0x16, 0x6d, 0x7f, 0x8b, 0x62, 0x55, 0xba, 0xc3, 0x58, 0x1e, 0x11, 0xda, 0x6b, 0x3e, 0x7c, 0x0f, + 0xe0, 0x5c, 0x6a, 0x8b, 0xe9, 0x01, 0x83, 0xd6, 0x57, 0xa1, 0x6c, 0xb4, 0x40, 0x7f, 0x53, 0xe1, + 0xe8, 0x0a, 0x2c, 0x3d, 0x74, 0x9d, 0x91, 0x3b, 0xe2, 0x27, 0x37, 0x5f, 0x12, 0x0c, 0x26, 0x1f, + 0xdc, 0xdd, 0xe8, 0xd4, 0x70, 0x45, 0x13, 0xa9, 0xc1, 0x59, 0x76, 0x02, 0x7a, 0xc7, 0x28, 0x0c, + 0x72, 0xe5, 0x94, 0x12, 0x11, 0x87, 0xb0, 0x0a, 0x1e, 0x4d, 0x15, 0xc4, 0x62, 0xb5, 0xcd, 0xb5, + 0xc3, 0x38, 0x88, 0xee, 0xe8, 0xf3, 0xe9, 0xd8, 0x64, 0x1b, 0x0a, 0x8c, 0x38, 0xbb, 0x16, 0xb0, + 0x57, 0x85, 0x2b, 0x33, 0xbf, 0x50, 0x42, 0xa3, 0x64, 0x3f, 0xf8, 0x9f, 0x9e, 0xd7, 0xf8, 0x80, + 0x6b, 0x1f, 0xcb, 0x8f, 0x26, 0xe6, 0x12, 0x02, 0xf9, 0x63, 0x89, 0xf6, 0x97, 0x8a, 0xe8, 0x6a, + 0xe4, 0x46, 0x4d, 0x97, 0x96, 0xef, 0xf6, 0xc5, 0xc3, 0xd1, 0xa2, 0xc9, 0x7f, 0x3d, 0xe1, 0x52, + 0x27, 0xaf, 0xc0, 0x12, 0x25, 0x7b, 0x38, 0xe9, 0xb3, 0x25, 0x97, 0x8d, 0x04, 0xf3, 0xa9, 0xb1, + 0x22, 0x3a, 0x6d, 0xe5, 0x33, 0x66, 0xe1, 0x38, 0xfc, 0x49, 0xa5, 0x65, 0xff, 0x78, 0x3c, 0x94, + 0x17, 0xaa, 0xd0, 0x2e, 0x5a, 0xb5, 0x56, 0x93, 0x57, 0xc9, 0x53, 0x9c, 0x50, 0x5a, 0xde, 0x9e, + 0x67, 0xfa, 0x45, 0xed, 0x39, 0x28, 0x48, 0xb4, 0x69, 0x67, 0x98, 0xbb, 0x8d, 0xe8, 0x0c, 0xfb, + 0xc5, 0x27, 0xfb, 0x21, 0xe4, 0x05, 0x49, 0x7a, 0x2d, 0x38, 0x1a, 0xf8, 0x62, 0x93, 0xe3, 0xff, + 0x14, 0x46, 0x47, 0x19, 0x3b, 0x39, 0x67, 0xe2, 0xff, 0x78, 0x96, 0x8c, 0x1d, 0x7a, 0x1f, 0xe8, + 0xf9, 0xf6, 0x10, 0xcd, 0xb6, 0x02, 0xe1, 0x99, 0xc2, 0x5b, 0x3d, 0x9f, 0x19, 0x73, 0xf1, 0x6f, + 0xfc, 0x59, 0x70, 0x08, 0xc7, 0x54, 0x10, 0xd3, 0x78, 0x66, 0xe4, 0xc8, 0xc8, 0x24, 0x8f, 0x0c, + 0x16, 0xa4, 0x85, 0xd7, 0x64, 0x5f, 0x06, 0x84, 0xe1, 0x91, 0x21, 0x71, 0x86, 0x5c, 0x84, 0x33, + 0x48, 0x77, 0xf2, 0x70, 0xf6, 0xd8, 0x89, 0x23, 0xee, 0xe4, 0x71, 0x3e, 0xf5, 0x6e, 0xb0, 0x42, + 0x22, 0x4a, 0x10, 0x2a, 0x3c, 0x33, 0xc1, 0x99, 0xa7, 0xf6, 0x8d, 0x31, 0xc8, 0xb3, 0x58, 0xc8, + 0x12, 0xff, 0x04, 0x8c, 0xf2, 0xf1, 0x77, 0x4e, 0xf2, 0x22, 0xac, 0x07, 0xc9, 0x28, 0xfd, 0xb7, + 0xbc, 0xa1, 0x8d, 0xe9, 0x48, 0x4f, 0xb8, 0x48, 0x4b, 0x44, 0x99, 0xf5, 0x96, 0x37, 0xdc, 0xc7, + 0x12, 0xde, 0xcc, 0x3f, 0xce, 0x08, 0x4d, 0xc6, 0xf6, 0x60, 0x30, 0xf6, 0xc7, 0x23, 0x67, 0x18, + 0x51, 0xf3, 0x92, 0x63, 0x78, 0x0a, 0x9b, 0x74, 0x1b, 0xd3, 0x83, 0x0c, 0x46, 0x22, 0x7c, 0x49, + 0xb0, 0xc1, 0x0a, 0xb7, 0x3f, 0x11, 0xbd, 0x8a, 0xe8, 0x14, 0x5b, 0x97, 0x91, 0xe9, 0xbe, 0x92, + 0xa8, 0x96, 0xcf, 0x98, 0x1b, 0x8c, 0x66, 0x02, 0x8b, 0x94, 0x53, 0x78, 0x4d, 0x5c, 0xcf, 0xbb, + 0x1d, 0x32, 0x9e, 0x28, 0x55, 0x99, 0x25, 0x91, 0xcf, 0xc0, 0xa2, 0xd7, 0x95, 0xb3, 0x60, 0xc6, + 0x35, 0x8c, 0x95, 0x2e, 0x8b, 0xc4, 0x1d, 0xd2, 0xa0, 0x5b, 0xc3, 0xe3, 0xd0, 0xed, 0xe5, 0x88, + 0x42, 0x5c, 0xdb, 0x16, 0x97, 0xe6, 0x64, 0x35, 0xb2, 0x02, 0x99, 0x60, 0x21, 0x66, 0xbc, 0x2e, + 0xe3, 0x02, 0x61, 0x2c, 0x70, 0x93, 0xff, 0xd2, 0xfe, 0x37, 0xdc, 0x38, 0xed, 0x18, 0x51, 0x8e, + 0x31, 0x65, 0xc0, 0x17, 0x59, 0x18, 0xce, 0xe8, 0xb8, 0x5d, 0x01, 0x39, 0x94, 0xb1, 0x27, 0x96, + 0x88, 0x80, 0xb5, 0x47, 0x9e, 0xf6, 0x2f, 0x59, 0x58, 0x89, 0x3e, 0x01, 0x90, 0xe7, 0x20, 0x27, + 0x31, 0xca, 0x8d, 0x94, 0x77, 0x02, 0x64, 0x8f, 0x88, 0x74, 0x2a, 0xc6, 0x48, 0xee, 0xc0, 0x0a, + 0x1a, 0x25, 0xa2, 0x84, 0x3c, 0xf6, 0xf8, 0xc3, 0xd2, 0xec, 0xb7, 0xc1, 0xfc, 0x8f, 0xdf, 0xbb, + 0x7c, 0x06, 0x9f, 0x01, 0x97, 0x68, 0x5d, 0x2a, 0xa4, 0xd2, 0x42, 0x49, 0xc3, 0x9b, 0x9b, 0xae, + 0xe1, 0xe5, 0x5d, 0x99, 0xa2, 0xe1, 0x9d, 0x9b, 0xa1, 0xe1, 0x0d, 0x6b, 0xca, 0x1a, 0x5e, 0xd4, + 0xf3, 0x2f, 0x4c, 0xd3, 0xf3, 0x87, 0x75, 0x98, 0x9e, 0x3f, 0xd4, 0xd0, 0xe6, 0xa7, 0x6a, 0x68, + 0xc3, 0x3a, 0x5c, 0x43, 0x1b, 0xea, 0x4c, 0x17, 0xa7, 0xea, 0x4c, 0xa5, 0x4a, 0x4c, 0x67, 0x7a, + 0x95, 0x0f, 0xec, 0xc8, 0x79, 0x64, 0xe3, 0x88, 0xf3, 0x23, 0x1f, 0x87, 0xcc, 0x74, 0x1e, 0xa1, + 0xb5, 0xd1, 0xf6, 0x22, 0x08, 0x13, 0x25, 0xed, 0x87, 0x31, 0x06, 0x24, 0xe6, 0xfc, 0x1a, 0xac, + 0xb0, 0x73, 0x98, 0x87, 0x77, 0x65, 0x07, 0xf1, 0xb2, 0xb9, 0x2c, 0xa0, 0xec, 0x2a, 0xfd, 0x31, + 0x58, 0x0d, 0xd0, 0xf8, 0x6d, 0x12, 0x3d, 0x17, 0xcd, 0xa0, 0x36, 0x0f, 0xc3, 0x23, 0xd3, 0x1b, + 0xf1, 0x40, 0x37, 0x11, 0x7a, 0x2c, 0x0a, 0xca, 0xf3, 0x40, 0x42, 0xb4, 0xc0, 0x60, 0x33, 0x87, + 0xa8, 0x6b, 0x01, 0x6a, 0x60, 0x55, 0xf9, 0xbb, 0x4a, 0x4c, 0x47, 0xfb, 0x51, 0x35, 0xff, 0x39, + 0x08, 0xbe, 0x6e, 0x73, 0x3d, 0x9b, 0xe8, 0x81, 0x2a, 0x0a, 0x9a, 0x1c, 0xae, 0x1d, 0xc6, 0xef, + 0x84, 0x1f, 0x51, 0xab, 0xb4, 0x1f, 0x65, 0x23, 0xfa, 0x2b, 0xf1, 0x19, 0x2a, 0xdf, 0xf8, 0x03, + 0x9b, 0x4f, 0x31, 0x67, 0xbf, 0x57, 0xa6, 0x2c, 0x53, 0x6e, 0xa2, 0x66, 0x59, 0x0d, 0x13, 0x7c, + 0x7f, 0x20, 0x2c, 0xd6, 0x6c, 0x76, 0xd7, 0x61, 0x12, 0x19, 0x6e, 0x53, 0x41, 0x8e, 0xf1, 0xda, + 0xe2, 0x6c, 0x72, 0x42, 0x81, 0x40, 0x77, 0x29, 0xde, 0x79, 0x82, 0x5f, 0xe2, 0x03, 0x6d, 0x40, + 0x75, 0xaf, 0x1f, 0x25, 0x9e, 0x4d, 0xb9, 0xd5, 0x26, 0x88, 0xe3, 0x28, 0x21, 0x65, 0x75, 0x22, + 0xfe, 0x15, 0x64, 0x0d, 0x58, 0x42, 0xed, 0x91, 0x20, 0x98, 0x4b, 0x79, 0x51, 0x49, 0x76, 0xbe, + 0x54, 0xa9, 0x99, 0x05, 0x5a, 0x4f, 0x90, 0x39, 0x82, 0xa7, 0x64, 0x9d, 0x4f, 0xb4, 0x91, 0x73, + 0x22, 0x28, 0xf3, 0xcc, 0x11, 0x08, 0x55, 0x43, 0xd8, 0xd4, 0xf3, 0x4e, 0x14, 0xc0, 0xd1, 0xb4, + 0x23, 0xd8, 0x9a, 0x3e, 0x25, 0x33, 0x12, 0x7e, 0x85, 0xa2, 0x4d, 0x46, 0x16, 0x6d, 0x64, 0x0d, + 0x50, 0x36, 0xa2, 0x01, 0xd2, 0xfe, 0x28, 0x0b, 0xcf, 0x9e, 0x62, 0xba, 0x66, 0x7c, 0xf3, 0xb3, + 0x51, 0xc1, 0x39, 0x13, 0xb9, 0xb3, 0x53, 0xa2, 0xfc, 0x4c, 0x38, 0xe9, 0x77, 0xa6, 0x88, 0xcd, + 0x5f, 0x81, 0x55, 0xc6, 0xf8, 0x99, 0x95, 0xe9, 0xc1, 0xa4, 0x77, 0x0a, 0xce, 0x7f, 0x41, 0xb8, + 0xc4, 0xc5, 0xaa, 0xe2, 0x61, 0x80, 0xfc, 0xce, 0x0a, 0x60, 0xa4, 0x05, 0x05, 0x44, 0x3b, 0x70, + 0xbc, 0xde, 0xa9, 0x7c, 0xb3, 0x84, 0xc3, 0x9d, 0x5c, 0x8d, 0x19, 0xc7, 0x53, 0xc0, 0x2e, 0xfe, + 0x26, 0xd7, 0x61, 0xb5, 0x3f, 0x39, 0xa6, 0x22, 0x21, 0x5b, 0x0b, 0xdc, 0x98, 0x67, 0xce, 0x5c, + 0xee, 0x4f, 0x8e, 0xf5, 0xe1, 0x10, 0xa7, 0x14, 0xad, 0x7e, 0xd6, 0x28, 0x1e, 0xdb, 0xb5, 0x02, + 0x73, 0x1e, 0x31, 0x29, 0x01, 0xb6, 0x6f, 0x39, 0xee, 0x3a, 0x30, 0x1b, 0x50, 0x9e, 0xf0, 0x8c, + 0xfd, 0xd0, 0xfe, 0x3d, 0x23, 0x34, 0x11, 0xd3, 0xd7, 0xfd, 0xaf, 0xa7, 0x28, 0x65, 0x8a, 0x6e, + 0x80, 0x4a, 0x87, 0x3e, 0x64, 0x2a, 0xc1, 0x1c, 0xad, 0xf4, 0x27, 0xc7, 0xc1, 0xd8, 0xc9, 0x03, + 0x3f, 0x2f, 0x0f, 0xfc, 0x2b, 0x42, 0x53, 0x91, 0xca, 0x1e, 0xa6, 0x0f, 0x39, 0x95, 0x98, 0xae, + 0x9f, 0x8e, 0x09, 0xfc, 0x7a, 0xde, 0x52, 0xe6, 0x2d, 0xa6, 0xd4, 0x9e, 0x4b, 0x28, 0xb5, 0x53, + 0xf6, 0xde, 0x7c, 0xda, 0xde, 0x4b, 0xa8, 0xd0, 0x17, 0x52, 0x54, 0xe8, 0xa9, 0x1b, 0x34, 0xff, + 0x98, 0x0d, 0xba, 0x28, 0xaf, 0x93, 0xbf, 0x0f, 0x54, 0x4b, 0xd1, 0x2b, 0xd0, 0x03, 0x38, 0x2b, + 0xae, 0x40, 0xec, 0xe4, 0x08, 0x5f, 0x46, 0x0a, 0xb7, 0x6f, 0xa6, 0x5d, 0x7e, 0x10, 0x2d, 0xe5, + 0x82, 0xb2, 0xc6, 0xaf, 0x3d, 0x61, 0xf9, 0xff, 0x9c, 0x0b, 0x0f, 0xb9, 0x0f, 0xe7, 0x31, 0x5d, + 0x40, 0x47, 0x7e, 0xd3, 0xb1, 0x47, 0xee, 0x01, 0x5f, 0x0f, 0x57, 0x12, 0xd7, 0x03, 0xaf, 0x23, + 0x35, 0xc7, 0x74, 0x0f, 0xca, 0x67, 0xcc, 0x75, 0x3f, 0x05, 0x1e, 0xbf, 0x4b, 0xfd, 0xa9, 0x02, + 0xda, 0xe3, 0xc7, 0x0b, 0xaf, 0xbd, 0xf1, 0x01, 0xa7, 0xd7, 0x5e, 0x69, 0xf4, 0x9e, 0x85, 0xe5, + 0x91, 0x7b, 0x30, 0x72, 0xfd, 0xa3, 0x88, 0x6e, 0x6a, 0x89, 0x03, 0xc5, 0xc0, 0x88, 0x18, 0xa3, + 0x4f, 0x74, 0x19, 0x11, 0x95, 0xb4, 0xdd, 0xe0, 0x8a, 0x9c, 0x3a, 0x0f, 0x74, 0x35, 0xc9, 0x0d, + 0x64, 0x3f, 0xee, 0xe4, 0xf2, 0x19, 0x35, 0x6b, 0xf2, 0x48, 0xa8, 0x07, 0x5e, 0xcf, 0xd5, 0xfe, + 0x5c, 0x11, 0x12, 0x41, 0xda, 0xe0, 0x91, 0x07, 0x92, 0x6d, 0x76, 0x36, 0x21, 0x86, 0xa4, 0x55, + 0x91, 0xcd, 0x58, 0x79, 0xb4, 0x4d, 0x04, 0x44, 0xa2, 0x6d, 0x22, 0xe4, 0x03, 0x18, 0x98, 0x72, + 0x45, 0xc1, 0x6b, 0xc2, 0xc0, 0x8b, 0xf2, 0xbc, 0xfd, 0x5b, 0xe4, 0x26, 0x2c, 0x30, 0x9b, 0x2e, + 0xd1, 0xdc, 0xd5, 0x48, 0x73, 0xf7, 0x6f, 0x99, 0xa2, 0x5c, 0x7b, 0x27, 0x78, 0x71, 0x4c, 0x74, + 0x62, 0xff, 0x16, 0x79, 0xe5, 0x74, 0xb6, 0xd6, 0x79, 0x61, 0x6b, 0x1d, 0xd8, 0x59, 0xbf, 0x1a, + 0xb1, 0xb3, 0xbe, 0x3a, 0x7b, 0xb4, 0xf8, 0x3b, 0x31, 0x8b, 0x2e, 0x19, 0x46, 0x1d, 0xfb, 0x79, + 0x06, 0x9e, 0x9e, 0x59, 0x83, 0x5c, 0x84, 0xbc, 0xde, 0xac, 0xb4, 0xc2, 0xf9, 0xa5, 0x7b, 0x46, + 0x40, 0xc8, 0x1e, 0x2c, 0x6e, 0x3b, 0xbe, 0xd7, 0xa1, 0xcb, 0x38, 0xf5, 0xe1, 0x26, 0x41, 0x36, + 0x40, 0x2f, 0x9f, 0x31, 0xc3, 0xba, 0xc4, 0x86, 0x35, 0xdc, 0x0b, 0x91, 0x4c, 0x62, 0xd9, 0x14, + 0xf5, 0x4a, 0x82, 0x60, 0xa2, 0x1a, 0xe5, 0x33, 0x09, 0x20, 0x79, 0x08, 0xc4, 0xb2, 0xca, 0x25, + 0x77, 0x34, 0xe6, 0x6a, 0x87, 0xb1, 0x17, 0x18, 0xee, 0xbe, 0xf8, 0x98, 0xb1, 0x4b, 0xd4, 0x2b, + 0x9f, 0x31, 0x53, 0xa8, 0xc5, 0xb7, 0xf9, 0xdb, 0x42, 0xde, 0x99, 0x3e, 0x08, 0x4f, 0x10, 0xb9, + 0xf7, 0x06, 0xe4, 0x9b, 0xc2, 0x4a, 0x44, 0x72, 0x80, 0x10, 0x16, 0x21, 0x66, 0x50, 0xaa, 0xfd, + 0x86, 0x22, 0xf4, 0x2c, 0x8f, 0x1f, 0x2c, 0x29, 0xd1, 0x5b, 0x77, 0x76, 0xa2, 0xb7, 0xee, 0x2f, + 0x99, 0xe8, 0x4d, 0xf3, 0xe0, 0xe6, 0xa9, 0x07, 0x96, 0x7c, 0x1a, 0x54, 0xcc, 0x89, 0xe5, 0x48, + 0x93, 0xc4, 0xf6, 0xd7, 0x5a, 0x10, 0xca, 0xbd, 0xcc, 0x13, 0x0f, 0x9a, 0xab, 0x9d, 0x68, 0x6d, + 0xed, 0x4f, 0x78, 0x08, 0xff, 0x4a, 0xb7, 0x19, 0x7b, 0x02, 0xf8, 0xa0, 0x3e, 0x33, 0x46, 0x64, + 0xb3, 0x3d, 0x2b, 0xe5, 0x24, 0x4d, 0x7e, 0x6b, 0xba, 0xeb, 0x8c, 0xb4, 0xf3, 0xfe, 0x20, 0x0b, + 0x17, 0x67, 0x55, 0x4f, 0xcd, 0x7a, 0xae, 0x3c, 0x59, 0xd6, 0xf3, 0x9b, 0x90, 0x67, 0xb0, 0xc0, + 0x21, 0x04, 0xe7, 0x96, 0x57, 0xa5, 0x73, 0x2b, 0x8a, 0xc9, 0xb3, 0x30, 0xaf, 0x97, 0xac, 0x30, + 0x11, 0x1f, 0x5a, 0x6e, 0x3b, 0x1d, 0x1f, 0x6d, 0x82, 0x79, 0x11, 0xf9, 0x72, 0x32, 0xf7, 0x24, + 0xcf, 0xc0, 0x77, 0x41, 0x1a, 0x90, 0x44, 0x76, 0x0d, 0x6c, 0x6f, 0x98, 0x0d, 0x82, 0x07, 0x58, + 0x37, 0x93, 0x79, 0x2c, 0x35, 0x98, 0x6f, 0x8e, 0x5c, 0xdf, 0x1d, 0xcb, 0x56, 0xd5, 0x43, 0x84, + 0x98, 0xbc, 0x84, 0xdb, 0x3c, 0x3b, 0x27, 0x2c, 0xc4, 0xc5, 0xbc, 0x1c, 0x76, 0x08, 0x8d, 0xa4, + 0x29, 0xd8, 0x94, 0x50, 0x68, 0x85, 0xaa, 0x33, 0xe9, 0x77, 0x8e, 0xda, 0x66, 0x95, 0x4b, 0x4e, + 0xac, 0x42, 0x0f, 0xa1, 0xb4, 0x83, 0xbe, 0x29, 0xa1, 0x68, 0xdf, 0x51, 0x60, 0x3d, 0xad, 0x1f, + 0xe4, 0x22, 0xe4, 0xfa, 0xa9, 0x69, 0x36, 0xfb, 0xcc, 0x33, 0xbf, 0x40, 0xff, 0xda, 0x07, 0x83, + 0xd1, 0xb1, 0x33, 0x96, 0x6d, 0xcf, 0x25, 0xb0, 0x09, 0xf4, 0xc7, 0x2e, 0xfe, 0x4f, 0x2e, 0x8b, + 0x23, 0x27, 0x9b, 0x48, 0xcc, 0x89, 0x7f, 0x34, 0x1d, 0xa0, 0xd2, 0x6d, 0x36, 0x86, 0x2c, 0xbb, + 0xc3, 0x4b, 0x90, 0xa3, 0xcd, 0x8a, 0xad, 0x5e, 0xba, 0x7e, 0xf4, 0x5a, 0x95, 0x23, 0xb1, 0x56, + 0xf9, 0xce, 0x71, 0xcf, 0x44, 0x64, 0xed, 0x1e, 0xac, 0x44, 0x31, 0x88, 0x11, 0x0d, 0xf0, 0x5b, + 0xb8, 0xad, 0x72, 0x4a, 0xdb, 0x83, 0x01, 0xf3, 0x7f, 0xda, 0x7e, 0xea, 0xe7, 0xef, 0x5d, 0x06, + 0xfa, 0x93, 0xd5, 0x49, 0x0b, 0x00, 0xac, 0x7d, 0x37, 0x03, 0xeb, 0x61, 0xc8, 0x05, 0xb1, 0x87, + 0x7e, 0x65, 0xfd, 0x7f, 0xf5, 0x88, 0x7f, 0xaa, 0x90, 0x1b, 0x93, 0x1d, 0x9c, 0xe1, 0x16, 0xb7, + 0x07, 0x9b, 0xd3, 0xf0, 0xc9, 0x73, 0xb0, 0x88, 0x51, 0xba, 0x86, 0x4e, 0xc7, 0x95, 0xd9, 0x6c, + 0x5f, 0x00, 0xcd, 0xb0, 0x5c, 0xfb, 0xa9, 0x02, 0x5b, 0xdc, 0x6b, 0xa7, 0xe6, 0x78, 0x7d, 0x7c, + 0xbf, 0xe9, 0xb8, 0x1f, 0x8e, 0xff, 0xfa, 0x5e, 0x84, 0x8f, 0x5d, 0x8b, 0x3a, 0x67, 0x25, 0xbe, + 0x36, 0xbd, 0xb7, 0xe4, 0x26, 0x46, 0x9e, 0xe3, 0xf6, 0x0d, 0x39, 0x16, 0x2f, 0xa4, 0x4f, 0x01, + 0x72, 0xbc, 0x10, 0xc4, 0xd0, 0xfe, 0x0f, 0x5c, 0x9a, 0xfd, 0x01, 0xf2, 0x25, 0x58, 0xc6, 0x54, + 0x6a, 0xed, 0xe1, 0xe1, 0xc8, 0xe9, 0xba, 0x42, 0xb3, 0x27, 0x14, 0xd0, 0x72, 0x19, 0x0b, 0xa4, + 0xc7, 0xe3, 0x57, 0x1c, 0x62, 0x92, 0x36, 0x5e, 0x29, 0xe2, 0x1a, 0x27, 0x53, 0xd3, 0xbe, 0xa9, + 0x00, 0x49, 0xd2, 0x20, 0x9f, 0x84, 0xa5, 0x76, 0xab, 0x64, 0x8d, 0x9d, 0xd1, 0xb8, 0x3c, 0x98, + 0x8c, 0x78, 0x14, 0x3b, 0x16, 0xce, 0x60, 0xdc, 0xb1, 0xd9, 0x4b, 0xdd, 0xd1, 0x60, 0x32, 0x32, + 0x23, 0x78, 0x98, 0xb2, 0xcb, 0x75, 0xdf, 0xea, 0x3a, 0x27, 0xd1, 0x94, 0x5d, 0x1c, 0x16, 0x49, + 0xd9, 0xc5, 0x61, 0xda, 0xbb, 0x0a, 0x5c, 0x10, 0x66, 0xab, 0xdd, 0x94, 0xb6, 0x94, 0x30, 0x68, + 0xcf, 0x48, 0x84, 0x4d, 0x9e, 0x25, 0xa1, 0xaf, 0x89, 0xb8, 0x56, 0xd8, 0x40, 0x14, 0xd5, 0x59, + 0x5d, 0xf2, 0x59, 0xc8, 0x59, 0xe3, 0xc1, 0xf0, 0x14, 0x81, 0xad, 0xd4, 0x60, 0x46, 0xc7, 0x83, + 0x21, 0x92, 0xc0, 0x9a, 0x9a, 0x0b, 0xeb, 0x72, 0xe3, 0x44, 0x8b, 0x49, 0x0d, 0x16, 0x78, 0x04, + 0xc3, 0x98, 0x45, 0xc8, 0x8c, 0x3e, 0x6d, 0xaf, 0x8a, 0xe8, 0x59, 0x3c, 0x6c, 0xaf, 0x29, 0x68, + 0x68, 0xbf, 0xa5, 0x40, 0x81, 0x0a, 0x36, 0x78, 0x29, 0xfd, 0xa0, 0x4b, 0x3a, 0x2a, 0x07, 0x0b, + 0x03, 0xa7, 0x80, 0xfc, 0xa9, 0x4e, 0xe3, 0x97, 0x61, 0x35, 0x56, 0x81, 0x68, 0x18, 0x37, 0xa5, + 0xe7, 0x75, 0x1c, 0x96, 0x01, 0x88, 0x19, 0x07, 0x45, 0x60, 0xda, 0xff, 0x53, 0x60, 0xbd, 0xf1, + 0xd6, 0xd8, 0x61, 0x0f, 0xea, 0xe6, 0xa4, 0x27, 0xf6, 0x3b, 0x15, 0xd6, 0x84, 0xfd, 0x33, 0x8b, + 0xe9, 0xc0, 0x84, 0x35, 0x0e, 0x33, 0x83, 0x52, 0x52, 0x86, 0x3c, 0x3f, 0x5f, 0x7c, 0x1e, 0x6d, + 0xf7, 0x92, 0xa4, 0x1b, 0x09, 0x09, 0x73, 0x24, 0xda, 0x13, 0x64, 0x61, 0xbc, 0x8e, 0x19, 0xd4, + 0xd6, 0xfe, 0x55, 0x81, 0x8d, 0x29, 0x75, 0xc8, 0x1b, 0x30, 0x87, 0xfe, 0xa6, 0x7c, 0xf6, 0x2e, + 0x4e, 0xf9, 0xc4, 0xb8, 0x73, 0xb4, 0x7f, 0x8b, 0x1d, 0x44, 0xc7, 0xf4, 0x87, 0xc9, 0x6a, 0x91, + 0x07, 0xb0, 0xa8, 0x77, 0xbb, 0xfc, 0x76, 0x96, 0x89, 0xdc, 0xce, 0xa6, 0x7c, 0xf1, 0x85, 0x00, + 0x9f, 0xdd, 0xce, 0x98, 0xe7, 0x53, 0xb7, 0x6b, 0x73, 0x5f, 0xda, 0x90, 0xde, 0xd6, 0xa7, 0x61, + 0x25, 0x8a, 0xfc, 0x44, 0xee, 0x7f, 0xef, 0x28, 0xa0, 0x46, 0xdb, 0xf0, 0xd1, 0xc4, 0xfd, 0x4a, + 0x9b, 0xe6, 0xc7, 0x2c, 0xaa, 0xdf, 0xc9, 0xc0, 0xb9, 0xd4, 0x11, 0x26, 0xcf, 0xc3, 0xbc, 0x3e, + 0x1c, 0x56, 0x76, 0xf8, 0xaa, 0xe2, 0x12, 0x12, 0x2a, 0xbd, 0x23, 0x97, 0x57, 0x86, 0x44, 0x5e, + 0x82, 0x3c, 0xb3, 0xdb, 0xd8, 0x11, 0x0c, 0x07, 0x03, 0x19, 0x71, 0xa3, 0x92, 0x68, 0xdc, 0x5b, + 0x81, 0x48, 0x76, 0x61, 0x85, 0x87, 0x00, 0x32, 0xdd, 0x43, 0xf7, 0xeb, 0x41, 0x02, 0x06, 0xcc, + 0x11, 0x21, 0x34, 0xe9, 0xf6, 0x88, 0x95, 0xc9, 0x41, 0x70, 0xa2, 0xb5, 0x48, 0x15, 0x54, 0xa4, + 0x29, 0x53, 0x62, 0xc1, 0x77, 0x31, 0x28, 0x13, 0x6b, 0xc4, 0x14, 0x5a, 0x89, 0x9a, 0xc1, 0x74, + 0xe9, 0xbe, 0xef, 0x1d, 0xf6, 0x8f, 0xdd, 0xfe, 0xf8, 0xa3, 0x9b, 0xae, 0xf0, 0x1b, 0xa7, 0x9a, + 0xae, 0xdf, 0xcb, 0xb1, 0xcd, 0x1c, 0xaf, 0x46, 0x25, 0x1a, 0x29, 0xde, 0x3a, 0x4a, 0x34, 0xf4, + 0x7e, 0xc6, 0x83, 0xdc, 0xec, 0xc0, 0x02, 0x0b, 0x3e, 0x24, 0x76, 0xc6, 0xd3, 0xa9, 0x4d, 0x60, + 0x38, 0xfb, 0xb7, 0x98, 0xf8, 0xc2, 0x1c, 0x5f, 0x7d, 0x53, 0x54, 0x25, 0xfb, 0x50, 0x28, 0xf5, + 0x5c, 0xa7, 0x3f, 0x19, 0xb6, 0x4e, 0xf7, 0x68, 0xbc, 0xc9, 0xfb, 0xb2, 0xd4, 0x61, 0xd5, 0xf0, + 0xb1, 0x19, 0x39, 0xb9, 0x4c, 0x88, 0xb4, 0x02, 0x5f, 0xb8, 0x1c, 0x2a, 0x5e, 0x5f, 0x9c, 0x31, + 0x3e, 0x71, 0x20, 0xd6, 0x8b, 0x3a, 0x7a, 0x72, 0x67, 0x39, 0x1b, 0x56, 0xaa, 0x8e, 0x3f, 0x6e, + 0x8d, 0x9c, 0xbe, 0x8f, 0x41, 0x4b, 0x4f, 0x11, 0xd4, 0xed, 0x82, 0x48, 0xc8, 0x8d, 0x2a, 0xd3, + 0x71, 0x50, 0x95, 0x29, 0x64, 0xa3, 0xe4, 0xa8, 0xbc, 0xb4, 0xeb, 0xf5, 0x9d, 0x9e, 0xf7, 0x0d, + 0xe1, 0x32, 0xcc, 0xe4, 0xa5, 0x03, 0x01, 0x34, 0xc3, 0x72, 0xed, 0x8b, 0x89, 0x79, 0x63, 0xad, + 0x2c, 0xc0, 0x02, 0x0f, 0x28, 0xc1, 0x02, 0x2c, 0x34, 0x8d, 0xfa, 0x4e, 0xa5, 0xbe, 0xa7, 0x2a, + 0x64, 0x05, 0xa0, 0x69, 0x36, 0x4a, 0x86, 0x65, 0xd1, 0xdf, 0x19, 0xfa, 0x9b, 0x47, 0x5f, 0xd8, + 0x6d, 0x57, 0xd5, 0xac, 0x14, 0x80, 0x21, 0xa7, 0xfd, 0x44, 0x81, 0xf3, 0xe9, 0x53, 0x49, 0x5a, + 0x80, 0x21, 0x38, 0xb8, 0xf9, 0xc0, 0x27, 0x67, 0xce, 0x7b, 0x2a, 0x38, 0x1e, 0xca, 0x63, 0xcc, + 0x42, 0x44, 0x64, 0xc4, 0xdb, 0x17, 0xf3, 0x39, 0xf5, 0xba, 0x66, 0xc6, 0xeb, 0x6a, 0x25, 0xd8, + 0x9c, 0x46, 0x23, 0xda, 0xd5, 0x55, 0x28, 0xe8, 0xcd, 0x66, 0xb5, 0x52, 0xd2, 0x5b, 0x95, 0x46, + 0x5d, 0x55, 0xc8, 0x22, 0xcc, 0xed, 0x99, 0x8d, 0x76, 0x53, 0xcd, 0x68, 0xdf, 0x53, 0x60, 0xb9, + 0x12, 0xda, 0x03, 0x7e, 0xd0, 0xcd, 0xf7, 0x7a, 0x64, 0xf3, 0x6d, 0x06, 0xc1, 0x6a, 0x82, 0x0f, + 0x9c, 0x6a, 0xe7, 0xbd, 0x9f, 0x81, 0xb5, 0x44, 0x1d, 0x62, 0xc1, 0x82, 0x7e, 0xcf, 0x6a, 0x54, + 0x76, 0x4a, 0xbc, 0x65, 0x97, 0x43, 0x43, 0x36, 0x4c, 0x5f, 0x96, 0xf8, 0x0a, 0x73, 0xf0, 0x7e, + 0xe4, 0xdb, 0x03, 0xaf, 0x2b, 0xe5, 0x32, 0x2e, 0x9f, 0x31, 0x05, 0x25, 0x3c, 0xc9, 0xbe, 0x31, + 0x19, 0xb9, 0x48, 0x36, 0x13, 0xd1, 0xeb, 0x06, 0xf0, 0x24, 0x61, 0xf4, 0xac, 0x71, 0x68, 0x79, + 0x92, 0x74, 0x48, 0x8f, 0xd4, 0x61, 0x7e, 0xcf, 0x1b, 0x97, 0x27, 0x0f, 0xf9, 0xfe, 0xbd, 0x14, + 0x26, 0xb3, 0x2a, 0x4f, 0x1e, 0x26, 0xc9, 0xa2, 0xca, 0x92, 0x05, 0x63, 0x8a, 0x90, 0xe4, 0x54, + 0xe2, 0x3e, 0xa9, 0xb9, 0x27, 0xf2, 0x49, 0xdd, 0x5e, 0x86, 0x02, 0xbf, 0x43, 0xe1, 0xf5, 0xe4, + 0x47, 0x0a, 0x6c, 0x4e, 0x1b, 0x39, 0x7a, 0x2d, 0x8b, 0xc6, 0x9e, 0x38, 0x1f, 0x64, 0x3b, 0x89, + 0x06, 0x9d, 0x10, 0x68, 0xe4, 0x4d, 0x28, 0x30, 0x2b, 0x2d, 0xeb, 0xa5, 0xb6, 0x59, 0xe1, 0xcb, + 0xf5, 0xe9, 0x7f, 0x7a, 0xef, 0xf2, 0x06, 0x37, 0xec, 0xf2, 0x5f, 0xb2, 0x27, 0x23, 0x2f, 0x92, + 0x19, 0x42, 0xae, 0x41, 0xa5, 0x68, 0x67, 0xd2, 0xf5, 0x5c, 0x71, 0x87, 0x10, 0xfe, 0xf9, 0x1c, + 0x26, 0x9f, 0x69, 0x02, 0xa6, 0x7d, 0x5b, 0x81, 0xad, 0xe9, 0xd3, 0x44, 0xcf, 0xc9, 0x16, 0x33, + 0x76, 0x13, 0x1e, 0xf2, 0x78, 0x4e, 0x06, 0x16, 0x71, 0x32, 0x4d, 0x81, 0x48, 0x2b, 0x71, 0x0d, + 0x97, 0x50, 0x92, 0xc8, 0xe9, 0xcd, 0xa3, 0x95, 0x04, 0xa2, 0x76, 0x1f, 0x36, 0xa6, 0x4c, 0x2a, + 0xf9, 0x4c, 0x6a, 0x0e, 0x25, 0x74, 0x20, 0x93, 0x73, 0x28, 0x45, 0x92, 0xf1, 0x49, 0x70, 0xed, + 0x9f, 0x33, 0x70, 0x9e, 0xee, 0xae, 0x9e, 0xeb, 0xfb, 0x7a, 0x98, 0x6e, 0x98, 0x72, 0xc5, 0x57, + 0x60, 0xfe, 0xe8, 0xc9, 0x54, 0xc5, 0x0c, 0x9d, 0x10, 0xc0, 0x13, 0x4b, 0xb8, 0x2d, 0xd1, 0xff, + 0xc9, 0x15, 0x90, 0x73, 0xd5, 0x67, 0x31, 0x5a, 0x6d, 0x66, 0x53, 0x31, 0x17, 0x87, 0x41, 0x5a, + 0xe9, 0x57, 0x61, 0x0e, 0xf5, 0x29, 0xfc, 0xec, 0x10, 0x32, 0x7f, 0x7a, 0xeb, 0x50, 0xdb, 0x62, + 0xb2, 0x0a, 0xe4, 0x13, 0x00, 0x61, 0xa2, 0x0f, 0x7e, 0x38, 0x08, 0x3d, 0x43, 0x90, 0xeb, 0xc3, + 0x5c, 0x3c, 0x3e, 0x70, 0x78, 0xf6, 0x8c, 0x22, 0xac, 0x89, 0x11, 0x1f, 0x8a, 0x20, 0x97, 0xfc, + 0x15, 0x73, 0x95, 0x15, 0x54, 0x86, 0x22, 0xd0, 0xe5, 0xd5, 0x44, 0xbe, 0x6d, 0x8c, 0x75, 0x1d, + 0x4b, 0xaa, 0x7d, 0x35, 0x91, 0x54, 0x3b, 0xcf, 0xb0, 0xe4, 0xcc, 0xd9, 0xda, 0xdf, 0x65, 0x60, + 0xf1, 0x1e, 0x95, 0xca, 0x50, 0xd7, 0x30, 0x5b, 0x77, 0x71, 0x1b, 0x0a, 0xd5, 0x81, 0xc3, 0x9f, + 0x8b, 0xb8, 0xb7, 0x0f, 0x73, 0xd1, 0xef, 0x0d, 0x1c, 0xf1, 0xf2, 0xe4, 0x9b, 0x32, 0xd2, 0x63, + 0xc2, 0x0b, 0xdc, 0x81, 0x79, 0xf6, 0x7c, 0xc7, 0xd5, 0x68, 0x42, 0x2e, 0x0f, 0x5a, 0xf4, 0x02, + 0x2b, 0x96, 0x5e, 0x38, 0xd8, 0x13, 0xa0, 0x2c, 0x24, 0xf2, 0x90, 0xbd, 0x92, 0x66, 0x65, 0xee, + 0x74, 0x9a, 0x15, 0x29, 0x34, 0xe1, 0xfc, 0x69, 0x42, 0x13, 0x6e, 0xbd, 0x06, 0x05, 0xa9, 0x3d, + 0x4f, 0x24, 0xa6, 0x7f, 0x2b, 0x03, 0xcb, 0xd8, 0xab, 0xc0, 0x96, 0xe7, 0x57, 0x53, 0x4f, 0xf4, + 0x7a, 0x44, 0x4f, 0xb4, 0x29, 0xcf, 0x17, 0xeb, 0xd9, 0x0c, 0x05, 0xd1, 0x1d, 0x58, 0x4b, 0x20, + 0x92, 0x97, 0x61, 0x8e, 0x36, 0x5f, 0xdc, 0xab, 0xd5, 0xf8, 0x0a, 0x08, 0xc3, 0x58, 0xd3, 0x8e, + 0xfb, 0x26, 0xc3, 0xd6, 0xfe, 0x4d, 0x81, 0x25, 0x9e, 0x45, 0xa6, 0x7f, 0x30, 0x78, 0xec, 0x70, + 0x5e, 0x8f, 0x0f, 0x27, 0x0b, 0x96, 0xc3, 0x87, 0xf3, 0xbf, 0x7a, 0x10, 0x5f, 0x8b, 0x0c, 0xe2, + 0x46, 0x10, 0xd4, 0x52, 0x74, 0x67, 0xc6, 0x18, 0xfe, 0x10, 0xc3, 0x3c, 0x47, 0x11, 0xc9, 0x97, + 0x61, 0xb1, 0xee, 0x3e, 0x8a, 0x5c, 0x4f, 0xaf, 0x4f, 0x21, 0xfa, 0x42, 0x80, 0xc8, 0xf6, 0x14, + 0x9e, 0xec, 0x7d, 0xf7, 0x91, 0x9d, 0x78, 0x39, 0x0c, 0x49, 0xd2, 0x1b, 0x6a, 0xb4, 0xda, 0x93, + 0x2c, 0x7d, 0xee, 0x7a, 0x8c, 0xf1, 0x9f, 0xbe, 0x93, 0x05, 0x08, 0xbd, 0x36, 0xe9, 0x06, 0x8c, + 0x18, 0x4d, 0x08, 0xcd, 0x3e, 0x82, 0xe4, 0x35, 0x2e, 0x6c, 0x29, 0xae, 0x73, 0x0d, 0x74, 0x66, + 0x7a, 0xd0, 0x51, 0xd4, 0x45, 0x97, 0xb8, 0x9b, 0x60, 0xd7, 0xed, 0x39, 0x8c, 0xb7, 0x67, 0xb7, + 0xaf, 0x62, 0x8c, 0xe9, 0x00, 0x3a, 0x25, 0x7b, 0x38, 0x3a, 0x13, 0xee, 0x50, 0x84, 0x84, 0x27, + 0x74, 0xee, 0xc9, 0x3c, 0xa1, 0x9b, 0xb0, 0xe8, 0xf5, 0xdf, 0x76, 0xfb, 0xe3, 0xc1, 0xe8, 0x04, + 0xd5, 0xee, 0xa1, 0x3e, 0x8f, 0x0e, 0x41, 0x45, 0x94, 0xb1, 0x79, 0xc0, 0x33, 0x37, 0xc0, 0x97, + 0xa7, 0x21, 0x00, 0x06, 0x9e, 0xdc, 0x73, 0xea, 0xfc, 0x9d, 0x5c, 0x7e, 0x5e, 0x5d, 0xb8, 0x93, + 0xcb, 0xe7, 0xd5, 0xc5, 0x3b, 0xb9, 0xfc, 0xa2, 0x0a, 0xa6, 0xf4, 0x66, 0x16, 0xbc, 0x89, 0x49, + 0xcf, 0x58, 0xd1, 0x27, 0x2a, 0xed, 0x17, 0x19, 0x20, 0xc9, 0x66, 0x90, 0xd7, 0xa1, 0xc0, 0x18, + 0xac, 0x3d, 0xf2, 0xbf, 0xc6, 0x1d, 0x41, 0x58, 0x14, 0x2d, 0x09, 0x2c, 0x47, 0xd1, 0x62, 0x60, + 0xd3, 0xff, 0x5a, 0x8f, 0x7c, 0x09, 0xce, 0xe2, 0xf0, 0x0e, 0xdd, 0x91, 0x37, 0xe8, 0xda, 0x18, + 0xf2, 0xd8, 0xe9, 0xf1, 0x4c, 0x9f, 0xcf, 0x63, 0x4a, 0xea, 0x64, 0xf1, 0x94, 0x69, 0x40, 0xe7, + 0xcc, 0x26, 0x62, 0x36, 0x19, 0x22, 0x69, 0x81, 0x2a, 0xd7, 0x3f, 0x98, 0xf4, 0x7a, 0x7c, 0x66, + 0x8b, 0xf4, 0x46, 0x1f, 0x2f, 0x9b, 0x42, 0x78, 0x25, 0x24, 0xbc, 0x3b, 0xe9, 0xf5, 0xc8, 0x2b, + 0x00, 0x83, 0xbe, 0x7d, 0xec, 0xf9, 0x3e, 0x7b, 0xcc, 0x09, 0xfc, 0xc8, 0x43, 0xa8, 0x3c, 0x19, + 0x83, 0x7e, 0x8d, 0x01, 0xc9, 0xff, 0x02, 0x0c, 0xbe, 0x81, 0x51, 0x69, 0x98, 0x35, 0x12, 0x4f, + 0xc6, 0x23, 0x80, 0x51, 0xb7, 0xf5, 0x43, 0xd7, 0xf2, 0xbe, 0x21, 0x9c, 0x70, 0xbe, 0x00, 0x6b, + 0xdc, 0x5e, 0xfa, 0x9e, 0x37, 0x3e, 0xe2, 0x57, 0x89, 0x0f, 0x72, 0x0f, 0x91, 0xee, 0x12, 0x7f, + 0x95, 0x03, 0xd0, 0xef, 0x59, 0x22, 0xe0, 0xdb, 0x4d, 0x98, 0xa3, 0x17, 0x24, 0xa1, 0x68, 0x41, + 0x35, 0x35, 0xd2, 0x95, 0xd5, 0xd4, 0x88, 0x41, 0x77, 0xa3, 0x89, 0xee, 0x0e, 0x42, 0xc9, 0x82, + 0xbb, 0x91, 0x79, 0x40, 0x44, 0x02, 0x6e, 0x73, 0x2c, 0x52, 0x05, 0x08, 0x43, 0xb0, 0x71, 0x91, + 0x7f, 0x2d, 0x8c, 0x65, 0xc4, 0x0b, 0x78, 0xd2, 0x8f, 0x30, 0x8c, 0x9b, 0xbc, 0x7c, 0x42, 0x34, + 0x72, 0x17, 0x72, 0x2d, 0x27, 0xf0, 0x92, 0x9e, 0x12, 0x98, 0xee, 0x19, 0x9e, 0x89, 0x35, 0x0c, + 0x4e, 0xb7, 0x32, 0x76, 0x22, 0x09, 0xab, 0x91, 0x08, 0x31, 0x60, 0x9e, 0x67, 0xd9, 0x9f, 0x12, + 0xd0, 0x94, 0x27, 0xd9, 0xe7, 0x61, 0xcc, 0x11, 0x28, 0xcb, 0x14, 0x3c, 0x9f, 0xfe, 0x6d, 0xc8, + 0x5a, 0x56, 0x8d, 0x87, 0x63, 0x59, 0x0e, 0xaf, 0x5f, 0x96, 0x55, 0x63, 0xef, 0xbe, 0xbe, 0x7f, + 0x2c, 0x55, 0xa3, 0xc8, 0xe4, 0x53, 0x50, 0x90, 0x84, 0x62, 0x1e, 0xc8, 0x08, 0xc7, 0x40, 0xf2, + 0x43, 0x93, 0x99, 0x86, 0x84, 0x4d, 0xaa, 0xa0, 0xde, 0x9d, 0x3c, 0x74, 0xf5, 0xe1, 0x10, 0x1d, + 0x54, 0xdf, 0x76, 0x47, 0x4c, 0x6c, 0xcb, 0x87, 0x11, 0xc0, 0xd1, 0x7b, 0xa5, 0x2b, 0x4a, 0x65, + 0x65, 0x53, 0xbc, 0x26, 0x69, 0xc2, 0x9a, 0xe5, 0x8e, 0x27, 0x43, 0x66, 0x5f, 0xb3, 0x3b, 0x18, + 0xd1, 0xfb, 0x0d, 0x0b, 0x7b, 0x84, 0xc1, 0x92, 0x7d, 0x5a, 0x28, 0x8c, 0x9a, 0x0e, 0x06, 0xa3, + 0xd8, 0x5d, 0x27, 0x59, 0x59, 0x73, 0xe5, 0x29, 0xa7, 0xa7, 0x6a, 0xf4, 0xd6, 0x84, 0xa7, 0xaa, + 0xb8, 0x35, 0x85, 0x77, 0xa5, 0x4f, 0xa4, 0x84, 0xe6, 0xc3, 0x97, 0x41, 0x29, 0x34, 0x5f, 0x24, + 0x20, 0xdf, 0xbb, 0x39, 0x29, 0x3a, 0x2c, 0x9f, 0x8b, 0x37, 0x00, 0xee, 0x0c, 0xbc, 0x7e, 0xcd, + 0x1d, 0x1f, 0x0d, 0xba, 0x52, 0x84, 0xc0, 0xc2, 0x57, 0x07, 0x5e, 0xdf, 0x3e, 0x46, 0xf0, 0x2f, + 0xde, 0xbb, 0x2c, 0x21, 0x99, 0xd2, 0xff, 0xe4, 0xe3, 0xb0, 0x48, 0x7f, 0xb5, 0x42, 0x2b, 0x21, + 0xa6, 0x93, 0xc5, 0xda, 0x2c, 0x87, 0x4a, 0x88, 0x40, 0x5e, 0xc3, 0xac, 0x41, 0xde, 0x70, 0x2c, + 0x09, 0xaf, 0x22, 0x45, 0x90, 0x37, 0x1c, 0xc7, 0x03, 0x7e, 0x4b, 0xc8, 0xa4, 0x1c, 0x34, 0x5d, + 0x24, 0xfa, 0xe2, 0xc9, 0x89, 0x50, 0xf1, 0xc8, 0xd7, 0x9a, 0x2d, 0x22, 0x0d, 0xcb, 0x19, 0x9c, + 0x63, 0xd5, 0xb0, 0x11, 0x56, 0x79, 0x87, 0xbd, 0x14, 0x71, 0xa1, 0x96, 0x35, 0xc2, 0x3f, 0xea, + 0xda, 0x1d, 0x04, 0x47, 0x1a, 0x11, 0x20, 0x93, 0x6d, 0x58, 0x65, 0x32, 0x7e, 0x90, 0x30, 0x94, + 0x8b, 0xb8, 0xc8, 0xdb, 0xc2, 0x8c, 0xa2, 0xf2, 0xe7, 0x63, 0x15, 0xc8, 0x2e, 0xcc, 0xe1, 0x5d, + 0x93, 0x7b, 0x43, 0x5c, 0x90, 0xd5, 0x04, 0xf1, 0x7d, 0x84, 0x7c, 0x05, 0x15, 0x04, 0x32, 0x5f, + 0x41, 0x54, 0xf2, 0x79, 0x00, 0xa3, 0x3f, 0x1a, 0xf4, 0x7a, 0x18, 0x0b, 0x3b, 0x8f, 0x57, 0xa9, + 0xa7, 0xa3, 0xfb, 0x11, 0xa9, 0x84, 0x48, 0x3c, 0x6e, 0x23, 0xfe, 0xb6, 0x63, 0x11, 0xb3, 0x25, + 0x5a, 0x5a, 0x05, 0xe6, 0xd9, 0x66, 0xc4, 0xb8, 0xf2, 0x3c, 0x53, 0x8e, 0x14, 0x95, 0x9c, 0xc5, + 0x95, 0xe7, 0xf0, 0x64, 0x5c, 0x79, 0xa9, 0x82, 0x76, 0x17, 0xd6, 0xd3, 0x3a, 0x16, 0xb9, 0x1d, + 0x2b, 0xa7, 0xbd, 0x1d, 0xff, 0x20, 0x0b, 0x4b, 0x48, 0x4d, 0x70, 0x61, 0x1d, 0x96, 0xad, 0xc9, + 0xc3, 0x20, 0xe8, 0x9a, 0xe0, 0xc6, 0xd8, 0x3e, 0x5f, 0x2e, 0x90, 0xdf, 0xf0, 0x22, 0x35, 0x88, + 0x01, 0x2b, 0xe2, 0x24, 0xd8, 0x13, 0x9e, 0x03, 0x41, 0x48, 0x77, 0xe1, 0x50, 0x91, 0x4c, 0x98, + 0x1c, 0xab, 0x14, 0x9e, 0x07, 0xd9, 0x27, 0x39, 0x0f, 0x72, 0xa7, 0x3a, 0x0f, 0x1e, 0xc0, 0x92, + 0xf8, 0x1a, 0x72, 0xf2, 0xb9, 0x0f, 0xc6, 0xc9, 0x23, 0xc4, 0x48, 0x35, 0xe0, 0xe8, 0xf3, 0x33, + 0x39, 0x3a, 0x3e, 0x8c, 0x8a, 0x5d, 0x36, 0x44, 0x58, 0x92, 0xb1, 0x63, 0x46, 0xd1, 0xbd, 0x52, + 0xf3, 0x97, 0x38, 0x25, 0x5f, 0x86, 0xc5, 0xea, 0x40, 0xbc, 0x89, 0x49, 0x8f, 0x11, 0x3d, 0x01, + 0x94, 0xc5, 0x85, 0x00, 0x33, 0x38, 0xdd, 0xb2, 0x1f, 0xc6, 0xe9, 0xf6, 0x1a, 0x00, 0x77, 0x49, + 0x09, 0x33, 0x01, 0xe2, 0x96, 0x11, 0xb1, 0x63, 0xa2, 0x6f, 0x22, 0x12, 0x32, 0xe5, 0x4e, 0xdc, + 0xdc, 0x46, 0xef, 0x74, 0x06, 0x93, 0xfe, 0x38, 0x92, 0x3a, 0x5b, 0xf8, 0x16, 0x3b, 0xbc, 0x4c, + 0x66, 0x0f, 0xb1, 0x6a, 0x1f, 0xee, 0x84, 0x90, 0xcf, 0x05, 0xc6, 0x8f, 0x0b, 0xb3, 0x46, 0x48, + 0x4b, 0x8c, 0xd0, 0x54, 0x93, 0x47, 0xed, 0x27, 0x8a, 0x9c, 0x4f, 0xe3, 0x97, 0x98, 0xea, 0x57, + 0x01, 0x02, 0xa3, 0x04, 0x31, 0xd7, 0xec, 0xbe, 0x14, 0x40, 0xe5, 0x51, 0x0e, 0x71, 0xa5, 0xde, + 0x64, 0x3f, 0xac, 0xde, 0xb4, 0xa0, 0xd0, 0x78, 0x6b, 0xec, 0x84, 0x56, 0x2c, 0x60, 0x05, 0x92, + 0x2c, 0x72, 0xa6, 0xec, 0xf6, 0x35, 0x3c, 0x1b, 0x42, 0x39, 0x78, 0x8a, 0x08, 0x2c, 0x55, 0xd4, + 0xfe, 0x43, 0x81, 0x55, 0x39, 0x20, 0xc2, 0x49, 0xbf, 0x43, 0x3e, 0xc3, 0xc2, 0xfb, 0x2a, 0x91, + 0x2b, 0x8b, 0x84, 0x44, 0x59, 0xee, 0x49, 0xbf, 0xc3, 0x04, 0x20, 0xe7, 0x91, 0xdc, 0x58, 0x5a, + 0x91, 0x3c, 0x84, 0xa5, 0xe6, 0xa0, 0xd7, 0xa3, 0x62, 0xcd, 0xe8, 0x6d, 0x7e, 0x01, 0xa0, 0x84, + 0xe2, 0x4f, 0x23, 0xa2, 0x41, 0xdb, 0xcf, 0xf2, 0x7b, 0xee, 0xc6, 0x90, 0xf2, 0x7b, 0x8f, 0xd7, + 0x0b, 0xc9, 0xbe, 0x83, 0xae, 0x81, 0x32, 0xcd, 0xf0, 0x6c, 0x8a, 0xe6, 0x85, 0x90, 0x5b, 0x49, + 0x8b, 0xb1, 0x9d, 0x33, 0xce, 0x26, 0xed, 0x67, 0x0a, 0x90, 0x64, 0xd7, 0x64, 0xd6, 0xa7, 0xfc, + 0x37, 0x88, 0xc2, 0x31, 0x11, 0x32, 0xf7, 0x24, 0x22, 0xa4, 0xf6, 0x7d, 0x05, 0xd6, 0xd3, 0xc6, + 0x81, 0x9e, 0x20, 0xf2, 0x91, 0x12, 0x1c, 0x68, 0x78, 0x82, 0xc8, 0xa7, 0x50, 0xf4, 0x58, 0x8b, + 0x55, 0x8a, 0x37, 0x2e, 0xf3, 0x24, 0x8d, 0x2b, 0xfe, 0xb6, 0x02, 0xab, 0x15, 0xbd, 0xc6, 0xd3, + 0x88, 0xb0, 0x67, 0xaa, 0x2b, 0xf0, 0x74, 0x45, 0xaf, 0xd9, 0xcd, 0x46, 0xb5, 0x52, 0xba, 0x6f, + 0xa7, 0x46, 0x07, 0x7f, 0x1a, 0x9e, 0x4a, 0xa2, 0x84, 0xcf, 0x59, 0x17, 0x61, 0x33, 0x59, 0x2c, + 0x22, 0x88, 0xa7, 0x57, 0x16, 0xc1, 0xc6, 0xb3, 0xc5, 0x37, 0x61, 0x55, 0x44, 0xcb, 0x6e, 0x55, + 0x2d, 0xcc, 0xc7, 0xb1, 0x0a, 0x85, 0x7d, 0xc3, 0xac, 0xec, 0xde, 0xb7, 0x77, 0xdb, 0xd5, 0xaa, + 0x7a, 0x86, 0x2c, 0xc3, 0x22, 0x07, 0x94, 0x74, 0x55, 0x21, 0x4b, 0x90, 0xaf, 0xd4, 0x2d, 0xa3, + 0xd4, 0x36, 0x0d, 0x35, 0x53, 0x7c, 0x13, 0x56, 0x9a, 0x23, 0xef, 0x6d, 0x67, 0xec, 0xde, 0x75, + 0x4f, 0xf0, 0x35, 0x6a, 0x01, 0xb2, 0xa6, 0x7e, 0x4f, 0x3d, 0x43, 0x00, 0xe6, 0x9b, 0x77, 0x4b, + 0xd6, 0xad, 0x5b, 0xaa, 0x42, 0x0a, 0xb0, 0xb0, 0x57, 0x6a, 0xda, 0x77, 0x6b, 0x96, 0x9a, 0xa1, + 0x3f, 0xf4, 0x7b, 0x16, 0xfe, 0xc8, 0x16, 0x5f, 0x84, 0x35, 0x94, 0xba, 0xaa, 0x9e, 0x3f, 0x76, + 0xfb, 0xee, 0x08, 0xdb, 0xb0, 0x04, 0x79, 0xcb, 0xa5, 0xec, 0x72, 0xec, 0xb2, 0x06, 0xd4, 0x26, + 0xbd, 0xb1, 0x37, 0xec, 0xb9, 0x5f, 0x57, 0x95, 0xe2, 0x6b, 0xb0, 0x6a, 0x0e, 0x26, 0x63, 0xaf, + 0x7f, 0x68, 0x8d, 0x29, 0xc6, 0xe1, 0x09, 0x39, 0x07, 0x6b, 0xed, 0xba, 0x5e, 0xdb, 0xae, 0xec, + 0xb5, 0x1b, 0x6d, 0xcb, 0xae, 0xe9, 0xad, 0x52, 0x99, 0xbd, 0x85, 0xd5, 0x1a, 0x56, 0xcb, 0x36, + 0x8d, 0x92, 0x51, 0x6f, 0xa9, 0x4a, 0xf1, 0xbb, 0xa8, 0x40, 0xea, 0x0c, 0xfa, 0xdd, 0x5d, 0xa7, + 0x33, 0x1e, 0x8c, 0xb0, 0xc1, 0x1a, 0x5c, 0xb2, 0x8c, 0x52, 0xa3, 0xbe, 0x63, 0xef, 0xea, 0xa5, + 0x56, 0xc3, 0x4c, 0x0b, 0x4f, 0xbf, 0x05, 0xe7, 0x53, 0x70, 0x1a, 0xad, 0xa6, 0xaa, 0x90, 0xcb, + 0x70, 0x21, 0xa5, 0xec, 0x9e, 0xb1, 0xad, 0xb7, 0x5b, 0xe5, 0xba, 0x9a, 0x99, 0x52, 0xd9, 0xb2, + 0x1a, 0x6a, 0xb6, 0xf8, 0xff, 0x15, 0x58, 0x69, 0xfb, 0xdc, 0xae, 0xbe, 0x8d, 0x5e, 0xc4, 0xcf, + 0xc0, 0xc5, 0xb6, 0x65, 0x98, 0x76, 0xab, 0x71, 0xd7, 0xa8, 0xdb, 0x6d, 0x4b, 0xdf, 0x8b, 0xb7, + 0xe6, 0x32, 0x5c, 0x90, 0x30, 0x4c, 0xa3, 0xd4, 0xd8, 0x37, 0x4c, 0xbb, 0xa9, 0x5b, 0xd6, 0xbd, + 0x86, 0xb9, 0xa3, 0x2a, 0xf4, 0x8b, 0x29, 0x08, 0xb5, 0x5d, 0x9d, 0xb5, 0x26, 0x52, 0x56, 0x37, + 0xee, 0xe9, 0x55, 0x7b, 0xbb, 0xd1, 0x52, 0xb3, 0xc5, 0x1a, 0x15, 0x62, 0x30, 0x48, 0x34, 0x33, + 0x9f, 0xcc, 0x43, 0xae, 0xde, 0xa8, 0x1b, 0xf1, 0x17, 0xd4, 0x25, 0xc8, 0xeb, 0xcd, 0xa6, 0xd9, + 0xd8, 0xc7, 0x25, 0x06, 0x30, 0xbf, 0x63, 0xd4, 0x69, 0xcb, 0xb2, 0xb4, 0xa4, 0x69, 0x36, 0x6a, + 0x8d, 0x96, 0xb1, 0xa3, 0xe6, 0x8a, 0xa6, 0xe0, 0x2f, 0x82, 0x68, 0x67, 0xc0, 0x9e, 0x2b, 0x77, + 0x8c, 0x5d, 0xbd, 0x5d, 0x6d, 0xf1, 0x29, 0xba, 0x6f, 0x9b, 0xc6, 0xe7, 0xda, 0x86, 0xd5, 0xb2, + 0x54, 0x85, 0xa8, 0xb0, 0x54, 0x37, 0x8c, 0x1d, 0xcb, 0x36, 0x8d, 0xfd, 0x8a, 0x71, 0x4f, 0xcd, + 0x50, 0x9a, 0xec, 0x7f, 0xfa, 0x85, 0xe2, 0xbb, 0x0a, 0x10, 0x16, 0x60, 0x5b, 0x64, 0x6d, 0xc2, + 0x15, 0x73, 0x09, 0xb6, 0xca, 0x74, 0xaa, 0xb1, 0x6b, 0xb5, 0xc6, 0x4e, 0x7c, 0xc8, 0xce, 0x03, + 0x89, 0x95, 0x37, 0x76, 0x77, 0x55, 0x85, 0x5c, 0x80, 0xb3, 0x31, 0xf8, 0x8e, 0xd9, 0x68, 0xaa, + 0x99, 0xad, 0x4c, 0x5e, 0x21, 0x1b, 0x89, 0xc2, 0xbb, 0x86, 0xd1, 0x54, 0xb3, 0x74, 0x8a, 0x62, + 0x05, 0x62, 0x4b, 0xb0, 0xea, 0xb9, 0xe2, 0xb7, 0x15, 0x38, 0xcf, 0x9a, 0x29, 0xf6, 0x57, 0xd0, + 0xd4, 0x8b, 0xb0, 0xc9, 0xd3, 0x06, 0xa4, 0x35, 0x74, 0x1d, 0xd4, 0x48, 0x29, 0x6b, 0xe6, 0x39, + 0x58, 0x8b, 0x40, 0xb1, 0x1d, 0x19, 0xca, 0x3d, 0x22, 0xe0, 0x6d, 0xc3, 0x6a, 0xd9, 0xc6, 0xee, + 0x6e, 0xc3, 0x6c, 0xb1, 0x86, 0x64, 0x8b, 0x1a, 0xac, 0x95, 0xdc, 0xd1, 0x98, 0xde, 0x2f, 0xfb, + 0xbe, 0x37, 0xe8, 0x63, 0x13, 0x96, 0x61, 0xd1, 0xf8, 0x7c, 0xcb, 0xa8, 0x5b, 0x95, 0x46, 0x5d, + 0x3d, 0x53, 0xbc, 0x18, 0xc3, 0x11, 0xfb, 0xd8, 0xb2, 0xca, 0xea, 0x99, 0xa2, 0x03, 0xcb, 0xc2, + 0xba, 0x9c, 0xad, 0x8a, 0x4b, 0xb0, 0x25, 0xd6, 0x1a, 0x72, 0x94, 0x78, 0x17, 0x36, 0x61, 0x3d, + 0x59, 0x6e, 0xb4, 0x54, 0x85, 0xce, 0x42, 0xac, 0x84, 0xc2, 0x33, 0xc5, 0xff, 0xab, 0xc0, 0x72, + 0xf0, 0x32, 0x84, 0xba, 0xe8, 0xcb, 0x70, 0xa1, 0xb6, 0xab, 0xdb, 0x3b, 0xc6, 0x7e, 0xa5, 0x64, + 0xd8, 0x77, 0x2b, 0xf5, 0x9d, 0xd8, 0x47, 0x9e, 0x82, 0x73, 0x29, 0x08, 0xf8, 0x95, 0x4d, 0x58, + 0x8f, 0x17, 0xb5, 0xe8, 0x56, 0xcd, 0xd0, 0xa1, 0x8f, 0x97, 0x04, 0xfb, 0x34, 0x5b, 0xdc, 0x87, + 0x15, 0x4b, 0xaf, 0x55, 0x77, 0x07, 0xa3, 0x8e, 0xab, 0x4f, 0xc6, 0x47, 0x7d, 0x72, 0x01, 0x36, + 0x76, 0x1b, 0x66, 0xc9, 0xb0, 0x11, 0x25, 0xd6, 0x82, 0xb3, 0xb0, 0x2a, 0x17, 0xde, 0x37, 0xe8, + 0xf2, 0x25, 0xb0, 0x22, 0x03, 0xeb, 0x0d, 0x35, 0x53, 0xfc, 0x22, 0x2c, 0x45, 0x92, 0x37, 0x6e, + 0xc0, 0x59, 0xf9, 0x77, 0xd3, 0xed, 0x77, 0xbd, 0xfe, 0xa1, 0x7a, 0x26, 0x5e, 0x60, 0x4e, 0xfa, + 0x7d, 0x5a, 0x80, 0xfb, 0x59, 0x2e, 0x68, 0xb9, 0xa3, 0x63, 0xaf, 0xef, 0x8c, 0xdd, 0xae, 0x9a, + 0x29, 0xbe, 0x00, 0xcb, 0x91, 0x90, 0xf1, 0x74, 0xe2, 0xaa, 0x0d, 0xce, 0x80, 0x6b, 0xc6, 0x4e, + 0xa5, 0x5d, 0x53, 0xe7, 0xe8, 0x4e, 0x2e, 0x57, 0xf6, 0xca, 0x2a, 0x14, 0xbf, 0xa7, 0xd0, 0xcb, + 0x14, 0x26, 0x82, 0xaa, 0xed, 0xea, 0x62, 0xaa, 0xe9, 0x32, 0x63, 0x89, 0x28, 0x0c, 0xcb, 0x62, + 0x86, 0x03, 0x17, 0x61, 0x93, 0xff, 0xb0, 0xf5, 0xfa, 0x8e, 0x5d, 0xd6, 0xcd, 0x9d, 0x7b, 0xba, + 0x49, 0xd7, 0xde, 0x7d, 0x35, 0x83, 0x1b, 0x4a, 0x82, 0xd8, 0xad, 0x46, 0xbb, 0x54, 0x56, 0xb3, + 0x74, 0xfd, 0x46, 0xe0, 0xcd, 0x4a, 0x5d, 0xcd, 0xe1, 0xf6, 0x4c, 0x60, 0x23, 0x59, 0x5a, 0x3e, + 0x57, 0x7c, 0x5f, 0x81, 0x0d, 0xcb, 0x3b, 0xec, 0x3b, 0xe3, 0xc9, 0xc8, 0xd5, 0x7b, 0x87, 0x83, + 0x91, 0x37, 0x3e, 0x3a, 0xb6, 0x26, 0xde, 0xd8, 0x25, 0x37, 0xe1, 0x9a, 0x55, 0xd9, 0xab, 0xeb, + 0x2d, 0xba, 0xbd, 0xf4, 0xea, 0x5e, 0xc3, 0xac, 0xb4, 0xca, 0x35, 0xdb, 0x6a, 0x57, 0x12, 0x2b, + 0xef, 0x2a, 0x3c, 0x33, 0x1d, 0xb5, 0x6a, 0xec, 0xe9, 0xa5, 0xfb, 0xaa, 0x32, 0x9b, 0xe0, 0xb6, + 0x5e, 0xd5, 0xeb, 0x25, 0x63, 0xc7, 0xde, 0xbf, 0xa5, 0x66, 0xc8, 0x35, 0xb8, 0x32, 0x1d, 0x75, + 0xb7, 0xd2, 0xb4, 0x28, 0x5a, 0x76, 0xf6, 0x77, 0xcb, 0x56, 0x8d, 0x62, 0xe5, 0x8a, 0xdf, 0x57, + 0x60, 0x73, 0x5a, 0x08, 0x30, 0x72, 0x1d, 0x34, 0xa3, 0xde, 0x32, 0xf5, 0xca, 0x8e, 0x5d, 0x32, + 0x8d, 0x1d, 0xa3, 0xde, 0xaa, 0xe8, 0x55, 0xcb, 0xb6, 0x1a, 0x6d, 0xba, 0x9a, 0x42, 0xfb, 0x8e, + 0x67, 0xe1, 0xf2, 0x0c, 0xbc, 0x46, 0x65, 0xa7, 0xa4, 0x2a, 0xe4, 0x16, 0x3c, 0x3f, 0x03, 0xc9, + 0xba, 0x6f, 0xb5, 0x8c, 0x9a, 0x5c, 0xa2, 0x66, 0x8a, 0x25, 0xd8, 0x9a, 0x1e, 0x23, 0x88, 0xb2, + 0xe9, 0xe8, 0x48, 0xe7, 0x21, 0xb7, 0x43, 0x4f, 0x86, 0x48, 0xbe, 0x92, 0xa2, 0x07, 0x6a, 0x3c, + 0x7e, 0x46, 0xc2, 0x10, 0xc7, 0x6c, 0xd7, 0xeb, 0xec, 0x18, 0x59, 0x85, 0x42, 0xa3, 0x55, 0x36, + 0x4c, 0x9e, 0xf1, 0x05, 0x53, 0xbc, 0xb4, 0xeb, 0x74, 0xe3, 0x34, 0xcc, 0xca, 0x17, 0xf0, 0x3c, + 0xd9, 0x84, 0x75, 0xab, 0xaa, 0x97, 0xee, 0xda, 0xf5, 0x46, 0xcb, 0xae, 0xd4, 0xed, 0x52, 0x59, + 0xaf, 0xd7, 0x8d, 0xaa, 0x0a, 0x38, 0x98, 0xd3, 0x1c, 0x48, 0xc9, 0xc7, 0xe1, 0x46, 0xe3, 0x6e, + 0x4b, 0xb7, 0x9b, 0xd5, 0xf6, 0x5e, 0xa5, 0x6e, 0x5b, 0xf7, 0xeb, 0x25, 0x21, 0xfb, 0x94, 0x92, + 0x2c, 0xf7, 0x06, 0x5c, 0x9d, 0x89, 0x1d, 0xe6, 0x66, 0xb9, 0x0e, 0xda, 0x4c, 0x4c, 0xde, 0x91, + 0xe2, 0x4f, 0x15, 0xb8, 0x30, 0xe3, 0xa1, 0x9c, 0x3c, 0x0f, 0x37, 0xcb, 0x86, 0xbe, 0x53, 0x35, + 0x2c, 0x0b, 0x19, 0x05, 0x9d, 0x06, 0x66, 0xb0, 0x93, 0xca, 0x50, 0x6f, 0xc2, 0xb5, 0xd9, 0xe8, + 0xe1, 0xd1, 0x7c, 0x03, 0xae, 0xce, 0x46, 0xe5, 0x47, 0x75, 0x86, 0x14, 0xe1, 0xfa, 0x6c, 0xcc, + 0xe0, 0x88, 0xcf, 0x16, 0x7f, 0x53, 0x81, 0xf3, 0xe9, 0xda, 0x2a, 0xda, 0xb6, 0x4a, 0xdd, 0x6a, + 0xe9, 0xd5, 0xaa, 0xdd, 0xd4, 0x4d, 0xbd, 0x66, 0x1b, 0x75, 0xb3, 0x51, 0xad, 0xa6, 0x1d, 0x6d, + 0x57, 0xe1, 0x99, 0xe9, 0xa8, 0x56, 0xc9, 0xac, 0x34, 0x29, 0xf7, 0xd6, 0xe0, 0xd2, 0x74, 0x2c, + 0xa3, 0x52, 0x32, 0xd4, 0xcc, 0xf6, 0x1b, 0x3f, 0xfe, 0xdb, 0x4b, 0x67, 0x7e, 0xfc, 0xfe, 0x25, + 0xe5, 0x67, 0xef, 0x5f, 0x52, 0xfe, 0xe6, 0xfd, 0x4b, 0xca, 0x17, 0x9e, 0x3b, 0x5d, 0x5a, 0x33, + 0xbc, 0x94, 0x3c, 0x9c, 0xc7, 0x6b, 0xd8, 0x4b, 0xff, 0x19, 0x00, 0x00, 0xff, 0xff, 0x20, 0x8d, + 0x25, 0x54, 0x46, 0xc2, 0x01, 0x00, } func (this *PluginSpecV1) Equal(that interface{}) bool { @@ -33214,8 +33221,8 @@ func (m *AccessRequestSpecV3) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.AssumeStartTime != nil { - n140, err140 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.AssumeStartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.AssumeStartTime):]) + if m.ResourceExpiry != nil { + n140, err140 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.ResourceExpiry, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.ResourceExpiry):]) if err140 != nil { return 0, err140 } @@ -33224,6 +33231,18 @@ func (m *AccessRequestSpecV3) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1 i-- + dAtA[i] = 0xb2 + } + if m.AssumeStartTime != nil { + n141, err141 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.AssumeStartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.AssumeStartTime):]) + if err141 != nil { + return 0, err141 + } + i -= n141 + i = encodeVarintTypes(dAtA, i, uint64(n141)) + i-- + dAtA[i] = 0x1 + i-- dAtA[i] = 0xaa } if m.AccessList != nil { @@ -33240,22 +33259,22 @@ func (m *AccessRequestSpecV3) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0xa2 } - n142, err142 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.SessionTTL, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.SessionTTL):]) - if err142 != nil { - return 0, err142 + n143, err143 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.SessionTTL, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.SessionTTL):]) + if err143 != nil { + return 0, err143 } - i -= n142 - i = encodeVarintTypes(dAtA, i, uint64(n142)) + i -= n143 + i = encodeVarintTypes(dAtA, i, uint64(n143)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 - n143, err143 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.MaxDuration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.MaxDuration):]) - if err143 != nil { - return 0, err143 + n144, err144 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.MaxDuration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.MaxDuration):]) + if err144 != nil { + return 0, err144 } - i -= n143 - i = encodeVarintTypes(dAtA, i, uint64(n143)) + i -= n144 + i = encodeVarintTypes(dAtA, i, uint64(n144)) i-- dAtA[i] = 0x1 i-- @@ -33388,21 +33407,21 @@ func (m *AccessRequestSpecV3) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n147, err147 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err147 != nil { - return 0, err147 - } - i -= n147 - i = encodeVarintTypes(dAtA, i, uint64(n147)) - i-- - dAtA[i] = 0x2a - n148, err148 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + n148, err148 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) if err148 != nil { return 0, err148 } i -= n148 i = encodeVarintTypes(dAtA, i, uint64(n148)) i-- + dAtA[i] = 0x2a + n149, err149 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + if err149 != nil { + return 0, err149 + } + i -= n149 + i = encodeVarintTypes(dAtA, i, uint64(n149)) + i-- dAtA[i] = 0x22 if m.State != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.State)) @@ -34398,12 +34417,12 @@ func (m *RoleOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0xfa } - n162, err162 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.MFAVerificationInterval, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.MFAVerificationInterval):]) - if err162 != nil { - return 0, err162 + n163, err163 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.MFAVerificationInterval, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.MFAVerificationInterval):]) + if err163 != nil { + return 0, err163 } - i -= n162 - i = encodeVarintTypes(dAtA, i, uint64(n162)) + i -= n163 + i = encodeVarintTypes(dAtA, i, uint64(n163)) i-- dAtA[i] = 0x1 i-- @@ -36417,12 +36436,12 @@ func (m *UserSpecV2) MarshalToSizedBuffer(dAtA []byte) (int, error) { } i-- dAtA[i] = 0x42 - n193, err193 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err193 != nil { - return 0, err193 + n194, err194 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err194 != nil { + return 0, err194 } - i -= n193 - i = encodeVarintTypes(dAtA, i, uint64(n193)) + i -= n194 + i = encodeVarintTypes(dAtA, i, uint64(n194)) i-- dAtA[i] = 0x3a { @@ -36578,21 +36597,21 @@ func (m *LoginStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n196, err196 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LockExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LockExpires):]) - if err196 != nil { - return 0, err196 - } - i -= n196 - i = encodeVarintTypes(dAtA, i, uint64(n196)) - i-- - dAtA[i] = 0x22 - n197, err197 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LockedTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LockedTime):]) + n197, err197 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LockExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LockExpires):]) if err197 != nil { return 0, err197 } i -= n197 i = encodeVarintTypes(dAtA, i, uint64(n197)) i-- + dAtA[i] = 0x22 + n198, err198 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LockedTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LockedTime):]) + if err198 != nil { + return 0, err198 + } + i -= n198 + i = encodeVarintTypes(dAtA, i, uint64(n198)) + i-- dAtA[i] = 0x1a if len(m.LockedMessage) > 0 { i -= len(m.LockedMessage) @@ -36648,12 +36667,12 @@ func (m *CreatedBy) MarshalToSizedBuffer(dAtA []byte) (int, error) { } i-- dAtA[i] = 0x1a - n199, err199 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) - if err199 != nil { - return 0, err199 + n200, err200 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err200 != nil { + return 0, err200 } - i -= n199 - i = encodeVarintTypes(dAtA, i, uint64(n199)) + i -= n200 + i = encodeVarintTypes(dAtA, i, uint64(n200)) i-- dAtA[i] = 0x12 if m.Connector != nil { @@ -36771,21 +36790,21 @@ func (m *MFADevice) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } } - n202, err202 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastUsed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastUsed):]) - if err202 != nil { - return 0, err202 - } - i -= n202 - i = encodeVarintTypes(dAtA, i, uint64(n202)) - i-- - dAtA[i] = 0x3a - n203, err203 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.AddedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.AddedAt):]) + n203, err203 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastUsed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastUsed):]) if err203 != nil { return 0, err203 } i -= n203 i = encodeVarintTypes(dAtA, i, uint64(n203)) i-- + dAtA[i] = 0x3a + n204, err204 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.AddedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.AddedAt):]) + if err204 != nil { + return 0, err204 + } + i -= n204 + i = encodeVarintTypes(dAtA, i, uint64(n204)) + i-- dAtA[i] = 0x32 if len(m.Id) > 0 { i -= len(m.Id) @@ -37481,12 +37500,12 @@ func (m *TunnelConnectionSpecV2) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x22 } - n215, err215 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastHeartbeat, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastHeartbeat):]) - if err215 != nil { - return 0, err215 + n216, err216 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastHeartbeat, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastHeartbeat):]) + if err216 != nil { + return 0, err216 } - i -= n215 - i = encodeVarintTypes(dAtA, i, uint64(n215)) + i -= n216 + i = encodeVarintTypes(dAtA, i, uint64(n216)) i-- dAtA[i] = 0x1a if len(m.ProxyName) > 0 { @@ -37578,12 +37597,12 @@ func (m *AcquireSemaphoreRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x2a } - n216, err216 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err216 != nil { - return 0, err216 + n217, err217 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err217 != nil { + return 0, err217 } - i -= n216 - i = encodeVarintTypes(dAtA, i, uint64(n216)) + i -= n217 + i = encodeVarintTypes(dAtA, i, uint64(n217)) i-- dAtA[i] = 0x22 if m.MaxLeases != 0 { @@ -37632,12 +37651,12 @@ func (m *SemaphoreLease) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n217, err217 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err217 != nil { - return 0, err217 + n218, err218 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err218 != nil { + return 0, err218 } - i -= n217 - i = encodeVarintTypes(dAtA, i, uint64(n217)) + i -= n218 + i = encodeVarintTypes(dAtA, i, uint64(n218)) i-- dAtA[i] = 0x2a if len(m.LeaseID) > 0 { @@ -37695,12 +37714,12 @@ func (m *SemaphoreLeaseRef) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - n218, err218 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err218 != nil { - return 0, err218 + n219, err219 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err219 != nil { + return 0, err219 } - i -= n218 - i = encodeVarintTypes(dAtA, i, uint64(n218)) + i -= n219 + i = encodeVarintTypes(dAtA, i, uint64(n219)) i-- dAtA[i] = 0x12 if len(m.LeaseID) > 0 { @@ -37972,29 +37991,29 @@ func (m *WebSessionSpecV2) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x48 } - n225, err225 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LoginTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LoginTime):]) - if err225 != nil { - return 0, err225 - } - i -= n225 - i = encodeVarintTypes(dAtA, i, uint64(n225)) - i-- - dAtA[i] = 0x42 - n226, err226 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + n226, err226 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LoginTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LoginTime):]) if err226 != nil { return 0, err226 } i -= n226 i = encodeVarintTypes(dAtA, i, uint64(n226)) i-- - dAtA[i] = 0x3a - n227, err227 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.BearerTokenExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.BearerTokenExpires):]) + dAtA[i] = 0x42 + n227, err227 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) if err227 != nil { return 0, err227 } i -= n227 i = encodeVarintTypes(dAtA, i, uint64(n227)) i-- + dAtA[i] = 0x3a + n228, err228 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.BearerTokenExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.BearerTokenExpires):]) + if err228 != nil { + return 0, err228 + } + i -= n228 + i = encodeVarintTypes(dAtA, i, uint64(n228)) + i-- dAtA[i] = 0x32 if len(m.BearerToken) > 0 { i -= len(m.BearerToken) @@ -38226,21 +38245,21 @@ func (m *SAMLSessionData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x22 } - n228, err228 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExpireTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExpireTime):]) - if err228 != nil { - return 0, err228 - } - i -= n228 - i = encodeVarintTypes(dAtA, i, uint64(n228)) - i-- - dAtA[i] = 0x1a - n229, err229 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreateTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreateTime):]) + n229, err229 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExpireTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExpireTime):]) if err229 != nil { return 0, err229 } i -= n229 i = encodeVarintTypes(dAtA, i, uint64(n229)) i-- + dAtA[i] = 0x1a + n230, err230 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreateTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreateTime):]) + if err230 != nil { + return 0, err230 + } + i -= n230 + i = encodeVarintTypes(dAtA, i, uint64(n230)) + i-- dAtA[i] = 0x12 if len(m.ID) > 0 { i -= len(m.ID) @@ -38521,12 +38540,12 @@ func (m *RemoteClusterStatusV3) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n233, err233 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastHeartbeat, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastHeartbeat):]) - if err233 != nil { - return 0, err233 + n234, err234 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastHeartbeat, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastHeartbeat):]) + if err234 != nil { + return 0, err234 } - i -= n233 - i = encodeVarintTypes(dAtA, i, uint64(n233)) + i -= n234 + i = encodeVarintTypes(dAtA, i, uint64(n234)) i-- dAtA[i] = 0x12 if len(m.Connection) > 0 { @@ -41304,12 +41323,12 @@ func (m *GithubAuthRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x62 } if m.Expires != nil { - n274, err274 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) - if err274 != nil { - return 0, err274 + n275, err275 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) + if err275 != nil { + return 0, err275 } - i -= n274 - i = encodeVarintTypes(dAtA, i, uint64(n274)) + i -= n275 + i = encodeVarintTypes(dAtA, i, uint64(n275)) i-- dAtA[i] = 0x5a } @@ -42328,21 +42347,21 @@ func (m *LockSpecV2) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - n292, err292 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) - if err292 != nil { - return 0, err292 + n293, err293 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) + if err293 != nil { + return 0, err293 } - i -= n292 - i = encodeVarintTypes(dAtA, i, uint64(n292)) + i -= n293 + i = encodeVarintTypes(dAtA, i, uint64(n293)) i-- dAtA[i] = 0x22 if m.Expires != nil { - n293, err293 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) - if err293 != nil { - return 0, err293 + n294, err294 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) + if err294 != nil { + return 0, err294 } - i -= n293 - i = encodeVarintTypes(dAtA, i, uint64(n293)) + i -= n294 + i = encodeVarintTypes(dAtA, i, uint64(n294)) i-- dAtA[i] = 0x1a } @@ -43059,12 +43078,12 @@ func (m *RegisterUsingTokenRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro dAtA[i] = 0x6a } if m.Expires != nil { - n305, err305 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) - if err305 != nil { - return 0, err305 + n306, err306 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) + if err306 != nil { + return 0, err306 } - i -= n305 - i = encodeVarintTypes(dAtA, i, uint64(n305)) + i -= n306 + i = encodeVarintTypes(dAtA, i, uint64(n306)) i-- dAtA[i] = 0x62 } @@ -43244,12 +43263,12 @@ func (m *RecoveryCodesSpecV1) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n308, err308 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) - if err308 != nil { - return 0, err308 + n309, err309 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + if err309 != nil { + return 0, err309 } - i -= n308 - i = encodeVarintTypes(dAtA, i, uint64(n308)) + i -= n309 + i = encodeVarintTypes(dAtA, i, uint64(n309)) i-- dAtA[i] = 0x12 if len(m.Codes) > 0 { @@ -43629,21 +43648,21 @@ func (m *SessionTrackerSpecV1) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n312, err312 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err312 != nil { - return 0, err312 - } - i -= n312 - i = encodeVarintTypes(dAtA, i, uint64(n312)) - i-- - dAtA[i] = 0x2a - n313, err313 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + n313, err313 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) if err313 != nil { return 0, err313 } i -= n313 i = encodeVarintTypes(dAtA, i, uint64(n313)) i-- + dAtA[i] = 0x2a + n314, err314 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + if err314 != nil { + return 0, err314 + } + i -= n314 + i = encodeVarintTypes(dAtA, i, uint64(n314)) + i-- dAtA[i] = 0x22 if m.State != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.State)) @@ -43746,12 +43765,12 @@ func (m *Participant) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n314, err314 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastActive, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastActive):]) - if err314 != nil { - return 0, err314 + n315, err315 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastActive, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastActive):]) + if err315 != nil { + return 0, err315 } - i -= n314 - i = encodeVarintTypes(dAtA, i, uint64(n314)) + i -= n315 + i = encodeVarintTypes(dAtA, i, uint64(n315)) i-- dAtA[i] = 0x22 if len(m.Mode) > 0 { @@ -44463,12 +44482,12 @@ func (m *ClusterAlertSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n327, err327 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) - if err327 != nil { - return 0, err327 + n328, err328 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + if err328 != nil { + return 0, err328 } - i -= n327 - i = encodeVarintTypes(dAtA, i, uint64(n327)) + i -= n328 + i = encodeVarintTypes(dAtA, i, uint64(n328)) i-- dAtA[i] = 0x1a if len(m.Message) > 0 { @@ -44598,12 +44617,12 @@ func (m *AlertAcknowledgement) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n328, err328 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err328 != nil { - return 0, err328 + n329, err329 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err329 != nil { + return 0, err329 } - i -= n328 - i = encodeVarintTypes(dAtA, i, uint64(n328)) + i -= n329 + i = encodeVarintTypes(dAtA, i, uint64(n329)) i-- dAtA[i] = 0x22 if len(m.Reason) > 0 { @@ -47004,12 +47023,12 @@ func (m *PluginStatusV1) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n364, err364 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastSyncTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastSyncTime):]) - if err364 != nil { - return 0, err364 + n365, err365 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastSyncTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastSyncTime):]) + if err365 != nil { + return 0, err365 } - i -= n364 - i = encodeVarintTypes(dAtA, i, uint64(n364)) + i -= n365 + i = encodeVarintTypes(dAtA, i, uint64(n365)) i-- dAtA[i] = 0x1a if len(m.ErrorMessage) > 0 { @@ -47438,22 +47457,22 @@ func (m *PluginOktaStatusDetailsAppGroupSync) MarshalToSizedBuffer(dAtA []byte) dAtA[i] = 0x28 } if m.LastFailed != nil { - n375, err375 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastFailed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastFailed):]) - if err375 != nil { - return 0, err375 + n376, err376 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastFailed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastFailed):]) + if err376 != nil { + return 0, err376 } - i -= n375 - i = encodeVarintTypes(dAtA, i, uint64(n375)) + i -= n376 + i = encodeVarintTypes(dAtA, i, uint64(n376)) i-- dAtA[i] = 0x22 } if m.LastSuccessful != nil { - n376, err376 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastSuccessful, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastSuccessful):]) - if err376 != nil { - return 0, err376 + n377, err377 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastSuccessful, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastSuccessful):]) + if err377 != nil { + return 0, err377 } - i -= n376 - i = encodeVarintTypes(dAtA, i, uint64(n376)) + i -= n377 + i = encodeVarintTypes(dAtA, i, uint64(n377)) i-- dAtA[i] = 0x1a } @@ -47512,22 +47531,22 @@ func (m *PluginOktaStatusDetailsUsersSync) MarshalToSizedBuffer(dAtA []byte) (in dAtA[i] = 0x28 } if m.LastFailed != nil { - n377, err377 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastFailed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastFailed):]) - if err377 != nil { - return 0, err377 + n378, err378 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastFailed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastFailed):]) + if err378 != nil { + return 0, err378 } - i -= n377 - i = encodeVarintTypes(dAtA, i, uint64(n377)) + i -= n378 + i = encodeVarintTypes(dAtA, i, uint64(n378)) i-- dAtA[i] = 0x22 } if m.LastSuccessful != nil { - n378, err378 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastSuccessful, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastSuccessful):]) - if err378 != nil { - return 0, err378 + n379, err379 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastSuccessful, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastSuccessful):]) + if err379 != nil { + return 0, err379 } - i -= n378 - i = encodeVarintTypes(dAtA, i, uint64(n378)) + i -= n379 + i = encodeVarintTypes(dAtA, i, uint64(n379)) i-- dAtA[i] = 0x1a } @@ -47646,22 +47665,22 @@ func (m *PluginOktaStatusDetailsAccessListsSync) MarshalToSizedBuffer(dAtA []byt } } if m.LastFailed != nil { - n379, err379 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastFailed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastFailed):]) - if err379 != nil { - return 0, err379 + n380, err380 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastFailed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastFailed):]) + if err380 != nil { + return 0, err380 } - i -= n379 - i = encodeVarintTypes(dAtA, i, uint64(n379)) + i -= n380 + i = encodeVarintTypes(dAtA, i, uint64(n380)) i-- dAtA[i] = 0x22 } if m.LastSuccessful != nil { - n380, err380 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastSuccessful, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastSuccessful):]) - if err380 != nil { - return 0, err380 + n381, err381 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.LastSuccessful, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.LastSuccessful):]) + if err381 != nil { + return 0, err381 } - i -= n380 - i = encodeVarintTypes(dAtA, i, uint64(n380)) + i -= n381 + i = encodeVarintTypes(dAtA, i, uint64(n381)) i-- dAtA[i] = 0x1a } @@ -47827,12 +47846,12 @@ func (m *PluginOAuth2AccessTokenCredentials) MarshalToSizedBuffer(dAtA []byte) ( i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n385, err385 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err385 != nil { - return 0, err385 + n386, err386 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err386 != nil { + return 0, err386 } - i -= n385 - i = encodeVarintTypes(dAtA, i, uint64(n385)) + i -= n386 + i = encodeVarintTypes(dAtA, i, uint64(n386)) i-- dAtA[i] = 0x1a if len(m.RefreshToken) > 0 { @@ -48774,21 +48793,21 @@ func (m *ScheduledAgentUpgradeWindow) MarshalToSizedBuffer(dAtA []byte) (int, er i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n400, err400 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Stop, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Stop):]) - if err400 != nil { - return 0, err400 - } - i -= n400 - i = encodeVarintTypes(dAtA, i, uint64(n400)) - i-- - dAtA[i] = 0x12 - n401, err401 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):]) + n401, err401 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Stop, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Stop):]) if err401 != nil { return 0, err401 } i -= n401 i = encodeVarintTypes(dAtA, i, uint64(n401)) i-- + dAtA[i] = 0x12 + n402, err402 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):]) + if err402 != nil { + return 0, err402 + } + i -= n402 + i = encodeVarintTypes(dAtA, i, uint64(n402)) + i-- dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -49214,12 +49233,12 @@ func (m *OktaAssignmentSpecV1) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x30 } - n408, err408 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastTransition, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastTransition):]) - if err408 != nil { - return 0, err408 + n409, err409 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastTransition, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastTransition):]) + if err409 != nil { + return 0, err409 } - i -= n408 - i = encodeVarintTypes(dAtA, i, uint64(n408)) + i -= n409 + i = encodeVarintTypes(dAtA, i, uint64(n409)) i-- dAtA[i] = 0x2a if m.Status != 0 { @@ -49227,12 +49246,12 @@ func (m *OktaAssignmentSpecV1) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x20 } - n409, err409 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CleanupTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CleanupTime):]) - if err409 != nil { - return 0, err409 + n410, err410 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CleanupTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CleanupTime):]) + if err410 != nil { + return 0, err410 } - i -= n409 - i = encodeVarintTypes(dAtA, i, uint64(n409)) + i -= n410 + i = encodeVarintTypes(dAtA, i, uint64(n410)) i-- dAtA[i] = 0x1a if len(m.Targets) > 0 { @@ -50756,12 +50775,12 @@ func (m *AccessGraphSync) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x1a } } - n434, err434 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.PollInterval, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.PollInterval):]) - if err434 != nil { - return 0, err434 + n435, err435 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.PollInterval, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.PollInterval):]) + if err435 != nil { + return 0, err435 } - i -= n434 - i = encodeVarintTypes(dAtA, i, uint64(n434)) + i -= n435 + i = encodeVarintTypes(dAtA, i, uint64(n435)) i-- dAtA[i] = 0x12 if len(m.AWS) > 0 { @@ -54416,6 +54435,10 @@ func (m *AccessRequestSpecV3) Size() (n int) { l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.AssumeStartTime) n += 2 + l + sovTypes(uint64(l)) } + if m.ResourceExpiry != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.ResourceExpiry) + n += 2 + l + sovTypes(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -85851,6 +85874,42 @@ func (m *AccessRequestSpecV3) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 22: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceExpiry", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceExpiry == nil { + m.ResourceExpiry = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.ResourceExpiry, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/api/version.go b/api/version.go index 17bbdd36f118c..78532e11f4ece 100644 --- a/api/version.go +++ b/api/version.go @@ -3,6 +3,6 @@ package api import "github.com/coreos/go-semver/semver" -const Version = "17.0.0-dev" +const Version = "18.0.0-dev" var SemVersion = semver.New(Version) diff --git a/assets/aws/Makefile b/assets/aws/Makefile index a13720c8310e3..6f545daf8075c 100644 --- a/assets/aws/Makefile +++ b/assets/aws/Makefile @@ -2,7 +2,7 @@ # This must be a _released_ version of Teleport, i.e. one which has binaries # available for download on https://goteleport.com/download # Unreleased versions will fail to build. -TELEPORT_VERSION ?= 17.1.0 +TELEPORT_VERSION ?= 17.1.4 # Teleport UID is the UID of a non-privileged 'teleport' user TELEPORT_UID ?= 1007 diff --git a/assets/aws/README.md b/assets/aws/README.md index cf852f4d75dff..692e45f8286cc 100644 --- a/assets/aws/README.md +++ b/assets/aws/README.md @@ -53,7 +53,7 @@ read our [Getting Started Guide](https://goteleport.com/docs/get-started). You can use your Teleport AMI to deploy EC2 instances running any Teleport service. To read how to join your instance to a Teleport cluster in order to protect resources in your infrastructure, see our [Joining Services to a -Cluster](https://goteleport.com/docs/management/join-services-to-your-cluster/) +Cluster](https://goteleport.com/docs/enroll-resources/agents/join-services-to-your-cluster/) guides. If you are hosting the Teleport Auth and Proxy Services yourself, [read our diff --git a/assets/aws/go.mod b/assets/aws/go.mod index a401d78599015..6efad10702559 100644 --- a/assets/aws/go.mod +++ b/assets/aws/go.mod @@ -4,23 +4,23 @@ go 1.21 require ( github.com/alecthomas/kingpin/v2 v2.3.2 // replaced - github.com/aws/aws-sdk-go-v2 v1.32.5 - github.com/aws/aws-sdk-go-v2/config v1.28.5 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.194.0 + github.com/aws/aws-sdk-go-v2 v1.32.7 + github.com/aws/aws-sdk-go-v2/config v1.28.7 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 ) require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.46 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect github.com/aws/smithy-go v1.22.1 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect ) diff --git a/assets/aws/go.sum b/assets/aws/go.sum index e2dcb8facdf3c..4169857472547 100644 --- a/assets/aws/go.sum +++ b/assets/aws/go.sum @@ -1,31 +1,31 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= -github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0= -github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= +github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= +github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= +github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.194.0 h1:56YXcRmryw9wiTrvdVeJEUwBCoN/+o33R52PA7CCi08= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.194.0/go.mod h1:mzj8EEjIHSN2oZRXiw1Dd+uB4HZTl7hC8nBzX9IZMWw= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 h1:YbNopxjd9baM83YEEmkaYHi+NuJt0AszeaSLqo0CVr0= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1/go.mod h1:mwr3iRm8u1+kkEx4ftDM2Q6Yr0XQFBKrP036ng+k5Lk= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/assets/loadtest/helm/README.md b/assets/loadtest/helm/README.md index 95de866bee962..85b33d5933cca 100644 --- a/assets/loadtest/helm/README.md +++ b/assets/loadtest/helm/README.md @@ -18,7 +18,7 @@ they are not part of the product and no support will be provided. Start by creating a working cluster: - Create EKS cluster with the correct policies - [according to our EKS guide](https://goteleport.com/docs/ver/12.x/deploy-a-cluster/helm-deployments/aws/) + [according to our EKS guide](https://goteleport.com/docs/admin-guides/deploy-a-cluster/helm-deployments/aws/) - Make sure EBS CSI addon is deployed - Make sure the policy `AmazonEBSCSIDriverPolicy` is granted to the instance role associated with the EKS nodegroups which are running your Kubernetes nodes. diff --git a/bpf/README.md b/bpf/README.md index b2adfe68aeee9..d7169a47ed09d 100644 --- a/bpf/README.md +++ b/bpf/README.md @@ -79,17 +79,11 @@ Example: ## BPF in Teleport -Teleport uses BPF to implement enhanced session recording and restricted networking. Both features work only on Linux with +Teleport uses BPF to implement enhanced session recording. Enhanced session recording works only on Linux with the kernel 5.8+. Enhanced session recording records all: * exec family system calls * open family system calls * network connections -All events are recorded in the audit log. See https://goteleport.com/docs/server-access/guides/bpf-session-recording/. - -Restricted networking allows you to restrict network access for users. -It's implemented by using LSM hooks and BPF programs ( -see https://goteleport.com/docs/server-access/guides/restricted-session/). -On ubuntu systems LSM hooks are not enabled in some versions. -Here are the instructions on how to enable them https://github.com/gravitational/teleport/issues/8089#issuecomment-924990678. +All events are recorded in the audit log. See https://goteleport.com/docs/enroll-resources/server-access/guides/bpf-session-recording/. diff --git a/build.assets/images.mk b/build.assets/images.mk index fa6bda68b512e..b13c9c43e58cb 100644 --- a/build.assets/images.mk +++ b/build.assets/images.mk @@ -5,7 +5,7 @@ DIR := $(dir $(lastword $(MAKEFILE_LIST))) include $(DIR)arch.mk endif -BUILDBOX_VERSION ?= teleport17 +BUILDBOX_VERSION ?= teleport18 BUILDBOX_BASE_NAME ?= ghcr.io/gravitational/teleport-buildbox BUILDBOX = $(BUILDBOX_BASE_NAME):$(BUILDBOX_VERSION) diff --git a/build.assets/tooling/go.mod b/build.assets/tooling/go.mod index 1f54c7da795e5..480bab224df3e 100644 --- a/build.assets/tooling/go.mod +++ b/build.assets/tooling/go.mod @@ -16,15 +16,16 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d - golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/mod v0.22.0 golang.org/x/oauth2 v0.24.0 - helm.sh/helm/v3 v3.16.3 + helm.sh/helm/v3 v3.16.4 howett.net/plist v1.0.1 - k8s.io/apiextensions-apiserver v0.31.3 + k8s.io/apiextensions-apiserver v0.32.0 ) require ( + dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect @@ -49,6 +50,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.7.0 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -57,18 +59,15 @@ require ( golang.org/x/net v0.33.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.26.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.31.3 // indirect + k8s.io/apimachinery v0.32.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) -require dario.cat/mergo v1.0.1 // indirect - replace github.com/alecthomas/kingpin/v2 => github.com/gravitational/kingpin/v2 v2.1.11-0.20230515143221-4ec6b70ecd33 diff --git a/build.assets/tooling/go.sum b/build.assets/tooling/go.sum index 8db0e9b8359de..23ef44081f583 100644 --- a/build.assets/tooling/go.sum +++ b/build.assets/tooling/go.sum @@ -1008,8 +1008,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1351,8 +1351,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1635,14 +1635,11 @@ gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -helm.sh/helm/v3 v3.16.3 h1:kb8bSxMeRJ+knsK/ovvlaVPfdis0X3/ZhYCSFRP+YmY= -helm.sh/helm/v3 v3.16.3/go.mod h1:zeVWGDR4JJgiRbT3AnNsjYaX8OTJlIE9zC+Q7F7iUSU= +helm.sh/helm/v3 v3.16.4 h1:rBn/h9MACw+QlhxQTjpl8Ifx+VTWaYsw3rguGBYBzr0= +helm.sh/helm/v3 v3.16.4/go.mod h1:k8QPotUt57wWbi90w3LNmg3/MWcLPigVv+0/X4B8BzA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1654,14 +1651,14 @@ honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= -k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= +k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= @@ -1700,9 +1697,9 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/constants.go b/constants.go index 63ba08b791c46..ff9356f2b63d6 100644 --- a/constants.go +++ b/constants.go @@ -288,6 +288,9 @@ const ( // ComponentUpdater represents the teleport-update binary. ComponentUpdater = "updater" + // ComponentRolloutController represents the autoupdate_agent_rollout controller. + ComponentRolloutController = "rollout-controller" + // VerboseLogsEnvVar forces all logs to be verbose (down to DEBUG level) VerboseLogsEnvVar = "TELEPORT_DEBUG" diff --git a/docs/config.json b/docs/config.json index f5ce95aca7d60..204ec4d25b211 100644 --- a/docs/config.json +++ b/docs/config.json @@ -132,7 +132,7 @@ "aws_secret_access_key": "zyxw9876-this-is-an-example" }, "cloud": { - "version": "16.4.9", + "version": "16.4.11", "major_version": "16", "sla": { "monthly_percentage": "99.9%", @@ -209,5 +209,71 @@ } } }, - "redirects": [] + "redirects": [ + { + "source": "/reference/operator-resources/resources.teleport.dev_accesslists/", + "destination": "/reference/operator-resources/resources-teleport-dev-accesslists/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_githubconnectors/", + "destination": "/reference/operator-resources/resources-teleport-dev-githubconnectors/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_loginrules/", + "destination": "/reference/operator-resources/resources-teleport-dev-loginrules/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_oidcconnectors/", + "destination": "/reference/operator-resources/resources-teleport-dev-oidcconnectors/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_oktaimportrules/", + "destination": "/reference/operator-resources/resources-teleport-dev-oktaimportrules/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_openssheiceserversv2/", + "destination": "/reference/operator-resources/resources-teleport-dev-openssheiceserversv2/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_opensshserversv2/", + "destination": "/reference/operator-resources/resources-teleport-dev-opensshserversv2/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_provisiontokens/", + "destination": "/reference/operator-resources/resources-teleport-dev-provisiontokens/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_roles/", + "destination": "/reference/operator-resources/resources-teleport-dev-roles/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_rolesv6/", + "destination": "/reference/operator-resources/resources-teleport-dev-rolesv6/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_rolesv7/", + "destination": "/reference/operator-resources/resources-teleport-dev-rolesv7/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_samlconnectors/", + "destination": "/reference/operator-resources/resources-teleport-dev-samlconnectors/", + "permanent": true + }, + { + "source": "/reference/operator-resources/resources.teleport.dev_users/", + "destination": "/reference/operator-resources/resources-teleport-dev-users/", + "permanent": true + } + ] } diff --git a/docs/cspell.json b/docs/cspell.json index 08c89c4305a08..c369da521ab43 100644 --- a/docs/cspell.json +++ b/docs/cspell.json @@ -2,6 +2,14 @@ "language": "en", "version": "0.2", "words": [ + "aada", + "abee", + "fffc", + "fabfc", + "microservices", + "configmaps", + "genrsa", + "displayname", "AADUSER", "ABCDEFGHIJKL", "ADFS", diff --git a/docs/img/connect-your-client/webui-db-sessions/connect-dialog-custom.png b/docs/img/connect-your-client/webui-db-sessions/connect-dialog-custom.png new file mode 100644 index 0000000000000..1ed7f0e494230 Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/connect-dialog-custom.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/connect-dialog.png b/docs/img/connect-your-client/webui-db-sessions/connect-dialog.png new file mode 100644 index 0000000000000..f1614f0a2826e Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/connect-dialog.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/resources-connect-dialog.png b/docs/img/connect-your-client/webui-db-sessions/resources-connect-dialog.png new file mode 100644 index 0000000000000..51137d8769469 Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/resources-connect-dialog.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/resources-list.png b/docs/img/connect-your-client/webui-db-sessions/resources-list.png new file mode 100644 index 0000000000000..447fe1a42a13a Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/resources-list.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/session-terminal.png b/docs/img/connect-your-client/webui-db-sessions/session-terminal.png new file mode 100644 index 0000000000000..c062ec00e2ace Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/session-terminal.png differ diff --git a/docs/img/database-access/guides/oracle/dbeaver-connect-to-a-database@2x.png b/docs/img/database-access/guides/oracle/dbeaver-connect-to-a-database@2x.png new file mode 100644 index 0000000000000..e7b7f6d389a1a Binary files /dev/null and b/docs/img/database-access/guides/oracle/dbeaver-connect-to-a-database@2x.png differ diff --git a/docs/img/database-access/guides/oracle/dbeaver-jdbc-details@2x.png b/docs/img/database-access/guides/oracle/dbeaver-jdbc-details@2x.png new file mode 100644 index 0000000000000..142fae564a29e Binary files /dev/null and b/docs/img/database-access/guides/oracle/dbeaver-jdbc-details@2x.png differ diff --git a/docs/img/database-access/guides/oracle/dbeaver-new-connection@2x.png b/docs/img/database-access/guides/oracle/dbeaver-new-connection@2x.png new file mode 100644 index 0000000000000..1c1a244f42900 Binary files /dev/null and b/docs/img/database-access/guides/oracle/dbeaver-new-connection@2x.png differ diff --git a/docs/img/database-access/guides/oracle/oracle-sql-developer-add-database.png b/docs/img/database-access/guides/oracle/sql-developer-standalone-add-database.png similarity index 100% rename from docs/img/database-access/guides/oracle/oracle-sql-developer-add-database.png rename to docs/img/database-access/guides/oracle/sql-developer-standalone-add-database.png diff --git a/docs/img/database-access/guides/oracle/oracle-sql-developer-general.png b/docs/img/database-access/guides/oracle/sql-developer-standalone-conn-details-tcps.png similarity index 100% rename from docs/img/database-access/guides/oracle/oracle-sql-developer-general.png rename to docs/img/database-access/guides/oracle/sql-developer-standalone-conn-details-tcps.png diff --git a/docs/img/database-access/guides/oracle/oracle-sql-developer-success.png b/docs/img/database-access/guides/oracle/sql-developer-standalone-success.png similarity index 100% rename from docs/img/database-access/guides/oracle/oracle-sql-developer-success.png rename to docs/img/database-access/guides/oracle/sql-developer-standalone-success.png diff --git a/docs/img/database-access/guides/oracle/sql-developer-vscode-conn-details-basic@2x.png b/docs/img/database-access/guides/oracle/sql-developer-vscode-conn-details-basic@2x.png new file mode 100644 index 0000000000000..3d975db639dcd Binary files /dev/null and b/docs/img/database-access/guides/oracle/sql-developer-vscode-conn-details-basic@2x.png differ diff --git a/docs/img/database-access/guides/oracle/sql-developer-vscode-conn-details-jdbc@2x.png b/docs/img/database-access/guides/oracle/sql-developer-vscode-conn-details-jdbc@2x.png new file mode 100644 index 0000000000000..2acf298ef2137 Binary files /dev/null and b/docs/img/database-access/guides/oracle/sql-developer-vscode-conn-details-jdbc@2x.png differ diff --git a/docs/img/database-access/guides/oracle/sql-developer-vscode-connected-basic@2x.png b/docs/img/database-access/guides/oracle/sql-developer-vscode-connected-basic@2x.png new file mode 100644 index 0000000000000..5ac123d030546 Binary files /dev/null and b/docs/img/database-access/guides/oracle/sql-developer-vscode-connected-basic@2x.png differ diff --git a/docs/img/database-access/guides/oracle/sql-developer-vscode-connected-jdbc@2x.png b/docs/img/database-access/guides/oracle/sql-developer-vscode-connected-jdbc@2x.png new file mode 100644 index 0000000000000..127186e737f20 Binary files /dev/null and b/docs/img/database-access/guides/oracle/sql-developer-vscode-connected-jdbc@2x.png differ diff --git a/docs/img/database-access/guides/oracle/sql-developer-vscode-start@2x.png b/docs/img/database-access/guides/oracle/sql-developer-vscode-start@2x.png new file mode 100644 index 0000000000000..e03f30cbd8102 Binary files /dev/null and b/docs/img/database-access/guides/oracle/sql-developer-vscode-start@2x.png differ diff --git a/docs/img/database-access/guides/oracle/toad-add-login-record@2x.png b/docs/img/database-access/guides/oracle/toad-add-login-record@2x.png new file mode 100644 index 0000000000000..ae04c4522fd43 Binary files /dev/null and b/docs/img/database-access/guides/oracle/toad-add-login-record@2x.png differ diff --git a/docs/img/database-access/guides/oracle/toad-add-login-tested@2x.png b/docs/img/database-access/guides/oracle/toad-add-login-tested@2x.png new file mode 100644 index 0000000000000..fd04dac0a9cec Binary files /dev/null and b/docs/img/database-access/guides/oracle/toad-add-login-tested@2x.png differ diff --git a/docs/img/database-access/guides/oracle/toad-login-list@2x.png b/docs/img/database-access/guides/oracle/toad-login-list@2x.png new file mode 100644 index 0000000000000..2a6b5739caa82 Binary files /dev/null and b/docs/img/database-access/guides/oracle/toad-login-list@2x.png differ diff --git a/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx b/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx index 9ac5c819060c2..82cc5e4dff7c7 100644 --- a/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx +++ b/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx @@ -111,7 +111,7 @@ metadata: name: cluster-auth-preference spec: type: local - second_factor: "on" + second_factors: ["webauthn"] webauthn: rp_id: (=clusterDefaults.clusterName=) device_trust: diff --git a/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx b/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx index 7749f44ee445f..291e4469b4340 100644 --- a/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx +++ b/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx @@ -280,7 +280,7 @@ Yubikey for Hardware Key support, you might get an error on rare occasions. Depending on your settings, you might be asked to tap your Yubikey many times. Each tap is necessary to safely authenticate you. -For example, if you have `second_factor: webauthn` set in your `cluster_auth_preference`, +For example, if you have `second_factors: ["webauthn"]` set in your `cluster_auth_preference`, and `require_session_mfa: hardware_key_touch` set on your role, you'll see the following output when you first sign in: diff --git a/docs/pages/admin-guides/access-controls/guides/headless.mdx b/docs/pages/admin-guides/access-controls/guides/headless.mdx index 195d7c6a8e0dd..2a39c646aef7d 100644 --- a/docs/pages/admin-guides/access-controls/guides/headless.mdx +++ b/docs/pages/admin-guides/access-controls/guides/headless.mdx @@ -53,7 +53,7 @@ metadata: name: cluster-auth-preference spec: type: local - second_factor: "on" + second_factors: ["webauthn"] webauthn: rp_id: example.com connector_name: headless # headless by default @@ -83,7 +83,7 @@ metadata: name: cluster-auth-preference spec: type: local - second_factor: "on" + second_factors: ["webauthn"] webauthn: rp_id: example.com headless: false # disable Headless WebAuthn diff --git a/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx b/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx index 809a3b193ba8d..406eb9bd41396 100644 --- a/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx +++ b/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx @@ -71,8 +71,7 @@ metadata: name: cluster-auth-preference spec: type: local - # To make webauthn the only form of second factor allowed, set this field to 'webauthn'. - second_factor: "webauthn" + second_factors: ["webauthn"] webauthn: rp_id: example.com ``` diff --git a/docs/pages/admin-guides/access-controls/guides/passwordless.mdx b/docs/pages/admin-guides/access-controls/guides/passwordless.mdx index a2931cda9ed6c..e70236729c425 100644 --- a/docs/pages/admin-guides/access-controls/guides/passwordless.mdx +++ b/docs/pages/admin-guides/access-controls/guides/passwordless.mdx @@ -138,7 +138,7 @@ metadata: name: cluster-auth-preference spec: type: local - second_factor: "on" + second_factors: ["webauthn"] webauthn: rp_id: example.com connector_name: passwordless # passwordless by default @@ -229,7 +229,7 @@ metadata: name: cluster-auth-preference spec: type: local - second_factor: "on" + second_factors: ["webauthn"] webauthn: rp_id: example.com passwordless: false # disable passwordless diff --git a/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx b/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx index 1c6b1c99d4152..5af34433d8803 100644 --- a/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx +++ b/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx @@ -43,7 +43,7 @@ per-session MFA with FIPS builds, provide the following in your `teleport.yaml`: teleport: auth_service: local_auth: false - second_factor: on + second_factors: ["webauthn"] webauthn: rp_id: teleport.example.com ``` diff --git a/docs/pages/admin-guides/access-controls/guides/webauthn.mdx b/docs/pages/admin-guides/access-controls/guides/webauthn.mdx index 98614b8c36dca..f6f3bdf4a0a42 100644 --- a/docs/pages/admin-guides/access-controls/guides/webauthn.mdx +++ b/docs/pages/admin-guides/access-controls/guides/webauthn.mdx @@ -62,8 +62,8 @@ Teleport configuration as below: name: cluster-auth-preference spec: type: local - # To enable WebAuthn support, set this field to 'on', 'optional' or 'webauthn' - second_factor: "on" + # To enable WebAuthn support, include "webauthn" as a second factor method. + second_factors: ["webauthn"] webauthn: # Required, replace with proxy web address (example.com, example.teleport.sh). # rp_id is the public domain of the Teleport Proxy Service, *excluding* protocol @@ -469,8 +469,7 @@ Update the `cluster_auth_preference` definition to include the following content name: cluster-auth-preference spec: type: local - # To make webauthn the only form of multi-factor allowed, set this field to 'webauthn'. - second_factor: "webauthn" + second_factors: ["webauthn"] webauthn: rp_id: example.com ``` diff --git a/docs/pages/admin-guides/infrastructure-as-code/infrastructure-as-code.mdx b/docs/pages/admin-guides/infrastructure-as-code/infrastructure-as-code.mdx index f1b8d92aee5b1..048b0e0a67ec7 100644 --- a/docs/pages/admin-guides/infrastructure-as-code/infrastructure-as-code.mdx +++ b/docs/pages/admin-guides/infrastructure-as-code/infrastructure-as-code.mdx @@ -174,6 +174,7 @@ static configuration file: |---|---| |`spec.type`|`auth_service.authentication.type`| |`spec.second_factor`|`auth_service.authentication.second_factor`| +|`spec.second_factors`|`auth_service.authentication.second_factors`| |`spec.connector_name`|`auth_service.authentication.connector_name` |`spec.u2f`|`auth_service.authentication.u2f`| |`spec.disconnect_expired_cert`|`auth_service.disconnect_expired_cert`| diff --git a/docs/pages/admin-guides/teleport-policy/integrations/aws-sync.mdx b/docs/pages/admin-guides/teleport-policy/integrations/aws-sync.mdx index 5341c70af34b3..421a732a44384 100644 --- a/docs/pages/admin-guides/teleport-policy/integrations/aws-sync.mdx +++ b/docs/pages/admin-guides/teleport-policy/integrations/aws-sync.mdx @@ -156,6 +156,8 @@ The IAM policy includes the following directives: "s3:ListBucket", "s3:GetBucketLocation", "s3:GetBucketTagging", + "s3:GetBucketPolicyStatus", + "s3:GetBucketAcl", "iam:ListUsers", "iam:GetUser", diff --git a/docs/pages/connect-your-client/gui-clients.mdx b/docs/pages/connect-your-client/gui-clients.mdx index 3403430765eea..0d82d7144b2da 100644 --- a/docs/pages/connect-your-client/gui-clients.mdx +++ b/docs/pages/connect-your-client/gui-clients.mdx @@ -501,31 +501,160 @@ Click "Proceed", then click "Finish". (!docs/pages/includes/database-access/gui-clients/spanner-reuse-port-note.mdx!) -## Oracle SQL Developer +## Oracle graphical clients The Oracle integration works only in the authenticated proxy mode. Start a local proxy for connections to your Oracle database by using the command below: + ``` -tsh proxy db --tunnel --port 1521 --db-user= --db-name= oracle +> tsh proxy db --tunnel --port 11555 --db-user= --db-name= oracle + +Started authenticated tunnel for the Oracle database "oracle" in cluster "teleport.example.com" on 127.0.0.1:11555. ``` -This command uses the local port 1521, but you can choose any port, or let -`tsh` pick a local port at random if you omit the `--port` flag. -You should specify a port to avoid the need to reconfigure your GUI client again -later. +The command above uses the local port 11555, but you can choose any available port. Leaving `--port` empty will cause `tsh` to pick a random one. + + +The local proxy supports TCP and TCPS modes. Different clients prefer different modes. + +TCP: +- requires no username or password +- generally easier to configure + +TCPS: +- requires no username or password +- depends on automatically created wallet +- uses JDBC URL for configuration + + +Teleport versions earlier than 17.2.0 support only a limited range of clients and only offer TCPS mode. `tsh` will automatically detect this situation and warn the user. We recommend updating to the latest version of Teleport to access full client support and additional connection options. +### Oracle SQL Developer (standalone) + In "Connections" click the "+" button for a new database connection: -![Oracle SQL Developer Add Database Connection](../../img/database-access/guides/oracle/oracle-sql-developer-add-database.png) +![Oracle SQL Developer Add Database Connection](../../img/database-access/guides/oracle/sql-developer-standalone-add-database.png) Next, set the name and username from the `--db-user` parameter. Set connection type to "Custom JDBC" and set the "Custom JDBC URL" from the `tsh proxy db` command. -![Oracle SQL Developer](../../img/database-access/guides/oracle/oracle-sql-developer-general.png) +![Oracle SQL Developer](../../img/database-access/guides/oracle/sql-developer-standalone-conn-details-tcps.png) Now you can click "Test" to check your configuration. -![Oracle SQL Developer Success](../../img/database-access/guides/oracle/oracle-sql-developer-success.png) +![Oracle SQL Developer Success](../../img/database-access/guides/oracle/sql-developer-standalone-success.png) + +### Oracle SQL Developer (VS Code extension) + +Install the extension from [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=Oracle.sql-developer). + +Both TCP and TCPS modes can be used. + + + + +Open the extension toolbar and click on "Create Connection" button. + +![SQL Developer (VS Code) Start](../../img/database-access/guides/oracle/sql-developer-vscode-start@2x.png) + +Enter the following connection details: + +| Field | Value | +|-----------------|------------------------| +| Connection name | Choose unique name | +| User name | `/` | +| Password | `/` | +| Save Password | Mark checkbox | +| Connection type | Basic | +| Host name | `localhost` | +| Port number | `--port` flag value | +| Type | Service Name | +| Service name | `--db-name` flag value | + +Test and create the connection. + +![SQL Developer (VS Code) Connection Details (basic)](../../img/database-access/guides/oracle/sql-developer-vscode-conn-details-basic@2x.png) + +The new connection should appear on the list. + +
+![SQL Developer (VS Code) Connected (basic)](../../img/database-access/guides/oracle/sql-developer-vscode-connected-basic@2x.png) +
+ +
+ + + +Open the extension toolbar and click on "Create Connection" button. + +![SQL Developer (VS Code) Start](../../img/database-access/guides/oracle/sql-developer-vscode-start@2x.png) + +Enter the following connection details: + +| Field | Value | +|------------------|---------------------------------| +| Connection name | (choose per your preference) | +| User name | `/` | +| Password | `/` | +| Save Password | Mark the checkbox | +| Connection type | "Custom JDBC" | +| Custom JDBC URL | Copy from `tsh proxy db` output | + +Test and create the connection. + +![SQL Developer (VS Code) Connection Details (JDBC)](../../img/database-access/guides/oracle/sql-developer-vscode-conn-details-jdbc@2x.png) + +The new connection should appear on the list. + +
+![SQL Developer (VS Code) Connected (JDBC)](../../img/database-access/guides/oracle/sql-developer-vscode-connected-jdbc@2x.png) +
+ + +
+
+ +### Toad + +Add new login record in the logins dialog. + +![Toad Add Login Record](../../img/database-access/guides/oracle/toad-add-login-record@2x.png) + +Enter the connection details in "Direct" tab: + +| Field | Value | +|-----------------|------------------------------| +| Host name | `127.0.0.1` | +| Port number | `--port` flag value | +| Service name | `--db-name` flag value | +| User name | `EXTERNAL` | +| Password | (leave empty) | +| Connection name | (choose per your preference) | + +Test the connection to verify the setup. + +![Toad Add Login Tested](../../img/database-access/guides/oracle/toad-add-login-tested@2x.png) + +The newly added login should appear on the login list. + +![Toad Login List](../../img/database-access/guides/oracle/toad-login-list@2x.png) + + +You can also configure Toad to use an external Oracle client. Both native and external clients are supported. + + +### DBeaver + +Click on the "New Database Connection" button. + +![DBeaver new connection button](../../img/database-access/guides/oracle/dbeaver-new-connection@2x.png) + +Select "Oracle" from the driver list. You may use the search toolbar to narrow down the list. +![DBeaver connect to a database](../../img/database-access/guides/oracle/dbeaver-connect-to-a-database@2x.png) + +Choose "Custom" connection type and paste the JDBC connection string printed by `tsh proxy db`. +![DBeaver JDBC details](../../img/database-access/guides/oracle/dbeaver-jdbc-details@2x.png) + +Test the connection to verify the setup. Finalize by clicking "Finish". -Congratulations! You have just connected to your Oracle instance. diff --git a/docs/pages/connect-your-client/web-ui.mdx b/docs/pages/connect-your-client/web-ui.mdx index be05318f09d57..9bce88e3eac52 100644 --- a/docs/pages/connect-your-client/web-ui.mdx +++ b/docs/pages/connect-your-client/web-ui.mdx @@ -65,3 +65,57 @@ After you save and exit the editor, `tctl` will update the resource: cluster networking configuration has been updated ``` +## Starting a database session + +Starting from version `17.1`, users can establish database sessions using the +Teleport Web UI. Currently, it is supported in PostgreSQL databases. + +To start a new session, locate your database in the resources list and click +"Connect". + +![Resources list](../../img/connect-your-client/webui-db-sessions/resources-list.png) + +For supported databases, the dialog will present the option to start +the session in your browser. + +![Resources list database connect dialog](../../img/connect-your-client/webui-db-sessions/resources-connect-dialog.png) + +After clicking on the "Connect in the browser" button, a new tab will open with +a form. Teleport will pre-fill this form based on your permissions, but you can +adjust the options as needed. + +![New database session connect dialog](../../img/connect-your-client/webui-db-sessions/connect-dialog.png) + +If your user has wildcard permissions (*), you can type custom values into the +form fields. This allows flexibility in selecting specific databases or +credentials. + +![New database session connect dialog with custom values](../../img/connect-your-client/webui-db-sessions/connect-dialog-custom.png) + +Once you've filled in the session details, click the "Connect" button. Your +session will start, and a terminal interface will appear in the browser. + +The browser-based terminal allows you to execute queries and interact with your +database. Follow the on-screen instructions to see available commands and +limitations. + +![Database session terminal](../../img/connect-your-client/webui-db-sessions/session-terminal.png) + + + While the terminal interface provided in the Teleport Web UI is designed to + resemble popular database CLIs such as `psql`, it is a custom implementation + with some differences and limitations: + - **Feature Set:** Not all features available in `psql` are implemented. + For instance, scripting capabilities, query cancellation, or informational + commands like `\d` or `\dt` are currently unsupported. + - **Error Handling:** Error messages and diagnostics might differ from what + users are accustomed to in `psql`. + + These distinctions are designed to maintain a lightweight and secure interface + directly in your browser. For more complex operations, you may prefer + accessing your database from your terminal using `tsh` and your preferred + tool. + + Future updates may expand functionality or address feedback based on user + needs and supported databases. + diff --git a/docs/pages/enroll-resources/application-access/getting-started.mdx b/docs/pages/enroll-resources/application-access/getting-started.mdx index 28a23fad24da2..3fe1756f69c93 100644 --- a/docs/pages/enroll-resources/application-access/getting-started.mdx +++ b/docs/pages/enroll-resources/application-access/getting-started.mdx @@ -1,6 +1,6 @@ --- -title: Getting Started with Teleport Application Access -description: Getting started with Teleport application access. +title: Protect a Web Application with Teleport +description: Provides instructions to set up the Teleport Application Service and enable secure access to a web application. videoBanner: cvW4b96aPL0 --- @@ -20,6 +20,21 @@ At a high level, configuring access for applications involves the following step - Install and configure Teleport on the application host. - Add a user to verify access to the application. +## How it works + +In the setup we demonstrate in this guide, the Teleport Application Service +joins your Teleport cluster with a secure token. You configure the Application +Service to protect a web application using a configuration file. After the +Application Service joins the cluster, the Teleport Proxy Service routes +requests from end users to the Teleport Application Service, and responses from +the Application Serve back to end users. + +The Application Service authenticates user requests by validating a JSON web +token (JWT) in the request against a CA maintained by the Teleport Auth Service. +The requesting user's roles are encoded in the JWT, allowing the Application +Service to determine whether the user has permissions to make a request to a +Teleport-protected application. + ## Prerequisites For this tutorial, verify your environment meets the following requirements: @@ -179,7 +194,7 @@ $ helm install teleport-kube-agent teleport/teleport-kube-agent \ - Change `apps[0].name` and `apps[0].uri` if you're configuring access to a different web application. -Make sure that the Teleport agent pod is running. You should see one +Make sure that the Teleport Agent pod is running. You should see one `teleport-kube-agent` pod with a single ready container: ```code @@ -203,8 +218,9 @@ To assign to the `access` role to a new local user named `alice`, run the follow $ tctl users add --roles=access alice ``` -The command generates an invitation URL for the new user. You can use the URL to choose -a password, set up a second factor for authentication, and sign in to the Teleport Web UI. +The command generates an invitation URL for the new user. You can use the URL to +choose a password, set up multi-factor authentication, and sign in to the +Teleport Web UI. ## Step 5/5. Access the application @@ -225,5 +241,5 @@ Learn more about protecting applications with Teleport in the following topics: - [Connecting applications](./guides/connecting-apps.mdx). - Integrating with [JWT tokens](./jwt/introduction.mdx). - Accessing applications with [RESTful APIs](./guides/api-access.mdx). -- Setting configuration options AND running CLI commands in the [Application Access reference](../../reference/agent-services/application-access.mdx). +- Setting configuration options AND running CLI commands in the [Application Service reference](../../reference/agent-services/application-access.mdx). - Using the Let's Encrypt [ACME protocol](https://letsencrypt.org/how-it-works/). diff --git a/docs/pages/enroll-resources/application-access/guides/connecting-apps.mdx b/docs/pages/enroll-resources/application-access/guides/connecting-apps.mdx index 26ed6d6c3fbc9..816ae2484d96c 100644 --- a/docs/pages/enroll-resources/application-access/guides/connecting-apps.mdx +++ b/docs/pages/enroll-resources/application-access/guides/connecting-apps.mdx @@ -132,10 +132,9 @@ Teleport, you must configure these yourself: Service domain, e.g., `*.teleport.example.com`, with the domain name of the Teleport Proxy Service. -1. Ensure that your system provisionings TLS certificates for - Teleport-registered applications. The method to use depends on how you - originally set up TLS for your self-hosted Teleport deployment, and is - outside the scope of this guide. +1. Ensure that your system provisions TLS certificates for Teleport-registered + applications. The method to use depends on how you originally set up TLS for + your self-hosted Teleport deployment, and is outside the scope of this guide. In general, the same system that provisions TLS certificates signed for the web address of the Proxy Service (e.g., `teleport.example.com`) must also diff --git a/docs/pages/enroll-resources/application-access/guides/dynamodb.mdx b/docs/pages/enroll-resources/application-access/guides/dynamodb.mdx index 58d846981d619..681752478ecb5 100644 --- a/docs/pages/enroll-resources/application-access/guides/dynamodb.mdx +++ b/docs/pages/enroll-resources/application-access/guides/dynamodb.mdx @@ -22,15 +22,15 @@ This guide will help you to: - +## How it works The Teleport Application Service enables secure access to DynamoDB via its [integration](../cloud-apis/aws-console.mdx) with the AWS management console and API. This is an alternative to accessing DynamoDB through the Teleport Database -service, as described in our [Database Access with AWS -DynamoDB](../../database-access/enroll-aws-databases/aws-dynamodb.mdx) guide. +service, as described in our [Protect Amazon DynamoDB with +Teleport](../../database-access/enroll-aws-databases/aws-dynamodb.mdx) guide. + The Application Service's integration with AWS is not designed specifically for DynamoDB, while the Database Service has a purpose-built DynamoDB integration. As a result, we recommend using the Database Service to enable secure access to @@ -40,7 +40,6 @@ It is worth noting that the Database Service will allow you to connect with GUI clients, whereas the Application Service does not. On the other hand, a single Application Service configuration can access DynamoDB across regions, while database resources must be configured for each region with DynamoDB databases. - ## Prerequisites @@ -173,6 +172,6 @@ $ tsh apps logout aws ``` ## Next steps -- More information on [AWS Management and API with Teleport Application Access](../../application-access/cloud-apis/aws-console.mdx). +- More information on [protecting AWS Console with Teleport](../../application-access/cloud-apis/aws-console.mdx). - Learn more about [AWS service endpoints](https://docs.aws.amazon.com/general/latest/gr/rande.html). diff --git a/docs/pages/enroll-resources/auto-discovery/kubernetes-applications/get-started.mdx b/docs/pages/enroll-resources/auto-discovery/kubernetes-applications/get-started.mdx index fd9a5d9fa8c19..62f4d807c3f35 100644 --- a/docs/pages/enroll-resources/auto-discovery/kubernetes-applications/get-started.mdx +++ b/docs/pages/enroll-resources/auto-discovery/kubernetes-applications/get-started.mdx @@ -11,6 +11,14 @@ setup step. In this guide, we show you how to enable Kubernetes application auto-discovery. +## How it works + +The Teleport Discovery Service queries the API server of the Kubernetes cluster +in which you want to detect applications, maintaining dynamic `app` resources to +match the Kubernetes services that it detects within the cluster. The Teleport +Application Service queries the Teleport Auth Service to fetch `app` resources, +and proxies applications based the dynamically generated configuration. + ## Prerequisites (!docs/pages/includes/edition-prereqs-tabs.mdx!) @@ -26,7 +34,7 @@ In this guide, we show you how to enable Kubernetes application auto-discovery. ## Step 1/2. Create a join token -Create a join token for a new Teleport agent that will run the Teleport +Create a join token for a new Teleport Agent that will run the Teleport Kubernetes Service, Application Service, and Discovery Service: ```code @@ -40,15 +48,15 @@ Teleport applications created from discovered Kubernetes services. ## Step 2/2. Deploy the agent -If you want to install a new Teleport agent in your Kubernetes cluster, you can -use the `teleport-kube-agent` Helm chart. If you already have a Teleport agent +If you want to install a new Teleport Agent in your Kubernetes cluster, you can +use the `teleport-kube-agent` Helm chart. If you already have a Teleport Agent installed, you can upgrade it to enable the Kubernetes Application Discovery by adding the `kube`, `app`, and `discovery` to roles as shown below. -Deploy a new Teleport agent running your configured services by installing the +Deploy a new Teleport Agent running your configured services by installing the `teleport-kube-agent` Helm chart: ```code diff --git a/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx b/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx index 77e487d14bb3b..87be741c8513d 100644 --- a/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx @@ -205,6 +205,8 @@ $ tsh db connect --db-user=role/example-iam-role --db-name=dev my-redshift +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx b/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx index 4837cb6e7a4e4..5783d60c6570d 100644 --- a/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx @@ -204,13 +204,15 @@ Token Type Labels Expiry Time (UTC) Create a Helm values file called `values.yaml`, assigning to the value of the join token you retrieved above, to the host **and port** of your Teleport -Proxy Service, and to the host **and port** of your RDS +Proxy Service, `enterprise` to false if you are using the community/OSS version, +and to the host **and port** of your RDS database (e.g., `myrds.us-east-1.rds.amazonaws.com:5432`): ```yaml authToken: proxyAddr: roles: db +enterprise: true databases: - name: example uri: "" @@ -222,11 +224,24 @@ annotations: eks.amazonaws.com/role-arn: arn:aws:iam:::role/teleport-rds-role ``` +Get the version of Teleport to install. If you have automatic agent updates enabled in your cluster, query the latest Teleport version that is compatible with the updater: + +```code +$ TELEPORT_VERSION="$(curl https:///v1/webapi/automaticupgrades/channel/default/version | sed 's/v//')" +``` + +Otherwise, get the version of your Teleport cluster: + +```code +$ TELEPORT_VERSION="$(curl https:///v1/webapi/ping | jq -r '.server_version')" +``` + + Install the Helm chart for Teleport agent services, `teleport-kube-agent`: ```code $ helm -n teleport-agent install teleport-kube-agent teleport/teleport-kube-agent \ - --values values.yaml --create-namespace + --values values.yaml --create-namespace --version $TELEPORT_VERSION ``` Make sure that the Teleport agent pod is running. You should see one @@ -295,6 +310,8 @@ Retrieve credentials for the database and connect to it as the `alice` user: $ tsh db connect --db-user=alice --db-name=postgres rds-example ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + The appropriate database command-line client (`psql`, `mysql`, `mariadb`) should be available in `PATH` in order to be able to connect. diff --git a/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx b/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx index 8de82a3035a91..7b01cb3a2a959 100644 --- a/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx @@ -235,6 +235,8 @@ Type "help" for help. dev=> ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx b/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx index b639ab2a0673a..e18afa60ca7e9 100644 --- a/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx @@ -428,6 +428,8 @@ $ tsh db connect --db-user=teleport --db-name=postgres azure-db +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + The appropriate database command-line client (`psql`, `mysql`) should be available in the `PATH` of the machine you're running `tsh db connect` from. diff --git a/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx b/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx index 15a3d9ece0f22..05f58d2e21788 100644 --- a/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx @@ -149,6 +149,8 @@ Retrieve credentials for the "cloudsql" example database and connect to it: $ tsh db connect --db-user=cloudsql-user@.iam --db-name=postgres cloudsql ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx b/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx index 1cbb3d8a0f427..02acd22cd3fdc 100644 --- a/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx @@ -142,6 +142,8 @@ To retrieve credentials for a database and connect to it: $ tsh db connect --db-user=postgres --db-name=postgres example-postgres ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/enroll-resources/database-access/rbac.mdx b/docs/pages/enroll-resources/database-access/rbac.mdx index 38a63801be474..d383ab0d0b8d9 100644 --- a/docs/pages/enroll-resources/database-access/rbac.mdx +++ b/docs/pages/enroll-resources/database-access/rbac.mdx @@ -264,6 +264,35 @@ spec: version: v1 ``` +### Disabling the default import rule + +Teleport expects at least one import rule to be defined. If it is missing, the Teleport Auth Service will create a default import rule on startup. + +If you don't want to import any database objects, create a rule that matches no databases. In the example below, the list of matching label values is empty, so no database will ever match this selector. + +```yaml +kind: db_object_import_rule +metadata: + name: import_no_objects +spec: + database_labels: + - {} + mappings: + - {} +version: v1 +``` + +Create the custom rule and remove the default one: + +{/* spell-checker: disable */} +```code +$ tctl create -f import_no_objects.yaml +rule "import_no_objects" has been created +$ tctl rm db_object_import_rule/import_all_objects +Rule "import_all_objects" has been deleted +``` +{/* spell-checker: enable */} + ### Database admin user A database admin user is responsible for granting permissions to end users. You diff --git a/docs/pages/enroll-resources/kubernetes-access/faq.mdx b/docs/pages/enroll-resources/kubernetes-access/faq.mdx index de245c4d35dfd..a41bfda84eb87 100644 --- a/docs/pages/enroll-resources/kubernetes-access/faq.mdx +++ b/docs/pages/enroll-resources/kubernetes-access/faq.mdx @@ -33,3 +33,8 @@ Check out the [Kubernetes Service Discovery Guide](../auto-discovery/kubernetes/kubernetes.mdx) for more documentation and examples. +## Does Teleport work with Kubernetes desktop UI applications? + +Yes, Teleport generates a kubeconfig file (default `~/.kube/config`) when a user +logs in to a Kubernetes cluster. GUI tools such as Lens can +interact with the Kubernetes cluster through Teleport as with any other standard kubeconfig. diff --git a/docs/pages/enroll-resources/machine-id/access-guides/kubernetes.mdx b/docs/pages/enroll-resources/machine-id/access-guides/kubernetes.mdx index 72ab9ad3fb13b..3283fc5cf03e3 100644 --- a/docs/pages/enroll-resources/machine-id/access-guides/kubernetes.mdx +++ b/docs/pages/enroll-resources/machine-id/access-guides/kubernetes.mdx @@ -70,7 +70,7 @@ Create a file called `role.yaml` with the following content: ```yaml kind: role -version: v6 +version: v7 metadata: name: example-role spec: @@ -83,6 +83,7 @@ spec: - kind: "*" namespace: "*" name: "*" + verbs: ["*"] ``` Replace `example-role` with a descriptive name related to your use case. diff --git a/docs/pages/enroll-resources/server-access/openssh/openssh-agentless.mdx b/docs/pages/enroll-resources/server-access/openssh/openssh-agentless.mdx index 76c9ecbd8c987..f87f4bd76536f 100644 --- a/docs/pages/enroll-resources/server-access/openssh/openssh-agentless.mdx +++ b/docs/pages/enroll-resources/server-access/openssh/openssh-agentless.mdx @@ -69,7 +69,7 @@ In this setup, the Teleport SSH Service performs RBAC checks as well as audits a configured. This must be done *before* your Teleport cluster is upgraded to Teleport 14. If you are having issues registering OpenSSH nodes or need to upgrade your - Teleport cluster to Teleport 14 before registering all of your OpenSSH nodes, you can + Teleport cluster to Teleport 14 before registering all of your OpenSSH nodes, you can pass the `TELEPORT_UNSTABLE_UNLISTED_AGENT_DIALING` environment variable to your Proxy Service and set it to `yes`. This will allow connections to unregistered OpenSSH nodes but will be removed in Teleport v15. @@ -104,10 +104,19 @@ Change the command-line options to assign the following values: - Set to the address and port of your Teleport Proxy Service. - Set to the join token value. -Check that your new node is listed with `tsh ls` or in the Web UI. You can edit the -hostname and labels with `tctl edit nodes/`. If the hostname isn't unique, get the UUID -from `tctl nodes ls -v` and edit with `tctl edit nodes/`. After you've confirmed the node -was registered successfully you can delete the copied `teleport` binary. +Check that your new node is listed with `tsh ls` or in the Web UI. + +You can edit the node with `tctl edit nodes/` or +`tctl edit nodes/`. The node's UUID can be found in the output of +`tctl nodes ls -v`. Be careful when changing the node's hostname, as the +hostname is a principal embedded in the host certificate that was generated +with `teleport join openssh`. If you want to modify the node's hostname, +you should either change the hostname of the instance and re-run +`teleport join openssh`, or manually issue a new host certificate with +`tctl auth sign --format=openssh`. + +After you've confirmed the node was registered successfully you can delete the +copied `teleport` binary. ## Step 2/3. Generate an SSH client configuration @@ -327,7 +336,7 @@ host's SSH port.
-You can log in to a host in a trusted leaf cluster by placing the name of +You can log in to a host in a trusted leaf cluster by placing the name of the leaf cluster between the name of the node and the name of the root cluster: ```code diff --git a/docs/pages/includes/config-reference/auth-service.yaml b/docs/pages/includes/config-reference/auth-service.yaml index 6967c5f814c02..668071408f7a3 100644 --- a/docs/pages/includes/config-reference/auth-service.yaml +++ b/docs/pages/includes/config-reference/auth-service.yaml @@ -156,11 +156,18 @@ auth_service: # Possible values: true, false, "hardware_key", "hardware_key_touch". # Defaults to false. require_session_mfa: false + + # second_factors is the list of allowed second factors for the cluster. + # Possible values: "otp", "webauthn", and "sso". Order does not matter. + # Defaults to ["otp"]. + second_factors: ["webauthn", "otp"] # second_factor can be 'on', 'otp' or 'webauthn'. # - 'on' requires either otp or webauthn second factor. # - 'otp' and 'webauthn' require the corresponding second factor. - second_factor: otp + # + # Prefer setting second_factors instead. + #second_factor: otp # Sets whether passwordless authentication is allowed. # Passwordless requires WebAuthn. diff --git a/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx b/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx index 2d55518cc26ec..891efcc664187 100644 --- a/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx +++ b/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx @@ -2,6 +2,8 @@ Now, log into your Teleport cluster and connect to the database: +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + ```code $ tsh login --proxy=teleport.example.com $ tsh db connect --db-name example diff --git a/docs/pages/includes/database-access/pg-access-webui.mdx b/docs/pages/includes/database-access/pg-access-webui.mdx new file mode 100644 index 0000000000000..5f6f7cddb4b33 --- /dev/null +++ b/docs/pages/includes/database-access/pg-access-webui.mdx @@ -0,0 +1,3 @@ + + Starting from version `17.1`, you can now [access your PostgreSQL databases using the Web UI.](../../connect-your-client/web-ui.mdx#starting-a-database-session) + diff --git a/docs/pages/includes/database-access/rds-proxy.mdx b/docs/pages/includes/database-access/rds-proxy.mdx index 348708e348aa9..dccbc48e02bc9 100644 --- a/docs/pages/includes/database-access/rds-proxy.mdx +++ b/docs/pages/includes/database-access/rds-proxy.mdx @@ -184,6 +184,8 @@ Retrieve credentials for the database and connect to it as the `alice` user: $ tsh db connect --db-user=alice --db-name=dev rds-proxy ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/includes/plugins/finish-event-handler-config.mdx b/docs/pages/includes/plugins/finish-event-handler-config.mdx index 50874fdb0b716..795ba52253461 100644 --- a/docs/pages/includes/plugins/finish-event-handler-config.mdx +++ b/docs/pages/includes/plugins/finish-event-handler-config.mdx @@ -15,6 +15,21 @@ namespace = "default" # for the default window size. # The window size should be specified as a duration string, parsed by Go's time.ParseDuration. window-size = "24h" +# types is a comma-separated list of event types to search when forwarding audit +# events. For example, to limit forwarded events to user logins +# and new Access Requests, you can assign this field to +# "user.login,access_request.create". +types = "" +# skip-event-types is a comma-separated list of types of events to skip. For +# example, to forward all audit events except for new app deletion events, you +# can include the following assignment: +# skip-event-types = "app.delete" +skip-event-types: [] +# skip-session-types is a comma-separated list of session event types to skip. +# For example, to forward all session events except for malformed SQL packet +# events, you can include the following assignment: +# skip-session-types = "db.session.malformed_packet" +skip-session-types: [] [forward.fluentd] ca = "/home/bob/event-handler/ca.crt" @@ -48,6 +63,20 @@ eventHandler: # for the default window size. # The window size should be specified as a duration string, parsed by Go's time.ParseDuration. windowSize: "24h" + # types is a list of event types to search when forwarding audit + # events. For example, to limit forwarded events to user logins + # and new Access Requests, you can assign this field to: + # ["user.login", "access_request.create"] + types: [] + # skipEventTypes lists types of events to skip. For example, to forward all + # audit events except for new app deletion events, you can assign this to: + # ["app.delete"] + skipEventTypes: [] + # skipSessionTypes lists session event types to skip. For example, to forward + # all session events except for malformed SQL packet events, you can assign + # this to: + # ["db.session.malformed_packet"] + skipSessionTypes: [] teleport: address: "example.teleport.com:443" diff --git a/docs/pages/reference/access-controls/authentication.mdx b/docs/pages/reference/access-controls/authentication.mdx index 6568df2c1e211..2d1a8a6c7ee59 100644 --- a/docs/pages/reference/access-controls/authentication.mdx +++ b/docs/pages/reference/access-controls/authentication.mdx @@ -47,7 +47,7 @@ Add the following to your Teleport configuration file, which is stored in auth_service: authentication: type: local - second_factor: on + second_factors: ["webauthn"] webauthn: rp_id: example.teleport.sh ``` @@ -68,7 +68,7 @@ metadata: name: cluster-auth-preference spec: type: local - second_factor: "on" + second_factors: ["webauthn"] webauthn: rp_id: example.teleport.sh version: v2 @@ -102,7 +102,7 @@ metadata: name: cluster-auth-preference spec: type: local - second_factor: "on" + second_factors: ["webauthn"] webauthn: rp_id: example.teleport.sh version: v2 diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_accesslists.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-accesslists.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_accesslists.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-accesslists.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_githubconnectors.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-githubconnectors.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_githubconnectors.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-githubconnectors.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_loginrules.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-loginrules.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_loginrules.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-loginrules.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_oidcconnectors.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-oidcconnectors.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_oidcconnectors.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-oidcconnectors.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_oktaimportrules.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-oktaimportrules.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_oktaimportrules.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-oktaimportrules.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_openssheiceserversv2.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-openssheiceserversv2.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_openssheiceserversv2.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-openssheiceserversv2.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_opensshserversv2.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-opensshserversv2.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_opensshserversv2.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-opensshserversv2.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_provisiontokens.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-provisiontokens.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_provisiontokens.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-provisiontokens.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_roles.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-roles.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_roles.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-roles.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_rolesv6.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-rolesv6.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_rolesv6.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-rolesv6.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_rolesv7.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-rolesv7.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_rolesv7.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-rolesv7.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_samlconnectors.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-samlconnectors.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_samlconnectors.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-samlconnectors.mdx diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_users.mdx b/docs/pages/reference/operator-resources/resources-teleport-dev-users.mdx similarity index 100% rename from docs/pages/reference/operator-resources/resources.teleport.dev_users.mdx rename to docs/pages/reference/operator-resources/resources-teleport-dev-users.mdx diff --git a/docs/pages/reference/resources.mdx b/docs/pages/reference/resources.mdx index 7705f6456d4c9..a1371935a7f7d 100644 --- a/docs/pages/reference/resources.mdx +++ b/docs/pages/reference/resources.mdx @@ -126,7 +126,7 @@ When no `kubernetes_resource` is set: {/* This table is cursed. Our current docs engine doesn't support HTML tables (due to SSR and the rehydration process). We have dto do everything inlined in markdown. Some HTML character codes are used to render specific chars like {} -or to avoid line breaks in the middle fo the YAML. Whitespaces before br tags +or to avoid line breaks in the middle fo the YAML. Spaces before br tags are required.*/} | Allow rule | Role v5 | Role v6 | Role v7 | @@ -222,10 +222,17 @@ Global cluster configuration options for authentication. metadata: name: cluster-auth-preference spec: - # Sets the type of second factor to use. + # Sets the list of allowed second factors for the cluster. + # Possible values: "otp", "webauthn", and "sso". + # Defaults to ["otp"]. + second_factors: ["webauthn", "otp"] + + # second_factors is the list of allowed second factors for the cluster. # Possible values: "on", "otp" and "webauthn" # If "on" is set, all MFA protocols are supported. - second_factor: "otp" + # + # Prefer setting second_factors instead. + #second_factor: "webauthn" # The name of the OIDC or SAML connector. if this is not set, the first connector in the backend is used. connector_name: "" diff --git a/docs/pages/reference/signature-algorithms.mdx b/docs/pages/reference/signature-algorithms.mdx index 8b96acfe67be5..c41186f3d7bc3 100644 --- a/docs/pages/reference/signature-algorithms.mdx +++ b/docs/pages/reference/signature-algorithms.mdx @@ -192,8 +192,8 @@ in each suite. Try it and let us know! We aim to balance security, performance, and compatibility with the chosen signature algorithm suites. -It is okay to continue using the `legacy` suite for the forseeable future and we -expect it may be required for some user's environments. +It is okay to continue using the `legacy` suite for the foreseeable future and we +expect it may be required for some users' environments. ### How did you choose these algorithms? diff --git a/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx b/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx index 5a5b56875d835..369413e01b824 100644 --- a/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx @@ -41,8 +41,8 @@ Optional: - `message_of_the_day` (String) - `okta` (Attributes) Okta is a set of options related to the Okta service in Teleport. Requires Teleport Enterprise. (see [below for nested schema](#nested-schema-for-specokta)) - `require_session_mfa` (Number) RequireMFAType is the type of MFA requirement enforced for this cluster. 0 is "OFF", 1 is "SESSION", 2 is "SESSION_AND_HARDWARE_KEY", 3 is "HARDWARE_KEY_TOUCH", 4 is "HARDWARE_KEY_PIN", 5 is "HARDWARE_KEY_TOUCH_AND_PIN". -- `second_factor` (String) SecondFactor is the type of mult-factor. -- `second_factors` (List of Number) SecondFactors is a list of supported second factor types. +- `second_factor` (String) SecondFactor is the type of mult-factor. Deprecated: Prefer using SecondFactors instead. +- `second_factors` (List of Number) SecondFactors is a list of supported multi-factor types. 1 is "otp", 2 is "webauthn", 3 is "sso", If unspecified, the current default value is [1], or ["otp"]. - `signature_algorithm_suite` (Number) SignatureAlgorithmSuite is the configured signature algorithm suite for the cluster. If unspecified, the current default value is "legacy". 1 is "legacy", 2 is "balanced-v1", 3 is "fips-v1", 4 is "hsm-v1". - `type` (String) Type is the type of authentication. - `u2f` (Attributes) U2F are the settings for the U2F device. (see [below for nested schema](#nested-schema-for-specu2f)) diff --git a/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx b/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx index 92e37199d2dd6..0e6dfabb1b4d4 100644 --- a/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx @@ -34,3 +34,4 @@ The Teleport Terraform provider supports the following data-sources: - [`teleport_trusted_cluster`](./trusted_cluster.mdx) - [`teleport_trusted_device`](./trusted_device.mdx) - [`teleport_user`](./user.mdx) + - [`teleport_workload_identity`](./workload_identity.mdx) diff --git a/docs/pages/reference/terraform-provider/data-sources/workload_identity.mdx b/docs/pages/reference/terraform-provider/data-sources/workload_identity.mdx new file mode 100644 index 0000000000000..7c7e1a05a5af0 --- /dev/null +++ b/docs/pages/reference/terraform-provider/data-sources/workload_identity.mdx @@ -0,0 +1,76 @@ +--- +title: Reference for the teleport_workload_identity Terraform data-source +sidebar_label: workload_identity +description: This page describes the supported values of the teleport_workload_identity data-source of the Teleport Terraform provider. +--- + +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + + + + + +{/* schema generated by tfplugindocs */} +## Schema + +### Optional + +- `metadata` (Attributes) Common metadata that all resources share. (see [below for nested schema](#nested-schema-for-metadata)) +- `spec` (Attributes) The configured properties of the WorkloadIdentity (see [below for nested schema](#nested-schema-for-spec)) +- `sub_kind` (String) Differentiates variations of the same kind. All resources should contain one, even if it is never populated. +- `version` (String) The version of the resource being represented. + +### Nested Schema for `metadata` + +Optional: + +- `description` (String) description is object description. +- `expires` (String) expires is a global expiry time header can be set on any resource in the system. +- `labels` (Map of String) labels is a set of labels. +- `name` (String) name is an object name. + + +### Nested Schema for `spec` + +Optional: + +- `rules` (Attributes) The rules which are evaluated before the WorkloadIdentity can be issued. (see [below for nested schema](#nested-schema-for-specrules)) +- `spiffe` (Attributes) Configuration pertaining to the issuance of SPIFFE-compatible workload identity credentials. (see [below for nested schema](#nested-schema-for-specspiffe)) + +### Nested Schema for `spec.rules` + +Optional: + +- `allow` (Attributes List) A list of rules used to determine if a WorkloadIdentity can be issued. If none are provided, it will be considered a pass. If any are provided, then at least one must pass for the rules to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallow)) + +### Nested Schema for `spec.rules.allow` + +Optional: + +- `conditions` (Attributes List) The conditions that must be met for this rule to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallowconditions)) + +### Nested Schema for `spec.rules.allow.conditions` + +Optional: + +- `attribute` (String) The name of the attribute to evaluate the condition against. +- `equals` (String) An exact string that the attribute must match. + + + + +### Nested Schema for `spec.spiffe` + +Optional: + +- `hint` (String) A freeform text field which is provided to workloads along with a credential produced by this WorkloadIdentity. This can be used to provide additional context that can be used to select between multiple credentials. +- `id` (String) The path of the SPIFFE ID that will be issued to the workload. This should be prefixed with a forward-slash ("/"). This field supports templating using attributes. +- `x509` (Attributes) Configuration specific to X509-SVIDs. (see [below for nested schema](#nested-schema-for-specspiffex509)) + +### Nested Schema for `spec.spiffe.x509` + +Optional: + +- `dns_sans` (List of String) The DNS Subject Alternative Names (SANs) that should be included in an X509-SVID issued using this WorkloadIdentity. Each entry in this list supports templating using attributes. + diff --git a/docs/pages/reference/terraform-provider/resources/auth_preference.mdx b/docs/pages/reference/terraform-provider/resources/auth_preference.mdx index cfb44794a26d4..32c28f7f39f1d 100644 --- a/docs/pages/reference/terraform-provider/resources/auth_preference.mdx +++ b/docs/pages/reference/terraform-provider/resources/auth_preference.mdx @@ -60,8 +60,8 @@ Optional: - `message_of_the_day` (String) - `okta` (Attributes) Okta is a set of options related to the Okta service in Teleport. Requires Teleport Enterprise. (see [below for nested schema](#nested-schema-for-specokta)) - `require_session_mfa` (Number) RequireMFAType is the type of MFA requirement enforced for this cluster. 0 is "OFF", 1 is "SESSION", 2 is "SESSION_AND_HARDWARE_KEY", 3 is "HARDWARE_KEY_TOUCH", 4 is "HARDWARE_KEY_PIN", 5 is "HARDWARE_KEY_TOUCH_AND_PIN". -- `second_factor` (String) SecondFactor is the type of mult-factor. -- `second_factors` (List of Number) SecondFactors is a list of supported second factor types. +- `second_factor` (String) SecondFactor is the type of mult-factor. Deprecated: Prefer using SecondFactors instead. +- `second_factors` (List of Number) SecondFactors is a list of supported multi-factor types. 1 is "otp", 2 is "webauthn", 3 is "sso", If unspecified, the current default value is [1], or ["otp"]. - `signature_algorithm_suite` (Number) SignatureAlgorithmSuite is the configured signature algorithm suite for the cluster. If unspecified, the current default value is "legacy". 1 is "legacy", 2 is "balanced-v1", 3 is "fips-v1", 4 is "hsm-v1". - `type` (String) Type is the type of authentication. - `u2f` (Attributes) U2F are the settings for the U2F device. (see [below for nested schema](#nested-schema-for-specu2f)) diff --git a/docs/pages/reference/terraform-provider/resources/resources.mdx b/docs/pages/reference/terraform-provider/resources/resources.mdx index 51d7bb8d2e3b3..e962f85c38abb 100644 --- a/docs/pages/reference/terraform-provider/resources/resources.mdx +++ b/docs/pages/reference/terraform-provider/resources/resources.mdx @@ -36,3 +36,4 @@ The Teleport Terraform provider supports the following resources: - [`teleport_trusted_cluster`](./trusted_cluster.mdx) - [`teleport_trusted_device`](./trusted_device.mdx) - [`teleport_user`](./user.mdx) + - [`teleport_workload_identity`](./workload_identity.mdx) diff --git a/docs/pages/reference/terraform-provider/resources/workload_identity.mdx b/docs/pages/reference/terraform-provider/resources/workload_identity.mdx new file mode 100644 index 0000000000000..fbbeb1306abd8 --- /dev/null +++ b/docs/pages/reference/terraform-provider/resources/workload_identity.mdx @@ -0,0 +1,101 @@ +--- +title: Reference for the teleport_workload_identity Terraform resource +sidebar_label: workload_identity +description: This page describes the supported values of the teleport_workload_identity resource of the Teleport Terraform provider. +--- + +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + + + +## Example Usage + +```hcl +resource "teleport_workload_identity" "example" { + version = "v1" + metadata = { + name = "example" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "noah" + }] + } + ] + } + spiffe = { + id = "/my/spiffe/id/path" + hint = "my-hint" + } + } +} +``` + +{/* schema generated by tfplugindocs */} +## Schema + +### Optional + +- `metadata` (Attributes) Common metadata that all resources share. (see [below for nested schema](#nested-schema-for-metadata)) +- `spec` (Attributes) The configured properties of the WorkloadIdentity (see [below for nested schema](#nested-schema-for-spec)) +- `sub_kind` (String) Differentiates variations of the same kind. All resources should contain one, even if it is never populated. +- `version` (String) The version of the resource being represented. + +### Nested Schema for `metadata` + +Optional: + +- `description` (String) description is object description. +- `expires` (String) expires is a global expiry time header can be set on any resource in the system. +- `labels` (Map of String) labels is a set of labels. +- `name` (String) name is an object name. + + +### Nested Schema for `spec` + +Optional: + +- `rules` (Attributes) The rules which are evaluated before the WorkloadIdentity can be issued. (see [below for nested schema](#nested-schema-for-specrules)) +- `spiffe` (Attributes) Configuration pertaining to the issuance of SPIFFE-compatible workload identity credentials. (see [below for nested schema](#nested-schema-for-specspiffe)) + +### Nested Schema for `spec.rules` + +Optional: + +- `allow` (Attributes List) A list of rules used to determine if a WorkloadIdentity can be issued. If none are provided, it will be considered a pass. If any are provided, then at least one must pass for the rules to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallow)) + +### Nested Schema for `spec.rules.allow` + +Optional: + +- `conditions` (Attributes List) The conditions that must be met for this rule to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallowconditions)) + +### Nested Schema for `spec.rules.allow.conditions` + +Optional: + +- `attribute` (String) The name of the attribute to evaluate the condition against. +- `equals` (String) An exact string that the attribute must match. + + + + +### Nested Schema for `spec.spiffe` + +Optional: + +- `hint` (String) A freeform text field which is provided to workloads along with a credential produced by this WorkloadIdentity. This can be used to provide additional context that can be used to select between multiple credentials. +- `id` (String) The path of the SPIFFE ID that will be issued to the workload. This should be prefixed with a forward-slash ("/"). This field supports templating using attributes. +- `x509` (Attributes) Configuration specific to X509-SVIDs. (see [below for nested schema](#nested-schema-for-specspiffex509)) + +### Nested Schema for `spec.spiffe.x509` + +Optional: + +- `dns_sans` (List of String) The DNS Subject Alternative Names (SANs) that should be included in an X509-SVID issued using this WorkloadIdentity. Each entry in this list supports templating using attributes. + diff --git a/docs/pages/reference/user-types.mdx b/docs/pages/reference/user-types.mdx index 07ef90fcff601..962a8ec3d5c69 100644 --- a/docs/pages/reference/user-types.mdx +++ b/docs/pages/reference/user-types.mdx @@ -50,7 +50,7 @@ authentication method required by the upstream SSO provider. Teleport is not aware of the authentication method not the user credentials, it trusts the IdP response. -If `teleport.auth_service.authentication.second_factor` is `webauthn`, Teleport +If `teleport.auth_service.authentication.second_factors` is `["webauthn"]`, Teleport might ask for an additional MFA for administrative actions. This protects against IdP compromise. diff --git a/e b/e index 7d37990b7f263..f00dbc995fee6 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit 7d37990b7f2637d31cc9cc4c5b036f74dd512740 +Subproject commit f00dbc995fee6e9159442c62300026e32759e86a diff --git a/examples/aws/terraform/AMIS.md b/examples/aws/terraform/AMIS.md index c9006e85440da..a3f62aeb1b533 100644 --- a/examples/aws/terraform/AMIS.md +++ b/examples/aws/terraform/AMIS.md @@ -6,116 +6,116 @@ This list is updated when new AMI versions are released. ### OSS ``` -# ap-northeast-1 v17.1.0 arm64 OSS: ami-05d48ab4187b69aad -# ap-northeast-1 v17.1.0 x86_64 OSS: ami-0771bb7ea243be53e -# ap-northeast-2 v17.1.0 arm64 OSS: ami-07184d83509a4de9f -# ap-northeast-2 v17.1.0 x86_64 OSS: ami-0f77764082d35f51f -# ap-northeast-3 v17.1.0 arm64 OSS: ami-030b6b0910f582924 -# ap-northeast-3 v17.1.0 x86_64 OSS: ami-0fea7130c366b6660 -# ap-south-1 v17.1.0 arm64 OSS: ami-00536811675fb7ca5 -# ap-south-1 v17.1.0 x86_64 OSS: ami-004c201af536d8c4c -# ap-southeast-1 v17.1.0 arm64 OSS: ami-0512146122ffad85e -# ap-southeast-1 v17.1.0 x86_64 OSS: ami-0a06cfa6a7088c06a -# ap-southeast-2 v17.1.0 arm64 OSS: ami-04ee7db250ee72884 -# ap-southeast-2 v17.1.0 x86_64 OSS: ami-02827e89f41e222c2 -# ca-central-1 v17.1.0 arm64 OSS: ami-0bea774e474572c9a -# ca-central-1 v17.1.0 x86_64 OSS: ami-09b1350d81173ca9f -# eu-central-1 v17.1.0 arm64 OSS: ami-006b6efbc199ba1b7 -# eu-central-1 v17.1.0 x86_64 OSS: ami-0d350bffc56ed6a7e -# eu-north-1 v17.1.0 arm64 OSS: ami-025c9c63e6681a10c -# eu-north-1 v17.1.0 x86_64 OSS: ami-0f757ccc0186db97b -# eu-west-1 v17.1.0 arm64 OSS: ami-0a628413a28dc743e -# eu-west-1 v17.1.0 x86_64 OSS: ami-0c44493ee0f75b08f -# eu-west-2 v17.1.0 arm64 OSS: ami-074beb9d44ee7f8b0 -# eu-west-2 v17.1.0 x86_64 OSS: ami-0526b4f61f6c8ac19 -# eu-west-3 v17.1.0 arm64 OSS: ami-09f0631b07f10f700 -# eu-west-3 v17.1.0 x86_64 OSS: ami-0a3c6bbf9ed4b06fb -# sa-east-1 v17.1.0 arm64 OSS: ami-08e90ce1a60c5d9d7 -# sa-east-1 v17.1.0 x86_64 OSS: ami-0714cd595e4739862 -# us-east-1 v17.1.0 arm64 OSS: ami-060da265f62727bf6 -# us-east-1 v17.1.0 x86_64 OSS: ami-0a481eea172b4293d -# us-east-2 v17.1.0 arm64 OSS: ami-04efea40fcbc97719 -# us-east-2 v17.1.0 x86_64 OSS: ami-0cc8d9116cc499a3c -# us-west-1 v17.1.0 arm64 OSS: ami-063e9e5c822a37abb -# us-west-1 v17.1.0 x86_64 OSS: ami-0f423d0d5bd921fa3 -# us-west-2 v17.1.0 arm64 OSS: ami-073a4498a5bdb3d9f -# us-west-2 v17.1.0 x86_64 OSS: ami-0f091942faaef349a +# ap-northeast-1 v17.1.4 arm64 OSS: ami-04c280237d4cf0833 +# ap-northeast-1 v17.1.4 x86_64 OSS: ami-06c1f487a00ef61b5 +# ap-northeast-2 v17.1.4 arm64 OSS: ami-0fcb1303377d3d4a8 +# ap-northeast-2 v17.1.4 x86_64 OSS: ami-048b699cbd8d7f796 +# ap-northeast-3 v17.1.4 arm64 OSS: ami-0500cd373b82cdb95 +# ap-northeast-3 v17.1.4 x86_64 OSS: ami-03d321729da0ec81a +# ap-south-1 v17.1.4 arm64 OSS: ami-08f91f9655adf0a5a +# ap-south-1 v17.1.4 x86_64 OSS: ami-01807acbcce023395 +# ap-southeast-1 v17.1.4 arm64 OSS: ami-0294f7de9d7db6cfc +# ap-southeast-1 v17.1.4 x86_64 OSS: ami-01ebe20b36e44cce4 +# ap-southeast-2 v17.1.4 arm64 OSS: ami-0ea7d4f47467e9841 +# ap-southeast-2 v17.1.4 x86_64 OSS: ami-0d50c370428d73bf7 +# ca-central-1 v17.1.4 arm64 OSS: ami-0f7d7f8643255425e +# ca-central-1 v17.1.4 x86_64 OSS: ami-0d36e40088c427ffa +# eu-central-1 v17.1.4 arm64 OSS: ami-06e74483401b0daad +# eu-central-1 v17.1.4 x86_64 OSS: ami-01ac616f4ecf8c7f8 +# eu-north-1 v17.1.4 arm64 OSS: ami-0817d77460aacdcdf +# eu-north-1 v17.1.4 x86_64 OSS: ami-008164fe024c0c77d +# eu-west-1 v17.1.4 arm64 OSS: ami-082271d71f55708d3 +# eu-west-1 v17.1.4 x86_64 OSS: ami-0da783c76f4a93de9 +# eu-west-2 v17.1.4 arm64 OSS: ami-0ca02a6c5eea6e798 +# eu-west-2 v17.1.4 x86_64 OSS: ami-0dea7bba37863918a +# eu-west-3 v17.1.4 arm64 OSS: ami-033f4214500d59041 +# eu-west-3 v17.1.4 x86_64 OSS: ami-024a03120d33206cc +# sa-east-1 v17.1.4 arm64 OSS: ami-04ae82102f936f84f +# sa-east-1 v17.1.4 x86_64 OSS: ami-06e5d76c71fd17a97 +# us-east-1 v17.1.4 arm64 OSS: ami-06aa1a489c76bbdae +# us-east-1 v17.1.4 x86_64 OSS: ami-0005ac0b4ab7b509d +# us-east-2 v17.1.4 arm64 OSS: ami-002f52ecac8e7deb2 +# us-east-2 v17.1.4 x86_64 OSS: ami-0256f105240d7b918 +# us-west-1 v17.1.4 arm64 OSS: ami-0c1aab7963bae4258 +# us-west-1 v17.1.4 x86_64 OSS: ami-0a114d514ce39c078 +# us-west-2 v17.1.4 arm64 OSS: ami-075ad0d96dd7431c2 +# us-west-2 v17.1.4 x86_64 OSS: ami-0b140a8f7580e17ac ``` ### Enterprise ``` -# ap-northeast-1 v17.1.0 arm64 Enterprise: ami-0054beb624b93a3d0 -# ap-northeast-1 v17.1.0 x86_64 Enterprise: ami-0dbef8379e4c2c225 -# ap-northeast-2 v17.1.0 arm64 Enterprise: ami-0c38f0f52cd32a5ed -# ap-northeast-2 v17.1.0 x86_64 Enterprise: ami-049e5fbf9f1d9fce8 -# ap-northeast-3 v17.1.0 arm64 Enterprise: ami-00656bf0363c42865 -# ap-northeast-3 v17.1.0 x86_64 Enterprise: ami-001609c2cb7449fbc -# ap-south-1 v17.1.0 arm64 Enterprise: ami-036ece94f64bf3ea7 -# ap-south-1 v17.1.0 x86_64 Enterprise: ami-0b42050e95f926aeb -# ap-southeast-1 v17.1.0 arm64 Enterprise: ami-06a26bb15bd9ca42a -# ap-southeast-1 v17.1.0 x86_64 Enterprise: ami-0c5cf93801310fc9c -# ap-southeast-2 v17.1.0 arm64 Enterprise: ami-0a3bc5db7482b39e8 -# ap-southeast-2 v17.1.0 x86_64 Enterprise: ami-063a7f0c4ce5b1db2 -# ca-central-1 v17.1.0 arm64 Enterprise: ami-080d8bd9ad1ab8fc7 -# ca-central-1 v17.1.0 x86_64 Enterprise: ami-00a89e0f143218b4f -# eu-central-1 v17.1.0 arm64 Enterprise: ami-0c24ed4801d5d8930 -# eu-central-1 v17.1.0 x86_64 Enterprise: ami-08e2317992e40cae6 -# eu-north-1 v17.1.0 arm64 Enterprise: ami-0272e7fe996421cb3 -# eu-north-1 v17.1.0 x86_64 Enterprise: ami-0f324a587f30af7a9 -# eu-west-1 v17.1.0 arm64 Enterprise: ami-0a016a18b345679c8 -# eu-west-1 v17.1.0 x86_64 Enterprise: ami-092241ead9afd9dfe -# eu-west-2 v17.1.0 arm64 Enterprise: ami-098ca4be33a079de5 -# eu-west-2 v17.1.0 x86_64 Enterprise: ami-05d03222fd3f32735 -# eu-west-3 v17.1.0 arm64 Enterprise: ami-05f4a7124ca6c2c66 -# eu-west-3 v17.1.0 x86_64 Enterprise: ami-0bf3fff32d23ef3c1 -# sa-east-1 v17.1.0 arm64 Enterprise: ami-0610008b54140b89b -# sa-east-1 v17.1.0 x86_64 Enterprise: ami-00da41d2bf0698b3f -# us-east-1 v17.1.0 arm64 Enterprise: ami-0bf51a258f80e2ec2 -# us-east-1 v17.1.0 x86_64 Enterprise: ami-0b1da60eb131922ab -# us-east-2 v17.1.0 arm64 Enterprise: ami-0a4367cf4cb92be24 -# us-east-2 v17.1.0 x86_64 Enterprise: ami-0c4bc58b0d9ef0c61 -# us-west-1 v17.1.0 arm64 Enterprise: ami-0660af60a0aba46c1 -# us-west-1 v17.1.0 x86_64 Enterprise: ami-05c3f83de44830858 -# us-west-2 v17.1.0 arm64 Enterprise: ami-0f2da6ca41d40371a -# us-west-2 v17.1.0 x86_64 Enterprise: ami-0797199b139ee8a16 +# ap-northeast-1 v17.1.4 arm64 Enterprise: ami-02d10e31e68847a0c +# ap-northeast-1 v17.1.4 x86_64 Enterprise: ami-0b61e2c8b50d9d62b +# ap-northeast-2 v17.1.4 arm64 Enterprise: ami-0a6adcba917acacb6 +# ap-northeast-2 v17.1.4 x86_64 Enterprise: ami-0c003f45be68328f8 +# ap-northeast-3 v17.1.4 arm64 Enterprise: ami-0dbe9569a28fd8b55 +# ap-northeast-3 v17.1.4 x86_64 Enterprise: ami-04bdde4e67bd9db4a +# ap-south-1 v17.1.4 arm64 Enterprise: ami-0ea9b27a5d3f21371 +# ap-south-1 v17.1.4 x86_64 Enterprise: ami-017e7ffef7a748e6e +# ap-southeast-1 v17.1.4 arm64 Enterprise: ami-0d8cf314be9951880 +# ap-southeast-1 v17.1.4 x86_64 Enterprise: ami-0c25299871d0e5eff +# ap-southeast-2 v17.1.4 arm64 Enterprise: ami-09708e43fa35bfdbb +# ap-southeast-2 v17.1.4 x86_64 Enterprise: ami-03ddab1dbceaf923a +# ca-central-1 v17.1.4 arm64 Enterprise: ami-0a81ed114fc7aa1f4 +# ca-central-1 v17.1.4 x86_64 Enterprise: ami-01389b21a85fbda71 +# eu-central-1 v17.1.4 arm64 Enterprise: ami-057bec1f2f501b07b +# eu-central-1 v17.1.4 x86_64 Enterprise: ami-0dd60768649574583 +# eu-north-1 v17.1.4 arm64 Enterprise: ami-0c8013d555ba72fbb +# eu-north-1 v17.1.4 x86_64 Enterprise: ami-062b25e011c8c34e5 +# eu-west-1 v17.1.4 arm64 Enterprise: ami-0d16e9afac0643837 +# eu-west-1 v17.1.4 x86_64 Enterprise: ami-0e9c876c7e9bc0b00 +# eu-west-2 v17.1.4 arm64 Enterprise: ami-04b12e3b27aad7e52 +# eu-west-2 v17.1.4 x86_64 Enterprise: ami-0c990dd569d908035 +# eu-west-3 v17.1.4 arm64 Enterprise: ami-06f86ffaa20f8b62b +# eu-west-3 v17.1.4 x86_64 Enterprise: ami-0e4200ff5ea85055e +# sa-east-1 v17.1.4 arm64 Enterprise: ami-051c982de689ffa7b +# sa-east-1 v17.1.4 x86_64 Enterprise: ami-05b67968ad4784107 +# us-east-1 v17.1.4 arm64 Enterprise: ami-0b46e41e985c66c67 +# us-east-1 v17.1.4 x86_64 Enterprise: ami-0058d1d106d6c05eb +# us-east-2 v17.1.4 arm64 Enterprise: ami-072b5fd705008644b +# us-east-2 v17.1.4 x86_64 Enterprise: ami-04a2f283333041d0f +# us-west-1 v17.1.4 arm64 Enterprise: ami-01973af051056842c +# us-west-1 v17.1.4 x86_64 Enterprise: ami-0d5824cdcbb00e382 +# us-west-2 v17.1.4 arm64 Enterprise: ami-042cdd70f934b5d23 +# us-west-2 v17.1.4 x86_64 Enterprise: ami-0ad8d190b27b4959e ``` ### Enterprise FIPS ``` -# ap-northeast-1 v17.1.0 arm64 Enterprise FIPS: ami-006e277130d6697b8 -# ap-northeast-1 v17.1.0 x86_64 Enterprise FIPS: ami-0d2097cc785d5ddd1 -# ap-northeast-2 v17.1.0 arm64 Enterprise FIPS: ami-05d7526f2fa772bae -# ap-northeast-2 v17.1.0 x86_64 Enterprise FIPS: ami-0b95d703d0fb35264 -# ap-northeast-3 v17.1.0 arm64 Enterprise FIPS: ami-058a1688e3746346f -# ap-northeast-3 v17.1.0 x86_64 Enterprise FIPS: ami-0a3609711beac9b12 -# ap-south-1 v17.1.0 arm64 Enterprise FIPS: ami-04fd535409f9546b2 -# ap-south-1 v17.1.0 x86_64 Enterprise FIPS: ami-0c8eb1f599b03ba3d -# ap-southeast-1 v17.1.0 arm64 Enterprise FIPS: ami-0859705827146286a -# ap-southeast-1 v17.1.0 x86_64 Enterprise FIPS: ami-08ce8518812498f8e -# ap-southeast-2 v17.1.0 arm64 Enterprise FIPS: ami-099243139967a52ef -# ap-southeast-2 v17.1.0 x86_64 Enterprise FIPS: ami-04e9d6967cf82c029 -# ca-central-1 v17.1.0 arm64 Enterprise FIPS: ami-0724d756ac222374c -# ca-central-1 v17.1.0 x86_64 Enterprise FIPS: ami-0ba9b501211abdfa3 -# eu-central-1 v17.1.0 arm64 Enterprise FIPS: ami-0db5328407fdf56cb -# eu-central-1 v17.1.0 x86_64 Enterprise FIPS: ami-0c811a9693f2ddb6e -# eu-north-1 v17.1.0 arm64 Enterprise FIPS: ami-0e70137a138a7c919 -# eu-north-1 v17.1.0 x86_64 Enterprise FIPS: ami-01ed85ddc0cdbafb4 -# eu-west-1 v17.1.0 arm64 Enterprise FIPS: ami-04678f3cda0b28ae8 -# eu-west-1 v17.1.0 x86_64 Enterprise FIPS: ami-0f2ccfb2586e70f99 -# eu-west-2 v17.1.0 arm64 Enterprise FIPS: ami-0e89768c72aa20b1a -# eu-west-2 v17.1.0 x86_64 Enterprise FIPS: ami-0fb819f8af7d2c478 -# eu-west-3 v17.1.0 arm64 Enterprise FIPS: ami-0e6824016a67d2446 -# eu-west-3 v17.1.0 x86_64 Enterprise FIPS: ami-010efc79993d1ea33 -# sa-east-1 v17.1.0 arm64 Enterprise FIPS: ami-0658ae24643ca38e6 -# sa-east-1 v17.1.0 x86_64 Enterprise FIPS: ami-0d14d90a5352b3771 -# us-east-1 v17.1.0 arm64 Enterprise FIPS: ami-08d29a6b64f3344be -# us-east-1 v17.1.0 x86_64 Enterprise FIPS: ami-0430151efc1689906 -# us-east-2 v17.1.0 arm64 Enterprise FIPS: ami-0da38d632101bf3d1 -# us-east-2 v17.1.0 x86_64 Enterprise FIPS: ami-09340caeb2a02f47d -# us-west-1 v17.1.0 arm64 Enterprise FIPS: ami-0456b2b106888013e -# us-west-1 v17.1.0 x86_64 Enterprise FIPS: ami-0b58799c4f10967c2 -# us-west-2 v17.1.0 arm64 Enterprise FIPS: ami-06b404ae4fed6d673 -# us-west-2 v17.1.0 x86_64 Enterprise FIPS: ami-084c64cf7ead63339 +# ap-northeast-1 v17.1.4 arm64 Enterprise FIPS: ami-04cbf7a26f5eaac9d +# ap-northeast-1 v17.1.4 x86_64 Enterprise FIPS: ami-047907213df5405a1 +# ap-northeast-2 v17.1.4 arm64 Enterprise FIPS: ami-0a722ef42b507fb48 +# ap-northeast-2 v17.1.4 x86_64 Enterprise FIPS: ami-046b89d1a9f5ff679 +# ap-northeast-3 v17.1.4 arm64 Enterprise FIPS: ami-0553a49a294e333fe +# ap-northeast-3 v17.1.4 x86_64 Enterprise FIPS: ami-00b8a9fc7cda5047e +# ap-south-1 v17.1.4 arm64 Enterprise FIPS: ami-0d0c4fc6499981168 +# ap-south-1 v17.1.4 x86_64 Enterprise FIPS: ami-079016557d21dbbff +# ap-southeast-1 v17.1.4 arm64 Enterprise FIPS: ami-0768fae575f8355ed +# ap-southeast-1 v17.1.4 x86_64 Enterprise FIPS: ami-07d8f2a1ddb624e07 +# ap-southeast-2 v17.1.4 arm64 Enterprise FIPS: ami-00c05cda63b8c4022 +# ap-southeast-2 v17.1.4 x86_64 Enterprise FIPS: ami-0b40ec54a1bfabc6e +# ca-central-1 v17.1.4 arm64 Enterprise FIPS: ami-01ec31b58a34bf114 +# ca-central-1 v17.1.4 x86_64 Enterprise FIPS: ami-00b5997c74ec67a6d +# eu-central-1 v17.1.4 arm64 Enterprise FIPS: ami-0316e6fef72ee12f1 +# eu-central-1 v17.1.4 x86_64 Enterprise FIPS: ami-0bffb2b82d19b9fe4 +# eu-north-1 v17.1.4 arm64 Enterprise FIPS: ami-0cc803ee0103b5452 +# eu-north-1 v17.1.4 x86_64 Enterprise FIPS: ami-026e1dad3b3338ced +# eu-west-1 v17.1.4 arm64 Enterprise FIPS: ami-02aa57ad66c706c3e +# eu-west-1 v17.1.4 x86_64 Enterprise FIPS: ami-01cce0cb789f1244b +# eu-west-2 v17.1.4 arm64 Enterprise FIPS: ami-0279a9754f3fc1687 +# eu-west-2 v17.1.4 x86_64 Enterprise FIPS: ami-0f517fc8be59a56b4 +# eu-west-3 v17.1.4 arm64 Enterprise FIPS: ami-03007e46071d1691e +# eu-west-3 v17.1.4 x86_64 Enterprise FIPS: ami-0685cfbc21fe131af +# sa-east-1 v17.1.4 arm64 Enterprise FIPS: ami-0647e36b1c825d8d5 +# sa-east-1 v17.1.4 x86_64 Enterprise FIPS: ami-0e253b6060ae1cddd +# us-east-1 v17.1.4 arm64 Enterprise FIPS: ami-042cc5ce729365547 +# us-east-1 v17.1.4 x86_64 Enterprise FIPS: ami-0870e3e82bb0c6889 +# us-east-2 v17.1.4 arm64 Enterprise FIPS: ami-0ebfe9f7ecb7abdfe +# us-east-2 v17.1.4 x86_64 Enterprise FIPS: ami-0494af14fdf669b2e +# us-west-1 v17.1.4 arm64 Enterprise FIPS: ami-0df427cdcd044f1aa +# us-west-1 v17.1.4 x86_64 Enterprise FIPS: ami-034ee42a899329ad4 +# us-west-2 v17.1.4 arm64 Enterprise FIPS: ami-09250b44b4e05592f +# us-west-2 v17.1.4 x86_64 Enterprise FIPS: ami-055c4b60b96a52656 ``` diff --git a/examples/aws/terraform/ha-autoscale-cluster/README.md b/examples/aws/terraform/ha-autoscale-cluster/README.md index 3aea34e392677..4c6b8bbcaee9a 100644 --- a/examples/aws/terraform/ha-autoscale-cluster/README.md +++ b/examples/aws/terraform/ha-autoscale-cluster/README.md @@ -46,7 +46,7 @@ export TF_VAR_cluster_name="teleport.example.com" # OSS: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-oss-*' # Enterprise: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-ent-*' # FIPS 140-2 images are also available for Enterprise customers, look for '-fips' on the end of the AMI's name -export TF_VAR_ami_name="teleport-ent-17.1.0-arm64" +export TF_VAR_ami_name="teleport-ent-17.1.4-arm64" # Instance types used for authentication server auto scaling group # This should match to the AMI instance architecture type, ARM or x86 diff --git a/examples/aws/terraform/starter-cluster/README.md b/examples/aws/terraform/starter-cluster/README.md index be6ef3107f3f3..6f0fe03b295cf 100644 --- a/examples/aws/terraform/starter-cluster/README.md +++ b/examples/aws/terraform/starter-cluster/README.md @@ -98,7 +98,7 @@ TF_VAR_license_path ?= "/path/to/license" # OSS: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-oss-*' # Enterprise: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-ent-*' # FIPS 140-2 images are also available for Enterprise customers, look for '-fips' on the end of the AMI's name -TF_VAR_ami_name ?= "teleport-ent-17.1.0-arm64" +TF_VAR_ami_name ?= "teleport-ent-17.1.4-arm64" # Route 53 hosted zone to use, must be a root zone registered in AWS, e.g. example.com TF_VAR_route53_zone ?= "example.com" diff --git a/examples/chart/access/datadog/Chart.yaml b/examples/chart/access/datadog/Chart.yaml index cd2ee76d02052..780b08f38eb06 100644 --- a/examples/chart/access/datadog/Chart.yaml +++ b/examples/chart/access/datadog/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-datadog diff --git a/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap index 501c844054d76..4c0e2f9ccf058 100644 --- a/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap @@ -26,6 +26,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-datadog - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-datadog-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-datadog-18.0.0-dev name: RELEASE-NAME-teleport-plugin-datadog diff --git a/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap index 2059ce41c0b04..90fca5a21d942 100644 --- a/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-datadog - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-datadog-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-datadog-18.0.0-dev name: RELEASE-NAME-teleport-plugin-datadog spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-datadog - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-datadog-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-datadog-18.0.0-dev spec: containers: - command: diff --git a/examples/chart/access/discord/Chart.yaml b/examples/chart/access/discord/Chart.yaml index 345bb8c893c0b..1dd61c72910d9 100644 --- a/examples/chart/access/discord/Chart.yaml +++ b/examples/chart/access/discord/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-discord diff --git a/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap index d2ca5c3a439b3..743646baa06b0 100644 --- a/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap @@ -24,6 +24,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-discord - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-discord-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-discord-18.0.0-dev name: RELEASE-NAME-teleport-plugin-discord diff --git a/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap index 208c6936060fc..cf255389b1d94 100644 --- a/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-discord - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-discord-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-discord-18.0.0-dev name: RELEASE-NAME-teleport-plugin-discord spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-discord - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-discord-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-discord-18.0.0-dev spec: containers: - command: diff --git a/examples/chart/access/email/Chart.yaml b/examples/chart/access/email/Chart.yaml index f589b552357d2..0f661981e882a 100644 --- a/examples/chart/access/email/Chart.yaml +++ b/examples/chart/access/email/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-email diff --git a/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap index 07109f0b0b1f9..7e8b22428edd0 100644 --- a/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap @@ -26,8 +26,8 @@ should match the snapshot (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on): 1: | @@ -59,8 +59,8 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, no starttls): 1: | @@ -92,8 +92,8 @@ should match the snapshot (smtp on, no starttls): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, password file): 1: | @@ -125,8 +125,8 @@ should match the snapshot (smtp on, password file): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, roleToRecipients set): 1: | @@ -161,8 +161,8 @@ should match the snapshot (smtp on, roleToRecipients set): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, starttls disabled): 1: | @@ -194,6 +194,6 @@ should match the snapshot (smtp on, starttls disabled): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email diff --git a/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap index b7e81f481eedb..a00d0d6644629 100644 --- a/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should be possible to override volume name (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -22,8 +22,8 @@ should be possible to override volume name (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev spec: containers: - command: @@ -34,7 +34,7 @@ should be possible to override volume name (smtp on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-email:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -75,8 +75,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -90,8 +90,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev spec: containers: - command: @@ -136,8 +136,8 @@ should match the snapshot (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -151,8 +151,8 @@ should match the snapshot (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev spec: containers: - command: @@ -163,7 +163,7 @@ should match the snapshot (mailgun on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-email:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -204,8 +204,8 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -219,8 +219,8 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev spec: containers: - command: @@ -231,7 +231,7 @@ should match the snapshot (smtp on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-email:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -272,8 +272,8 @@ should mount external secret (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -287,8 +287,8 @@ should mount external secret (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev spec: containers: - command: @@ -299,7 +299,7 @@ should mount external secret (mailgun on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-email:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -340,8 +340,8 @@ should mount external secret (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -355,8 +355,8 @@ should mount external secret (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-email-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-email-18.0.0-dev spec: containers: - command: @@ -367,7 +367,7 @@ should mount external secret (smtp on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-email:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: diff --git a/examples/chart/access/jira/Chart.yaml b/examples/chart/access/jira/Chart.yaml index bda1cd653e046..7efa93cb9eb86 100644 --- a/examples/chart/access/jira/Chart.yaml +++ b/examples/chart/access/jira/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-jira diff --git a/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap index d1a5b3b3d5390..c3eb26a29fb70 100644 --- a/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap @@ -32,6 +32,6 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-jira - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-jira-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-jira-18.0.0-dev name: RELEASE-NAME-teleport-plugin-jira diff --git a/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap index 6911e6e41ed78..6beb3d5e41d09 100644 --- a/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-jira - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-jira-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-jira-18.0.0-dev name: RELEASE-NAME-teleport-plugin-jira spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-jira - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-jira-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-jira-18.0.0-dev spec: containers: - command: diff --git a/examples/chart/access/mattermost/Chart.yaml b/examples/chart/access/mattermost/Chart.yaml index 2f171d58f08da..074f9ca626108 100644 --- a/examples/chart/access/mattermost/Chart.yaml +++ b/examples/chart/access/mattermost/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-mattermost diff --git a/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap index 0b130d51c7218..770c441b8ac59 100644 --- a/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap @@ -22,6 +22,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-mattermost-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-mattermost-18.0.0-dev name: RELEASE-NAME-teleport-plugin-mattermost diff --git a/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap index 723e8279c09d3..b087c6fe56263 100644 --- a/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-mattermost-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-mattermost-18.0.0-dev name: RELEASE-NAME-teleport-plugin-mattermost spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-mattermost-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-mattermost-18.0.0-dev spec: containers: - command: @@ -75,8 +75,8 @@ should mount external secret: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-mattermost-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-mattermost-18.0.0-dev name: RELEASE-NAME-teleport-plugin-mattermost spec: replicas: 1 @@ -90,8 +90,8 @@ should mount external secret: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-mattermost-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-mattermost-18.0.0-dev spec: containers: - command: @@ -102,7 +102,7 @@ should mount external secret: env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-mattermost:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-mattermost:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-mattermost ports: @@ -143,8 +143,8 @@ should override volume name: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-mattermost-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-mattermost-18.0.0-dev name: RELEASE-NAME-teleport-plugin-mattermost spec: replicas: 1 @@ -158,8 +158,8 @@ should override volume name: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-mattermost-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-mattermost-18.0.0-dev spec: containers: - command: @@ -170,7 +170,7 @@ should override volume name: env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-mattermost:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-mattermost:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-mattermost ports: diff --git a/examples/chart/access/msteams/Chart.yaml b/examples/chart/access/msteams/Chart.yaml index 9ba10864dc698..efa37f9118153 100644 --- a/examples/chart/access/msteams/Chart.yaml +++ b/examples/chart/access/msteams/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-msteams diff --git a/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap index 553e036515e21..9823f589ccdf6 100644 --- a/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap @@ -29,6 +29,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-msteams - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-msteams-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-msteams-18.0.0-dev name: RELEASE-NAME-teleport-plugin-msteams diff --git a/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap index 0c95e9d24e3ff..b1b3c735e173c 100644 --- a/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-msteams - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-msteams-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-msteams-18.0.0-dev name: RELEASE-NAME-teleport-plugin-msteams spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-msteams - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-msteams-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-msteams-18.0.0-dev spec: containers: - command: diff --git a/examples/chart/access/pagerduty/Chart.yaml b/examples/chart/access/pagerduty/Chart.yaml index 711acf2fa83e8..721e204baa89b 100644 --- a/examples/chart/access/pagerduty/Chart.yaml +++ b/examples/chart/access/pagerduty/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-pagerduty diff --git a/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap index 64083c3642b5c..7b5b7d971c412 100644 --- a/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap @@ -21,6 +21,6 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-pagerduty - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-pagerduty-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-pagerduty-18.0.0-dev name: RELEASE-NAME-teleport-plugin-pagerduty diff --git a/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap index c44a3284ccc85..fc4b46644d074 100644 --- a/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-pagerduty - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-pagerduty-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-pagerduty-18.0.0-dev name: RELEASE-NAME-teleport-plugin-pagerduty spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-pagerduty - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-pagerduty-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-pagerduty-18.0.0-dev spec: containers: - command: diff --git a/examples/chart/access/slack/Chart.yaml b/examples/chart/access/slack/Chart.yaml index d295bc30b3f90..7d4b14b6be3c4 100644 --- a/examples/chart/access/slack/Chart.yaml +++ b/examples/chart/access/slack/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-slack diff --git a/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap index 78e6b06bb439a..734127f067b8c 100644 --- a/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap @@ -24,6 +24,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-slack - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-slack-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-slack-18.0.0-dev name: RELEASE-NAME-teleport-plugin-slack diff --git a/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap index d19f3c2783750..0dc816f98e1ad 100644 --- a/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-slack - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-slack-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-slack-18.0.0-dev name: RELEASE-NAME-teleport-plugin-slack spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-slack - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-slack-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-slack-18.0.0-dev spec: containers: - command: diff --git a/examples/chart/event-handler/Chart.yaml b/examples/chart/event-handler/Chart.yaml index 7eaacdb7f530b..45d3d52a5bfcd 100644 --- a/examples/chart/event-handler/Chart.yaml +++ b/examples/chart/event-handler/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" apiVersion: v2 name: teleport-plugin-event-handler diff --git a/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap index 4d0b7a15e3ad0..96c2b224a922f 100644 --- a/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap @@ -26,6 +26,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-event-handler - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-event-handler-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-event-handler-18.0.0-dev name: RELEASE-NAME-teleport-plugin-event-handler diff --git a/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap index d7a6dc7a36b7a..8e4c4a3071659 100644 --- a/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-event-handler - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-plugin-event-handler-17.0.0-dev + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-plugin-event-handler-18.0.0-dev name: RELEASE-NAME-teleport-plugin-event-handler spec: replicas: 1 @@ -82,7 +82,7 @@ should mount tls.existingCASecretName and set environment when set in values: value: "true" - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-plugin-event-handler:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-plugin-event-handler:18.0.0-dev imagePullPolicy: IfNotPresent name: teleport-plugin-event-handler ports: diff --git a/examples/chart/tbot/.lint/full.yaml b/examples/chart/tbot/.lint/full.yaml index 706b3d207ee5c..9d076782b506e 100644 --- a/examples/chart/tbot/.lint/full.yaml +++ b/examples/chart/tbot/.lint/full.yaml @@ -1,7 +1,7 @@ clusterName: "test.teleport.sh" teleportAuthAddress: "my-auth:3024" defaultOutput: - enabled: false + enabled: true token: "my-token" joinMethod: "modified-join-method" diff --git a/examples/chart/tbot/Chart.yaml b/examples/chart/tbot/Chart.yaml index 5371135557a01..572025c41c937 100644 --- a/examples/chart/tbot/Chart.yaml +++ b/examples/chart/tbot/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" name: tbot apiVersion: v2 diff --git a/examples/chart/tbot/templates/_config.tpl b/examples/chart/tbot/templates/_config.tpl index 344b25e403fc5..d8c9aff491862 100644 --- a/examples/chart/tbot/templates/_config.tpl +++ b/examples/chart/tbot/templates/_config.tpl @@ -40,10 +40,10 @@ outputs: name: {{ include "tbot.defaultOutputName" . }} {{- end }} {{- if .Values.outputs }} -{{- toYaml .Values.outputs | nindent 6}} +{{- toYaml .Values.outputs | nindent 2}} {{- end }} {{- end }} {{- if .Values.services }} -services: {{- toYaml .Values.services | nindent 6}} +services: {{- toYaml .Values.services | nindent 2}} {{- end }} {{- end -}} diff --git a/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap b/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap index a1e58c58b4643..2dbfb81532f58 100644 --- a/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap +++ b/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap @@ -35,6 +35,10 @@ should match the snapshot (full): join_method: modified-join-method token: my-token outputs: + - destination: + name: RELEASE-NAME-tbot-out + type: kubernetes_secret + type: identity - app_name: foo destination: path: /bar diff --git a/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap index d615816877e63..9d0bbe3716956 100644 --- a/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap @@ -22,14 +22,14 @@ should match the snapshot (full): template: metadata: annotations: - checksum/config: 094cdbfc4e4fe3824a33426d8eea4e9e8a4b2711823d4fbb4102e11caa7f62c0 + checksum/config: 010d3421120a26bed12d1b9df8443e0eeafa362e88bd830e4a81688d13689483 test-key: test-annotation-pod labels: app.kubernetes.io/component: tbot app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: tbot - helm.sh/chart: tbot-17.0.0-dev + helm.sh/chart: tbot-18.0.0-dev test-key: test-label-pod spec: affinity: @@ -68,7 +68,7 @@ should match the snapshot (full): value: "1" - name: TEST_ENV value: test-value - image: public.ecr.aws/gravitational/tbot-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/tbot-distroless:18.0.0-dev imagePullPolicy: Always livenessProbe: failureThreshold: 6 @@ -154,7 +154,7 @@ should match the snapshot (simple): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: tbot - helm.sh/chart: tbot-17.0.0-dev + helm.sh/chart: tbot-18.0.0-dev spec: containers: - args: @@ -176,7 +176,7 @@ should match the snapshot (simple): fieldPath: spec.nodeName - name: KUBERNETES_TOKEN_PATH value: /var/run/secrets/tokens/join-sa-token - image: public.ecr.aws/gravitational/tbot-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/tbot-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/examples/chart/teleport-cluster/Chart.yaml b/examples/chart/teleport-cluster/Chart.yaml index ea212b038a5d4..f9f5e800d3714 100644 --- a/examples/chart/teleport-cluster/Chart.yaml +++ b/examples/chart/teleport-cluster/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" name: teleport-cluster apiVersion: v2 diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml index 36e7fe2312354..0cdeac0816dcc 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" name: teleport-operator apiVersion: v2 diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap index 81a2e28b7c823..6ccb7b5467fce 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap @@ -8,9 +8,9 @@ adds operator permissions to ClusterRole: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-cluster-17.0.0-dev - teleport.dev/majorVersion: "17" + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-cluster-18.0.0-dev + teleport.dev/majorVersion: "18" name: RELEASE-NAME rules: - apiGroups: diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap index bff9e2a397a73..a5d5f9fffba3b 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap @@ -1848,9 +1848,9 @@ sets clusterDomain on Configmap: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-cluster-17.0.0-dev - teleport.dev/majorVersion: "17" + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-cluster-18.0.0-dev + teleport.dev/majorVersion: "18" name: RELEASE-NAME-auth namespace: NAMESPACE uses athena as primary backend when configured: diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap index 4fef4cd91bf80..629314d35e4de 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap @@ -8,7 +8,7 @@ - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -141,7 +141,7 @@ should set nodeSelector when set in values: - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -238,7 +238,7 @@ should set resources when set in values: - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -324,7 +324,7 @@ should set securityContext when set in values: - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap index 32830dc1a9d48..bd048d4cad03b 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap @@ -567,8 +567,8 @@ sets clusterDomain on Configmap: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-cluster-17.0.0-dev - teleport.dev/majorVersion: "17" + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-cluster-18.0.0-dev + teleport.dev/majorVersion: "18" name: RELEASE-NAME-proxy namespace: NAMESPACE diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap index 4e3f20a977377..d7cdea4ff9e27 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap @@ -11,9 +11,9 @@ sets clusterDomain on Deployment Pods: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-cluster-17.0.0-dev - teleport.dev/majorVersion: "17" + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-cluster-18.0.0-dev + teleport.dev/majorVersion: "18" name: RELEASE-NAME-proxy namespace: NAMESPACE spec: @@ -26,7 +26,7 @@ sets clusterDomain on Deployment Pods: template: metadata: annotations: - checksum/config: 9ba7ee951511bedbbb6ab297c0b044322b0a39391e801ed8fc3bf44a58f112b9 + checksum/config: 5cee21778063eafa4687da4372a09f8c752ea114a3cee79883d96f9f15c14eb1 kubernetes.io/pod: test-annotation kubernetes.io/pod-different: 4 labels: @@ -34,9 +34,9 @@ sets clusterDomain on Deployment Pods: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.0.0-dev - helm.sh/chart: teleport-cluster-17.0.0-dev - teleport.dev/majorVersion: "17" + app.kubernetes.io/version: 18.0.0-dev + helm.sh/chart: teleport-cluster-18.0.0-dev + teleport.dev/majorVersion: "18" spec: affinity: podAntiAffinity: null @@ -44,7 +44,7 @@ sets clusterDomain on Deployment Pods: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -104,8 +104,8 @@ sets clusterDomain on Deployment Pods: - teleport - wait - no-resolve - - RELEASE-NAME-auth-v16.NAMESPACE.svc.test.com - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + - RELEASE-NAME-auth-v17.NAMESPACE.svc.test.com + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev name: wait-auth-update serviceAccountName: RELEASE-NAME-proxy terminationGracePeriodSeconds: 60 @@ -136,8 +136,8 @@ should provision initContainer correctly when set in values: - teleport - wait - no-resolve - - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + - RELEASE-NAME-auth-v17.NAMESPACE.svc.cluster.local + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev name: wait-auth-update resources: limits: @@ -201,7 +201,7 @@ should set nodeSelector when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -261,8 +261,8 @@ should set nodeSelector when set in values: - teleport - wait - no-resolve - - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + - RELEASE-NAME-auth-v17.NAMESPACE.svc.cluster.local + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev name: wait-auth-update nodeSelector: environment: security @@ -313,7 +313,7 @@ should set resources for wait-auth-update initContainer when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -380,8 +380,8 @@ should set resources for wait-auth-update initContainer when set in values: - teleport - wait - no-resolve - - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + - RELEASE-NAME-auth-v17.NAMESPACE.svc.cluster.local + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev name: wait-auth-update resources: limits: @@ -421,7 +421,7 @@ should set resources when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -488,8 +488,8 @@ should set resources when set in values: - teleport - wait - no-resolve - - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + - RELEASE-NAME-auth-v17.NAMESPACE.svc.cluster.local + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev name: wait-auth-update resources: limits: @@ -529,7 +529,7 @@ should set securityContext for initContainers when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -596,8 +596,8 @@ should set securityContext for initContainers when set in values: - teleport - wait - no-resolve - - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + - RELEASE-NAME-auth-v17.NAMESPACE.svc.cluster.local + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev name: wait-auth-update securityContext: allowPrivilegeEscalation: false @@ -637,7 +637,7 @@ should set securityContext when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -704,8 +704,8 @@ should set securityContext when set in values: - teleport - wait - no-resolve - - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + - RELEASE-NAME-auth-v17.NAMESPACE.svc.cluster.local + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev name: wait-auth-update securityContext: allowPrivilegeEscalation: false diff --git a/examples/chart/teleport-kube-agent/Chart.yaml b/examples/chart/teleport-kube-agent/Chart.yaml index a8b88bcc772fb..cc359d3019b83 100644 --- a/examples/chart/teleport-kube-agent/Chart.yaml +++ b/examples/chart/teleport-kube-agent/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.0.0-dev" +.version: &version "18.0.0-dev" name: teleport-kube-agent apiVersion: v2 diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap index bee6bf8282eac..e933dd7d18e57 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap @@ -32,7 +32,7 @@ sets Deployment annotations when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -109,7 +109,7 @@ sets Deployment labels when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -173,7 +173,7 @@ sets Pod annotations when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -237,7 +237,7 @@ sets Pod labels when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -322,7 +322,7 @@ should add emptyDir for data when existingDataVolume is not set if action is Upg value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -387,7 +387,7 @@ should add insecureSkipProxyTLSVerify to args when set in values if action is Up value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -451,7 +451,7 @@ should correctly configure existingDataVolume when set if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -513,7 +513,7 @@ should expose diag port if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -589,7 +589,7 @@ should have multiple replicas when replicaCount is set (using .replicaCount, dep value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -665,7 +665,7 @@ should have multiple replicas when replicaCount is set (using highAvailability.r value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -729,7 +729,7 @@ should have one replica when replicaCount is not set if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -793,7 +793,7 @@ should mount extraVolumes and extraVolumeMounts if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -862,7 +862,7 @@ should mount jamfCredentialsSecret if it already exists and when role is jamf an value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -932,7 +932,7 @@ should mount jamfCredentialsSecret.name when role is jamf and action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1004,7 +1004,7 @@ should mount tls.existingCASecretName and set environment when set in values if value: cluster.local - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1078,7 +1078,7 @@ should mount tls.existingCASecretName and set extra environment when set in valu value: http://username:password@my.proxy.host:3128 - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1148,7 +1148,7 @@ should provision initContainer correctly when set in values if action is Upgrade value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1270,7 +1270,7 @@ should set affinity when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1334,7 +1334,7 @@ should set default serviceAccountName when not set in values if action is Upgrad value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1411,7 +1411,7 @@ should set environment when extraEnv set in values if action is Upgrade: value: cluster.local - name: HTTPS_PROXY value: http://username:password@my.proxy.host:3128 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1539,7 +1539,7 @@ should set imagePullPolicy when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: Always livenessProbe: failureThreshold: 6 @@ -1603,7 +1603,7 @@ should set nodeSelector if set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1669,7 +1669,7 @@ should set not set priorityClassName when not set in values if action is Upgrade value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1745,7 +1745,7 @@ should set preferred affinity when more than one replica is used if action is Up value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1809,7 +1809,7 @@ should set priorityClassName when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1874,7 +1874,7 @@ should set probeTimeoutSeconds when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1948,7 +1948,7 @@ should set required affinity when highAvailability.requireAntiAffinity is set if value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2012,7 +2012,7 @@ should set resources when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2083,7 +2083,7 @@ should set serviceAccountName when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2147,7 +2147,7 @@ should set tolerations when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap index 5778a68644853..1e86a682420e3 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap @@ -25,7 +25,7 @@ should create ServiceAccount for post-delete hook by default: fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -108,7 +108,7 @@ should not create ServiceAccount for post-delete hook if serviceAccount.create i fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -138,7 +138,7 @@ should not create ServiceAccount, Role or RoleBinding for post-delete hook if se fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -168,7 +168,7 @@ should set nodeSelector in post-delete hook: fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -200,7 +200,7 @@ should set resources in the Job's pod spec if resources is set in values: fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent name: post-delete-job resources: diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap index d6ce26457c58a..d11ff50cfe5b2 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap @@ -18,7 +18,7 @@ sets Pod annotations when specified: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -91,7 +91,7 @@ sets Pod labels when specified: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -188,7 +188,7 @@ sets StatefulSet labels when specified: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -293,7 +293,7 @@ should add insecureSkipProxyTLSVerify to args when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -366,7 +366,7 @@ should add volumeClaimTemplate for data volume when using StatefulSet and action value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -459,7 +459,7 @@ should add volumeClaimTemplate for data volume when using StatefulSet and is Fre value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -542,7 +542,7 @@ should add volumeMount for data volume when using StatefulSet: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -615,7 +615,7 @@ should expose diag port: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -688,7 +688,7 @@ should generate Statefulset when storage is disabled and mode is a Upgrade: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -775,7 +775,7 @@ should have multiple replicas when replicaCount is set (using .replicaCount, dep value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -860,7 +860,7 @@ should have multiple replicas when replicaCount is set (using highAvailability.r value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -933,7 +933,7 @@ should have one replica when replicaCount is not set: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1006,7 +1006,7 @@ should install Statefulset when storage is disabled and mode is a Fresh Install: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1081,7 +1081,7 @@ should mount extraVolumes and extraVolumeMounts: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1159,7 +1159,7 @@ should mount jamfCredentialsSecret if it already exists and when role is jamf: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1240,7 +1240,7 @@ should mount jamfCredentialsSecret.name when role is jamf: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1323,7 +1323,7 @@ should mount tls.existingCASecretName and set environment when set in values: value: cluster.local - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1408,7 +1408,7 @@ should mount tls.existingCASecretName and set extra environment when set in valu value: /etc/teleport-tls-ca/ca.pem - name: HTTPS_PROXY value: http://username:password@my.proxy.host:3128 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1489,7 +1489,7 @@ should not add emptyDir for data when using StatefulSet: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1562,7 +1562,7 @@ should provision initContainer correctly when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1693,7 +1693,7 @@ should set affinity when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1766,7 +1766,7 @@ should set default serviceAccountName when not set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1852,7 +1852,7 @@ should set environment when extraEnv set in values: value: cluster.local - name: HTTPS_PROXY value: http://username:password@my.proxy.host:3128 - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1998,7 +1998,7 @@ should set imagePullPolicy when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: Always livenessProbe: failureThreshold: 6 @@ -2071,7 +2071,7 @@ should set nodeSelector if set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2158,7 +2158,7 @@ should set preferred affinity when more than one replica is used: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2231,7 +2231,7 @@ should set probeTimeoutSeconds when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2314,7 +2314,7 @@ should set required affinity when highAvailability.requireAntiAffinity is set: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2387,7 +2387,7 @@ should set resources when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2467,7 +2467,7 @@ should set serviceAccountName when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2540,7 +2540,7 @@ should set storage.requests when set in values and action is an Upgrade: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2613,7 +2613,7 @@ should set storage.storageClassName when set in values and action is an Upgrade: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2686,7 +2686,7 @@ should set tolerations when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-distroless:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap index a0ad90efdf202..625ef1b7276ec 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap @@ -27,7 +27,7 @@ sets the affinity: - --base-image=public.ecr.aws/gravitational/teleport-distroless - --version-server=https://my-custom-version-server/v1 - --version-channel=custom/preview - image: public.ecr.aws/gravitational/teleport-kube-agent-updater:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-kube-agent-updater:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -73,7 +73,7 @@ sets the tolerations: - --base-image=public.ecr.aws/gravitational/teleport-distroless - --version-server=https://my-custom-version-server/v1 - --version-channel=custom/preview - image: public.ecr.aws/gravitational/teleport-kube-agent-updater:17.0.0-dev + image: public.ecr.aws/gravitational/teleport-kube-agent-updater:18.0.0-dev imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/gen/proto/go/accessgraph/v1alpha/access_graph_service.pb.go b/gen/proto/go/accessgraph/v1alpha/access_graph_service.pb.go index 9b727ca3a2a0c..f2e88f23d89c8 100644 --- a/gen/proto/go/accessgraph/v1alpha/access_graph_service.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/access_graph_service.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/access_graph_service.proto diff --git a/gen/proto/go/accessgraph/v1alpha/aws.pb.go b/gen/proto/go/accessgraph/v1alpha/aws.pb.go index cad4fa81e9895..3a4f1065253e3 100644 --- a/gen/proto/go/accessgraph/v1alpha/aws.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/aws.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/aws.proto diff --git a/gen/proto/go/accessgraph/v1alpha/azure.pb.go b/gen/proto/go/accessgraph/v1alpha/azure.pb.go index e2f78bee99f2a..b7ea6443a8964 100644 --- a/gen/proto/go/accessgraph/v1alpha/azure.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/azure.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/azure.proto diff --git a/gen/proto/go/accessgraph/v1alpha/entra.pb.go b/gen/proto/go/accessgraph/v1alpha/entra.pb.go index d40698a29a134..5617e3154135c 100644 --- a/gen/proto/go/accessgraph/v1alpha/entra.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/entra.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/entra.proto diff --git a/gen/proto/go/accessgraph/v1alpha/events.pb.go b/gen/proto/go/accessgraph/v1alpha/events.pb.go index bc7569f3fb422..fd30d09ff450b 100644 --- a/gen/proto/go/accessgraph/v1alpha/events.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/events.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/events.proto diff --git a/gen/proto/go/accessgraph/v1alpha/gitlab.pb.go b/gen/proto/go/accessgraph/v1alpha/gitlab.pb.go index 7806c7a9b599e..23b3e914240dc 100644 --- a/gen/proto/go/accessgraph/v1alpha/gitlab.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/gitlab.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/gitlab.proto diff --git a/gen/proto/go/accessgraph/v1alpha/graph.pb.go b/gen/proto/go/accessgraph/v1alpha/graph.pb.go index 14834899a5610..066df973254e9 100644 --- a/gen/proto/go/accessgraph/v1alpha/graph.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/graph.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/graph.proto diff --git a/gen/proto/go/accessgraph/v1alpha/netiq.pb.go b/gen/proto/go/accessgraph/v1alpha/netiq.pb.go index 2d86d8a0b4f94..e39260c159875 100644 --- a/gen/proto/go/accessgraph/v1alpha/netiq.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/netiq.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/netiq.proto diff --git a/gen/proto/go/accessgraph/v1alpha/resources.pb.go b/gen/proto/go/accessgraph/v1alpha/resources.pb.go index 0e9776bc509d9..fe7e9a261e694 100644 --- a/gen/proto/go/accessgraph/v1alpha/resources.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/resources.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: accessgraph/v1alpha/resources.proto diff --git a/gen/proto/go/prehog/v1/teleport.pb.go b/gen/proto/go/prehog/v1/teleport.pb.go index 9603c82dabfe7..7dfca32709ee9 100644 --- a/gen/proto/go/prehog/v1/teleport.pb.go +++ b/gen/proto/go/prehog/v1/teleport.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: prehog/v1/teleport.proto diff --git a/gen/proto/go/prehog/v1alpha/connect.pb.go b/gen/proto/go/prehog/v1alpha/connect.pb.go index 95f74db0e4a67..a86322f17adbc 100644 --- a/gen/proto/go/prehog/v1alpha/connect.pb.go +++ b/gen/proto/go/prehog/v1alpha/connect.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: prehog/v1alpha/connect.proto diff --git a/gen/proto/go/prehog/v1alpha/tbot.pb.go b/gen/proto/go/prehog/v1alpha/tbot.pb.go index 4855721797d81..654c1f2645217 100644 --- a/gen/proto/go/prehog/v1alpha/tbot.pb.go +++ b/gen/proto/go/prehog/v1alpha/tbot.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: prehog/v1alpha/tbot.proto diff --git a/gen/proto/go/prehog/v1alpha/teleport.pb.go b/gen/proto/go/prehog/v1alpha/teleport.pb.go index 66c02fa515281..e67592e2b7984 100644 --- a/gen/proto/go/prehog/v1alpha/teleport.pb.go +++ b/gen/proto/go/prehog/v1alpha/teleport.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: prehog/v1alpha/teleport.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/access_request.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/access_request.pb.go index dd1ac1691363e..df21c55cc973e 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/access_request.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/access_request.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/access_request.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/app.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/app.pb.go index 322b8a085b6a1..222ee783e19c0 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/app.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/app.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/app.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/auth_settings.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/auth_settings.pb.go index 4be23b9de07f0..cadcb84907e9f 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/auth_settings.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/auth_settings.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/auth_settings.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go index ee91a32cb84d9..51dbd7b580553 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/cluster.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/cluster.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/database.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/database.pb.go index ea2412225ab7a..8ce033e5c9c93 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/database.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/database.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/database.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/gateway.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/gateway.pb.go index 0dc296f5e76cc..f2bc2e5b6c174 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/gateway.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/gateway.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/gateway.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/kube.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/kube.pb.go index 1cb3f9100031d..056780e54a086 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/kube.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/kube.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/kube.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/label.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/label.pb.go index c04c7a2cb6bda..115728588ead1 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/label.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/label.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/label.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/server.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/server.pb.go index 2c499f8dd9316..2d26b10c2ebf4 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/server.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/server.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/server.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go index f2ff4bcaced0d..6a60ee36d074d 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/service.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go index 8cce353289d5a..d4c27c8f47fcd 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/tshd_events_service.proto diff --git a/gen/proto/go/teleport/lib/teleterm/v1/usage_events.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/usage_events.pb.go index 7cd8fa552bba2..8b6bab199aaf1 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/usage_events.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/usage_events.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/v1/usage_events.proto diff --git a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go index 9f5907015d443..285b72529c17b 100644 --- a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/teleterm/vnet/v1/vnet_service.proto diff --git a/gen/proto/go/teleport/quicpeering/v1alpha/dial.pb.go b/gen/proto/go/teleport/quicpeering/v1alpha/dial.pb.go index 6844da6249b34..1e6fed8a15dc1 100644 --- a/gen/proto/go/teleport/quicpeering/v1alpha/dial.pb.go +++ b/gen/proto/go/teleport/quicpeering/v1alpha/dial.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/quicpeering/v1alpha/dial.proto diff --git a/go.mod b/go.mod index eb16212086763..9b7f9a944bc36 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43 github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.34.1 github.com/aws/aws-sdk-go-v2/service/athena v1.49.0 + github.com/aws/aws-sdk-go-v2/service/dax v1.23.7 github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.0 github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.8 github.com/aws/aws-sdk-go-v2/service/ec2 v1.195.0 @@ -212,9 +213,9 @@ require ( golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 google.golang.org/api v0.207.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 - google.golang.org/grpc v1.68.0 + google.golang.org/grpc v1.69.2 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 - google.golang.org/protobuf v1.36.0 + google.golang.org/protobuf v1.36.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/dnaeon/go-vcr.v3 v3.2.0 gopkg.in/ini.v1 v1.67.0 @@ -240,7 +241,7 @@ require ( ) require ( - cel.dev/expr v0.16.1 // indirect + cel.dev/expr v0.16.2 // indirect cloud.google.com/go v0.116.0 // indirect cloud.google.com/go/auth v0.10.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect @@ -256,7 +257,7 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect @@ -528,9 +529,9 @@ require ( github.com/zmap/zlint/v3 v3.6.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.17 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.31.0 // indirect go.opentelemetry.io/otel/metric v1.32.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/mock v0.4.0 // indirect @@ -541,7 +542,6 @@ require ( gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20241113202542-65e8d215514f // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect - google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -573,3 +573,10 @@ replace ( github.com/redis/go-redis/v9 => github.com/gravitational/redis/v9 v9.6.1-teleport.1 github.com/vulcand/predicate => github.com/gravitational/predicate v1.3.1 ) + +// stats/opentelemetry is provided by grpc-go since v1.69.0, v1.68.2 and v1.67.3. +// Before that it was its own module. Until all of our dependencies use, at +// least, one of the grpc-go versions above we need to exclude +// stats/opentelemetry in order to avoid "ambiguous import" errors on build. +// TODO(codingllama): Remove once no dependencies import stats/opentelemetry. +exclude google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a diff --git a/go.sum b/go.sum index 82132a3ee7268..1795f10be5367 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= -cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= +cel.dev/expr v0.16.2 h1:RwRhoH17VhAu9U5CMvMhH1PDVgf0tuz9FT+24AfMLfU= +cel.dev/expr v0.16.2/go.mod h1:gXngZQMkWJoSbE8mOzehJlXQyubn/Vg0vR9/F3W7iw8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -745,8 +745,8 @@ github.com/DanielTitkov/go-adaptive-cards v0.2.2 h1:tBFExyvsbCcrBJEvPaV3FW4gcAkw github.com/DanielTitkov/go-adaptive-cards v0.2.2/go.mod h1:RtCzt65p/zEos6+zhiCFQmiaHmro6M63l9NP7xXx/Lg= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 h1:pB2F2JKCj1Znmp2rwxxt1J0Fg0wezTMgWYk5Mpbi1kg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2 h1:cZpsGsWTIFKymTA0je7IIvi1O7Es7apb9CF3EQlOcfE= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1 h1:oTX4vsorBZo/Zdum6OKPA4o7544hm6smoRv1QjpTwGo= @@ -883,6 +883,8 @@ github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.34.1 h1:8EwNbY+A/ github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.34.1/go.mod h1:2mMP2R86zLPAUz0TpJdsKW8XawHgs9Nk97fYJomO3o8= github.com/aws/aws-sdk-go-v2/service/athena v1.49.0 h1:D+iatX9gV6gCuNd6BnUkfwfZJw/cXlEk+LwwDdSMdtw= github.com/aws/aws-sdk-go-v2/service/athena v1.49.0/go.mod h1:27ljwDsnZvfrZKsLzWD4WFjI4OZutEFIjvVtYfj9gHc= +github.com/aws/aws-sdk-go-v2/service/dax v1.23.7 h1:hZg1sHhWXGZShzHGpwcaOT8HZfx26kkbRDNZgZda4xI= +github.com/aws/aws-sdk-go-v2/service/dax v1.23.7/go.mod h1:fYBjETTq8hZfirBEgXM1xIMy+tvCGYZTeWpjeKKp0bU= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.0 h1:isKhHsjpQR3CypQJ4G1g8QWx7zNpiC/xKw1zjgJYVno= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.0/go.mod h1:xDvUyIkwBwNtVZJdHEwAuhFly3mezwdEWkbJ5oNYwIw= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.8 h1:ntqHwZb+ZyVz0CFYUG0sQ02KMMJh+iXeV3bXoba+s4A= @@ -2289,8 +2291,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= +go.opentelemetry.io/contrib/detectors/gcp v1.31.0 h1:G1JQOreVrfhRkner+l4mrGxmfqYCAuy76asTDAo0xsA= +go.opentelemetry.io/contrib/detectors/gcp v1.31.0/go.mod h1:tzQL6E1l+iV44YFTkcAeNQqzXUiekSYP9jjJjXwEd00= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= @@ -2309,8 +2311,8 @@ go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZk go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= -go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -3071,13 +3073,11 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -3096,8 +3096,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= diff --git a/integration/appaccess/pack.go b/integration/appaccess/pack.go index 609a60adca036..cf7afae964fe6 100644 --- a/integration/appaccess/pack.go +++ b/integration/appaccess/pack.go @@ -49,7 +49,6 @@ import ( "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/events" - "github.com/gravitational/teleport/lib/httplib/csrf" "github.com/gravitational/teleport/lib/httplib/reverseproxy" "github.com/gravitational/teleport/lib/reversetunnelclient" "github.com/gravitational/teleport/lib/service" @@ -251,15 +250,9 @@ func (p *Pack) initWebSession(t *testing.T) { req, err := http.NewRequest(http.MethodPost, u.String(), bytes.NewBuffer(csReq)) require.NoError(t, err) - // Attach CSRF token in cookie and header. - csrfToken, err := utils.CryptoRandomHex(32) - require.NoError(t, err) - req.AddCookie(&http.Cookie{ - Name: csrf.CookieName, - Value: csrfToken, - }) + // Set Content-Type header, otherwise Teleport's CSRF protection will + // reject the request. req.Header.Set("Content-Type", "application/json; charset=utf-8") - req.Header.Set(csrf.HeaderName, csrfToken) // Issue request. client := &http.Client{ diff --git a/integration/helpers/instance.go b/integration/helpers/instance.go index b132b027b3bef..f6b25459ea275 100644 --- a/integration/helpers/instance.go +++ b/integration/helpers/instance.go @@ -60,7 +60,6 @@ import ( "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" - "github.com/gravitational/teleport/lib/httplib/csrf" "github.com/gravitational/teleport/lib/observability/tracing" "github.com/gravitational/teleport/lib/reversetunnel" "github.com/gravitational/teleport/lib/reversetunnelclient" @@ -1531,18 +1530,7 @@ func CreateWebSession(proxyHost, user, password string) (*web.CreateSessionRespo return nil, nil, trace.Wrap(err) } - // Attach CSRF token in cookie and header. - csrfToken, err := utils.CryptoRandomHex(32) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - req.AddCookie(&http.Cookie{ - Name: csrf.CookieName, - Value: csrfToken, - }) req.Header.Set("Content-Type", "application/json; charset=utf-8") - req.Header.Set(csrf.HeaderName, csrfToken) // Issue request. httpClient := &http.Client{ diff --git a/integration/helpers/trustedclusters.go b/integration/helpers/trustedclusters.go index 1b3f43b61507c..cfc68f571ce5d 100644 --- a/integration/helpers/trustedclusters.go +++ b/integration/helpers/trustedclusters.go @@ -61,7 +61,66 @@ func TryCreateTrustedCluster(t *testing.T, authServer *auth.Server, trustedClust ctx := context.TODO() for i := 0; i < 10; i++ { log.Debugf("Will create trusted cluster %v, attempt %v.", trustedCluster, i) - _, err := authServer.UpsertTrustedCluster(ctx, trustedCluster) + _, err := authServer.CreateTrustedCluster(ctx, trustedCluster) + if err == nil { + return + } + if trace.IsConnectionProblem(err) { + log.Debugf("Retrying on connection problem: %v.", err) + time.Sleep(500 * time.Millisecond) + continue + } + if trace.IsAccessDenied(err) { + log.Debugf("Retrying on access denied: %v.", err) + time.Sleep(500 * time.Millisecond) + continue + } + require.FailNow(t, "Terminating on unexpected problem", "%v.", err) + } + require.FailNow(t, "Timeout creating trusted cluster") +} + +// TryUpdateTrustedCluster performs several attempts to update a trusted cluster, +// retries on connection problems and access denied errors to let caches +// propagate and services to start +func TryUpdateTrustedCluster(t *testing.T, authServer *auth.Server, trustedCluster types.TrustedCluster) { + t.Helper() + ctx := context.TODO() + for i := 0; i < 10; i++ { + log.Debugf("Will create trusted cluster %v, attempt %v.", trustedCluster, i) + _, err := authServer.UpdateTrustedCluster(ctx, trustedCluster) + if err == nil { + return + } + if trace.IsConnectionProblem(err) { + log.Debugf("Retrying on connection problem: %v.", err) + time.Sleep(500 * time.Millisecond) + continue + } + if trace.IsAccessDenied(err) { + log.Debugf("Retrying on access denied: %v.", err) + time.Sleep(500 * time.Millisecond) + continue + } + require.FailNow(t, "Terminating on unexpected problem", "%v.", err) + } + require.FailNow(t, "Timeout creating trusted cluster") +} + +// TryUpdateTrustedCluster performs several attempts to upsert a trusted cluster, +// retries on connection problems and access denied errors to let caches +// propagate and services to start +func TryUpsertTrustedCluster(t *testing.T, authServer *auth.Server, trustedCluster types.TrustedCluster, skipNameValidation bool) { + t.Helper() + ctx := context.TODO() + for i := 0; i < 10; i++ { + log.Debugf("Will create trusted cluster %v, attempt %v.", trustedCluster, i) + var err error + if skipNameValidation { + _, err = authServer.UpsertTrustedCluster(ctx, trustedCluster) + } else { + _, err = authServer.UpsertTrustedClusterV2(ctx, trustedCluster) + } if err == nil { return } diff --git a/integration/helpers/web.go b/integration/helpers/web.go index 5cfc4b87f64b6..815891ea45bcd 100644 --- a/integration/helpers/web.go +++ b/integration/helpers/web.go @@ -34,8 +34,6 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/lib/client" - "github.com/gravitational/teleport/lib/httplib/csrf" - "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/web" websession "github.com/gravitational/teleport/lib/web/session" "github.com/gravitational/teleport/lib/web/ui" @@ -68,15 +66,7 @@ func LoginWebClient(t *testing.T, host, username, password string) *WebClientPac req, err := http.NewRequest(http.MethodPost, u.String(), bytes.NewBuffer(csReq)) require.NoError(t, err) - // Attach CSRF token in cookie and header. - csrfToken, err := utils.CryptoRandomHex(32) - require.NoError(t, err) - req.AddCookie(&http.Cookie{ - Name: csrf.CookieName, - Value: csrfToken, - }) req.Header.Set("Content-Type", "application/json; charset=utf-8") - req.Header.Set(csrf.HeaderName, csrfToken) // Issue request. client := &http.Client{ diff --git a/integration/integration_test.go b/integration/integration_test.go index 9b5ff50af1f85..9dcb2332b44e6 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -196,6 +196,8 @@ func TestIntegrations(t *testing.T) { t.Run("TrustedDisabledClusters", suite.bind(testDisabledTrustedClusters)) t.Run("TrustedClustersRoleMapChanges", suite.bind(testTrustedClustersRoleMapChanges)) t.Run("TrustedClustersWithLabels", suite.bind(testTrustedClustersWithLabels)) + t.Run("TrustedClustersSkipNameValidation", suite.bind(testTrustedClustersSkipNameValidation)) + t.Run("CreateAndUpdateTrustedClusters", suite.bind(testCreateAndUpdateTrustedClusters)) t.Run("TrustedTunnelNode", suite.bind(testTrustedTunnelNode)) t.Run("TwoClustersProxy", suite.bind(testTwoClustersProxy)) t.Run("TwoClustersTunnel", suite.bind(testTwoClustersTunnel)) @@ -2744,17 +2746,14 @@ func testMapRoles(t *testing.T, suite *integrationTestSuite) { {Remote: mainDevs, Local: []string{auxDevs}}, }) - // modify trusted cluster resource name so it would not - // match the cluster name to check that it does not matter - trustedCluster.SetName(main.Secrets.SiteName + "-cluster") - require.NoError(t, main.Start()) require.NoError(t, aux.Start()) require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) // try and upsert a trusted cluster - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + const skipNameValidation = false + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, skipNameValidation) helpers.WaitForTunnelConnections(t, main.Process.GetAuthServer(), clusterAux, 1) sshPort, _, _ := aux.StartNodeAndProxy(t, "aux-node") @@ -2904,6 +2903,9 @@ type trustedClusterTest struct { // useLabels turns on trusted cluster labels and // verifies RBAC useLabels bool + // skipNameValidation uses the deprecated UpsertTrustedCluster and skips + // cluster name validation. + skipNameValidation bool } // TestTrustedClusters tests remote clusters scenarios @@ -2940,6 +2942,15 @@ func testTrustedClustersWithLabels(t *testing.T, suite *integrationTestSuite) { trustedClusters(t, suite, trustedClusterTest{multiplex: false, useLabels: true}) } +// TestTrustedClustersSkipNameValidation tests remote clusters scenarios +// skipping name validation. +func testTrustedClustersSkipNameValidation(t *testing.T, suite *integrationTestSuite) { + tr := utils.NewTracer(utils.ThisFunction()).Start() + defer tr.Stop() + + trustedClusters(t, suite, trustedClusterTest{skipNameValidation: true}) +} + // TestJumpTrustedClusters tests remote clusters scenarios // using trusted clusters feature using jumphost connection func testJumpTrustedClusters(t *testing.T, suite *integrationTestSuite) { @@ -2967,6 +2978,15 @@ func testMultiplexingTrustedClusters(t *testing.T, suite *integrationTestSuite) trustedClusters(t, suite, trustedClusterTest{multiplex: true}) } +// TestCreateAndUpdateTrustedClusters tests the basic create and update +// operations for a trusted cluster. +func testCreateAndUpdateTrustedClusters(t *testing.T, suite *integrationTestSuite) { + tr := utils.NewTracer(utils.ThisFunction()).Start() + defer tr.Stop() + + createAndUpdateTrustedClusters(t, suite, trustedClusterTest{}) +} + func standardPortsOrMuxSetup(t *testing.T, mux bool, fds *[]*servicecfg.FileDescriptor) *helpers.InstanceListeners { if mux { return helpers.WebReverseTunnelMuxPortSetup(t, fds) @@ -2974,6 +2994,116 @@ func standardPortsOrMuxSetup(t *testing.T, mux bool, fds *[]*servicecfg.FileDesc return helpers.StandardListenerSetup(t, fds) } +func createAndUpdateTrustedClusters(t *testing.T, suite *integrationTestSuite, test trustedClusterTest) { + ctx := context.Background() + username := suite.Me.Username + + clusterMain := "cluster-main" + clusterAux := "cluster-aux" + mainCfg := helpers.InstanceConfig{ + ClusterName: clusterMain, + HostID: helpers.HostID, + NodeName: Host, + Priv: suite.Priv, + Pub: suite.Pub, + Log: suite.Log, + } + mainCfg.Listeners = standardPortsOrMuxSetup(t, test.multiplex, &mainCfg.Fds) + main := helpers.NewInstance(t, mainCfg) + aux := suite.newNamedTeleportInstance(t, clusterAux) + + // main cluster has a local user and belongs to role "main-devs" and "main-admins" + mainDevs := "main-devs" + mainRole, err := types.NewRole(mainDevs, types.RoleSpecV6{ + Allow: types.RoleConditions{ + Logins: []string{username}, + NodeLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + }, + }) + require.NoError(t, err) + + mainAdmins := "main-admins" + adminsRole, err := types.NewRole(mainAdmins, types.RoleSpecV6{ + Allow: types.RoleConditions{ + Logins: []string{"superuser"}, + NodeLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + }, + }) + require.NoError(t, err) + + main.AddUserWithRole(username, mainRole, adminsRole) + + // for role mapping test we turn on Web API on the main cluster + // as it's used + makeConfig := func(instance *helpers.TeleInstance, enableSSH bool) (*testing.T, *servicecfg.Config) { + tconf := suite.defaultServiceConfig() + tconf.Proxy.DisableWebService = false + tconf.Proxy.DisableWebInterface = true + tconf.SSH.Enabled = enableSSH + tconf, err := instance.GenerateConfig(t, nil, tconf) + require.NoError(t, err) + + tconf.CachePolicy.Enabled = false + return t, tconf + } + lib.SetInsecureDevMode(true) + defer lib.SetInsecureDevMode(false) + + require.NoError(t, main.CreateWithConf(makeConfig(main, false))) + require.NoError(t, aux.CreateWithConf(makeConfig(aux, true))) + + // auxiliary cluster has only a role aux-devs + // connect aux cluster to main cluster + // using trusted clusters, so remote user will be allowed to assume + // role specified by mapping remote role "devs" to local role "local-devs" + auxDevs := "aux-devs" + auxRole, err := types.NewRole(auxDevs, types.RoleSpecV6{ + Allow: types.RoleConditions{ + Logins: []string{username}, + NodeLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + }, + }) + require.NoError(t, err) + _, err = aux.Process.GetAuthServer().UpsertRole(ctx, auxRole) + require.NoError(t, err) + + trustedClusterToken := "trusted-cluster-token" + tokenResource, err := types.NewProvisionToken(trustedClusterToken, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}) + require.NoError(t, err) + err = main.Process.GetAuthServer().UpsertToken(ctx, tokenResource) + require.NoError(t, err) + + trustedCluster := main.AsTrustedCluster(trustedClusterToken, types.RoleMap{ + {Remote: mainDevs, Local: []string{auxDevs}}, + }) + + require.NoError(t, main.Start()) + require.NoError(t, aux.Start()) + + require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) + + // Note that the trusted cluster resource name must match the cluster name. + // Modify the trusted cluster resource name and expect the create to fail. + trustedCluster.SetName(main.Secrets.SiteName + "-cluster") + _, err = aux.Process.GetAuthServer().CreateTrustedCluster(ctx, trustedCluster) + require.ErrorContains(t, err, "trusted cluster resource name must be the same as the remote cluster name", "expected failure due to tc name mismatch") + + // Modify the trusted cluster resource name back to what it was originally. + // Try and create a trusted cluster + trustedCluster.SetName(main.Secrets.SiteName) + helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + + // Update the trusted cluster resource with new role mappings. + trustedCluster.SetRoleMap(types.RoleMap{ + {Remote: mainAdmins, Local: []string{auxDevs}}, + }) + helpers.TryUpdateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + + // stop clusters and remaining nodes + require.NoError(t, main.StopAll()) + require.NoError(t, aux.StopAll()) +} + func trustedClusters(t *testing.T, suite *integrationTestSuite, test trustedClusterTest) { ctx := context.Background() username := suite.Me.Username @@ -3077,17 +3207,25 @@ func trustedClusters(t *testing.T, suite *integrationTestSuite, test trustedClus {Remote: mainOps, Local: []string{auxDevs}}, }) - // modify trusted cluster resource name, so it would not - // match the cluster name to check that it does not matter - trustedCluster.SetName(main.Secrets.SiteName + "-cluster") - require.NoError(t, main.Start()) require.NoError(t, aux.Start()) require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) + // Note that the trusted cluster resource name must match the cluster name. + // Modify the trusted cluster resource name and expect the upsert to fail. + trustedCluster.SetName(main.Secrets.SiteName + "-cluster") + + _, err = aux.Process.GetAuthServer().UpsertTrustedClusterV2(ctx, trustedCluster) + require.ErrorContains(t, err, "trusted cluster resource name must be the same as the remote cluster name", "expected failure due to tc name mismatch") + + if !test.skipNameValidation { + // Modify the trusted cluster resource name back to what it was originally. + trustedCluster.SetName(main.Secrets.SiteName) + } + // try and upsert a trusted cluster - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, test.skipNameValidation) helpers.WaitForTunnelConnections(t, main.Process.GetAuthServer(), clusterAux, 1) sshPort, _, _ := aux.StartNodeAndProxy(t, "aux-node") @@ -3174,7 +3312,7 @@ func trustedClusters(t *testing.T, suite *integrationTestSuite, test trustedClus require.Error(t, err, "expected tunnel to close and SSH client to start failing") // recreating the trusted cluster should re-establish connection - _, err = aux.Process.GetAuthServer().UpsertTrustedCluster(ctx, trustedCluster) + _, err = aux.Process.GetAuthServer().UpsertTrustedClusterV2(ctx, trustedCluster) require.NoError(t, err) // check that remote cluster has been re-provisioned @@ -3320,9 +3458,6 @@ func trustedDisabledCluster(t *testing.T, suite *integrationTestSuite, test trus {Remote: mainOps, Local: []string{auxDevs}}, }) - // modify trusted cluster resource name, so it would not - // match the cluster name to check that it does not matter - trustedCluster.SetName(main.Secrets.SiteName + "-cluster") // disable cluster trustedCluster.SetEnabled(false) @@ -3332,11 +3467,11 @@ func trustedDisabledCluster(t *testing.T, suite *integrationTestSuite, test trus require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) // try and upsert a trusted cluster while disabled - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, test.skipNameValidation) // try to enable disabled cluster trustedCluster.SetEnabled(true) - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, test.skipNameValidation) helpers.WaitForTunnelConnections(t, main.Process.GetAuthServer(), clusterAux, 1) helpers.CheckTrustedClustersCanConnect(ctx, t, helpers.TrustedClusterSetup{ @@ -3460,16 +3595,12 @@ func trustedClustersRoleMapChanges(t *testing.T, suite *integrationTestSuite, te {Remote: mainOps, Local: []string{auxDevs}}, }) - // modify trusted cluster resource name, so it would not - // match the cluster name to check that it does not matter - trustedCluster.SetName(main.Secrets.SiteName + "-cluster") - require.NoError(t, main.Start()) require.NoError(t, aux.Start()) require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, test.skipNameValidation) helpers.WaitForTunnelConnections(t, main.Process.GetAuthServer(), clusterAux, 1) // change role mapping to ensure updating works @@ -3477,7 +3608,7 @@ func trustedClustersRoleMapChanges(t *testing.T, suite *integrationTestSuite, te {Remote: mainDevs, Local: []string{auxDevs}}, }) - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, test.skipNameValidation) helpers.WaitForTunnelConnections(t, main.Process.GetAuthServer(), clusterAux, 1) helpers.CheckTrustedClustersCanConnect(ctx, t, helpers.TrustedClusterSetup{ @@ -3490,7 +3621,7 @@ func trustedClustersRoleMapChanges(t *testing.T, suite *integrationTestSuite, te // disable the enabled trusted cluster and ensure it no longer works trustedCluster.SetEnabled(false) - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, test.skipNameValidation) // Wait for both cluster to no longer see each other via reverse tunnels. require.Eventually(t, helpers.WaitForClusters(main.Tunnel, 0), 10*time.Second, 1*time.Second, @@ -3561,17 +3692,14 @@ func testTrustedTunnelNode(t *testing.T, suite *integrationTestSuite) { {Remote: mainDevs, Local: []string{auxDevs}}, }) - // modify trusted cluster resource name, so it would not - // match the cluster name to check that it does not matter - trustedCluster.SetName(main.Secrets.SiteName + "-cluster") - require.NoError(t, main.Start()) require.NoError(t, aux.Start()) require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) // try and upsert a trusted cluster - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + const skipNameValidation = false + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, skipNameValidation) helpers.WaitForTunnelConnections(t, main.Process.GetAuthServer(), clusterAux, 1) // Create a Teleport instance with a node that dials back to the aux cluster. @@ -3745,17 +3873,14 @@ func testTrustedClusterAgentless(t *testing.T, suite *integrationTestSuite) { {Remote: devsRoleName, Local: []string{devsRoleName}}, }) - // modify trusted cluster resource name, so it would not - // match the cluster name to check that it does not matter - trustedCluster.SetName(main.Secrets.SiteName + "-cluster") - require.NoError(t, main.Start()) require.NoError(t, leaf.Start()) require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) // try and upsert a trusted cluster - helpers.TryCreateTrustedCluster(t, leaf.Process.GetAuthServer(), trustedCluster) + const skipNameValidation = false + helpers.TryUpsertTrustedCluster(t, leaf.Process.GetAuthServer(), trustedCluster, skipNameValidation) helpers.WaitForTunnelConnections(t, main.Process.GetAuthServer(), clusterAux, 1) // Wait for both cluster to see each other via reverse tunnels. @@ -5583,7 +5708,8 @@ func testRotateTrustedClusters(t *testing.T, suite *integrationTestSuite) { lib.SetInsecureDevMode(true) defer lib.SetInsecureDevMode(false) - helpers.TryCreateTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster) + const skipNameValidation = false + helpers.TryUpsertTrustedCluster(t, aux.Process.GetAuthServer(), trustedCluster, skipNameValidation) helpers.WaitForTunnelConnections(t, svc.GetAuthServer(), aux.Secrets.SiteName, 1) // capture credentials before reload has started to simulate old client @@ -7482,7 +7608,9 @@ func createTrustedClusterPair(t *testing.T, suite *integrationTestSuite, extraSe t.Cleanup(func() { leaf.StopAll() }) require.NoError(t, services.CheckAndSetDefaults(trustedCluster)) - helpers.TryCreateTrustedCluster(t, leaf.Process.GetAuthServer(), trustedCluster) + + const skipNameValidation = false + helpers.TryUpsertTrustedCluster(t, leaf.Process.GetAuthServer(), trustedCluster, skipNameValidation) helpers.WaitForTunnelConnections(t, root.Process.GetAuthServer(), leafName, 1) _, _, rootProxySSHPort := root.StartNodeAndProxy(t, "root-zero") diff --git a/integration/kube_integration_test.go b/integration/kube_integration_test.go index c9fd5ff21efdb..2127486ff7a7f 100644 --- a/integration/kube_integration_test.go +++ b/integration/kube_integration_test.go @@ -695,7 +695,7 @@ func testKubeTrustedClustersClientCert(t *testing.T, suite *KubeSuite) { var upsertSuccess bool for i := 0; i < 10; i++ { log.Debugf("Will create trusted cluster %v, attempt %v", trustedCluster, i) - _, err = aux.Process.GetAuthServer().UpsertTrustedCluster(ctx, trustedCluster) + _, err = aux.Process.GetAuthServer().UpsertTrustedClusterV2(ctx, trustedCluster) if err != nil { if trace.IsConnectionProblem(err) { log.Debugf("retrying on connection problem: %v", err) @@ -971,7 +971,7 @@ func testKubeTrustedClustersSNI(t *testing.T, suite *KubeSuite) { var upsertSuccess bool for i := 0; i < 10; i++ { log.Debugf("Will create trusted cluster %v, attempt %v", trustedCluster, i) - _, err = aux.Process.GetAuthServer().UpsertTrustedCluster(ctx, trustedCluster) + _, err = aux.Process.GetAuthServer().UpsertTrustedClusterV2(ctx, trustedCluster) if err != nil { if trace.IsConnectionProblem(err) { log.Debugf("retrying on connection problem: %v", err) diff --git a/integration/proxy/proxy_helpers.go b/integration/proxy/proxy_helpers.go index e0e3c7b587224..9de514caf73e1 100644 --- a/integration/proxy/proxy_helpers.go +++ b/integration/proxy/proxy_helpers.go @@ -184,7 +184,8 @@ func newSuite(t *testing.T, opts ...proxySuiteOptionsFunc) *Suite { } if options.trustedCluster != nil { - helpers.TryCreateTrustedCluster(t, suite.leaf.Process.GetAuthServer(), options.trustedCluster) + const skipNameValidation = false + helpers.TryUpsertTrustedCluster(t, suite.leaf.Process.GetAuthServer(), options.trustedCluster, skipNameValidation) helpers.WaitForTunnelConnections(t, suite.root.Process.GetAuthServer(), suite.leaf.Secrets.SiteName, 1) } diff --git a/integrations/access/slack/bot.go b/integrations/access/slack/bot.go index dff6c17bbad27..9c58093cb9897 100644 --- a/integrations/access/slack/bot.go +++ b/integrations/access/slack/bot.go @@ -291,6 +291,7 @@ func (b Bot) slackAccessListReminderMsgSection(accessList *accesslist.AccessList if b.webProxyURL != nil { reqURL := *b.webProxyURL reqURL.Path = lib.BuildURLPath("web", "accesslists", accessList.Metadata.Name) + reqURL.Fragment = "review" link = fmt.Sprintf("*Link*: %s", reqURL.String()) } diff --git a/integrations/event-handler/go.mod b/integrations/event-handler/go.mod index 56ac4bd95bd73..85aa9286df663 100644 --- a/integrations/event-handler/go.mod +++ b/integrations/event-handler/go.mod @@ -17,7 +17,7 @@ require ( github.com/stretchr/testify v1.10.0 golang.org/x/net v0.33.0 golang.org/x/time v0.8.0 - google.golang.org/protobuf v1.36.0 + google.golang.org/protobuf v1.36.1 ) require ( @@ -87,6 +87,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.37.7 // indirect github.com/aws/aws-sdk-go-v2/service/organizations v1.36.0 // indirect github.com/aws/aws-sdk-go-v2/service/rds v1.92.0 // indirect + github.com/aws/aws-sdk-go-v2/service/redshift v1.53.0 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 // indirect github.com/aws/aws-sdk-go-v2/service/ssm v1.56.1 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect @@ -295,7 +296,7 @@ require ( google.golang.org/genproto v0.0.0-20241113202542-65e8d215514f // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect - google.golang.org/grpc v1.68.0 // indirect + google.golang.org/grpc v1.69.2 // indirect google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect @@ -349,3 +350,6 @@ replace ( // otherwise we get significant increase in size of the "teleport" binary. sigs.k8s.io/kustomize/api => github.com/gravitational/kustomize/api v0.16.0-teleport.1 ) + +// TODO(codingllama): Remove once no dependencies import stats/opentelemetry. +exclude google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a diff --git a/integrations/event-handler/go.sum b/integrations/event-handler/go.sum index 2b0f134829fcd..55b2c7576dae4 100644 --- a/integrations/event-handler/go.sum +++ b/integrations/event-handler/go.sum @@ -777,6 +777,8 @@ github.com/aws/aws-sdk-go-v2/service/organizations v1.36.0 h1:CVHfN8ZVvWzDkAf/Qj github.com/aws/aws-sdk-go-v2/service/organizations v1.36.0/go.mod h1:SVY+doFrL3KTvVMWzFLKvD7KYQ6GQfwNRPSQS7eA3cA= github.com/aws/aws-sdk-go-v2/service/rds v1.92.0 h1:W0gUYAjO24u/M6tpR041wMHJWGzleOhxtCnNLImdrZs= github.com/aws/aws-sdk-go-v2/service/rds v1.92.0/go.mod h1:ADD2uROOoEIXjbjDPEvDDZWnGmfKFYMddgKwG5RlBGw= +github.com/aws/aws-sdk-go-v2/service/redshift v1.53.0 h1:4/hmROBioc89sKlMVjHgOaH92zAkrAAMZR3BIvYwyD0= +github.com/aws/aws-sdk-go-v2/service/redshift v1.53.0/go.mod h1:UydVhUJOB/DaCJWiaBkPlvuzvWVcUlgbS2Bxn33bcKI= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= github.com/aws/aws-sdk-go-v2/service/ssm v1.56.1 h1:cfVjoEwOMOJOI6VoRQua0nI0KjZV9EAnR8bKaMeSppE= @@ -1631,6 +1633,8 @@ go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZk go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -2314,8 +2318,8 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= @@ -2337,8 +2341,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integrations/operator/crdgen/format.go b/integrations/operator/crdgen/format.go index 3cdd0afb6add3..b2193284c4271 100644 --- a/integrations/operator/crdgen/format.go +++ b/integrations/operator/crdgen/format.go @@ -31,15 +31,17 @@ import ( ) // crdFormatFunc formats the given CRD into a document. It returns the document -// as a byte slice, plus the file extension for the document. -type crdFormatFunc func(apiextv1.CustomResourceDefinition) ([]byte, string, error) +// as a byte slice, plus the file name for the document. The file name is based +// on the CRD's API group name and plural name. +type crdFormatFunc func(crd apiextv1.CustomResourceDefinition, groupName, pluralName string) ([]byte, string, error) -func formatAsYAML(crd apiextv1.CustomResourceDefinition) ([]byte, string, error) { +func formatAsCRD(crd apiextv1.CustomResourceDefinition, groupName, pluralName string) ([]byte, string, error) { doc, err := yaml.Marshal(crd) if err != nil { return nil, "", err } - return doc, "yaml", nil + filename := fmt.Sprintf("%s_%s.%v", groupName, pluralName, "yaml") + return doc, filename, nil } var crdDocTmpl string = `--- @@ -226,7 +228,7 @@ func propertyTable(currentFieldName string, props *apiextv1.JSONSchemaProps) ([] return tables, nil } -func formatAsDocsPage(crd apiextv1.CustomResourceDefinition) ([]byte, string, error) { +func formatAsDocsPage(crd apiextv1.CustomResourceDefinition, groupName, pluralName string) ([]byte, string, error) { var buf bytes.Buffer rp := ResourcePage{ Title: crd.Spec.Names.Kind, @@ -263,5 +265,11 @@ resource, which you can apply after installing the Teleport Kubernetes operator. return nil, "", trace.Wrap(err) } - return buf.Bytes(), "mdx", nil + filename := fmt.Sprintf( + "%s-%s.%v", + strings.ReplaceAll(groupName, ".", "-"), + pluralName, + "mdx", + ) + return buf.Bytes(), filename, nil } diff --git a/integrations/operator/crdgen/handlerequest.go b/integrations/operator/crdgen/handlerequest.go index 669211d76e3bd..66d90324cc0f4 100644 --- a/integrations/operator/crdgen/handlerequest.go +++ b/integrations/operator/crdgen/handlerequest.go @@ -19,7 +19,6 @@ package crdgen import ( - "fmt" "os" gogodesc "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" @@ -34,7 +33,7 @@ import ( ) func HandleCRDRequest(req *gogoplugin.CodeGeneratorRequest) error { - return handleRequest(req, formatAsYAML) + return handleRequest(req, formatAsCRD) } func HandleDocsRequest(req *gogoplugin.CodeGeneratorRequest) error { @@ -232,13 +231,12 @@ func generateSchema(file *File, groupName string, format crdFormatFunc, resp *go if err != nil { return trace.Wrap(err, "generating CRD") } - data, ext, err := format(crd) + data, filename, err := format(crd, groupName, root.pluralName) if err != nil { return trace.Wrap(err) } - name := fmt.Sprintf("%s_%s.%v", groupName, root.pluralName, ext) content := string(data) - resp.File = append(resp.File, &gogoplugin.CodeGeneratorResponse_File{Name: &name, Content: &content}) + resp.File = append(resp.File, &gogoplugin.CodeGeneratorResponse_File{Name: &filename, Content: &content}) } return nil diff --git a/integrations/terraform/Makefile b/integrations/terraform/Makefile index 0d1c0ed65e6d6..572a07d4d45dc 100644 --- a/integrations/terraform/Makefile +++ b/integrations/terraform/Makefile @@ -115,10 +115,18 @@ endif --terraform_out=config=protoc-gen-terraform-statichostuser.yaml:./tfschema \ teleport/userprovisioning/v2/statichostuser.proto + @protoc \ + -I=../../api/proto \ + -I=$(PROTOBUF_MOD_PATH) \ + --plugin=$(PROTOC_GEN_TERRAFORM) \ + --terraform_out=config=protoc-gen-terraform-workloadidentity.yaml:./tfschema \ + teleport/workloadidentity/v1/resource.proto + mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1/loginrule_terraform.go ./tfschema/loginrule/v1/ mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1/accesslist_terraform.go ./tfschema/accesslist/v1/ mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_terraform.go ./tfschema/accessmonitoringrules/v1/ mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser_terraform.go ./tfschema/userprovisioning/v2/ + mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1/resource_terraform.go ./tfschema/workloadidentity/v1/ mv ./tfschema/github.com/gravitational/teleport/api/types/device_terraform.go ./tfschema/devicetrust/v1/ rm -r ./tfschema/github.com/ @go run ./gen/main.go diff --git a/integrations/terraform/examples/resources/teleport_workload_identity/resource.tf b/integrations/terraform/examples/resources/teleport_workload_identity/resource.tf new file mode 100644 index 0000000000000..e48ab1e5d0dd2 --- /dev/null +++ b/integrations/terraform/examples/resources/teleport_workload_identity/resource.tf @@ -0,0 +1,22 @@ +resource "teleport_workload_identity" "example" { + version = "v1" + metadata = { + name = "example" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "noah" + }] + } + ] + } + spiffe = { + id = "/my/spiffe/id/path" + hint = "my-hint" + } + } +} \ No newline at end of file diff --git a/integrations/terraform/gen/main.go b/integrations/terraform/gen/main.go index 3053e7b56098a..ca639ac758ac2 100644 --- a/integrations/terraform/gen/main.go +++ b/integrations/terraform/gen/main.go @@ -519,6 +519,31 @@ var ( ExtraImports: []string{"apitypes \"github.com/gravitational/teleport/api/types\""}, ForceSetKind: "apitypes.KindStaticHostUser", } + + workloadIdentity = payload{ + Name: "WorkloadIdentity", + TypeName: "WorkloadIdentity", + VarName: "workloadIdentity", + GetMethod: "GetWorkloadIdentity", + CreateMethod: "CreateWorkloadIdentity", + UpsertMethodArity: 2, + UpdateMethod: "UpsertWorkloadIdentity", + DeleteMethod: "DeleteWorkloadIdentity", + ID: "workloadIdentity.Metadata.Name", + Kind: "workload_identity", + HasStaticID: false, + ProtoPackage: "workloadidentityv1", + ProtoPackagePath: "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1", + SchemaPackage: "schemav1", + SchemaPackagePath: "github.com/gravitational/teleport/integrations/terraform/tfschema/workloadidentity/v1", + TerraformResourceType: "teleport_workload_identity", + // Since [RFD 153](https://github.com/gravitational/teleport/blob/master/rfd/0153-resource-guidelines.md) + // resources are plain structs + IsPlainStruct: true, + // As 153-style resources don't have CheckAndSetDefaults, we must set the Kind manually. + // We import the package containing kinds, then use ForceSetKind. + ForceSetKind: `"workload_identity"`, + } ) func main() { @@ -570,6 +595,8 @@ func genTFSchema() { generateDataSource(accessMonitoringRule, pluralDataSource) generateResource(staticHostUser, pluralResource) generateDataSource(staticHostUser, pluralDataSource) + generateResource(workloadIdentity, pluralResource) + generateDataSource(workloadIdentity, pluralDataSource) } func generateResource(p payload, tpl string) { diff --git a/integrations/terraform/go.mod b/integrations/terraform/go.mod index e6e5624117945..ecc2f0d21e193 100644 --- a/integrations/terraform/go.mod +++ b/integrations/terraform/go.mod @@ -23,8 +23,8 @@ require ( github.com/jonboulle/clockwork v0.4.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 - google.golang.org/grpc v1.68.0 - google.golang.org/protobuf v1.36.0 + google.golang.org/grpc v1.69.2 + google.golang.org/protobuf v1.36.1 ) require ( @@ -100,6 +100,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.37.7 // indirect github.com/aws/aws-sdk-go-v2/service/organizations v1.36.0 // indirect github.com/aws/aws-sdk-go-v2/service/rds v1.92.0 // indirect + github.com/aws/aws-sdk-go-v2/service/redshift v1.53.0 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 // indirect github.com/aws/aws-sdk-go-v2/service/ssm v1.56.1 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect @@ -411,3 +412,6 @@ replace ( github.com/vulcand/predicate => github.com/gravitational/predicate v1.3.1 sigs.k8s.io/kustomize/api => github.com/gravitational/kustomize/api v0.16.0-teleport.1 ) + +// TODO(codingllama): Remove once no dependencies import stats/opentelemetry. +exclude google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index e2d3ece037acc..ba2f9d7df83f7 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= -cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= +cel.dev/expr v0.16.2 h1:RwRhoH17VhAu9U5CMvMhH1PDVgf0tuz9FT+24AfMLfU= +cel.dev/expr v0.16.2/go.mod h1:gXngZQMkWJoSbE8mOzehJlXQyubn/Vg0vR9/F3W7iw8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -698,8 +698,8 @@ github.com/DanielTitkov/go-adaptive-cards v0.2.2 h1:tBFExyvsbCcrBJEvPaV3FW4gcAkw github.com/DanielTitkov/go-adaptive-cards v0.2.2/go.mod h1:RtCzt65p/zEos6+zhiCFQmiaHmro6M63l9NP7xXx/Lg= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 h1:pB2F2JKCj1Znmp2rwxxt1J0Fg0wezTMgWYk5Mpbi1kg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2 h1:cZpsGsWTIFKymTA0je7IIvi1O7Es7apb9CF3EQlOcfE= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= @@ -812,6 +812,8 @@ github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.34.1 h1:8EwNbY+A/ github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.34.1/go.mod h1:2mMP2R86zLPAUz0TpJdsKW8XawHgs9Nk97fYJomO3o8= github.com/aws/aws-sdk-go-v2/service/athena v1.49.0 h1:D+iatX9gV6gCuNd6BnUkfwfZJw/cXlEk+LwwDdSMdtw= github.com/aws/aws-sdk-go-v2/service/athena v1.49.0/go.mod h1:27ljwDsnZvfrZKsLzWD4WFjI4OZutEFIjvVtYfj9gHc= +github.com/aws/aws-sdk-go-v2/service/dax v1.23.7 h1:hZg1sHhWXGZShzHGpwcaOT8HZfx26kkbRDNZgZda4xI= +github.com/aws/aws-sdk-go-v2/service/dax v1.23.7/go.mod h1:fYBjETTq8hZfirBEgXM1xIMy+tvCGYZTeWpjeKKp0bU= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.0 h1:isKhHsjpQR3CypQJ4G1g8QWx7zNpiC/xKw1zjgJYVno= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.0/go.mod h1:xDvUyIkwBwNtVZJdHEwAuhFly3mezwdEWkbJ5oNYwIw= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.8 h1:ntqHwZb+ZyVz0CFYUG0sQ02KMMJh+iXeV3bXoba+s4A= @@ -846,6 +848,8 @@ github.com/aws/aws-sdk-go-v2/service/organizations v1.36.0 h1:CVHfN8ZVvWzDkAf/Qj github.com/aws/aws-sdk-go-v2/service/organizations v1.36.0/go.mod h1:SVY+doFrL3KTvVMWzFLKvD7KYQ6GQfwNRPSQS7eA3cA= github.com/aws/aws-sdk-go-v2/service/rds v1.92.0 h1:W0gUYAjO24u/M6tpR041wMHJWGzleOhxtCnNLImdrZs= github.com/aws/aws-sdk-go-v2/service/rds v1.92.0/go.mod h1:ADD2uROOoEIXjbjDPEvDDZWnGmfKFYMddgKwG5RlBGw= +github.com/aws/aws-sdk-go-v2/service/redshift v1.53.0 h1:4/hmROBioc89sKlMVjHgOaH92zAkrAAMZR3BIvYwyD0= +github.com/aws/aws-sdk-go-v2/service/redshift v1.53.0/go.mod h1:UydVhUJOB/DaCJWiaBkPlvuzvWVcUlgbS2Bxn33bcKI= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= github.com/aws/aws-sdk-go-v2/service/sns v1.33.7 h1:N3o8mXK6/MP24BtD9sb51omEO9J9cgPM3Ughc293dZc= @@ -1947,8 +1951,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= +go.opentelemetry.io/contrib/detectors/gcp v1.31.0 h1:G1JQOreVrfhRkner+l4mrGxmfqYCAuy76asTDAo0xsA= +go.opentelemetry.io/contrib/detectors/gcp v1.31.0/go.mod h1:tzQL6E1l+iV44YFTkcAeNQqzXUiekSYP9jjJjXwEd00= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= @@ -1965,8 +1969,8 @@ go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZk go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= -go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -2675,13 +2679,11 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2700,8 +2702,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integrations/terraform/protoc-gen-terraform-workloadidentity.yaml b/integrations/terraform/protoc-gen-terraform-workloadidentity.yaml new file mode 100644 index 0000000000000..016f3209037ba --- /dev/null +++ b/integrations/terraform/protoc-gen-terraform-workloadidentity.yaml @@ -0,0 +1,68 @@ +--- +target_package_name: "v1" +default_package_name: "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" +duration_custom_type: Duration +use_state_for_unknown_by_default: true + +# Top-level type names to export +types: + - "WorkloadIdentity" + +# These import paths were not being automatically picked up by +# protoc-gen-terraform without these overrides +import_path_overrides: + "types": "github.com/gravitational/teleport/api/types" + "wrappers": "github.com/gravitational/teleport/api/types/wrappers" + "durationpb": "google.golang.org/protobuf/types/known/durationpb" + "timestamppb": "google.golang.org/protobuf/types/known/timestamppb" + "structpb": "google.golang.org/protobuf/types/known/structpb" + "v1": "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + "v11": "github.com/gravitational/teleport/api/gen/proto/go/teleport/label/v1" + "github_com_gravitational_teleport_integrations_terraform_tfschema": "github.com/gravitational/teleport/integrations/terraform/tfschema" + + +# id field is required for integration tests. It is not used by provider. +# We have to add it manually (might be removed in the future versions). +injected_fields: + WorkloadIdentity: + - name: id + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + computed: true + plan_modifiers: + - "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()" + +# These fields will be excluded +exclude_fields: + # Metadata (we id resources by name on our side) + - "WorkloadIdentity.metadata.id" + +# These fields will be marked as Computed: true +computed_fields: + # Metadata + - "WorkloadIdentity.metadata.namespace" + - "WorkloadIdentity.kind" + +# These fields will be marked as Required: true +required_fields: [] + + +plan_modifiers: + # Force to recreate resource if it's name changes + Metadata.name: + - "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()" + +# This must be defined for the generator to be happy, but in reality all time +# fields are overridden (because the protobuf timestamps contain locks and the +# linter gets mad if we use raw structs instead of pointers). +time_type: + type: "PlaceholderType" +duration_type: + type: "PlaceholderType" + +validators: + # Expires must be in the future + Metadata.expires: + - github_com_gravitational_teleport_integrations_terraform_tfschema.MustTimeBeInFuture() + +custom_types: + "WorkloadIdentity.metadata.expires": Timestamp \ No newline at end of file diff --git a/integrations/terraform/provider/data_source_teleport_workload_identity.go b/integrations/terraform/provider/data_source_teleport_workload_identity.go new file mode 100755 index 0000000000000..1b1d15fb99dcd --- /dev/null +++ b/integrations/terraform/provider/data_source_teleport_workload_identity.go @@ -0,0 +1,82 @@ +// Code generated by _gen/main.go DO NOT EDIT +/* +Copyright 2015-2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + + + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + + schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/workloadidentity/v1" +) + +// dataSourceTeleportWorkloadIdentityType is the data source metadata type +type dataSourceTeleportWorkloadIdentityType struct{} + +// dataSourceTeleportWorkloadIdentity is the resource +type dataSourceTeleportWorkloadIdentity struct { + p Provider +} + +// GetSchema returns the data source schema +func (r dataSourceTeleportWorkloadIdentityType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return schemav1.GenSchemaWorkloadIdentity(ctx) +} + +// NewDataSource creates the empty data source +func (r dataSourceTeleportWorkloadIdentityType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return dataSourceTeleportWorkloadIdentity{ + p: *(p.(*Provider)), + }, nil +} + +// Read reads teleport WorkloadIdentity +func (r dataSourceTeleportWorkloadIdentity) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var id types.String + diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentityI, err := r.p.Client.GetWorkloadIdentity(ctx, id.Value) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + var state types.Object + workloadIdentity := workloadIdentityI + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/integrations/terraform/provider/provider.go b/integrations/terraform/provider/provider.go index ec2648df1b274..13b20d20c434f 100644 --- a/integrations/terraform/provider/provider.go +++ b/integrations/terraform/provider/provider.go @@ -504,6 +504,7 @@ func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp "teleport_installer": resourceTeleportInstallerType{}, "teleport_access_monitoring_rule": resourceTeleportAccessMonitoringRuleType{}, "teleport_static_host_user": resourceTeleportStaticHostUserType{}, + "teleport_workload_identity": resourceTeleportWorkloadIdentityType{}, }, nil } @@ -531,6 +532,7 @@ func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc "teleport_installer": dataSourceTeleportInstallerType{}, "teleport_access_monitoring_rule": dataSourceTeleportAccessMonitoringRuleType{}, "teleport_static_host_user": dataSourceTeleportStaticHostUserType{}, + "teleport_workload_identity": dataSourceTeleportWorkloadIdentityType{}, }, nil } diff --git a/integrations/terraform/provider/resource_teleport_workload_identity.go b/integrations/terraform/provider/resource_teleport_workload_identity.go new file mode 100755 index 0000000000000..e5c59e0993b44 --- /dev/null +++ b/integrations/terraform/provider/resource_teleport_workload_identity.go @@ -0,0 +1,317 @@ +// Code generated by _gen/main.go DO NOT EDIT +/* +Copyright 2015-2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "fmt" + + workloadidentityv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" + + "github.com/gravitational/teleport/integrations/lib/backoff" + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/jonboulle/clockwork" + + schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/workloadidentity/v1" +) + +// resourceTeleportWorkloadIdentityType is the resource metadata type +type resourceTeleportWorkloadIdentityType struct{} + +// resourceTeleportWorkloadIdentity is the resource +type resourceTeleportWorkloadIdentity struct { + p Provider +} + +// GetSchema returns the resource schema +func (r resourceTeleportWorkloadIdentityType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return schemav1.GenSchemaWorkloadIdentity(ctx) +} + +// NewResource creates the empty resource +func (r resourceTeleportWorkloadIdentityType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) { + return resourceTeleportWorkloadIdentity{ + p: *(p.(*Provider)), + }, nil +} + +// Create creates the WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error + if !r.p.IsConfigured(resp.Diagnostics) { + return + } + + var plan types.Object + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentity := &workloadidentityv1.WorkloadIdentity{} + diags = schemav1.CopyWorkloadIdentityFromTerraform(ctx, plan, workloadIdentity) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + + workloadIdentityResource := workloadIdentity + + workloadIdentityResource.Kind = "workload_identity" + + id := workloadIdentityResource.Metadata.Name + + _, err = r.p.Client.GetWorkloadIdentity(ctx, id) + if !trace.IsNotFound(err) { + if err == nil { + existErr := fmt.Sprintf("WorkloadIdentity exists in Teleport. Either remove it (tctl rm workload_identity/%v)"+ + " or import it to the existing state (terraform import teleport_workload_identity.%v %v)", id, id, id) + + resp.Diagnostics.Append(diagFromErr("WorkloadIdentity exists in Teleport", trace.Errorf(existErr))) + return + } + + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + _, err = r.p.Client.CreateWorkloadIdentity(ctx, workloadIdentityResource) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error creating WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + var workloadIdentityI *workloadidentityv1.WorkloadIdentity + tries := 0 + backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) + for { + tries = tries + 1 + workloadIdentityI, err = r.p.Client.GetWorkloadIdentity(ctx, id) + if trace.IsNotFound(err) { + if bErr := backoff.Do(ctx); bErr != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(bErr), "workload_identity")) + return + } + if tries >= r.p.RetryConfig.MaxTries { + diagMessage := fmt.Sprintf("Error reading WorkloadIdentity (tried %d times) - state outdated, please import resource", tries) + resp.Diagnostics.AddError(diagMessage, "workload_identity") + } + continue + } + break + } + + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + workloadIdentityResource = workloadIdentityI + + workloadIdentity = workloadIdentityResource + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + plan.Attrs["id"] = types.String{Value: workloadIdentity.Metadata.Name} + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Read reads teleport WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + var id types.String + diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentityI, err := r.p.Client.GetWorkloadIdentity(ctx, id.Value) + if trace.IsNotFound(err) { + resp.State.RemoveResource(ctx) + return + } + + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + workloadIdentity := workloadIdentityI + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update updates teleport WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) { + if !r.p.IsConfigured(resp.Diagnostics) { + return + } + + var plan types.Object + diags := req.Plan.Get(ctx, &plan) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentity := &workloadidentityv1.WorkloadIdentity{} + diags = schemav1.CopyWorkloadIdentityFromTerraform(ctx, plan, workloadIdentity) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + workloadIdentityResource := workloadIdentity + + + + name := workloadIdentityResource.Metadata.Name + + workloadIdentityBefore, err := r.p.Client.GetWorkloadIdentity(ctx, name) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", err, "workload_identity")) + return + } + + _, err = r.p.Client.UpsertWorkloadIdentity(ctx, workloadIdentityResource) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error updating WorkloadIdentity", err, "workload_identity")) + return + } + var workloadIdentityI *workloadidentityv1.WorkloadIdentity + + tries := 0 + backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) + for { + tries = tries + 1 + workloadIdentityI, err = r.p.Client.GetWorkloadIdentity(ctx, name) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", err, "workload_identity")) + return + } + if workloadIdentityBefore.GetMetadata().Revision != workloadIdentityI.GetMetadata().Revision || false { + break + } + + if err := backoff.Do(ctx); err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + if tries >= r.p.RetryConfig.MaxTries { + diagMessage := fmt.Sprintf("Error reading WorkloadIdentity (tried %d times) - state outdated, please import resource", tries) + resp.Diagnostics.AddError(diagMessage, "workload_identity") + return + } + } + + workloadIdentityResource = workloadIdentityI + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete deletes Teleport WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) { + var id types.String + diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + err := r.p.Client.DeleteWorkloadIdentity(ctx, id.Value) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error deleting WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + resp.State.RemoveResource(ctx) +} + +// ImportState imports WorkloadIdentity state +func (r resourceTeleportWorkloadIdentity) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { + workloadIdentity, err := r.p.Client.GetWorkloadIdentity(ctx, req.ID) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + workloadIdentityResource := workloadIdentity + + + var state types.Object + + diags := resp.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentityResource, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + id := workloadIdentity.Metadata.Name + + state.Attrs["id"] = types.String{Value: id} + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/integrations/terraform/testlib/fixtures/workload_identity_0_create.tf b/integrations/terraform/testlib/fixtures/workload_identity_0_create.tf new file mode 100644 index 0000000000000..b5d0ebe8aae08 --- /dev/null +++ b/integrations/terraform/testlib/fixtures/workload_identity_0_create.tf @@ -0,0 +1,21 @@ +resource "teleport_workload_identity" "test" { + version = "v1" + metadata = { + name = "test" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "foo" + }] + } + ] + } + spiffe = { + id = "/test" + } + } +} \ No newline at end of file diff --git a/integrations/terraform/testlib/fixtures/workload_identity_1_update.tf b/integrations/terraform/testlib/fixtures/workload_identity_1_update.tf new file mode 100644 index 0000000000000..cced0a4f8ecdd --- /dev/null +++ b/integrations/terraform/testlib/fixtures/workload_identity_1_update.tf @@ -0,0 +1,21 @@ +resource "teleport_workload_identity" "test" { + version = "v1" + metadata = { + name = "test" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "foo" + }] + } + ] + } + spiffe = { + id = "/test/updated" + } + } +} \ No newline at end of file diff --git a/integrations/terraform/testlib/workload_identity_test.go b/integrations/terraform/testlib/workload_identity_test.go new file mode 100644 index 0000000000000..1e6d84cf6feb9 --- /dev/null +++ b/integrations/terraform/testlib/workload_identity_test.go @@ -0,0 +1,143 @@ +// Copyright 2024 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testlib + +import ( + "context" + "fmt" + "time" + + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/stretchr/testify/require" + + v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" + "github.com/gravitational/teleport/api/types" +) + +func (s *TerraformSuiteOSS) TestWorkloadIdentity() { + t := s.T() + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + checkDestroyed := func(state *terraform.State) error { + _, err := s.client.GetWorkloadIdentity(ctx, "test") + if trace.IsNotFound(err) { + return nil + } + return trace.Errorf("expected not found, actual: %v", err) + } + + name := "teleport_workload_identity.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: s.terraformProviders, + CheckDestroy: checkDestroyed, + IsUnitTest: true, + Steps: []resource.TestStep{ + { + Config: s.getFixture("workload_identity_0_create.tf"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "kind", "workload_identity"), + resource.TestCheckResourceAttr(name, "spec.spiffe.id", "/test"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.attribute", "user.name"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.equals", "foo"), + ), + }, + { + Config: s.getFixture("workload_identity_0_create.tf"), + PlanOnly: true, + }, + { + Config: s.getFixture("workload_identity_1_update.tf"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "kind", "workload_identity"), + resource.TestCheckResourceAttr(name, "spec.spiffe.id", "/test/updated"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.attribute", "user.name"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.equals", "foo"), + ), + }, + { + Config: s.getFixture("workload_identity_1_update.tf"), + PlanOnly: true, + }, + }, + }) +} + +func (s *TerraformSuiteOSS) TestImportWorkloadIdentity() { + t := s.T() + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + r := "teleport_workload_identity" + id := "test_import" + name := r + "." + id + + shu := &workloadidentityv1pb.WorkloadIdentity{ + Metadata: &v1.Metadata{ + Name: id, + }, + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{ + Allow: []*workloadidentityv1pb.WorkloadIdentityRule{ + { + Conditions: []*workloadidentityv1pb.WorkloadIdentityCondition{ + { + Attribute: "user.name", + Equals: "foo", + }, + }, + }, + }, + }, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/test", + }, + }, + } + shu, err := s.client.CreateWorkloadIdentity(ctx, shu) + require.NoError(t, err) + + require.Eventually(t, func() bool { + _, err := s.client.GetWorkloadIdentity(ctx, shu.GetMetadata().Name) + return err == nil + }, 5*time.Second, time.Second) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: s.terraformProviders, + IsUnitTest: true, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf("%s\nresource %q %q { }", s.terraformConfig, r, id), + ResourceName: name, + ImportState: true, + ImportStateId: id, + ImportStateCheck: func(state []*terraform.InstanceState) error { + require.Equal(t, types.KindWorkloadIdentity, state[0].Attributes["kind"]) + require.Equal(t, "/test", state[0].Attributes["spec.spiffe.id"]) + require.Equal(t, "user.name", state[0].Attributes["spec.rules.allow.0.conditions.0.attribute"]) + require.Equal(t, "foo", state[0].Attributes["spec.rules.allow.0.conditions.0.equals"]) + + return nil + }, + }, + }, + }) +} diff --git a/integrations/terraform/tfschema/types_terraform.go b/integrations/terraform/tfschema/types_terraform.go index dcc27576c8159..80c5ef1c906b3 100644 --- a/integrations/terraform/tfschema/types_terraform.go +++ b/integrations/terraform/tfschema/types_terraform.go @@ -1458,13 +1458,13 @@ func GenSchemaAuthPreferenceV2(ctx context.Context) (github_com_hashicorp_terraf }, "second_factor": { Computed: true, - Description: "SecondFactor is the type of mult-factor.", + Description: "SecondFactor is the type of mult-factor. Deprecated: Prefer using SecondFactors instead.", Optional: true, PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, }, "second_factors": { - Description: "SecondFactors is a list of supported second factor types.", + Description: "SecondFactors is a list of supported multi-factor types. 1 is \"otp\", 2 is \"webauthn\", 3 is \"sso\", If unspecified, the current default value is [1], or [\"otp\"].", Optional: true, Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.Int64Type}, }, diff --git a/integrations/terraform/tfschema/workloadidentity/v1/custom_types.go b/integrations/terraform/tfschema/workloadidentity/v1/custom_types.go new file mode 100644 index 0000000000000..eb615d5b1888b --- /dev/null +++ b/integrations/terraform/tfschema/workloadidentity/v1/custom_types.go @@ -0,0 +1,25 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package v1 + +import "github.com/gravitational/teleport/integrations/terraform/tfschema/resource153" + +var ( + GenSchemaTimestamp = resource153.GenSchemaTimestamp + CopyToTimestamp = resource153.CopyToTimestamp + CopyFromTimestamp = resource153.CopyFromTimestamp +) diff --git a/integrations/terraform/tfschema/workloadidentity/v1/resource_terraform.go b/integrations/terraform/tfschema/workloadidentity/v1/resource_terraform.go new file mode 100644 index 0000000000000..5a76525cde345 --- /dev/null +++ b/integrations/terraform/tfschema/workloadidentity/v1/resource_terraform.go @@ -0,0 +1,1316 @@ +/* +Copyright 2015-2022 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: teleport/workloadidentity/v1/resource.proto + +package v1 + +import ( + context "context" + fmt "fmt" + math "math" + + proto "github.com/gogo/protobuf/proto" + _ "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" + github_com_gravitational_teleport_integrations_terraform_tfschema "github.com/gravitational/teleport/integrations/terraform/tfschema" + github_com_hashicorp_terraform_plugin_framework_attr "github.com/hashicorp/terraform-plugin-framework/attr" + github_com_hashicorp_terraform_plugin_framework_diag "github.com/hashicorp/terraform-plugin-framework/diag" + github_com_hashicorp_terraform_plugin_framework_tfsdk "github.com/hashicorp/terraform-plugin-framework/tfsdk" + github_com_hashicorp_terraform_plugin_framework_types "github.com/hashicorp/terraform-plugin-framework/types" + github_com_hashicorp_terraform_plugin_go_tftypes "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// GenSchemaWorkloadIdentity returns tfsdk.Schema definition for WorkloadIdentity +func GenSchemaWorkloadIdentity(ctx context.Context) (github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema, github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics) { + return github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema{Attributes: map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "id": { + Computed: true, + Optional: false, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Required: false, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "kind": { + Computed: true, + Description: "The kind of resource represented.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "metadata": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "description is object description.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "expires": GenSchemaTimestamp(ctx, github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + Description: "expires is a global expiry time header can be set on any resource in the system.", + Optional: true, + Validators: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributeValidator{github_com_gravitational_teleport_integrations_terraform_tfschema.MustTimeBeInFuture()}, + }), + "labels": { + Description: "labels is a set of labels.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.MapType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "name": { + Description: "name is an object name.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.RequiresReplace()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "namespace": { + Computed: true, + Description: "namespace is object namespace. The field should be called \"namespace\" when it returns in Teleport 2.4.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "revision": { + Description: "revision is an opaque identifier which tracks the versions of a resource over time. Clients should ignore and not alter its value but must return the revision in any updates of a resource.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "Common metadata that all resources share.", + Optional: true, + }, + "spec": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "rules": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{"allow": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{"conditions": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "attribute": { + Description: "The name of the attribute to evaluate the condition against.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "equals": { + Description: "An exact string that the attribute must match.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "The conditions that must be met for this rule to be considered passed.", + Optional: true, + }}), + Description: "A list of rules used to determine if a WorkloadIdentity can be issued. If none are provided, it will be considered a pass. If any are provided, then at least one must pass for the rules to be considered passed.", + Optional: true, + }}), + Description: "The rules which are evaluated before the WorkloadIdentity can be issued.", + Optional: true, + }, + "spiffe": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "hint": { + Description: "A freeform text field which is provided to workloads along with a credential produced by this WorkloadIdentity. This can be used to provide additional context that can be used to select between multiple credentials.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "id": { + Description: "The path of the SPIFFE ID that will be issued to the workload. This should be prefixed with a forward-slash (\"/\"). This field supports templating using attributes.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "x509": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{"dns_sans": { + Description: "The DNS Subject Alternative Names (SANs) that should be included in an X509-SVID issued using this WorkloadIdentity. Each entry in this list supports templating using attributes.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }}), + Description: "Configuration specific to X509-SVIDs.", + Optional: true, + }, + }), + Description: "Configuration pertaining to the issuance of SPIFFE-compatible workload identity credentials.", + Optional: true, + }, + }), + Description: "The configured properties of the WorkloadIdentity", + Optional: true, + }, + "sub_kind": { + Description: "Differentiates variations of the same kind. All resources should contain one, even if it is never populated.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "version": { + Description: "The version of the resource being represented.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }}, nil +} + +// CopyWorkloadIdentityFromTerraform copies contents of the source Terraform object into a target struct +func CopyWorkloadIdentityFromTerraform(_ context.Context, tf github_com_hashicorp_terraform_plugin_framework_types.Object, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentity) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + { + a, ok := tf.Attrs["kind"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Kind = t + } + } + } + { + a, ok := tf.Attrs["sub_kind"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.sub_kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SubKind = t + } + } + } + { + a, ok := tf.Attrs["version"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.version"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Version = t + } + } + } + { + a, ok := tf.Attrs["metadata"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Metadata = nil + if !v.Null && !v.Unknown { + tf := v + obj.Metadata = &github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1.Metadata{} + obj := obj.Metadata + { + a, ok := tf.Attrs["name"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.name"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Name = t + } + } + } + { + a, ok := tf.Attrs["namespace"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.namespace"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Namespace = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["labels"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.labels"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.Map"}) + } else { + obj.Labels = make(map[string]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Labels[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["expires"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.expires"}) + } + CopyFromTimestamp(diags, a, &obj.Expires) + } + { + a, ok := tf.Attrs["revision"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.revision"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Revision = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["spec"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Spec = nil + if !v.Null && !v.Unknown { + tf := v + obj.Spec = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentitySpec{} + obj := obj.Spec + { + a, ok := tf.Attrs["rules"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Rules = nil + if !v.Null && !v.Unknown { + tf := v + obj.Rules = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRules{} + obj := obj.Rules + { + a, ok := tf.Attrs["allow"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Allow = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRule, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRule + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRule{} + obj := t + { + a, ok := tf.Attrs["conditions"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Conditions = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityCondition, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityCondition + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityCondition{} + obj := t + { + a, ok := tf.Attrs["attribute"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Attribute = t + } + } + } + { + a, ok := tf.Attrs["equals"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Equals = t + } + } + } + } + obj.Conditions[k] = t + } + } + } + } + } + } + } + obj.Allow[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["spiffe"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Spiffe = nil + if !v.Null && !v.Unknown { + tf := v + obj.Spiffe = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentitySPIFFE{} + obj := obj.Spiffe + { + a, ok := tf.Attrs["id"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe.id"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe.id", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Id = t + } + } + } + { + a, ok := tf.Attrs["hint"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe.hint"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe.hint", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Hint = t + } + } + } + { + a, ok := tf.Attrs["x509"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe.x509"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe.x509", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.X509 = nil + if !v.Null && !v.Unknown { + tf := v + obj.X509 = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentitySPIFFEX509{} + obj := obj.X509 + { + a, ok := tf.Attrs["dns_sans"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe.x509.dns_sans"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe.x509.dns_sans", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.DnsSans = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe.x509.dns_sans", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.DnsSans[k] = t + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return diags +} + +// CopyWorkloadIdentityToTerraform copies contents of the source Terraform object into a target struct +func CopyWorkloadIdentityToTerraform(ctx context.Context, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentity, tf *github_com_hashicorp_terraform_plugin_framework_types.Object) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + tf.Null = false + tf.Unknown = false + if tf.Attrs == nil { + tf.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value) + } + { + t, ok := tf.AttrTypes["kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.kind"}) + } else { + v, ok := tf.Attrs["kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Kind) == "" + } + v.Value = string(obj.Kind) + v.Unknown = false + tf.Attrs["kind"] = v + } + } + { + t, ok := tf.AttrTypes["sub_kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.sub_kind"}) + } else { + v, ok := tf.Attrs["sub_kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.sub_kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SubKind) == "" + } + v.Value = string(obj.SubKind) + v.Unknown = false + tf.Attrs["sub_kind"] = v + } + } + { + t, ok := tf.AttrTypes["version"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.version"}) + } else { + v, ok := tf.Attrs["version"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.version", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Version) == "" + } + v.Value = string(obj.Version) + v.Unknown = false + tf.Attrs["version"] = v + } + } + { + a, ok := tf.AttrTypes["metadata"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["metadata"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Metadata == nil { + v.Null = true + } else { + obj := obj.Metadata + tf := &v + { + t, ok := tf.AttrTypes["name"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.name"}) + } else { + v, ok := tf.Attrs["name"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.name", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Name) == "" + } + v.Value = string(obj.Name) + v.Unknown = false + tf.Attrs["name"] = v + } + } + { + t, ok := tf.AttrTypes["namespace"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.namespace"}) + } else { + v, ok := tf.Attrs["namespace"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.namespace", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Namespace) == "" + } + v.Value = string(obj.Namespace) + v.Unknown = false + tf.Attrs["namespace"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + a, ok := tf.AttrTypes["labels"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.labels"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.MapType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.MapType"}) + } else { + c, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.Map{ + + ElemType: o.ElemType, + Elems: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)) + } + } + if obj.Labels != nil { + t := o.ElemType + for k, a := range obj.Labels { + v, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.labels", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = false + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Labels) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["labels"] = c + } + } + } + { + t, ok := tf.AttrTypes["expires"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.expires"}) + } else { + v := CopyToTimestamp(diags, obj.Expires, t, tf.Attrs["expires"]) + tf.Attrs["expires"] = v + } + } + { + t, ok := tf.AttrTypes["revision"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.revision"}) + } else { + v, ok := tf.Attrs["revision"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.revision", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Revision) == "" + } + v.Value = string(obj.Revision) + v.Unknown = false + tf.Attrs["revision"] = v + } + } + } + v.Unknown = false + tf.Attrs["metadata"] = v + } + } + } + { + a, ok := tf.AttrTypes["spec"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["spec"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Spec == nil { + v.Null = true + } else { + obj := obj.Spec + tf := &v + { + a, ok := tf.AttrTypes["rules"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["rules"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Rules == nil { + v.Null = true + } else { + obj := obj.Rules + tf := &v + { + a, ok := tf.AttrTypes["allow"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["allow"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Allow)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Allow)) + } + } + if obj.Allow != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Allow) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Allow)) + } + for k, a := range obj.Allow { + v, ok := tf.Attrs["allow"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + a, ok := tf.AttrTypes["conditions"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["conditions"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Conditions)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Conditions)) + } + } + if obj.Conditions != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Conditions) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Conditions)) + } + for k, a := range obj.Conditions { + v, ok := tf.Attrs["conditions"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["attribute"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute"}) + } else { + v, ok := tf.Attrs["attribute"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.rules.allow.conditions.attribute", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Attribute) == "" + } + v.Value = string(obj.Attribute) + v.Unknown = false + tf.Attrs["attribute"] = v + } + } + { + t, ok := tf.AttrTypes["equals"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals"}) + } else { + v, ok := tf.Attrs["equals"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.rules.allow.conditions.equals", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Equals) == "" + } + v.Value = string(obj.Equals) + v.Unknown = false + tf.Attrs["equals"] = v + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Conditions) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["conditions"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Allow) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["allow"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["rules"] = v + } + } + } + { + a, ok := tf.AttrTypes["spiffe"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["spiffe"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Spiffe == nil { + v.Null = true + } else { + obj := obj.Spiffe + tf := &v + { + t, ok := tf.AttrTypes["id"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe.id"}) + } else { + v, ok := tf.Attrs["id"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.spiffe.id", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe.id", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Id) == "" + } + v.Value = string(obj.Id) + v.Unknown = false + tf.Attrs["id"] = v + } + } + { + t, ok := tf.AttrTypes["hint"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe.hint"}) + } else { + v, ok := tf.Attrs["hint"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.spiffe.hint", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe.hint", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Hint) == "" + } + v.Value = string(obj.Hint) + v.Unknown = false + tf.Attrs["hint"] = v + } + } + { + a, ok := tf.AttrTypes["x509"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe.x509"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe.x509", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["x509"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.X509 == nil { + v.Null = true + } else { + obj := obj.X509 + tf := &v + { + a, ok := tf.AttrTypes["dns_sans"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe.x509.dns_sans"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe.x509.dns_sans", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["dns_sans"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.DnsSans)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.DnsSans)) + } + } + if obj.DnsSans != nil { + t := o.ElemType + if len(obj.DnsSans) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.DnsSans)) + } + for k, a := range obj.DnsSans { + v, ok := tf.Attrs["dns_sans"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.spiffe.x509.dns_sans", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe.x509.dns_sans", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.DnsSans) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["dns_sans"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["x509"] = v + } + } + } + } + v.Unknown = false + tf.Attrs["spiffe"] = v + } + } + } + } + v.Unknown = false + tf.Attrs["spec"] = v + } + } + } + return diags +} + +// attrReadMissingDiag represents diagnostic message on an attribute missing in the source object +type attrReadMissingDiag struct { + Path string +} + +func (d attrReadMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadMissingDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object Attrs", d.Path) +} + +func (d attrReadMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrReadConversionFailureDiag represents diagnostic message on a failed type conversion on read +type attrReadConversionFailureDiag struct { + Path string + Type string +} + +func (d attrReadConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadConversionFailureDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrReadConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteMissingDiag represents diagnostic message on an attribute missing in the target object +type attrWriteMissingDiag struct { + Path string +} + +func (d attrWriteMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteMissingDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object AttrTypes", d.Path) +} + +func (d attrWriteMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteConversionFailureDiag represents diagnostic message on a failed type conversion on write +type attrWriteConversionFailureDiag struct { + Path string + Type string +} + +func (d attrWriteConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteConversionFailureDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrWriteConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteGeneralError represents diagnostic message on a generic error on write +type attrWriteGeneralError struct { + Path string + Err error +} + +func (d attrWriteGeneralError) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteGeneralError) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteGeneralError) Detail() string { + return fmt.Sprintf("%s: %s", d.Path, d.Err.Error()) +} + +func (d attrWriteGeneralError) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} diff --git a/lib/auth/accountrecovery.go b/lib/auth/accountrecovery.go index c7aa11c403f5a..7f133e4f3f3d1 100644 --- a/lib/auth/accountrecovery.go +++ b/lib/auth/accountrecovery.go @@ -210,7 +210,7 @@ func (a *Server) VerifyAccountRecovery(ctx context.Context, req *proto.VerifyAcc return nil, trace.AccessDenied(verifyRecoveryBadAuthnErrMsg) } - if err := a.verifyUserToken(startToken, authclient.UserTokenTypeRecoveryStart); err != nil { + if err := a.verifyUserToken(ctx, startToken, authclient.UserTokenTypeRecoveryStart); err != nil { return nil, trace.Wrap(err) } @@ -304,7 +304,7 @@ func (a *Server) CompleteAccountRecovery(ctx context.Context, req *proto.Complet return trace.AccessDenied(completeRecoveryGenericErrMsg) } - if err := a.verifyUserToken(approvedToken, authclient.UserTokenTypeRecoveryApproved); err != nil { + if err := a.verifyUserToken(ctx, approvedToken, authclient.UserTokenTypeRecoveryApproved); err != nil { return trace.Wrap(err) } @@ -403,7 +403,7 @@ func (a *Server) CreateAccountRecoveryCodes(ctx context.Context, req *proto.Crea return nil, trace.AccessDenied("only local users may create recovery codes") } - if err := a.verifyUserToken(token, authclient.UserTokenTypeRecoveryApproved, authclient.UserTokenTypePrivilege); err != nil { + if err := a.verifyUserToken(ctx, token, authclient.UserTokenTypeRecoveryApproved, authclient.UserTokenTypePrivilege); err != nil { return nil, trace.Wrap(err) } @@ -428,7 +428,7 @@ func (a *Server) GetAccountRecoveryToken(ctx context.Context, req *proto.GetAcco return nil, trace.AccessDenied("access denied") } - if err := a.verifyUserToken(token, authclient.UserTokenTypeRecoveryStart, authclient.UserTokenTypeRecoveryApproved); err != nil { + if err := a.verifyUserToken(ctx, token, authclient.UserTokenTypeRecoveryStart, authclient.UserTokenTypeRecoveryApproved); err != nil { return nil, trace.Wrap(err) } diff --git a/lib/auth/auth.go b/lib/auth/auth.go index 4a88c5e083603..067cea661c7e1 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -123,6 +123,7 @@ import ( usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/interval" + logutils "github.com/gravitational/teleport/lib/utils/log" vc "github.com/gravitational/teleport/lib/versioncontrol" "github.com/gravitational/teleport/lib/versioncontrol/github" uw "github.com/gravitational/teleport/lib/versioncontrol/upgradewindow" @@ -538,14 +539,14 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { if g, ok := connectedResourceGauges[s]; ok { g.Inc() } else { - log.Warnf("missing connected resources gauge for keep alive %s (this is a bug)", s) + as.logger.WarnContext(closeCtx, "missing connected resources gauge for keep alive (this is a bug)", "keep_alive_type", s) } }), inventory.WithOnDisconnect(func(s string, c int) { if g, ok := connectedResourceGauges[s]; ok { g.Sub(float64(c)) } else { - log.Warnf("missing connected resources gauge for keep alive %s (this is a bug)", s) + as.logger.WarnContext(closeCtx, "missing connected resources gauge for keep alive (this is a bug)", "keep_alive_type", s) } }), ) @@ -652,7 +653,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { // Add in a login hook for generating state during user login. as.ulsGenerator, err = userloginstate.NewGenerator(userloginstate.GeneratorConfig{ - Log: log, + Log: as.logger, AccessLists: &as, Access: &as, UsageEvents: &as, @@ -666,7 +667,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { as.RegisterLoginHook(as.ulsGenerator.LoginHook(services.UserLoginStates)) if _, ok := as.getCache(); !ok { - log.Warn("Auth server starting without cache (may have negative performance implications).") + as.logger.WarnContext(closeCtx, "Auth server starting without cache (may have negative performance implications)") } return &as, nil @@ -1444,7 +1445,7 @@ func (a *Server) runPeriodicOperations() { Jitter: retryutils.HalfJitter, }) } else if err := a.DeleteClusterAlert(a.closeCtx, OSSDesktopsAlertID); err != nil && !trace.IsNotFound(err) { - log.Warnf("Can't delete OSS non-AD desktops limit alert: %v", err) + a.logger.WarnContext(a.closeCtx, "Can't delete OSS non-AD desktops limit alert", "error", err) } // isolate the schedule of potentially long-running refreshRemoteClusters() from other tasks @@ -1487,9 +1488,9 @@ func (a *Server) runPeriodicOperations() { go func() { if err := a.AutoRotateCertAuthorities(a.closeCtx); err != nil { if trace.IsCompareFailed(err) { - log.Debugf("Cert authority has been updated concurrently: %v.", err) + a.logger.DebugContext(a.closeCtx, "Cert authority has been updated concurrently", "error", err) } else { - log.Errorf("Failed to perform cert rotation check: %v.", err) + a.logger.ErrorContext(a.closeCtx, "Failed to perform cert rotation check", "error", err) } } }() @@ -1535,7 +1536,7 @@ func (a *Server) runPeriodicOperations() { return false, nil } if _, err := a.Services.UpdateNode(a.closeCtx, srv); err != nil && !trace.IsCompareFailed(err) { - log.Warnf("Failed to update node hostname: %v", err) + logger.WarnContext(a.closeCtx, "Failed to update node hostname", "error", err) } } @@ -1544,7 +1545,7 @@ func (a *Server) runPeriodicOperations() { req, ) if err != nil { - log.Errorf("Failed to load nodes for heartbeat metric calculation: %v", err) + a.logger.ErrorContext(a.closeCtx, "Failed to load nodes for heartbeat metric calculation", "error", err) return } @@ -1662,7 +1663,7 @@ func (a *Server) doInstancePeriodics(ctx context.Context) { } if err := instances.Done(); err != nil { - log.Warnf("Failed stream instances for periodics: %v", err) + a.logger.WarnContext(ctx, "Failed stream instances for periodics", "error", err) return } @@ -1680,7 +1681,7 @@ func (a *Server) handleUpgradeEnrollPrompt(ctx context.Context, msg string, shou if !shouldPrompt { if err := a.DeleteClusterAlert(ctx, upgradeEnrollAlertID); err != nil && !trace.IsNotFound(err) { - log.Warnf("Failed to delete %s alert: %v", upgradeEnrollAlertID, err) + a.logger.WarnContext(ctx, "Failed to delete auto-upgrade-enroll alert", "error", err) } return } @@ -1698,11 +1699,11 @@ func (a *Server) handleUpgradeEnrollPrompt(ctx context.Context, msg string, shou types.WithAlertExpires(a.clock.Now().Add(alertTTL)), ) if err != nil { - log.Warnf("Failed to build %s alert: %v (this is a bug)", upgradeEnrollAlertID, err) + a.logger.WarnContext(ctx, "Failed to build auto-upgrade-enroll alert (this is a bug)", "error", err) return } if err := a.UpsertClusterAlert(ctx, alert); err != nil { - log.Warnf("Failed to set %s alert: %v", upgradeEnrollAlertID, err) + a.logger.WarnContext(ctx, "Failed to set auto-upgrade-enroll alert", "error", err) return } } @@ -1717,7 +1718,7 @@ const ( // is true it pulls the latest release info from GitHub. Otherwise, it loads the versions used // for the most recent alerts and re-syncs with latest cluster state. func (a *Server) syncReleaseAlerts(ctx context.Context, checkRemote bool) { - log.Debug("Checking for new teleport releases via github api.") + a.logger.DebugContext(ctx, "Checking for new teleport releases via github api") // NOTE: essentially everything in this function is going to be // scrapped/replaced once the inventory and version-control systems @@ -1746,12 +1747,12 @@ func (a *Server) syncReleaseAlerts(ctx context.Context, checkRemote bool) { if checkRemote { // scrape the github releases API with our visitor if err := github.Visit(&visitor); err != nil { - log.Warnf("Failed to load github releases: %v (this will not impact teleport functionality)", err) + a.logger.WarnContext(ctx, "Failed to load github releases (this will not impact teleport functionality)", "error", err) loadFailed = true } } else { if err := a.visitCachedAlertVersions(ctx, &visitor); err != nil { - log.Warnf("Failed to load release alert into: %v (this will not impact teleport functionality)", err) + a.logger.WarnContext(ctx, "Failed to load release alert info (this will not impact teleport functionality)", "error", err) loadFailed = true } } @@ -1816,7 +1817,10 @@ func (a *Server) doReleaseAlertSync(ctx context.Context, current vc.Target, visi if sp := visitor.NewestSecurityPatch(); sp.Ok() && sp.NewerThan(current) && !sp.SecurityPatchAltOf(current) { // explicit security patch alerts have a more limited audience, so we generate // them as their own separate alert. - log.Warnf("A newer security patch has been detected. current=%s, patch=%s", current.Version(), sp.Version()) + a.logger.WarnContext(ctx, "A newer security patch has been detected", + "current_version", current.Version(), + "patch_version", sp.Version(), + ) secMsg := fmt.Sprintf("A security patch is available for Teleport. Please upgrade your Cluster to %s or newer.", sp.Version()) alert, err := types.NewClusterAlert( @@ -1835,18 +1839,18 @@ func (a *Server) doReleaseAlertSync(ctx context.Context, current vc.Target, visi types.WithAlertExpires(a.clock.Now().Add(alertTTL)), ) if err != nil { - log.Warnf("Failed to build %s alert: %v (this is a bug)", secAlertID, err) + a.logger.WarnContext(ctx, "Failed to build security-patch-available alert (this is a bug)", "error", err) return } if err := a.UpsertClusterAlert(ctx, alert); err != nil { - log.Warnf("Failed to set %s alert: %v", secAlertID, err) + a.logger.WarnContext(ctx, "Failed to set security-patch-available alert", "error", err) return } } else if cleanup { err := a.DeleteClusterAlert(ctx, secAlertID) if err != nil && !trace.IsNotFound(err) { - log.Warnf("Failed to delete %s alert: %v", secAlertID, err) + a.logger.WarnContext(ctx, "Failed to delete security-patch-available alert", "error", err) } } } @@ -1900,13 +1904,13 @@ var ( func (a *Server) refreshRemoteClusters(ctx context.Context) { remoteClusters, err := a.Services.GetRemoteClusters(ctx) if err != nil { - log.WithError(err).Error("Failed to load remote clusters for status refresh") + a.logger.ErrorContext(ctx, "Failed to load remote clusters for status refresh", "error", err) return } netConfig, err := a.GetClusterNetworkingConfig(ctx) if err != nil { - log.WithError(err).Error("Failed to load networking config for remote cluster status refresh") + a.logger.ErrorContext(ctx, "Failed to load networking config for remote cluster status refresh", "error", err) return } @@ -1926,7 +1930,7 @@ func (a *Server) refreshRemoteClusters(ctx context.Context) { var updateCount int for _, remoteCluster := range remoteClusters { if updated, err := a.updateRemoteClusterStatus(ctx, netConfig, remoteCluster); err != nil { - log.WithError(err).Error("Failed to perform remote cluster status refresh") + a.logger.ErrorContext(ctx, "Failed to perform remote cluster status refresh", "error", err) } else if updated { updateCount++ } @@ -3483,7 +3487,9 @@ func (a *Server) attestHardwareKey(ctx context.Context, params *attestHardwareKe // a serial number in the user's traits, if any are set. registeredSerialNumbers, ok := params.userTraits[serialNumberTraitName] if !ok || len(registeredSerialNumbers) == 0 { - log.Debugf("user %q tried to sign in with hardware key support, but has no known hardware keys. A user's known hardware key serial numbers should be set \"in user.traits.%v\"", params.userName, serialNumberTraitName) + a.logger.DebugContext(ctx, "A user tried to sign in with hardware key support, but has no known hardware keys set in their traits", + "user", params.userName, + "expected_trait", "user.traits."+serialNumberTraitName) return attestedKeyPolicy, trace.BadParameter("cannot generate certs for user with no known hardware keys") } @@ -3493,7 +3499,10 @@ func (a *Server) attestHardwareKey(ctx context.Context, params *attestHardwareKe if !slices.ContainsFunc(registeredSerialNumbers, func(s string) bool { return slices.Contains(strings.Split(s, ","), attestedSerialNumber) }) { - log.Debugf("user %q tried to sign in with hardware key support with an unknown hardware key and was denied: YubiKey serial number %q", params.userName, attestedSerialNumber) + a.logger.DebugContext(ctx, "A user tried to sign in with hardware key support with an unknown hardware key and was denied", + "user", params.userName, + "yubikey_serial_number", attestedSerialNumber, + ) return attestedKeyPolicy, trace.BadParameter("cannot generate certs for user with unknown hardware key: YubiKey serial number %q", attestedSerialNumber) } } @@ -3585,7 +3594,7 @@ func (a *Server) emitCertCreateEvent(ctx context.Context, identity *tlsca.Identi UserAgent: trimUserAgent(metadata.UserAgentFromContext(ctx)), }, }); err != nil { - log.WithError(err).Warn("Failed to emit certificate create event.") + a.logger.WarnContext(ctx, "Failed to emit certificate create event", "error", err) } } @@ -3610,8 +3619,11 @@ func (a *Server) WithUserLock(ctx context.Context, username string, authenticate status := user.GetStatus() if status.IsLocked { if status.LockExpires.After(a.clock.Now().UTC()) { - log.Debugf("%v exceeds %v failed login attempts, locked until %v", - user.GetName(), defaults.MaxLoginAttempts, apiutils.HumanTimeFormat(status.LockExpires)) + a.logger.DebugContext(ctx, "Locking user that exceeded the failed login attempt limit", + "user", user.GetName(), + "failed_attempt_limit", defaults.MaxLoginAttempts, + "locked_until", apiutils.HumanTimeFormat(status.LockExpires), + ) err := trace.AccessDenied(MaxFailedAttemptsErrMsg) return trace.WithField(err, ErrFieldKeyUserMaxedAttempts, true) @@ -3635,25 +3647,31 @@ func (a *Server) WithUserLock(ctx context.Context, username string, authenticate attempt := services.LoginAttempt{Time: a.clock.Now().UTC(), Success: false} err = a.AddUserLoginAttempt(username, attempt, defaults.AttemptTTL) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "unable to persist failed login attempt", "error", err) return trace.Wrap(fnErr) } loginAttempts, err := a.GetUserLoginAttempts(username) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "unable to retrieve user login attempts", "error", err) return trace.Wrap(fnErr) } if !services.LastFailed(defaults.MaxLoginAttempts, loginAttempts) { - log.Debugf("%v user has less than %v failed login attempts", username, defaults.MaxLoginAttempts) + a.logger.DebugContext(ctx, "user has less than the failed login attempt limit", + "user", username, + "failed_attempt_limit", defaults.MaxLoginAttempts, + ) return trace.Wrap(fnErr) } lockUntil := a.clock.Now().UTC().Add(defaults.AccountLockInterval) - log.Debug(fmt.Sprintf("%v exceeds %v failed login attempts, locked until %v", - username, defaults.MaxLoginAttempts, apiutils.HumanTimeFormat(lockUntil))) + a.logger.DebugContext(ctx, "Locking user that exceeded the failed login attempt limit", + "user", username, + "failed_attempt_limit", defaults.MaxLoginAttempts, + "locked_until", apiutils.HumanTimeFormat(lockUntil), + ) user.SetLocked(lockUntil, "user has exceeded maximum failed login attempts") _, err = a.UpsertUser(ctx, user) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "failed to persist user record", "error", err) return trace.Wrap(fnErr) } @@ -3695,7 +3713,7 @@ func (a *Server) CreateAuthenticateChallenge(ctx context.Context, req *proto.Cre username: username, authErr: err, }); err != nil { - log.WithError(err).Warn("Failed to emit login event") + a.logger.WarnContext(ctx, "Failed to emit login event", "error", err) // err swallowed on purpose. } return nil, trace.Wrap(err) @@ -3708,11 +3726,11 @@ func (a *Server) CreateAuthenticateChallenge(ctx context.Context, req *proto.Cre case *proto.CreateAuthenticateChallengeRequest_RecoveryStartTokenID: token, err := a.GetUserToken(ctx, req.GetRecoveryStartTokenID()) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "failed to get user token", "error", err) return nil, trace.AccessDenied("invalid token") } - if err := a.verifyUserToken(token, authclient.UserTokenTypeRecoveryStart); err != nil { + if err := a.verifyUserToken(ctx, token, authclient.UserTokenTypeRecoveryStart); err != nil { return nil, trace.Wrap(err) } @@ -3747,7 +3765,7 @@ func (a *Server) CreateAuthenticateChallenge(ctx context.Context, req *proto.Cre return nil, trace.Wrap(err) } - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "failed to create MFA challenge", "error", err) return nil, trace.AccessDenied("unable to create MFA challenges") } @@ -3763,7 +3781,7 @@ func (a *Server) CreateRegisterChallenge(ctx context.Context, req *proto.CreateR var err error token, err = a.GetUserToken(ctx, req.GetTokenID()) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "failed to retrieve user token", "error", err) return nil, trace.AccessDenied("invalid token") } @@ -3774,7 +3792,7 @@ func (a *Server) CreateRegisterChallenge(ctx context.Context, req *proto.CreateR authclient.UserTokenTypeResetPasswordInvite, authclient.UserTokenTypeRecoveryApproved, } - if err := a.verifyUserToken(token, allowedTokenTypes...); err != nil { + if err := a.verifyUserToken(ctx, token, allowedTokenTypes...); err != nil { return nil, trace.AccessDenied("invalid token") } username = token.GetUser() @@ -3930,11 +3948,11 @@ func (a *Server) GetMFADevices(ctx context.Context, req *proto.GetMFADevicesRequ if req.GetTokenID() != "" { token, err := a.GetUserToken(ctx, req.GetTokenID()) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "failed to retrieve user token", "error", err) return nil, trace.AccessDenied("invalid token") } - if err := a.verifyUserToken(token, authclient.UserTokenTypeRecoveryApproved); err != nil { + if err := a.verifyUserToken(ctx, token, authclient.UserTokenTypeRecoveryApproved); err != nil { return nil, trace.Wrap(err) } @@ -3966,12 +3984,12 @@ func (a *Server) DeleteMFADeviceSync(ctx context.Context, req *proto.DeleteMFADe case req.TokenID != "": token, err := a.GetUserToken(ctx, req.TokenID) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "failed to retrieve user token", "error", err) return trace.AccessDenied("invalid token") } user = token.GetUser() - if err := a.verifyUserToken(token, authclient.UserTokenTypeRecoveryApproved, authclient.UserTokenTypePrivilege); err != nil { + if err := a.verifyUserToken(ctx, token, authclient.UserTokenTypeRecoveryApproved, authclient.UserTokenTypePrivilege); err != nil { return trace.Wrap(err) } @@ -4045,7 +4063,7 @@ func (a *Server) deleteMFADeviceSafely(ctx context.Context, user, deviceName str case *types.MFADevice_Sso: remainingDevices[types.SecondFactorType_SECOND_FACTOR_TYPE_SSO]++ default: - log.Warnf("Ignoring unknown device with type %T in deletion.", d.Device) + a.logger.WarnContext(ctx, "Ignoring unknown device type in deletion", "device_type", logutils.TypeAttr(d.Device)) continue } @@ -4123,11 +4141,12 @@ func (a *Server) AddMFADeviceSync(ctx context.Context, req *proto.AddMFADeviceSy case token != "": privilegeToken, err := a.GetUserToken(ctx, token) if err != nil { - log.Error(trace.DebugReport(err)) + a.logger.ErrorContext(ctx, "failed to retrieve user token", "error", err) return nil, trace.AccessDenied("invalid token") } if err := a.verifyUserToken( + ctx, privilegeToken, authclient.UserTokenTypePrivilege, authclient.UserTokenTypePrivilegeException, @@ -4221,7 +4240,7 @@ func (a *Server) verifyMFARespAndAddDevice(ctx context.Context, req *newMFADevic MFADeviceMetadata: mfaDeviceEventMetadata(dev), ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit add mfa device event.") + a.logger.WarnContext(ctx, "Failed to emit add mfa device event", "error", err) } return dev, nil @@ -4590,7 +4609,11 @@ func (a *Server) GenerateHostCerts(ctx context.Context, req *proto.HostCertsRequ if err := a.limiter.AcquireConnection(req.Role.String()); err != nil { generateThrottledRequestsCount.Inc() - log.Debugf("Node %q [%v] is rate limited: %v.", req.NodeName, req.HostID, req.Role) + a.logger.DebugContext(ctx, "Rejecting request to generate host certs because host is rate limited", + "name", req.NodeName, + "host_id", req.HostID, + "role", req.Role, + ) return nil, trace.Wrap(err) } defer a.limiter.ReleaseConnection(req.Role.String()) @@ -4648,7 +4671,10 @@ func (a *Server) GenerateHostCerts(ctx context.Context, req *proto.HostCertsRequ // cache is out of sync, this will result in higher read rate // to the backend, which is a fine tradeoff if !req.NoCache && !req.Rotation.IsZero() && !req.Rotation.Matches(ca.GetRotation()) { - log.Debugf("Client sent rotation state %v, cache state is %v, using state from the DB.", req.Rotation, ca.GetRotation()) + a.logger.DebugContext(ctx, "Client sent rotation state and cache state mismatch, retrieving state from the DB", + "client_state", req.Rotation, + "cache_state", ca.GetRotation(), + ) ca, err = a.Services.GetCertAuthority(ctx, types.CertAuthID{ Type: types.HostCA, DomainName: clusterName.GetClusterName(), @@ -4956,7 +4982,7 @@ func (a *Server) checkTokenTTL(tok types.ProvisionToken) bool { defer cancel() if err := a.DeleteToken(ctx, tok.GetName()); err != nil { if !trace.IsNotFound(err) { - log.Warnf("Unable to delete token from backend: %v.", err) + a.logger.WarnContext(ctx, "Unable to delete token from backend", "error", err) } } }() @@ -5160,7 +5186,10 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ return nil, trace.Wrap(err) } - log.Debugf("Creating Access Request %v with expiry %v.", req.GetName(), req.Expiry()) + a.logger.DebugContext(ctx, "Creating Access Request", + "request_name", req.GetName(), + "request_expiry", req.Expiry(), + ) if _, err := a.Services.CreateAccessRequestV2(ctx, req); err != nil { return nil, trace.Wrap(err) @@ -5171,7 +5200,7 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ var err error annotations, err = apievents.EncodeMapStrings(sa) if err != nil { - log.WithError(err).Debug("Failed to encode access request annotations.") + a.logger.DebugContext(ctx, "Failed to encode access request annotations", "error", err) } } @@ -5193,7 +5222,7 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ Annotations: annotations, }) if err != nil { - log.WithError(err).Warn("Failed to emit access request create event.") + a.logger.WarnContext(ctx, "Failed to emit access request create event", "error", err) } // Create a notification. @@ -5238,7 +5267,7 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ }, }) if err != nil { - log.WithError(err).Warn("Failed to create access request notification") + a.logger.WarnContext(ctx, "Failed to create access request notification", "error", err) } // calculate the promotions @@ -5247,7 +5276,7 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ // Create the promotion entry even if the allowed promotion is empty. Otherwise, we won't // be able to distinguish between an allowed empty set and generation failure. if err := a.Services.CreateAccessRequestAllowedPromotions(ctx, reqCopy, promotions); err != nil { - log.WithError(err).Warn("Failed to update access request with promotions.") + a.logger.WarnContext(ctx, "Failed to update access request with promotions", "error", err) } } @@ -5328,7 +5357,7 @@ func (a *Server) generateAccessRequestPromotions(ctx context.Context, req types. if err != nil { // Do not fail the request if the promotions failed to generate. // The request promotion will be blocked, but the request can still be approved. - log.WithError(err).Warn("Failed to generate access list promotions.") + a.logger.WarnContext(ctx, "Failed to generate access list promotions", "error", err) } return reqCopy, promotions } @@ -5347,7 +5376,7 @@ func updateAccessRequestWithAdditionalReviewers(ctx context.Context, req types.A for _, promotion := range promotions.Promotions { allOwners, err := accessLists.GetAccessListOwners(ctx, promotion.AccessListName) if err != nil { - log.WithError(err).Warnf("Failed to get nested access list owners for %v, skipping additional reviewers", promotion.AccessListName) + logger.WarnContext(ctx, "Failed to get nested access list owners, skipping additional reviewers", "error", err, "access_list", promotion.AccessListName) break } @@ -5374,7 +5403,7 @@ func (a *Server) DeleteAccessRequest(ctx context.Context, name string) error { UserMetadata: authz.ClientUserMetadata(ctx), RequestID: name, }); err != nil { - log.WithError(err).Warn("Failed to emit access request delete event.") + a.logger.WarnContext(ctx, "Failed to emit access request delete event", "error", err) } return nil } @@ -5403,7 +5432,7 @@ func (a *Server) SetAccessRequestState(ctx context.Context, params types.AccessR var err error event.Annotations, err = apievents.EncodeMapStrings(sa) if err != nil { - log.WithError(err).Debug("Failed to encode access request annotations.") + a.logger.DebugContext(ctx, "Failed to encode access request annotations", "error", err) } } @@ -5414,14 +5443,14 @@ func (a *Server) SetAccessRequestState(ctx context.Context, params types.AccessR if len(params.Annotations) > 0 { annotations, err := apievents.EncodeMapStrings(params.Annotations) if err != nil { - log.WithError(err).Debugf("Failed to encode access request annotations.") + a.logger.DebugContext(ctx, "Failed to encode access request annotations", "error", err) } else { event.Annotations = annotations } } err = a.emitter.EmitAuditEvent(a.closeCtx, event) if err != nil { - log.WithError(err).Warn("Failed to emit access request update event.") + a.logger.WarnContext(ctx, "Failed to emit access request update event", "error", err) } return trace.Wrap(err) } @@ -5498,20 +5527,20 @@ func (a *Server) submitAccessReview( if !req.GetState().IsPending() { _, err = a.Services.CreateUserNotification(ctx, generateAccessRequestReviewedNotification(req, params)) if err != nil { - log.WithError(err).Debugf("Failed to emit access request reviewed notification.") + a.logger.DebugContext(ctx, "Failed to emit access request reviewed notification", "error", err) } } if len(params.Review.Annotations) > 0 { annotations, err := apievents.EncodeMapStrings(params.Review.Annotations) if err != nil { - log.WithError(err).Debugf("Failed to encode access request annotations.") + a.logger.DebugContext(ctx, "Failed to encode access request annotations", "error", err) } else { event.Annotations = annotations } } if err := a.emitter.EmitAuditEvent(a.closeCtx, event); err != nil { - log.WithError(err).Warn("Failed to emit access request update event.") + a.logger.WarnContext(ctx, "Failed to emit access request update event", "error", err) } return req, nil @@ -5836,7 +5865,7 @@ func (a *Server) DeleteWindowsDesktop(ctx context.Context, hostID, name string) return trace.Wrap(err) } if _, err := a.desktopsLimitExceeded(ctx); err != nil { - log.Warnf("Can't check OSS non-AD desktops limit: %v", err) + a.logger.WarnContext(ctx, "Can't check OSS non-AD desktops limit", "error", err) } return nil } @@ -5911,7 +5940,7 @@ func (a *Server) streamWindowsDesktops(ctx context.Context, startKey string) str func (a *Server) syncDesktopsLimitAlert(ctx context.Context) { exceeded, err := a.desktopsLimitExceeded(ctx) if err != nil { - log.Warnf("Can't check OSS non-AD desktops limit: %v", err) + a.logger.WarnContext(ctx, "Can't check OSS non-AD desktops limit", "error", err) } if !exceeded { return @@ -5924,10 +5953,10 @@ func (a *Server) syncDesktopsLimitAlert(ctx context.Context) { types.WithAlertLabel(types.AlertLinkText, OSSDesktopsAlertLinkText), types.WithAlertExpires(time.Now().Add(OSSDesktopsCheckPeriod))) if err != nil { - log.Warnf("Can't create OSS non-AD desktops limit alert: %v", err) + a.logger.WarnContext(ctx, "Can't create OSS non-AD desktops limit alert", "error", err) } if err := a.UpsertClusterAlert(ctx, alert); err != nil { - log.Warnf("Can't upsert OSS non-AD desktops limit alert: %v", err) + a.logger.WarnContext(ctx, "Can't upsert OSS non-AD desktops limit alert", "error", err) } } @@ -5957,7 +5986,7 @@ func (a *Server) desktopsLimitExceeded(ctx context.Context) (bool, error) { func (a *Server) syncDynamicLabelsAlert(ctx context.Context) { roles, err := a.GetRoles(ctx) if err != nil { - log.Warnf("Can't get roles: %v", err) + a.logger.WarnContext(ctx, "Can't get roles", "error", err) } var rolesWithDynamicDenyLabels bool for _, role := range roles { @@ -5967,7 +5996,10 @@ func (a *Server) syncDynamicLabelsAlert(ctx context.Context) { break } if err != nil { - log.Warnf("Error checking labels in role %s: %v", role.GetName(), err) + a.logger.WarnContext(ctx, "Error checking labels in role", + "role", role.GetName(), + "error", err, + ) continue } } @@ -5981,10 +6013,10 @@ func (a *Server) syncDynamicLabelsAlert(ctx context.Context) { types.WithAlertLabel(types.AlertVerbPermit, fmt.Sprintf("%s:%s", types.KindRole, types.VerbRead)), ) if err != nil { - log.Warnf("Failed to build %s alert: %v (this is a bug)", dynamicLabelAlertID, err) + a.logger.WarnContext(ctx, "Failed to build dynamic-labels-in-deny-rules alert(this is a bug)", "error", err) } if err := a.UpsertClusterAlert(ctx, alert); err != nil { - log.Warnf("Failed to set %s alert: %v", dynamicLabelAlertID, err) + a.logger.WarnContext(ctx, "Failed to set dynamic-labels-in-deny-rules alert", "error", err) } } @@ -6002,7 +6034,7 @@ func (a *Server) CleanupNotifications(ctx context.Context) { } response, nextKey, err := a.Cache.ListUserNotifications(ctx, 20, userNotificationsPageKey) if err != nil { - slog.WarnContext(ctx, "failed to list user notifications for periodic cleanup", "error", err) + a.logger.WarnContext(ctx, "failed to list user notifications for periodic cleanup", "error", err) } userNotifications = append(userNotifications, response...) if nextKey == "" { @@ -6023,7 +6055,7 @@ func (a *Server) CleanupNotifications(ctx context.Context) { } response, nextKey, err := a.Cache.ListGlobalNotifications(ctx, 20, globalNotificationsPageKey) if err != nil { - slog.WarnContext(ctx, "failed to list global notifications for periodic cleanup", "error", err) + a.logger.WarnContext(ctx, "failed to list global notifications for periodic cleanup", "error", err) } globalNotifications = append(globalNotifications, response...) if nextKey == "" { @@ -6050,7 +6082,7 @@ func (a *Server) CleanupNotifications(ctx context.Context) { return } if err := a.DeleteGlobalNotification(ctx, notificationID); err != nil && !trace.IsNotFound(err) { - slog.WarnContext(ctx, "encountered error attempting to cleanup global notification", "error", err, "notification_id", notificationID) + a.logger.WarnContext(ctx, "encountered error attempting to cleanup global notification", "error", err, "notification_id", notificationID) } } else { nonExpiredGlobalNotificationsByID[notificationID] = gn @@ -6071,7 +6103,7 @@ func (a *Server) CleanupNotifications(ctx context.Context) { return } if err := a.DeleteUserNotification(ctx, user, notificationID); err != nil && !trace.IsNotFound(err) { - slog.WarnContext(ctx, "encountered error attempting to cleanup user notification", "error", err, "notification_id", notificationID, "target_user", user) + a.logger.WarnContext(ctx, "encountered error attempting to cleanup user notification", "error", err, "notification_id", notificationID, "target_user", user) } } else { nonExpiredUserNotificationsByID[notificationID] = un @@ -6090,7 +6122,7 @@ func (a *Server) CleanupNotifications(ctx context.Context) { } response, nextKey, err := a.ListNotificationStatesForAllUsers(ctx, 20, userNotificationStatesPageKey) if err != nil { - slog.WarnContext(ctx, "encountered error attempting to list notification states for cleanup", "error", err) + a.logger.WarnContext(ctx, "encountered error attempting to list notification states for cleanup", "error", err) } userNotificationStates = append(userNotificationStates, response...) if nextKey == "" { @@ -6112,7 +6144,7 @@ func (a *Server) CleanupNotifications(ctx context.Context) { return } if err := a.DeleteUserNotificationState(ctx, username, id); err != nil { - slog.WarnContext(ctx, "encountered error attempting to cleanup notification state", "error", err, "user", username, "id", id) + a.logger.WarnContext(ctx, "encountered error attempting to cleanup notification state", "error", err, "user", username, "id", id) } } } @@ -6219,7 +6251,7 @@ func (a *Server) CreateApp(ctx context.Context, app types.Application) error { AppLabels: app.GetStaticLabels(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit app create event.") + a.logger.WarnContext(ctx, "Failed to emit app create event", "error", err) } return nil } @@ -6245,7 +6277,7 @@ func (a *Server) UpdateApp(ctx context.Context, app types.Application) error { AppLabels: app.GetStaticLabels(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit app update event.") + a.logger.WarnContext(ctx, "Failed to emit app update event", "error", err) } return nil } @@ -6265,7 +6297,7 @@ func (a *Server) DeleteApp(ctx context.Context, name string) error { Name: name, }, }); err != nil { - log.WithError(err).Warn("Failed to emit app delete event.") + a.logger.WarnContext(ctx, "Failed to emit app delete event", "error", err) } return nil } @@ -6309,7 +6341,7 @@ func (a *Server) CreateDatabase(ctx context.Context, database types.Database) er DatabaseGCPInstanceID: database.GetGCP().InstanceID, }, }); err != nil { - log.WithError(err).Warn("Failed to emit database create event.") + a.logger.WarnContext(ctx, "Failed to emit database create event", "error", err) } return nil } @@ -6339,7 +6371,7 @@ func (a *Server) UpdateDatabase(ctx context.Context, database types.Database) er DatabaseGCPInstanceID: database.GetGCP().InstanceID, }, }); err != nil { - log.WithError(err).Warn("Failed to emit database update event.") + a.logger.WarnContext(ctx, "Failed to emit database update event", "error", err) } return nil } @@ -6359,7 +6391,7 @@ func (a *Server) DeleteDatabase(ctx context.Context, name string) error { Name: name, }, }); err != nil { - log.WithError(err).Warn("Failed to emit database delete event.") + a.logger.WarnContext(ctx, "Failed to emit database delete event", "error", err) } return nil } @@ -6427,7 +6459,7 @@ func (a *Server) CreateKubernetesCluster(ctx context.Context, kubeCluster types. KubeLabels: kubeCluster.GetStaticLabels(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit kube cluster create event.") + a.logger.WarnContext(ctx, "Failed to emit kube cluster create event", "error", err) } return nil } @@ -6454,7 +6486,7 @@ func (a *Server) UpdateKubernetesCluster(ctx context.Context, kubeCluster types. KubeLabels: kubeCluster.GetStaticLabels(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit kube cluster update event.") + a.logger.WarnContext(ctx, "Failed to emit kube cluster update event", "error", err) } return nil } @@ -6474,7 +6506,7 @@ func (a *Server) DeleteKubernetesCluster(ctx context.Context, name string) error Name: name, }, }); err != nil { - log.WithError(err).Warn("Failed to emit kube cluster delete event.") + a.logger.WarnContext(ctx, "Failed to emit kube cluster delete event", "error", err) } return nil } @@ -6573,12 +6605,12 @@ func (a *Server) exportUpgradeWindowsCached(ctx context.Context) (proto.ExportUp rsp.KubeControllerSchedule, err = uw.EncodeKubeControllerSchedule(sched) if err != nil { - log.Warnf("Failed to encode kube controller maintenance schedule: %v", err) + a.logger.WarnContext(ctx, "Failed to encode kube controller maintenance schedule", "error", err) } rsp.SystemdUnitSchedule, err = uw.EncodeSystemdUnitSchedule(sched) if err != nil { - log.Warnf("Failed to encode systemd unit maintenance schedule: %v", err) + a.logger.WarnContext(ctx, "Failed to encode systemd unit maintenance schedule", "error", err) } return rsp, nil @@ -6837,7 +6869,7 @@ func (a *Server) isMFARequired(ctx context.Context, checker services.AccessCheck // most likely access denied. if !errors.Is(noMFAAccessErr, services.ErrSessionMFARequired) { if !trace.IsAccessDenied(noMFAAccessErr) { - log.WithError(noMFAAccessErr).Warn("Could not determine MFA access") + a.logger.WarnContext(ctx, "Could not determine MFA access", "error", noMFAAccessErr) } // Mask the access denied errors by returning false to prevent resource @@ -6921,7 +6953,7 @@ func (a *Server) mfaAuthChallenge(ctx context.Context, user string, ssoClientRed ChallengeScope: challengeExtensions.Scope.String(), ChallengeAllowReuse: challengeExtensions.AllowReuse == mfav1.ChallengeAllowReuse_CHALLENGE_ALLOW_REUSE_YES, }); err != nil { - log.WithError(err).Warn("Failed to emit CreateMFAAuthChallenge event.") + a.logger.WarnContext(ctx, "Failed to emit CreateMFAAuthChallenge event", "error", err) } return &proto.MFAAuthenticateChallenge{ @@ -6983,7 +7015,7 @@ func (a *Server) mfaAuthChallenge(ctx context.Context, user string, ssoClientRed ChallengeScope: challengeExtensions.Scope.String(), ChallengeAllowReuse: challengeExtensions.AllowReuse == mfav1.ChallengeAllowReuse_CHALLENGE_ALLOW_REUSE_YES, }); err != nil { - log.WithError(err).Warn("Failed to emit CreateMFAAuthChallenge event.") + a.logger.WarnContext(ctx, "Failed to emit CreateMFAAuthChallenge event", "error", err) } return challenge, nil @@ -7008,7 +7040,7 @@ func groupByDeviceType(devs []*types.MFADevice) devicesByType { case *types.MFADevice_Sso: res.SSO = dev default: - log.Warningf("Skipping MFA device of unknown type %T.", dev.Device) + logger.WarnContext(context.Background(), "Skipping MFA device with unknown type", "device_type", logutils.TypeAttr(dev.Device)) } } return res @@ -7080,7 +7112,7 @@ func (a *Server) ValidateMFAAuthResponse( // Read ClusterName for audit. var clusterName string if cn, err := a.GetClusterName(); err != nil { - log.WithError(err).Warn("Failed to read cluster name") + a.logger.WarnContext(ctx, "Failed to read cluster name", "error", err) // err swallowed on purpose. } else { clusterName = cn.GetClusterName() @@ -7114,7 +7146,7 @@ func (a *Server) ValidateMFAAuthResponse( auditEvent.ChallengeAllowReuse = authData.AllowReuse == mfav1.ChallengeAllowReuse_CHALLENGE_ALLOW_REUSE_YES } if err := a.emitter.EmitAuditEvent(ctx, auditEvent); err != nil { - log.WithError(err).Warn("Failed to emit ValidateMFAAuthResponse event") + a.logger.WarnContext(ctx, "Failed to emit ValidateMFAAuthResponse event", "error", err) // err swallowed on purpose. } @@ -7196,7 +7228,7 @@ func (a *Server) validateMFAAuthResponseInternal( }, nil case *proto.MFAAuthenticateResponse_TOTP: - dev, err := a.checkOTP(user, res.TOTP.Code) + dev, err := a.checkOTP(ctx, user, res.TOTP.Code) if err != nil { return nil, trace.Wrap(err) } @@ -7365,7 +7397,7 @@ func (a *Server) ensureLocalAdditionalKeys(ctx context.Context, ca types.CertAut if err != nil { return trace.Wrap(err) } - log.Infof("Successfully added locally usable additional trusted keys to %s CA.", ca.GetType()) + a.logger.InfoContext(ctx, "Successfully added locally usable additional trusted keys to CA", "ca_type", ca.GetType()) return nil } @@ -7458,7 +7490,11 @@ func (a *Server) getProxyPublicAddr() string { continue } if _, err := utils.ParseAddr(addr); err != nil { - log.Warningf("Invalid public address on the proxy %q: %q: %v.", p.GetName(), addr, err) + a.logger.WarnContext(a.closeCtx, "Invalid public address found in proxy", + "proxy", p.GetName(), + "public_addr", addr, + "error", err, + ) continue } return addr diff --git a/lib/auth/auth_test.go b/lib/auth/auth_test.go index 8f535a1727588..b9e9eb0a5aa8b 100644 --- a/lib/auth/auth_test.go +++ b/lib/auth/auth_test.go @@ -1262,7 +1262,7 @@ func TestTrustedClusterCRUDEventEmitted(t *testing.T) { // test create event for switch case: when tc exists but enabled is false tc.SetEnabled(false) - _, err = s.a.UpsertTrustedCluster(ctx, tc) + _, err = s.a.UpsertTrustedClusterV2(ctx, tc) require.NoError(t, err) require.Equal(t, events.TrustedClusterCreateEvent, s.mockEmitter.LastEvent().GetType()) createEvt := s.mockEmitter.LastEvent().(*apievents.TrustedClusterCreate) @@ -1272,7 +1272,7 @@ func TestTrustedClusterCRUDEventEmitted(t *testing.T) { // test create event for switch case: when tc exists but enabled is true tc.SetEnabled(true) - _, err = s.a.UpsertTrustedCluster(ctx, tc) + _, err = s.a.UpsertTrustedClusterV2(ctx, tc) require.NoError(t, err) require.Equal(t, events.TrustedClusterCreateEvent, s.mockEmitter.LastEvent().GetType()) createEvt = s.mockEmitter.LastEvent().(*apievents.TrustedClusterCreate) diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index c48bdf2684c83..85cc6fe6237b1 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -30,7 +30,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" collectortracev1 "go.opentelemetry.io/proto/otlp/collector/trace/v1" otlpcommonv1 "go.opentelemetry.io/proto/otlp/common/v1" @@ -65,6 +64,7 @@ import ( "github.com/gravitational/teleport/lib/services/local" "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/tlsca" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // ServerWithRoles is a wrapper around auth service @@ -163,14 +163,17 @@ func (a *ServerWithRoles) identityCenterAction(namespace string, resource string // actionForListWithCondition extracts a restrictive filter condition to be // added to a list query after a simple resource check fails. -func (a *ServerWithRoles) actionForListWithCondition(resource, identifier string) (*types.WhereExpr, error) { +func (a *ServerWithRoles) actionForListWithCondition(ctx context.Context, resource, identifier string) (*types.WhereExpr, error) { origErr := a.action(resource, types.VerbList) if origErr == nil || !trace.IsAccessDenied(origErr) { return nil, trace.Wrap(origErr) } cond, err := a.context.Checker.ExtractConditionForIdentifier(&services.Context{User: a.context.User}, apidefaults.Namespace, resource, types.VerbList, identifier) if trace.IsAccessDenied(err) { - log.WithError(err).Infof("Access to %v %v in namespace %v denied to %v.", types.VerbList, resource, apidefaults.Namespace, a.context.Checker) + a.authServer.logger.InfoContext(ctx, "Access to list resource denied", + "error", err, + "resource", resource, + ) // Return the original AccessDenied to avoid leaking information. return nil, trace.Wrap(origErr) } @@ -179,14 +182,14 @@ func (a *ServerWithRoles) actionForListWithCondition(resource, identifier string // actionWithExtendedContext performs an additional RBAC check with extended // rule context after a simple resource check fails. -func (a *ServerWithRoles) actionWithExtendedContext(kind, verb string, extendContext func(*services.Context) error) error { +func (a *ServerWithRoles) actionWithExtendedContext(ctx context.Context, kind, verb string, extendContext func(*services.Context) error) error { ruleCtx := &services.Context{User: a.context.User} origErr := a.context.Checker.CheckAccessToRule(ruleCtx, apidefaults.Namespace, kind, verb) if origErr == nil || !trace.IsAccessDenied(origErr) { return trace.Wrap(origErr) } if err := extendContext(ruleCtx); err != nil { - log.WithError(err).Warning("Failed to extend context for second RBAC check.") + a.authServer.logger.WarnContext(ctx, "Failed to extend context for second RBAC check", "error", err) // Return the original AccessDenied to avoid leaking information. return trace.Wrap(origErr) } @@ -196,30 +199,14 @@ func (a *ServerWithRoles) actionWithExtendedContext(kind, verb string, extendCon // actionForKindSession is a special checker that grants access to session // recordings. It can allow access to a specific recording based on the // `where` section of the user's access rule for kind `session`. -func (a *ServerWithRoles) actionForKindSession(ctx context.Context, sid session.ID) (types.SessionKind, error) { - sessionEnd, err := a.findSessionEndEvent(ctx, sid) - - extendContext := func(ctx *services.Context) error { - ctx.Session = sessionEnd +func (a *ServerWithRoles) actionForKindSession(ctx context.Context, sid session.ID) error { + extendContext := func(servicesCtx *services.Context) error { + sessionEnd, err := a.findSessionEndEvent(ctx, sid) + servicesCtx.Session = sessionEnd return trace.Wrap(err) } - var sessionKind types.SessionKind - switch e := sessionEnd.(type) { - case *apievents.SessionEnd: - sessionKind = types.SSHSessionKind - if e.KubernetesCluster != "" { - sessionKind = types.KubernetesSessionKind - } - case *apievents.DatabaseSessionEnd: - sessionKind = types.DatabaseSessionKind - case *apievents.AppSessionEnd: - sessionKind = types.AppSessionKind - case *apievents.WindowsDesktopSessionEnd: - sessionKind = types.WindowsDesktopSessionKind - } - - return sessionKind, trace.Wrap(a.actionWithExtendedContext(types.KindSession, types.VerbRead, extendContext)) + return trace.Wrap(a.actionWithExtendedContext(ctx, types.KindSession, types.VerbRead, extendContext)) } // localServerAction returns an access denied error if the role is not one of the builtin server roles. @@ -612,17 +599,17 @@ func (a *ServerWithRoles) RegisterUsingToken(ctx context.Context, req *types.Reg // service). They will be derived from the client certificate otherwise. if !isProxy { if req.BotInstanceID != "" { - log.WithFields(logrus.Fields{ - "bot_instance_id": req.BotInstanceID, - }).Warnf("Untrusted client attempted to provide a bot instance ID, this will be ignored") + a.authServer.logger.WarnContext(ctx, "Untrusted client attempted to provide a bot instance ID, this will be ignored", + "bot_instance_id", req.BotInstanceID, + ) req.BotInstanceID = "" } if req.BotGeneration > 0 { - log.WithFields(logrus.Fields{ - "bot_generation": req.BotGeneration, - }).Warnf("Untrusted client attempted to provide a bot generation, this will be ignored") + a.authServer.logger.WarnContext(ctx, "Untrusted client attempted to provide a bot generation, this will be ignored", + "bot_generation", req.BotGeneration, + ) req.BotGeneration = 0 } @@ -700,7 +687,11 @@ func (a *ServerWithRoles) checkAdditionalSystemRoles(ctx context.Context, req *p if err != nil { // include this error in the logs, since it might be indicative of a bug if it occurs outside of the context // of a general backend outage. - log.Warnf("Failed to load system role assertion set %q for instance %q: %v", req.SystemRoleAssertionID, req.HostID, err) + a.authServer.logger.WarnContext(ctx, "Failed to load system role assertion set for instance", + "system_role_assertion", req.SystemRoleAssertionID, + "instance", req.HostID, + "error", err, + ) return trace.AccessDenied("failed to load system role assertion set with ID %q", req.SystemRoleAssertionID) } } @@ -789,7 +780,10 @@ func (a *ServerWithRoles) RegisterInventoryControlStream(ics client.UpstreamInve var filteredServices []types.SystemRole for _, service := range hello.Services { if !a.hasBuiltinRole(service) { - log.Warnf("Omitting service %q for control stream of instance %q (unknown or unauthorized).", service, role.GetServerID()) + a.authServer.logger.WarnContext(a.CloseContext(), "Omitting unknown or unauthorized service for instance control stream", + "omitted_service", service, + "instance", role.GetServerID(), + ) continue } filteredServices = append(filteredServices, service) @@ -861,7 +855,7 @@ func (a *ServerWithRoles) GetClusterAlerts(ctx context.Context, query types.GetC if err != nil { // we don't fail here since users are allowed to see acknowledged alerts, acks // are intended only as a tool for reducing noise. - log.Warnf("Failed to load alert acks: %v", err) + a.authServer.logger.WarnContext(ctx, "Failed to load alert acks", "error", err) } } @@ -1261,12 +1255,11 @@ func (c *resourceAccess) checkAccess(resource types.ResourceWithLabels, filter s // Filter first and only check RBAC if there is a match to improve perf. match, err := services.MatchResourceByFilters(resource, filter, nil) if err != nil { - log.WithFields(logrus.Fields{ - "resource_name": resource.GetName(), - "resource_kind": resourceKind, - "error": err, - }). - Warn("Unable to determine access to resource, matching with filter failed") + logger.WarnContext(context.Background(), "Unable to determine access to resource, matching with filter failed", + "resource_name", resource.GetName(), + "resource_kind", resourceKind, + "error", err, + ) return false, nil } @@ -1464,14 +1457,20 @@ func (a *ServerWithRoles) ListUnifiedResources(ctx context.Context, req *proto.L if n := r.GetNode(); n != nil { logins, err := checker.GetAllowedLoginsForResource(n) if err != nil { - log.WithError(err).WithField("resource", n.GetName()).Warn("Unable to determine logins for node") + a.authServer.logger.WarnContext(ctx, "Unable to determine logins for node", + "error", err, + "resource", n.GetName(), + ) continue } r.Logins = logins } else if d := r.GetWindowsDesktop(); d != nil { logins, err := checker.GetAllowedLoginsForResource(d) if err != nil { - log.WithError(err).WithField("resource", d.GetName()).Warn("Unable to determine logins for desktop") + a.authServer.logger.WarnContext(ctx, "Unable to determine logins for desktop", + "error", err, + "resource", d.GetName(), + ) continue } r.Logins = logins @@ -1482,13 +1481,19 @@ func (a *ServerWithRoles) ListUnifiedResources(ctx context.Context, req *proto.L // no access and treat the whole app as requiring an access request if _any_ of the contained // permission sets require one. if err := a.filterICPermissionSets(r, d.GetApp(), resourceAccess); err != nil { - log.WithError(err).WithField("resource", d.GetApp().GetName()).Warn("Unable to filter ") + a.authServer.logger.WarnContext(ctx, "Unable to filter", + "error", err, + "resource", d.GetApp().GetName(), + ) continue } logins, err := checker.GetAllowedLoginsForResource(d.GetApp()) if err != nil { - log.WithError(err).WithField("resource", d.GetApp().GetName()).Warn("Unable to determine logins for app") + a.authServer.logger.WarnContext(ctx, "Unable to determine logins for app", + "error", err, + "resource", d.GetApp().GetName(), + ) continue } r.Logins = logins @@ -1581,13 +1586,14 @@ func (a *ServerWithRoles) GetNodes(ctx context.Context, namespace string) ([]typ } elapsedFilter := time.Since(startFilter) - log.WithFields(logrus.Fields{ - "user": a.context.User.GetName(), - "elapsed_fetch": elapsedFetch, - "elapsed_filter": elapsedFilter, - }).Debugf( - "GetServers(%v->%v) in %v.", - len(nodes), len(filteredNodes), elapsedFetch+elapsedFilter) + a.authServer.logger.DebugContext(ctx, "Retrieved servers", + "node_count", len(nodes), + "filtered_node_count", len(filteredNodes), + "user", a.context.User.GetName(), + "elapsed_fetch", elapsedFetch, + "elapsed_filter", elapsedFilter, + "elapsed_total", elapsedFetch+elapsedFilter, + ) return filteredNodes, nil } @@ -1665,7 +1671,9 @@ func (a *ServerWithRoles) GetSSHTargets(ctx context.Context, req *proto.GetSSHTa for _, rsc := range lrsp.Resources { srv := rsc.GetNode() if srv == nil { - log.Warnf("Unexpected resource type %T, expected *types.ServerV2 (skipping)", rsc) + a.authServer.logger.WarnContext(ctx, "Skipping unexpected resource type, expected *types.ServerV2", + "resource_type", logutils.TypeAttr(rsc), + ) continue } @@ -1732,7 +1740,9 @@ func (a *ServerWithRoles) ResolveSSHTarget(ctx context.Context, req *proto.Resol for _, rsc := range lrsp.Resources { srv := rsc.GetNode() if srv == nil { - log.Warnf("Unexpected resource type %T, expected *types.ServerV2 (skipping)", rsc) + a.authServer.logger.WarnContext(ctx, "Skipping unexpected resource type, expected *types.ServerV2", + "resource_type", logutils.TypeAttr(rsc), + ) continue } @@ -1913,7 +1923,10 @@ func (a *ServerWithRoles) ListResources(ctx context.Context, req proto.ListResou logins, err := resourceChecker.GetAllowedLoginsForResource(checkableResource) if err != nil { - log.WithError(err).WithField("resource", resource.GetName()).Warn("Unable to determine logins for resource") + a.authServer.logger.WarnContext(ctx, "Unable to determine logins for resource", + "error", err, + "resource", resource.GetName(), + ) } resource = &types.EnrichedResource{ @@ -2195,7 +2208,10 @@ func (a *ServerWithRoles) listResourcesWithSort(ctx context.Context, req proto.L logins, err := resourceChecker.GetAllowedLoginsForResource(checkableResource) if err != nil { - log.WithError(err).WithField("resource", r.GetName()).Warn("Unable to determine logins for resource") + a.authServer.logger.WarnContext(ctx, "Unable to determine logins for resource", + "error", err, + "resource", r.GetName(), + ) } return &types.EnrichedResource{ResourceWithLabels: r, Logins: logins}, nil @@ -2395,7 +2411,7 @@ func emitTokenEvent(ctx context.Context, e apievents.Emitter, token types.Provis Roles: token.GetRoles(), JoinMethod: token.GetJoinMethod(), }); err != nil { - log.WithError(err).Warn("Failed to emit join token create event.") + logger.WarnContext(ctx, "Failed to emit join token create event", "error", err) } } @@ -2708,7 +2724,11 @@ func (a *ServerWithRoles) ListAccessRequests(ctx context.Context, req *proto.Lis canReview, err := checker.CanReviewRequest(accessRequest) if err != nil { - log.Warnf("Failed to evaluate review permissions for user %q against request %q: %v", a.context.User.GetName(), accessRequest.GetName(), err) + a.authServer.logger.WarnContext(ctx, "Failed to evaluate review permissions for user against access request", + "user", a.context.User.GetName(), + "access_request", accessRequest.GetName(), + "error", err, + ) return false } @@ -2736,7 +2756,10 @@ func (a *ServerWithRoles) CreateAccessRequestV2(ctx context.Context, req types.A return nil, trace.AccessDenied("cannot create access requests in non-pending state while using an access request") } - log.Warnf("Use of resource-level access request 'create' permission by user %q to create non-pending access request for user %q. Creation of non-pending requests will be deprecated in future version of teleport. Consider migrating to a workflow with a separate approval step.", a.context.User.GetName(), req.GetUser()) + a.authServer.logger.WarnContext(ctx, "Use of resource-level access request 'create' permission by user to create non-pending access request for user. Creation of non-pending requests will be deprecated in future version of teleport. Consider migrating to a workflow with a separate approval step.", + "user", a.context.User.GetName(), + "request_user", req.GetUser(), + ) } if !authz.IsCurrentUser(a.context, req.GetUser()) { @@ -2939,22 +2962,25 @@ func (a *ServerWithRoles) GetCurrentUserRoles(ctx context.Context) ([]types.Role func (a *ServerWithRoles) desiredAccessInfo(ctx context.Context, req *proto.UserCertsRequest, user types.User) (*services.AccessInfo, error) { if req.Username != a.context.User.GetName() { if isRoleImpersonation(*req) { - err := trace.AccessDenied("User %v tried to issue a cert for %v and added role requests. This is not supported.", a.context.User.GetName(), req.Username) - log.WithError(err).Warn() - return nil, err + a.authServer.logger.WarnContext(ctx, "User tried to issue a cert for another user wjile adding role requests", + "user", a.context.User.GetName(), + "requested_user", req.Username, + ) + return nil, trace.AccessDenied("User %v tried to issue a cert for %v and added role requests. This is not supported.", a.context.User.GetName(), req.Username) } if len(req.AccessRequests) > 0 { - err := trace.AccessDenied("User %v tried to issue a cert for %v and added access requests. This is not supported.", a.context.User.GetName(), req.Username) - log.WithError(err).Warn() - return nil, err + a.authServer.logger.WarnContext(ctx, "User tried to issue a cert for another user wihile adding access requests", + "user", a.context.User.GetName(), + "requested_user", req.Username, + ) + return nil, trace.AccessDenied("User %v tried to issue a cert for %v and added access requests. This is not supported.", a.context.User.GetName(), req.Username) } return a.desiredAccessInfoForImpersonation(user) } if isRoleImpersonation(*req) { if len(req.AccessRequests) > 0 { - err := trace.AccessDenied("User %v tried to issue a cert with both role and access requests. This is not supported.", a.context.User.GetName()) - log.WithError(err).Warn() - return nil, err + a.authServer.logger.WarnContext(ctx, "User tried to issue a cert with both role and access requests", "user", a.context.User.GetName()) + return nil, trace.AccessDenied("User %v tried to issue a cert with both role and access requests. This is not supported.", a.context.User.GetName()) } return a.desiredAccessInfoForRoleRequest(req, user.GetTraits()) } @@ -3169,13 +3195,19 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC // permissions to read user data. user, err := a.authServer.GetUser(ctx, req.Username, false) if err != nil { - log.WithError(err).Debugf("Could not impersonate user %v. The user could not be fetched from local store.", req.Username) + a.authServer.logger.DebugContext(ctx, "Could not impersonate user, the user could not be fetched from local store", + "error", err, + "user", req.Username, + ) return nil, trace.AccessDenied("access denied") } // Do not allow SSO users to be impersonated. if req.Username != a.context.User.GetName() && user.GetUserType() == types.UserTypeSSO { - log.Warningf("User %v tried to issue a cert for externally managed user %v, this is not supported.", a.context.User.GetName(), req.Username) + a.authServer.logger.WarnContext(ctx, "User tried to issue a cert for externally managed user", + "user", a.context.User.GetName(), + "external_user", req.Username, + ) return nil, trace.AccessDenied("access denied") } @@ -3185,7 +3217,9 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC identity := a.context.Identity.GetIdentity() sessionExpires := identity.Expires if sessionExpires.IsZero() { - log.Warningf("Encountered identity with no expiry: %v and denied request. Must be internal logic error.", a.context.Identity) + a.authServer.logger.WarnContext(ctx, "Denied cert issuance for identity with no expiry", + "identity", identity.Username, + ) return nil, trace.AccessDenied("access denied") } if req.Expires.Before(a.authServer.GetClock().Now()) { @@ -3273,7 +3307,10 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC // certificates with alternate RoleRequests. err = a.context.Checker.CheckImpersonateRoles(a.context.User, parsedRoles) if err != nil { - log.Warning(err) + a.authServer.logger.WarnContext(ctx, "user request for role impersonation denied", + "user", a.context.User.GetName(), + "error", err, + ) err := trace.AccessDenied("user %q has requested role impersonation for %q", a.context.User.GetName(), accessInfo.Roles) if err := a.authServer.emitter.EmitAuditEvent(a.CloseContext(), &apievents.UserLogin{ Metadata: apievents.Metadata{ @@ -3287,7 +3324,7 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC UserMessage: err.Error(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit local login failure event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit local login failure event", "error", err) } return nil, trace.Wrap(err) } @@ -3300,7 +3337,10 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC ttl = checker.AdjustSessionTTL(ttl) req.Expires = a.authServer.GetClock().Now().Add(ttl) if err != nil { - log.Warning(err) + a.authServer.logger.WarnContext(ctx, "user request for user impersonation denied", + "user", a.context.User.GetName(), + "error", err, + ) err := trace.AccessDenied("user %q has requested to generate certs for %q.", a.context.User.GetName(), accessInfo.Roles) if err := a.authServer.emitter.EmitAuditEvent(a.CloseContext(), &apievents.UserLogin{ Metadata: apievents.Metadata{ @@ -3314,7 +3354,7 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC UserMessage: err.Error(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit local login failure event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit local login failure event", "error", err) } return nil, trace.Wrap(err) } @@ -3550,7 +3590,7 @@ func (a *ServerWithRoles) ChangeUserAuthentication(ctx context.Context, req *pro isPasswordless := req.NewMFARegisterResponse != nil && len(req.NewPassword) == 0 if isPasswordless && modules.GetModules().Features().Cloud { if err := a.trySettingConnectorNameToPasswordless(ctx); err != nil { - log.WithError(err).Error("Failed to set passwordless as connector name.") + a.authServer.logger.ErrorContext(ctx, "Failed to set passwordless as connector name", "error", err) } } @@ -4179,9 +4219,13 @@ func (a *ServerWithRoles) EmitAuditEvent(ctx context.Context, event apievents.Au if err != nil { // TODO: this should be a proper audit event // notifying about access violation - log.Warningf("Rejecting audit event %v(%q) from %q: %v. The client is attempting to "+ - "submit events for an identity other than the one on its x509 certificate.", - event.GetType(), event.GetID(), role.GetServerID(), err) + const msg = "Rejecting audit event, the client is attempting to " + + "submit events for an identity other than the one on its x509 certificate." + a.authServer.logger.WarnContext(ctx, msg, + "event_type", event.GetType(), + "event_id", event.GetID(), + "server_id", role.GetServerID(), + "error", err) // this message is sparse on purpose to avoid conveying extra data to an attacker return trace.AccessDenied("failed to validate event metadata") } @@ -4263,9 +4307,14 @@ func (s *streamWithRoles) RecordEvent(ctx context.Context, pe apievents.Prepared if err != nil { // TODO: this should be a proper audit event // notifying about access violation - log.Warningf("Rejecting audit event %v from %v: %v. A node is attempting to "+ - "submit events for an identity other than the one on its x509 certificate.", - event.GetID(), s.serverID, err) + const msg = "Rejecting audit event, a node is attempting to " + + "submit events for an identity other than the one on its x509 certificate." + s.a.authServer.logger.WarnContext(ctx, msg, + "event_id", event.GetID(), + "event_type", event.GetType(), + "server_id", s.serverID, + "error", err, + ) // this message is sparse on purpose to avoid conveying extra data to an attacker return trace.AccessDenied("failed to validate event metadata") } @@ -4751,7 +4800,7 @@ func (a *ServerWithRoles) SetAuthPreference(ctx context.Context, newAuthPref typ }, AdminActionsMFA: clusterconfigv1.GetAdminActionsMFAStatus(storedAuthPref, newAuthPref), }); auditErr != nil { - log.WithError(auditErr).Warn("Failed to emit auth preference update event event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit auth preference update event event", "error", auditErr) } return trace.Wrap(err) @@ -4801,7 +4850,7 @@ func (a *ServerWithRoles) ResetAuthPreference(ctx context.Context) error { }, AdminActionsMFA: clusterconfigv1.GetAdminActionsMFAStatus(storedAuthPref, defaultAuthPref), }); auditErr != nil { - log.WithError(auditErr).Warn("Failed to emit auth preference update event event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit auth preference update event event", "error", auditErr) } return trace.Wrap(err) @@ -4885,7 +4934,7 @@ func (a *ServerWithRoles) SetClusterNetworkingConfig(ctx context.Context, newNet UserMessage: msg, }, }); auditErr != nil { - log.WithError(auditErr).Warn("Failed to emit cluster networking config update event event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit cluster networking config update event event", "error", auditErr) } return trace.Wrap(err) } @@ -4937,7 +4986,7 @@ func (a *ServerWithRoles) ResetClusterNetworkingConfig(ctx context.Context) erro UserMessage: msg, }, }); auditErr != nil { - log.WithError(auditErr).Warn("Failed to emit cluster networking config update event event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit cluster networking config update event event", "error", auditErr) } return trace.Wrap(err) @@ -4990,7 +5039,7 @@ func (a *ServerWithRoles) SetSessionRecordingConfig(ctx context.Context, newRecC UserMessage: msg, }, }); auditErr != nil { - log.WithError(auditErr).Warn("Failed to emit session recording config update event event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit session recording config update event event", "error", auditErr) } return trace.Wrap(err) @@ -5035,7 +5084,7 @@ func (a *ServerWithRoles) ResetSessionRecordingConfig(ctx context.Context) error UserMessage: msg, }, }); auditErr != nil { - log.WithError(auditErr).Warn("Failed to emit session recording config update event event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit session recording config update event event", "error", auditErr) } return trace.Wrap(err) @@ -5247,12 +5296,10 @@ func (a *ServerWithRoles) ProcessKubeCSR(req authclient.KubeCSR) (*authclient.Ku if proxyClusterName != "" && proxyClusterName != clusterName.GetClusterName() && proxyClusterName != identityClusterName { - log.WithFields( - logrus.Fields{ - "proxy_cluster_name": proxyClusterName, - "identity_cluster_name": identityClusterName, - }, - ).Warn("KubeCSR request denied because the proxy and identity clusters didn't match") + a.authServer.logger.WarnContext(a.CloseContext(), "KubeCSR request denied because the proxy and identity clusters didn't match", + "proxy_cluster_name", proxyClusterName, + "identity_cluster_name", identityClusterName, + ) return nil, trace.AccessDenied("can not sign certs for users via a different cluster proxy") } return a.authServer.ProcessKubeCSR(req) @@ -5396,8 +5443,12 @@ func (a *ServerWithRoles) checkAccessToGenerateDatabaseCert(resourceKind string) // user that is allowed to impersonate database service. if !a.hasBuiltinRole(types.RoleDatabase, types.RoleAdmin) { if err := a.canImpersonateBuiltinRole(types.RoleDatabase); err != nil { - log.WithError(err).Warnf("User %v tried to generate database certificate but does not have '%s' permission for '%s' kind, nor is allowed to impersonate %q system role", - a.context.User.GetName(), verb, resourceKind, types.RoleDatabase) + a.authServer.logger.WarnContext(a.CloseContext(), "User tried to generate database certificate but does not have permissions, nor is allowed to impersonate database system role", + "user", a.context.User.GetName(), + "verb", verb, + "resource_kind", resourceKind, + "error", err, + ) return trace.AccessDenied("access denied. User must have '%s' permission for '%s' kind to generate the certificate ", verb, resourceKind) } @@ -5412,8 +5463,10 @@ func (a *ServerWithRoles) GenerateSnowflakeJWT(ctx context.Context, req *proto.S // user that is allowed to impersonate database service. if !a.hasBuiltinRole(types.RoleDatabase, types.RoleAdmin) { if err := a.canImpersonateBuiltinRole(types.RoleDatabase); err != nil { - log.WithError(err).Warnf("User %v tried to generate database certificate but is not allowed to impersonate %q system role.", - a.context.User.GetName(), types.RoleDatabase) + a.authServer.logger.WarnContext(ctx, "User tried to generate database certificate but is not allowed to impersonate Database system role.", + "user", a.context.User.GetName(), + "error", err, + ) return nil, trace.AccessDenied(`access denied. The user must be able to impersonate the builtin role and user "Db" in order to generate database certificates, for more info see https://goteleport.com/docs/database-access/reference/cli/#tctl-auth-sign.`) } } @@ -6030,7 +6083,7 @@ func (a *ServerWithRoles) SearchSessionEvents(ctx context.Context, req events.Se return nil, "", trace.BadParameter("cond is an internal parameter, should not be set by client") } - cond, err := a.actionForListWithCondition(types.KindSession, services.SessionIdentifier) + cond, err := a.actionForListWithCondition(ctx, types.KindSession, services.SessionIdentifier) if err != nil { return nil, "", trace.Wrap(err) } @@ -6121,29 +6174,25 @@ func (a *ServerWithRoles) ReplaceRemoteLocks(ctx context.Context, clusterName st // channel if one is encountered. Otherwise the event channel is closed when the stream ends. // The event channel is not closed on error to prevent race conditions in downstream select statements. func (a *ServerWithRoles) StreamSessionEvents(ctx context.Context, sessionID session.ID, startIndex int64) (chan apievents.AuditEvent, chan error) { - createErrorChannel := func(err error) (chan apievents.AuditEvent, chan error) { - e := make(chan error, 1) - e <- trace.Wrap(err) - return nil, e - } - err := a.localServerAction() isTeleportServer := err == nil - var sessionType types.SessionKind - if !isTeleportServer { - var err error - sessionType, err = a.actionForKindSession(ctx, sessionID) - if err != nil { - c, e := make(chan apievents.AuditEvent), make(chan error, 1) - e <- trace.Wrap(err) - return c, e - } + // StreamSessionEvents can be called internally, and when that + // happens we don't want to emit an event or check for permissions. + if isTeleportServer { + return a.alog.StreamSessionEvents(ctx, sessionID, startIndex) } - // StreamSessionEvents can be called internally, and when that happens we don't want to emit an event. - shouldEmitAuditEvent := !isTeleportServer - if shouldEmitAuditEvent { + if err := a.actionForKindSession(ctx, sessionID); err != nil { + c, e := make(chan apievents.AuditEvent), make(chan error, 1) + e <- trace.Wrap(err) + return c, e + } + + // We can only determine the session type after the streaming started. For + // this reason, we delay the emit audit event until the first event or if + // the streaming returns an error. + cb := func(evt apievents.AuditEvent, _ error) { if err := a.authServer.emitter.EmitAuditEvent(a.authServer.closeCtx, &apievents.SessionRecordingAccess{ Metadata: apievents.Metadata{ Type: events.SessionRecordingAccessEvent, @@ -6151,14 +6200,34 @@ func (a *ServerWithRoles) StreamSessionEvents(ctx context.Context, sessionID ses }, SessionID: sessionID.String(), UserMetadata: a.context.Identity.GetIdentity().GetUserMetadata(), - SessionType: string(sessionType), + SessionType: string(sessionTypeFromStartEvent(evt)), Format: metadata.SessionRecordingFormatFromContext(ctx), }); err != nil { - return createErrorChannel(err) + a.authServer.logger.ErrorContext(ctx, "Failed to emit stream session event audit event", "error", err) } } - return a.alog.StreamSessionEvents(ctx, sessionID, startIndex) + return a.alog.StreamSessionEvents(events.ContextWithSessionStartCallback(ctx, cb), sessionID, startIndex) +} + +// sessionTypeFromStartEvent determines the session type given the session start +// event. +func sessionTypeFromStartEvent(sessionStart apievents.AuditEvent) types.SessionKind { + switch e := sessionStart.(type) { + case *apievents.SessionStart: + if e.KubernetesCluster != "" { + return types.KubernetesSessionKind + } + return types.SSHSessionKind + case *apievents.DatabaseSessionStart: + return types.DatabaseSessionKind + case *apievents.AppSessionStart: + return types.AppSessionKind + case *apievents.WindowsDesktopSessionStart: + return types.WindowsDesktopSessionKind + default: + return types.UnknownSessionKind + } } // CreateApp creates a new application resource. @@ -7055,7 +7124,7 @@ func (a *ServerWithRoles) CreateSAMLIdPServiceProvider(ctx context.Context, sp t AttributeMapping: typesAttrMapToEventAttrMap(sp.GetAttributeMapping()), }, }); emitErr != nil { - log.WithError(trace.NewAggregate(emitErr, err)).Warn("Failed to emit SAML IdP service provider created event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit SAML IdP service provider created event", "error", emitErr) } }() @@ -7108,7 +7177,7 @@ func (a *ServerWithRoles) UpdateSAMLIdPServiceProvider(ctx context.Context, sp t AttributeMapping: typesAttrMapToEventAttrMap(sp.GetAttributeMapping()), }, }); emitErr != nil { - log.WithError(trace.NewAggregate(emitErr, err)).Warn("Failed to emit SAML IdP service provider updated event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit SAML IdP service provider updated event", "error", emitErr) } }() @@ -7174,7 +7243,7 @@ func (a *ServerWithRoles) DeleteSAMLIdPServiceProvider(ctx context.Context, name ServiceProviderEntityID: entityID, }, }); emitErr != nil { - log.WithError(trace.NewAggregate(emitErr, err)).Warn("Failed to emit SAML IdP service provider deleted event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit SAML IdP service provider deleted event", "error", emitErr) } }() @@ -7222,7 +7291,7 @@ func (a *ServerWithRoles) DeleteAllSAMLIdPServiceProviders(ctx context.Context) UpdatedBy: authz.ClientUsername(ctx), }, }); emitErr != nil { - log.WithError(trace.NewAggregate(emitErr, err)).Warn("Failed to emit SAML IdP service provider deleted all event.") + a.authServer.logger.WarnContext(ctx, "Failed to emit SAML IdP service provider deleted all event", "error", emitErr) } }() @@ -7645,7 +7714,7 @@ func emitHeadlessLoginEvent(ctx context.Context, code string, emitter apievents. } if emitErr := emitter.EmitAuditEvent(ctx, &event); emitErr != nil { - log.WithError(err).Warnf("Failed to emit %q login event, code %q: %v", events.LoginMethodHeadless, code, emitErr) + logger.WarnContext(ctx, "Failed to emit headless login event", "event_code", code, "error", emitErr) } } @@ -7669,7 +7738,11 @@ func emitSSOLoginFailureEvent(ctx context.Context, emitter apievents.Emitter, me }) if emitErr != nil { - log.WithError(err).Warnf("Failed to emit %v login failure event: %v", method, emitErr) + logger.WarnContext(ctx, "Failed to emit sso login event", + "sso_method", method, + "event_code", code, + "error", emitErr, + ) } } diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index 1576986648191..923e74f46f4e7 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -24,7 +24,6 @@ import ( "crypto/tls" "crypto/x509/pkix" "fmt" - "io" "net/url" "slices" "strconv" @@ -38,7 +37,6 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/pquerna/otp/totp" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" @@ -77,7 +75,6 @@ import ( "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/srv/discovery/common" "github.com/gravitational/teleport/lib/tlsca" - logutils "github.com/gravitational/teleport/lib/utils/log" "github.com/gravitational/teleport/lib/utils/pagination" ) @@ -1824,12 +1821,6 @@ func BenchmarkListNodes(b *testing.B) { const nodeCount = 50_000 const roleCount = 32 - logger := logrus.StandardLogger() - logger.ReplaceHooks(make(logrus.LevelHooks)) - logrus.SetFormatter(logutils.NewTestJSONFormatter()) - logger.SetLevel(logrus.DebugLevel) - logger.SetOutput(io.Discard) - ctx := context.Background() srv := newTestTLSServer(b) @@ -2267,7 +2258,29 @@ func TestStreamSessionEvents(t *testing.T) { func TestStreamSessionEvents_SessionType(t *testing.T) { t.Parallel() - srv := newTestTLSServer(t) + authServerConfig := TestAuthServerConfig{ + Dir: t.TempDir(), + Clock: clockwork.NewFakeClockAt(time.Now().Round(time.Second).UTC()), + } + require.NoError(t, authServerConfig.CheckAndSetDefaults()) + + uploader := eventstest.NewMemoryUploader() + localLog, err := events.NewAuditLog(events.AuditLogConfig{ + DataDir: authServerConfig.Dir, + ServerID: authServerConfig.ClusterName, + Clock: authServerConfig.Clock, + UploadHandler: uploader, + }) + require.NoError(t, err) + authServerConfig.AuditLog = localLog + + as, err := NewTestAuthServer(authServerConfig) + require.NoError(t, err) + + srv, err := as.NewTestTLSServer() + require.NoError(t, err) + t.Cleanup(func() { srv.Close() }) + ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -2278,22 +2291,29 @@ func TestStreamSessionEvents_SessionType(t *testing.T) { identity := TestUser(user.GetName()) clt, err := srv.NewClient(identity) require.NoError(t, err) - sessionID := "44c6cea8-362f-11ea-83aa-125400432324" + sessionID := session.NewID() - // Emitting a session end event will cause the listing to correctly locate - // the recording (even if there might not be a recording file to stream). - require.NoError(t, srv.Auth().EmitAuditEvent(ctx, &apievents.DatabaseSessionEnd{ + streamer, err := events.NewProtoStreamer(events.ProtoStreamerConfig{ + Uploader: uploader, + }) + require.NoError(t, err) + stream, err := streamer.CreateAuditStream(ctx, sessionID) + require.NoError(t, err) + // The event is not required to pass through the auth server, we only need + // the upload to be present. + require.NoError(t, stream.RecordEvent(ctx, eventstest.PrepareEvent(&apievents.DatabaseSessionStart{ Metadata: apievents.Metadata{ - Type: events.DatabaseSessionEndEvent, - Code: events.DatabaseSessionEndCode, + Type: events.DatabaseSessionStartEvent, + Code: events.DatabaseSessionStartCode, }, SessionMetadata: apievents.SessionMetadata{ - SessionID: sessionID, + SessionID: sessionID.String(), }, - })) + }))) + require.NoError(t, stream.Complete(ctx)) accessedFormat := teleport.PTY - clt.StreamSessionEvents(metadata.WithSessionRecordingFormatContext(ctx, accessedFormat), session.ID(sessionID), 0) + clt.StreamSessionEvents(metadata.WithSessionRecordingFormatContext(ctx, accessedFormat), sessionID, 0) // Perform the listing an eventually loop to ensure the event is emitted. var searchEvents []apievents.AuditEvent @@ -6095,12 +6115,6 @@ func BenchmarkListUnifiedResourcesFilter(b *testing.B) { const nodeCount = 150_000 const roleCount = 32 - logger := logrus.StandardLogger() - logger.ReplaceHooks(make(logrus.LevelHooks)) - logrus.SetFormatter(logutils.NewTestJSONFormatter()) - logger.SetLevel(logrus.PanicLevel) - logger.SetOutput(io.Discard) - ctx := context.Background() srv := newTestTLSServer(b) @@ -6228,12 +6242,6 @@ func BenchmarkListUnifiedResources(b *testing.B) { const nodeCount = 150_000 const roleCount = 32 - logger := logrus.StandardLogger() - logger.ReplaceHooks(make(logrus.LevelHooks)) - logrus.SetFormatter(logutils.NewTestJSONFormatter()) - logger.SetLevel(logrus.DebugLevel) - logger.SetOutput(io.Discard) - ctx := context.Background() srv := newTestTLSServer(b) diff --git a/lib/auth/authclient/api.go b/lib/auth/authclient/api.go index 409e4850e8a97..742ea527ab248 100644 --- a/lib/auth/authclient/api.go +++ b/lib/auth/authclient/api.go @@ -817,6 +817,18 @@ type DiscoveryAccessPoint interface { UpsertUserTask(ctx context.Context, req *usertasksv1.UserTask) (*usertasksv1.UserTask, error) } +// ExpiryAccessPoint is the API used by the expiry service. +type ExpiryAccessPoint interface { + // Semaphores provides semaphore operations + types.Semaphores + + // ListAccessRequests is an access request getter with pagination and sorting options. + ListAccessRequests(ctx context.Context, req *proto.ListAccessRequestsRequest) (*proto.ListAccessRequestsResponse, error) + + // DeleteAccessRequest deletes an access request. + DeleteAccessRequest(ctx context.Context, reqID string) error +} + // ReadOktaAccessPoint is a read only API interface to be // used by an Okta component. // diff --git a/lib/auth/bot.go b/lib/auth/bot.go index d2ce2518abb50..104518ea7687e 100644 --- a/lib/auth/bot.go +++ b/lib/auth/bot.go @@ -26,7 +26,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/timestamppb" "github.com/gravitational/teleport/api/client/proto" @@ -42,6 +41,7 @@ import ( "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/sshutils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // legacyValidateGenerationLabel validates and updates a generation label. @@ -121,7 +121,10 @@ func (a *Server) legacyValidateGenerationLabel(ctx context.Context, username str // The current generations must match to continue: if currentIdentityGeneration != currentUserGeneration { if err := a.tryLockBotDueToGenerationMismatch(ctx, user.GetName()); err != nil { - log.WithError(err).Warnf("Failed to lock bot %q when a generation mismatch was detected", user.GetName()) + a.logger.WarnContext(ctx, "Failed to lock bot when a generation mismatch was detected", + "error", err, + "bot", user.GetName(), + ) } return trace.AccessDenied( @@ -248,7 +251,7 @@ func (a *Server) tryLockBotDueToGenerationMismatch(ctx context.Context, username }, UserMetadata: userMetadata, }); err != nil { - log.WithError(err).Warn("Failed to emit renewable cert generation mismatch event") + a.logger.WarnContext(ctx, "Failed to emit renewable cert generation mismatch event", "error", err) } return nil @@ -348,11 +351,11 @@ func (a *Server) updateBotInstance( authRecord.Generation = 1 } - log.WithFields(logrus.Fields{ - "bot_name": botName, - "invalid_instance_id": botInstanceID, - "new_instance_id": instanceID.String(), - }).Info("bot has no valid instance ID, a new instance will be generated") + a.logger.InfoContext(ctx, "bot has no valid instance ID, a new instance will be generated", + "bot_name", botName, + "invalid_instance_id", botInstanceID, + "new_instance_id", logutils.StringerAttr(instanceID), + ) expires := a.GetClock().Now().Add(req.ttl + machineidv1.ExpiryMargin) @@ -371,14 +374,14 @@ func (a *Server) updateBotInstance( return nil } - l := log.WithFields(logrus.Fields{ - "bot_name": botName, - "bot_instance_id": botInstanceID, - }) + log := a.logger.With( + "bot_name", botName, + "bot_instance_id", botInstanceID, + ) if currentIdentityGeneration == 0 { // Nothing to do. - l.Warn("bot attempted to fetch certificates without providing a current identity generation, this is not allowed") + log.WarnContext(ctx, "bot attempted to fetch certificates without providing a current identity generation, this is not allowed") return trace.AccessDenied("a current identity generation must be provided") } else if currentIdentityGeneration > 0 && currentIdentityGeneration != instanceGeneration { @@ -386,7 +389,7 @@ func (a *Server) updateBotInstance( // renewable (i.e. token) identities. if req.renewable { if err := a.tryLockBotDueToGenerationMismatch(ctx, username); err != nil { - l.WithError(err).Warn("Failed to lock bot when a generation mismatch was detected") + log.WarnContext(ctx, "Failed to lock bot when a generation mismatch was detected", "error", err) } return trace.AccessDenied( @@ -398,12 +401,13 @@ func (a *Server) updateBotInstance( // We'll still log the check failure, but won't deny access. This // log data will help make an informed decision about reliability of // the generation counter for all join methods in the future. - l.WithFields(logrus.Fields{ - "bot_instance_generation": instanceGeneration, - "bot_identity_generation": currentIdentityGeneration, - "bot_join_method": authRecord.JoinMethod, - }).Warn("Bot generation counter mismatch detected. This check is not enforced for this join method, " + - "but may indicate multiple uses of a bot identity and possibly a compromised certificate.") + const msg = "Bot generation counter mismatch detected. This check is not enforced for this join method, " + + "but may indicate multiple uses of a bot identity and possibly a compromised certificate." + log.WarnContext(ctx, msg, + "bot_instance_generation", instanceGeneration, + "bot_identity_generation", currentIdentityGeneration, + "bot_join_method", authRecord.JoinMethod, + ) } } @@ -419,7 +423,7 @@ func (a *Server) updateBotInstance( // setting this for other methods will break compatibility. if req.renewable { if err := a.commitLegacyGenerationCounterToBotUser(ctx, username, uint64(newGeneration)); err != nil { - l.WithError(err).Warn("unable to commit legacy generation counter to bot user") + log.WarnContext(ctx, "unable to commit legacy generation counter to bot user", "error", err) } } @@ -442,7 +446,7 @@ func (a *Server) updateBotInstance( // An initial auth record should have been added during initial join, // but if not, add it now. if bi.Status.InitialAuthentication == nil { - l.Warn("bot instance is missing its initial authentication record, a new one will be added") + log.WarnContext(ctx, "bot instance is missing its initial authentication record, a new one will be added") bi.Status.InitialAuthentication = authRecord } @@ -500,13 +504,16 @@ func (a *Server) generateInitialBotCerts( // permissions to read user data. userState, err := a.GetUserOrLoginState(ctx, username) if err != nil { - log.WithError(err).Debugf("Could not impersonate user %v. The user could not be fetched from local store.", username) + a.logger.DebugContext(ctx, "Could not impersonate user - the user could not be fetched from local store", + "error", err, + "user", username, + ) return nil, "", trace.AccessDenied("access denied") } // Do not allow SSO users to be impersonated. if userState.GetUserType() == types.UserTypeSSO { - log.Warningf("Tried to issue a renewable cert for externally managed user %v, this is not supported.", username) + a.logger.WarnContext(ctx, "Tried to issue a renewable cert for externally managed user, this is not supported", "user", username) return nil, "", trace.AccessDenied("access denied") } diff --git a/lib/auth/db.go b/lib/auth/db.go index 33f2b1fb71d0c..ce7080b7ba3f6 100644 --- a/lib/auth/db.go +++ b/lib/auth/db.go @@ -216,7 +216,7 @@ func (a *Server) SignDatabaseCSR(ctx context.Context, req *proto.DatabaseCSRRequ "this Teleport cluster is not licensed for database access, please contact the cluster administrator") } - log.Debugf("Signing database CSR for cluster %v.", req.ClusterName) + a.logger.DebugContext(ctx, "Signing database CSR for cluster", "cluster", req.ClusterName) clusterName, err := a.GetClusterName() if err != nil { @@ -348,7 +348,7 @@ func (a *Server) GenerateSnowflakeJWT(ctx context.Context, req *proto.SnowflakeJ return nil, trace.Wrap(err) } - subject, issuer := getSnowflakeJWTParams(req.AccountName, req.UserName, pubKey) + subject, issuer := getSnowflakeJWTParams(ctx, req.AccountName, req.UserName, pubKey) _, signer, err := a.GetKeyStore().GetTLSCertAndSigner(ctx, ca) if err != nil { @@ -371,7 +371,7 @@ func (a *Server) GenerateSnowflakeJWT(ctx context.Context, req *proto.SnowflakeJ }, nil } -func getSnowflakeJWTParams(accountName, userName string, publicKey []byte) (string, string) { +func getSnowflakeJWTParams(ctx context.Context, accountName, userName string, publicKey []byte) (string, string) { // Use only the first part of the account name to generate JWT // Based on: // https://github.com/snowflakedb/snowflake-connector-python/blob/f2f7e6f35a162484328399c8a50a5015825a5573/src/snowflake/connector/auth_keypair.py#L83 @@ -383,7 +383,10 @@ func getSnowflakeJWTParams(accountName, userName string, publicKey []byte) (stri accnToken, _, _ := strings.Cut(accountName, accNameSeparator) accnTokenCap := strings.ToUpper(accnToken) userNameCap := strings.ToUpper(userName) - log.Debugf("Signing database JWT token for %s %s", accnTokenCap, userNameCap) + logger.DebugContext(ctx, "Signing database JWT token", + "account_name", accnTokenCap, + "user_name", userNameCap, + ) subject := fmt.Sprintf("%s.%s", accnTokenCap, userNameCap) diff --git a/lib/auth/db_test.go b/lib/auth/db_test.go index 2565f168a1905..2ce873a09eae2 100644 --- a/lib/auth/db_test.go +++ b/lib/auth/db_test.go @@ -90,7 +90,7 @@ func Test_getSnowflakeJWTParams(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - subject, issuer := getSnowflakeJWTParams(tt.args.accountName, tt.args.userName, tt.args.publicKey) + subject, issuer := getSnowflakeJWTParams(context.Background(), tt.args.accountName, tt.args.userName, tt.args.publicKey) require.Equal(t, tt.wantSubject, subject) require.Equal(t, tt.wantIssuer, issuer) diff --git a/lib/auth/github.go b/lib/auth/github.go index a447f9dca3594..a122352d99e5b 100644 --- a/lib/auth/github.go +++ b/lib/auth/github.go @@ -33,7 +33,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/oauth2" "github.com/gravitational/teleport" @@ -167,8 +166,7 @@ func (a *Server) CreateGithubAuthRequest(ctx context.Context, req types.GithubAu config := newGithubOAuth2Config(connector) req.RedirectURL = config.AuthCodeURL(req.StateToken) - log.WithFields(logrus.Fields{teleport.ComponentKey: "github"}).Debugf( - "Redirect URL: %v.", req.RedirectURL) + a.logger.DebugContext(ctx, "Creating github auth request", "redirect_url", req.RedirectURL) req.SetExpiry(a.GetClock().Now().UTC().Add(defaults.GithubAuthRequestTTL)) err = a.Services.CreateGithubAuthRequest(ctx, req) if err != nil { @@ -197,7 +195,7 @@ func (a *Server) upsertGithubConnector(ctx context.Context, connector types.Gith }, ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit GitHub connector create event.") + a.logger.WarnContext(ctx, "Failed to emit GitHub connector create event", "error", err) } return upserted, nil @@ -224,7 +222,7 @@ func (a *Server) createGithubConnector(ctx context.Context, connector types.Gith }, ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit GitHub connector create event.") + a.logger.WarnContext(ctx, "Failed to emit GitHub connector create event", "error", err) } return created, nil @@ -251,7 +249,7 @@ func (a *Server) updateGithubConnector(ctx context.Context, connector types.Gith }, ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit GitHub connector update event.") + a.logger.WarnContext(ctx, "Failed to emit GitHub connector update event", "error", err) } return updated, nil @@ -350,7 +348,7 @@ func orgUsesExternalSSO(ctx context.Context, endpointURL, org string, client htt if resp != nil { io.Copy(io.Discard, resp.Body) if bodyErr := resp.Body.Close(); bodyErr != nil { - logrus.WithError(bodyErr).Error("Error closing response body.") + logger.ErrorContext(ctx, "Error closing response body", "error", bodyErr) } } // Handle makeHTTPGetReq errors. @@ -407,7 +405,7 @@ func (a *Server) deleteGithubConnector(ctx context.Context, connectorName string }, ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit GitHub connector delete event.") + a.logger.WarnContext(ctx, "Failed to emit GitHub connector delete event", "error", err) } return nil @@ -433,10 +431,10 @@ type githubManager interface { // ValidateGithubAuthCallback validates Github auth callback redirect func (a *Server) ValidateGithubAuthCallback(ctx context.Context, q url.Values) (*authclient.GithubAuthResponse, error) { diagCtx := NewSSODiagContext(types.KindGithub, a) - return validateGithubAuthCallbackHelper(ctx, a, diagCtx, q, a.emitter) + return validateGithubAuthCallbackHelper(ctx, a, diagCtx, q, a.emitter, a.logger) } -func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, diagCtx *SSODiagContext, q url.Values, emitter apievents.Emitter) (*authclient.GithubAuthResponse, error) { +func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, diagCtx *SSODiagContext, q url.Values, emitter apievents.Emitter, logger *slog.Logger) (*authclient.GithubAuthResponse, error) { event := &apievents.UserLogin{ Metadata: apievents.Metadata{ Type: events.UserLoginEvent, @@ -456,7 +454,7 @@ func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, diag attributes, err := apievents.EncodeMapStrings(claims.OrganizationToTeams) if err != nil { event.Status.UserMessage = fmt.Sprintf("Failed to encode identity attributes: %v", err.Error()) - log.WithError(err).Debug("Failed to encode identity attributes.") + logger.DebugContext(ctx, "Failed to encode identity attributes", "error", err) } else { event.IdentityAttributes = attributes } @@ -472,7 +470,7 @@ func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, diag event.Status.UserMessage = err.Error() if err := emitter.EmitAuditEvent(ctx, event); err != nil { - log.WithError(err).Warn("Failed to emit GitHub login failed event.") + logger.WarnContext(ctx, "Failed to emit GitHub login failed event", "error", err) } return nil, trace.Wrap(err) } @@ -484,7 +482,7 @@ func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, diag event.User = auth.Username if err := emitter.EmitAuditEvent(ctx, event); err != nil { - log.WithError(err).Warn("Failed to emit GitHub login event.") + logger.WarnContext(ctx, "Failed to emit GitHub login event", "error", err) } return auth, nil @@ -602,6 +600,16 @@ func (a *Server) validateGithubAuthCallback(ctx context.Context, diagCtx *SSODia if err != nil { return nil, trace.Wrap(err, "Failed to query GitHub API for user claims.") } + + logger.DebugContext(ctx, "Retrieved GitHub claims", + slog.Group("claims", + slog.String("user_name", claims.Username), + slog.String("user_id", claims.UserID), + slog.Any("organization_to_teams", claims.Teams), + slog.Any("roles", claims.OrganizationToTeams), + ), + ) + diagCtx.Info.GithubClaims = claims // Calculate (figure out name, roles, traits, session TTL) of user and @@ -945,9 +953,12 @@ func (a *Server) calculateGithubUser(ctx context.Context, diagCtx *SSODiagContex } func (a *Server) createGithubUser(ctx context.Context, p *CreateUserParams, dryRun bool) (types.User, error) { - log.WithFields(logrus.Fields{teleport.ComponentKey: "github"}).Debugf( - "Generating dynamic GitHub identity %v/%v with roles: %v. Dry run: %v.", - p.ConnectorName, p.Username, p.Roles, dryRun) + a.logger.DebugContext(ctx, "Generating dynamic GitHub identity", + "connector_name", p.ConnectorName, + "user_name", p.Username, + "role", p.Roles, + "dry_run", dryRun, + ) expires := a.GetClock().Now().UTC().Add(p.SessionTTL) @@ -1125,15 +1136,12 @@ func populateGithubClaims(user *GithubUserResponse, teams []GithubTeamResponse) return nil, trace.AccessDenied( "list of user teams is empty, did you grant access?") } - claims := &types.GithubClaims{ + return &types.GithubClaims{ Username: user.Login, OrganizationToTeams: orgToTeams, Teams: teamList, UserID: user.getIDStr(), - } - log.WithFields(logrus.Fields{teleport.ComponentKey: "github"}).Debugf( - "Claims: %#v.", claims) - return claims, nil + }, nil } // githubAPIClient is a tiny wrapper around some of Github APIs @@ -1223,11 +1231,11 @@ func (c *githubAPIClient) getTeams(ctx context.Context) ([]GithubTeamResponse, e // of pages, print an error when it does happen, and return the results up // to that point. if count > MaxPages { - warningMessage := "Truncating list of teams used to populate claims: " + + const warningMessage = "Truncating list of teams used to populate claims: " + "hit maximum number pages that can be fetched from GitHub." // Print warning to Teleport logs as well as the Audit Log. - log.Warn(warningMessage) + c.authServer.logger.WarnContext(ctx, warningMessage) if err := c.authServer.emitter.EmitAuditEvent(c.authServer.closeCtx, &apievents.UserLogin{ Metadata: apievents.Metadata{ Type: events.UserLoginEvent, @@ -1240,7 +1248,7 @@ func (c *githubAPIClient) getTeams(ctx context.Context) ([]GithubTeamResponse, e }, ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit GitHub login failure event.") + c.authServer.logger.WarnContext(ctx, "Failed to emit GitHub login failure event", "error", err) } return result, nil } diff --git a/lib/auth/github_test.go b/lib/auth/github_test.go index 56d33fcf91711..862f3ce2d03c0 100644 --- a/lib/auth/github_test.go +++ b/lib/auth/github_test.go @@ -188,6 +188,7 @@ func TestValidateGithubAuthCallbackEventsEmitted(t *testing.T) { clientAddr := &net.TCPAddr{IP: net.IPv4(10, 255, 0, 0)} ctx := authz.ContextWithClientSrcAddr(context.Background(), clientAddr) tt := setupGithubContext(ctx, t) + logger := utils.NewSlogLoggerForTests() auth := &authclient.GithubAuthResponse{ Username: "test-name", @@ -220,7 +221,7 @@ func TestValidateGithubAuthCallbackEventsEmitted(t *testing.T) { diagCtx.Info.AppliedLoginRules = []string{"login-rule"} return auth, nil } - _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter) + _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter, logger) require.Equal(t, events.UserLoginEvent, tt.mockEmitter.LastEvent().GetType()) require.Equal(t, events.UserSSOLoginCode, tt.mockEmitter.LastEvent().GetCode()) loginEvt := tt.mockEmitter.LastEvent().(*apievents.UserLogin) @@ -235,7 +236,7 @@ func TestValidateGithubAuthCallbackEventsEmitted(t *testing.T) { diagCtx.Info.GithubClaims = claims return auth, trace.BadParameter("") } - _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter) + _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter, logger) require.Equal(t, events.UserLoginEvent, tt.mockEmitter.LastEvent().GetType()) require.Equal(t, events.UserSSOLoginFailureCode, tt.mockEmitter.LastEvent().GetCode()) loginEvt = tt.mockEmitter.LastEvent().(*apievents.UserLogin) @@ -248,7 +249,7 @@ func TestValidateGithubAuthCallbackEventsEmitted(t *testing.T) { diagCtx.Info.GithubClaims = claims return auth, nil } - _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter) + _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter, logger) require.Equal(t, events.UserLoginEvent, tt.mockEmitter.LastEvent().GetType()) require.Equal(t, events.UserSSOTestFlowLoginCode, tt.mockEmitter.LastEvent().GetCode()) loginEvt = tt.mockEmitter.LastEvent().(*apievents.UserLogin) @@ -262,7 +263,7 @@ func TestValidateGithubAuthCallbackEventsEmitted(t *testing.T) { diagCtx.Info.GithubClaims = claims return auth, trace.BadParameter("") } - _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter) + _, _ = validateGithubAuthCallbackHelper(ctx, m, diagCtx, nil, tt.a.emitter, logger) require.Equal(t, events.UserLoginEvent, tt.mockEmitter.LastEvent().GetType()) require.Equal(t, events.UserSSOTestFlowLoginFailureCode, tt.mockEmitter.LastEvent().GetCode()) loginEvt = tt.mockEmitter.LastEvent().(*apievents.UserLogin) diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 2699035c0b211..d5dfc1f553f2b 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net" "os" "strconv" @@ -34,7 +35,6 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/trace/trail" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" collectortracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -123,6 +123,7 @@ import ( "github.com/gravitational/teleport/lib/srv/server/installer" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) var ( @@ -179,7 +180,7 @@ var ( type GRPCServer struct { authpb.UnimplementedAuthServiceServer auditlogpb.UnimplementedAuditLogServiceServer - *logrus.Entry + logger *slog.Logger APIConfig server *grpc.Server @@ -256,11 +257,11 @@ func (g *GRPCServer) SendKeepAlives(stream authpb.AuthService_SendKeepAlivesServ } keepAlive, err := stream.Recv() if errors.Is(err, io.EOF) { - g.Logger.Debug("Connection closed.") + g.logger.DebugContext(stream.Context(), "Connection closed") return nil } if err != nil { - g.Logger.Debugf("Failed to receive heartbeat: %v", err) + g.logger.DebugContext(stream.Context(), "Failed to receive heartbeat", "error", err) return trace.Wrap(err) } err = auth.KeepAliveServer(stream.Context(), *keepAlive) @@ -268,7 +269,10 @@ func (g *GRPCServer) SendKeepAlives(stream authpb.AuthService_SendKeepAlivesServ return trace.Wrap(err) } if firstIteration { - g.Logger.Debugf("Got %s heartbeat connection from %v.", keepAlive.GetType(), auth.User.GetName()) + g.logger.DebugContext(stream.Context(), "Got heartbeat connection", + "heartbeat_type", keepAlive.GetType(), + "identity", auth.User.GetName(), + ) heartbeatConnectionsReceived.Inc() metric, ok := connectedResourceGauges[keepAlive.GetType()] @@ -276,7 +280,7 @@ func (g *GRPCServer) SendKeepAlives(stream authpb.AuthService_SendKeepAlivesServ metric.Inc() defer metric.Dec() } else { - g.Logger.Warnf("missing connected resources gauge for keep alive %s (this is a bug)", keepAlive.GetType()) + g.logger.WarnContext(stream.Context(), "missing connected resources gauge for keep alive (this is a bug)", "heartbeat_type", keepAlive.GetType()) } firstIteration = false @@ -308,7 +312,7 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre var eventStream apievents.Stream var sessionID session.ID - g.Debugf("CreateAuditStream connection from %v.", auth.User.GetName()) + g.logger.DebugContext(stream.Context(), "CreateAuditStream connection", "identity", auth.User.GetName()) streamStart := time.Now() processed := int64(0) counter := 0 @@ -319,7 +323,7 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre return case statusUpdate := <-eventStream.Status(): if err := stream.Send(&statusUpdate); err != nil { - g.WithError(err).Debugf("Failed to send status update.") + g.logger.DebugContext(stream.Context(), "Failed to send status update", "error", err) } } } @@ -328,10 +332,10 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre closeStream := func(eventStream apievents.Stream) { if err := eventStream.Close(auth.CloseContext()); err != nil { if auth.CloseContext().Err() == nil { - g.WithError(err).Warn("Failed to flush close the stream.") + g.logger.WarnContext(stream.Context(), "Failed to flush close the stream", "error", err) } } else { - g.Debugf("Flushed and closed the stream.") + g.logger.DebugContext(stream.Context(), "Flushed and closed the stream") } } @@ -342,7 +346,7 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre } if err != nil { if stream.Context().Err() == nil { - g.WithError(err).Debug("Failed to receive stream request.") + g.logger.DebugContext(stream.Context(), "Failed to receive stream request", "error", err) } return trace.Wrap(err) } @@ -355,11 +359,11 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre // Log the reason why audit stream creation failed. This will // surface things like AWS/GCP/MinIO credential/configuration // errors. - g.Errorf("Failed to create audit stream: %q.", err) + g.logger.ErrorContext(stream.Context(), "Failed to create audit stream", "error", err) return trace.Wrap(err) } sessionID = session.ID(create.SessionID) - g.Debugf("Created stream for session %v", sessionID) + g.logger.DebugContext(stream.Context(), "Created stream for session", "session_id", sessionID) go forwardEvents(eventStream) defer closeStream(eventStream) } else if resume := request.GetResumeStream(); resume != nil { @@ -370,7 +374,7 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre if err != nil { return trace.Wrap(err) } - g.Debugf("Resumed stream for session %v", resume.SessionID) + g.logger.DebugContext(stream.Context(), "Resumed stream for session", "session_id", resume.SessionID) go forwardEvents(eventStream) defer closeStream(eventStream) } else if complete := request.GetCompleteStream(); complete != nil { @@ -408,7 +412,7 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre return trace.Wrap(err) } } - g.Debugf("Completed stream for session %v", sessionID) + g.logger.DebugContext(stream.Context(), "Completed stream for session", "session_id", sessionID) return nil } else if flushAndClose := request.GetFlushAndCloseStream(); flushAndClose != nil { if eventStream == nil { @@ -422,7 +426,7 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre } event, err := apievents.FromOneOf(*oneof) if err != nil { - g.WithError(err).Debug("Failed to decode event.") + g.logger.DebugContext(stream.Context(), "Failed to decode event", "error", err) return trace.Wrap(err) } // Currently only api/client.auditStreamer calls with an event @@ -451,8 +455,15 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre if err != nil { switch { case events.IsPermanentEmitError(err): - g.WithError(err).WithField("event", event). - Error("Failed to EmitAuditEvent due to a permanent error. Event wil be omitted.") + g.logger.ErrorContext(stream.Context(), "Failed to EmitAuditEvent due to a permanent error, event wil be omitted", + slog.Any("error", err), + slog.Group("event", + slog.String("type", event.GetType()), + slog.String("code", event.GetCode()), + slog.String("id", event.GetID()), + slog.Int64("index", event.GetIndex()), + ), + ) continue default: return trace.Wrap(err) @@ -465,15 +476,18 @@ func (g *GRPCServer) CreateAuditStream(stream authpb.AuthService_CreateAuditStre if counter%logInterval == 0 { if seconds > 0 { kbytes := float64(processed) / 1000 - g.Debugf("Processed %v events, tx rate kbytes %v/second.", counter, kbytes/float64(seconds)) + g.logger.DebugContext(stream.Context(), "Processed events", "event_count", counter, "tx_rate", kbytes/float64(seconds)) } } diff := time.Since(start) if diff > 100*time.Millisecond { - g.Warningf("RecordEvent(%v) took longer than 100ms: %v", event.GetType(), time.Since(event.GetTime())) + g.logger.WarnContext(stream.Context(), "RecordEvent took longer than 100ms", + "event_type", event.GetType(), + "duration", time.Since(event.GetTime()), + ) } } else { - g.Errorf("Rejecting unsupported stream request: %v.", request) + g.logger.ErrorContext(stream.Context(), "Rejecting unsupported stream request", "request", request) return trace.BadParameter("unsupported stream request") } } @@ -569,7 +583,7 @@ func (g *GRPCServer) GenerateUserCerts(ctx context.Context, req *authpb.UserCert return nil, trace.Wrap(err) } if err := validateUserCertsRequest(auth, req); err != nil { - g.Entry.Debugf("Validation of user certs request failed: %v", err) + g.logger.DebugContext(ctx, "Validation of user certs request failed", "error", err) return nil, trace.Wrap(err) } @@ -645,7 +659,7 @@ func (g *GRPCServer) generateUserSingleUseCerts(ctx context.Context, actx *grpcC actx, *req) if err != nil { - g.Entry.Warningf("Failed to generate single-use cert: %v", err) + g.logger.WarnContext(ctx, "Failed to generate single-use cert", "error", err) return nil, trace.Wrap(err) } @@ -806,7 +820,10 @@ func (g *GRPCServer) GetInstances(filter *types.InstanceFilter, stream authpb.Au for instances.Next() { instance, ok := instances.Item().(*types.InstanceV1) if !ok { - log.Warnf("Skipping unexpected instance type %T, expected %T.", instances.Item(), instance) + g.logger.WarnContext(stream.Context(), "Skipping unexpected instance type", + "instance_type", logutils.TypeAttr(instances.Item()), + "expected_instance_type", logutils.TypeAttr(instance), + ) continue } if err := stream.Send(instance); err != nil { @@ -904,7 +921,10 @@ func (g *GRPCServer) GetCurrentUserRoles(_ *emptypb.Empty, stream authpb.AuthSer for _, role := range roles { v6, ok := role.(*types.RoleV6) if !ok { - log.Warnf("expected type RoleV6, got %T for role %q", role, role.GetName()) + g.logger.WarnContext(stream.Context(), "expected type RoleV6, got unexpected for role type", + "role_type", logutils.TypeAttr(role), + "role", role.GetName(), + ) return trace.Errorf("encountered unexpected role type") } if err := stream.Send(v6); err != nil { @@ -2097,7 +2117,10 @@ func (g *GRPCServer) ListRoles(ctx context.Context, req *authpb.ListRolesRequest for _, role := range rsp.Roles { downgraded, err := maybeDowngradeRole(ctx, role) if err != nil { - log.Warnf("Failed to downgrade role %q, this is a bug and may result in spurious access denied errors. err=%q", role.GetName(), err) + g.logger.WarnContext(ctx, "Failed to downgrade role, this is a bug and may result in spurious access denied errors", + "role", role.GetName(), + "error", err, + ) continue } downgradedRoles = append(downgradedRoles, downgraded) @@ -2132,11 +2155,14 @@ func (g *GRPCServer) CreateRole(ctx context.Context, req *authpb.CreateRoleReque return nil, trace.Wrap(err) } - g.Debugf("%q role upserted", req.Role.GetName()) + g.logger.DebugContext(ctx, "role upserted", "role_name", req.Role.GetName()) v6, ok := created.(*types.RoleV6) if !ok { - log.Warnf("expected type RoleV6, got %T for role %q", created, created.GetName()) + g.logger.WarnContext(ctx, "expected type RoleV6, got unexpected type", + "role_type", logutils.TypeAttr(created), + "role", created.GetName(), + ) return nil, trace.BadParameter("encountered unexpected role type") } @@ -2168,11 +2194,14 @@ func (g *GRPCServer) UpdateRole(ctx context.Context, req *authpb.UpdateRoleReque return nil, trace.Wrap(err) } - g.Debugf("%q role upserted", req.Role.GetName()) + g.logger.DebugContext(ctx, "role upserted", "role", req.Role.GetName()) v6, ok := updated.(*types.RoleV6) if !ok { - log.Warnf("expected type RoleV6, got %T for role %q", updated, updated.GetName()) + g.logger.WarnContext(ctx, "expected type RoleV6, got unexpected type", + "role_type", logutils.TypeAttr(updated), + "role", updated.GetName(), + ) return nil, trace.BadParameter("encountered unexpected role type") } @@ -2204,11 +2233,14 @@ func (g *GRPCServer) UpsertRoleV2(ctx context.Context, req *authpb.UpsertRoleReq return nil, trace.Wrap(err) } - g.Debugf("%q role upserted", req.Role.GetName()) + g.logger.DebugContext(ctx, "role upserted", "role", req.Role.GetName()) v6, ok := upserted.(*types.RoleV6) if !ok { - log.Warnf("expected type RoleV6, got %T for role %q", upserted, upserted.GetName()) + g.logger.WarnContext(ctx, "expected type RoleV6, got unexpected type", + "role_type", logutils.TypeAttr(upserted), + "role", upserted.GetName(), + ) return nil, trace.BadParameter("encountered unexpected role type") } @@ -2231,7 +2263,7 @@ func (g *GRPCServer) DeleteRole(ctx context.Context, req *authpb.DeleteRoleReque return nil, trace.Wrap(err) } - g.Debugf("%q role deleted", req.GetName()) + g.logger.DebugContext(ctx, "role deleted", "role", req.GetName()) return &emptypb.Empty{}, nil } @@ -2907,7 +2939,10 @@ func (g *GRPCServer) GetServerInfos(_ *emptypb.Empty, stream authpb.AuthService_ for infos.Next() { si, ok := infos.Item().(*types.ServerInfoV1) if !ok { - log.Warnf("Skipping unexpected instance type %T, expected %T.", infos.Item(), si) + g.logger.WarnContext(stream.Context(), "expected type ServerInfoV1, got unexpected type", + "server_info_type", logutils.TypeAttr(infos.Item()), + "server_info_name", infos.Item().GetName(), + ) } if err := stream.Send(si); err != nil { infos.Done() @@ -3014,6 +3049,8 @@ func (g *GRPCServer) GetTrustedClusters(ctx context.Context, _ *emptypb.Empty) ( } // UpsertTrustedCluster upserts a Trusted Cluster. +// +// Deprecated: Use UpsertTrustedClusterV2 instead. func (g *GRPCServer) UpsertTrustedCluster(ctx context.Context, cluster *types.TrustedClusterV2) (*types.TrustedClusterV2, error) { auth, err := g.authenticate(ctx) if err != nil { @@ -3836,7 +3873,7 @@ func (g *GRPCServer) UpsertWindowsDesktopService(ctx context.Context, service *t // the closest thing we have to a public IP for the service. clientAddr, err := authz.ClientSrcAddrFromContext(ctx) if err != nil { - g.Logger.WithError(err).Warn("error getting client address from context") + g.logger.WarnContext(ctx, "error getting client address from context", "error", err) return nil, status.Errorf(codes.FailedPrecondition, "client address not found in request context") } service.Spec.Addr = utils.ReplaceLocalhost(service.GetAddr(), clientAddr.String()) @@ -4223,7 +4260,7 @@ func (g *GRPCServer) CreateSessionTracker(ctx context.Context, req *authpb.Creat } if req.SessionTracker == nil { - g.Errorf("Missing SessionTracker in CreateSessionTrackerRequest. This can be caused by an outdated Teleport node running against your cluster.") + g.logger.ErrorContext(ctx, "Missing SessionTracker in CreateSessionTrackerRequest, this can be caused by an outdated Teleport node running against your cluster") return nil, trace.BadParameter("missing SessionTracker from CreateSessionTrackerRequest") } @@ -5078,7 +5115,13 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { if err := cfg.CheckAndSetDefaults(); err != nil { return nil, trace.Wrap(err) } - log.Debugf("gRPC(SERVER): keep alive %v count: %v.", cfg.KeepAlivePeriod, cfg.KeepAliveCount) + + logger := slog.With(teleport.ComponentKey, teleport.Component(teleport.ComponentAuth, teleport.ComponentGRPC)) + + logger.DebugContext(context.Background(), "creating gRPC server", + "keep_alive_period", cfg.KeepAlivePeriod, + "keep_alive_count", cfg.KeepAliveCount, + ) // httplib.TLSCreds are explicitly used instead of credentials.NewTLS because the latter // modifies the tls.Config.NextProtos which causes problems due to multiplexing on the auth @@ -5119,6 +5162,7 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { Emitter: cfg.Emitter, Reporter: cfg.AuthServer.Services.UsageReporter, Clock: cfg.AuthServer.GetClock(), + Logger: cfg.AuthServer.logger.With(teleport.ComponentKey, "users.service"), }) if err != nil { return nil, trace.Wrap(err) @@ -5133,6 +5177,7 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { Emitter: cfg.Emitter, Reporter: cfg.AuthServer.Services.UsageReporter, Clock: cfg.AuthServer.GetClock(), + Logger: cfg.AuthServer.logger.With(teleport.ComponentKey, "presence.service"), }) if err != nil { return nil, trace.Wrap(err) @@ -5240,20 +5285,18 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { authServer := &GRPCServer{ APIConfig: cfg.APIConfig, - Entry: logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.Component(teleport.ComponentAuth, teleport.ComponentGRPC), - }), - server: server, + logger: logger, + server: server, } if en := os.Getenv("TELEPORT_UNSTABLE_CREATEAUDITSTREAM_INFLIGHT_LIMIT"); en != "" { inflightLimit, err := strconv.ParseInt(en, 10, 64) if err != nil { - log.Error("Failed to parse the TELEPORT_UNSTABLE_CREATEAUDITSTREAM_INFLIGHT_LIMIT envvar, limit will not be enforced.") + logger.ErrorContext(context.Background(), "Failed to parse the TELEPORT_UNSTABLE_CREATEAUDITSTREAM_INFLIGHT_LIMIT envvar, limit will not be enforced") inflightLimit = -1 } if inflightLimit == 0 { - log.Warn("TELEPORT_UNSTABLE_CREATEAUDITSTREAM_INFLIGHT_LIMIT is set to 0, no CreateAuditStream RPCs will be allowed.") + logger.WarnContext(context.Background(), "TELEPORT_UNSTABLE_CREATEAUDITSTREAM_INFLIGHT_LIMIT is set to 0, no CreateAuditStream RPCs will be allowed") } metrics.RegisterPrometheusCollectors( createAuditStreamAcceptedTotalMetric, diff --git a/lib/auth/init.go b/lib/auth/init.go index 215dd4aaae512..b9fd40f1ed576 100644 --- a/lib/auth/init.go +++ b/lib/auth/init.go @@ -35,7 +35,6 @@ import ( "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" oteltrace "go.opentelemetry.io/otel/trace" @@ -71,11 +70,12 @@ import ( "github.com/gravitational/teleport/lib/tlsca" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) -var log = logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentAuth, -}) +var ( + logger = logutils.NewPackageLogger(teleport.ComponentKey, teleport.ComponentAuth) +) // VersionStorage local storage for saving the version. type VersionStorage interface { @@ -401,7 +401,7 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { // on initial startup. if len(cfg.BootstrapResources) > 0 { if firstStart { - log.Infof("Applying %v bootstrap resources (first initialization)", len(cfg.BootstrapResources)) + asrv.logger.InfoContext(ctx, "Applying bootstrap resources (first initialization)", "resource_count", len(cfg.BootstrapResources)) if err := checkResourceConsistency(ctx, asrv.keyStore, domainName, cfg.BootstrapResources...); err != nil { return trace.Wrap(err, "refusing to bootstrap backend") } @@ -409,13 +409,13 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { return trace.Wrap(err, "backend bootstrap failed") } } else { - log.Warnf("Ignoring %v bootstrap resources (previously initialized)", len(cfg.BootstrapResources)) + asrv.logger.WarnContext(ctx, "Ignoring bootstrap resources (previously initialized)", "resource_count", len(cfg.BootstrapResources)) } } // if apply-on-startup resources are supplied, apply them if len(cfg.ApplyOnStartupResources) > 0 { - log.Infof("Applying %v resources (apply-on-startup)", len(cfg.ApplyOnStartupResources)) + asrv.logger.InfoContext(ctx, "Applying resources (apply-on-startup)", "resource_count", len(cfg.ApplyOnStartupResources)) if err := applyResources(ctx, asrv.Services, cfg.ApplyOnStartupResources); err != nil { return trace.Wrap(err, "applying resources failed") @@ -432,7 +432,7 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { if _, err := asrv.UpsertRole(ctx, role); err != nil { return trace.Wrap(err) } - log.Infof("Created role: %v.", role) + asrv.logger.InfoContext(ctx, "Created role", "role", role.GetName()) } for i := range cfg.Authorities { ca := cfg.Authorities[i] @@ -450,14 +450,17 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { return trace.Wrap(err) } } else { - log.Infof("Created trusted certificate authority: %q, type: %q.", ca.GetName(), ca.GetType()) + asrv.logger.InfoContext(ctx, "Created trusted certificate authority", + "ca_name", ca.GetName(), + "ca_type", ca.GetType(), + ) } } for _, tunnel := range cfg.ReverseTunnels { if err := asrv.UpsertReverseTunnel(ctx, tunnel); err != nil { return trace.Wrap(err) } - log.Infof("Created reverse tunnel: %v.", tunnel) + asrv.logger.InfoContext(ctx, "Created reverse tunnel", "tunnel", tunnel.GetName()) } g, gctx := errgroup.WithContext(ctx) @@ -494,14 +497,14 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { g.Go(func() error { _, span := cfg.Tracer.Start(gctx, "auth/SetStaticTokens") defer span.End() - log.Infof("Updating cluster configuration: %v.", cfg.StaticTokens) + asrv.logger.InfoContext(ctx, "Updating cluster configuration", "static_tokens", cfg.StaticTokens) return trace.Wrap(asrv.SetStaticTokens(cfg.StaticTokens)) }) g.Go(func() error { _, span := cfg.Tracer.Start(gctx, "auth/SetClusterNamespace") defer span.End() - log.Infof("Creating namespace: %q.", apidefaults.Namespace) + asrv.logger.InfoContext(ctx, "Creating namespace", "namespace", apidefaults.Namespace) return trace.Wrap(asrv.UpsertNamespace(types.DefaultNamespace())) }) @@ -530,18 +533,18 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { return trace.Wrap(err) } if cn.GetClusterName() != cfg.ClusterName.GetClusterName() { - msg := "Cannot rename cluster: continuing with current cluster name. Teleport " + + const msg = "Cannot rename cluster: continuing with current cluster name. Teleport " + "clusters can not be renamed once they are created. You are seeing this " + "message for one of two reasons. Either you have not set \"cluster_name\" in " + "Teleport configuration and changed the hostname of the auth server or you " + "are trying to change the value of \"cluster_name\"." - log.WithFields(logrus.Fields{ - "current_cluster_name": cn.GetClusterName(), - "configured_cluster_name": cfg.ClusterName.GetClusterName(), - }).Error(msg) + asrv.logger.ErrorContext(ctx, msg, + "current_cluster_name", cn.GetClusterName(), + "configured_cluster_name", cfg.ClusterName.GetClusterName(), + ) } - log.Debugf("Cluster configuration: %v.", cn.GetClusterName()) + asrv.logger.DebugContext(ctx, "Cluster configuration", "cluster_name", cn.GetClusterName()) return nil }) @@ -563,10 +566,10 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { } if lib.IsInsecureDevMode() { - warningMessage := "Starting teleport in insecure mode. This is " + + const warningMessage = "Starting teleport in insecure mode. This is " + "dangerous! Sensitive information will be logged to console and " + "certificates will not be verified. Proceed with caution!" - log.Warn(warningMessage) + asrv.logger.WarnContext(ctx, warningMessage) } span.AddEvent("migrating legacy resources") @@ -593,18 +596,18 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error { span.AddEvent("creating preset database object import rules") if err := createPresetDatabaseObjectImportRule(ctx, asrv); err != nil { // merely raise a warning; this is not a fatal error. - log.WithError(err).Warn("error creating preset database object import rules") + asrv.logger.WarnContext(ctx, "error creating preset database object import rules", "error", err) } span.AddEvent("completed creating database object import rules") } else { - log.Info("skipping preset role and user creation") + asrv.logger.InfoContext(ctx, "skipping preset role and user creation") } if !cfg.SkipPeriodicOperations { - log.Infof("Auth server is running periodic operations.") + asrv.logger.InfoContext(ctx, "Auth server is running periodic operations") go asrv.runPeriodicOperations() } else { - log.Infof("Auth server is skipping periodic operations.") + asrv.logger.InfoContext(ctx, "Auth server is skipping periodic operations") } return nil @@ -650,7 +653,7 @@ func initializeAuthorities(ctx context.Context, asrv *Server, cfg *InitConfig) e // Key deletion is best-effort, log a warning if it fails and carry on. // We don't want to prevent a CA rotation, which may be necessary in // some cases where this would fail. - log.Warnf("An attempt to clean up unused HSM or KMS CA keys has failed unexpectedly: %v", err) + asrv.logger.WarnContext(ctx, "An attempt to clean up unused HSM or KMS CA keys has failed unexpectedly", "error", err) } return nil } @@ -662,7 +665,7 @@ func initializeAuthority(ctx context.Context, asrv *Server, caID types.CertAuthI return nil, nil, trace.Wrap(err) } - log.Infof("First start: generating %s certificate authority.", caID.Type) + asrv.logger.InfoContext(ctx, "First start: generating certificate authority", "ca_type", caID.Type) if ca, err = generateAuthority(ctx, asrv, caID); err != nil { return nil, nil, trace.Wrap(err) } @@ -705,28 +708,34 @@ func initializeAuthority(ctx context.Context, asrv *Server, caID types.CertAuthI return nil, nil, trace.Wrap(err) } } else { - log.Warnf("This Auth Service is configured to use %s but the %s CA contains only %s. "+ - "No new certificates can be signed with the existing keys. "+ - "You must perform a CA rotation to generate new keys, or adjust your configuration to use the existing keys.", - usableKeysResult.PreferredKeyType, - caID.Type, - strings.Join(usableKeysResult.CAKeyTypes, " and ")) + const msg = "This Auth Service is configured to use key types that the CA does not contain. " + + "No new certificates can be signed with the existing keys. " + + "You must perform a CA rotation to generate new keys, or adjust your configuration to use the existing keys." + asrv.logger.WarnContext(ctx, msg, + "configured_key_type", usableKeysResult.PreferredKeyType, + "ca_type", caID.Type, + "available_key_types", usableKeysResult.CAKeyTypes, + ) } } else if !usableKeysResult.CAHasPreferredKeyType { - log.Warnf("This Auth Service is configured to use %s but the %s CA contains only %s. "+ - "New certificates will continue to be signed with raw software keys but you must perform a CA rotation to begin using %s.", - usableKeysResult.PreferredKeyType, - caID.Type, - strings.Join(usableKeysResult.CAKeyTypes, " and "), - usableKeysResult.PreferredKeyType) + const msg = "This Auth Service is configured to use key types that the CA does not contain. " + + "New certificates will continue to be signed with raw software keys but you must perform a CA rotation to begin using the new key type." + asrv.logger.WarnContext(ctx, msg, + "configured_key_type", usableKeysResult.PreferredKeyType, + "ca_type", caID.Type, + "available_key_types", usableKeysResult.CAKeyTypes, + ) } allKeyTypes := ca.AllKeyTypes() numKeyTypes := len(allKeyTypes) if numKeyTypes > 1 { - log.Warnf("%s CA contains a combination of %s and %s keys. If you are attempting to"+ - " configure HSM or KMS key storage, make sure it is configured on all auth servers in"+ - " this cluster and then perform a CA rotation: https://goteleport.com/docs/management/operations/ca-rotation/", - caID.Type, strings.Join(allKeyTypes[:numKeyTypes-1], ", "), allKeyTypes[numKeyTypes-1]) + const msg = "CA contains a combination of key types. If you are attempting to" + + " configure HSM or KMS key storage, make sure it is configured on all auth servers in" + + " this cluster and then perform a CA rotation: https://goteleport.com/docs/management/operations/ca-rotation/" + asrv.logger.WarnContext(ctx, msg, + "ca_type", caID.Type, + "key_types", []string{strings.Join(allKeyTypes[:numKeyTypes-1], ", "), allKeyTypes[numKeyTypes-1]}, + ) } for _, keySet := range []types.CAKeySet{ca.GetActiveKeys(), ca.GetAdditionalTrustedKeys()} { @@ -785,7 +794,7 @@ func initializeAuthPreference(ctx context.Context, asrv *Server, newAuthPref typ return trace.Wrap(err) } - shouldReplace, err := shouldInitReplaceResourceWithOrigin(storedAuthPref, newAuthPref) + shouldReplace, err := shouldInitReplaceResourceWithOrigin(ctx, storedAuthPref, newAuthPref, asrv.logger) if err != nil { return trace.Wrap(err) } @@ -830,7 +839,7 @@ func initializeAuthPreference(ctx context.Context, asrv *Server, newAuthPref typ } if storedAuthPref == nil { - log.Infof("Creating cluster auth preference: %v.", newAuthPref) + asrv.logger.InfoContext(ctx, "Creating cluster auth preference", "auth_preference", newAuthPref) _, err := asrv.CreateAuthPreference(ctx, newAuthPref) if trace.IsAlreadyExists(err) { continue @@ -861,7 +870,7 @@ func initializeClusterNetworkingConfig(ctx context.Context, asrv *Server, newNet return trace.Wrap(err) } - shouldReplace, err := shouldInitReplaceResourceWithOrigin(storedNetConfig, newNetConfig) + shouldReplace, err := shouldInitReplaceResourceWithOrigin(ctx, storedNetConfig, newNetConfig, asrv.logger) if err != nil { return trace.Wrap(err) } @@ -871,7 +880,7 @@ func initializeClusterNetworkingConfig(ctx context.Context, asrv *Server, newNet } if storedNetConfig == nil { - log.Infof("Creating cluster networking configuration: %v.", newNetConfig) + asrv.logger.InfoContext(ctx, "Creating cluster networking configuration", "networking_config", newNetConfig) _, err = asrv.CreateClusterNetworkingConfig(ctx, newNetConfig) if trace.IsAlreadyExists(err) { continue @@ -880,7 +889,7 @@ func initializeClusterNetworkingConfig(ctx context.Context, asrv *Server, newNet return trace.Wrap(err) } - log.Infof("Updating cluster networking configuration: %v.", newNetConfig) + asrv.logger.InfoContext(ctx, "Updating cluster networking configuration", "networking_config", newNetConfig) newNetConfig.SetRevision(storedNetConfig.GetRevision()) _, err = asrv.UpdateClusterNetworkingConfig(ctx, newNetConfig) if trace.IsCompareFailed(err) { @@ -901,7 +910,7 @@ func initializeSessionRecordingConfig(ctx context.Context, asrv *Server, newRecC return trace.Wrap(err) } - shouldReplace, err := shouldInitReplaceResourceWithOrigin(storedRecConfig, newRecConfig) + shouldReplace, err := shouldInitReplaceResourceWithOrigin(ctx, storedRecConfig, newRecConfig, asrv.logger) if err != nil { return trace.Wrap(err) } @@ -911,7 +920,7 @@ func initializeSessionRecordingConfig(ctx context.Context, asrv *Server, newRecC } if storedRecConfig == nil { - log.Infof("Creating session recording config: %v.", newRecConfig) + asrv.logger.InfoContext(ctx, "Creating session recording config", "recording_config", newRecConfig) _, err := asrv.CreateSessionRecordingConfig(ctx, newRecConfig) if trace.IsAlreadyExists(err) { continue @@ -920,7 +929,7 @@ func initializeSessionRecordingConfig(ctx context.Context, asrv *Server, newRecC return trace.Wrap(err) } - log.Infof("Updating session recording config: %v.", newRecConfig) + asrv.logger.InfoContext(ctx, "Updating session recording config", "recording_config", newRecConfig) newRecConfig.SetRevision(storedRecConfig.GetRevision()) _, err = asrv.UpdateSessionRecordingConfig(ctx, newRecConfig) if trace.IsCompareFailed(err) { @@ -949,7 +958,7 @@ func initializeAccessGraphSettings(ctx context.Context, asrv *Server) error { return trace.Wrap(err) } - log.Infof("Creating access graph settings: %v.", stored) + asrv.logger.InfoContext(ctx, "Creating access graph settings", "settings", stored) _, err = asrv.CreateAccessGraphSettings(ctx, stored) if trace.IsAlreadyExists(err) { return nil @@ -962,7 +971,7 @@ func initializeAccessGraphSettings(ctx context.Context, asrv *Server) error { // resource should be used to replace the stored resource during auth server // initialization. Dynamically configured resources must not be overwritten // when the corresponding file config is left unspecified (i.e., by defaults). -func shouldInitReplaceResourceWithOrigin(stored, candidate types.ResourceWithOrigin) (bool, error) { +func shouldInitReplaceResourceWithOrigin(ctx context.Context, stored, candidate types.ResourceWithOrigin, logger *slog.Logger) (bool, error) { if candidate == nil || (candidate.Origin() != types.OriginDefaults && candidate.Origin() != types.OriginConfigFile) { return false, trace.BadParameter("candidate origin must be either defaults or config-file (this is a bug)") } @@ -978,7 +987,7 @@ func shouldInitReplaceResourceWithOrigin(stored, candidate types.ResourceWithOri if candidate.Origin() == types.OriginConfigFile { // Log a warning when about to overwrite a dynamically configured resource. if stored.Origin() == types.OriginDynamic { - log.Warnf("Stored %v resource that was configured dynamically is about to be discarded in favor of explicit file configuration.", stored.GetKind()) + logger.WarnContext(ctx, "Stored resource that was configured dynamically is about to be discarded in favor of explicit file configuration", "resource", stored.GetKind()) } return true, nil } @@ -991,15 +1000,15 @@ func shouldInitReplaceResourceWithOrigin(stored, candidate types.ResourceWithOri // migrationStart marks the migration as active. // It should be called when a migration starts. -func migrationStart(ctx context.Context, migrationName string) { - log.Debugf("Migrations: %q migration started.", migrationName) +func migrationStart(ctx context.Context, migrationName string, logger *slog.Logger) { + logger.DebugContext(ctx, "Migration started", "migration_name", migrationName) migrations.WithLabelValues(migrationName).Set(1) } // migrationEnd marks the migration as inactive. // It should be called when a migration ends. -func migrationEnd(ctx context.Context, migrationName string) { - log.Debugf("Migrations: %q migration ended.", migrationName) +func migrationEnd(ctx context.Context, migrationName string, logger *slog.Logger) { + logger.DebugContext(ctx, "Migration ended", "migration_name", migrationName) migrations.WithLabelValues(migrationName).Set(0) } @@ -1080,7 +1089,7 @@ func createPresetRoles(ctx context.Context, rm PresetRoleManager) error { return trace.Wrap(err) } - role, err := services.AddRoleDefaults(currentRole) + role, err := services.AddRoleDefaults(gctx, currentRole) if trace.IsAlreadyExists(err) { return nil } @@ -1340,8 +1349,8 @@ func CertAuthorityInfo(ca types.CertAuthority) string { // where the presence of remote cluster was identified only by presence // of host certificate authority with cluster name not equal local cluster name func migrateRemoteClusters(ctx context.Context, asrv *Server) error { - migrationStart(ctx, "remote_clusters") - defer migrationEnd(ctx, "remote_clusters") + migrationStart(ctx, "remote_clusters", asrv.logger) + defer migrationEnd(ctx, "remote_clusters", asrv.logger) clusterName, err := asrv.Services.GetClusterName() if err != nil { @@ -1355,13 +1364,13 @@ func migrateRemoteClusters(ctx context.Context, asrv *Server) error { // forward and forward agent allowed for _, certAuthority := range certAuthorities { if certAuthority.GetName() == clusterName.GetClusterName() { - log.Debugf("Migrations: skipping local cluster cert authority %q.", certAuthority.GetName()) + asrv.logger.DebugContext(ctx, "Migrations: skipping local cluster cert authority", "cert_authority", certAuthority.GetName()) continue } // remote cluster already exists _, err = asrv.Services.GetRemoteCluster(ctx, certAuthority.GetName()) if err == nil { - log.Debugf("Migrations: remote cluster already exists for cert authority %q.", certAuthority.GetName()) + asrv.logger.DebugContext(ctx, "Migrations: remote cluster already exists for cert authority", "cert_authority", certAuthority.GetName()) continue } if !trace.IsNotFound(err) { @@ -1370,7 +1379,7 @@ func migrateRemoteClusters(ctx context.Context, asrv *Server) error { // the cert authority is associated with trusted cluster _, err = asrv.Services.GetTrustedCluster(ctx, certAuthority.GetName()) if err == nil { - log.Debugf("Migrations: trusted cluster resource exists for cert authority %q.", certAuthority.GetName()) + asrv.logger.DebugContext(ctx, "Migrations: trusted cluster resource exists for cert authority", "cert_authority", certAuthority.GetName()) continue } if !trace.IsNotFound(err) { @@ -1386,7 +1395,7 @@ func migrateRemoteClusters(ctx context.Context, asrv *Server) error { return trace.Wrap(err) } } - log.Infof("Migrations: added remote cluster resource for cert authority %q.", certAuthority.GetName()) + asrv.logger.InfoContext(ctx, "Migrations: added remote cluster resource for cert authority", "cert_authority", certAuthority.GetName()) } return nil diff --git a/lib/auth/join.go b/lib/auth/join.go index 53880bf9955e2..00d4f8847f1e9 100644 --- a/lib/auth/join.go +++ b/lib/auth/join.go @@ -22,13 +22,12 @@ import ( "context" "crypto/rand" "encoding/base64" - "fmt" + "log/slog" "net" "slices" "strings" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "google.golang.org/grpc/peer" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -60,7 +59,11 @@ func (a *Server) checkTokenJoinRequestCommon(ctx context.Context, req *types.Reg // make sure the token is valid provisionToken, err := a.ValidateToken(ctx, req.Token) if err != nil { - log.Warningf("%q can not join the cluster with role %s, token error: %v", req.NodeName, req.Role, err) + a.logger.WarnContext(ctx, "cannot join the cluster with invalid token", + "node_name", req.NodeName, + "role", req.Role, + "error", err, + ) msg := "the token is not valid" // default to most generic message if strings.Contains(err.Error(), TokenExpiredOrNotFound) { // propagate ExpiredOrNotFound message so that clients can attempt @@ -80,17 +83,22 @@ func (a *Server) checkTokenJoinRequestCommon(ctx context.Context, req *types.Reg } } if !hasLocalServiceRole { - msg := fmt.Sprintf("%q [%v] cannot requisition instance certs (token contains no local service roles)", req.NodeName, req.HostID) - log.Warn(msg) - return nil, trace.AccessDenied(msg) + a.logger.WarnContext(ctx, "cannot requisition instance certs (token contains no local service roles)", + "node_name", req.NodeName, + "host_id", req.HostID, + ) + return nil, trace.AccessDenied("%s [%v] cannot requisition instance certs (token contains no local service roles)", req.NodeName, req.HostID) } } // make sure the caller is requesting a role allowed by the token if !provisionToken.GetRoles().Include(req.Role) && req.Role != types.RoleInstance { - msg := fmt.Sprintf("node %q [%v] can not join the cluster, the token does not allow %q role", req.NodeName, req.HostID, req.Role) - log.Warn(msg) - return nil, trace.BadParameter(msg) + a.logger.WarnContext(ctx, "token does not allow role to join the cluster", + "node_name", req.NodeName, + "host_id", req.HostID, + "role", req.Role, + ) + return nil, trace.BadParameter("node %q [%v] can not join the cluster, the token does not allow %q role", req.NodeName, req.HostID, req.Role) } return provisionToken, nil @@ -121,17 +129,20 @@ func setRemoteAddrFromContext(ctx context.Context, req *types.RegisterUsingToken // handleJoinFailure logs and audits the failure of a join. It is intentionally // designed to handle potential nullness of the input parameters. func (a *Server) handleJoinFailure( + ctx context.Context, origErr error, pt types.ProvisionToken, attributeSource joinAttributeSourcer, req *types.RegisterUsingTokenRequest, ) { - fields := logrus.Fields{} + attrs := []slog.Attr{slog.Any("error", origErr)} if req != nil { - fields["role"] = req.Role - fields["host_id"] = req.HostID - fields["node_name"] = req.NodeName - fields["remote_addr"] = req.RemoteAddr + attrs = append(attrs, []slog.Attr{ + slog.String("role", string(req.Role)), + slog.String("host_id", req.HostID), + slog.String("node_name", req.NodeName), + slog.String("remote_addr", req.RemoteAddr), + }...) } // Fetch and encode attributes if they are available. @@ -140,21 +151,21 @@ func (a *Server) handleJoinFailure( var err error attributes, err := attributeSource.JoinAuditAttributes() if err != nil { - log.WithError(err).Warn("Unable to fetch join attributes from join method") + a.logger.WarnContext(ctx, "Unable to fetch join attributes from join method", "error", err) } - fields["attributes"] = attributes + attrs = append(attrs, slog.Any("attributes", attributes)) attributesProto, err = apievents.EncodeMap(attributes) if err != nil { - log.WithError(err).Warn("Unable to encode join attributes for audit event") + a.logger.WarnContext(ctx, "Unable to encode join attributes for audit event", "error", err) } } // Add log fields from token if available. if pt != nil { - fields["join_method"] = string(pt.GetJoinMethod()) - fields["token_name"] = pt.GetSafeName() + attrs = append(attrs, slog.String("join_method", string(pt.GetJoinMethod()))) + attrs = append(attrs, slog.String("token_name", pt.GetSafeName())) } - log.WithError(origErr).WithFields(fields).Warn("Failure to join cluster occurred") + a.logger.LogAttrs(ctx, slog.LevelWarn, "Failure to join cluster occurred", attrs...) var evt apievents.AuditEvent status := apievents.Status{ @@ -201,7 +212,7 @@ func (a *Server) handleJoinFailure( evt = instanceJoinEvent } if err := a.emitter.EmitAuditEvent(a.closeCtx, evt); err != nil { - log.WithError(err).Warn("Failed to emit failed join event") + a.logger.WarnContext(ctx, "Failed to emit failed join event", "error", err) } } @@ -222,7 +233,7 @@ func (a *Server) RegisterUsingToken(ctx context.Context, req *types.RegisterUsin defer func() { // Emit a log message and audit event on join failure. if err != nil { - a.handleJoinFailure(err, provisionToken, joinAttributeSrc, req) + a.handleJoinFailure(ctx, err, provisionToken, joinAttributeSrc, req) } }() @@ -398,16 +409,16 @@ func (a *Server) generateCertsBot( if joinAttributeSrc != nil { attributes, err := joinAttributeSrc.JoinAuditAttributes() if err != nil { - log.WithError(err).Warn("Unable to fetch join attributes from join method.") + a.logger.WarnContext(ctx, "Unable to fetch join attributes from join method", "error", err) } joinEvent.Attributes, err = apievents.EncodeMap(attributes) if err != nil { - log.WithError(err).Warn("Unable to encode join attributes for audit event.") + a.logger.WarnContext(ctx, "Unable to encode join attributes for audit event", "error", err) } auth.Metadata, err = structpb.NewStruct(attributes) if err != nil { - log.WithError(err).Warn("Unable to encode struct value for join metadata.") + a.logger.WarnContext(ctx, "Unable to encode struct value for join metadata", "error", err) } } @@ -432,16 +443,20 @@ func (a *Server) generateCertsBot( if shouldDeleteToken { // delete ephemeral bot join tokens so they can't be re-used if err := a.DeleteToken(ctx, provisionToken.GetName()); err != nil { - log.WithError(err).Warnf("Could not delete bot provision token %q after generating certs", - provisionToken.GetSafeName(), + a.logger.WarnContext(ctx, "Could not delete bot provision token after generating certs", + "provision_token", provisionToken.GetSafeName(), + "error", err, ) } } // Emit audit event for bot join. - log.Infof("Bot %q (instance: %s) has joined the cluster.", botName, botInstanceID) + a.logger.InfoContext(ctx, "Bot has joined the cluster", + "bot_name", botName, + "bot_instance", botInstanceID, + ) if err := a.emitter.EmitAuditEvent(ctx, joinEvent); err != nil { - log.WithError(err).Warn("Failed to emit bot join event.") + a.logger.WarnContext(ctx, "Failed to emit bot join event", "error", err) } return certs, nil } @@ -464,7 +479,7 @@ func (a *Server) generateCerts( if r.IsLocalService() { systemRoles = append(systemRoles, r) } else { - log.Warnf("Omitting non-service system role from instance cert: %q", r) + a.logger.WarnContext(ctx, "Omitting non-service system role from instance cert", "system_role", string(r)) } } } @@ -488,9 +503,18 @@ func (a *Server) generateCerts( // Emit audit event if req.Role == types.RoleInstance { - log.Infof("Instance %q [%v] has joined the cluster. role=%s, systemRoles=%+v", req.NodeName, req.HostID, req.Role, systemRoles) + a.logger.InfoContext(ctx, "Instance has joined the cluster", + "node_name", req.NodeName, + "host_id", req.HostID, + "role", req.Role, + "system_roles", systemRoles, + ) } else { - log.Infof("Instance %q [%v] has joined the cluster. role=%s", req.NodeName, req.HostID, req.Role) + a.logger.InfoContext(ctx, "Instance has joined the cluster", + "node_name", req.NodeName, + "host_id", req.HostID, + "role", req.Role, + ) } joinEvent := &apievents.InstanceJoin{ Metadata: apievents.Metadata{ @@ -513,15 +537,15 @@ func (a *Server) generateCerts( if joinAttributeSrc != nil { attributes, err := joinAttributeSrc.JoinAuditAttributes() if err != nil { - log.WithError(err).Warn("Unable to fetch join attributes from join method.") + a.logger.WarnContext(ctx, "Unable to fetch join attributes from join method", "error", err) } joinEvent.Attributes, err = apievents.EncodeMap(attributes) if err != nil { - log.WithError(err).Warn("Unable to encode join attributes for audit event.") + a.logger.WarnContext(ctx, "Unable to encode join attributes for audit event", "error", err) } } if err := a.emitter.EmitAuditEvent(ctx, joinEvent); err != nil { - log.WithError(err).Warn("Failed to emit instance join event.") + a.logger.WarnContext(ctx, "Failed to emit instance join event", "error", err) } return certs, nil } diff --git a/lib/auth/join_azure.go b/lib/auth/join_azure.go index 721a53ff2d7fa..70ae17918b7fa 100644 --- a/lib/auth/join_azure.go +++ b/lib/auth/join_azure.go @@ -366,9 +366,7 @@ func (a *Server) RegisterUsingAzureMethodWithOpts( defer func() { // Emit a log message and audit event on join failure. if err != nil { - a.handleJoinFailure( - err, provisionToken, nil, joinRequest, - ) + a.handleJoinFailure(ctx, err, provisionToken, nil, joinRequest) } }() diff --git a/lib/auth/join_ec2.go b/lib/auth/join_ec2.go index 0b5cf51252cef..ee4ee567b7434 100644 --- a/lib/auth/join_ec2.go +++ b/lib/auth/join_ec2.go @@ -338,8 +338,12 @@ func (a *Server) tryToDetectIdentityReuse(ctx context.Context, req *types.Regist return trace.Wrap(err) } if instanceExists { - log.Warnf("Server with ID %q and role %q is attempting to join the cluster with a Simplified Node Joining request, but"+ - " a server with this ID is already present in the cluster.", req.HostID, req.Role) + const msg = "Server is attempting to join the cluster with a Simplified Node Joining request, but" + + " a server with this ID is already present in the cluster" + a.logger.WarnContext(ctx, msg, + "host_id", req.HostID, + "role", req.Role, + ) return trace.AccessDenied("server with host ID %q and role %q already exists", req.HostID, req.Role) } return nil @@ -363,7 +367,7 @@ func (a *Server) checkEC2JoinRequest(ctx context.Context, req *types.RegisterUsi return trace.Wrap(err) } - log.Debugf("Received Simplified Node Joining request for host %q", req.HostID) + a.logger.DebugContext(ctx, "Received Simplified Node Joining request", "host_id", req.HostID) if len(req.EC2IdentityDocument) == 0 { return trace.AccessDenied("this token is only valid for the EC2 join " + diff --git a/lib/auth/join_gcp.go b/lib/auth/join_gcp.go index a6cf1db4ccd7e..9cfecca822637 100644 --- a/lib/auth/join_gcp.go +++ b/lib/auth/join_gcp.go @@ -24,7 +24,6 @@ import ( "strings" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/gcp" @@ -50,17 +49,18 @@ func (a *Server) checkGCPJoinRequest(ctx context.Context, req *types.RegisterUsi claims, err := a.gcpIDTokenValidator.Validate(ctx, req.IDToken) if err != nil { - log.WithFields(logrus.Fields{ - "claims": claims, - "token": pt.GetName(), - }).WithError(err).Warn("Unable to validate GCP IDToken") + a.logger.WarnContext(ctx, "Unable to validate GCP IDToken", + "error", err, + "claims", claims, + "token", pt.GetName(), + ) return nil, trace.Wrap(err) } - log.WithFields(logrus.Fields{ - "claims": claims, - "token": pt.GetName(), - }).Info("GCP VM trying to join cluster") + a.logger.InfoContext(ctx, "GCP VM trying to join cluster", + "claims", claims, + "token", pt.GetName(), + ) if err := checkGCPAllowRules(token, claims); err != nil { return nil, trace.Wrap(err) diff --git a/lib/auth/join_github.go b/lib/auth/join_github.go index 226f1c501c6b0..87d9e0aaa3ea4 100644 --- a/lib/auth/join_github.go +++ b/lib/auth/join_github.go @@ -24,7 +24,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/githubactions" @@ -87,10 +86,10 @@ func (a *Server) checkGitHubJoinRequest(ctx context.Context, req *types.Register } } - log.WithFields(logrus.Fields{ - "claims": claims, - "token": pt.GetName(), - }).Info("Github actions run trying to join cluster") + a.logger.InfoContext(ctx, "Github actions run trying to join cluster", + "claims", claims, + "token", pt.GetName(), + ) return claims, trace.Wrap(checkGithubAllowRules(token, claims)) } diff --git a/lib/auth/join_gitlab.go b/lib/auth/join_gitlab.go index e20b9f8f343d8..0296c9ad3ed24 100644 --- a/lib/auth/join_gitlab.go +++ b/lib/auth/join_gitlab.go @@ -24,7 +24,6 @@ import ( "strings" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/gitlab" @@ -57,10 +56,10 @@ func (a *Server) checkGitLabJoinRequest(ctx context.Context, req *types.Register return nil, trace.Wrap(err) } - log.WithFields(logrus.Fields{ - "claims": claims, - "token": pt.GetName(), - }).Info("GitLab CI run trying to join cluster") + a.logger.InfoContext(ctx, "GitLab CI run trying to join cluster", + "claims", claims, + "token", pt.GetName(), + ) return claims, trace.Wrap(checkGitLabAllowRules(token, claims)) } diff --git a/lib/auth/join_iam.go b/lib/auth/join_iam.go index ba2209105c7c0..7b284733bee1c 100644 --- a/lib/auth/join_iam.go +++ b/lib/auth/join_iam.go @@ -109,7 +109,7 @@ func validateSTSIdentityRequest(req *http.Request, challenge string, cfg *iamReg // invalid sts:GetCallerIdentity request, it's either going to be caused // by a node in a unknown region or an attacker. if err != nil { - log.WithError(err).Warn("Detected an invalid sts:GetCallerIdentity used by a client attempting to use the IAM join method.") + logger.WarnContext(req.Context(), "Detected an invalid sts:GetCallerIdentity used by a client attempting to use the IAM join method", "error", err) } }() @@ -344,9 +344,7 @@ func (a *Server) RegisterUsingIAMMethodWithOpts( defer func() { // Emit a log message and audit event on join failure. if err != nil { - a.handleJoinFailure( - err, provisionToken, nil, joinRequest, - ) + a.handleJoinFailure(ctx, err, provisionToken, nil, joinRequest) } }() diff --git a/lib/auth/join_kubernetes.go b/lib/auth/join_kubernetes.go index d5bbc6586d831..317f6cc7dfd3e 100644 --- a/lib/auth/join_kubernetes.go +++ b/lib/auth/join_kubernetes.go @@ -24,7 +24,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" kubetoken "github.com/gravitational/teleport/lib/kube/token" @@ -82,10 +81,10 @@ func (a *Server) checkKubernetesJoinRequest(ctx context.Context, req *types.Regi ) } - log.WithFields(logrus.Fields{ - "validated_identity": result, - "token": token.GetName(), - }).Info("Kubernetes workload trying to join cluster") + a.logger.InfoContext(ctx, "Kubernetes workload trying to join cluster", + "validated_identity", result, + "token", token.GetName(), + ) return result, trace.Wrap(checkKubernetesAllowRules(token, result)) } diff --git a/lib/auth/join_spacelift.go b/lib/auth/join_spacelift.go index 30fecf5424abd..67b84de5f0e38 100644 --- a/lib/auth/join_spacelift.go +++ b/lib/auth/join_spacelift.go @@ -23,7 +23,6 @@ import ( "fmt" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/modules" @@ -64,10 +63,10 @@ func (a *Server) checkSpaceliftJoinRequest(ctx context.Context, req *types.Regis return nil, trace.Wrap(err) } - log.WithFields(logrus.Fields{ - "claims": claims, - "token": pt.GetName(), - }).Info("Spacelift run trying to join cluster") + a.logger.InfoContext(ctx, "Spacelift run trying to join cluster", + "claims", claims, + "token", pt.GetName(), + ) return claims, trace.Wrap(checkSpaceliftAllowRules(token, claims)) } diff --git a/lib/auth/join_terraformcloud.go b/lib/auth/join_terraformcloud.go index 9302004858608..827319733a39a 100644 --- a/lib/auth/join_terraformcloud.go +++ b/lib/auth/join_terraformcloud.go @@ -23,7 +23,6 @@ import ( "fmt" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/modules" @@ -75,10 +74,10 @@ func (a *Server) checkTerraformCloudJoinRequest(ctx context.Context, req *types. return nil, trace.Wrap(err) } - log.WithFields(logrus.Fields{ - "claims": claims, - "token": pt.GetName(), - }).Info("Terraform Cloud run trying to join cluster") + a.logger.InfoContext(ctx, "Terraform Cloud run trying to join cluster", + "claims", claims, + "token", pt.GetName(), + ) return claims, trace.Wrap(checkTerraformCloudAllowRules(token, claims)) } diff --git a/lib/auth/join_tpm.go b/lib/auth/join_tpm.go index 05bf9e3c35a54..12463e8ecd811 100644 --- a/lib/auth/join_tpm.go +++ b/lib/auth/join_tpm.go @@ -43,9 +43,7 @@ func (a *Server) RegisterUsingTPMMethod( defer func() { // Emit a log message and audit event on join failure. if err != nil { - a.handleJoinFailure( - err, provisionToken, attributeSrc, initReq.JoinRequest, - ) + a.handleJoinFailure(ctx, err, provisionToken, attributeSrc, initReq.JoinRequest) } }() diff --git a/lib/auth/kube.go b/lib/auth/kube.go index 4d9eaf4b2c864..bcacbd0e6bf1f 100644 --- a/lib/auth/kube.go +++ b/lib/auth/kube.go @@ -52,7 +52,7 @@ func (a *Server) ProcessKubeCSR(req authclient.KubeCSR) (*authclient.KubeCSRResp // Certificate for remote cluster is a user certificate // with special provisions. - log.Debugf("Generating certificate to access remote Kubernetes clusters.") + a.logger.DebugContext(ctx, "Generating certificate to access remote Kubernetes clusters") hostCA, err := a.GetCertAuthority(ctx, types.CertAuthID{ Type: types.HostCA, diff --git a/lib/auth/machineid/workloadidentityv1/decision.go b/lib/auth/machineid/workloadidentityv1/decision.go new file mode 100644 index 0000000000000..4e959efe6ee12 --- /dev/null +++ b/lib/auth/machineid/workloadidentityv1/decision.go @@ -0,0 +1,187 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package workloadidentityv1 + +import ( + "context" + "regexp" + "slices" + "strings" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" + "github.com/gravitational/teleport/lib/utils" +) + +type decision struct { + templatedWorkloadIdentity *workloadidentityv1pb.WorkloadIdentity + shouldIssue bool + reason error +} + +func decide( + ctx context.Context, + wi *workloadidentityv1pb.WorkloadIdentity, + attrs *workloadidentityv1pb.Attrs, +) *decision { + d := &decision{ + templatedWorkloadIdentity: proto.Clone(wi).(*workloadidentityv1pb.WorkloadIdentity), + } + + // First, evaluate the rules. + if err := evaluateRules(wi, attrs); err != nil { + d.reason = trace.Wrap(err, "attributes did not pass rule evaluation") + return d + } + + // Now we can cook up some templating... + templated, err := templateString(wi.GetSpec().GetSpiffe().GetId(), attrs) + if err != nil { + d.reason = trace.Wrap(err, "templating spec.spiffe.id") + return d + } + d.templatedWorkloadIdentity.Spec.Spiffe.Id = templated + + templated, err = templateString(wi.GetSpec().GetSpiffe().GetHint(), attrs) + if err != nil { + d.reason = trace.Wrap(err, "templating spec.spiffe.hint") + return d + } + d.templatedWorkloadIdentity.Spec.Spiffe.Hint = templated + + for i, san := range wi.GetSpec().GetSpiffe().GetX509().GetDnsSans() { + templated, err = templateString(san, attrs) + if err != nil { + d.reason = trace.Wrap(err, "templating spec.spiffe.x509.dns_sans[%d]", i) + return d + } + if !utils.IsValidHostname(templated) { + d.reason = trace.BadParameter( + "templating spec.spiffe.x509.dns_sans[%d] resulted in an invalid DNS name %q", + i, + templated, + ) + return d + } + d.templatedWorkloadIdentity.Spec.Spiffe.X509.DnsSans[i] = templated + } + + // Yay - made it to the end! + d.shouldIssue = true + return d +} + +// getFieldStringValue returns a string value from the given attribute set. +// The attribute is specified as a dot-separated path to the field in the +// attribute set. +// +// The specified attribute must be a string field. If the attribute is not +// found, an error is returned. +// +// TODO(noah): This function will be replaced by the Teleport predicate language +// in a coming PR. +func getFieldStringValue(attrs *workloadidentityv1pb.Attrs, attr string) (string, error) { + attrParts := strings.Split(attr, ".") + message := attrs.ProtoReflect() + // TODO(noah): Improve errors by including the fully qualified attribute + // (e.g add up the parts of the attribute path processed thus far) + for i, part := range attrParts { + fieldDesc := message.Descriptor().Fields().ByTextName(part) + if fieldDesc == nil { + return "", trace.NotFound("attribute %q not found", part) + } + // We expect the final key to point to a string field - otherwise - we + // return an error. + if i == len(attrParts)-1 { + if !slices.Contains([]protoreflect.Kind{ + protoreflect.StringKind, + protoreflect.BoolKind, + protoreflect.Int32Kind, + protoreflect.Int64Kind, + protoreflect.Uint64Kind, + protoreflect.Uint32Kind, + }, fieldDesc.Kind()) { + return "", trace.BadParameter("attribute %q of type %q cannot be converted to string", part, fieldDesc.Kind()) + } + return message.Get(fieldDesc).String(), nil + } + // If we're not processing the final key part, we expect this to point + // to a message that we can further explore. + if fieldDesc.Kind() != protoreflect.MessageKind { + return "", trace.BadParameter("attribute %q is not a message", part) + } + message = message.Get(fieldDesc).Message() + } + return "", nil +} + +// templateString takes a given input string and replaces any values within +// {{ }} with values from the attribute set. +// +// If the specified value is not found in the attribute set, an error is +// returned. +// +// TODO(noah): In a coming PR, this will be replaced by evaluating the values +// within the handlebars as expressions. +func templateString(in string, attrs *workloadidentityv1pb.Attrs) (string, error) { + re := regexp.MustCompile(`\{\{([^{}]+?)\}\}`) + matches := re.FindAllStringSubmatch(in, -1) + + for _, match := range matches { + attrKey := strings.TrimSpace(match[1]) + value, err := getFieldStringValue(attrs, attrKey) + if err != nil { + return "", trace.Wrap(err, "fetching attribute value for %q", attrKey) + } + // We want to have an implicit rule here that if an attribute is + // included in the template, but is not set, we should refuse to issue + // the credential. + if value == "" { + return "", trace.NotFound("attribute %q unset", attrKey) + } + in = strings.Replace(in, match[0], value, 1) + } + + return in, nil +} + +func evaluateRules( + wi *workloadidentityv1pb.WorkloadIdentity, + attrs *workloadidentityv1pb.Attrs, +) error { + if len(wi.GetSpec().GetRules().GetAllow()) == 0 { + return nil + } +ruleLoop: + for _, rule := range wi.GetSpec().GetRules().GetAllow() { + for _, condition := range rule.GetConditions() { + val, err := getFieldStringValue(attrs, condition.Attribute) + if err != nil { + return trace.Wrap(err) + } + if val != condition.Equals { + continue ruleLoop + } + } + return nil + } + // TODO: Eventually, we'll need to work support for deny rules into here. + return trace.AccessDenied("no matching rule found") +} diff --git a/lib/auth/machineid/workloadidentityv1/issuer_service_test.go b/lib/auth/machineid/workloadidentityv1/decision_test.go similarity index 80% rename from lib/auth/machineid/workloadidentityv1/issuer_service_test.go rename to lib/auth/machineid/workloadidentityv1/decision_test.go index bf18594416609..e8cb267bb0879 100644 --- a/lib/auth/machineid/workloadidentityv1/issuer_service_test.go +++ b/lib/auth/machineid/workloadidentityv1/decision_test.go @@ -17,6 +17,7 @@ package workloadidentityv1 import ( + "context" "testing" "github.com/stretchr/testify/require" @@ -26,6 +27,55 @@ import ( "github.com/gravitational/teleport/api/types" ) +func Test_decide(t *testing.T) { + standardAttrs := &workloadidentityv1pb.Attrs{ + User: &workloadidentityv1pb.UserAttrs{ + Name: "jeff", + }, + Workload: &workloadidentityv1pb.WorkloadAttrs{ + Kubernetes: &workloadidentityv1pb.WorkloadAttrsKubernetes{ + PodName: "pod1", + Namespace: "default", + }, + }, + } + tests := []struct { + name string + wid *workloadidentityv1pb.WorkloadIdentity + attrs *workloadidentityv1pb.Attrs + wantIssue bool + assertReason require.ErrorAssertionFunc + }{ + { + name: "invalid dns name", + wid: &workloadidentityv1pb.WorkloadIdentity{ + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/valid", + X509: &workloadidentityv1pb.WorkloadIdentitySPIFFEX509{ + DnsSans: []string{ + "//imvalid;;", + }, + }, + }, + }, + }, + attrs: standardAttrs, + wantIssue: false, + assertReason: func(t require.TestingT, err error, i ...interface{}) { + require.ErrorContains(t, err, "templating spec.spiffe.x509.dns_sans[0] resulted in an invalid DNS name") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := decide(context.Background(), tt.wid, tt.attrs) + require.Equal(t, tt.wantIssue, d.shouldIssue) + tt.assertReason(t, d.reason) + }) + } +} + func Test_getFieldStringValue(t *testing.T) { tests := []struct { name string diff --git a/lib/auth/machineid/workloadidentityv1/issuer_service.go b/lib/auth/machineid/workloadidentityv1/issuer_service.go index 70a7fa1197974..eb75befe32b0b 100644 --- a/lib/auth/machineid/workloadidentityv1/issuer_service.go +++ b/lib/auth/machineid/workloadidentityv1/issuer_service.go @@ -24,8 +24,6 @@ import ( "log/slog" "math/big" "net/url" - "regexp" - "slices" "strings" "time" @@ -33,7 +31,6 @@ import ( "github.com/jonboulle/clockwork" "github.com/spiffe/go-spiffe/v2/spiffeid" "go.opentelemetry.io/otel" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -126,104 +123,6 @@ func NewIssuanceService(cfg *IssuanceServiceConfig) (*IssuanceService, error) { }, nil } -// getFieldStringValue returns a string value from the given attribute set. -// The attribute is specified as a dot-separated path to the field in the -// attribute set. -// -// The specified attribute must be a string field. If the attribute is not -// found, an error is returned. -// -// TODO(noah): This function will be replaced by the Teleport predicate language -// in a coming PR. -func getFieldStringValue(attrs *workloadidentityv1pb.Attrs, attr string) (string, error) { - attrParts := strings.Split(attr, ".") - message := attrs.ProtoReflect() - // TODO(noah): Improve errors by including the fully qualified attribute - // (e.g add up the parts of the attribute path processed thus far) - for i, part := range attrParts { - fieldDesc := message.Descriptor().Fields().ByTextName(part) - if fieldDesc == nil { - return "", trace.NotFound("attribute %q not found", part) - } - // We expect the final key to point to a string field - otherwise - we - // return an error. - if i == len(attrParts)-1 { - if !slices.Contains([]protoreflect.Kind{ - protoreflect.StringKind, - protoreflect.BoolKind, - protoreflect.Int32Kind, - protoreflect.Int64Kind, - protoreflect.Uint64Kind, - protoreflect.Uint32Kind, - }, fieldDesc.Kind()) { - return "", trace.BadParameter("attribute %q of type %q cannot be converted to string", part, fieldDesc.Kind()) - } - return message.Get(fieldDesc).String(), nil - } - // If we're not processing the final key part, we expect this to point - // to a message that we can further explore. - if fieldDesc.Kind() != protoreflect.MessageKind { - return "", trace.BadParameter("attribute %q is not a message", part) - } - message = message.Get(fieldDesc).Message() - } - return "", nil -} - -// templateString takes a given input string and replaces any values within -// {{ }} with values from the attribute set. -// -// If the specified value is not found in the attribute set, an error is -// returned. -// -// TODO(noah): In a coming PR, this will be replaced by evaluating the values -// within the handlebars as expressions. -func templateString(in string, attrs *workloadidentityv1pb.Attrs) (string, error) { - re := regexp.MustCompile(`\{\{([^{}]+?)\}\}`) - matches := re.FindAllStringSubmatch(in, -1) - - for _, match := range matches { - attrKey := strings.TrimSpace(match[1]) - value, err := getFieldStringValue(attrs, attrKey) - if err != nil { - return "", trace.Wrap(err, "fetching attribute value for %q", attrKey) - } - // We want to have an implicit rule here that if an attribute is - // included in the template, but is not set, we should refuse to issue - // the credential. - if value == "" { - return "", trace.NotFound("attribute %q unset", attrKey) - } - in = strings.Replace(in, match[0], value, 1) - } - - return in, nil -} - -func evaluateRules( - wi *workloadidentityv1pb.WorkloadIdentity, - attrs *workloadidentityv1pb.Attrs, -) error { - if len(wi.GetSpec().GetRules().GetAllow()) == 0 { - return nil - } -ruleLoop: - for _, rule := range wi.GetSpec().GetRules().GetAllow() { - for _, condition := range rule.GetConditions() { - val, err := getFieldStringValue(attrs, condition.Attribute) - if err != nil { - return trace.Wrap(err) - } - if val != condition.Equals { - continue ruleLoop - } - } - return nil - } - // TODO: Eventually, we'll need to work support for deny rules into here. - return trace.AccessDenied("no matching rule found") -} - func (s *IssuanceService) deriveAttrs( authzCtx *authz.Context, workloadAttrs *workloadidentityv1pb.WorkloadAttrs, @@ -251,11 +150,6 @@ func (s *IssuanceService) IssueWorkloadIdentity( return nil, trace.AccessDenied("workload identity issuance experiment is disabled") } - authCtx, err := s.authorizer.Authorize(ctx) - if err != nil { - return nil, trace.Wrap(err) - } - switch { case req.GetName() == "": return nil, trace.BadParameter("name: is required") @@ -263,6 +157,18 @@ func (s *IssuanceService) IssueWorkloadIdentity( return nil, trace.BadParameter("at least one credential type must be requested") } + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindWorkloadIdentity, types.VerbRead); err != nil { + return nil, trace.Wrap(err) + } + attrs, err := s.deriveAttrs(authCtx, req.GetWorkloadAttrs()) + if err != nil { + return nil, trace.Wrap(err, "deriving attributes") + } + wi, err := s.cache.GetWorkloadIdentity(ctx, req.GetName()) if err != nil { return nil, trace.Wrap(err) @@ -276,126 +182,167 @@ func (s *IssuanceService) IssueWorkloadIdentity( return nil, trace.Wrap(err) } - attrs, err := s.deriveAttrs(authCtx, req.GetWorkloadAttrs()) - if err != nil { - return nil, trace.Wrap(err, "deriving attributes") + decision := decide(ctx, wi, attrs) + if !decision.shouldIssue { + return nil, trace.Wrap(decision.reason, "workload identity failed evaluation") } - // Evaluate any rules explicitly configured by the user - if err := evaluateRules(wi, attrs); err != nil { - return nil, trace.Wrap(err) + + var cred *workloadidentityv1pb.Credential + switch v := req.GetCredential().(type) { + case *workloadidentityv1pb.IssueWorkloadIdentityRequest_X509SvidParams: + ca, err := s.getX509CA(ctx) + if err != nil { + return nil, trace.Wrap(err, "fetching X509 SPIFFE CA") + } + cred, err = s.issueX509SVID( + ctx, + ca, + decision.templatedWorkloadIdentity, + v.X509SvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap(err, "issuing X509 SVID") + } + case *workloadidentityv1pb.IssueWorkloadIdentityRequest_JwtSvidParams: + key, issuer, err := s.getJWTIssuerKey(ctx) + if err != nil { + return nil, trace.Wrap(err, "getting JWT issuer key") + } + cred, err = s.issueJWTSVID( + ctx, + key, + issuer, + decision.templatedWorkloadIdentity, + v.JwtSvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap(err, "issuing JWT SVID") + } + default: + return nil, trace.BadParameter("credential: unknown type %T", req.GetCredential()) } - // Perform any templating - spiffeIDPath, err := templateString(wi.GetSpec().GetSpiffe().GetId(), attrs) - if err != nil { - return nil, trace.Wrap(err, "templating spec.spiffe.id") + return &workloadidentityv1pb.IssueWorkloadIdentityResponse{ + Credential: cred, + }, nil +} + +// maxWorkloadIdentitiesIssued is the maximum number of workload identities that +// can be issued in a single request. +// TODO(noah): We'll want to make this tunable via env var or similar to make +// sure we can adjust it as needed. +var maxWorkloadIdentitiesIssued = 10 + +func (s *IssuanceService) IssueWorkloadIdentities( + ctx context.Context, + req *workloadidentityv1pb.IssueWorkloadIdentitiesRequest, +) (*workloadidentityv1pb.IssueWorkloadIdentitiesResponse, error) { + if !experiment.Enabled() { + return nil, trace.AccessDenied("workload identity issuance experiment is disabled") } - spiffeID, err := spiffeid.FromURI(&url.URL{ - Scheme: "spiffe", - Host: s.clusterName, - Path: spiffeIDPath, - }) - if err != nil { - return nil, trace.Wrap(err, "creating SPIFFE ID") + + switch { + case len(req.LabelSelectors) == 0: + return nil, trace.BadParameter("label_selectors: at least one label selector must be specified") + case req.GetCredential() == nil: + return nil, trace.BadParameter("at least one credential type must be requested") } - hint, err := templateString(wi.GetSpec().GetSpiffe().GetHint(), attrs) + authCtx, err := s.authorizer.Authorize(ctx) if err != nil { - return nil, trace.Wrap(err, "templating spec.spiffe.hint") + return nil, trace.Wrap(err) } - - // TODO(noah): Add more sophisticated control of the TTL. - ttl := time.Hour - if req.RequestedTtl != nil && req.RequestedTtl.AsDuration() != 0 { - ttl = req.RequestedTtl.AsDuration() - if ttl > defaultMaxTTL { - ttl = defaultMaxTTL - } + if err := authCtx.CheckAccessToKind(types.KindWorkloadIdentity, types.VerbRead, types.VerbList); err != nil { + return nil, trace.Wrap(err) } - - now := s.clock.Now() - notBefore := now.Add(-1 * time.Minute) - notAfter := now.Add(ttl) - - // Prepare event - evt := &apievents.SPIFFESVIDIssued{ - Metadata: apievents.Metadata{ - Type: events.SPIFFESVIDIssuedEvent, - Code: events.SPIFFESVIDIssuedSuccessCode, - }, - UserMetadata: authz.ClientUserMetadata(ctx), - ConnectionMetadata: authz.ConnectionMetadata(ctx), - SPIFFEID: spiffeID.String(), - Hint: hint, - WorkloadIdentity: wi.GetMetadata().GetName(), - WorkloadIdentityRevision: wi.GetMetadata().GetRevision(), + attrs, err := s.deriveAttrs(authCtx, req.GetWorkloadAttrs()) + if err != nil { + return nil, trace.Wrap(err, "deriving attributes") } - cred := &workloadidentityv1pb.Credential{ - WorkloadIdentityName: wi.GetMetadata().GetName(), - WorkloadIdentityRevision: wi.GetMetadata().GetRevision(), - SpiffeId: spiffeID.String(), - Hint: hint, + // Fetch all workload identities that match the label selectors AND the + // principal can access. + workloadIdentities, err := s.matchingAndAuthorizedWorkloadIdentities( + ctx, + authCtx, + convertLabels(req.LabelSelectors), + ) + if err != nil { + return nil, trace.Wrap(err) + } - ExpiresAt: timestamppb.New(notAfter), - Ttl: durationpb.New(ttl), + // Evaluate rules/templating for each worklaod identity, filtering out those + // that should not be issued. + shouldIssue := []*workloadidentityv1pb.WorkloadIdentity{} + for _, wi := range workloadIdentities { + decision := decide(ctx, wi, attrs) + if decision.shouldIssue { + shouldIssue = append(shouldIssue, decision.templatedWorkloadIdentity) + } + if len(shouldIssue) > maxWorkloadIdentitiesIssued { + // If we're now above the limit, then we want to exit out... + return nil, trace.BadParameter( + "number of identities that would be issued exceeds maximum permitted (max = %d), use more specific labels", + maxWorkloadIdentitiesIssued, + ) + } } + var creds = make([]*workloadidentityv1pb.Credential, 0, len(shouldIssue)) switch v := req.GetCredential().(type) { - case *workloadidentityv1pb.IssueWorkloadIdentityRequest_X509SvidParams: - evt.SVIDType = "x509" - certDer, certSerial, err := s.issueX509SVID( - ctx, - v.X509SvidParams, - notBefore, - notAfter, - spiffeID, - ) + case *workloadidentityv1pb.IssueWorkloadIdentitiesRequest_X509SvidParams: + ca, err := s.getX509CA(ctx) if err != nil { - return nil, trace.Wrap(err, "issuing X509 SVID") + return nil, trace.Wrap(err, "fetching CA to sign X509 SVID") } - serialStr := serialString(certSerial) - cred.Credential = &workloadidentityv1pb.Credential_X509Svid{ - X509Svid: &workloadidentityv1pb.X509SVIDCredential{ - Cert: certDer, - SerialNumber: serialStr, - }, + for _, wi := range shouldIssue { + cred, err := s.issueX509SVID( + ctx, + ca, + wi, + v.X509SvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap( + err, + "issuing X509 SVID for workload identity %q", + wi.GetMetadata().GetName(), + ) + } + creds = append(creds, cred) } - evt.SerialNumber = serialStr - case *workloadidentityv1pb.IssueWorkloadIdentityRequest_JwtSvidParams: - evt.SVIDType = "jwt" - signedJwt, jti, err := s.issueJWTSVID( - ctx, - v.JwtSvidParams, - now, - notAfter, - spiffeID, - ) + case *workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams: + key, issuer, err := s.getJWTIssuerKey(ctx) if err != nil { - return nil, trace.Wrap(err, "issuing JWT SVID") + return nil, trace.Wrap(err, "getting JWT issuer key") } - cred.Credential = &workloadidentityv1pb.Credential_JwtSvid{ - JwtSvid: &workloadidentityv1pb.JWTSVIDCredential{ - Jwt: signedJwt, - Jti: jti, - }, + for _, wi := range shouldIssue { + cred, err := s.issueJWTSVID( + ctx, + key, + issuer, + wi, + v.JwtSvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap( + err, + "issuing JWT SVID for workload identity %q", + wi.GetMetadata().GetName(), + ) + } + creds = append(creds, cred) } - evt.JTI = jti default: return nil, trace.BadParameter("credential: unknown type %T", req.GetCredential()) } - if err := s.emitter.EmitAuditEvent(ctx, evt); err != nil { - s.logger.WarnContext( - ctx, - "failed to emit audit event for SVID issuance", - "error", err, - "event", evt, - ) - } - - return &workloadidentityv1pb.IssueWorkloadIdentityResponse{ - Credential: cred, + return &workloadidentityv1pb.IssueWorkloadIdentitiesResponse{ + Credentials: creds, }, nil } @@ -409,6 +356,7 @@ func x509Template( notBefore time.Time, notAfter time.Time, spiffeID spiffeid.ID, + dnsSANs []string, ) *x509.Certificate { return &x509.Certificate{ SerialNumber: serialNumber, @@ -436,7 +384,8 @@ func x509Template( // - The corresponding SPIFFE ID is set as a URI type in the Subject Alternative Name extension // - An X.509 SVID MUST contain exactly one URI SAN, and by extension, exactly one SPIFFE ID. // - An X.509 SVID MAY contain any number of other SAN field types, including DNS SANs. - URIs: []*url.URL{spiffeID.URL()}, + URIs: []*url.URL{spiffeID.URL()}, + DNSNames: dnsSANs, } } @@ -461,53 +410,134 @@ func (s *IssuanceService) getX509CA( return tlsCA, nil } +func baseEvent( + ctx context.Context, + wi *workloadidentityv1pb.WorkloadIdentity, + spiffeID spiffeid.ID, +) *apievents.SPIFFESVIDIssued { + return &apievents.SPIFFESVIDIssued{ + Metadata: apievents.Metadata{ + Type: events.SPIFFESVIDIssuedEvent, + Code: events.SPIFFESVIDIssuedSuccessCode, + }, + UserMetadata: authz.ClientUserMetadata(ctx), + ConnectionMetadata: authz.ConnectionMetadata(ctx), + SPIFFEID: spiffeID.String(), + Hint: wi.GetSpec().GetSpiffe().GetHint(), + WorkloadIdentity: wi.GetMetadata().GetName(), + WorkloadIdentityRevision: wi.GetMetadata().GetRevision(), + } +} + +func calculateTTL( + clock clockwork.Clock, + requestedTTL time.Duration, +) (time.Time, time.Time, time.Time, time.Duration) { + ttl := time.Hour + if requestedTTL != 0 { + ttl = requestedTTL + if ttl > defaultMaxTTL { + ttl = defaultMaxTTL + } + } + now := clock.Now() + notBefore := now.Add(-1 * time.Minute) + notAfter := now.Add(ttl) + return now, notBefore, notAfter, ttl +} + func (s *IssuanceService) issueX509SVID( ctx context.Context, + ca *tlsca.CertAuthority, + wid *workloadidentityv1pb.WorkloadIdentity, params *workloadidentityv1pb.X509SVIDParams, - notBefore time.Time, - notAfter time.Time, - spiffeID spiffeid.ID, -) (_ []byte, _ *big.Int, err error) { + requestedTTL time.Duration, +) (_ *workloadidentityv1pb.Credential, err error) { ctx, span := tracer.Start(ctx, "IssuanceService/issueX509SVID") defer func() { tracing.EndSpan(span, err) }() switch { case params == nil: - return nil, nil, trace.BadParameter("x509_svid_params: is required") + return nil, trace.BadParameter("x509_svid_params: is required") case len(params.PublicKey) == 0: - return nil, nil, trace.BadParameter("x509_svid_params.public_key: is required") + return nil, trace.BadParameter("x509_svid_params.public_key: is required") } - pubKey, err := x509.ParsePKIXPublicKey(params.PublicKey) + spiffeID, err := spiffeid.FromURI(&url.URL{ + Scheme: "spiffe", + Host: s.clusterName, + Path: wid.GetSpec().GetSpiffe().GetId(), + }) if err != nil { - return nil, nil, trace.Wrap(err, "parsing public key") + return nil, trace.Wrap(err, "parsing SPIFFE ID") } + _, notBefore, notAfter, ttl := calculateTTL(s.clock, requestedTTL) - certSerial, err := generateCertSerial() + pubKey, err := x509.ParsePKIXPublicKey(params.PublicKey) if err != nil { - return nil, nil, trace.Wrap(err, "generating certificate serial") + return nil, trace.Wrap(err, "parsing public key") } - template := x509Template(certSerial, notBefore, notAfter, spiffeID) - ca, err := s.getX509CA(ctx) + certSerial, err := generateCertSerial() if err != nil { - return nil, nil, trace.Wrap(err, "fetching CA to sign X509 SVID") + return nil, trace.Wrap(err, "generating certificate serial") } + serialString := serialString(certSerial) + certBytes, err := x509.CreateCertificate( - rand.Reader, template, ca.Cert, pubKey, ca.Signer, + rand.Reader, + x509Template( + certSerial, + notBefore, + notAfter, + spiffeID, + wid.GetSpec().GetSpiffe().GetX509().GetDnsSans(), + ), + ca.Cert, + pubKey, + ca.Signer, ) if err != nil { - return nil, nil, trace.Wrap(err) + return nil, trace.Wrap(err) } - return certBytes, certSerial, nil + evt := baseEvent(ctx, wid, spiffeID) + evt.SVIDType = "x509" + evt.SerialNumber = serialString + evt.DNSSANs = wid.GetSpec().GetSpiffe().GetX509().GetDnsSans() + if err := s.emitter.EmitAuditEvent(ctx, evt); err != nil { + s.logger.WarnContext( + ctx, + "failed to emit audit event for SVID issuance", + "error", err, + "event", evt, + ) + } + + return &workloadidentityv1pb.Credential{ + WorkloadIdentityName: wid.GetMetadata().GetName(), + WorkloadIdentityRevision: wid.GetMetadata().GetRevision(), + + SpiffeId: spiffeID.String(), + Hint: wid.GetSpec().GetSpiffe().GetHint(), + + ExpiresAt: timestamppb.New(notAfter), + Ttl: durationpb.New(ttl), + + Credential: &workloadidentityv1pb.Credential_X509Svid{ + X509Svid: &workloadidentityv1pb.X509SVIDCredential{ + Cert: certBytes, + SerialNumber: serialString, + }, + }, + }, nil } const jtiLength = 16 func (s *IssuanceService) getJWTIssuerKey( ctx context.Context, -) (_ *jwt.Key, err error) { +) (_ *jwt.Key, _ string, err error) { ctx, span := tracer.Start(ctx, "IssuanceService/getJWTIssuerKey") defer func() { tracing.EndSpan(span, err) }() @@ -516,79 +546,173 @@ func (s *IssuanceService) getJWTIssuerKey( DomainName: s.clusterName, }, true) if err != nil { - return nil, trace.Wrap(err, "getting SPIFFE CA") + return nil, "", trace.Wrap(err, "getting SPIFFE CA") } jwtSigner, err := s.keyStore.GetJWTSigner(ctx, ca) if err != nil { - return nil, trace.Wrap(err, "getting JWT signer") + return nil, "", trace.Wrap(err, "getting JWT signer") } jwtKey, err := services.GetJWTSigner( jwtSigner, s.clusterName, s.clock, ) if err != nil { - return nil, trace.Wrap(err, "creating JWT signer") + return nil, "", trace.Wrap(err, "creating JWT signer") + } + + // Determine the public address of the proxy for inclusion in the JWT as + // the issuer for purposes of OIDC compatibility. + issuer, err := oidc.IssuerForCluster(ctx, s.cache, "/workload-identity") + if err != nil { + return nil, "", trace.Wrap(err, "determining issuer URI") } - return jwtKey, nil + + return jwtKey, issuer, nil } func (s *IssuanceService) issueJWTSVID( ctx context.Context, + issuerKey *jwt.Key, + issuerURI string, + wid *workloadidentityv1pb.WorkloadIdentity, params *workloadidentityv1pb.JWTSVIDParams, - now time.Time, - notAfter time.Time, - spiffeID spiffeid.ID, -) (_ string, _ string, err error) { + requestedTTL time.Duration, +) (_ *workloadidentityv1pb.Credential, err error) { ctx, span := tracer.Start(ctx, "IssuanceService/issueJWTSVID") defer func() { tracing.EndSpan(span, err) }() switch { case params == nil: - return "", "", trace.BadParameter("jwt_svid_params: is required") + return nil, trace.BadParameter("jwt_svid_params: is required") case len(params.Audiences) == 0: - return "", "", trace.BadParameter("jwt_svid_params.audiences: at least one audience should be specified") + return nil, trace.BadParameter("jwt_svid_params.audiences: at least one audience should be specified") } - jti, err := utils.CryptoRandomHex(jtiLength) - if err != nil { - return "", "", trace.Wrap(err, "generating JTI") - } - - key, err := s.getJWTIssuerKey(ctx) + spiffeID, err := spiffeid.FromURI(&url.URL{ + Scheme: "spiffe", + Host: s.clusterName, + Path: wid.GetSpec().GetSpiffe().GetId(), + }) if err != nil { - return "", "", trace.Wrap(err, "getting JWT issuer key") + return nil, trace.Wrap(err, "parsing SPIFFE ID") } + now, _, notAfter, ttl := calculateTTL(s.clock, requestedTTL) - // Determine the public address of the proxy for inclusion in the JWT as - // the issuer for purposes of OIDC compatibility. - issuer, err := oidc.IssuerForCluster(ctx, s.cache, "/workload-identity") + jti, err := utils.CryptoRandomHex(jtiLength) if err != nil { - return "", "", trace.Wrap(err, "determining issuer URI") + return nil, trace.Wrap(err, "generating JTI") } - signed, err := key.SignJWTSVID(jwt.SignParamsJWTSVID{ + signed, err := issuerKey.SignJWTSVID(jwt.SignParamsJWTSVID{ Audiences: params.Audiences, SPIFFEID: spiffeID, JTI: jti, - Issuer: issuer, + Issuer: issuerURI, SetIssuedAt: now, SetExpiry: notAfter, }) if err != nil { - return "", "", trace.Wrap(err, "signing jwt") + return nil, trace.Wrap(err, "signing jwt") + } + + evt := baseEvent(ctx, wid, spiffeID) + evt.SVIDType = "jwt" + evt.JTI = jti + if err := s.emitter.EmitAuditEvent(ctx, evt); err != nil { + s.logger.WarnContext( + ctx, + "failed to emit audit event for SVID issuance", + "error", err, + "event", evt, + ) } - return signed, jti, nil + return &workloadidentityv1pb.Credential{ + WorkloadIdentityName: wid.GetMetadata().GetName(), + WorkloadIdentityRevision: wid.GetMetadata().GetRevision(), + + SpiffeId: spiffeID.String(), + Hint: wid.GetSpec().GetSpiffe().GetHint(), + + ExpiresAt: timestamppb.New(notAfter), + Ttl: durationpb.New(ttl), + + Credential: &workloadidentityv1pb.Credential_JwtSvid{ + JwtSvid: &workloadidentityv1pb.JWTSVIDCredential{ + Jwt: signed, + Jti: jti, + }, + }, + }, nil } -func (s *IssuanceService) IssueWorkloadIdentities( +func (s *IssuanceService) getAllWorkloadIdentities( ctx context.Context, - req *workloadidentityv1pb.IssueWorkloadIdentitiesRequest, -) (*workloadidentityv1pb.IssueWorkloadIdentitiesResponse, error) { - // TODO(noah): Coming to a PR near you soon! - return nil, trace.NotImplemented("not implemented") +) ([]*workloadidentityv1pb.WorkloadIdentity, error) { + workloadIdentities := []*workloadidentityv1pb.WorkloadIdentity{} + page := "" + for { + pageItems, nextPage, err := s.cache.ListWorkloadIdentities(ctx, 0, page) + if err != nil { + return nil, trace.Wrap(err) + } + workloadIdentities = append(workloadIdentities, pageItems...) + if nextPage == "" { + break + } + page = nextPage + } + return workloadIdentities, nil +} + +// matchingAndAuthorizedWorkloadIdentities returns the workload identities that +// match the provided labels and the principal has access to. +func (s *IssuanceService) matchingAndAuthorizedWorkloadIdentities( + ctx context.Context, + authCtx *authz.Context, + labels types.Labels, +) ([]*workloadidentityv1pb.WorkloadIdentity, error) { + allWorkloadIdentities, err := s.getAllWorkloadIdentities(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + canAccess := []*workloadidentityv1pb.WorkloadIdentity{} + // Filter out identities user cannot access. + for _, wid := range allWorkloadIdentities { + if err := authCtx.Checker.CheckAccess( + types.Resource153ToResourceWithLabels(wid), + services.AccessState{}, + ); err == nil { + canAccess = append(canAccess, wid) + } + } + + canAccessAndInSearch := []*workloadidentityv1pb.WorkloadIdentity{} + for _, wid := range canAccess { + match, _, err := services.MatchLabelGetter( + labels, types.Resource153ToResourceWithLabels(wid), + ) + if err != nil { + // Maybe log and skip rather than returning an error? + return nil, trace.Wrap(err) + } + if match { + canAccessAndInSearch = append(canAccessAndInSearch, wid) + } + } + + return canAccessAndInSearch, nil +} + +func convertLabels(selectors []*workloadidentityv1pb.LabelSelector) types.Labels { + labels := types.Labels{} + for _, selector := range selectors { + labels[selector.Key] = selector.Values + } + return labels } func serialString(serial *big.Int) string { diff --git a/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go b/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go index 3b7a7b1d85759..3f615c2749c89 100644 --- a/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go +++ b/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go @@ -18,6 +18,7 @@ package workloadidentityv1_test import ( "context" + "crypto" "crypto/x509" "errors" "fmt" @@ -81,13 +82,19 @@ func newTestTLSServer(t testing.TB) (*auth.TestTLSServer, *eventstest.MockRecord return srv, emitter } -func TestIssueWorkloadIdentity(t *testing.T) { - experimentStatus := experiment.Enabled() - defer experiment.SetEnabled(experimentStatus) - experiment.SetEnabled(true) +type issuanceTestPack struct { + srv *auth.TestTLSServer + eventRecorder *eventstest.MockRecorderEmitter + clock clockwork.Clock + issuer string + spiffeX509CAPool *x509.CertPool + spiffeJWTSigner crypto.Signer + spiffeJWTSignerKID string +} + +func newIssuanceTestPack(t *testing.T, ctx context.Context) *issuanceTestPack { srv, eventRecorder := newTestTLSServer(t) - ctx := context.Background() clock := srv.Auth().GetClock() // Upsert a fake proxy to ensure we have a public address to use for the @@ -119,11 +126,35 @@ func TestIssueWorkloadIdentity(t *testing.T) { kid, err := libjwt.KeyID(jwtSigner.Public()) require.NoError(t, err) + return &issuanceTestPack{ + srv: srv, + eventRecorder: eventRecorder, + clock: clock, + issuer: wantIssuer, + spiffeX509CAPool: spiffeX509CAPool, + spiffeJWTSigner: jwtSigner, + spiffeJWTSignerKID: kid, + } +} + +func TestIssueWorkloadIdentity(t *testing.T) { + experimentStatus := experiment.Enabled() + defer experiment.SetEnabled(experimentStatus) + experiment.SetEnabled(true) + + ctx := context.Background() + tp := newIssuanceTestPack(t, ctx) + wildcardAccess, _, err := auth.CreateUserAndRole( - srv.Auth(), + tp.srv.Auth(), "dog", []string{}, - []types.Rule{}, + []types.Rule{ + types.NewRule( + types.KindWorkloadIdentity, + []string{types.VerbRead, types.VerbList}, + ), + }, auth.WithRoleMutator(func(role types.Role) { role.SetWorkloadIdentityLabels(types.Allow, types.Labels{ types.Wildcard: []string{types.Wildcard}, @@ -131,14 +162,19 @@ func TestIssueWorkloadIdentity(t *testing.T) { }), ) require.NoError(t, err) - wilcardAccessClient, err := srv.NewClient(auth.TestUser(wildcardAccess.GetName())) + wilcardAccessClient, err := tp.srv.NewClient(auth.TestUser(wildcardAccess.GetName())) require.NoError(t, err) specificAccess, _, err := auth.CreateUserAndRole( - srv.Auth(), + tp.srv.Auth(), "cat", []string{}, - []types.Rule{}, + []types.Rule{ + types.NewRule( + types.KindWorkloadIdentity, + []string{types.VerbRead, types.VerbList}, + ), + }, auth.WithRoleMutator(func(role types.Role) { role.SetWorkloadIdentityLabels(types.Allow, types.Labels{ "foo": []string{"bar"}, @@ -146,7 +182,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { }), ) require.NoError(t, err) - specificAccessClient, err := srv.NewClient(auth.TestUser(specificAccess.GetName())) + specificAccessClient, err := tp.srv.NewClient(auth.TestUser(specificAccess.GetName())) require.NoError(t, err) // Generate a keypair to generate x509 SVIDs for. @@ -156,7 +192,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { require.NoError(t, err) // Create some WorkloadIdentity resources - full, err := srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + full, err := tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ Kind: types.KindWorkloadIdentity, Version: types.V1, Metadata: &headerv1.Metadata{ @@ -182,6 +218,12 @@ func TestIssueWorkloadIdentity(t *testing.T) { Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ Id: "/example/{{user.name}}/{{ workload.kubernetes.namespace }}/{{ workload.kubernetes.service_account }}", Hint: "Wow - what a lovely hint, {{user.name}}!", + X509: &workloadidentityv1pb.WorkloadIdentitySPIFFEX509{ + DnsSans: []string{ + "example.com", + "{{user.name}}.example.com", + }, + }, }, }, }) @@ -247,28 +289,28 @@ func TestIssueWorkloadIdentity(t *testing.T) { ), )) // Check expiry makes sense - require.WithinDuration(t, clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) // Check the JWT parsed, err := jwt.ParseSigned(cred.GetJwtSvid().GetJwt()) require.NoError(t, err) claims := jwt.Claims{} - err = parsed.Claims(jwtSigner.Public(), &claims) + err = parsed.Claims(tp.spiffeJWTSigner.Public(), &claims) require.NoError(t, err) // Check headers require.Len(t, parsed.Headers, 1) - require.Equal(t, kid, parsed.Headers[0].KeyID) + require.Equal(t, tp.spiffeJWTSignerKID, parsed.Headers[0].KeyID) // Check claims require.Equal(t, wantSPIFFEID, claims.Subject) require.NotEmpty(t, claims.ID) require.Equal(t, jwt.Audience{"example.com", "test.example.com"}, claims.Audience) - require.Equal(t, wantIssuer, claims.Issuer) - require.WithinDuration(t, clock.Now().Add(wantTTL), claims.Expiry.Time(), 5*time.Second) - require.WithinDuration(t, clock.Now(), claims.IssuedAt.Time(), 5*time.Second) + require.Equal(t, tp.issuer, claims.Issuer) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), claims.Expiry.Time(), 5*time.Second) + require.WithinDuration(t, tp.clock.Now(), claims.IssuedAt.Time(), 5*time.Second) // Check audit log event - evt, ok := eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) + evt, ok := tp.eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) require.True(t, ok) require.NotEmpty(t, evt.ConnectionMetadata.RemoteAddr) require.Equal(t, claims.ID, evt.JTI) @@ -337,7 +379,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { ), )) // Check expiry makes sense - require.WithinDuration(t, clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) // Check the X509 cert, err := x509.ParseCertificate(cred.GetX509Svid().GetCert()) @@ -345,11 +387,12 @@ func TestIssueWorkloadIdentity(t *testing.T) { // Check included public key matches require.Equal(t, workloadKey.Public(), cert.PublicKey) // Check cert expiry - require.WithinDuration(t, clock.Now().Add(wantTTL), cert.NotAfter, time.Second) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), cert.NotAfter, time.Second) // Check cert nbf - require.WithinDuration(t, clock.Now().Add(-1*time.Minute), cert.NotBefore, time.Second) + require.WithinDuration(t, tp.clock.Now().Add(-1*time.Minute), cert.NotBefore, time.Second) // Check cert TTL require.Equal(t, cert.NotAfter.Sub(cert.NotBefore), wantTTL+time.Minute) + require.Equal(t, []string{"example.com", "dog.example.com"}, cert.DNSNames) // Check against SPIFFE SPEC // References are to https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md @@ -371,13 +414,13 @@ func TestIssueWorkloadIdentity(t *testing.T) { // Check cert signature is valid _, err = cert.Verify(x509.VerifyOptions{ - Roots: spiffeX509CAPool, - CurrentTime: srv.Auth().GetClock().Now(), + Roots: tp.spiffeX509CAPool, + CurrentTime: tp.srv.Auth().GetClock().Now(), }) require.NoError(t, err) // Check audit log event - evt, ok := eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) + evt, ok := tp.eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) require.True(t, ok) require.NotEmpty(t, evt.ConnectionMetadata.RemoteAddr) require.Equal(t, cred.GetX509Svid().GetSerialNumber(), evt.SerialNumber) @@ -397,6 +440,10 @@ func TestIssueWorkloadIdentity(t *testing.T) { Hint: "Wow - what a lovely hint, dog!", WorkloadIdentity: full.GetMetadata().GetName(), WorkloadIdentityRevision: full.GetMetadata().GetRevision(), + DNSSANs: []string{ + "example.com", + "dog.example.com", + }, }, cmpopts.IgnoreFields( events.SPIFFESVIDIssued{}, @@ -459,7 +506,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - eventRecorder.Reset() + tp.eventRecorder.Reset() c := workloadidentityv1pb.NewWorkloadIdentityIssuanceServiceClient( tt.client.GetConnection(), ) @@ -472,6 +519,316 @@ func TestIssueWorkloadIdentity(t *testing.T) { } } +func TestIssueWorkloadIdentities(t *testing.T) { + experimentStatus := experiment.Enabled() + defer experiment.SetEnabled(experimentStatus) + experiment.SetEnabled(true) + + ctx := context.Background() + tp := newIssuanceTestPack(t, ctx) + + user, _, err := auth.CreateUserAndRole( + tp.srv.Auth(), + "cat", + []string{}, + []types.Rule{ + types.NewRule( + types.KindWorkloadIdentity, + []string{types.VerbRead, types.VerbList}, + ), + }, + auth.WithRoleMutator(func(role types.Role) { + role.SetWorkloadIdentityLabels(types.Allow, types.Labels{ + "access": []string{"yes"}, + }) + }), + ) + require.NoError(t, err) + client, err := tp.srv.NewClient(auth.TestUser(user.GetName())) + require.NoError(t, err) + + // Generate a keypair to generate x509 SVIDs for. + workloadKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) + require.NoError(t, err) + workloadKeyPubBytes, err := x509.MarshalPKIXPublicKey(workloadKey.Public()) + require.NoError(t, err) + + // Create some WorkloadIdentity resources + _, err = tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: "bar-labeled", + Labels: map[string]string{ + "foo": "bar", + "access": "yes", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{ + Allow: []*workloadidentityv1pb.WorkloadIdentityRule{ + { + Conditions: []*workloadidentityv1pb.WorkloadIdentityCondition{ + { + Attribute: "workload.kubernetes.namespace", + Equals: "default", + }, + }, + }, + }, + }, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/example/{{user.name}}/{{ workload.kubernetes.namespace }}/{{ workload.kubernetes.service_account }}", + Hint: "Wow - what a lovely hint, {{user.name}}!", + }, + }, + }) + require.NoError(t, err) + _, err = tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: "buzz-labeled", + Labels: map[string]string{ + "foo": "buzz", + "access": "yes", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{ + Allow: []*workloadidentityv1pb.WorkloadIdentityRule{ + { + Conditions: []*workloadidentityv1pb.WorkloadIdentityCondition{ + { + Attribute: "workload.kubernetes.namespace", + Equals: "default", + }, + }, + }, + }, + }, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/example/{{user.name}}/{{ workload.kubernetes.namespace }}/{{ workload.kubernetes.service_account }}", + Hint: "Wow - what a lovely hint, {{user.name}}!", + }, + }, + }) + require.NoError(t, err) + + _, err = tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: "inaccessible", + Labels: map[string]string{ + "foo": "bar", + "access": "no", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{}, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/example", + }, + }, + }) + require.NoError(t, err) + + // Make enough to trip the "too many" error + for i := 0; i < 12; i++ { + _, err := tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: fmt.Sprintf("%d", i), + Labels: map[string]string{ + "error": "too-many", + "access": "yes", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{}, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/exampled", + }, + }, + }) + require.NoError(t, err) + } + + workloadAttrs := func(f func(attrs *workloadidentityv1pb.WorkloadAttrs)) *workloadidentityv1pb.WorkloadAttrs { + attrs := &workloadidentityv1pb.WorkloadAttrs{ + Kubernetes: &workloadidentityv1pb.WorkloadAttrsKubernetes{ + Attested: true, + Namespace: "default", + PodName: "test", + ServiceAccount: "bar", + }, + } + if f != nil { + f(attrs) + } + return attrs + } + tests := []struct { + name string + client *authclient.Client + req *workloadidentityv1pb.IssueWorkloadIdentitiesRequest + requireErr require.ErrorAssertionFunc + assert func(*testing.T, *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) + }{ + { + name: "jwt svid", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"bar", "buzz"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + workloadIdentitiesIssued := []string{} + for _, cred := range res.Credentials { + workloadIdentitiesIssued = append(workloadIdentitiesIssued, cred.WorkloadIdentityName) + + // Check a credential was actually included and is valid. + parsed, err := jwt.ParseSigned(cred.GetJwtSvid().GetJwt()) + require.NoError(t, err) + claims := jwt.Claims{} + err = parsed.Claims(tp.spiffeJWTSigner.Public(), &claims) + require.NoError(t, err) + } + require.EqualValues(t, []string{"bar-labeled", "buzz-labeled"}, workloadIdentitiesIssued) + }, + }, + { + name: "x509 svid", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"bar", "buzz"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_X509SvidParams{ + X509SvidParams: &workloadidentityv1pb.X509SVIDParams{ + PublicKey: workloadKeyPubBytes, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + workloadIdentitiesIssued := []string{} + for _, cred := range res.Credentials { + workloadIdentitiesIssued = append(workloadIdentitiesIssued, cred.WorkloadIdentityName) + // Check X509 cert actually included and signed. + cert, err := x509.ParseCertificate(cred.GetX509Svid().GetCert()) + require.NoError(t, err) + // Check included public key matches + require.Equal(t, workloadKey.Public(), cert.PublicKey) + _, err = cert.Verify(x509.VerifyOptions{ + Roots: tp.spiffeX509CAPool, + CurrentTime: tp.srv.Auth().GetClock().Now(), + }) + require.NoError(t, err) + } + require.EqualValues(t, []string{"bar-labeled", "buzz-labeled"}, workloadIdentitiesIssued) + }, + }, + { + name: "rules prevent issuing", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"bar", "buzz"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(func(attrs *workloadidentityv1pb.WorkloadAttrs) { + attrs.Kubernetes.Namespace = "not-default" + }), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + require.Empty(t, res.Credentials) + }, + }, + { + name: "no matching labels", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"muahah"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + require.Empty(t, res.Credentials) + }, + }, + { + name: "too many to issue", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "error", + Values: []string{"too-many"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: func(t require.TestingT, err error, i ...interface{}) { + require.ErrorContains(t, err, "number of identities that would be issued exceeds maximum permitted (max = 10), use more specific labels") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tp.eventRecorder.Reset() + c := workloadidentityv1pb.NewWorkloadIdentityIssuanceServiceClient( + tt.client.GetConnection(), + ) + res, err := c.IssueWorkloadIdentities(ctx, tt.req) + tt.requireErr(t, err) + if tt.assert != nil { + tt.assert(t, res) + } + }) + } +} + func TestResourceService_CreateWorkloadIdentity(t *testing.T) { t.Parallel() srv, eventRecorder := newTestTLSServer(t) diff --git a/lib/auth/methods.go b/lib/auth/methods.go index 0e7682370490f..ab485dfa96f9d 100644 --- a/lib/auth/methods.go +++ b/lib/auth/methods.go @@ -22,7 +22,6 @@ import ( "bytes" "context" "errors" - "log/slog" "net" "time" @@ -71,16 +70,19 @@ func (a *Server) authenticateUserLogin(ctx context.Context, req authclient.Authe clientMetadata: req.ClientMetadata, authErr: err, }); err != nil { - log.WithError(err).Warn("Failed to emit login event") + a.logger.WarnContext(ctx, "Failed to emit login event", "error", err) } return nil, nil, trace.Wrap(err) } switch { case username != "" && actualUsername != "" && username != actualUsername: - log.Warnf("Authenticate user mismatch (%q vs %q). Using request user (%q)", username, actualUsername, username) + a.logger.WarnContext(ctx, "Authenticate user mismatch, using request user", + "username", username, + "request_user", actualUsername, + ) case username == "" && actualUsername != "": - log.Debugf("User %q authenticated via passwordless", actualUsername) + a.logger.DebugContext(ctx, "User authenticated via passwordless", "username", actualUsername) username = actualUsername } @@ -123,7 +125,7 @@ func (a *Server) authenticateUserLogin(ctx context.Context, req authclient.Authe checker: checker, authErr: err, }); err != nil { - log.WithError(err).Warn("Failed to emit login event") + a.logger.WarnContext(ctx, "Failed to emit login event", "error", err) } return nil, nil, trace.Wrap(err) } @@ -135,7 +137,7 @@ func (a *Server) authenticateUserLogin(ctx context.Context, req authclient.Authe mfaDevice: mfaDev, checker: checker, }); err != nil { - log.WithError(err).Warn("Failed to emit login event") + a.logger.WarnContext(ctx, "Failed to emit login event", "error", err) } return userState, checker, trace.Wrap(err) @@ -303,7 +305,7 @@ func (a *Server) authenticateUserInternal( if req.HeadlessAuthenticationID != "" { mfaDev, err = a.authenticateHeadless(ctx, req) if err != nil { - slog.DebugContext(ctx, "Headless authenticate failed while waiting for approval", + a.logger.DebugContext(ctx, "Headless authenticate failed while waiting for approval", "user", user, "error", err, ) @@ -330,7 +332,7 @@ func (a *Server) authenticateUserInternal( case err != nil: return nil, "", trace.Wrap(err) case u.GetUserType() != types.UserTypeLocal: - slog.WarnContext(ctx, "Non-local user attempted local authentication", + a.logger.WarnContext(ctx, "Non-local user attempted local authentication", "user", user, "user_type", u.GetUserType(), ) @@ -381,7 +383,7 @@ func (a *Server) authenticateUserInternal( }) switch { case err != nil: - slog.DebugContext(ctx, "User failed to authenticate.", + a.logger.DebugContext(ctx, "User failed to authenticate", "user", user, "error", err, ) @@ -391,7 +393,7 @@ func (a *Server) authenticateUserInternal( return nil, "", trace.Wrap(authErr) case mfaDev == nil: - slog.DebugContext(ctx, "MFA authentication returned nil device.", + a.logger.DebugContext(ctx, "MFA authentication returned nil device", "webauthn", req.Webauthn != nil, "totp", req.OTP != nil, "headless", req.HeadlessAuthenticationID != "", @@ -420,7 +422,7 @@ func (a *Server) authenticateUserInternal( // Some form of MFA is required but none provided. Either client is // buggy (didn't send MFA response) or someone is trying to bypass // MFA. - slog.WarnContext(ctx, "MFA bypass attempt, access denied.", "user", user) + a.logger.WarnContext(ctx, "MFA bypass attempt, access denied", "user", user) return nil, "", trace.AccessDenied("missing second factor") case authPreference.IsSecondFactorEnabled(): // 2FA is optional. Make sure that a user does not have MFA devices @@ -430,7 +432,7 @@ func (a *Server) authenticateUserInternal( return nil, "", trace.Wrap(err) } if len(devs) != 0 { - slog.WarnContext(ctx, "MFA bypass attempt, access denied.", "user", user) + a.logger.WarnContext(ctx, "MFA bypass attempt, access denied", "user", user) return nil, "", trace.AccessDenied("missing second factor authentication") } default: @@ -444,7 +446,7 @@ func (a *Server) authenticateUserInternal( } // provide obscure message on purpose, while logging the real // error server side - slog.DebugContext(ctx, "User failed to authenticate.", + a.logger.DebugContext(ctx, "User failed to authenticate", "user", user, "error", err, ) @@ -467,7 +469,7 @@ func (a *Server) authenticatePasswordless(ctx context.Context, req authclient.Au case errors.Is(err, types.ErrPassswordlessLoginBySSOUser): return nil, "", trace.Wrap(err) case err != nil: - log.Debugf("Passwordless authentication failed: %v", err) + a.logger.DebugContext(ctx, "Passwordless authentication failed", "error", err) return nil, "", trace.Wrap(authenticateWebauthnError) } @@ -475,7 +477,10 @@ func (a *Server) authenticatePasswordless(ctx context.Context, req authclient.Au // acquire the user lock beforehand (or at all on failures!) // We do grab it here so successful logins go through the regular process. if err := a.WithUserLock(ctx, mfaData.User, func() error { return nil }); err != nil { - log.Debugf("WithUserLock for user %q failed during passwordless authentication: %v", mfaData.User, err) + a.logger.DebugContext(ctx, "WithUserLock failed during passwordless authentication", + "user", mfaData.User, + "error", err, + ) return nil, mfaData.User, trace.Wrap(authenticateWebauthnError) } @@ -487,7 +492,7 @@ func (a *Server) authenticateHeadless(ctx context.Context, req authclient.Authen defer func() { if err != nil { if err := a.DeleteHeadlessAuthentication(a.CloseContext(), req.Username, req.HeadlessAuthenticationID); err != nil && !trace.IsNotFound(err) { - log.Debugf("Failed to delete headless authentication: %v", err) + a.logger.DebugContext(ctx, "Failed to delete headless authentication", "error", err) } } }() @@ -773,7 +778,7 @@ func (a *Server) emitNoLocalAuthEvent(username string) { Error: noLocalAuth, }, }); err != nil { - log.WithError(err).Warn("Failed to emit no local auth event.") + a.logger.WarnContext(a.closeCtx, "Failed to emit no local auth event", "error", err) } } @@ -794,7 +799,7 @@ func getErrorByTraceField(err error) error { ok := errors.As(err, &traceErr) switch { case !ok: - log.WithError(err).Warn("Unexpected error type, wanted TraceError") + logger.WarnContext(context.Background(), "Unexpected error type, wanted TraceError", "error", err) return trace.AccessDenied("an error has occurred") case traceErr.GetFields()[ErrFieldKeyUserMaxedAttempts] != nil: return trace.AccessDenied(MaxFailedAttemptsErrMsg) diff --git a/lib/auth/middleware.go b/lib/auth/middleware.go index 8e21c071eb4a5..4d5b913f9db56 100644 --- a/lib/auth/middleware.go +++ b/lib/auth/middleware.go @@ -24,6 +24,7 @@ import ( "crypto/x509" "encoding/json" "fmt" + "log/slog" "net" "net/http" "os" @@ -36,7 +37,6 @@ import ( "github.com/gravitational/trace" grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "golang.org/x/net/http2" "google.golang.org/grpc" @@ -57,6 +57,7 @@ import ( "github.com/gravitational/teleport/lib/observability/metrics" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const ( @@ -139,7 +140,7 @@ type TLSServer struct { // cfg is TLS server configuration used for auth server cfg TLSServerConfig // log is TLS server logging entry - log *logrus.Entry + log *slog.Logger // mux is a listener that multiplexes HTTP/2 and HTTP/1.1 // on different listeners mux *multiplexer.TLSListener @@ -215,9 +216,7 @@ func NewTLSServer(ctx context.Context, cfg TLSServerConfig) (*TLSServer, error) return authz.ContextWithConn(ctx, c) }, }, - log: logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: cfg.Component, - }), + log: slog.With(teleport.ComponentKey, cfg.Component), } tlsConfig := cfg.TLS.Clone() @@ -306,7 +305,7 @@ func (t *TLSServer) Serve() error { errC := make(chan error, 2) go func() { err := t.mux.Serve() - t.log.WithError(err).Warningf("Mux serve failed.") + t.log.WarnContext(context.Background(), "Mux serve failed", "error", err) }() go func() { errC <- t.httpServer.Serve(t.mux.HTTP()) @@ -372,7 +371,9 @@ func getCustomRate(endpoint string) *limiter.RateSet { rates := limiter.NewRateSet() // This limit means: 1 request per minute with bursts up to 10 requests. if err := rates.Add(time.Minute, 1, 10); err != nil { - log.WithError(err).Debugf("Failed to define a custom rate for rpc method %q, using default rate", endpoint) + logger.DebugContext(context.Background(), "Failed to define a custom rate for rpc method, using default rate", + "error", err, + "rpc_method", endpoint) return nil } return rates @@ -383,7 +384,10 @@ func getCustomRate(endpoint string) *limiter.RateSet { const burst = defaults.LimiterBurst rates := limiter.NewRateSet() if err := rates.Add(period, average, burst); err != nil { - log.WithError(err).Debugf("Failed to define a custom rate for rpc method %q, using default rate", endpoint) + logger.DebugContext(context.Background(), "Failed to define a custom rate for rpc method, using default rate", + "error", err, + "rpc_method", endpoint, + ) return nil } return rates @@ -406,24 +410,29 @@ func (a *Middleware) ValidateClientVersion(ctx context.Context, info IdentityInf ua := metadata.UserAgentFromContext(ctx) - logger := log.WithFields(logrus.Fields{"user_agent": ua, "identity": info.IdentityGetter.GetIdentity().Username, "version": clientVersionString, "addr": info.Conn.RemoteAddr().String()}) + logger := slog.With( + "user_agent", ua, + "identity", info.IdentityGetter.GetIdentity().Username, + "version", clientVersionString, + "addr", logutils.StringerAttr(info.Conn.RemoteAddr()), + ) clientVersion, err := semver.NewVersion(clientVersionString) if err != nil { - logger.WithError(err).Warn("Failed to determine client version") + logger.WarnContext(ctx, "Failed to determine client version", "error", err) a.displayRejectedClientAlert(ctx, clientVersionString, info.Conn.RemoteAddr(), ua, info.IdentityGetter) if err := info.Conn.Close(); err != nil { - logger.WithError(err).Warn("Failed to close client connection") + logger.WarnContext(ctx, "Failed to close client connection", "error", err) } return trace.AccessDenied("client version is unsupported") } if clientVersion.LessThan(*a.OldestSupportedVersion) { - logger.Info("Terminating connection of client using unsupported version") + logger.InfoContext(ctx, "Terminating connection of client using unsupported version") a.displayRejectedClientAlert(ctx, clientVersionString, info.Conn.RemoteAddr(), ua, info.IdentityGetter) if err := info.Conn.Close(); err != nil { - logger.WithError(err).Warn("Failed to close client connection") + logger.WarnContext(ctx, "Failed to close client connection", "error", err) } return trace.AccessDenied("client version is unsupported") @@ -486,12 +495,12 @@ func (a *Middleware) displayRejectedClientAlert(ctx context.Context, clientVersi types.WithAlertLabel(types.AlertVerbPermit, fmt.Sprintf("%s:%s", types.KindToken, types.VerbCreate)), ) if err != nil { - log.WithError(err).Warn("failed to create rejected-unsupported-connection alert") + logger.WarnContext(ctx, "failed to create rejected-unsupported-connection alert", "error", err) return } if err := a.AlertCreator(ctx, alert); err != nil { - log.WithError(err).Warn("failed to persist rejected-unsupported-connection alert") + logger.WarnContext(ctx, "failed to persist rejected-unsupported-connection alert", "error", err) return } } @@ -656,7 +665,7 @@ func (a *Middleware) GetUser(connState tls.ConnectionState) (authz.IdentityGette if certClusterName == "" { certClusterName, err = tlsca.ClusterName(clientCert.Issuer) if err != nil { - log.Warnf("Failed to parse client certificate %v.", err) + logger.WarnContext(context.Background(), "Failed to parse client certificate", "error", err) return nil, trace.AccessDenied("access denied: invalid client certificate") } identity.TeleportCluster = certClusterName @@ -667,8 +676,11 @@ func (a *Middleware) GetUser(connState tls.ConnectionState) (authz.IdentityGette // against auth server. Later on we can extend more // advanced cert usage, but for now this is the safest option. if len(identity.Usage) != 0 && !slices.Equal(a.AcceptedUsage, identity.Usage) { - log.Warningf("Restricted certificate of user %q with usage %v rejected while accessing the auth endpoint with acceptable usage %v.", - identity.Username, identity.Usage, a.AcceptedUsage) + logger.WarnContext(context.Background(), "Restricted certificate rejected while accessing the auth endpoint", + "user", identity.Username, + "cert_usage", identity.Usage, + "acceptable_usage", a.AcceptedUsage, + ) return nil, trace.AccessDenied("access denied: invalid client certificate") } @@ -734,7 +746,7 @@ func extractAdditionalSystemRoles(roles []string) types.SystemRoles { if err != nil { // ignore unknown system roles rather than rejecting them, since new unknown system // roles may be present on certs if we rolled back from a newer version. - log.Warnf("Ignoring unknown system role: %q", role) + logger.WarnContext(context.Background(), "Ignoring unknown system role", "unknown_role", role) continue } systemRoles = append(systemRoles, systemRole) diff --git a/lib/auth/oidc.go b/lib/auth/oidc.go index fd138373f2540..00557fddd545c 100644 --- a/lib/auth/oidc.go +++ b/lib/auth/oidc.go @@ -55,7 +55,7 @@ func (a *Server) UpsertOIDCConnector(ctx context.Context, connector types.OIDCCo Name: connector.GetName(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit OIDC connector create event.") + a.logger.WarnContext(ctx, "Failed to emit OIDC connector create event", "error", err) } return upserted, nil @@ -77,7 +77,7 @@ func (a *Server) UpdateOIDCConnector(ctx context.Context, connector types.OIDCCo Name: connector.GetName(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit OIDC connector update event.") + a.logger.WarnContext(ctx, "Failed to emit OIDC connector update event", "error", err) } return updated, nil @@ -99,7 +99,7 @@ func (a *Server) CreateOIDCConnector(ctx context.Context, connector types.OIDCCo Name: connector.GetName(), }, }); err != nil { - log.WithError(err).Warn("Failed to emit OIDC connector create event.") + a.logger.WarnContext(ctx, "Failed to emit OIDC connector create event", "error", err) } return created, nil @@ -120,7 +120,7 @@ func (a *Server) DeleteOIDCConnector(ctx context.Context, connectorName string) Name: connectorName, }, }); err != nil { - log.WithError(err).Warn("Failed to emit OIDC connector delete event.") + a.logger.WarnContext(ctx, "Failed to emit OIDC connector delete event", "error", err) } return nil } diff --git a/lib/auth/password.go b/lib/auth/password.go index f2045ddd2ef0c..02523ce941d28 100644 --- a/lib/auth/password.go +++ b/lib/auth/password.go @@ -40,6 +40,7 @@ import ( "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/services" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // This is bcrypt hash for password "barbaz". @@ -164,7 +165,7 @@ func (a *Server) ChangePassword(ctx context.Context, req *proto.ChangePasswordRe UserMetadata: authz.ClientUserMetadataWithUser(ctx, user), ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit password change event.") + a.logger.WarnContext(ctx, "Failed to emit password change event", "error", err) } return nil } @@ -182,12 +183,12 @@ func (a *Server) checkPasswordWOToken(ctx context.Context, user string, password userFound := true if trace.IsNotFound(err) { userFound = false - log.Debugf("Password for username %q not found, using fake hash to mitigate timing attacks.", user) + a.logger.DebugContext(ctx, "Password for username not found, using fake hash to mitigate timing attacks", "user", user) hash = fakePasswordHash } if err = bcrypt.CompareHashAndPassword(hash, password); err != nil { - log.Debugf("Password for %q does not match", user) + a.logger.DebugContext(ctx, "Password for user does not match", "user", user) return trace.BadParameter(errMsg) } @@ -210,10 +211,10 @@ func (a *Server) checkPasswordWOToken(ctx context.Context, user string, password }) if err != nil { // Don't let the password state flag change fail the entire operation. - log. - WithError(err). - WithField("user", user). - Warn("Failed to set password state") + a.logger.WarnContext(ctx, "Failed to set password state", + "error", err, + "user", user, + ) } return nil @@ -230,7 +231,7 @@ func (a *Server) checkPassword(ctx context.Context, user string, password []byte return nil, trace.Wrap(err) } - mfaDev, err := a.checkOTP(user, otpToken) + mfaDev, err := a.checkOTP(ctx, user, otpToken) if err != nil { return nil, trace.Wrap(err) } @@ -238,7 +239,7 @@ func (a *Server) checkPassword(ctx context.Context, user string, password []byte } // checkOTP checks if the OTP token is valid. -func (a *Server) checkOTP(user string, otpToken string) (*types.MFADevice, error) { +func (a *Server) checkOTP(ctx context.Context, user string, otpToken string) (*types.MFADevice, error) { // get the previously used token to mitigate token replay attacks usedToken, err := a.GetUsedTOTPToken(user) if err != nil { @@ -249,7 +250,6 @@ func (a *Server) checkOTP(user string, otpToken string) (*types.MFADevice, error return nil, trace.BadParameter("previously used totp token") } - ctx := context.TODO() devs, err := a.Services.GetMFADevices(ctx, user, true) if err != nil { return nil, trace.Wrap(err) @@ -262,10 +262,10 @@ func (a *Server) checkOTP(user string, otpToken string) (*types.MFADevice, error } if err := a.checkTOTP(ctx, user, otpToken, dev); err != nil { - log. - WithError(err). - WithField("device", dev.GetName()). - Debug("TOTP device failed verification. This is expected if the user has multiple TOTP devices.") + a.logger.DebugContext(ctx, "TOTP device failed verification, this is expected if the user has multiple TOTP devices", + "error", err, + "device", dev.GetName(), + ) continue } return dev, nil @@ -414,7 +414,9 @@ func (a *Server) changeUserSecondFactor(ctx context.Context, req *proto.ChangeUs // Fallback to something reasonable while letting verifyMFARespAndAddDevice // worry about the "unknown" response type. deviceName = "mfa" - log.Warnf("Unexpected MFA register response type, setting device name to %q: %T", deviceName, req.GetNewMFARegisterResponse().Response) + a.logger.WarnContext(ctx, "Unexpected MFA register response type, setting device name to mfa", + "response_type", logutils.TypeAttr(req.GetNewMFARegisterResponse().Response), + ) } } diff --git a/lib/auth/presence/presencev1/service.go b/lib/auth/presence/presencev1/service.go index c83e36fc453e3..3eb28af2e740e 100644 --- a/lib/auth/presence/presencev1/service.go +++ b/lib/auth/presence/presencev1/service.go @@ -20,10 +20,10 @@ package presencev1 import ( "context" + "log/slog" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/emptypb" "github.com/gravitational/teleport" @@ -34,6 +34,7 @@ import ( "github.com/gravitational/teleport/lib/services" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // Backend is the subset of the backend resources that the Service modifies. @@ -65,7 +66,7 @@ type ServiceConfig struct { AuthServer AuthServer Backend Backend Cache Cache - Logger logrus.FieldLogger + Logger *slog.Logger Emitter apievents.Emitter Reporter usagereporter.UsageReporter Clock clockwork.Clock @@ -79,7 +80,7 @@ type Service struct { authServer AuthServer backend Backend cache Cache - logger logrus.FieldLogger + logger *slog.Logger emitter apievents.Emitter reporter usagereporter.UsageReporter clock clockwork.Clock @@ -103,7 +104,7 @@ func NewService(cfg ServiceConfig) (*Service, error) { } if cfg.Logger == nil { - cfg.Logger = logrus.WithField(teleport.ComponentKey, "presence.service") + cfg.Logger = slog.With(teleport.ComponentKey, "presence.service") } if cfg.Clock == nil { cfg.Clock = clockwork.NewRealClock() @@ -149,7 +150,11 @@ func (s *Service) GetRemoteCluster( v3, ok := rc.(*types.RemoteClusterV3) if !ok { - s.logger.Warnf("expected type RemoteClusterV3, got %T for %q", rc, rc.GetName()) + s.logger.WarnContext(ctx, "unexpected remote cluster type", + "got_type", logutils.TypeAttr(rc), + "expected_type", "RemoteClusterV3", + "remote_cluster", rc.GetName(), + ) return nil, trace.BadParameter("encountered unexpected remote cluster type") } @@ -180,7 +185,11 @@ func (s *Service) ListRemoteClusters( for _, rc := range page { v3, ok := rc.(*types.RemoteClusterV3) if !ok { - s.logger.Warnf("expected type RemoteClusterV3, got %T for %q", rc, rc.GetName()) + s.logger.WarnContext(ctx, "unexpected remote cluster type", + "got_type", logutils.TypeAttr(rc), + "expected_type", "RemoteClusterV3", + "remote_cluster", rc.GetName(), + ) continue } concretePage = append(concretePage, v3) @@ -234,7 +243,11 @@ func (s *Service) UpdateRemoteCluster( } v3, ok := rc.(*types.RemoteClusterV3) if !ok { - s.logger.Warnf("expected type RemoteClusterV3, got %T for user %q", rc, rc.GetName()) + s.logger.WarnContext(ctx, "unexpected remote cluster type", + "got_type", logutils.TypeAttr(rc), + "expected_type", "RemoteClusterV3", + "remote_cluster", rc.GetName(), + ) return nil, trace.BadParameter("encountered unexpected remote cluster type") } return v3, nil @@ -271,7 +284,11 @@ func (s *Service) UpdateRemoteCluster( } v3, ok := rc.(*types.RemoteClusterV3) if !ok { - s.logger.Warnf("expected type RemoteClusterV3, got %T for user %q", rc, rc.GetName()) + s.logger.WarnContext(ctx, "unexpected remote cluster type", + "got_type", logutils.TypeAttr(rc), + "expected_type", "RemoteClusterV3", + "remote_cluster", rc.GetName(), + ) return nil, trace.BadParameter("encountered unexpected remote cluster type") } @@ -330,7 +347,11 @@ func (s *Service) ListReverseTunnels( for _, rc := range page { v3, ok := rc.(*types.ReverseTunnelV2) if !ok { - s.logger.Warnf("expected type ReverseTunnelV2, got %T for %q", rc, rc.GetName()) + s.logger.WarnContext(ctx, "unexpected reverse tunnel type", + "got_type", logutils.TypeAttr(rc), + "expected_type", "ReverseTunnelV2", + "reverse_tunnel", rc.GetName(), + ) continue } concretePage = append(concretePage, v3) diff --git a/lib/auth/rotate.go b/lib/auth/rotate.go index 5110909d53654..7418cff56b780 100644 --- a/lib/auth/rotate.go +++ b/lib/auth/rotate.go @@ -27,7 +27,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "github.com/gravitational/teleport/api/types" @@ -165,9 +164,14 @@ func (a *Server) RotateCertAuthority(ctx context.Context, req types.RotateReques rotation := rotated.GetRotation() switch rotation.State { case types.RotationStateInProgress: - log.WithFields(logrus.Fields{"type": req.Type}).Infof("Updated rotation state, set current phase to: %q.", rotation.Phase) + a.logger.InfoContext(ctx, "Updated rotation state", + "current_phase", rotation.Phase, + "ca_type", req.Type, + ) case types.RotationStateStandby: - log.WithFields(logrus.Fields{"type": req.Type}).Infof("Updated and completed rotation.") + a.logger.InfoContext(ctx, "Updated and completed rotation", + "ca_type", req.Type, + ) } return nil @@ -222,7 +226,7 @@ func (a *Server) autoRotate(ctx context.Context, ca types.CertAuthority) error { if rotation.State != types.RotationStateInProgress { return nil } - logger := log.WithFields(logrus.Fields{"type": ca.GetType()}) + logger := a.logger.With("type", ca.GetType()) var req *rotationReq switch rotation.Phase { case types.RotationPhaseInit: @@ -264,7 +268,7 @@ func (a *Server) autoRotate(ctx context.Context, ca types.CertAuthority) error { default: return trace.BadParameter("phase is not supported: %q", rotation.Phase) } - logger.Infof("Setting rotation phase to %q", req.targetPhase) + logger.InfoContext(ctx, "Updating rotation phase", "target_phase", req.targetPhase) rotated, err := a.processRotationRequest(ctx, *req) if err != nil { return trace.Wrap(err) @@ -272,7 +276,7 @@ func (a *Server) autoRotate(ctx context.Context, ca types.CertAuthority) error { if _, err := a.UpdateCertAuthority(ctx, rotated); err != nil { return trace.Wrap(err) } - logger.Infof("Cert authority rotation request is completed") + logger.InfoContext(ctx, "Cert authority rotation request is completed") return nil } @@ -372,7 +376,7 @@ func (a *Server) startNewRotation(ctx context.Context, req rotationReq, ca types // generate keys and certificates: if len(req.privateKey) != 0 { - log.Infof("Generating CA, using pregenerated test private key.") + a.logger.InfoContext(ctx, "Generating CA, using pregenerated test private key") signer, err := keys.ParsePrivateKey(req.privateKey) if err != nil { diff --git a/lib/auth/saml.go b/lib/auth/saml.go index 4c11479967156..cf2dfe40ae039 100644 --- a/lib/auth/saml.go +++ b/lib/auth/saml.go @@ -87,7 +87,7 @@ func (a *Server) UpsertSAMLConnector(ctx context.Context, connector types.SAMLCo }, Connector: upsertedConnector, }); err != nil { - log.WithError(err).Warn("Failed to emit SAML connector create event.") + a.logger.WarnContext(ctx, "Failed to emit SAML connector create event", "error", err) } return upserted, nil @@ -133,7 +133,7 @@ func (a *Server) UpdateSAMLConnector(ctx context.Context, connector types.SAMLCo }, Connector: updatedConnector, }); err != nil { - log.WithError(err).Warn("Failed to emit SAML connector update event.") + a.logger.WarnContext(ctx, "Failed to emit SAML connector update event", "error", err) } return updated, nil @@ -175,7 +175,7 @@ func (a *Server) CreateSAMLConnector(ctx context.Context, connector types.SAMLCo }, Connector: newConnector, }); err != nil { - log.WithError(err).Warn("Failed to emit SAML connector create event.") + a.logger.WarnContext(ctx, "Failed to emit SAML connector create event", "error", err) } return created, nil @@ -196,7 +196,7 @@ func (a *Server) DeleteSAMLConnector(ctx context.Context, connectorID string) er Name: connectorID, }, }); err != nil { - log.WithError(err).Warn("Failed to emit SAML connector delete event.") + a.logger.WarnContext(ctx, "Failed to emit SAML connector delete event", "error", err) } return nil diff --git a/lib/auth/sessions.go b/lib/auth/sessions.go index 9947279aad248..7f202bd9110b3 100644 --- a/lib/auth/sessions.go +++ b/lib/auth/sessions.go @@ -25,7 +25,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "github.com/gravitational/teleport" @@ -154,7 +153,7 @@ func (a *Server) augmentSessionForDeviceTrust( }) switch { case err != nil: - log.WithError(err).Warn("Failed to create DeviceWebToken for user") + a.logger.WarnContext(ctx, "Failed to create DeviceWebToken for user", "error", err) case webToken != nil: // May be nil even if err==nil. session.SetDeviceWebToken(&types.DeviceWebToken{ Id: webToken.Id, @@ -210,7 +209,7 @@ func (a *Server) newWebSession( } if req.LoginIP == "" { // TODO(antonam): consider turning this into error after all use cases are covered (before v14.0 testplan) - log.Debug("Creating new web session without login IP specified.") + a.logger.DebugContext(ctx, "Creating new web session without login IP specified") } clusterName, err := a.GetClusterName() @@ -351,17 +350,15 @@ func (a *Server) newWebSession( if tdr, err := a.calculateTrustedDeviceMode(ctx, func() ([]types.Role, error) { return checker.Roles(), nil }); err != nil { - log. - WithError(err). - Warn("Failed to calculate trusted device mode for session") + a.logger.WarnContext(ctx, "Failed to calculate trusted device mode for session", "error", err) } else { sess.SetTrustedDeviceRequirement(tdr) if tdr != types.TrustedDeviceRequirement_TRUSTED_DEVICE_REQUIREMENT_UNSPECIFIED { - log.WithFields(logrus.Fields{ - "user": req.User, - "trusted_device_requirement": tdr, - }).Debug("Calculated trusted device requirement for session") + a.logger.DebugContext(ctx, "Calculated trusted device requirement for session", + "user", req.User, + "trusted_device_requirement", tdr, + ) } } @@ -597,7 +594,7 @@ func (a *Server) CreateAppSessionFromReq(ctx context.Context, req NewAppSessionR if err = a.UpsertAppSession(ctx, session); err != nil { return nil, trace.Wrap(err) } - log.Debugf("Generated application web session for %v with TTL %v.", req.User, req.SessionTTL) + a.logger.DebugContext(ctx, "Generated application web session", "user", req.User, "ttl", req.SessionTTL) UserLoginCount.Inc() // Extract the identity of the user from the certificate, this will include metadata from any actively assumed access requests. @@ -643,7 +640,7 @@ func (a *Server) CreateAppSessionFromReq(ctx context.Context, req NewAppSessionR }, }) if err != nil { - log.WithError(err).Warn("Failed to emit app session start event") + a.logger.WarnContext(ctx, "Failed to emit app session start event", "error", err) } return session, nil @@ -784,7 +781,7 @@ func (a *Server) CreateSnowflakeSession(ctx context.Context, req types.CreateSno if err = a.UpsertSnowflakeSession(ctx, session); err != nil { return nil, trace.Wrap(err) } - log.Debugf("Generated Snowflake web session for %v with TTL %v.", req.Username, ttl) + a.logger.DebugContext(ctx, "Generated Snowflake web session", "user", req.Username, "ttl", ttl) return session, nil } @@ -808,7 +805,7 @@ func (a *Server) CreateSAMLIdPSession(ctx context.Context, req types.CreateSAMLI if err = a.UpsertSAMLIdPSession(ctx, session); err != nil { return nil, trace.Wrap(err) } - log.Debugf("Generated SAML IdP web session for %v.", req.Username) + a.logger.DebugContext(ctx, "Generated SAML IdP web session", "user", req.Username) return session, nil } diff --git a/lib/auth/sso_diag_context.go b/lib/auth/sso_diag_context.go index a2699886ec70f..4db3d77d32efa 100644 --- a/lib/auth/sso_diag_context.go +++ b/lib/auth/sso_diag_context.go @@ -56,7 +56,10 @@ func (c *SSODiagContext) WriteToBackend(ctx context.Context) { if c.Info.TestFlow { err := c.DiagService.CreateSSODiagnosticInfo(ctx, c.AuthKind, c.RequestID, c.Info) if err != nil { - log.WithError(err).WithField("requestID", c.RequestID).Warn("failed to write SSO diag info data") + logger.WarnContext(ctx, "failed to write SSO diag info data", + "error", err, + "request_id", c.RequestID, + ) } } } diff --git a/lib/auth/touchid/api.go b/lib/auth/touchid/api.go index 7769b6dd5d2f7..0d96a61f05297 100644 --- a/lib/auth/touchid/api.go +++ b/lib/auth/touchid/api.go @@ -20,6 +20,7 @@ package touchid import ( "bytes" + "context" "crypto/ecdsa" "crypto/sha256" "encoding/base64" @@ -38,10 +39,11 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/protocol/webauthncose" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" + "github.com/gravitational/teleport" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/darwin" + logutils "github.com/gravitational/teleport/lib/utils/log" ) var ( @@ -52,6 +54,8 @@ var ( PromptPlatformMessage = "Using platform authenticator, follow the OS prompt" // PromptWriter is the writer used for prompt messages. PromptWriter io.Writer = os.Stderr + + logger = logutils.NewPackageLogger(teleport.ComponentKey, "TouchID") ) func promptPlatform() { @@ -167,7 +171,7 @@ func IsAvailable() bool { var err error cachedDiag, err = Diag() if err != nil { - log.WithError(err).Warn("Touch ID self-diagnostics failed") + logger.WarnContext(context.Background(), "self-diagnostics failed", "error", err) return false } } @@ -356,7 +360,7 @@ func HasCredentials(rpid, user string) bool { } creds, err := native.FindCredentials(rpid, user) if err != nil { - log.WithError(err).Debug("Touch ID: Could not find credentials") + logger.DebugContext(context.Background(), "Could not find credentials", "error", err) return false } return len(creds) > 0 @@ -494,7 +498,7 @@ func Login(origin, user string, assertion *wantypes.CredentialAssertion, picker if err != nil { return nil, "", trace.Wrap(err) } - log.Debugf("Touch ID: using credential %q", cred.CredentialID) + logger.DebugContext(context.Background(), "using credential", "credential_id", cred.CredentialID) attData, err := makeAttestationData(protocol.AssertCeremony, origin, rpID, assertion.Response.Challenge, nil /* cred */) if err != nil { @@ -609,7 +613,7 @@ func ListCredentials() ([]CredentialInfo, error) { info := &infos[i] key, err := darwin.ECDSAPublicKeyFromRaw(info.publicKeyRaw) if err != nil { - log.Warnf("Failed to convert public key: %v", err) + logger.WarnContext(context.Background(), "Failed to convert public key", "error", err) } info.PublicKey = key // this is OK, even if it's nil info.publicKeyRaw = nil diff --git a/lib/auth/touchid/api_darwin.go b/lib/auth/touchid/api_darwin.go index a7ac71653a863..723660e831c50 100644 --- a/lib/auth/touchid/api_darwin.go +++ b/lib/auth/touchid/api_darwin.go @@ -33,6 +33,7 @@ package touchid import "C" import ( + "context" "encoding/base64" "fmt" "runtime/cgo" @@ -42,7 +43,8 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" + + logutils "github.com/gravitational/teleport/lib/utils/log" ) const ( @@ -110,7 +112,7 @@ func (touchIDImpl) Diag() (*DiagResult, error) { laErrorDomain := C.GoString(resC.la_error_domain) laErrorDescription := C.GoString(resC.la_error_description) if !passedLA && laErrorDescription != "" { - log.Debugf("Touch ID: LAError description: %v", laErrorDescription) + logger.DebugContext(context.Background(), "Received non-empty LAError description", "description", laErrorDescription) } isAvailable := signed && entitled && passedLA && passedEnclave @@ -141,7 +143,7 @@ func runGoFuncHandle(handle C.uintptr_t) { val := cgo.Handle(handle).Value() fn, ok := val.(func()) if !ok { - log.Warnf("Touch ID: received unexpected function handle: %T", val) + logger.WarnContext(context.Background(), "received unexpected function handle", "handle", logutils.TypeAttr(val)) return } fn() @@ -304,6 +306,8 @@ func readCredentialInfos(find func(**C.CredentialInfo) C.int) ([]CredentialInfo, var infosC *C.CredentialInfo defer func() { C.free(unsafe.Pointer(infosC)) }() + ctx := context.Background() + res := find(&infosC) if res < 0 { return nil, int(res) @@ -338,21 +342,30 @@ func readCredentialInfos(find func(**C.CredentialInfo) C.int) ([]CredentialInfo, // user@rpid parsedLabel, err := parseLabel(label) if err != nil { - log.Debugf("Skipping credential %q: %v", credentialID, err) + logger.DebugContext(ctx, "Skipping credential", + "credential_id", credentialID, + "error", err, + ) continue } // user handle userHandle, err := base64.RawURLEncoding.DecodeString(appTag) if err != nil { - log.Debugf("Skipping credential %q: unexpected application tag: %q", credentialID, appTag) + logger.DebugContext(ctx, "Skipping credential, unexpected application tag", + "credential_id", credentialID, + "app_tag", appTag, + ) continue } // ECDSA public key pubKeyRaw, err := base64.StdEncoding.DecodeString(pubKeyB64) if err != nil { - log.WithError(err).Warnf("Failed to decode public key for credential %q", credentialID) + logger.WarnContext(ctx, "Failed to decode public key for credential", + "credential_id", credentialID, + "error", err, + ) // Do not return or break out of the loop, it needs to run in order to // deallocate the structs within. } @@ -361,7 +374,11 @@ func readCredentialInfos(find func(**C.CredentialInfo) C.int) ([]CredentialInfo, const iso8601Format = "2006-01-02T15:04:05Z0700" createTime, err := time.Parse(iso8601Format, creationDate) if err != nil { - log.WithError(err).Warnf("Failed to parse creation time %q for credential %q", creationDate, credentialID) + logger.WarnContext(ctx, "Failed to parse creation time for credential", + "creation_time", creationDate, + "credential_id", credentialID, + "error", err, + ) } infos = append(infos, CredentialInfo{ diff --git a/lib/auth/transport_credentials.go b/lib/auth/transport_credentials.go index 1e0e87904637e..9eb499a5368c4 100644 --- a/lib/auth/transport_credentials.go +++ b/lib/auth/transport_credentials.go @@ -185,7 +185,7 @@ func newTimeoutConn(conn net.Conn, clock clockwork.Clock, expires time.Time) (ne return &timeoutConn{ Conn: conn, timer: clock.AfterFunc(expires.Sub(clock.Now()), func() { - log.Debug("Closing gRPC connection due to certificate expiry") + logger.DebugContext(context.Background(), "Closing gRPC connection due to certificate expiry") conn.Close() }), }, nil diff --git a/lib/auth/trust/trustv1/service.go b/lib/auth/trust/trustv1/service.go index 91f99a4819be9..345bb8419940d 100644 --- a/lib/auth/trust/trustv1/service.go +++ b/lib/auth/trust/trustv1/service.go @@ -23,10 +23,8 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/emptypb" - "github.com/gravitational/teleport" trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/authz" @@ -44,6 +42,13 @@ type authServer interface { // RotateCertAuthority starts or restarts certificate authority rotation process. RotateCertAuthority(ctx context.Context, req types.RotateRequest) error + + // UpsertTrustedClusterV2 upserts a Trusted Cluster. + UpsertTrustedClusterV2(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error) + // CreateTrustedCluster creates a Trusted Cluster. + CreateTrustedCluster(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error) + // UpdateTrustedCluster updates a Trusted Cluster. + UpdateTrustedCluster(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error) } // ServiceConfig holds configuration options for @@ -52,7 +57,6 @@ type ServiceConfig struct { Authorizer authz.Authorizer Cache services.AuthorityGetter Backend services.TrustInternal - Logger *logrus.Entry AuthServer authServer } @@ -63,7 +67,6 @@ type Service struct { cache services.AuthorityGetter backend services.TrustInternal authServer authServer - logger *logrus.Entry } // NewService returns a new trust gRPC service. @@ -77,12 +80,9 @@ func NewService(cfg *ServiceConfig) (*Service, error) { return nil, trace.BadParameter("authorizer is required") case cfg.AuthServer == nil: return nil, trace.BadParameter("authServer is required") - case cfg.Logger == nil: - cfg.Logger = logrus.WithField(teleport.ComponentKey, "trust.service") } return &Service{ - logger: cfg.Logger, authorizer: cfg.Authorizer, cache: cfg.Cache, backend: cfg.Backend, diff --git a/lib/auth/trust/trustv1/service_test.go b/lib/auth/trust/trustv1/service_test.go index 152c2558c22b3..23ae66a5149b5 100644 --- a/lib/auth/trust/trustv1/service_test.go +++ b/lib/auth/trust/trustv1/service_test.go @@ -100,6 +100,18 @@ func (f *fakeAuthServer) RotateCertAuthority(ctx context.Context, req types.Rota return f.rotateCertAuthorityData[string(req.Type)] } +func (f *fakeAuthServer) UpsertTrustedClusterV2(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error) { + return tc, nil +} + +func (f *fakeAuthServer) CreateTrustedCluster(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error) { + return tc, nil +} + +func (f *fakeAuthServer) UpdateTrustedCluster(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error) { + return tc, nil +} + type fakeChecker struct { services.AccessChecker allow map[check]bool diff --git a/lib/auth/trust/trustv1/trustedcluster.go b/lib/auth/trust/trustv1/trustedcluster.go new file mode 100644 index 0000000000000..48f57a43ba0ec --- /dev/null +++ b/lib/auth/trust/trustv1/trustedcluster.go @@ -0,0 +1,126 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package trustv1 + +import ( + "context" + + "github.com/gravitational/trace" + + trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/services" +) + +// UpsertTrustedCluster upserts a Trusted Cluster. +func (s *Service) UpsertTrustedCluster(ctx context.Context, req *trustpb.UpsertTrustedClusterRequest) (*types.TrustedClusterV2, error) { + // Don't allow a Cloud tenant to be a leaf cluster. + if modules.GetModules().Features().Cloud { + return nil, trace.NotImplemented("cloud tenants cannot be leaf clusters") + } + + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindTrustedCluster, types.VerbCreate, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + if err = services.ValidateTrustedCluster(req.GetTrustedCluster()); err != nil { + return nil, trace.Wrap(err) + } + tc, err := s.authServer.UpsertTrustedClusterV2(ctx, req.GetTrustedCluster()) + if err != nil { + return nil, trace.Wrap(err) + } + trustedClusterV2, ok := tc.(*types.TrustedClusterV2) + if !ok { + return nil, trace.Errorf("encountered unexpected Trusted Cluster type: %T", tc) + } + return trustedClusterV2, nil +} + +// CreateTrustedCluster creates a Trusted Cluster. +func (s *Service) CreateTrustedCluster(ctx context.Context, req *trustpb.CreateTrustedClusterRequest) (*types.TrustedClusterV2, error) { + // Don't allow a Cloud tenant to be a leaf cluster. + if modules.GetModules().Features().Cloud { + return nil, trace.NotImplemented("cloud tenants cannot be leaf clusters") + } + + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindTrustedCluster, types.VerbCreate); err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + if err = services.ValidateTrustedCluster(req.GetTrustedCluster()); err != nil { + return nil, trace.Wrap(err) + } + tc, err := s.authServer.CreateTrustedCluster(ctx, req.GetTrustedCluster()) + if err != nil { + return nil, trace.Wrap(err) + } + trustedClusterV2, ok := tc.(*types.TrustedClusterV2) + if !ok { + return nil, trace.Errorf("encountered unexpected Trusted Cluster type: %T", tc) + } + return trustedClusterV2, nil +} + +// UpdateTrustedCluster updates a Trusted Cluster. +func (s *Service) UpdateTrustedCluster(ctx context.Context, req *trustpb.UpdateTrustedClusterRequest) (*types.TrustedClusterV2, error) { + // Don't allow a Cloud tenant to be a leaf cluster. + if modules.GetModules().Features().Cloud { + return nil, trace.NotImplemented("cloud tenants cannot be leaf clusters") + } + + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindTrustedCluster, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + if err = services.ValidateTrustedCluster(req.GetTrustedCluster()); err != nil { + return nil, trace.Wrap(err) + } + tc, err := s.authServer.UpdateTrustedCluster(ctx, req.GetTrustedCluster()) + if err != nil { + return nil, trace.Wrap(err) + } + trustedClusterV2, ok := tc.(*types.TrustedClusterV2) + if !ok { + return nil, trace.Errorf("encountered unexpected Trusted Cluster type: %T", tc) + } + return trustedClusterV2, nil +} diff --git a/lib/auth/trust/trustv1/trustedcluster_test.go b/lib/auth/trust/trustv1/trustedcluster_test.go new file mode 100644 index 0000000000000..d4aea850fedc4 --- /dev/null +++ b/lib/auth/trust/trustv1/trustedcluster_test.go @@ -0,0 +1,281 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package trustv1 + +import ( + "context" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport" + trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/services/local" +) + +// TestCloudProhibited verifies that Trusted Clusters cannot be created or updated +// in a Cloud hosted environment. +// Tests cannot be run in parallel because it relies on environment variables. +func TestCloudProhibited(t *testing.T) { + ctx := context.Background() + p := newTestPack(t) + + trust := local.NewCAService(p.mem) + cfg := &ServiceConfig{ + Cache: trust, + Backend: trust, + Authorizer: &fakeAuthorizer{}, + AuthServer: &fakeAuthServer{}, + } + + service, err := NewService(cfg) + require.NoError(t, err) + + modules.SetTestModules(t, &modules.TestModules{ + TestFeatures: modules.Features{Cloud: true}, + }) + + tc, err := types.NewTrustedCluster("test", types.TrustedClusterSpecV2{ + RoleMap: []types.RoleMapping{ + {Remote: teleport.PresetAccessRoleName, Local: []string{teleport.PresetAccessRoleName}}, + }, + }) + require.NoError(t, err, "creating trusted cluster resource") + + trustedClusterV2, ok := tc.(*types.TrustedClusterV2) + require.True(t, ok) + + t.Run("Cloud prohibits being a leaf cluster (UpsertTrustedCluster)", func(t *testing.T) { + _, err = service.UpsertTrustedCluster(ctx, &trustpb.UpsertTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsNotImplemented(err), "UpsertTrustedClusterV2 returned an unexpected error, got = %v (%T), want trace.NotImplementedError", err, err) + }) + + t.Run("Cloud prohibits being a leaf cluster (CreateTrustedCluster)", func(t *testing.T) { + _, err = service.CreateTrustedCluster(ctx, &trustpb.CreateTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsNotImplemented(err), "CreateTrustedCluster returned an unexpected error, got = %v (%T), want trace.NotImplementedError", err, err) + }) + + t.Run("Cloud prohibits being a leaf cluster (UpdateTrustedCluster)", func(t *testing.T) { + _, err = service.UpdateTrustedCluster(ctx, &trustpb.UpdateTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsNotImplemented(err), "UpdateTrustedCluster returned an unexpected error, got = %v (%T), want trace.NotImplementedError", err, err) + }) +} + +func TestTrustedClusterRBAC(t *testing.T) { + t.Parallel() + ctx := context.Background() + p := newTestPack(t) + + tc, err := types.NewTrustedCluster("test", types.TrustedClusterSpecV2{ + RoleMap: []types.RoleMapping{ + {Remote: teleport.PresetAccessRoleName, Local: []string{teleport.PresetAccessRoleName}}, + }, + }) + require.NoError(t, err, "creating trusted cluster resource") + + trustedClusterV2, ok := tc.(*types.TrustedClusterV2) + require.True(t, ok) + + tests := []struct { + desc string + f func(t *testing.T, service *Service) + authorizer fakeAuthorizer + expectChecks []check + }{ + { + desc: "upsert no access", + f: func(t *testing.T, service *Service) { + _, err := service.UpsertTrustedCluster(ctx, &trustpb.UpsertTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, got %v", err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{}, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbCreate}, + {types.KindTrustedCluster, types.VerbUpdate}, + }, + }, + { + desc: "upsert no create access", + f: func(t *testing.T, service *Service) { + _, err := service.UpsertTrustedCluster(ctx, &trustpb.UpsertTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, got %v", err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{ + {types.KindTrustedCluster, types.VerbCreate}: false, + {types.KindTrustedCluster, types.VerbUpdate}: true, + }, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbCreate}, + {types.KindTrustedCluster, types.VerbUpdate}, + }, + }, + { + desc: "upsert no update access", + f: func(t *testing.T, service *Service) { + _, err := service.UpsertTrustedCluster(ctx, &trustpb.UpsertTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, got %v", err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{ + {types.KindTrustedCluster, types.VerbCreate}: true, + {types.KindTrustedCluster, types.VerbUpdate}: false, + }, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbCreate}, + {types.KindTrustedCluster, types.VerbUpdate}, + }, + }, + { + desc: "upsert ok", + f: func(t *testing.T, service *Service) { + _, err := service.UpsertTrustedCluster(ctx, &trustpb.UpsertTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.NoError(t, err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{ + {types.KindTrustedCluster, types.VerbCreate}: true, + {types.KindTrustedCluster, types.VerbUpdate}: true, + }, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbCreate}, + {types.KindTrustedCluster, types.VerbUpdate}, + }, + }, + { + desc: "create no access", + f: func(t *testing.T, service *Service) { + _, err := service.CreateTrustedCluster(ctx, &trustpb.CreateTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, got %v", err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{}, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbCreate}, + }, + }, + { + desc: "create ok", + f: func(t *testing.T, service *Service) { + _, err := service.CreateTrustedCluster(ctx, &trustpb.CreateTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.NoError(t, err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{ + {types.KindTrustedCluster, types.VerbCreate}: true, + }, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbCreate}, + }, + }, + { + desc: "update no access", + f: func(t *testing.T, service *Service) { + _, err := service.UpdateTrustedCluster(ctx, &trustpb.UpdateTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, got %v", err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{}, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbUpdate}, + }, + }, + { + desc: "update ok", + f: func(t *testing.T, service *Service) { + _, err := service.UpdateTrustedCluster(ctx, &trustpb.UpdateTrustedClusterRequest{ + TrustedCluster: trustedClusterV2, + }) + require.NoError(t, err) + }, + authorizer: fakeAuthorizer{ + checker: &fakeChecker{ + allow: map[check]bool{ + {types.KindTrustedCluster, types.VerbUpdate}: true, + }, + }, + }, + expectChecks: []check{ + {types.KindTrustedCluster, types.VerbUpdate}, + }, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + trust := local.NewCAService(p.mem) + cfg := &ServiceConfig{ + Cache: trust, + Backend: trust, + Authorizer: &test.authorizer, + AuthServer: &fakeAuthServer{}, + } + + service, err := NewService(cfg) + require.NoError(t, err) + test.f(t, service) + require.ElementsMatch(t, test.expectChecks, test.authorizer.checker.checks) + }) + } +} diff --git a/lib/auth/trustedcluster.go b/lib/auth/trustedcluster.go index 784d6aad010dc..443cb6579013e 100644 --- a/lib/auth/trustedcluster.go +++ b/lib/auth/trustedcluster.go @@ -30,6 +30,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/client/webclient" tracehttp "github.com/gravitational/teleport/api/observability/tracing/http" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" @@ -45,7 +46,53 @@ import ( ) // UpsertTrustedCluster creates or toggles a Trusted Cluster relationship. +// Deprecated: UpsertTrustedClusterV2 should be preferred instead. func (a *Server) UpsertTrustedCluster(ctx context.Context, tc types.TrustedCluster) (newTrustedCluster types.TrustedCluster, returnErr error) { + const validateNameFalse = false + upserted, err := a.upsertTrustedCluster(ctx, tc, validateNameFalse) + return upserted, trace.Wrap(err) +} + +// UpsertTrustedClusterV2 creates or toggles a Trusted Cluster relationship. +// The trusted cluster resource name must match the cluster name. +func (a *Server) UpsertTrustedClusterV2(ctx context.Context, tc types.TrustedCluster) (newTrustedCluster types.TrustedCluster, returnErr error) { + const validateNameTrue = true + upserted, err := a.upsertTrustedCluster(ctx, tc, validateNameTrue) + return upserted, trace.Wrap(err) +} + +// CreateTrustedCluster creates a Trusted Cluster relationship. +func (a *Server) CreateTrustedCluster(ctx context.Context, tc types.TrustedCluster) (newTrustedCluster types.TrustedCluster, returnErr error) { + // verify that trusted cluster role map does not reference non-existent roles + if err := a.checkLocalRoles(ctx, tc.GetRoleMap()); err != nil { + return nil, trace.Wrap(err) + } + + const validateNameTrue = true + created, err := a.createTrustedCluster(ctx, tc, validateNameTrue) + return created, trace.Wrap(err) +} + +// UpdateTrustedCluster updates a Trusted Cluster relationship. +func (a *Server) UpdateTrustedCluster(ctx context.Context, tc types.TrustedCluster) (newTrustedCluster types.TrustedCluster, returnErr error) { + // verify that trusted cluster role map does not reference non-existent roles + if err := a.checkLocalRoles(ctx, tc.GetRoleMap()); err != nil { + return nil, trace.Wrap(err) + } + + existingCluster, err := a.GetTrustedCluster(ctx, tc.GetName()) + if err != nil { + return nil, trace.Wrap(err) + } + + updated, err := a.updateTrustedCluster(ctx, tc, existingCluster) + return updated, trace.Wrap(err) +} + +// upsertTrustedCluster upserts the trusted cluster. +// If validateName is true, the trusted cluster resource name must be validated +// before the trusted cluster is created. +func (a *Server) upsertTrustedCluster(ctx context.Context, tc types.TrustedCluster, validateName bool) (types.TrustedCluster, error) { // verify that trusted cluster role map does not reference non-existent roles if err := a.checkLocalRoles(ctx, tc.GetRoleMap()); err != nil { return nil, trace.Wrap(err) @@ -67,7 +114,7 @@ func (a *Server) UpsertTrustedCluster(ctx context.Context, tc types.TrustedClust // if there is no existing cluster, switch to the create case if existingCluster == nil { - return a.createTrustedCluster(ctx, tc) + return a.createTrustedCluster(ctx, tc, validateName) } if err := existingCluster.CanChangeStateTo(tc); err != nil { @@ -103,8 +150,11 @@ func (a *Server) UpsertTrustedCluster(ctx context.Context, tc types.TrustedClust return tc, nil } -func (a *Server) createTrustedCluster(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error) { - remoteCAs, err := a.establishTrust(ctx, tc) +// createTrustedCluster creates the trusted cluster. +// If validateName is true, the trusted cluster resource name must be validated +// before the trusted cluster is created. +func (a *Server) createTrustedCluster(ctx context.Context, tc types.TrustedCluster, validateName bool) (types.TrustedCluster, error) { + remoteCAs, err := a.establishTrust(ctx, tc, validateName) if err != nil { return nil, trace.Wrap(err) } @@ -130,6 +180,38 @@ func (a *Server) createTrustedCluster(ctx context.Context, tc types.TrustedClust return tc, nil } +// updateTrustedCluster updates the trusted cluster. +func (a *Server) updateTrustedCluster(ctx context.Context, tc types.TrustedCluster, existingCluster types.TrustedCluster) (types.TrustedCluster, error) { + if err := existingCluster.CanChangeStateTo(tc); err != nil { + return nil, trace.Wrap(err) + } + + // always load all current CAs. even if we aren't changing them as part of + // this function, Services.UpdateTrustedCluster will only correctly activate/deactivate + // CAs that are explicitly passed to it. note that we pass in the existing cluster state + // since where CAs are stored depends on the current state of the trusted cluster. + cas, err := a.getCAsForTrustedCluster(ctx, existingCluster) + if err != nil { + return nil, trace.Wrap(err) + } + + // propagate any role map changes to cas + configureCAsForTrustedCluster(tc, cas) + + revision, err := a.Services.UpdateTrustedCluster(ctx, tc, cas) + if err != nil { + return nil, trace.Wrap(err) + } + + tc.SetRevision(revision) + + if err := a.onTrustedClusterWrite(ctx, tc); err != nil { + return nil, trace.Wrap(err) + } + + return tc, nil +} + // configureCAsForTrustedCluster modifies remote CAs for use as trusted cluster CAs. func configureCAsForTrustedCluster(tc types.TrustedCluster, cas []types.CertAuthority) { // modify the remote CAs for use as tc cas. @@ -261,13 +343,13 @@ func (a *Server) DeleteTrustedCluster(ctx context.Context, name string) error { }, ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - log.WithError(err).Warn("Failed to emit trusted cluster delete event.") + a.logger.WarnContext(ctx, "Failed to emit trusted cluster delete event", "error", err) } return nil } -func (a *Server) establishTrust(ctx context.Context, trustedCluster types.TrustedCluster) ([]types.CertAuthority, error) { +func (a *Server) establishTrust(ctx context.Context, trustedCluster types.TrustedCluster, validateName bool) ([]types.CertAuthority, error) { var localCertAuthorities []types.CertAuthority domainName, err := a.GetDomainName() @@ -286,6 +368,14 @@ func (a *Server) establishTrust(ctx context.Context, trustedCluster types.Truste } } + // Validate cluster names before establishing trust to avoid unnecessarily + // creating a remote_cluster resource on the root cluster. + if validateName { + if err := a.validateTrustedClusterName(ctx, trustedCluster); err != nil { + return nil, trace.Wrap(err) + } + } + // create a request to validate a trusted cluster (token and local certificate authorities) validateRequest := authclient.ValidateTrustedClusterRequest{ Token: trustedCluster.GetToken(), @@ -294,12 +384,15 @@ func (a *Server) establishTrust(ctx context.Context, trustedCluster types.Truste } // log the local certificate authorities that we are sending - log.Infof("Sending validate request; token=%s, CAs=%v", backend.MaskKeyName(validateRequest.Token), validateRequest.CAs) + a.logger.InfoContext(ctx, "Sending validate request", + "token", backend.MaskKeyName(validateRequest.Token), + "cas", validateRequest.CAs, + ) // send the request to the remote auth server via the proxy - validateResponse, err := a.sendValidateRequestToProxy(trustedCluster.GetProxyAddress(), &validateRequest) + validateResponse, err := a.sendValidateRequestToProxy(ctx, trustedCluster.GetProxyAddress(), &validateRequest) if err != nil { - log.Error(err) + a.logger.ErrorContext(ctx, "failed to send validation request", "error", err) if strings.Contains(err.Error(), "x509") { return nil, trace.AccessDenied("the trusted cluster uses misconfigured HTTP/TLS certificate.") } @@ -307,7 +400,7 @@ func (a *Server) establishTrust(ctx context.Context, trustedCluster types.Truste } // log the remote certificate authorities we are adding - log.Infof("Received validate response; CAs=%v", validateResponse.CAs) + a.logger.InfoContext(ctx, "Received validate response", "cas", validateResponse.CAs) for _, ca := range validateResponse.CAs { for _, keyPair := range ca.GetActiveKeys().TLS { @@ -322,9 +415,10 @@ func (a *Server) establishTrust(ctx context.Context, trustedCluster types.Truste if remoteClusterName == domainName { return nil, trace.BadParameter("remote cluster name can not be the same as local cluster name") } - // TODO(klizhentas) in 2.5.0 prohibit adding trusted cluster resource name - // different from cluster name (we had no way of checking this before x509, - // because SSH CA was a public key, not a cert with metadata) + if validateName && trustedCluster.GetName() != remoteClusterName { + return nil, trace.BadParameter("trusted cluster resource name must be the same as the remote cluster name. got: %q", + trustedCluster.GetName()) + } } } @@ -442,11 +536,14 @@ func (a *Server) GetRemoteClusters(ctx context.Context) ([]types.RemoteCluster, func (a *Server) validateTrustedCluster(ctx context.Context, validateRequest *authclient.ValidateTrustedClusterRequest) (resp *authclient.ValidateTrustedClusterResponse, err error) { defer func() { if err != nil { - log.WithError(err).Info("Trusted cluster validation failed") + a.logger.InfoContext(ctx, "Trusted cluster validation failed", "error", err) } }() - log.Debugf("Received validate request: token=%s, CAs=%v", backend.MaskKeyName(validateRequest.Token), validateRequest.CAs) + a.logger.DebugContext(ctx, "Received validate request", + "token", backend.MaskKeyName(validateRequest.Token), + "cas", validateRequest.CAs, + ) domainName, err := a.GetDomainName() if err != nil { @@ -513,7 +610,7 @@ func (a *Server) validateTrustedCluster(ctx context.Context, validateRequest *au } // log the local certificate authorities we are sending - log.Debugf("Sending validate response: CAs=%v", validateResponse.CAs) + a.logger.DebugContext(ctx, "Sending validate response", "cas", validateResponse.CAs) return &validateResponse, nil } @@ -550,7 +647,7 @@ func (a *Server) validateTrustedClusterToken(ctx context.Context, tokenName stri return provisionToken.GetMetadata().Labels, nil } -func (a *Server) sendValidateRequestToProxy(host string, validateRequest *authclient.ValidateTrustedClusterRequest) (*authclient.ValidateTrustedClusterResponse, error) { +func (a *Server) sendValidateRequestToProxy(ctx context.Context, host string, validateRequest *authclient.ValidateTrustedClusterRequest) (*authclient.ValidateTrustedClusterResponse, error) { proxyAddr := url.URL{ Scheme: "https", Host: host, @@ -561,7 +658,7 @@ func (a *Server) sendValidateRequestToProxy(host string, validateRequest *authcl } if lib.IsInsecureDevMode() { - log.Warn("The setting insecureSkipVerify is used to communicate with proxy. Make sure you intend to run Teleport in insecure mode!") + a.logger.WarnContext(ctx, "The setting insecureSkipVerify is used to communicate with proxy. Make sure you intend to run Teleport in insecure mode!") // Get the default transport, this allows picking up proxy from the // environment. @@ -586,13 +683,12 @@ func (a *Server) sendValidateRequestToProxy(host string, validateRequest *authcl if err != nil { return nil, trace.Wrap(err) } - validateRequestRaw, err := validateRequest.ToRaw() if err != nil { return nil, trace.Wrap(err) } - out, err := httplib.ConvertResponse(clt.PostJSON(context.TODO(), clt.Endpoint("webapi", "trustedclusters", "validate"), validateRequestRaw)) + out, err := httplib.ConvertResponse(clt.PostJSON(ctx, clt.Endpoint("webapi", "trustedclusters", "validate"), validateRequestRaw)) if err != nil { return nil, trace.Wrap(err) } @@ -611,6 +707,24 @@ func (a *Server) sendValidateRequestToProxy(host string, validateRequest *authcl return validateResponse, nil } +// validateTrustedClusterName validates that the trusted cluster resource name +// matches the cluster name. +func (a *Server) validateTrustedClusterName(ctx context.Context, trustedCluster types.TrustedCluster) error { + resp, err := webclient.Find(&webclient.Config{ + Context: ctx, + ProxyAddr: trustedCluster.GetProxyAddress(), + Insecure: lib.IsInsecureDevMode(), + }) + if err != nil { + return trace.Wrap(err) + } + if trustedCluster.GetName() != resp.ClusterName { + return trace.BadParameter("trusted cluster resource name must be the same as the remote cluster name. got: %q", + trustedCluster.GetName()) + } + return nil +} + // createReverseTunnel will create a services.ReverseTunnel givenin the // services.TrustedCluster resource. func (a *Server) createReverseTunnel(ctx context.Context, t types.TrustedCluster) error { diff --git a/lib/auth/trustedcluster_test.go b/lib/auth/trustedcluster_test.go index c65287c5064ff..aca02a7dc4943 100644 --- a/lib/auth/trustedcluster_test.go +++ b/lib/auth/trustedcluster_test.go @@ -453,17 +453,23 @@ func TestUpsertTrustedCluster(t *testing.T) { err = a.SetStaticTokens(tks) require.NoError(t, err) - trustedCluster, err := types.NewTrustedCluster("trustedcluster", - types.TrustedClusterSpecV2{ - Enabled: true, - RoleMap: []types.RoleMapping{ - { - Local: []string{"someRole"}, - Remote: "someRole", - }, + role, err := types.NewRole("test-role", types.RoleSpecV6{}) + require.NoError(t, err) + _, err = a.UpsertRole(ctx, role) + require.NoError(t, err) + + trustedClusterSpec := types.TrustedClusterSpecV2{ + Enabled: true, + RoleMap: []types.RoleMapping{ + { + Local: []string{"test-role"}, + Remote: "someRole", }, - ProxyAddress: "localhost", - }) + }, + ProxyAddress: "localhost", + } + + trustedCluster, err := types.NewTrustedCluster("trustedcluster", trustedClusterSpec) require.NoError(t, err) ca := suite.NewTestCA(types.UserCA, "trustedcluster") @@ -489,7 +495,7 @@ func TestUpsertTrustedCluster(t *testing.T) { ProxyAddress: "localhost", }) require.NoError(t, err) - _, err = a.UpsertTrustedCluster(ctx, trustedCluster) + _, err = a.UpsertTrustedClusterV2(ctx, trustedCluster) require.ErrorContains(t, err, "someNewRole") }) t.Run("Change role map of existing enabled trusted cluster", func(t *testing.T) { @@ -505,7 +511,7 @@ func TestUpsertTrustedCluster(t *testing.T) { ProxyAddress: "localhost", }) require.NoError(t, err) - _, err = a.UpsertTrustedCluster(ctx, trustedCluster) + _, err = a.UpsertTrustedClusterV2(ctx, trustedCluster) require.NoError(t, err) }) t.Run("Disable existing trusted cluster", func(t *testing.T) { @@ -521,7 +527,7 @@ func TestUpsertTrustedCluster(t *testing.T) { ProxyAddress: "localhost", }) require.NoError(t, err) - _, err = a.UpsertTrustedCluster(ctx, trustedCluster) + _, err = a.UpsertTrustedClusterV2(ctx, trustedCluster) require.NoError(t, err) }) t.Run("Change role map of existing disabled trusted cluster", func(t *testing.T) { @@ -537,7 +543,7 @@ func TestUpsertTrustedCluster(t *testing.T) { ProxyAddress: "localhost", }) require.NoError(t, err) - _, err = a.UpsertTrustedCluster(ctx, trustedCluster) + _, err = a.UpsertTrustedClusterV2(ctx, trustedCluster) require.NoError(t, err) }) t.Run("Enable existing trusted cluster", func(t *testing.T) { @@ -553,23 +559,177 @@ func TestUpsertTrustedCluster(t *testing.T) { ProxyAddress: "localhost", }) require.NoError(t, err) - _, err = a.UpsertTrustedCluster(ctx, trustedCluster) + _, err = a.UpsertTrustedClusterV2(ctx, trustedCluster) require.NoError(t, err) }) - t.Run("Cloud prohibits being a leaf cluster", func(t *testing.T) { - modules.SetTestModules(t, &modules.TestModules{ - TestFeatures: modules.Features{Cloud: true}, - }) + t.Run("Upsert unmodified trusted cluster", func(t *testing.T) { + trustedCluster, err := types.NewTrustedCluster("trustedcluster", trustedClusterSpec) + require.NoError(t, err) + _, err = a.UpsertTrustedClusterV2(ctx, trustedCluster) + require.NoError(t, err) + }) +} + +func TestUpdateTrustedCluster(t *testing.T) { + ctx := context.Background() + testAuth, err := NewTestAuthServer(TestAuthServerConfig{ + ClusterName: "localcluster", + Dir: t.TempDir(), + }) + require.NoError(t, err) + a := testAuth.AuthServer + + const validToken = "validtoken" + tks, err := types.NewStaticTokens(types.StaticTokensSpecV2{ + StaticTokens: []types.ProvisionTokenV1{{ + Roles: []types.SystemRole{types.RoleTrustedCluster}, + Token: validToken, + }}, + }) + require.NoError(t, err) + + err = a.SetStaticTokens(tks) + require.NoError(t, err) - tc, err := types.NewTrustedCluster("test", types.TrustedClusterSpecV2{ - RoleMap: []types.RoleMapping{ - {Remote: teleport.PresetAccessRoleName, Local: []string{teleport.PresetAccessRoleName}}, + role, err := types.NewRole("test-role", types.RoleSpecV6{}) + require.NoError(t, err) + _, err = a.UpsertRole(ctx, role) + require.NoError(t, err) + + trustedClusterSpec := types.TrustedClusterSpecV2{ + Enabled: true, + RoleMap: []types.RoleMapping{ + { + Local: []string{"test-role"}, + Remote: "someRole", }, - }) - require.NoError(t, err, "creating trusted cluster resource") + }, + ProxyAddress: "localhost", + } - server := ServerWithRoles{authServer: a} - _, err = server.UpsertTrustedCluster(ctx, tc) - require.True(t, trace.IsNotImplemented(err), "UpsertTrustedCluster returned an unexpected error, got = %v (%T), want trace.NotImplementedError", err, err) + testClusterName := "trustedcluster" + trustedCluster, err := types.NewTrustedCluster(testClusterName, trustedClusterSpec) + require.NoError(t, err) + + ca := suite.NewTestCA(types.UserCA, testClusterName) + + configureCAsForTrustedCluster(trustedCluster, []types.CertAuthority{ca}) + + _, err = a.Services.CreateTrustedCluster(ctx, trustedCluster, []types.CertAuthority{ca}) + require.NoError(t, err) + + err = a.createReverseTunnel(ctx, trustedCluster) + require.NoError(t, err) + + t.Run("Invalid role change", func(t *testing.T) { + existing, err := a.GetTrustedCluster(ctx, testClusterName) + require.NoError(t, err) + trustedCluster, err := types.NewTrustedCluster(testClusterName, + types.TrustedClusterSpecV2{ + Enabled: true, + RoleMap: []types.RoleMapping{ + { + Local: []string{"someNewRole"}, + Remote: "someRole", + }, + }, + ProxyAddress: "localhost", + }) + require.NoError(t, err) + trustedCluster.SetRevision(existing.GetRevision()) + _, err = a.UpdateTrustedCluster(ctx, trustedCluster) + require.ErrorContains(t, err, "someNewRole") + }) + t.Run("Change role map of existing enabled trusted cluster", func(t *testing.T) { + existing, err := a.GetTrustedCluster(ctx, testClusterName) + require.NoError(t, err) + trustedCluster, err := types.NewTrustedCluster(testClusterName, + types.TrustedClusterSpecV2{ + Enabled: true, + RoleMap: []types.RoleMapping{ + { + Local: []string{constants.DefaultImplicitRole}, + Remote: "someRole", + }, + }, + ProxyAddress: "localhost", + }) + require.NoError(t, err) + trustedCluster.SetRevision(existing.GetRevision()) + _, err = a.UpdateTrustedCluster(ctx, trustedCluster) + require.NoError(t, err) + }) + t.Run("Disable existing trusted cluster", func(t *testing.T) { + existing, err := a.GetTrustedCluster(ctx, testClusterName) + require.NoError(t, err) + trustedCluster, err := types.NewTrustedCluster(testClusterName, + types.TrustedClusterSpecV2{ + Enabled: false, + RoleMap: []types.RoleMapping{ + { + Local: []string{constants.DefaultImplicitRole}, + Remote: "someRole", + }, + }, + ProxyAddress: "localhost", + }) + require.NoError(t, err) + trustedCluster.SetRevision(existing.GetRevision()) + _, err = a.UpdateTrustedCluster(ctx, trustedCluster) + require.NoError(t, err) + }) + t.Run("Change role map of existing disabled trusted cluster", func(t *testing.T) { + existing, err := a.GetTrustedCluster(ctx, testClusterName) + require.NoError(t, err) + trustedCluster, err := types.NewTrustedCluster(testClusterName, + types.TrustedClusterSpecV2{ + Enabled: false, + RoleMap: []types.RoleMapping{ + { + Local: []string{constants.DefaultImplicitRole}, + Remote: "someOtherRole", + }, + }, + ProxyAddress: "localhost", + }) + require.NoError(t, err) + trustedCluster.SetRevision(existing.GetRevision()) + _, err = a.UpdateTrustedCluster(ctx, trustedCluster) + require.NoError(t, err) + }) + t.Run("Enable existing trusted cluster", func(t *testing.T) { + existing, err := a.GetTrustedCluster(ctx, testClusterName) + require.NoError(t, err) + trustedCluster, err := types.NewTrustedCluster(testClusterName, + types.TrustedClusterSpecV2{ + Enabled: true, + RoleMap: []types.RoleMapping{ + { + Local: []string{constants.DefaultImplicitRole}, + Remote: "someOtherRole", + }, + }, + ProxyAddress: "localhost", + }) + require.NoError(t, err) + trustedCluster.SetRevision(existing.GetRevision()) + _, err = a.UpdateTrustedCluster(ctx, trustedCluster) + require.NoError(t, err) + }) + t.Run("Update unmodified trusted cluster", func(t *testing.T) { + existing, err := a.GetTrustedCluster(ctx, testClusterName) + require.NoError(t, err) + trustedCluster, err := types.NewTrustedCluster(testClusterName, trustedClusterSpec) + require.NoError(t, err) + trustedCluster.SetRevision(existing.GetRevision()) + _, err = a.UpdateTrustedCluster(ctx, trustedCluster) + require.NoError(t, err) + }) + + t.Run("Invalid revision", func(t *testing.T) { + trustedCluster, err := types.NewTrustedCluster(testClusterName, trustedClusterSpec) + require.NoError(t, err) + _, err = a.UpdateTrustedCluster(ctx, trustedCluster) + require.Error(t, err) }) } diff --git a/lib/auth/user.go b/lib/auth/user.go index fc8ed983930eb..5e51332e04071 100644 --- a/lib/auth/user.go +++ b/lib/auth/user.go @@ -65,7 +65,7 @@ func (a *Server) CompareAndSwapUser(ctx context.Context, new, existing types.Use Connector: connectorName, Roles: new.GetRoles(), }); err != nil { - log.WithError(err).Warn("Failed to emit user update event.") + a.logger.WarnContext(ctx, "Failed to emit user update event", "error", err) } usagereporter.EmitEditorChangeEvent(new.GetName(), existing.GetRoles(), new.GetRoles(), a.AnonymizeAndSubmit) diff --git a/lib/auth/userloginstate/generator.go b/lib/auth/userloginstate/generator.go index f9c72ac1a5f07..1eddc9c81ea53 100644 --- a/lib/auth/userloginstate/generator.go +++ b/lib/auth/userloginstate/generator.go @@ -21,10 +21,10 @@ package userloginstate import ( "context" "fmt" + "log/slog" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/client/proto" usageeventsv1 "github.com/gravitational/teleport/api/gen/proto/go/usageevents/v1" @@ -49,7 +49,7 @@ type AccessListsAndLockGetter interface { // GeneratorConfig is the configuration for the user login state generator. type GeneratorConfig struct { // Log is a logger to use for the generator. - Log *logrus.Entry + Log *slog.Logger // AccessLists is a service for retrieving access lists and locks from the backend. AccessLists AccessListsAndLockGetter @@ -107,7 +107,7 @@ func (g *GeneratorConfig) CheckAndSetDefaults() error { // Generator will generate a user login state from a user. type Generator struct { - log *logrus.Entry + log *slog.Logger accessLists AccessListsAndLockGetter access services.Access usageEvents UsageEventsClient @@ -191,7 +191,7 @@ func (g *Generator) Generate(ctx context.Context, user types.User, ulsService se if g.usageEvents != nil { // Emit the usage event metadata. if err := g.emitUsageEvent(ctx, user, uls, inheritedRoles, inheritedTraits); err != nil { - g.log.WithError(err).Debug("Error emitting usage event during user login state generation, skipping") + g.log.DebugContext(ctx, "Error emitting usage event during user login state generation, skipping", "error", err) } } @@ -245,7 +245,7 @@ func (g *Generator) handleAccessListMembership(ctx context.Context, user types.U if err != nil || membershipKind == accesslists.MembershipOrOwnershipTypeNone { // Log any error. if err != nil { - g.log.WithError(err).Warn("checking access list membership") + g.log.WarnContext(ctx, "checking access list membership", "error", err) } return inheritedRoles, inheritedTraits, nil } @@ -290,7 +290,7 @@ func (g *Generator) handleAccessListOwnership(ctx context.Context, user types.Us if err != nil || ownershipType == accesslists.MembershipOrOwnershipTypeNone { // Log any error. if err != nil { - g.log.WithError(err).Warn("checking access list ownership") + g.log.WarnContext(ctx, "checking access list ownership", "error", err) } return inheritedRoles, inheritedTraits, nil } @@ -497,6 +497,6 @@ func (g *Generator) emitSkippedAccessListEvent(ctx context.Context, accessListNa UserMessage: "access list skipped because it references non-existent role(s)", }, }); err != nil { - g.log.WithError(err).Warn("Failed to emit access list skipped warning audit event.") + g.log.WarnContext(ctx, "Failed to emit access list skipped warning audit event", "error", err) } } diff --git a/lib/auth/userloginstate/generator_test.go b/lib/auth/userloginstate/generator_test.go index 1154dec233de3..a263297c416c9 100644 --- a/lib/auth/userloginstate/generator_test.go +++ b/lib/auth/userloginstate/generator_test.go @@ -26,7 +26,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/client/proto" @@ -42,6 +41,7 @@ import ( "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/local" + "github.com/gravitational/teleport/lib/utils" ) const ownerUser = "owner" @@ -729,7 +729,6 @@ func initGeneratorSvc(t *testing.T) (*Generator, *svc) { ulsService, err := local.NewUserLoginStateService(mem) require.NoError(t, err) - log := logrus.WithField("test", "logger") svc := &svc{ AccessLists: accessListsSvc, Access: accessSvc, @@ -739,7 +738,7 @@ func initGeneratorSvc(t *testing.T) (*Generator, *svc) { emitter := &eventstest.MockRecorderEmitter{} generator, err := NewGenerator(GeneratorConfig{ - Log: log, + Log: utils.NewSlogLoggerForTests(), AccessLists: svc, Access: svc, UsageEvents: svc, diff --git a/lib/auth/userloginstate/userloginstatev1/service.go b/lib/auth/userloginstate/userloginstatev1/service.go index 1b4bbb43cc2a1..fc51ac2b299c6 100644 --- a/lib/auth/userloginstate/userloginstatev1/service.go +++ b/lib/auth/userloginstate/userloginstatev1/service.go @@ -23,10 +23,8 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/emptypb" - "github.com/gravitational/teleport" userloginstatev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/userloginstate/v1" "github.com/gravitational/teleport/api/types" conv "github.com/gravitational/teleport/api/types/userloginstate/convert/v1" @@ -36,8 +34,6 @@ import ( // ServiceConfig is the service config for the Access Lists gRPC service. type ServiceConfig struct { - // Logger is the logger to use. - Logger logrus.FieldLogger // Authorizer is the authorizer to use. Authorizer authz.Authorizer @@ -58,10 +54,6 @@ func (c *ServiceConfig) checkAndSetDefaults() error { return trace.BadParameter("user login states service is missing") } - if c.Logger == nil { - c.Logger = logrus.WithField(teleport.ComponentKey, "user_login_state_crud_service") - } - if c.Clock == nil { c.Clock = clockwork.NewRealClock() } @@ -72,7 +64,6 @@ func (c *ServiceConfig) checkAndSetDefaults() error { type Service struct { userloginstatev1.UnimplementedUserLoginStateServiceServer - log logrus.FieldLogger authorizer authz.Authorizer userLoginStates services.UserLoginStates clock clockwork.Clock @@ -85,7 +76,6 @@ func NewService(cfg ServiceConfig) (*Service, error) { } return &Service{ - log: cfg.Logger, authorizer: cfg.Authorizer, userLoginStates: cfg.UserLoginStates, clock: cfg.Clock, diff --git a/lib/auth/userpreferences/userpreferencesv1/service.go b/lib/auth/userpreferences/userpreferencesv1/service.go index 0a0b8605c5f11..2bf7b1b339979 100644 --- a/lib/auth/userpreferences/userpreferencesv1/service.go +++ b/lib/auth/userpreferences/userpreferencesv1/service.go @@ -22,10 +22,8 @@ import ( "context" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/emptypb" - "github.com/gravitational/teleport" userpreferences "github.com/gravitational/teleport/api/gen/proto/go/userpreferences/v1" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/services" @@ -35,7 +33,6 @@ import ( type ServiceConfig struct { Backend services.UserPreferences Authorizer authz.Authorizer - Logger *logrus.Entry } // Service implements the teleport.userpreferences.v1.UserPreferencesService RPC service. @@ -44,7 +41,6 @@ type Service struct { backend services.UserPreferences authorizer authz.Authorizer - log *logrus.Entry } // NewService returns a new user preferences gRPC service. @@ -54,14 +50,11 @@ func NewService(cfg *ServiceConfig) (*Service, error) { return nil, trace.BadParameter("backend is required") case cfg.Authorizer == nil: return nil, trace.BadParameter("authorizer is required") - case cfg.Logger == nil: - cfg.Logger = logrus.WithField(teleport.ComponentKey, "userpreferences.service") } return &Service{ backend: cfg.Backend, authorizer: cfg.Authorizer, - log: cfg.Logger, }, nil } diff --git a/lib/auth/users/usersv1/service.go b/lib/auth/users/usersv1/service.go index 217a258002392..fa22a20c71446 100644 --- a/lib/auth/users/usersv1/service.go +++ b/lib/auth/users/usersv1/service.go @@ -20,10 +20,10 @@ package usersv1 import ( "context" + "log/slog" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/emptypb" "github.com/gravitational/teleport" @@ -37,6 +37,7 @@ import ( "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/services" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // Cache is the subset of the cached resources that the Service queries. @@ -69,7 +70,7 @@ type ServiceConfig struct { Authorizer authz.Authorizer Cache Cache Backend Backend - Logger logrus.FieldLogger + Logger *slog.Logger Emitter apievents.Emitter Reporter usagereporter.UsageReporter Clock clockwork.Clock @@ -82,7 +83,7 @@ type Service struct { authorizer authz.Authorizer cache Cache backend Backend - logger logrus.FieldLogger + logger *slog.Logger emitter apievents.Emitter reporter usagereporter.UsageReporter clock clockwork.Clock @@ -104,7 +105,7 @@ func NewService(cfg ServiceConfig) (*Service, error) { } if cfg.Logger == nil { - cfg.Logger = logrus.WithField(teleport.ComponentKey, "users.service") + cfg.Logger = slog.With(teleport.ComponentKey, "users.service") } if cfg.Clock == nil { cfg.Clock = clockwork.NewRealClock() @@ -171,7 +172,7 @@ func (s *Service) GetUser(ctx context.Context, req *userspb.GetUserRequest) (*us // migrated to that model. if !authz.HasBuiltinRole(*authCtx, string(types.RoleAdmin)) { err := trace.AccessDenied("user %q requested access to user %q with secrets", authCtx.User.GetName(), req.Name) - s.logger.Warn(err) + s.logger.WarnContext(ctx, "user does not have permission to read user with secrets", "error", err) if err := s.emitter.EmitAuditEvent(ctx, &apievents.UserLogin{ Metadata: apievents.Metadata{ Type: events.UserLoginEvent, @@ -184,7 +185,7 @@ func (s *Service) GetUser(ctx context.Context, req *userspb.GetUserRequest) (*us UserMessage: err.Error(), }, }); err != nil { - s.logger.WithError(err).Warn("Failed to emit local login failure event.") + s.logger.WarnContext(ctx, "Failed to emit local login failure event", "error", err) } return nil, trace.AccessDenied("this request can be only executed by an admin") } @@ -206,7 +207,11 @@ func (s *Service) GetUser(ctx context.Context, req *userspb.GetUserRequest) (*us v2, ok := user.(*types.UserV2) if !ok { - s.logger.Warnf("expected type UserV2, got %T for user %q", user, user.GetName()) + s.logger.WarnContext(ctx, "unexpected user type", + "got_type", logutils.TypeAttr(user), + "expected_type", "UserV2", + "user", user.GetName(), + ) return nil, trace.BadParameter("encountered unexpected user type") } @@ -271,14 +276,18 @@ func (s *Service) CreateUser(ctx context.Context, req *userspb.CreateUserRequest Roles: created.GetRoles(), ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - s.logger.WithError(err).Warn("Failed to emit user create event.") + s.logger.WarnContext(ctx, "Failed to emit user create event", "error", err) } usagereporter.EmitEditorChangeEvent(created.GetName(), nil, created.GetRoles(), s.reporter.AnonymizeAndSubmit) v2, ok := created.(*types.UserV2) if !ok { - s.logger.Warnf("expected type UserV2, got %T for user %q", created, created.GetName()) + s.logger.WarnContext(ctx, "unexpected user type", + "got_type", logutils.TypeAttr(created), + "expected_type", "UserV2", + "user", created.GetName(), + ) return nil, trace.BadParameter("encountered unexpected user type") } @@ -319,7 +328,7 @@ func (s *Service) UpdateUser(ctx context.Context, req *userspb.UpdateUserRequest var omitEditorEvent bool if err != nil { // don't return error here since this call is for event emitting purposes only - s.logger.WithError(err).Warn("Failed getting previous user during update") + s.logger.WarnContext(ctx, "Failed getting previous user during update", "error", err) omitEditorEvent = true } @@ -356,7 +365,7 @@ func (s *Service) UpdateUser(ctx context.Context, req *userspb.UpdateUserRequest Roles: updated.GetRoles(), ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - s.logger.WithError(err).Warn("Failed to emit user update event.") + s.logger.WarnContext(ctx, "Failed to emit user update event", "error", err) } if !omitEditorEvent { @@ -365,7 +374,11 @@ func (s *Service) UpdateUser(ctx context.Context, req *userspb.UpdateUserRequest v2, ok := updated.(*types.UserV2) if !ok { - s.logger.Warnf("expected type UserV2, got %T for user %q", updated, updated.GetName()) + s.logger.WarnContext(ctx, "unexpected user type", + "got_type", logutils.TypeAttr(updated), + "expected_type", "UserV2", + "user", updated.GetName(), + ) return nil, trace.BadParameter("encountered unexpected user type") } @@ -405,7 +418,7 @@ func (s *Service) UpsertUser(ctx context.Context, req *userspb.UpsertUserRequest var omitEditorEvent bool if err != nil { // don't return error here since this call is for event emitting purposes only - s.logger.WithError(err).Warn("Failed getting previous user during update") + s.logger.WarnContext(ctx, "Failed getting previous user during update", "error", err) omitEditorEvent = true } @@ -446,7 +459,7 @@ func (s *Service) UpsertUser(ctx context.Context, req *userspb.UpsertUserRequest Roles: upserted.GetRoles(), ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - s.logger.WithError(err).Warn("Failed to emit user upsert event.") + s.logger.WarnContext(ctx, "Failed to emit user upsert event", "error", err) } if !omitEditorEvent { @@ -455,7 +468,11 @@ func (s *Service) UpsertUser(ctx context.Context, req *userspb.UpsertUserRequest v2, ok := upserted.(*types.UserV2) if !ok { - s.logger.Warnf("expected type UserV2, got %T for user %q", upserted, upserted.GetName()) + s.logger.WarnContext(ctx, "unexpected user type", + "got_type", logutils.TypeAttr(upserted), + "expected_type", "UserV2", + "user", upserted.GetName(), + ) return nil, trace.BadParameter("encountered unexpected user type") } @@ -480,7 +497,7 @@ func (s *Service) DeleteUser(ctx context.Context, req *userspb.DeleteUserRequest var omitEditorEvent bool if err != nil && !trace.IsNotFound(err) { // don't return error here, delete may still succeed - s.logger.WithError(err).Warn("Failed getting previous user during delete operation") + s.logger.WarnContext(ctx, "Failed getting previous user during delete operation", "error", err) prevUser = nil omitEditorEvent = true } @@ -518,7 +535,7 @@ func (s *Service) DeleteUser(ctx context.Context, req *userspb.DeleteUserRequest }, ConnectionMetadata: authz.ConnectionMetadata(ctx), }); err != nil { - s.logger.WithError(err).Warn("Failed to emit user delete event.") + s.logger.WarnContext(ctx, "Failed to emit user delete event", "error", err) } if !omitEditorEvent { @@ -539,7 +556,7 @@ func (s *Service) ListUsers(ctx context.Context, req *userspb.ListUsersRequest) // migrated to that model. if !authz.HasBuiltinRole(*authCtx, string(types.RoleAdmin)) { err := trace.AccessDenied("user %q requested access to all users with secrets", authCtx.User.GetName()) - s.logger.Warn(err) + s.logger.WarnContext(ctx, "user does not have permission to read all users with secrets", "error", err) if err := s.emitter.EmitAuditEvent(ctx, &apievents.UserLogin{ Metadata: apievents.Metadata{ Type: events.UserLoginEvent, @@ -552,7 +569,7 @@ func (s *Service) ListUsers(ctx context.Context, req *userspb.ListUsersRequest) UserMessage: err.Error(), }, }); err != nil { - s.logger.WithError(err).Warn("Failed to emit local login failure event.") + s.logger.WarnContext(ctx, "Failed to emit local login failure event", "error", err) } return nil, trace.AccessDenied("this request can be only executed by an admin") } diff --git a/lib/auth/usertoken.go b/lib/auth/usertoken.go index 5959f223cb491..2fceb89c64bb3 100644 --- a/lib/auth/usertoken.go +++ b/lib/auth/usertoken.go @@ -103,7 +103,7 @@ func (a *Server) CreateResetPasswordToken(ctx context.Context, req authclient.Cr Expires: a.GetClock().Now().UTC().Add(req.TTL), }, }); err != nil { - log.WithError(err).Warn("Failed to emit create reset password token event.") + a.logger.WarnContext(ctx, "Failed to emit create reset password token event", "error", err) } return a.GetUserToken(ctx, token.GetName()) @@ -150,7 +150,7 @@ func formatAccountName(s proxyDomainGetter, username string, authHostname string if len(proxies) == 0 { proxyHost, err = s.GetDomainName() if err != nil { - log.Errorf("Failed to retrieve cluster name, falling back to hostname: %v.", err) + logger.ErrorContext(context.TODO(), "Failed to retrieve cluster name, falling back to hostname", "error", err) proxyHost = authHostname } } else { @@ -363,7 +363,7 @@ func (a *Server) createRecoveryToken(ctx context.Context, username, tokenType st Expires: a.GetClock().Now().UTC().Add(req.TTL), }, }); err != nil { - log.WithError(err).Warn("Failed to emit create recovery token event.") + a.logger.WarnContext(ctx, "Failed to emit create recovery token event", "error", err) } return newToken, nil @@ -442,7 +442,7 @@ func (a *Server) createPrivilegeToken(ctx context.Context, username, tokenKind s Expires: a.GetClock().Now().UTC().Add(req.TTL), }, }); err != nil { - log.WithError(err).Warn("Failed to emit create privilege token event.") + a.logger.WarnContext(ctx, "Failed to emit create privilege token event", "error", err) } convertedToken, ok := token.(*types.UserTokenV3) @@ -454,10 +454,13 @@ func (a *Server) createPrivilegeToken(ctx context.Context, username, tokenKind s } // verifyUserToken verifies that the token is not expired and is of the allowed kinds. -func (a *Server) verifyUserToken(token types.UserToken, allowedKinds ...string) error { +func (a *Server) verifyUserToken(ctx context.Context, token types.UserToken, allowedKinds ...string) error { if token.Expiry().Before(a.clock.Now().UTC()) { // Provide obscure message on purpose, while logging the real error server side. - log.Debugf("Expired token(%s) type(%s)", token.GetName(), token.GetSubKind()) + a.logger.DebugContext(ctx, "Expired token", + "token", token.GetName(), + "token_type", token.GetSubKind(), + ) return trace.AccessDenied("invalid token") } @@ -467,6 +470,10 @@ func (a *Server) verifyUserToken(token types.UserToken, allowedKinds ...string) } } - log.Debugf("Invalid token(%s) type(%s), expected type: %v", token.GetName(), token.GetSubKind(), allowedKinds) + a.logger.DebugContext(ctx, "Invalid token", + "token", token.GetName(), + "token_type", token.GetSubKind(), + "expected_type", allowedKinds, + ) return trace.AccessDenied("invalid token") } diff --git a/lib/auth/webauthn/attestation.go b/lib/auth/webauthn/attestation.go index a382229dce048..fa14c23a21ed1 100644 --- a/lib/auth/webauthn/attestation.go +++ b/lib/auth/webauthn/attestation.go @@ -19,17 +19,22 @@ package webauthn import ( + "context" "crypto/x509" "encoding/pem" + "log/slog" "slices" "github.com/go-webauthn/webauthn/protocol" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" + logutils "github.com/gravitational/teleport/lib/utils/log" ) +var log = logutils.NewPackageLogger(teleport.ComponentKey, "WebAuthn") + // x5cFormats enumerates all attestation formats that supply an attestation // chain through the "x5c" field. // See https://www.w3.org/TR/webauthn/#sctn-defined-attestation-formats. @@ -138,13 +143,17 @@ func getChainFromX5C(obj protocol.AttestationObject) ([]*x509.Certificate, error // Print out attestation certs if debug is enabled. // This may come in handy for people having trouble with their setups. - if log.IsLevelEnabled(log.DebugLevel) { + ctx := context.Background() + if log.Handler().Enabled(ctx, slog.LevelDebug) { for _, cert := range chain { certPEM := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: cert.Raw, }) - log.Debugf("WebAuthn: got %q attestation certificate:\n\n%s", obj.Format, certPEM) + log.DebugContext(context.Background(), "got attestation certificate", + "format", obj.Format, + "certificate", string(certPEM), + ) } } diff --git a/lib/auth/webauthn/device.go b/lib/auth/webauthn/device.go index a78ec9f7fea4f..700f671b4d264 100644 --- a/lib/auth/webauthn/device.go +++ b/lib/auth/webauthn/device.go @@ -19,6 +19,7 @@ package webauthn import ( + "context" "crypto/ecdsa" "crypto/x509" @@ -26,7 +27,6 @@ import ( "github.com/go-webauthn/webauthn/protocol/webauthncose" wan "github.com/go-webauthn/webauthn/webauthn" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" ) @@ -51,7 +51,7 @@ func deviceToCredential( var err error pubKeyCBOR, err = u2fDERKeyToCBOR(dev.U2F.PubKey) if err != nil { - log.Warnf("WebAuthn: failed to convert U2F device key to CBOR: %v", err) + log.WarnContext(context.Background(), "failed to convert U2F device key to CBOR", "error", err) return wan.Credential{}, false } } diff --git a/lib/auth/webauthn/login.go b/lib/auth/webauthn/login.go index 2ed9f085155a6..9948602beeba6 100644 --- a/lib/auth/webauthn/login.go +++ b/lib/auth/webauthn/login.go @@ -31,7 +31,6 @@ import ( wan "github.com/go-webauthn/webauthn/webauthn" gogotypes "github.com/gogo/protobuf/types" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" mfav1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/mfa/v1" "github.com/gravitational/teleport/api/types" @@ -107,13 +106,14 @@ func (f *loginFlow) begin(ctx context.Context, user string, challengeExtensions continue } - log.Errorf(""+ - "User device %q/%q has unexpected RPID=%q, excluding from allowed credentials. "+ - "RPID changes are not supported by WebAuthn, this is likely to cause permanent authentication problems for your users. "+ - "Consider reverting the change or reset your users so they may register their devices again.", - user, - devices[i].GetName(), - webDev.CredentialRpId) + const msg = "User device has unexpected RPID, excluding from allowed credentials. " + + "RPID changes are not supported by WebAuthn, this is likely to cause permanent authentication problems for your users. " + + "Consider reverting the change or reset your users so they may register their devices again." + log.ErrorContext(ctx, msg, + "user", user, + "device", devices[i].GetName(), + "rpid", webDev.CredentialRpId, + ) // "Cut" device from slice. devices = slices.Delete(devices, i, i+1) @@ -249,7 +249,7 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *wantypes.Cred origin := parsedResp.Response.CollectedClientData.Origin if err := validateOrigin(origin, f.Webauthn.RPID); err != nil { - log.WithError(err).Debugf("WebAuthn: origin validation failed") + log.DebugContext(ctx, "origin validation failed", "error", err) return nil, trace.Wrap(err) } @@ -330,9 +330,9 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *wantypes.Cred if (discoverableLogin || uvr == protocol.VerificationRequired) && sd.UserVerification != string(protocol.VerificationRequired) { // This is not a failure yet, but will likely become one. sd.UserVerification = string(protocol.VerificationRequired) - log.Warnf(""+ - "WebAuthn: User verification required by extensions but not by challenge. "+ - "Increased SessionData.UserVerification to %s.", sd.UserVerification) + const msg = "User verification required by extensions but not by challenge. " + + "Increased SessionData.UserVerification." + log.WarnContext(ctx, msg, "user_verification", sd.UserVerification) } sessionData := wantypes.SessionDataToProtocol(sd) @@ -371,8 +371,10 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *wantypes.Cred return nil, trace.Wrap(err) } if credential.Authenticator.CloneWarning { - log.Warnf( - "WebAuthn: Clone warning detected for user %q / device %q. Device counter may be malfunctioning.", user, dev.GetName()) + log.WarnContext(ctx, "Clone warning detected for device, the device counter may be malfunctioning", + "user", user, + "device", dev.GetName(), + ) } // Update last used timestamp and device counter. @@ -381,7 +383,11 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *wantypes.Cred } // Retroactively write the credential RPID, now that it cleared authn. if webDev := dev.GetWebauthn(); webDev != nil && webDev.CredentialRpId == "" { - log.Debugf("WebAuthn: Recording RPID=%q in device %q/%q", rpID, user, dev.GetName()) + log.DebugContext(ctx, "Recording RPID in device", + "rpid", rpID, + "user", user, + "device", dev.GetName(), + ) webDev.CredentialRpId = rpID } @@ -395,7 +401,10 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *wantypes.Cred // passes. if sd.ChallengeExtensions.AllowReuse != mfav1.ChallengeAllowReuse_CHALLENGE_ALLOW_REUSE_YES { if err := f.sessionData.Delete(ctx, user, challenge); err != nil { - log.Warnf("WebAuthn: failed to delete login SessionData for user %v (scope = %s)", user, sd.ChallengeExtensions.Scope) + log.WarnContext(ctx, "failed to delete login SessionData for user", + "user", user, + "scope", sd.ChallengeExtensions.Scope, + ) } } @@ -463,19 +472,19 @@ func updateCredentialAndTimestamps( d.Webauthn.CredentialBackupEligible = &gogotypes.BoolValue{ Value: credential.Flags.BackupEligible, } - log.WithFields(log.Fields{ - "device": dest.GetName(), - "be": credential.Flags.BackupEligible, - }).Debug("Backfilled Webauthn device BE flag") + log.DebugContext(context.Background(), "Backfilled Webauthn device BE flag", + "device", dest.GetName(), + "be", credential.Flags.BackupEligible, + ) } if d.Webauthn.CredentialBackedUp == nil { d.Webauthn.CredentialBackedUp = &gogotypes.BoolValue{ Value: credential.Flags.BackupState, } - log.WithFields(log.Fields{ - "device": dest.GetName(), - "bs": credential.Flags.BackupState, - }).Debug("Backfilled Webauthn device BS flag") + log.DebugContext(context.Background(), "Backfilled Webauthn device BS flag", + "device", dest.GetName(), + "bs", credential.Flags.BackupState, + ) } default: diff --git a/lib/auth/webauthn/register.go b/lib/auth/webauthn/register.go index 50eb3420391eb..9d6a0726af232 100644 --- a/lib/auth/webauthn/register.go +++ b/lib/auth/webauthn/register.go @@ -32,7 +32,6 @@ import ( gogotypes "github.com/gogo/protobuf/types" "github.com/google/uuid" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" @@ -284,7 +283,7 @@ func (f *RegistrationFlow) Finish(ctx context.Context, req RegisterResponse) (*t origin := parsedResp.Response.CollectedClientData.Origin if err := validateOrigin(origin, f.Webauthn.RPID); err != nil { - log.WithError(err).Debugf("WebAuthn: origin validation failed") + log.DebugContext(ctx, "origin validation failed", "error", err) return nil, trace.Wrap(err) } @@ -332,7 +331,7 @@ func (f *RegistrationFlow) Finish(ctx context.Context, req RegisterResponse) (*t protocolErr.Type == protocol.ErrVerification.Type && passwordless && !parsedResp.Response.AttestationObject.AuthData.Flags.UserVerified() { - log.WithError(err).Debug("WebAuthn: Replacing verification error with PIN message") + log.DebugContext(ctx, "WebAuthn: Replacing verification error with PIN message", "error", err) return nil, trace.BadParameter("authenticator doesn't support passwordless, setting up a PIN may fix this") } @@ -378,7 +377,7 @@ func (f *RegistrationFlow) Finish(ctx context.Context, req RegisterResponse) (*t // Registration complete, remove the registration challenge we just used. if err := f.Identity.DeleteWebauthnSessionData(ctx, req.User, scopeSession); err != nil { - log.Warnf("WebAuthn: failed to delete registration SessionData for user %v", req.User) + log.WarnContext(ctx, "failed to delete registration SessionData for user", "user", req.User, "error", err) } return newDevice, nil diff --git a/lib/auth/webauthncli/api.go b/lib/auth/webauthncli/api.go index 8278c77a03634..5ee0f9e4f7d3c 100644 --- a/lib/auth/webauthncli/api.go +++ b/lib/auth/webauthncli/api.go @@ -24,14 +24,20 @@ import ( "strings" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" oteltrace "go.opentelemetry.io/otel/trace" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/observability/tracing" "github.com/gravitational/teleport/lib/auth/touchid" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" wanwin "github.com/gravitational/teleport/lib/auth/webauthnwin" + logutils "github.com/gravitational/teleport/lib/utils/log" +) + +var ( + log = logutils.NewPackageLogger(teleport.ComponentKey, "WebAuthn") + fidoLog = logutils.NewPackageLogger(teleport.ComponentKey, "FIDO2") ) // ErrUsingNonRegisteredDevice is returned from Login when the user attempts to @@ -137,10 +143,10 @@ func Login( switch { case origin == "", assertion == nil: // let downstream handle empty/nil case !strings.HasPrefix(origin, "https://"+assertion.Response.RelyingPartyID): - log.Warnf(""+ - "WebAuthn: origin and RPID mismatch, "+ - "if you are having authentication problems double check your proxy address "+ - "(%q vs %q)", origin, assertion.Response.RelyingPartyID) + log.WarnContext(ctx, "origin and RPID mismatch, if you are having authentication problems double check your proxy address", + "origin", origin, + "rpid", assertion.Response.RelyingPartyID, + ) } var attachment AuthenticatorAttachment @@ -151,7 +157,7 @@ func Login( } if wanwin.IsAvailable() { - log.Debug("WebAuthnWin: Using windows webauthn for credential assertion") + log.DebugContext(ctx, "Using windows webauthn for credential assertion") return wanwin.Login(ctx, origin, assertion, &wanwin.LoginOpts{ AuthenticatorAttachment: wanwin.AuthenticatorAttachment(attachment), }) @@ -159,19 +165,19 @@ func Login( switch attachment { case AttachmentCrossPlatform: - log.Debug("Cross-platform login") + log.DebugContext(ctx, "Cross-platform login") return crossPlatformLogin(ctx, origin, assertion, prompt, opts) case AttachmentPlatform: - log.Debug("Platform login") + log.DebugContext(ctx, "Platform login") return platformLogin(origin, user, assertion, prompt) default: - log.Debug("Attempting platform login") + log.DebugContext(ctx, "Attempting platform login") resp, credentialUser, err := platformLogin(origin, user, assertion, prompt) if !errors.Is(err, &touchid.ErrAttemptFailed{}) { return resp, credentialUser, trace.Wrap(err) } - log.WithError(err).Debug("Platform login failed, falling back to cross-platform") + log.DebugContext(ctx, "Platform login failed, falling back to cross-platform", "error", err) return crossPlatformLogin(ctx, origin, assertion, prompt, opts) } } @@ -180,7 +186,7 @@ func crossPlatformLogin( ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, ) (*proto.MFAAuthenticateResponse, string, error) { - log.Debug("FIDO2: Using libfido2 for assertion") + fidoLog.DebugContext(ctx, "Using libfido2 for assertion") resp, user, err := FIDO2Login(ctx, origin, assertion, prompt, opts) return resp, user, trace.Wrap(err) } @@ -223,11 +229,11 @@ func Register( ctx context.Context, origin string, cc *wantypes.CredentialCreation, prompt RegisterPrompt) (*proto.MFARegisterResponse, error) { if wanwin.IsAvailable() { - log.Debug("WebAuthnWin: Using windows webauthn for credential creation") + log.DebugContext(ctx, "Using windows webauthn for credential creation") return wanwin.Register(ctx, origin, cc) } - log.Debug("FIDO2: Using libfido2 for credential creation") + fidoLog.DebugContext(ctx, "Using libfido2 for credential creation") resp, err := FIDO2Register(ctx, origin, cc, prompt) return resp, trace.Wrap(err) } diff --git a/lib/auth/webauthncli/fido2.go b/lib/auth/webauthncli/fido2.go index a20b714fc2fcb..33a033a26b383 100644 --- a/lib/auth/webauthncli/fido2.go +++ b/lib/auth/webauthncli/fido2.go @@ -29,6 +29,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "sync" "time" @@ -37,7 +38,6 @@ import ( "github.com/go-webauthn/webauthn/protocol/webauthncose" "github.com/gravitational/trace" "github.com/keys-pub/go-libfido2" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/client/proto" wanpb "github.com/gravitational/teleport/api/types/webauthn" @@ -165,7 +165,11 @@ func fido2Login( // Presence of any allowed credential is interpreted as the user identity // being partially established, aka non-passwordless. passwordless := len(allowedCreds) == 0 - log.Debugf("FIDO2: assertion: passwordless=%v, uv=%v, %v allowed credentials", passwordless, uv, len(allowedCreds)) + fidoLog.DebugContext(ctx, "assertion", + "passwordless", passwordless, + "uv", uv, + "allowed_cred_count", len(allowedCreds), + ) // Prepare challenge data for the device. ccdJSON, err := json.Marshal(&CollectedClientData{ @@ -209,7 +213,11 @@ func fido2Login( deviceCallback := func(dev FIDODevice, info *deviceInfo, pin string) error { actualRPID := rpID if usesAppID(dev, info, ccdHash[:], allowedCreds, rpID, appID) { - log.Debugf("FIDO2: Device %v registered for AppID (%q) instead of RPID", info.path, appID) + fidoLog.DebugContext(ctx, "Device registered for AppID instead of RPID", + "device", info.path, + "app_id", appID, + "rp_id", rpID, + ) actualRPID = appID } @@ -227,7 +235,7 @@ func fido2Login( // Happens inconsistently in some authenticator series (YubiKey 5). // We are relying on the fact that, because the PIN is set, the // authenticator will set the UV bit regardless of it being requested. - log.Debugf("FIDO2: Device %v: retrying assertion without UV", info.path) + fidoLog.DebugContext(ctx, "Retrying assertion without UV", "device", info.path) opts.UV = libfido2.Default assertions, err = devAssertion(dev, info, actualRPID, ccdHash[:], allowedCreds, pin, opts) } @@ -239,7 +247,7 @@ func fido2Login( // touch - this causes another slew of problems with abandoned U2F // goroutines during registration. if !info.fido2 { - log.Debugf("FIDO2: U2F device %v not registered, ignoring it", info.path) + fidoLog.DebugContext(ctx, "U2F device not registered, ignoring it", "device", info.path) err = &nonInteractiveError{err: err} } else { err = ErrUsingNonRegisteredDevice // "Upgrade" error message. @@ -248,7 +256,7 @@ func fido2Login( if err != nil { return trace.Wrap(err) } - log.Debugf("FIDO2: Got %v assertions", len(assertions)) + fidoLog.DebugContext(ctx, "Got assertions", "assertion_count", len(assertions)) // Find assertion for target user, or show the prompt. assertion, err := pickAssertion(assertions, prompt, user, passwordless) @@ -256,9 +264,11 @@ func fido2Login( return trace.Wrap(err) } - log.Debugf( - "FIDO2: Authenticated: credential ID (b64) = %v, user ID (hex) = %x, user name = %q", - base64.RawURLEncoding.EncodeToString(assertion.CredentialID), assertion.User.ID, assertion.User.Name) + fidoLog.DebugContext(ctx, "Authenticated", + "credential_id", base64.RawURLEncoding.EncodeToString(assertion.CredentialID), + "user_id", assertion.User.ID, + "user_name", assertion.User.Name, + ) // Use the first successful assertion. // In practice it is very unlikely we'd hit this twice. @@ -342,13 +352,16 @@ func devAssertion( return nil, trace.Wrap(libfido2.ErrUserPresenceRequired) } - if log.IsLevelEnabled(log.DebugLevel) { + if fidoLog.Enabled(context.Background(), slog.LevelDebug) { credPrefix := hex.EncodeToString(cred) const prefixLen = 10 if len(credPrefix) > prefixLen { credPrefix = credPrefix[:prefixLen] } - log.Debugf("FIDO2: Device %v: Using credential %v...", info.path, credPrefix) + fidoLog.DebugContext(context.Background(), "Using credential", + "device", info.path, + "credential", credPrefix, + ) } allowedCreds = [][]byte{cred} @@ -451,7 +464,7 @@ func fido2Register( if err != nil { return nil, trace.Wrap(err) } - log.Debugf("FIDO2: registration: resident key=%v", rrk) + fidoLog.DebugContext(ctx, "registration", "resident_key", rrk) // Can we create ES256 keys? // TODO(codingllama): Consider supporting other algorithms and respecting @@ -526,12 +539,13 @@ func fido2Register( case err != nil: // Swallow unexpected errors: a double registration is better than // aborting the ceremony. - log.Debugf( - "FIDO2: Device %v: excluded credential assertion failed, letting device through: err=%q", - info.path, err) + fidoLog.DebugContext(ctx, "excluded credential assertion failed, letting device through", + "device", info.path, + "error", err, + ) return nil default: - log.Debugf("FIDO2: Device %v: filtered due to presence of excluded credential", info.path) + fidoLog.DebugContext(ctx, "filtered due to presence of excluded credential", "device", info.path) return errHasExcludedCredential } } @@ -619,7 +633,7 @@ func makeAttStatement(attestation *libfido2.Attestation) (string, map[string]int case none: return format, nil, nil default: - log.Debugf(`FIDO2: Unsupported attestation format %q, using "none"`, format) + fidoLog.DebugContext(context.Background(), `Unsupported attestation format, using "none"`, "attestation_format", format) return none, nil, nil } @@ -686,11 +700,11 @@ func runOnFIDO2Devices( case <-devicesC: receiveCount++ case <-maxWait.C: - log.Debugf("FIDO2: Abandoning device goroutines after %s", fido2DeviceMaxWait) + fidoLog.DebugContext(ctx, "Abandoning device goroutines after exceeding wait time", "max_wait_time", fido2DeviceMaxWait) return } } - log.Debug("FIDO2: Device goroutines exited cleanly") + fidoLog.DebugContext(ctx, "Device goroutines exited cleanly") }() // First "interactive" response wins. @@ -701,7 +715,7 @@ func runOnFIDO2Devices( // Keep going on cancels or non-interactive errors. if errors.Is(err, libfido2.ErrKeepaliveCancel) || errors.Is(err, &nonInteractiveError{}) { - log.Debugf("FIDO2: Got cancel or non-interactive device error: %v", err) + fidoLog.DebugContext(ctx, "Got cancel or non-interactive device error", "error", err) continue } @@ -729,7 +743,10 @@ func startDevices( for i, dev := range fidoDevs { path := openDevs[i].path err := dev.Close() - log.Debugf("FIDO2: Close device %v, err=%v", path, err) + fidoLog.DebugContext(context.Background(), "Close device", + "device", path, + "error", err, + ) } } @@ -747,7 +764,10 @@ func startDevices( // This is largely safe to ignore, as opening is fairly consistent in // other situations and failures are likely from a non-chosen device in // multi-device scenarios. - log.Debugf("FIDO2: Device %v failed to open, skipping: %v", path, err) + fidoLog.DebugContext(context.Background(), "Device failed to open, skipping", + "device", path, + "error", err, + ) continue } @@ -828,7 +848,10 @@ func (l *openedDevices) cancelAll(except FIDODevice) { // Note that U2F devices fail Cancel with "invalid argument". err := d.dev.Cancel() - log.Debugf("FIDO2: Cancel device %v, err=%v", d.path, err) + fidoLog.DebugContext(context.Background(), "Cancel device", + "device", d.path, + "error", err, + ) } } @@ -841,10 +864,14 @@ func handleDevice( firstTouchAck func() error, pinPrompt runPrompt, ) error { + ctx := context.Background() // handleDevice owns the device, thus it has the privilege to shut it down. defer func() { err := dev.Close() - log.Debugf("FIDO2: Close device %v, err=%v", path, err) + fidoLog.DebugContext(ctx, "Close device", + "device", path, + "error", err, + ) }() if err := dev.SetTimeout(fido2DeviceTimeout); err != nil { @@ -862,16 +889,22 @@ func handleDevice( if err != nil { return trace.Wrap(&nonInteractiveError{err: err}) } - log.Debugf("FIDO2: Device %v: info %#v", path, info) + fidoLog.DebugContext(ctx, "Device", + "path", path, + "info", info, + ) } else { - log.Debugf("FIDO2: Device %v: not a FIDO2 device", path) + fidoLog.DebugContext(ctx, "not a FIDO2 device", "device", path) } di := makeDevInfo(path, info, isFIDO2) // Apply initial filters, waiting for confirmation if the filter fails before // relaying the error. if err := filter(dev, di); err != nil { - log.Debugf("FIDO2: Device %v filtered, err=%v", path, err) + fidoLog.DebugContext(ctx, "Device filtered", + "device", path, + "error", err, + ) // If the device is chosen then treat the error as interactive. if touched, _ := waitForTouch(dev); touched { @@ -885,7 +918,11 @@ func handleDevice( // Run the callback. cb := withPINHandler(withRetries(deviceCallback)) requiresPIN, err := cb(dev, di, "" /* pin */) - log.Debugf("FIDO2: Device %v: callback returned, requiresPIN=%v, err=%v", path, requiresPIN, err) + fidoLog.DebugContext(ctx, "callback returned", + "device", path, + "requires_pin", requiresPIN, + "error", err, + ) if err != nil { return trace.Wrap(err) } @@ -932,7 +969,11 @@ func devInfo(path string, dev FIDODevice) (*libfido2.DeviceInfo, error) { } lastErr = err - log.Debugf("FIDO2: Device %v: Info failed, retrying after %s: %v", path, fido2RetryInterval, err) + fidoLog.DebugContext(context.Background(), "Info failed, retrying", + "device", path, + "backoff_duration", fido2RetryInterval, + "error", err, + ) time.Sleep(fido2RetryInterval) } @@ -955,7 +996,7 @@ func withRetries(callback deviceCallbackFunc) deviceCallbackFunc { // ErrOperationDenied happens when fingerprint reading fails (UV=false). if errors.Is(err, libfido2.ErrOperationDenied) { fmt.Println("Gesture validation failed, make sure you use a registered fingerprint") - log.Debug("FIDO2: Retrying libfido2 error 'operation denied'") + fidoLog.DebugContext(context.Background(), "Retrying libfido2 error 'operation denied'") continue } @@ -975,7 +1016,7 @@ func withRetries(callback deviceCallbackFunc) deviceCallbackFunc { "Alternatively, you may unblock your device by using it in the Web UI." return trace.Wrap(err, msg) case 63: // FIDO_ERR_UV_INVALID, 0x3f - log.Debug("FIDO2: Retrying libfido2 error 63") + fidoLog.DebugContext(context.Background(), "Retrying libfido2 error 63") continue default: // Unexpected code. return err @@ -1041,7 +1082,7 @@ func waitForTouch(dev FIDODevice) (touched bool, err error) { touch, err := dev.TouchBegin() if err != nil { // Error logged here as it's mostly ignored by callers. - log.Debugf("FIDO2: Device touch begin error: %v", err) + fidoLog.DebugContext(context.Background(), "Device touch begin error", "error", err) return false, trace.Wrap(err) } defer touch.Stop() @@ -1051,7 +1092,7 @@ func waitForTouch(dev FIDODevice) (touched bool, err error) { touched, err := touch.Status(fido2TouchMaxWait) if err != nil { // Error logged here as it's mostly ignored by callers. - log.Debugf("FIDO2: Device touch status error: %v", err) + fidoLog.DebugContext(context.Background(), "Device touch status error", "error", err) return false, trace.Wrap(err) } if touched { diff --git a/lib/auth/webauthnwin/api.go b/lib/auth/webauthnwin/api.go index 4e8de09029911..5d3cdcefe22e5 100644 --- a/lib/auth/webauthnwin/api.go +++ b/lib/auth/webauthnwin/api.go @@ -27,13 +27,13 @@ import ( "context" "fmt" "io" + "log/slog" "os" "sync" "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/protocol/webauthncose" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/client/proto" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" @@ -221,7 +221,7 @@ type CheckSupportResult struct { func IsAvailable() bool { supports := CheckSupport() if supports.HasCompileSupport && !supports.IsAvailable { - log.Warn("Webauthn is not supported on this version of Windows, supported from version 1903") + slog.WarnContext(context.Background(), "Webauthn is not supported on this version of Windows, supported from version 1903") } return supports.IsAvailable diff --git a/lib/auth/webauthnwin/webauthn_windows.go b/lib/auth/webauthnwin/webauthn_windows.go index 681120739aa92..0f07071ba498e 100644 --- a/lib/auth/webauthnwin/webauthn_windows.go +++ b/lib/auth/webauthnwin/webauthn_windows.go @@ -19,17 +19,19 @@ package webauthnwin import ( + "context" "encoding/base64" "errors" "fmt" + "log/slog" "syscall" "unsafe" "github.com/go-webauthn/webauthn/protocol" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "golang.org/x/sys/windows" + "github.com/gravitational/teleport" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) @@ -52,27 +54,26 @@ func newNativeImpl() *nativeImpl { hasCompileSupport: true, } + logger := slog.With(teleport.ComponentKey, "WebAuthnWin") + ctx := context.Background() + // Explicitly loading the module avoids a panic when calling DLL functions if // the DLL is missing. // https://github.com/gravitational/teleport/issues/36851 if err := modWebAuthn.Load(); err != nil { - log. - WithError(err). - Debug("WebAuthnWin: failed to load WebAuthn.dll (it's likely missing)") + logger.DebugContext(ctx, "failed to load WebAuthn.dll (it's likely missing)", "error", err) return n } // Load WebAuthNGetApiVersionNumber explicitly too, it avoids a panic on some // Windows Server 2019 installs. if err := procWebAuthNGetApiVersionNumber.Find(); err != nil { - log. - WithError(err). - Debug("WebAuthnWin: failed to load WebAuthNGetApiVersionNumber") + logger.DebugContext(ctx, "failed to load WebAuthNGetApiVersionNumber", "error", err) return n } v, err := webAuthNGetApiVersionNumber() if err != nil { - log.WithError(err).Debug("WebAuthnWin: failed to check version") + logger.DebugContext(ctx, "failed to check version", "error", err) return n } n.webauthnAPIVersion = v @@ -86,7 +87,7 @@ func newNativeImpl() *nativeImpl { if err != nil { // This should not happen if dll exists, however we are fine with // to proceed without uvPlatform. - log.WithError(err).Debug("WebAuthnWin: failed to check isUVPlatformAuthenticatorAvailable") + logger.DebugContext(ctx, "failed to check isUVPlatformAuthenticatorAvailable", "error", err) } return n diff --git a/lib/authz/permissions.go b/lib/authz/permissions.go index e905d6d9d479f..b935e1caeacd0 100644 --- a/lib/authz/permissions.go +++ b/lib/authz/permissions.go @@ -548,7 +548,7 @@ func (a *authorizer) isAdminActionAuthorizationRequired(ctx context.Context, aut if impersonator := ident.Impersonator; impersonator != "" { impersonatorUser, err := a.accessPoint.GetUser(ctx, impersonator, false) if err == nil && impersonatorUser.IsBot() { - a.logger.DebugContext(ctx, "Skipping admin action MFA check for bot-impersonated identity", "identtity", ident) + a.logger.DebugContext(ctx, "Skipping admin action MFA check for bot-impersonated identity", "identity", ident) return false, nil } @@ -1288,7 +1288,6 @@ func definitionForBuiltinRole(clusterName string, recConfig readonly.SessionReco }, }, }) - } return nil, trace.NotFound("builtin role %q is not recognized", role.String()) diff --git a/lib/autoupdate/rollout/controller.go b/lib/autoupdate/rollout/controller.go index eb253f366b6a7..7ac3861e2bce0 100644 --- a/lib/autoupdate/rollout/controller.go +++ b/lib/autoupdate/rollout/controller.go @@ -26,6 +26,7 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/utils/retryutils" "github.com/gravitational/teleport/lib/utils/interval" ) @@ -65,15 +66,27 @@ func NewController(client Client, log *slog.Logger, clock clockwork.Clock, perio period = defaultReconcilerPeriod } + log = log.With(teleport.ComponentLabel, teleport.ComponentRolloutController) + + haltOnError, err := newHaltOnErrorStrategy(log) + if err != nil { + return nil, trace.Wrap(err, "failed to initialize halt-on-error strategy") + } + timeBased, err := newTimeBasedStrategy(log) + if err != nil { + return nil, trace.Wrap(err, "failed to initialize time-based strategy") + } + return &Controller{ clock: clock, log: log, reconciler: reconciler{ - clt: client, - log: log, - clock: clock, + clt: client, + log: log, + clock: clock, rolloutStrategies: []rolloutStrategy{ - // TODO(hugoShaka): add the strategies here as we implement them + timeBased, + haltOnError, }, }, period: period, @@ -91,6 +104,7 @@ func (c *Controller) Run(ctx context.Context) error { ticker := interval.New(config) defer ticker.Stop() + c.log.InfoContext(ctx, "Starting autoupdate_agent_rollout controller", "period", c.period) for { select { case <-ctx.Done(): diff --git a/lib/autoupdate/rollout/reconciler.go b/lib/autoupdate/rollout/reconciler.go index a6264ba3cbf27..02f393a58ff8d 100644 --- a/lib/autoupdate/rollout/reconciler.go +++ b/lib/autoupdate/rollout/reconciler.go @@ -166,6 +166,7 @@ func (r *reconciler) tryReconcile(ctx context.Context) error { // if there are no existing rollout, we create a new one and set the status if !rolloutExists { + r.log.DebugContext(ctx, "creating rollout") rollout, err := update.NewAutoUpdateAgentRollout(newSpec) rollout.Status = newStatus if err != nil { @@ -175,6 +176,7 @@ func (r *reconciler) tryReconcile(ctx context.Context) error { return trace.Wrap(err, "creating rollout") } + r.log.DebugContext(ctx, "updating rollout") // If there was a previous rollout, we update its spec and status and do an update. // We don't create a new resource to keep the metadata containing the revision ID. existingRollout.Spec = newSpec @@ -295,6 +297,16 @@ func (r *reconciler) computeStatus( // compute the group state changes now := r.clock.Now() + // If timeOverride is set to a non-zero value (we have two potential zeros, go time's zero and timestamppb's zero) + // we use this instead of the clock's time. + if timeOverride := status.GetTimeOverride().AsTime(); !(timeOverride.IsZero() || timeOverride.Unix() == 0) { + r.log.DebugContext(ctx, "reconciling with synthetic time instead of real time", + "time_override", timeOverride, + "real_time", now, + ) + now = timeOverride + } + // If this is a new rollout or the rollout has been reset, we create groups from the config groups := status.GetGroups() var err error @@ -306,7 +318,7 @@ func (r *reconciler) computeStatus( } status.Groups = groups - err = r.progressRollout(ctx, newSpec.GetStrategy(), status) + err = r.progressRollout(ctx, newSpec.GetStrategy(), status, now) // Failing to progress the update is not a hard failure. // We want to update the status even if something went wrong to surface the failed reconciliation and potential errors to the user. if err != nil { @@ -322,10 +334,10 @@ func (r *reconciler) computeStatus( // groups are updated in place. // If an error is returned, the groups should still be upserted, depending on the strategy, // failing to update a group might not be fatal (other groups can still progress independently). -func (r *reconciler) progressRollout(ctx context.Context, strategyName string, status *autoupdate.AutoUpdateAgentRolloutStatus) error { +func (r *reconciler) progressRollout(ctx context.Context, strategyName string, status *autoupdate.AutoUpdateAgentRolloutStatus, now time.Time) error { for _, strategy := range r.rolloutStrategies { if strategy.name() == strategyName { - return strategy.progressRollout(ctx, status) + return strategy.progressRollout(ctx, status, now) } } return trace.NotImplemented("rollout strategy %q not implemented", strategyName) diff --git a/lib/autoupdate/rollout/reconciler_test.go b/lib/autoupdate/rollout/reconciler_test.go index b5b77cd735edb..c2739685b7f72 100644 --- a/lib/autoupdate/rollout/reconciler_test.go +++ b/lib/autoupdate/rollout/reconciler_test.go @@ -714,7 +714,7 @@ func (f *fakeRolloutStrategy) name() string { return f.strategyName } -func (f *fakeRolloutStrategy) progressRollout(ctx context.Context, status *autoupdate.AutoUpdateAgentRolloutStatus) error { +func (f *fakeRolloutStrategy) progressRollout(ctx context.Context, status *autoupdate.AutoUpdateAgentRolloutStatus, now time.Time) error { f.calls++ return nil } diff --git a/lib/autoupdate/rollout/strategy.go b/lib/autoupdate/rollout/strategy.go index b3e214f6cebd7..d2f8c2da81f93 100644 --- a/lib/autoupdate/rollout/strategy.go +++ b/lib/autoupdate/rollout/strategy.go @@ -40,7 +40,7 @@ const ( // This interface allows us to inject dummy strategies for simpler testing. type rolloutStrategy interface { name() string - progressRollout(context.Context, *autoupdate.AutoUpdateAgentRolloutStatus) error + progressRollout(context.Context, *autoupdate.AutoUpdateAgentRolloutStatus, time.Time) error } func inWindow(group *autoupdate.AutoUpdateAgentRolloutStatusGroup, now time.Time) (bool, error) { diff --git a/lib/autoupdate/rollout/strategy_haltonerror.go b/lib/autoupdate/rollout/strategy_haltonerror.go index 0ab57a052768d..6ed1a4aae049d 100644 --- a/lib/autoupdate/rollout/strategy_haltonerror.go +++ b/lib/autoupdate/rollout/strategy_haltonerror.go @@ -24,7 +24,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/jonboulle/clockwork" "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" update "github.com/gravitational/teleport/api/types/autoupdate" @@ -39,29 +38,23 @@ const ( ) type haltOnErrorStrategy struct { - log *slog.Logger - clock clockwork.Clock + log *slog.Logger } func (h *haltOnErrorStrategy) name() string { return update.AgentsStrategyHaltOnError } -func newHaltOnErrorStrategy(log *slog.Logger, clock clockwork.Clock) (rolloutStrategy, error) { +func newHaltOnErrorStrategy(log *slog.Logger) (rolloutStrategy, error) { if log == nil { return nil, trace.BadParameter("missing log") } - if clock == nil { - return nil, trace.BadParameter("missing clock") - } return &haltOnErrorStrategy{ - log: log.With("strategy", update.AgentsStrategyHaltOnError), - clock: clock, + log: log.With("strategy", update.AgentsStrategyHaltOnError), }, nil } -func (h *haltOnErrorStrategy) progressRollout(ctx context.Context, status *autoupdate.AutoUpdateAgentRolloutStatus) error { - now := h.clock.Now() +func (h *haltOnErrorStrategy) progressRollout(ctx context.Context, status *autoupdate.AutoUpdateAgentRolloutStatus, now time.Time) error { // We process every group in order, all the previous groups must be in the DONE state // for the next group to become active. Even if some early groups are not DONE, // later groups might be ACTIVE and need to transition to DONE, so we cannot diff --git a/lib/autoupdate/rollout/strategy_haltonerror_test.go b/lib/autoupdate/rollout/strategy_haltonerror_test.go index 84d4de069efe3..ee3eb8e80ffca 100644 --- a/lib/autoupdate/rollout/strategy_haltonerror_test.go +++ b/lib/autoupdate/rollout/strategy_haltonerror_test.go @@ -134,7 +134,7 @@ func Test_canStartHaltOnError(t *testing.T) { func Test_progressGroupsHaltOnError(t *testing.T) { clock := clockwork.NewFakeClockAt(testSunday) log := utils.NewSlogLoggerForTests() - strategy, err := newHaltOnErrorStrategy(log, clock) + strategy, err := newHaltOnErrorStrategy(log) require.NoError(t, err) fewMinutesAgo := clock.Now().Add(-5 * time.Minute) @@ -500,7 +500,7 @@ func Test_progressGroupsHaltOnError(t *testing.T) { State: 0, StartTime: tt.rolloutStartTime, } - err := strategy.progressRollout(ctx, status) + err := strategy.progressRollout(ctx, status, clock.Now()) require.NoError(t, err) // We use require.Equal instead of Elements match because group order matters. // It's not super important for time-based, but is crucial for halt-on-error. diff --git a/lib/autoupdate/rollout/strategy_timebased.go b/lib/autoupdate/rollout/strategy_timebased.go index 13e844f0e4a5a..5d06adf0e5ace 100644 --- a/lib/autoupdate/rollout/strategy_timebased.go +++ b/lib/autoupdate/rollout/strategy_timebased.go @@ -21,9 +21,9 @@ package rollout import ( "context" "log/slog" + "time" "github.com/gravitational/trace" - "github.com/jonboulle/clockwork" "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" update "github.com/gravitational/teleport/api/types/autoupdate" @@ -35,29 +35,23 @@ const ( ) type timeBasedStrategy struct { - log *slog.Logger - clock clockwork.Clock + log *slog.Logger } func (h *timeBasedStrategy) name() string { return update.AgentsStrategyTimeBased } -func newTimeBasedStrategy(log *slog.Logger, clock clockwork.Clock) (rolloutStrategy, error) { +func newTimeBasedStrategy(log *slog.Logger) (rolloutStrategy, error) { if log == nil { return nil, trace.BadParameter("missing log") } - if clock == nil { - return nil, trace.BadParameter("missing clock") - } return &timeBasedStrategy{ - log: log.With("strategy", update.AgentsStrategyTimeBased), - clock: clock, + log: log.With("strategy", update.AgentsStrategyTimeBased), }, nil } -func (h *timeBasedStrategy) progressRollout(ctx context.Context, status *autoupdate.AutoUpdateAgentRolloutStatus) error { - now := h.clock.Now() +func (h *timeBasedStrategy) progressRollout(ctx context.Context, status *autoupdate.AutoUpdateAgentRolloutStatus, now time.Time) error { // We always process every group regardless of the order. var errs []error for _, group := range status.Groups { diff --git a/lib/autoupdate/rollout/strategy_timebased_test.go b/lib/autoupdate/rollout/strategy_timebased_test.go index 6402da3b21c44..84367f9927c04 100644 --- a/lib/autoupdate/rollout/strategy_timebased_test.go +++ b/lib/autoupdate/rollout/strategy_timebased_test.go @@ -34,7 +34,7 @@ import ( func Test_progressGroupsTimeBased(t *testing.T) { clock := clockwork.NewFakeClockAt(testSunday) log := utils.NewSlogLoggerForTests() - strategy, err := newTimeBasedStrategy(log, clock) + strategy, err := newTimeBasedStrategy(log) require.NoError(t, err) groupName := "test-group" @@ -332,7 +332,7 @@ func Test_progressGroupsTimeBased(t *testing.T) { State: 0, StartTime: tt.rolloutStartTime, } - err := strategy.progressRollout(ctx, status) + err := strategy.progressRollout(ctx, status, clock.Now()) require.NoError(t, err) // We use require.Equal instead of Elements match because group order matters. // It's not super important for time-based, but is crucial for halt-on-error. diff --git a/lib/client/alpn.go b/lib/client/alpn.go index db58eb374701f..9f88c69f1bdb1 100644 --- a/lib/client/alpn.go +++ b/lib/client/alpn.go @@ -129,7 +129,7 @@ func RunALPNAuthTunnel(ctx context.Context, cfg ALPNAuthTunnelConfig) error { go func() { defer cfg.Listener.Close() if err := lp.Start(ctx); err != nil { - log.WithError(err).Info("ALPN proxy stopped.") + log.InfoContext(ctx, "ALPN proxy stopped", "error", err) } }() diff --git a/lib/client/api.go b/lib/client/api.go index 9dd82e6af631a..68084d4833089 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -40,7 +40,6 @@ import ( "unicode/utf8" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/attribute" oteltrace "go.opentelemetry.io/otel/trace" "golang.org/x/crypto/ssh" @@ -130,9 +129,7 @@ const ( const remoteForwardUnsupportedMessage = "ssh: tcpip-forward request denied by peer" -var log = logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentClient, -}) +var log = logutils.NewPackageLogger(teleport.ComponentKey, teleport.ComponentClient) // ForwardedPort specifies local tunnel to remote // destination managed by the client, is equivalent @@ -659,7 +656,7 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error, for _, o := range opts { o(opt) } - log.Debugf("Activating relogin on error=%q (type=%T)", fnErr, trace.Unwrap(fnErr)) + log.DebugContext(ctx, "Activating relogin on error", "error", fnErr, "error_type", logutils.TypeAttr(trace.Unwrap(fnErr))) if keys.IsPrivateKeyPolicyError(fnErr) { privateKeyPolicy, err := keys.ParsePrivateKeyPolicyError(fnErr) @@ -680,7 +677,7 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error, key, err := tc.Login(ctx) if err != nil { if errors.Is(err, prompt.ErrNotTerminal) { - log.WithError(err).Debugf("Relogin is not available in this environment") + log.DebugContext(ctx, "Relogin is not available in this environment", "error", err) return trace.Wrap(fnErr) } if trace.IsTrustError(err) { @@ -705,7 +702,7 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error, // Save profile to record proxy credentials if err := tc.SaveProfile(opt.makeCurrentProfile); err != nil { - log.Warningf("Failed to save profile: %v", err) + log.WarnContext(ctx, "Failed to save profile", "error", err) return trace.Wrap(err) } @@ -906,13 +903,16 @@ func (c *Config) LoadProfile(ps ProfileStore, proxyAddr string) error { c.DynamicForwardedPorts, err = ParseDynamicPortForwardSpec(profile.DynamicForwardedPorts) if err != nil { - log.Warnf("Unable to parse dynamic port forwarding in user profile: %v.", err) + log.WarnContext(context.Background(), "Unable to parse dynamic port forwarding in user profile", "error", err) } if required, ok := client.OverwriteALPNConnUpgradeRequirementByEnv(c.WebProxyAddr); ok { c.TLSRoutingConnUpgradeRequired = required } - log.Infof("ALPN connection upgrade required for %q: %v.", c.WebProxyAddr, c.TLSRoutingConnUpgradeRequired) + log.InfoContext(context.Background(), "ALPN connection upgrade required", + "web_proxy_addr", c.WebProxyAddr, + "upgrade_required", c.TLSRoutingConnUpgradeRequired, + ) return nil } @@ -1247,7 +1247,7 @@ func NewClient(c *Config) (tc *TeleportClient, err error) { if err != nil { return nil, trace.Wrap(err) } - log.Infof("No teleport login given. defaulting to %s", c.Username) + log.InfoContext(context.Background(), "No teleport login given, using default", "default_login", c.Username) } if c.WebProxyAddr == "" { return nil, trace.BadParameter("No proxy address specified, missed --proxy flag?") @@ -1257,7 +1257,7 @@ func NewClient(c *Config) (tc *TeleportClient, err error) { if err != nil { return nil, trace.Wrap(err) } - log.Infof("no host login given. defaulting to %s", c.HostLogin) + log.InfoContext(context.Background(), "no host login given, using default", "default_host_login", c.HostLogin) } c.Namespace = types.ProcessNamespace(c.Namespace) @@ -1426,7 +1426,10 @@ func (tc *TeleportClient) SignersForClusterWithReissue(ctx context.Context, clus signers, err = tc.localAgent.signersForCluster(clusterName) if err != nil { - log.WithError(err).Warnf("Failed to load/reissue certificates for cluster %q.", clusterName) + log.WarnContext(ctx, "Failed to load/reissue certificates for cluster", + "error", err, + "cluster", clusterName, + ) return nil, trace.Wrap(err) } return signers, nil @@ -1505,7 +1508,11 @@ func (tc *TeleportClient) GetTargetNodes(ctx context.Context, clt client.ListUni // Query for nodes if labels, fuzzy search, or predicate expressions were provided. if len(tc.Labels) > 0 || len(tc.SearchKeywords) > 0 || tc.PredicateExpression != "" { - log.Debugf("Attempting to resolve matching hosts from labels=%v|search=%v|predicate=%v", tc.Labels, tc.SearchKeywords, tc.PredicateExpression) + log.DebugContext(ctx, "Attempting to resolve matching hosts", + "labels", tc.Labels, + "search", tc.SearchKeywords, + "predicate", tc.PredicateExpression, + ) nodes, err := client.GetAllUnifiedResources(ctx, clt, &proto.ListUnifiedResourcesRequest{ Kinds: []string{types.KindNode}, SortBy: types.SortBy{Field: types.ResourceMetadataName}, @@ -1535,7 +1542,7 @@ func (tc *TeleportClient) GetTargetNodes(ctx context.Context, clt client.ListUni return retval, nil } - log.Debugf("Using provided host %s", tc.Host) + log.DebugContext(ctx, "Using provided host", "host", tc.Host) // detect the common error when users use host:port address format _, port, err := net.SplitHostPort(tc.Host) @@ -1572,7 +1579,7 @@ func (tc *TeleportClient) GetTargetNode(ctx context.Context, clt authclient.Clie } if len(tc.Labels) == 0 && len(tc.SearchKeywords) == 0 && tc.PredicateExpression == "" { - log.Debugf("Using provided host %s", tc.Host) + log.DebugContext(ctx, "Using provided host", "host", tc.Host) // detect the common error when users use host:port address format _, port, err := net.SplitHostPort(tc.Host) @@ -1589,7 +1596,11 @@ func (tc *TeleportClient) GetTargetNode(ctx context.Context, clt authclient.Clie } // Query for nodes if labels, fuzzy search, or predicate expressions were provided. - log.Debugf("Attempting to resolve matching host from labels=%v|search=%v|predicate=%v", tc.Labels, tc.SearchKeywords, tc.PredicateExpression) + log.DebugContext(ctx, "Attempting to resolve matching host", + "labels", tc.Labels, + "search", tc.SearchKeywords, + "predicate", tc.PredicateExpression, + ) resp, err := clt.ResolveSSHTarget(ctx, &proto.ResolveSSHTargetRequest{ PredicateExpression: tc.PredicateExpression, SearchKeywords: tc.SearchKeywords, @@ -2175,7 +2186,7 @@ func (tc *TeleportClient) runShellOrCommandOnSingleNode(ctx context.Context, clt go func() { connClosed <- nodeClient.Client.Wait() }() - log.Debugf("Connected to node, no remote command execution was requested, blocking indefinitely.") + log.DebugContext(ctx, "Connected to node, no remote command execution was requested, blocking indefinitely") select { case <-ctx.Done(): // Only return an error if the context was canceled by something other than SIGINT. @@ -2259,11 +2270,11 @@ func (tc *TeleportClient) startPortForwarding(ctx context.Context, nodeClient *N // We log the error here instead of returning it to be consistent with // the other port forwarding methods, which don't stop the session // if forwarding fails. - message := fmt.Sprintf("Failed to bind on remote host to %v: %v.", addr, err) if strings.Contains(err.Error(), remoteForwardUnsupportedMessage) { - message = "Node does not support remote port forwarding (-R)." + log.ErrorContext(ctx, "Node does not support remote port forwarding (-R)") + } else { + log.ErrorContext(ctx, "Failed to bind on remote host", "addr", addr, "error", err) } - log.Error(message) } else { go nodeClient.remoteListenAndForward(ctx, socket, net.JoinHostPort(fp.DestHost, strconv.Itoa(fp.DestPort)), addr) } @@ -2650,7 +2661,7 @@ func (tc *TeleportClient) ListNodesWithFilters(ctx context.Context) ([]types.Ser for _, r := range page { srv, ok := r.ResourceWithLabels.(types.Server) if !ok { - log.Warnf("expected types.Server but received unexpected type %T", r) + log.WarnContext(ctx, "expected types.Server but received unexpected type", "resource_type", logutils.TypeAttr(r)) continue } @@ -3046,7 +3057,7 @@ func (tc *TeleportClient) getProxySSHPrincipal() string { proxyPrincipal := tc.Config.HostLogin if len(tc.JumpHosts) > 0 && tc.JumpHosts[0].Username != "" { - log.Debugf("Setting proxy login to jump host's parameter user %q", tc.JumpHosts[0].Username) + log.DebugContext(context.Background(), "Setting proxy login to jump host's parameter user", "user", tc.JumpHosts[0].Username) proxyPrincipal = tc.JumpHosts[0].Username } return proxyPrincipal @@ -3178,7 +3189,7 @@ func (tc *TeleportClient) generateClientConfig(ctx context.Context) (*clientConf authMethods := append([]ssh.AuthMethod{}, tc.Config.AuthMethods...) clusterName := func() string { return tc.SiteName } if len(tc.JumpHosts) > 0 { - log.Debugf("Overriding SSH proxy to JumpHosts's address %q", tc.JumpHosts[0].Addr.String()) + log.DebugContext(ctx, "Overriding SSH proxy to JumpHosts's address", "addr", logutils.StringerAttr(&tc.JumpHosts[0].Addr)) proxyAddr = tc.JumpHosts[0].Addr.Addr if tc.localAgent != nil { @@ -3299,7 +3310,9 @@ func (g *proxyClusterGuesser) hostKeyCallback(hostname string, remote net.Addr, } if clusterName == "" { - log.Debugf("Target SSH server %q does not have a cluster name embedded in their certificate; will use all available client certificates to authenticate", hostname) + log.DebugContext(context.Background(), "Target SSH server does not have a cluster name embedded in their certificate; will use all available client certificates to authenticate", + "target_server", hostname, + ) } if g.nextHostKeyCallback != nil { @@ -3548,7 +3561,7 @@ func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, keyRing *KeyRi } if !tc.dtAttemptLoginIgnorePing && pingResp.Auth.DeviceTrust.Disabled { - log.Debug("Device Trust: skipping device authentication, device trust disabled") + log.DebugContext(ctx, "Device Trust: skipping device authentication, device trust disabled") return nil } @@ -3563,20 +3576,20 @@ func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, keyRing *KeyRi }) switch { case errors.Is(err, devicetrust.ErrDeviceKeyNotFound): - log.Debug("Device Trust: Skipping device authentication, device key not found") + log.DebugContext(ctx, "Device Trust: Skipping device authentication, device key not found") return nil // err swallowed on purpose case errors.Is(err, devicetrust.ErrPlatformNotSupported): - log.Debug("Device Trust: Skipping device authentication, platform not supported") + log.DebugContext(ctx, "Device Trust: Skipping device authentication, platform not supported") return nil // err swallowed on purpose case trace.IsNotImplemented(err): - log.Debug("Device Trust: Skipping device authentication, not supported by server") + log.DebugContext(ctx, "Device Trust: Skipping device authentication, not supported by server") return nil // err swallowed on purpose case err != nil: - log.WithError(err).Debug("Device Trust: device authentication failed") + log.DebugContext(ctx, "Device Trust: device authentication failed", "error", err) return nil // err swallowed on purpose } - log.Debug("Device Trust: acquired augmented user certificates") + log.DebugContext(ctx, "Device Trust: acquired augmented user certificates") cp := *keyRing cp.Cert = newCerts.SshAuthorizedKey cp.TLSCert = pem.EncodeToMemory(&pem.Block{ @@ -3636,7 +3649,7 @@ func (tc *TeleportClient) DeviceLogin(ctx context.Context, params *dtauthntypes. // Is auto-enroll enabled? pingResp, err := tc.Ping(ctx) if err != nil { - log.WithError(err).Debug("Device Trust: swallowing Ping error for previous Login error") + log.DebugContext(ctx, "Device Trust: swallowing Ping error for previous Login error", "error", err) return nil, trace.Wrap(loginErr) // err swallowed for loginErr } if !tc.dtAutoEnrollIgnorePing && !pingResp.Auth.DeviceTrust.AutoEnroll { @@ -3650,7 +3663,7 @@ func (tc *TeleportClient) DeviceLogin(ctx context.Context, params *dtauthntypes. // Auto-enroll and Login again. if _, err := autoEnroll(ctx, params.DevicesClient); err != nil { - log.WithError(err).Debug("Device Trust: device auto-enroll failed") + log.DebugContext(ctx, "Device Trust: device auto-enroll failed", "error", err) return nil, trace.Wrap(loginErr) // err swallowed for loginErr } newCerts, err = runCeremony(ctx, params) @@ -3690,7 +3703,7 @@ func (tc *TeleportClient) getSSHLoginFunc(pr *webclient.PingResponse) (SSHLoginF // registered, we can try to go with passwordless login even though // auth=local was selected. if tc.canDefaultToPasswordless(pr) { - log.Debug("Trying passwordless login because credentials were found") + log.DebugContext(context.Background(), "Trying passwordless login because credentials were found") return tc.pwdlessLogin, nil } @@ -3732,7 +3745,7 @@ func (tc *TeleportClient) getWebLoginFunc(pr *webclient.PingResponse) (WebLoginF // registered, we can try to go with passwordless login even though // auth=local was selected. if tc.canDefaultToPasswordless(pr) { - log.Debug("Trying passwordless login because credentials were found") + log.DebugContext(context.Background(), "Trying passwordless login because credentials were found") return tc.pwdlessLoginWeb, nil } @@ -3984,9 +3997,9 @@ func (tc *TeleportClient) GetNewLoginKeyRing(ctx context.Context) (keyRing *KeyR defer span.End() if tc.PrivateKeyPolicy.IsHardwareKeyPolicy() { - log.Debugf("Attempting to login with YubiKey private key.") + log.DebugContext(ctx, "Attempting to login with YubiKey private key") if tc.PIVSlot != "" { - log.Debugf("Using PIV slot %q specified by client or server settings.", tc.PIVSlot) + log.DebugContext(ctx, "Using PIV slot specified by client or server settings", "piv_slot", tc.PIVSlot) } priv, err := keys.GetYubiKeyPrivateKey(ctx, tc.PrivateKeyPolicy, tc.PIVSlot, tc.CustomHardwareKeyPrompt) if err != nil { @@ -3998,7 +4011,7 @@ func (tc *TeleportClient) GetNewLoginKeyRing(ctx context.Context) (keyRing *KeyR var sshKey, tlsKey crypto.Signer if tc.GenerateUnifiedKey { - log.Debugf("Attempting to login with a new software private key.") + log.DebugContext(ctx, "Attempting to login with a new software private key") // Using the UserTLS key algorithm for both keys because SSH generally // supports all TLS keys algorithms (RSA2048, ECDSAP256), but TLS does // not support Ed25519 which may be used for SSH. @@ -4008,7 +4021,7 @@ func (tc *TeleportClient) GetNewLoginKeyRing(ctx context.Context) (keyRing *KeyR } sshKey = tlsKey } else { - log.Debugf("Attempting to login with new software private keys.") + log.DebugContext(ctx, "Attempting to login with new software private keys") var err error sshKey, tlsKey, err = cryptosuites.GenerateUserSSHAndTLSKey(ctx, tc.GetCurrentSignatureAlgorithmSuite) if err != nil { @@ -4745,23 +4758,26 @@ func (tc *TeleportClient) EventsChannel() <-chan events.EventFields { // loopbackPool reads trusted CAs if it finds it in a predefined location // and will work only if target proxy address is loopback func loopbackPool(proxyAddr string) *x509.CertPool { + ctx := context.Background() + if !apiutils.IsLoopback(proxyAddr) { - log.Debugf("not using loopback pool for remote proxy addr: %v", proxyAddr) + log.DebugContext(ctx, "not using loopback pool for remote proxy addr", "proxy_addr", proxyAddr) return nil } - log.Debugf("attempting to use loopback pool for local proxy addr: %v", proxyAddr) + log.DebugContext(ctx, "attempting to use loopback pool for local proxy addr", "proxy_addr", proxyAddr) certPool, err := x509.SystemCertPool() if err != nil { - log.Debugf("could not open system cert pool, using empty cert pool instead: %v", err) + log.DebugContext(ctx, "could not open system cert pool, using empty cert pool instead", "error", err) certPool = x509.NewCertPool() } certPath := filepath.Join(defaults.DataDir, defaults.SelfSignedCertPath) - log.Debugf("reading self-signed certs from: %v", certPath) + logger := log.With("cert_path", certPath) + logger.DebugContext(ctx, "reading self-signed certs") pemByte, err := os.ReadFile(certPath) if err != nil { - log.Debugf("could not open any path in: %v", certPath) + logger.DebugContext(ctx, "could not open any path in") return nil } @@ -4773,25 +4789,31 @@ func loopbackPool(proxyAddr string) *x509.CertPool { } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - log.Debugf("could not parse cert in: %v, err: %v", certPath, err) + logger.DebugContext(ctx, "could not parse cert", "error", err) return nil } certPool.AddCert(cert) } - log.Debugf("using local pool for loopback proxy: %v, err: %v", certPath, err) + logger.DebugContext(ctx, "using local pool for loopback proxy", "error", err) return certPool } // connectToSSHAgent connects to the system SSH agent and returns an agent.Agent. func connectToSSHAgent() agent.ExtendedAgent { + ctx := context.Background() + logger := log.With(teleport.ComponentKey, "KEYAGENT") + socketPath := os.Getenv(teleport.SSHAuthSock) conn, err := agentconn.Dial(socketPath) if err != nil { - log.Warnf("[KEY AGENT] Unable to connect to SSH agent on socket %q: %v", socketPath, err) + logger.WarnContext(ctx, "Unable to connect to SSH agent on socket", + "socket_path", socketPath, + "error", err, + ) return nil } - log.Infof("[KEY AGENT] Connected to the system agent: %q", socketPath) + logger.InfoContext(ctx, "Connected to the system agent", "socket_path", socketPath) return agent.NewClient(conn) } @@ -5076,6 +5098,37 @@ func ParseDynamicPortForwardSpec(spec []string) (DynamicForwardedPorts, error) { return result, nil } +// PortMapping represents a mapping of LocalPort to TargetPort, e.g., "1337:42". +type PortMapping struct { + LocalPort int + TargetPort int +} + +// ParsePortMapping parses textual form of port mapping (e.g., "1337:42") into a struct. It accepts +// a single port number as well (e.g., "42"). Both numbers must be between 0 and 65535. +func ParsePortMapping(rawPorts string) (PortMapping, error) { + if rawPorts == "" { + return PortMapping{}, nil + } + + rawLocalPort, rawTargetPort, colonFound := strings.Cut(rawPorts, ":") + localPort, err := strconv.ParseUint(rawLocalPort, 10, 16) + if err != nil { + return PortMapping{}, trace.Wrap(err, "parsing local port") + } + + if !colonFound { + return PortMapping{LocalPort: int(localPort)}, nil + } + + targetPort, err := strconv.ParseUint(rawTargetPort, 10, 16) + if err != nil { + return PortMapping{}, trace.Wrap(err, "parsing target port") + } + + return PortMapping{LocalPort: int(localPort), TargetPort: int(targetPort)}, nil +} + // InsecureSkipHostKeyChecking is used when the user passes in // "StrictHostKeyChecking yes". func InsecureSkipHostKeyChecking(host string, remote net.Addr, key ssh.PublicKey) error { diff --git a/lib/client/api_login_test.go b/lib/client/api_login_test.go index cd39dd3acf0c1..e06e73c6ce648 100644 --- a/lib/client/api_login_test.go +++ b/lib/client/api_login_test.go @@ -36,7 +36,6 @@ import ( "github.com/google/uuid" "github.com/jonboulle/clockwork" "github.com/pquerna/otp/totp" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -64,8 +63,6 @@ import ( func TestTeleportClient_Login_local(t *testing.T) { t.Parallel() - silenceLogger(t) - type webauthnFunc func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) waitForCancelFn := func(ctx context.Context) (string, error) { @@ -324,8 +321,6 @@ func TestTeleportClient_Login_local(t *testing.T) { } func TestTeleportClient_DeviceLogin(t *testing.T) { - silenceLogger(t) - clock := clockwork.NewFakeClockAt(time.Now()) sa := newStandaloneTeleport(t, clock) username := sa.Username @@ -521,10 +516,6 @@ type standaloneBundle struct { func newStandaloneTeleport(t *testing.T, clock clockwork.Clock) *standaloneBundle { randomAddr := utils.NetAddr{AddrNetwork: "tcp", Addr: "127.0.0.1:0"} - // Silent logger and console. - logger := utils.NewLoggerForTests() - logger.SetLevel(log.PanicLevel) - logger.SetOutput(io.Discard) console := io.Discard staticToken := uuid.New().String() @@ -559,7 +550,7 @@ func newStandaloneTeleport(t *testing.T, clock clockwork.Clock) *standaloneBundl cfg.Hostname = "localhost" cfg.Clock = clock cfg.Console = console - cfg.Log = logger + cfg.Logger = utils.NewSlogLoggerForTests() cfg.SetAuthServerAddress(randomAddr) // must be present cfg.Auth.Preference, err = types.NewAuthPreferenceFromConfigFile(types.AuthPreferenceSpecV2{ Type: constants.Local, @@ -643,7 +634,7 @@ func newStandaloneTeleport(t *testing.T, clock clockwork.Clock) *standaloneBundl cfg.SetToken(staticToken) cfg.Clock = clock cfg.Console = console - cfg.Log = logger + cfg.Logger = utils.NewSlogLoggerForTests() cfg.SetAuthServerAddress(*authAddr) cfg.Auth.Enabled = false cfg.Proxy.Enabled = true @@ -683,17 +674,6 @@ func startAndWait(t *testing.T, cfg *servicecfg.Config, eventName string) *servi return instance } -// silenceLogger silences logger during testing. -func silenceLogger(t *testing.T) { - lvl := log.GetLevel() - t.Cleanup(func() { - log.SetOutput(os.Stderr) - log.SetLevel(lvl) - }) - log.SetOutput(io.Discard) - log.SetLevel(log.PanicLevel) -} - func TestRetryWithRelogin(t *testing.T) { clock := clockwork.NewFakeClockAt(time.Now()) sa := newStandaloneTeleport(t, clock) diff --git a/lib/client/api_test.go b/lib/client/api_test.go index 320922e551ef7..ff219515a5afb 100644 --- a/lib/client/api_test.go +++ b/lib/client/api_test.go @@ -1573,3 +1573,72 @@ Future versions of tsh will fail when incompatible versions are detected. }) } } + +func TestParsePortMapping(t *testing.T) { + tests := []struct { + in string + want PortMapping + wantErr bool + }{ + { + in: "", + want: PortMapping{}, + }, + { + in: "1337", + want: PortMapping{LocalPort: 1337}, + }, + { + in: "1337:42", + want: PortMapping{LocalPort: 1337, TargetPort: 42}, + }, + { + in: "0:0", + want: PortMapping{}, + }, + { + in: "0:42", + want: PortMapping{TargetPort: 42}, + }, + { + in: " ", + wantErr: true, + }, + { + in: "1337:", + wantErr: true, + }, + { + in: ":42", + wantErr: true, + }, + { + in: "13371337", + wantErr: true, + }, + { + in: "42:73317331", + wantErr: true, + }, + { + in: "1337:42:42", + wantErr: true, + }, + { + in: "1337:42:", + wantErr: true, + }, + } + + for _, test := range tests { + t.Run(test.in, func(t *testing.T) { + out, err := ParsePortMapping(test.in) + if test.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, test.want, out) + } + }) + } +} diff --git a/lib/client/client.go b/lib/client/client.go index f14a5a2df476d..ffa41d5fcbc5f 100644 --- a/lib/client/client.go +++ b/lib/client/client.go @@ -54,6 +54,7 @@ import ( "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/sshutils/sftp" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" "github.com/gravitational/teleport/lib/utils/socks" ) @@ -217,7 +218,7 @@ func makeDatabaseClientPEM(proto string, cert []byte, pk *keys.PrivateKey) ([]by } else if !trace.IsBadParameter(err) { return nil, trace.Wrap(err) } - log.WithError(err).Warn("MongoDB integration is not supported when logging in with a hardware private key.") + log.WarnContext(context.Background(), "MongoDB integration is not supported when logging in with a hardware private key", "error", err) } return cert, nil } @@ -333,7 +334,11 @@ func NewNodeClient(ctx context.Context, sshConfig *ssh.ClientConfig, conn net.Co // TODO(codingllama): Improve error message below for device trust. // An alternative we have here is querying the cluster to check if device // trust is required, a check similar to `IsMFARequired`. - log.Infof("Access denied to %v connecting to %v: %v", sshConfig.User, nodeName, err) + log.InfoContext(ctx, "Access denied connecting to host", + "login", sshConfig.User, + "target_host", nodeName, + "error", err, + ) return nil, trace.AccessDenied(`access denied to %v connecting to %v`, sshConfig.User, nodeName) } return nil, trace.Wrap(err) @@ -640,7 +645,7 @@ func (c *NodeClient) handleGlobalRequests(ctx context.Context, requestCh <-chan switch r.Type { case teleport.MFAPresenceRequest: if c.OnMFA == nil { - log.Warn("Received MFA presence request, but no callback was provided.") + log.WarnContext(ctx, "Received MFA presence request, but no callback was provided") continue } @@ -651,21 +656,21 @@ func (c *NodeClient) handleGlobalRequests(ctx context.Context, requestCh <-chan var e events.EventFields err := json.Unmarshal(r.Payload, &e) if err != nil { - log.Warnf("Unable to parse event: %v: %v.", string(r.Payload), err) + log.WarnContext(ctx, "Unable to parse event", "event", string(r.Payload), "error", err) continue } // Send event to event channel. err = c.TC.SendEvent(ctx, e) if err != nil { - log.Warnf("Unable to send event %v: %v.", string(r.Payload), err) + log.WarnContext(ctx, "Unable to send event", "event", string(r.Payload), "error", err) continue } default: // This handles keep-alive messages and matches the behavior of OpenSSH. err := r.Reply(false, nil) if err != nil { - log.Warnf("Unable to reply to %v request.", r.Type) + log.WarnContext(ctx, "Unable to reply to request", "request_type", r.Type, "error", err) continue } } @@ -707,7 +712,7 @@ func newClientConn( case <-ctx.Done(): errClose := conn.Close() if errClose != nil { - log.Error(errClose) + log.ErrorContext(ctx, "Failed closing connection", "error", errClose) } // drain the channel resp := <-respCh @@ -732,11 +737,16 @@ type netDialer interface { } func proxyConnection(ctx context.Context, conn net.Conn, remoteAddr string, dialer netDialer) error { + logger := log.With( + "source_addr", logutils.StringerAttr(conn.RemoteAddr()), + "target_addr", remoteAddr, + ) + defer conn.Close() - defer log.Debugf("Finished proxy from %v to %v.", conn.RemoteAddr(), remoteAddr) + defer logger.DebugContext(ctx, "Finished proxy connection") var remoteConn net.Conn - log.Debugf("Attempting to connect proxy from %v to %v.", conn.RemoteAddr(), remoteAddr) + logger.DebugContext(ctx, "Attempting to proxy connection") retry, err := retryutils.NewLinear(retryutils.LinearConfig{ First: 100 * time.Millisecond, @@ -756,7 +766,7 @@ func proxyConnection(ctx context.Context, conn net.Conn, remoteAddr string, dial break } - log.Debugf("Proxy connection attempt %v: %v.", attempt, err) + logger.DebugContext(ctx, "Proxy connection attempt", "attempt", attempt, "error", err) // Wait and attempt to connect again, if the context has closed, exit // right away. select { @@ -806,16 +816,19 @@ func acceptWithContext(ctx context.Context, l net.Listener) (net.Conn, error) { func (c *NodeClient) listenAndForward(ctx context.Context, ln net.Listener, localAddr string, remoteAddr string) { defer ln.Close() - log := log.WithField("localAddr", localAddr).WithField("remoteAddr", remoteAddr) + log := log.With( + "local_addr", localAddr, + "remote_addr", remoteAddr, + ) - log.Infof("Starting port forwarding") + log.InfoContext(ctx, "Starting port forwarding") for ctx.Err() == nil { // Accept connections from the client. conn, err := acceptWithContext(ctx, ln) if err != nil { if ctx.Err() == nil { - log.WithError(err).Errorf("Port forwarding failed.") + log.ErrorContext(ctx, "Port forwarding failed", "error", err) } continue } @@ -824,12 +837,12 @@ func (c *NodeClient) listenAndForward(ctx context.Context, ln net.Listener, loca go func() { // `err` must be a fresh variable, hence `:=` instead of `=`. if err := proxyConnection(ctx, conn, remoteAddr, c.Client); err != nil { - log.WithError(err).Warnf("Failed to proxy connection.") + log.WarnContext(ctx, "Failed to proxy connection", "error", err) } }() } - log.WithError(ctx.Err()).Infof("Shutting down port forwarding.") + log.InfoContext(ctx, "Shutting down port forwarding", "error", ctx.Err()) } // dynamicListenAndForward listens for connections, performs a SOCKS5 @@ -837,9 +850,11 @@ func (c *NodeClient) listenAndForward(ctx context.Context, ln net.Listener, loca func (c *NodeClient) dynamicListenAndForward(ctx context.Context, ln net.Listener, localAddr string) { defer ln.Close() - log := log.WithField("localAddr", localAddr) + log := log.With( + "local_addr", localAddr, + ) - log.Infof("Starting dynamic port forwarding.") + log.InfoContext(ctx, "Starting dynamic port forwarding") for ctx.Err() == nil { // Accept connection from the client. Here the client is typically @@ -847,7 +862,7 @@ func (c *NodeClient) dynamicListenAndForward(ctx context.Context, ln net.Listene conn, err := acceptWithContext(ctx, ln) if err != nil { if ctx.Err() == nil { - log.WithError(err).Errorf("Dynamic port forwarding (SOCKS5) failed.") + log.ErrorContext(ctx, "Dynamic port forwarding (SOCKS5) failed", "error", err) } continue } @@ -856,52 +871,55 @@ func (c *NodeClient) dynamicListenAndForward(ctx context.Context, ln net.Listene // address to proxy. remoteAddr, err := socks.Handshake(conn) if err != nil { - log.WithError(err).Errorf("SOCKS5 handshake failed.") + log.ErrorContext(ctx, "SOCKS5 handshake failed", "error", err) if err = conn.Close(); err != nil { - log.WithError(err).Errorf("Error closing failed proxy connection.") + log.ErrorContext(ctx, "Error closing failed proxy connection", "error", err) } continue } - log.Debugf("SOCKS5 proxy forwarding requests to %v.", remoteAddr) + log.DebugContext(ctx, "SOCKS5 proxy forwarding requests", "remote_addr", remoteAddr) // Proxy the connection to the remote address. go func() { // `err` must be a fresh variable, hence `:=` instead of `=`. if err := proxyConnection(ctx, conn, remoteAddr, c.Client); err != nil { - log.WithError(err).Warnf("Failed to proxy connection.") + log.WarnContext(ctx, "Failed to proxy connection", "error", err) if err = conn.Close(); err != nil { - log.WithError(err).Errorf("Error closing failed proxy connection.") + log.ErrorContext(ctx, "Error closing failed proxy connection", "error", err) } } }() } - log.WithError(ctx.Err()).Infof("Shutting down dynamic port forwarding.") + log.InfoContext(ctx, "Shutting down dynamic port forwarding", "error", ctx.Err()) } // remoteListenAndForward requests a listening socket and forwards all incoming // commands to the local address through the SSH tunnel. func (c *NodeClient) remoteListenAndForward(ctx context.Context, ln net.Listener, localAddr, remoteAddr string) { defer ln.Close() - log := log.WithField("localAddr", localAddr).WithField("remoteAddr", remoteAddr) - log.Infof("Starting remote port forwarding") + log := log.With( + "local_addr", localAddr, + "remote_addr", remoteAddr, + ) + log.InfoContext(ctx, "Starting remote port forwarding") for ctx.Err() == nil { conn, err := acceptWithContext(ctx, ln) if err != nil { if ctx.Err() == nil { - log.WithError(err).Errorf("Remote port forwarding failed.") + log.ErrorContext(ctx, "Remote port forwarding failed", "error", err) } continue } go func() { if err := proxyConnection(ctx, conn, localAddr, &net.Dialer{}); err != nil { - log.WithError(err).Warnf("Failed to proxy connection") + log.WarnContext(ctx, "Failed to proxy connection", "error", err) } }() } - log.WithError(ctx.Err()).Infof("Shutting down remote port forwarding.") + log.InfoContext(ctx, "Shutting down remote port forwarding", "error", ctx.Err()) } // GetRemoteTerminalSize fetches the terminal size of a given SSH session. diff --git a/lib/client/client_store.go b/lib/client/client_store.go index d10b1b47a7b4b..5762f9d7ec8ee 100644 --- a/lib/client/client_store.go +++ b/lib/client/client_store.go @@ -19,13 +19,14 @@ package client import ( + "context" "errors" "fmt" + "log/slog" "net/url" "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "github.com/gravitational/teleport" @@ -42,7 +43,7 @@ import ( // when using `tsh --add-keys-to-agent=only`, Store will be made up of an in-memory // key store and an FS (~/.tsh) profile and trusted certs store. type Store struct { - log *logrus.Entry + log *slog.Logger KeyStore TrustedCertsStore @@ -53,7 +54,7 @@ type Store struct { func NewFSClientStore(dirPath string) *Store { dirPath = profile.FullProfilePath(dirPath) return &Store{ - log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), + log: slog.With(teleport.ComponentKey, teleport.ComponentKeyStore), KeyStore: NewFSKeyStore(dirPath), TrustedCertsStore: NewFSTrustedCertsStore(dirPath), ProfileStore: NewFSProfileStore(dirPath), @@ -63,7 +64,7 @@ func NewFSClientStore(dirPath string) *Store { // NewMemClientStore initializes a new in-memory client store. func NewMemClientStore() *Store { return &Store{ - log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), + log: slog.With(teleport.ComponentKey, teleport.ComponentKeyStore), KeyStore: NewMemKeyStore(), TrustedCertsStore: NewMemTrustedCertsStore(), ProfileStore: NewMemProfileStore(), @@ -261,7 +262,10 @@ func (s *Store) FullProfileStatus() (*ProfileStatus, []*ProfileStatus, error) { } status, err := s.ReadProfileStatus(profileName) if err != nil { - s.log.WithError(err).Warnf("skipping profile %q due to error", profileName) + s.log.WarnContext(context.Background(), "skipping profile due to error", + "profile_name", profileName, + "error", err, + ) continue } profiles = append(profiles, status) diff --git a/lib/client/cluster_client.go b/lib/client/cluster_client.go index ce0a7d6485a6e..729f8f602863d 100644 --- a/lib/client/cluster_client.go +++ b/lib/client/cluster_client.go @@ -311,7 +311,7 @@ func (c *ClusterClient) SessionSSHConfig(ctx context.Context, user string, targe defer authClient.Close() } - log.Debug("Attempting to issue a single-use user certificate with an MFA check.") + log.DebugContext(ctx, "Attempting to issue a single-use user certificate with an MFA check") keyRing, err = c.performSessionMFACeremony(ctx, mfaClt, ReissueParams{ @@ -325,7 +325,7 @@ func (c *ClusterClient) SessionSSHConfig(ctx context.Context, user string, targe return nil, trace.Wrap(err) } - log.Debug("Issued single-use user certificate after an MFA check.") + log.DebugContext(ctx, "Issued single-use user certificate after an MFA check") am, err := keyRing.AsAuthMethod() if err != nil { return nil, trace.Wrap(ceremonyFailedErr{err}) @@ -561,7 +561,7 @@ func (c *ClusterClient) IssueUserCertsWithMFA(ctx context.Context, params Reissu // MFA is not required, but the user requires a new certificate with the // target included in it for routing. if !mfaRequired { - log.Debug("MFA not required for access.") + log.DebugContext(ctx, "MFA not required for access") keyRing, err := certClient.generateUserCerts(ctx, CertCacheKeep, params) return keyRing, proto.MFARequired_MFA_REQUIRED_NO, trace.Wrap(err) } @@ -572,7 +572,7 @@ func (c *ClusterClient) IssueUserCertsWithMFA(ctx context.Context, params Reissu return nil, proto.MFARequired_MFA_REQUIRED_YES, trace.Wrap(err) } - log.Debug("Issued single-use user certificate after an MFA check.") + log.DebugContext(ctx, "Issued single-use user certificate after an MFA check") return keyRing, proto.MFARequired_MFA_REQUIRED_YES, nil } @@ -648,7 +648,7 @@ func PerformSessionMFACeremony(ctx context.Context, params PerformSessionMFACere // that MFA was not required instead of the error received from the root cluster. if mfaRequiredReq != nil && !params.MFAAgainstRoot { mfaRequiredResp, err := currentClient.IsMFARequired(ctx, mfaRequiredReq) - log.Debugf("MFA requirement acquired from leaf, MFARequired=%s", mfaRequiredResp.GetMFARequired()) + log.DebugContext(ctx, "MFA requirement acquired from leaf", "mfa_required", mfaRequiredResp.GetMFARequired()) switch { case err != nil: return nil, nil, trace.Wrap(MFARequiredUnknown(err)) @@ -686,7 +686,7 @@ func PerformSessionMFACeremony(ctx context.Context, params PerformSessionMFACere certsReq := params.CertsReq certsReq.MFAResponse = mfaResp certsReq.Purpose = proto.UserCertsRequest_CERT_PURPOSE_SINGLE_USE_CERTS - log.Debug("Issuing single-use certificate from unary GenerateUserCerts") + log.DebugContext(ctx, "Issuing single-use certificate from unary GenerateUserCerts") newCerts, err := rootClient.GenerateUserCerts(ctx, *certsReq) if err != nil { return nil, nil, trace.Wrap(err) diff --git a/lib/client/conntest/database/mysql.go b/lib/client/conntest/database/mysql.go index 1c2a7ca2e561b..39d07a811fa17 100644 --- a/lib/client/conntest/database/mysql.go +++ b/lib/client/conntest/database/mysql.go @@ -22,13 +22,13 @@ import ( "context" "errors" "fmt" + "log/slog" "net" "strings" "github.com/go-mysql-org/go-mysql/client" "github.com/go-mysql-org/go-mysql/mysql" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/lib/defaults" ) @@ -73,7 +73,7 @@ func (p *MySQLPinger) Ping(ctx context.Context, params PingParams) error { defer func() { if err := conn.Quit(); err != nil { - logrus.WithError(err).Info("Failed to close connection in MySQLPinger.Ping") + slog.InfoContext(context.Background(), "Failed to close connection in MySQLPinger.Ping", "error", err) } }() diff --git a/lib/client/conntest/database/postgres.go b/lib/client/conntest/database/postgres.go index 6a574c56ce156..a048376aadc8a 100644 --- a/lib/client/conntest/database/postgres.go +++ b/lib/client/conntest/database/postgres.go @@ -22,12 +22,12 @@ import ( "context" "errors" "fmt" + "log/slog" "strings" "github.com/gravitational/trace" "github.com/jackc/pgconn" "github.com/jackc/pgerrcode" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/lib/defaults" ) @@ -65,7 +65,7 @@ func (p *PostgresPinger) Ping(ctx context.Context, params PingParams) error { defer func() { if err := conn.Close(ctx); err != nil { - logrus.WithError(err).Info("failed to close connection in PostgresPinger.Ping") + slog.InfoContext(context.Background(), "failed to close connection in PostgresPinger.Ping", "error", err) } }() diff --git a/lib/client/db/dbcmd/dbcmd.go b/lib/client/db/dbcmd/dbcmd.go index 376435b260d40..df547d86bc969 100644 --- a/lib/client/db/dbcmd/dbcmd.go +++ b/lib/client/db/dbcmd/dbcmd.go @@ -78,8 +78,8 @@ const ( openSearchSQLBin = "opensearchsql" // awsBin is the aws CLI program name. awsBin = "aws" - // oracleBin is the Oracle CLI program name. - oracleBin = "sql" + // sqlclBin is the SQLcl program name (Oracle client). + sqlclBin = "sql" // spannerBin is a Google Spanner interactive CLI program name. spannerBin = "spanner-cli" ) @@ -214,6 +214,7 @@ func (c *CLICommandBuilder) GetConnectCommand(ctx context.Context) (*exec.Cmd, e case defaults.ProtocolClickHouseHTTP: return c.getClickhouseHTTPCommand() + case defaults.ProtocolClickHouse: return c.getClickhouseNativeCommand() @@ -239,6 +240,8 @@ func (c *CLICommandBuilder) GetConnectCommandAlternatives(ctx context.Context) ( return c.getElasticsearchAlternativeCommands(), nil case defaults.ProtocolOpenSearch: return c.getOpenSearchAlternativeCommands(), nil + case defaults.ProtocolOracle: + return c.getOracleAlternativeCommands(), nil } cmd, err := c.GetConnectCommand(ctx) @@ -786,38 +789,64 @@ func (c *CLICommandBuilder) getSpannerCommand() (*exec.Cmd, error) { return cmd, nil } -type jdbcOracleThinConnection struct { - host string - port int - db string - tnsAdmin string +func (c *CLICommandBuilder) getOracleTNSDescriptorString() string { + return fmt.Sprintf("/@(DESCRIPTION=(SDU=8000)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%d)))(CONNECT_DATA=(SERVICE_NAME=%s)))", c.host, c.port, c.db.Database) } -func (j *jdbcOracleThinConnection) ConnString() string { - return fmt.Sprintf(`jdbc:oracle:thin:@tcps://%s:%d/%s?TNS_ADMIN=%s`, j.host, j.port, j.db, j.tnsAdmin) +func (c *CLICommandBuilder) getOracleDirectConnectionString() string { + return fmt.Sprintf("/@%s:%d/%s", c.host, c.port, c.db.Database) } -func (c *CLICommandBuilder) getOracleCommand() (*exec.Cmd, error) { +func (c *CLICommandBuilder) getOracleJDBCConnectionString() string { tnsAdminPath := c.profile.OracleWalletDir(c.tc.SiteName, c.db.ServiceName) if runtime.GOOS == constants.WindowsOS { tnsAdminPath = strings.ReplaceAll(tnsAdminPath, `\`, `\\`) } - cs := jdbcOracleThinConnection{ - host: c.host, - port: c.port, - db: c.db.Database, - tnsAdmin: tnsAdminPath, - } // Quote the address for printing as the address contains "?". - connString := cs.ConnString() + connString := fmt.Sprintf(`jdbc:oracle:thin:@tcps://%s:%d/%s?TNS_ADMIN=%s`, c.host, c.port, c.db.Database, tnsAdminPath) if c.options.printFormat { connString = fmt.Sprintf(`'%s'`, connString) } - args := []string{ - "-L", // dont retry - connString, + return connString +} + +func (c *CLICommandBuilder) getOracleCommand() (*exec.Cmd, error) { + alternatives := c.getOracleAlternativeCommands() + if len(alternatives) == 0 { + return nil, trace.BadParameter("no alternative commands found") + } + return alternatives[0].Command, nil +} + +func (c *CLICommandBuilder) getOracleAlternativeCommands() []CommandAlternative { + var commands []CommandAlternative + + ctx := context.Background() + + c.options.logger.DebugContext(ctx, "Building Oracle commands.") + c.options.logger.DebugContext(ctx, "Found servers with TCP support", "count", c.options.oracle.hasTCPServers) + c.options.logger.DebugContext(ctx, "All servers support TCP", "all_servers_support_tcp", c.options.oracle.canUseTCP) + + c.options.logger.DebugContext(ctx, "Connection strings:") + c.options.logger.DebugContext(ctx, "JDBC", "connection_string", c.getOracleJDBCConnectionString()) + if c.options.oracle.hasTCPServers { + c.options.logger.DebugContext(ctx, "TNS", "connection_string", c.getOracleTNSDescriptorString()) + c.options.logger.DebugContext(ctx, "Direct", "connection_string", c.getOracleDirectConnectionString()) } - return exec.Command(oracleBin, args...), nil + + const oneShotLogin = "-L" + + commandTCP := exec.Command(sqlclBin, oneShotLogin, c.getOracleDirectConnectionString()) + commandTCPS := exec.Command(sqlclBin, oneShotLogin, c.getOracleJDBCConnectionString()) + + if c.options.oracle.canUseTCP { + commands = append(commands, CommandAlternative{Description: "SQLcl", Command: commandTCP}) + commands = append(commands, CommandAlternative{Description: "SQLcl (JDBC)", Command: commandTCPS}) + } else { + commands = append(commands, CommandAlternative{Description: "SQLcl", Command: commandTCPS}) + } + + return commands } func (c *CLICommandBuilder) getElasticsearchAlternativeCommands() []CommandAlternative { @@ -915,6 +944,7 @@ type connectionCommandOpts struct { exe Execer password string gcp types.GCPCloudSQL + oracle oracleOpts getDatabase GetDatabaseFunc } @@ -1005,6 +1035,19 @@ func WithExecer(exe Execer) ConnectCommandFunc { } } +type oracleOpts struct { + canUseTCP bool + hasTCPServers bool +} + +// WithOracleOpts configures Oracle-specific options. +func WithOracleOpts(canUseTCP bool, hasTCPServers bool) ConnectCommandFunc { + return func(opts *connectionCommandOpts) { + opts.oracle.canUseTCP = canUseTCP + opts.oracle.hasTCPServers = hasTCPServers + } +} + // WithGCP adds GCP metadata for the database command to access. // TODO(greedy52) use GetDatabaseFunc instead. func WithGCP(gcp types.GCPCloudSQL) ConnectCommandFunc { diff --git a/lib/client/db/postgres/repl/repl.go b/lib/client/db/postgres/repl/repl.go index 514d9160e3efb..66c8cb0f046f2 100644 --- a/lib/client/db/postgres/repl/repl.go +++ b/lib/client/db/postgres/repl/repl.go @@ -82,7 +82,7 @@ func New(_ context.Context, cfg *dbrepl.NewREPLConfig) (dbrepl.REPLInstance, err func (r *REPL) Run(ctx context.Context) error { pgConn, err := pgconn.ConnectConfig(ctx, r.connConfig) if err != nil { - return trace.Wrap(err) + return trace.ConnectionProblem(err, "Unable to connect to database: %v", err) } defer func() { closeCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) diff --git a/lib/client/db/postgres/repl/repl_test.go b/lib/client/db/postgres/repl/repl_test.go index 0aa03b84c8023..e2d780f9ab9c7 100644 --- a/lib/client/db/postgres/repl/repl_test.go +++ b/lib/client/db/postgres/repl/repl_test.go @@ -175,6 +175,19 @@ func TestClose(t *testing.T) { } } +func TestConnectionError(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + instance, tc := StartWithServer(t, ctx, WithSkipREPLRun()) + + // Force the server to be closed + tc.CloseServer() + + err := instance.Run(ctx) + require.Error(t, err) + require.True(t, trace.IsConnectionProblem(err), "expected run to be a connection error but got %T", err) +} + func writeLine(t *testing.T, c *testCtx, line string) { t.Helper() data := []byte(line + lineBreak) diff --git a/lib/client/https_client.go b/lib/client/https_client.go index db980450238b0..66293120843b2 100644 --- a/lib/client/https_client.go +++ b/lib/client/https_client.go @@ -84,7 +84,7 @@ type WebClient struct { // and a the HTTPS failure will be considered final. func (w *WebClient) PostJSONWithFallback(ctx context.Context, endpoint string, val interface{}, allowHTTPFallback bool) (*roundtrip.Response, error) { // First try HTTPS and see how that goes - log.Debugf("Attempting %s", endpoint) + log.DebugContext(ctx, "Attempting request", "endpoint", endpoint) resp, httpsErr := w.Client.PostJSON(ctx, endpoint, val) if httpsErr == nil { // If all went well, then we don't need to do anything else - just return @@ -116,7 +116,7 @@ func (w *WebClient) PostJSONWithFallback(ctx context.Context, endpoint string, v // re-write the endpoint to try HTTP u.Scheme = "http" endpoint = u.String() - log.Warnf("Request for %s/%s falling back to PLAIN HTTP", u.Host, u.Path) + log.WarnContext(ctx, "Request for falling back to PLAIN HTTP", "endpoint", endpoint) return httplib.ConvertResponse(w.Client.PostJSON(ctx, endpoint, val)) } diff --git a/lib/client/interfaces.go b/lib/client/interfaces.go index 23502cb4f561c..b755a98de5970 100644 --- a/lib/client/interfaces.go +++ b/lib/client/interfaces.go @@ -28,6 +28,7 @@ import ( "crypto/tls" "crypto/x509" "fmt" + "log/slog" "strings" "time" @@ -81,6 +82,14 @@ func (idx KeyRingIndex) Match(matchKeyRing KeyRingIndex) bool { (matchKeyRing.Username == "" || matchKeyRing.Username == idx.Username) } +func (idx KeyRingIndex) LogValue() slog.Value { + return slog.GroupValue( + slog.String("proxy", idx.ProxyHost), + slog.String("cluster", idx.ClusterName), + slog.String("username", idx.Username), + ) +} + // TLSCredential holds a signed TLS certificate and matching private key. type TLSCredential struct { // PrivateKey is the private key of the credential. diff --git a/lib/client/keyagent.go b/lib/client/keyagent.go index 741c1f5b989ec..85e095f14a3c1 100644 --- a/lib/client/keyagent.go +++ b/lib/client/keyagent.go @@ -23,6 +23,7 @@ import ( "crypto/x509" "fmt" "io" + "log/slog" "net" "os" "runtime" @@ -30,7 +31,6 @@ import ( "strings" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" @@ -40,12 +40,13 @@ import ( "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/tlsca" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // LocalKeyAgent holds Teleport certificates for a user connected to a cluster. type LocalKeyAgent struct { // log holds the structured logger. - log *logrus.Entry + log *slog.Logger // ExtendedAgent is the teleport agent agent.ExtendedAgent @@ -120,9 +121,7 @@ func NewLocalAgent(conf LocalAgentConfig) (a *LocalKeyAgent, err error) { conf.Agent = keyring } a = &LocalKeyAgent{ - log: logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentKeyAgent, - }), + log: slog.With(teleport.ComponentKey, teleport.ComponentKeyAgent), ExtendedAgent: conf.Agent, clientStore: conf.ClientStore, noHosts: make(map[string]bool), @@ -136,10 +135,10 @@ func NewLocalAgent(conf LocalAgentConfig) (a *LocalKeyAgent, err error) { if shouldAddKeysToAgent(conf.KeysOption) { a.systemAgent = connectToSSHAgent() } else { - log.Debug("Skipping connection to the local ssh-agent.") + log.DebugContext(context.Background(), "Skipping connection to the local ssh-agent.") if !agentSupportsSSHCertificates() && agentIsPresent() { - log.Warn(`Certificate was not loaded into agent because the agent at SSH_AUTH_SOCK does not appear + log.WarnContext(context.Background(), `Certificate was not loaded into agent because the agent at SSH_AUTH_SOCK does not appear to support SSH certificates. To force load the certificate into the running agent, use the --add-keys-to-agent=yes flag.`) } @@ -195,13 +194,15 @@ func (a *LocalKeyAgent) LoadKeyRing(keyRing KeyRing) error { return trace.Wrap(err) } - a.log.Infof("Loading SSH key for user %q and cluster %q.", a.username, keyRing.ClusterName) + a.log.InfoContext(context.Background(), "Loading SSH key", "user", a.username, "cluster", keyRing.ClusterName) agents := []agent.ExtendedAgent{a.ExtendedAgent} if a.systemAgent != nil { if canAddToSystemAgent(agentKey) { agents = append(agents, a.systemAgent) } else { - a.log.Infof("Skipping adding key to SSH system agent for non-standard key type %T", agentKey.PrivateKey) + a.log.InfoContext(context.Background(), "Skipping adding key to SSH system agent for non-standard key type", + "key_type", logutils.TypeAttr(agentKey.PrivateKey), + ) } } @@ -249,14 +250,14 @@ func (a *LocalKeyAgent) UnloadKeyRing(keyRing KeyRingIndex) error { // get a list of all keys in the agent keyList, err := agent.List() if err != nil { - a.log.Warnf("Unable to communicate with agent and list keys: %v", err) + a.log.WarnContext(context.Background(), "Unable to communicate with agent and list keys", "error", err) } // remove any teleport keys we currently have loaded in the agent for this user and proxy for _, agentKey := range keyList { if agentKeyIdx, ok := parseTeleportAgentKeyComment(agentKey.Comment); ok && agentKeyIdx.Match(keyRing) { if err = agent.Remove(agentKey); err != nil { - a.log.Warnf("Unable to communicate with agent and remove key: %v", err) + a.log.WarnContext(context.Background(), "Unable to communicate with agent and remove key", "error", err) } } } @@ -278,14 +279,14 @@ func (a *LocalKeyAgent) UnloadKeys() error { // get a list of all keys in the agent keyList, err := agent.List() if err != nil { - a.log.Warnf("Unable to communicate with agent and list keys: %v", err) + a.log.WarnContext(context.Background(), "Unable to communicate with agent and list keys", "error", err) } // remove any teleport keys we currently have loaded in the agent for _, key := range keyList { if isTeleportAgentKey(key) { if err = agent.Remove(key); err != nil { - a.log.Warnf("Unable to communicate with agent and remove key: %v", err) + a.log.WarnContext(context.Background(), "Unable to communicate with agent and remove key", "error", err) } } } @@ -362,13 +363,15 @@ func (a *LocalKeyAgent) HostKeyCallback(addr string, remote net.Addr, hostKey ss }, FIPS: isFIPS(), } - a.log.Debugf("Checking key: %s.", ssh.MarshalAuthorizedKey(hostKey)) + + ctx := context.Background() + a.log.DebugContext(ctx, "Checking key", "host_key", string(ssh.MarshalAuthorizedKey(hostKey))) err = certChecker.CheckHostKey(addr, remote, hostKey) if err != nil { - a.log.Debugf("Host validation failed: %v.", err) + a.log.DebugContext(ctx, "Host validation failed", "error", err) return trace.Wrap(err) } - a.log.Debugf("Validated host %v.", addr) + a.log.DebugContext(ctx, "Validated host", "host_addr", addr) return nil } @@ -377,12 +380,14 @@ func (a *LocalKeyAgent) HostKeyCallback(addr string, remote net.Addr, hostKey ss // reject the server key. func (a *LocalKeyAgent) checkHostCertificateForClusters(clusters ...string) func(key ssh.PublicKey, addr string) bool { return func(key ssh.PublicKey, addr string) bool { + ctx := context.Background() + // Check the local cache (where all Teleport CAs are placed upon login) to // see if any of them match. var keys []ssh.PublicKey trustedCerts, err := a.clientStore.GetTrustedCerts(a.proxyHost) if err != nil { - a.log.Errorf("Failed to get trusted certs: %v.", err) + a.log.ErrorContext(ctx, "Failed to get trusted certs", "error", err) return false } @@ -395,7 +400,7 @@ func (a *LocalKeyAgent) checkHostCertificateForClusters(clusters ...string) func } key, err := sshutils.ParseAuthorizedKeys(cert.AuthorizedKeys) if err != nil { - a.log.Errorf("Failed to parse authorized keys: %v.", err) + a.log.ErrorContext(ctx, "Failed to parse authorized keys", "error", err) return false } keys = append(keys, key...) @@ -418,25 +423,26 @@ func (a *LocalKeyAgent) checkHostCertificateForClusters(clusters ...string) func // ~/.tsh/known_hosts cache and if not found, prompts the user to accept // or reject. func (a *LocalKeyAgent) checkHostKey(addr string, remote net.Addr, key ssh.PublicKey) error { - var err error + ctx := context.Background() + logger := a.log.With("host_addr", addr) // Unless --insecure flag was given, prohibit public keys or host certs // not signed by Teleport. if !a.insecure { - a.log.Debugf("Host %s presented a public key not signed by Teleport. Rejecting due to insecure mode being OFF.", addr) + logger.DebugContext(ctx, "Host presented a public key not signed by Teleport - Rejecting due to insecure mode being OFF") return trace.BadParameter("host %s presented a public key not signed by Teleport", addr) } - a.log.Warnf("Host %s presented a public key not signed by Teleport. Proceeding due to insecure mode being ON.", addr) + logger.WarnContext(ctx, "Host presented a public key not signed by Teleport - Proceeding due to insecure mode being ON") // Check if this exact host is in the local cache. keys, err := a.clientStore.GetTrustedHostKeys(addr) if err != nil { - a.log.WithError(err).Debugf("Failed to retrieve client's trusted host keys.") + logger.DebugContext(ctx, "Failed to retrieve client's trusted host keys", "error", err) } else { for _, trustedHostKey := range keys { if sshutils.KeysEqual(key, trustedHostKey) { - a.log.Debugf("Verified host %s.", addr) + logger.DebugContext(ctx, "Verified host") return nil } } @@ -456,7 +462,7 @@ func (a *LocalKeyAgent) checkHostKey(addr string, remote net.Addr, key ssh.Publi // If the user trusts the key, store the key in the client trusted certs store. err = a.clientStore.AddTrustedHostKeys(a.proxyHost, addr, key) if err != nil { - a.log.Warnf("Failed to save the host key: %v.", err) + logger.WarnContext(ctx, "Failed to save the host key", "error", err) return trace.Wrap(err) } return nil @@ -547,7 +553,7 @@ func (a *LocalKeyAgent) addKeyRing(keyRing *KeyRing) error { } } else { if !keyRing.EqualPrivateKey(storedKeyRing) { - a.log.Debugf("Deleting obsolete stored keyring with index %+v.", storedKeyRing.KeyRingIndex) + a.log.DebugContext(context.Background(), "Deleting obsolete stored keyring", "keyring_index", storedKeyRing.KeyRingIndex) if err := a.clientStore.DeleteKeyRing(storedKeyRing.KeyRingIndex); err != nil { return trace.Wrap(err) } diff --git a/lib/client/keyagent_test.go b/lib/client/keyagent_test.go index bbd2ae2677dc7..4c0c078e82293 100644 --- a/lib/client/keyagent_test.go +++ b/lib/client/keyagent_test.go @@ -812,7 +812,7 @@ func startDebugAgent(t *testing.T) error { conn, err := listener.Accept() if err != nil { if !utils.IsUseOfClosedNetworkError(err) { - log.Warnf("Unexpected response from listener.Accept: %v", err) + log.WarnContext(context.Background(), "Unexpected response from listener.Accept", "error", err) } return } diff --git a/lib/client/keystore.go b/lib/client/keystore.go index 2cba0d5810825..9215922f5c33b 100644 --- a/lib/client/keystore.go +++ b/lib/client/keystore.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" iofs "io/fs" + "log/slog" "os" "path/filepath" "runtime" @@ -31,7 +32,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "github.com/gravitational/teleport" @@ -95,7 +95,7 @@ type KeyStore interface { // The FS store uses the file layout outlined in `api/utils/keypaths.go`. type FSKeyStore struct { // log holds the structured logger. - log logrus.FieldLogger + log *slog.Logger // KeyDir is the directory where all keys are stored. KeyDir string @@ -111,7 +111,7 @@ type FSKeyStore struct { func NewFSKeyStore(dirPath string) *FSKeyStore { dirPath = profile.FullProfilePath(dirPath) return &FSKeyStore{ - log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), + log: slog.With(teleport.ComponentKey, teleport.ComponentKeyStore), KeyDir: dirPath, } } @@ -225,7 +225,7 @@ func (fs *FSKeyStore) AddKeyRing(keyRing *KeyRing) error { if runtime.GOOS == constants.WindowsOS { ppkFile, err := keyRing.SSHPrivateKey.PPKFile() if err != nil { - fs.log.Debugf("Cannot convert private key to PPK-formatted keypair: %v", err) + fs.log.DebugContext(context.Background(), "Cannot convert private key to PPK-formatted keypair", "error", err) } else { if err := fs.writeBytes(ppkFile, fs.ppkFilePath(keyRing.KeyRingIndex)); err != nil { return trace.Wrap(err) @@ -353,7 +353,10 @@ func tryLockFile(ctx context.Context, path string, lockFn func(string) (func() e case err == nil: return func() { if err := unlock(); err != nil { - log.Errorf("failed to unlock TLS credential at %s: %s", path, err) + log.ErrorContext(ctx, "failed to unlock TLS credential", + "credential_path", path, + "error", err, + ) } }, nil case errors.Is(err, utils.ErrUnsuccessfulLockTry): @@ -447,7 +450,7 @@ func (fs *FSKeyStore) DeleteKeyRing(idx KeyRingIndex) error { // And try to delete kube credentials lockfile in case it exists err := utils.RemoveSecure(fs.kubeCredLockfilePath(idx)) if err != nil && !errors.Is(err, iofs.ErrNotExist) { - log.Debugf("Could not remove kube credentials file: %v", err) + log.DebugContext(context.Background(), "Could not remove kube credentials file", "error", err) } // Clear ClusterName to delete the user certs stored for all clusters. diff --git a/lib/client/known_hosts_migrate.go b/lib/client/known_hosts_migrate.go index bd331dd3c8fb4..26fd22a667d2b 100644 --- a/lib/client/known_hosts_migrate.go +++ b/lib/client/known_hosts_migrate.go @@ -20,9 +20,10 @@ package client import ( "bytes" + "context" + "log/slog" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "github.com/gravitational/teleport" @@ -117,7 +118,7 @@ func canPruneOldHostsEntry(oldEntry *knownHostEntry, newEntries []*knownHostEntr // duplicate entry exists. This may modify order of host keys, but will not // change their content. func pruneOldHostKeys(output []string) []string { - log := logrus.WithField(teleport.ComponentKey, teleport.ComponentMigrate) + log := slog.With(teleport.ComponentKey, teleport.ComponentMigrate) var ( oldEntries = make([]*knownHostEntry, 0) @@ -130,7 +131,10 @@ func pruneOldHostKeys(output []string) []string { parsed, err := parseKnownHost(line) if err != nil { // If the line isn't parseable, pass it through. - log.WithError(err).Debugf("Unable to parse known host on line %d, skipping", i+1) + log.DebugContext(context.Background(), "Unable to parse known host, skipping", + "invalid_line_number", i+1, + "error", err, + ) prunedOutput = append(prunedOutput, line) continue } @@ -153,7 +157,7 @@ func pruneOldHostKeys(output []string) []string { // exists. If not, pass it through. for _, entry := range oldEntries { if canPruneOldHostsEntry(entry, newEntries) { - log.Debugf("Pruning old known_hosts entry for %s.", entry.hosts[0]) + log.DebugContext(context.Background(), "Pruning old known_hosts entry for host", "host", entry.hosts[0]) } else { prunedOutput = append(prunedOutput, entry.raw) } diff --git a/lib/client/kube/kube.go b/lib/client/kube/kube.go index dd26866d0938a..85980291f5d25 100644 --- a/lib/client/kube/kube.go +++ b/lib/client/kube/kube.go @@ -17,17 +17,17 @@ package kube import ( + "context" + "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/tlsca" + logutils "github.com/gravitational/teleport/lib/utils/log" ) -var log = logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentKubeClient, -}) +var log = logutils.NewPackageLogger(teleport.ComponentKey, teleport.ComponentKubeClient) // CheckIfCertsAreAllowedToAccessCluster evaluates if the new cert created by the user // to access kubeCluster has at least one kubernetes_user or kubernetes_group @@ -43,7 +43,7 @@ func CheckIfCertsAreAllowedToAccessCluster(k *client.KeyRing, rootCluster, telep return nil } if cred, ok := k.KubeTLSCredentials[kubeCluster]; ok { - log.Debugf("Got TLS cert for Kubernetes cluster %q", kubeCluster) + log.DebugContext(context.Background(), "Got TLS cert for Kubernetes cluster", "kubernetes_cluster", kubeCluster) exist, err := checkIfCertHasKubeGroupsAndUsers(cred.Cert) if err != nil { return trace.Wrap(err) diff --git a/lib/client/kubesession.go b/lib/client/kubesession.go index 5390555369c19..39681989a1abd 100644 --- a/lib/client/kubesession.go +++ b/lib/client/kubesession.go @@ -227,7 +227,7 @@ func (s *KubeSession) pipeInOut(stdout io.Writer, enableEscapeSequences bool, mo handleNonPeerControls(mode, s.term, func() { err := s.stream.ForceTerminate() if err != nil { - log.Debugf("Error sending force termination request: %v", err) + log.DebugContext(context.Background(), "Error sending force termination request", "error", err) fmt.Print("\n\rError while sending force termination request\n\r") } }) diff --git a/lib/client/local_proxy_middleware.go b/lib/client/local_proxy_middleware.go index 781e1ce4fa95b..080efa477915b 100644 --- a/lib/client/local_proxy_middleware.go +++ b/lib/client/local_proxy_middleware.go @@ -161,7 +161,10 @@ func (c *CertChecker) GetOrIssueCert(ctx context.Context) (tls.Certificate, erro } certTTL := cert.Leaf.NotAfter.Sub(c.clock.Now()).Round(time.Minute) - log.Debugf("Certificate renewed: valid until %s [valid for %v]", cert.Leaf.NotAfter.Format(time.RFC3339), certTTL) + log.DebugContext(ctx, "Certificate renewed", + "valid_until", cert.Leaf.NotAfter.Format(time.RFC3339), + "cert_ttl", certTTL, + ) c.cert = cert return c.cert, nil @@ -209,7 +212,7 @@ func (c *DBCertIssuer) CheckCert(cert *x509.Certificate) error { func (c *DBCertIssuer) IssueCert(ctx context.Context) (tls.Certificate, error) { var accessRequests []string if profile, err := c.Client.ProfileStatus(); err != nil { - log.WithError(err).Warn("unable to load profile, requesting database certs without access requests") + log.WarnContext(ctx, "unable to load profile, requesting database certs without access requests", "error", err) } else { accessRequests = profile.ActiveRequests.AccessRequests } @@ -281,7 +284,7 @@ func (c *AppCertIssuer) CheckCert(cert *x509.Certificate) error { func (c *AppCertIssuer) IssueCert(ctx context.Context) (tls.Certificate, error) { var accessRequests []string if profile, err := c.Client.ProfileStatus(); err != nil { - log.WithError(err).Warn("unable to load profile, requesting app certs without access requests") + log.WarnContext(ctx, "unable to load profile, requesting app certs without access requests", "error", err) } else { accessRequests = profile.ActiveRequests.AccessRequests } @@ -446,7 +449,10 @@ func (r *LocalCertGenerator) ensureValidCA(ctx context.Context) error { } certTTL := time.Until(caTLSCert.Leaf.NotAfter).Round(time.Minute) - log.Debugf("Local CA renewed: valid until %s [valid for %v]", caTLSCert.Leaf.NotAfter.Format(time.RFC3339), certTTL) + log.DebugContext(ctx, "Local CA renewed", + "valid_until", caTLSCert.Leaf.NotAfter.Format(time.RFC3339), + "cert_ttl", certTTL, + ) // Clear cert cache and use CA for hostnames in the CA. r.certsByHost = make(map[string]*tls.Certificate) diff --git a/lib/client/mfa/prompt.go b/lib/client/mfa/prompt.go index c96935c897f18..8adb56a3b262d 100644 --- a/lib/client/mfa/prompt.go +++ b/lib/client/mfa/prompt.go @@ -21,11 +21,11 @@ package mfa import ( "context" "errors" + "log/slog" "strings" "sync" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/mfa" @@ -115,9 +115,7 @@ func HandleMFAPromptGoroutines(ctx context.Context, startGoroutines func(context // Surface error immediately. return nil, trace.Wrap(resp.Err) case err != nil: - log. - WithError(err). - Debug("MFA goroutine failed, continuing so other goroutines have a chance to succeed") + slog.DebugContext(ctx, "MFA goroutine failed, continuing so other goroutines have a chance to succeed", "error", err) errs = append(errs, err) // Continue to give the other authn goroutine a chance to succeed. // If both have failed, this will exit the loop. diff --git a/lib/client/profile.go b/lib/client/profile.go index 985266ee3bc75..10e2754c82354 100644 --- a/lib/client/profile.go +++ b/lib/client/profile.go @@ -19,6 +19,7 @@ package client import ( + "context" "net/url" "os" "path/filepath" @@ -27,7 +28,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/profile" @@ -109,9 +109,6 @@ func (ms *MemProfileStore) SaveProfile(profile *profile.Profile, makecurrent boo // // The FS store uses the file layout outlined in `api/utils/keypaths.go`. type FSProfileStore struct { - // log holds the structured logger. - log logrus.FieldLogger - // Dir is the directory where all keys are stored. Dir string } @@ -120,7 +117,6 @@ type FSProfileStore struct { func NewFSProfileStore(dirPath string) *FSProfileStore { dirPath = profile.FullProfilePath(dirPath) return &FSProfileStore{ - log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), Dir: dirPath, } } @@ -434,14 +430,17 @@ func (p *ProfileStatus) virtualPathFromEnv(kind VirtualPathKind, params VirtualP // If we can't resolve any env vars, this will return garbage which we // should at least warn about. As ugly as this is, arguably making every // profile path lookup fallible is even uglier. - log.Debugf("Could not resolve path to virtual profile entry of type %s "+ - "with parameters %+v.", kind, params) + log.DebugContext(context.Background(), "Could not resolve path to virtual profile entry", + "entry_type", kind, + "parameters", params, + ) virtualPathWarnOnce.Do(func() { - log.Errorf("A virtual profile is in use due to an identity file " + + const msg = "A virtual profile is in use due to an identity file " + "(`-i ...`) but this functionality requires additional files on " + "disk and may fail. Consider using a compatible wrapper " + - "application (e.g. Machine ID) for this command.") + "application (e.g. Machine ID) for this command." + log.ErrorContext(context.Background(), msg) }) return "", false diff --git a/lib/client/session.go b/lib/client/session.go index 1c6cc9bea41e3..2ee4aad8227b5 100644 --- a/lib/client/session.go +++ b/lib/client/session.go @@ -151,7 +151,7 @@ func newSession(ctx context.Context, if ns.terminal.IsAttached() { err = ns.terminal.Resize(int16(terminalSize.Width), int16(terminalSize.Height)) if err != nil { - log.Error(err) + log.ErrorContext(ctx, "Failed to resize terminal", "error", err) } } @@ -179,7 +179,7 @@ func newSession(ctx context.Context, if ns.shouldClearOnExit { if err := ns.terminal.Clear(); err != nil { - log.Warnf("Failed to clear screen: %v.", err) + log.WarnContext(ctx, "Failed to clear screen", "error", err) } } ns.terminal.Close() @@ -247,7 +247,7 @@ func (ns *NodeSession) createServerSession(ctx context.Context, chanReqCallback } if err := sess.SetEnvs(ctx, envs); err != nil { - log.Warn(err) + log.WarnContext(ctx, "Failed to set environment variables", "error", err) } // if agent forwarding was requested (and we have a agent to forward), @@ -256,7 +256,7 @@ func (ns *NodeSession) createServerSession(ctx context.Context, chanReqCallback targetAgent := selectKeyAgent(tc) if targetAgent != nil { - log.Debugf("Forwarding Selected Key Agent") + log.DebugContext(ctx, "Forwarding Selected Key Agent") err = agent.ForwardToAgent(ns.nodeClient.Client.Client, targetAgent) if err != nil { return nil, trace.Wrap(err) @@ -275,13 +275,13 @@ func (ns *NodeSession) createServerSession(ctx context.Context, chanReqCallback func selectKeyAgent(tc *TeleportClient) agent.ExtendedAgent { switch tc.ForwardAgent { case ForwardAgentYes: - log.Debugf("Selecting system key agent.") + log.DebugContext(context.Background(), "Selecting system key agent") return connectToSSHAgent() case ForwardAgentLocal: - log.Debugf("Selecting local Teleport key agent.") + log.DebugContext(context.Background(), "Selecting local Teleport key agent") return tc.localAgent.ExtendedAgent default: - log.Debugf("No Key Agent selected.") + log.DebugContext(context.Background(), "No Key Agent selected") return nil } } @@ -355,7 +355,7 @@ func (ns *NodeSession) allocateTerminal(ctx context.Context, termType string, s if ns.terminal.IsAttached() { realWidth, realHeight, err := ns.terminal.Size() if err != nil { - log.Error(err) + log.ErrorContext(ctx, "Unable to determine terminal size", "error", err) } else { width = int(realWidth) height = int(realHeight) @@ -390,7 +390,7 @@ func (ns *NodeSession) allocateTerminal(ctx context.Context, termType string, s } go func() { if _, err := io.Copy(ns.nodeClient.TC.Stderr, stderr); err != nil { - log.Debugf("Error reading remote STDERR: %v", err) + log.DebugContext(ctx, "Error reading remote STDERR", "error", err) } }() return utils.NewPipeNetConn( @@ -407,7 +407,7 @@ func (ns *NodeSession) updateTerminalSize(ctx context.Context, s *tracessh.Sessi lastWidth, lastHeight, err := ns.terminal.Size() if err != nil { - log.Errorf("Unable to get window size: %v", err) + log.ErrorContext(ctx, "Unable to get window size", "error", err) return } @@ -432,7 +432,7 @@ func (ns *NodeSession) updateTerminalSize(ctx context.Context, s *tracessh.Sessi currWidth, currHeight, err := ns.terminal.Size() if err != nil { - log.Warnf("Unable to get window size: %v.", err) + log.WarnContext(ctx, "Unable to get window size", "error", err) continue } @@ -443,11 +443,16 @@ func (ns *NodeSession) updateTerminalSize(ctx context.Context, s *tracessh.Sessi // Send the "window-change" request over the channel. if err = s.WindowChange(ctx, int(currHeight), int(currWidth)); err != nil { - log.Warnf("Unable to send %v request: %v.", sshutils.WindowChangeRequest, err) + log.WarnContext(ctx, "Unable to send window change request", "error", err) continue } - log.Debugf("Updated window size from (%d, %d) to (%d, %d) due to SIGWINCH.", lastWidth, lastHeight, currWidth, currHeight) + log.DebugContext(ctx, "Updated window size from due to SIGWINCH.", + "original_width", lastWidth, + "original_height", lastHeight, + "current_width", currWidth, + "current_height", currHeight, + ) lastWidth, lastHeight = currWidth, currHeight @@ -460,14 +465,18 @@ func (ns *NodeSession) updateTerminalSize(ctx context.Context, s *tracessh.Sessi terminalParams, err := session.UnmarshalTerminalParams(event.GetString(events.TerminalSize)) if err != nil { - log.Warnf("Unable to unmarshal terminal parameters: %v.", err) + log.WarnContext(ctx, "Unable to unmarshal terminal parameters", "error", err) continue } lastSize := terminalParams.Winsize() lastWidth = int16(lastSize.Width) lastHeight = int16(lastSize.Height) - log.Debugf("Received window size %v from node in session %v.", lastSize, event.GetString(events.SessionEventID)) + log.DebugContext(ctx, "Received window size from node in session", + "width", lastSize.Width, + "height", lastSize.Height, + "session_id", event.GetString(events.SessionEventID), + ) // Update size of local terminal with the last size received from remote server. case <-tickerCh.C: @@ -475,7 +484,7 @@ func (ns *NodeSession) updateTerminalSize(ctx context.Context, s *tracessh.Sessi // received. currWidth, currHeight, err := ns.terminal.Size() if err != nil { - log.Warnf("Unable to get current terminal size: %v.", err) + log.WarnContext(ctx, "Unable to get current terminal size", "error", err) continue } @@ -488,11 +497,16 @@ func (ns *NodeSession) updateTerminalSize(ctx context.Context, s *tracessh.Sessi // the window. err = ns.terminal.Resize(lastWidth, lastHeight) if err != nil { - log.Warnf("Unable to update terminal size: %v.", err) + log.WarnContext(ctx, "Unable to update terminal size", "error", err) continue } - log.Debugf("Updated window size from (%d, %d) to (%d, %d) due to remote window change.", currWidth, currHeight, lastWidth, lastHeight) + log.DebugContext(ctx, "Updated window size due to remote window change", + "original_width", lastWidth, + "original_height", lastHeight, + "current_width", currWidth, + "current_height", currHeight, + ) case <-ns.closer.C: return } @@ -602,10 +616,10 @@ func (ns *NodeSession) runCommand(ctx context.Context, mode types.SessionPartici // Ctrl-C. case <-ctx.Done(): if err := s.Close(); err != nil { - log.Debugf("Unable to close SSH channel: %v", err) + log.DebugContext(ctx, "Unable to close SSH channel", "error", err) } if err := ns.NodeClient().Client.Close(); err != nil { - log.Debugf("Unable to close SSH client: %v", err) + log.DebugContext(ctx, "Unable to close SSH client", "error", err) } return trace.ConnectionProblem(ctx.Err(), "connection canceled") } @@ -640,7 +654,7 @@ func (ns *NodeSession) watchSignals(shell io.Writer) { case <-ctrlCSignal: _, err := shell.Write([]byte{ctrlCharC}) if err != nil { - log.Error(err.Error()) + log.ErrorContext(context.Background(), "Failed to forward ctrl+c", "error", err) } case <-ns.closer.C: return @@ -656,7 +670,7 @@ func (ns *NodeSession) watchSignals(shell io.Writer) { if _, ok := event.(terminal.StopEvent); ok { _, err := shell.Write([]byte{ctrlCharZ}) if err != nil { - log.Error(err.Error()) + log.ErrorContext(context.Background(), "Failed to forward ctrl+z", "error", err) } } } @@ -698,7 +712,7 @@ func handlePeerControls(term *terminal.Terminal, enableEscapeSequences bool, rem // by tsh. These can be used to force a client disconnect since CTRL-C is merely passed // to the other end and not interpreted as an exit request locally stdin = escape.NewReader(stdin, term.Stderr(), func(err error) { - log.Debugf("escape.NewReader error: %v", err) + log.DebugContext(context.Background(), "escape.NewReader error", "error", err) switch { case errors.Is(err, escape.ErrDisconnect): @@ -713,7 +727,7 @@ func handlePeerControls(term *terminal.Terminal, enableEscapeSequences bool, rem _, err := io.Copy(remoteStdin, stdin) if err != nil { - log.Debugf("Error copying data to remote peer: %v", err) + log.DebugContext(context.Background(), "Error copying data to remote peer", "error", err) fmt.Fprint(term.Stderr(), "\r\nError copying data to remote peer\r\n") forceDisconnect = true } @@ -728,8 +742,8 @@ func (ns *NodeSession) pipeInOut(ctx context.Context, shell io.ReadWriteCloser, go func() { defer ns.closer.Close() _, err := io.Copy(ns.terminal.Stdout(), shell) - if err != nil { - log.Error(err.Error()) + if err != nil && !utils.IsOKNetworkError(err) { + log.ErrorContext(ctx, "Failed copying data to session", "error", err) } }() diff --git a/lib/client/terminal/terminal_common.go b/lib/client/terminal/terminal_common.go index fe51fdc7f6f2b..11e9f00e66b20 100644 --- a/lib/client/terminal/terminal_common.go +++ b/lib/client/terminal/terminal_common.go @@ -23,14 +23,12 @@ import ( "sync" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" + logutils "github.com/gravitational/teleport/lib/utils/log" ) -var log = logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentClient, -}) +var log = logutils.NewPackageLogger(teleport.ComponentKey, teleport.ComponentClient) // ResizeEvent is emitted when a terminal window is resized. type ResizeEvent struct{} diff --git a/lib/client/terminal/terminal_unix.go b/lib/client/terminal/terminal_unix.go index 479ddfcfc492d..98da0332a4205 100644 --- a/lib/client/terminal/terminal_unix.go +++ b/lib/client/terminal/terminal_unix.go @@ -22,9 +22,10 @@ package terminal import ( - "bytes" + "context" "fmt" "io" + "log/slog" "os" "os/signal" "sync" @@ -32,9 +33,9 @@ import ( "github.com/gravitational/trace" "github.com/moby/term" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // Terminal is used to configure raw input and output modes for an attached @@ -80,26 +81,6 @@ func New(stdin io.Reader, stdout, stderr io.Writer) (*Terminal, error) { return &term, nil } -// addCRFormatter is a formatter which adds carriage return (CR) to the output of a base formatter. -// This is needed in case the logger output is fed into terminal in raw mode. -type addCRFormatter struct { - BaseFmt logrus.Formatter -} - -func (r addCRFormatter) Format(entry *logrus.Entry) ([]byte, error) { - out, err := r.BaseFmt.Format(entry) - if err != nil { - return nil, err - } - - replaced := bytes.ReplaceAll(out, []byte("\n"), []byte("\r\n")) - return replaced, nil -} - -func newCRFormatter(baseFmt logrus.Formatter) *addCRFormatter { - return &addCRFormatter{BaseFmt: baseFmt} -} - // InitRaw puts the terminal into raw mode. On Unix, no special input handling // is required beyond simply reading from stdin, so `input` has no effect. // Note that some implementations may replace one or more streams (particularly @@ -107,17 +88,18 @@ func newCRFormatter(baseFmt logrus.Formatter) *addCRFormatter { func (t *Terminal) InitRaw(input bool) error { // Put the terminal into raw mode. ts, err := term.SetRawTerminal(0) - fmtNew := newCRFormatter(logrus.StandardLogger().Formatter) - logrus.StandardLogger().Formatter = fmtNew + + originalHandler := slog.Default().Handler() + slog.SetDefault(slog.New(logutils.DiscardHandler{})) if err != nil { - log.Warnf("Could not put terminal into raw mode: %v", err) + log.WarnContext(context.Background(), "Could not put terminal into raw mode", "error", err) } else { // Ensure the terminal is reset on exit. t.closeWait.Add(1) go func() { <-t.closer.C term.RestoreTerminal(0, ts) - logrus.StandardLogger().Formatter = fmtNew.BaseFmt + slog.SetDefault(slog.New(originalHandler)) t.closeWait.Done() }() } diff --git a/lib/client/terminal/terminal_unix_test.go b/lib/client/terminal/terminal_unix_test.go deleted file mode 100644 index fe0fc2d586b3e..0000000000000 --- a/lib/client/terminal/terminal_unix_test.go +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package terminal - -import ( - "fmt" - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/require" -) - -// fixedDummyFmt is dummy formatter which ignores entry argument in Format() call and returns the same value, always. -type fixedDummyFmt struct { - bytes []byte - err error -} - -func (r fixedDummyFmt) Format(entry *logrus.Entry) ([]byte, error) { - return r.bytes, r.err -} - -func Test_addCRFormatter(t *testing.T) { - tests := []struct { - name string - input string - output string - wantErr bool - }{ - { - name: "no newlines", - input: "foo bar baz", - output: "foo bar baz", - }, - { - name: "single newline", - input: "foo bar baz\n", - output: "foo bar baz\r\n", - }, - { - name: "multiple newlines", - input: "foo\nbar\nbaz\n", - output: "foo\r\nbar\r\nbaz\r\n", - }, - { - name: "propagate error", - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - baseFmt := fixedDummyFmt{bytes: []byte(tt.input)} - if tt.wantErr { - baseFmt.err = fmt.Errorf("dummy") - } - - r := newCRFormatter(baseFmt) - actual, err := r.Format(nil) - - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - require.Equal(t, tt.output, string(actual)) - } - - }) - } -} diff --git a/lib/client/terminal/terminal_windows.go b/lib/client/terminal/terminal_windows.go index 9a9f71051310a..3c9e51ab9e229 100644 --- a/lib/client/terminal/terminal_windows.go +++ b/lib/client/terminal/terminal_windows.go @@ -19,6 +19,7 @@ package terminal import ( + "context" "fmt" "io" "os" @@ -76,7 +77,10 @@ func initTerminal(input bool) (func(), error) { // Attempt to reset the stdout mode before returning. err = winterm.SetConsoleMode(uintptr(stdoutFd), oldOutMode) if err != nil { - log.Errorf("Failed to reset terminal output mode to %d: %v\n", oldOutMode, err) + log.ErrorContext(context.Background(), "Failed to reset terminal output mode", + "original_output_mode", oldOutMode, + "error", err, + ) } return func() {}, fmt.Errorf("failed to set stdin mode: %w", err) @@ -86,13 +90,19 @@ func initTerminal(input bool) (func(), error) { return func() { err := winterm.SetConsoleMode(uintptr(stdoutFd), oldOutMode) if err != nil { - log.Errorf("Failed to reset terminal output mode to %d: %v\n", oldOutMode, err) + log.ErrorContext(context.Background(), "Failed to reset terminal output mode", + "original_output_mode", oldOutMode, + "error", err, + ) } if input { err = winterm.SetConsoleMode(uintptr(stdinFd), oldInMode) if err != nil { - log.Errorf("Failed to reset terminal input mode to %d: %v\n", oldInMode, err) + log.ErrorContext(context.Background(), "Failed to reset terminal input mode", + "original_input_mode", oldInMode, + "error", err, + ) } } }, nil diff --git a/lib/client/trusted_certs_store.go b/lib/client/trusted_certs_store.go index bb270e5ea1aa3..24dede0c3a9c3 100644 --- a/lib/client/trusted_certs_store.go +++ b/lib/client/trusted_certs_store.go @@ -25,13 +25,13 @@ import ( "encoding/pem" "fmt" iofs "io/fs" + "log/slog" "os" "path/filepath" "strings" "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "github.com/gravitational/teleport" @@ -161,7 +161,7 @@ func (ms *MemTrustedCertsStore) GetTrustedHostKeys(hostnames ...string) ([]ssh.P // The FS store uses the file layout outlined in `api/utils/keypaths.go`. type FSTrustedCertsStore struct { // log holds the structured logger. - log logrus.FieldLogger + log *slog.Logger // Dir is the directory where all keys are stored. Dir string @@ -171,7 +171,7 @@ type FSTrustedCertsStore struct { func NewFSTrustedCertsStore(dirPath string) *FSTrustedCertsStore { dirPath = profile.FullProfilePath(dirPath) return &FSTrustedCertsStore{ - log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), + log: slog.With(teleport.ComponentKey, teleport.ComponentKeyStore), Dir: dirPath, } } @@ -296,7 +296,7 @@ func (fs *FSTrustedCertsStore) saveTrustedCertsInCASDir(proxyHost string, cas [] } // check if cluster name is safe and doesn't contain miscellaneous characters. if strings.Contains(ca.ClusterName, "..") { - fs.log.Warnf("Skipped unsafe cluster name: %q", ca.ClusterName) + fs.log.WarnContext(context.Background(), "Skipped unsafe cluster name", "cluster_name", ca.ClusterName) continue } // Create CA files in cas dir for each cluster. @@ -392,7 +392,10 @@ func (fs *FSTrustedCertsStore) addKnownHosts(proxyHost string, cas []authclient. // add every host key to the list of entries for _, ca := range cas { for _, hostKey := range ca.AuthorizedKeys { - fs.log.Debugf("Adding known host %s with proxy %s", ca.ClusterName, proxyHost) + fs.log.DebugContext(context.Background(), "Adding known host entry", + "cluster_name", ca.ClusterName, + "proxy", proxyHost, + ) // Write keys in an OpenSSH-compatible format. A previous format was not // quite OpenSSH-compatible, so we may write a duplicate entry here. Any @@ -467,7 +470,10 @@ func (fs *FSTrustedCertsStore) GetTrustedCertsPEM(proxyHost string) ([][]byte, e break } if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { - fs.log.Debugf("Skipping PEM block type=%v headers=%v.", block.Type, block.Headers) + fs.log.DebugContext(context.Background(), "Skipping PEM block", + "type", block.Type, + "headers", block.Headers, + ) data = rest continue } diff --git a/lib/client/weblogin.go b/lib/client/weblogin.go index c3415e340417d..0aa7f07ea6503 100644 --- a/lib/client/weblogin.go +++ b/lib/client/weblogin.go @@ -21,12 +21,11 @@ package client import ( "bytes" "context" - "crypto/rand" "crypto/x509" - "encoding/hex" "encoding/json" "fmt" "io" + "log/slog" "net" "net/http" "net/http/cookiejar" @@ -37,7 +36,6 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/gravitational/roundtrip" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" @@ -50,7 +48,6 @@ import ( wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/httplib" - "github.com/gravitational/teleport/lib/httplib/csrf" websession "github.com/gravitational/teleport/lib/web/session" ) @@ -547,16 +544,18 @@ type TOTPRegisterChallenge struct { // initClient creates a new client to the HTTPS web proxy. func initClient(proxyAddr string, insecure bool, pool *x509.CertPool, extraHeaders map[string]string, opts ...roundtrip.ClientParam) (*WebClient, *url.URL, error) { - log := logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentClient, - }) - log.Debugf("HTTPS client init(proxyAddr=%v, insecure=%v, extraHeaders=%v)", proxyAddr, insecure, extraHeaders) + log := slog.With(teleport.ComponentKey, teleport.ComponentClient) + log.DebugContext(context.Background(), "Initializing proxy HTTPS client", + "proxy_addr", proxyAddr, + "insecure", insecure, + "extra_headers", extraHeaders, + ) // validate proxy address host, port, err := net.SplitHostPort(proxyAddr) if err != nil || host == "" || port == "" { if err != nil { - log.Error(err) + log.ErrorContext(context.Background(), "invalid proxy address", "error", err) } return nil, nil, trace.BadParameter("'%v' is not a valid proxy address", proxyAddr) } @@ -911,12 +910,6 @@ func SSHAgentLoginWeb(ctx context.Context, login SSHLoginDirect) (*WebClient, ty return nil, nil, trace.Wrap(err) } - token := make([]byte, 32) - if _, err := rand.Read(token); err != nil { - return nil, nil, trace.Wrap(err) - } - - csrfToken := hex.EncodeToString(token) resp, err := httplib.ConvertResponse(clt.RoundTrip(func() (*http.Response, error) { var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(&CreateWebSessionReq{ @@ -932,15 +925,7 @@ func SSHAgentLoginWeb(ctx context.Context, login SSHLoginDirect) (*WebClient, ty return nil, err } - cookie := &http.Cookie{ - Name: csrf.CookieName, - Value: csrfToken, - } - - req.AddCookie(cookie) - req.Header.Set("Content-Type", "application/json") - req.Header.Set(csrf.HeaderName, csrfToken) return clt.HTTPClient().Do(req) })) if err != nil { diff --git a/lib/client/weblogin_test.go b/lib/client/weblogin_test.go index 39722c78c2efe..cca05b892fe2b 100644 --- a/lib/client/weblogin_test.go +++ b/lib/client/weblogin_test.go @@ -121,7 +121,6 @@ func newServer(handler http.HandlerFunc, loopback bool) (*httptest.Server, error func TestSSHAgentPasswordlessLogin(t *testing.T) { t.Parallel() - silenceLogger(t) clock := clockwork.NewFakeClockAt(time.Now()) sa := newStandaloneTeleport(t, clock) diff --git a/lib/cloud/aws/aws.go b/lib/cloud/aws/aws.go index b1923a9bb9c3f..27ea56321b7df 100644 --- a/lib/cloud/aws/aws.go +++ b/lib/cloud/aws/aws.go @@ -22,12 +22,12 @@ import ( "slices" "strings" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/opensearchservice" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/coreos/go-semver/semver" "github.com/gravitational/teleport/lib/services" @@ -244,7 +244,7 @@ func IsDBClusterAvailable(clusterStatus, clusterIndetifier *string) bool { } // IsRedshiftClusterAvailable checks if the Redshift cluster is available. -func IsRedshiftClusterAvailable(cluster *redshift.Cluster) bool { +func IsRedshiftClusterAvailable(cluster *redshifttypes.Cluster) bool { // For a full list of status values, see: // https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-clusters.html#rs-mgmt-cluster-status // diff --git a/lib/cloud/aws/errors.go b/lib/cloud/aws/errors.go index 472590d35b1d4..576e7f4350ce2 100644 --- a/lib/cloud/aws/errors.go +++ b/lib/cloud/aws/errors.go @@ -31,14 +31,23 @@ import ( "github.com/gravitational/trace" ) -// ConvertRequestFailureError converts `error` into AWS RequestFailure errors -// to trace errors. If the provided error is not an `RequestFailure` it returns -// the error without modifying it. +// ConvertRequestFailureError converts `err` into AWS errors to trace errors. +// If the provided error is not a [awserr.RequestFailure] it delegates +// error conversion to [ConvertRequestFailureErrorV2] for SDK v2 compatibility. +// Prefer using [ConvertRequestFailureErrorV2] directly for AWS SDK v2 client +// errors. func ConvertRequestFailureError(err error) error { var requestErr awserr.RequestFailure if errors.As(err, &requestErr) { return convertRequestFailureErrorFromStatusCode(requestErr.StatusCode(), requestErr) } + return ConvertRequestFailureErrorV2(err) +} + +// ConvertRequestFailureErrorV2 converts AWS SDK v2 errors to trace errors. +// If the provided error is not a [awshttp.ResponseError] it returns the error +// without modifying it. +func ConvertRequestFailureErrorV2(err error) error { var re *awshttp.ResponseError if errors.As(err, &re) { return convertRequestFailureErrorFromStatusCode(re.HTTPStatusCode(), re.Err) diff --git a/lib/cloud/aws/policy_statements.go b/lib/cloud/aws/policy_statements.go index a1a0ef1b32a02..d266bed81a088 100644 --- a/lib/cloud/aws/policy_statements.go +++ b/lib/cloud/aws/policy_statements.go @@ -406,6 +406,9 @@ func StatementAccessGraphAWSSync() *Statement { "s3:ListBucket", "s3:GetBucketLocation", "s3:GetBucketTagging", + "s3:GetBucketPolicyStatus", + "s3:GetBucketAcl", + // IAM IAM "iam:ListUsers", "iam:GetUser", diff --git a/lib/cloud/aws/tags_helpers.go b/lib/cloud/aws/tags_helpers.go index 27dbe8238f178..3e61bd6fc1a42 100644 --- a/lib/cloud/aws/tags_helpers.go +++ b/lib/cloud/aws/tags_helpers.go @@ -25,13 +25,13 @@ import ( ec2TypesV2 "github.com/aws/aws-sdk-go-v2/service/ec2/types" rdsTypesV2 "github.com/aws/aws-sdk-go-v2/service/rds/types" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/opensearchservice" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/aws/aws-sdk-go/service/secretsmanager" "golang.org/x/exp/maps" @@ -45,9 +45,9 @@ type ResourceTag interface { // here and use a type switch for now. rdsTypesV2.Tag | ec2TypesV2.Tag | + redshifttypes.Tag | *ec2.Tag | *rds.Tag | - *redshift.Tag | *elasticache.Tag | *memorydb.Tag | *redshiftserverless.Tag | @@ -80,8 +80,6 @@ func resourceTagToKeyValue[Tag ResourceTag](tag Tag) (string, string) { return aws.StringValue(v.Key), aws.StringValue(v.Value) case *ec2.Tag: return aws.StringValue(v.Key), aws.StringValue(v.Value) - case *redshift.Tag: - return aws.StringValue(v.Key), aws.StringValue(v.Value) case *elasticache.Tag: return aws.StringValue(v.Key), aws.StringValue(v.Value) case *memorydb.Tag: @@ -92,6 +90,8 @@ func resourceTagToKeyValue[Tag ResourceTag](tag Tag) (string, string) { return aws.StringValue(v.Key), aws.StringValue(v.Value) case ec2TypesV2.Tag: return aws.StringValue(v.Key), aws.StringValue(v.Value) + case redshifttypes.Tag: + return aws.StringValue(v.Key), aws.StringValue(v.Value) case *opensearchservice.Tag: return aws.StringValue(v.Key), aws.StringValue(v.Value) case *secretsmanager.Tag: diff --git a/lib/cloud/awsconfig/awsconfig.go b/lib/cloud/awsconfig/awsconfig.go index 92f7e8aa96e86..8be00483f4012 100644 --- a/lib/cloud/awsconfig/awsconfig.go +++ b/lib/cloud/awsconfig/awsconfig.go @@ -47,16 +47,23 @@ const ( // This is used to generate aws configs for clients that must use an integration instead of ambient credentials. type IntegrationCredentialProviderFunc func(ctx context.Context, region, integration string) (aws.CredentialsProvider, error) +// AssumeRoleClientProviderFunc provides an AWS STS assume role API client. +type AssumeRoleClientProviderFunc func(aws.Config) stscreds.AssumeRoleAPIClient + +// AssumeRole is an AWS role to assume, optionally with an external ID. +type AssumeRole struct { + // RoleARN is the ARN of the role to assume. + RoleARN string `json:"role_arn"` + // ExternalID is an optional ID to include when assuming the role. + ExternalID string `json:"external_id"` +} + // options is a struct of additional options for assuming an AWS role // when construction an underlying AWS config. type options struct { - // baseConfigis a config to use instead of the default config for an - // AWS region, which is used to enable role chaining. - baseConfig *aws.Config - // assumeRoleARN is the AWS IAM Role ARN to assume. - assumeRoleARN string - // assumeRoleExternalID is used to assume an external AWS IAM Role. - assumeRoleExternalID string + // assumeRoles are AWS IAM roles that should be assumed one by one in order, + // as a chain of assumed roles. + assumeRoles []AssumeRole // credentialsSource describes which source to use to fetch credentials. credentialsSource credentialsSource // integration is the name of the integration to be used to fetch the credentials. @@ -67,22 +74,45 @@ type options struct { customRetryer func() aws.Retryer // maxRetries is the maximum number of retries to use for the config. maxRetries *int + // assumeRoleClientProvider sets the STS assume role client provider func. + assumeRoleClientProvider AssumeRoleClientProviderFunc } -func (a *options) checkAndSetDefaults() error { - switch a.credentialsSource { +func buildOptions(optFns ...OptionsFn) (*options, error) { + var opts options + for _, optFn := range optFns { + optFn(&opts) + } + if err := opts.checkAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + return &opts, nil +} + +func (o *options) checkAndSetDefaults() error { + switch o.credentialsSource { case credentialsSourceAmbient: - if a.integration != "" { + if o.integration != "" { return trace.BadParameter("integration and ambient credentials cannot be used at the same time") } case credentialsSourceIntegration: - if a.integration == "" { + if o.integration == "" { return trace.BadParameter("missing integration name") } default: return trace.BadParameter("missing credentials source (ambient or integration)") } + if len(o.assumeRoles) > 2 { + return trace.BadParameter("role chain contains more than 2 roles") + } + if o.assumeRoleClientProvider == nil { + o.assumeRoleClientProvider = func(cfg aws.Config) stscreds.AssumeRoleAPIClient { + return sts.NewFromConfig(cfg, func(o *sts.Options) { + o.TracerProvider = smithyoteltracing.Adapt(otel.GetTracerProvider()) + }) + } + } return nil } @@ -93,8 +123,14 @@ type OptionsFn func(*options) // WithAssumeRole configures options needed for assuming an AWS role. func WithAssumeRole(roleARN, externalID string) OptionsFn { return func(options *options) { - options.assumeRoleARN = roleARN - options.assumeRoleExternalID = externalID + if roleARN == "" { + // ignore empty role ARN for caller convenience. + return + } + options.assumeRoles = append(options.assumeRoles, AssumeRole{ + RoleARN: roleARN, + ExternalID: externalID, + }) } } @@ -146,96 +182,98 @@ func WithIntegrationCredentialProvider(cred IntegrationCredentialProviderFunc) O } } +// WithAssumeRoleClientProviderFunc sets the STS API client factory func used to +// assume roles. +func WithAssumeRoleClientProviderFunc(fn AssumeRoleClientProviderFunc) OptionsFn { + return func(options *options) { + options.assumeRoleClientProvider = fn + } +} + // GetConfig returns an AWS config for the specified region, optionally // assuming AWS IAM Roles. -func GetConfig(ctx context.Context, region string, opts ...OptionsFn) (aws.Config, error) { - var options options - for _, opt := range opts { - opt(&options) - } - if options.baseConfig == nil { - cfg, err := getConfigForRegion(ctx, region, options) - if err != nil { - return aws.Config{}, trace.Wrap(err) - } - options.baseConfig = &cfg +func GetConfig(ctx context.Context, region string, optFns ...OptionsFn) (aws.Config, error) { + opts, err := buildOptions(optFns...) + if err != nil { + return aws.Config{}, trace.Wrap(err) } - if options.assumeRoleARN == "" { - return *options.baseConfig, nil + + cfg, err := getBaseConfig(ctx, region, opts) + if err != nil { + return aws.Config{}, trace.Wrap(err) } - return getConfigForRole(ctx, region, options) + return getConfigForRoleChain(ctx, cfg, opts.assumeRoles, opts.assumeRoleClientProvider) } -// ambientConfigProvider loads a new config using the environment variables. -func ambientConfigProvider(region string, cred aws.CredentialsProvider, options options) (aws.Config, error) { - opts := buildConfigOptions(region, cred, options) - cfg, err := config.LoadDefaultConfig(context.Background(), opts...) +// loadDefaultConfig loads a new config. +func loadDefaultConfig(ctx context.Context, region string, cred aws.CredentialsProvider, opts *options) (aws.Config, error) { + configOpts := buildConfigOptions(region, cred, opts) + cfg, err := config.LoadDefaultConfig(ctx, configOpts...) return cfg, trace.Wrap(err) } -func buildConfigOptions(region string, cred aws.CredentialsProvider, options options) []func(*config.LoadOptions) error { - opts := []func(*config.LoadOptions) error{ +func buildConfigOptions(region string, cred aws.CredentialsProvider, opts *options) []func(*config.LoadOptions) error { + configOpts := []func(*config.LoadOptions) error{ config.WithDefaultRegion(defaultRegion), config.WithRegion(region), config.WithCredentialsProvider(cred), } if modules.GetModules().IsBoringBinary() { - opts = append(opts, config.WithUseFIPSEndpoint(aws.FIPSEndpointStateEnabled)) + configOpts = append(configOpts, config.WithUseFIPSEndpoint(aws.FIPSEndpointStateEnabled)) } - if options.customRetryer != nil { - opts = append(opts, config.WithRetryer(options.customRetryer)) + if opts.customRetryer != nil { + configOpts = append(configOpts, config.WithRetryer(opts.customRetryer)) } - if options.maxRetries != nil { - opts = append(opts, config.WithRetryMaxAttempts(*options.maxRetries)) + if opts.maxRetries != nil { + configOpts = append(configOpts, config.WithRetryMaxAttempts(*opts.maxRetries)) } - return opts + return configOpts } -// getConfigForRegion returns AWS config for the specified region. -func getConfigForRegion(ctx context.Context, region string, options options) (aws.Config, error) { - if err := options.checkAndSetDefaults(); err != nil { - return aws.Config{}, trace.Wrap(err) - } - +// getBaseConfig returns an AWS config without assuming any roles. +func getBaseConfig(ctx context.Context, region string, opts *options) (aws.Config, error) { var cred aws.CredentialsProvider - if options.credentialsSource == credentialsSourceIntegration { - if options.integrationCredentialsProvider == nil { + if opts.credentialsSource == credentialsSourceIntegration { + if opts.integrationCredentialsProvider == nil { return aws.Config{}, trace.BadParameter("missing aws integration credential provider") } - slog.DebugContext(ctx, "Initializing AWS config with integration", "region", region, "integration", options.integration) + slog.DebugContext(ctx, "Initializing AWS config with integration", "region", region, "integration", opts.integration) var err error - cred, err = options.integrationCredentialsProvider(ctx, region, options.integration) + cred, err = opts.integrationCredentialsProvider(ctx, region, opts.integration) if err != nil { return aws.Config{}, trace.Wrap(err) } } else { - slog.DebugContext(ctx, "Initializing AWS config from environment", "region", region) + slog.DebugContext(ctx, "Initializing AWS config from default credential chain", "region", region) } - cfg, err := ambientConfigProvider(region, cred, options) + cfg, err := loadDefaultConfig(ctx, region, cred, opts) return cfg, trace.Wrap(err) } -// getConfigForRole returns an AWS config for the specified region and role. -func getConfigForRole(ctx context.Context, region string, options options) (aws.Config, error) { - if err := options.checkAndSetDefaults(); err != nil { - return aws.Config{}, trace.Wrap(err) +func getConfigForRoleChain(ctx context.Context, cfg aws.Config, roles []AssumeRole, newCltFn AssumeRoleClientProviderFunc) (aws.Config, error) { + for _, r := range roles { + cfg.Credentials = getAssumeRoleProvider(ctx, newCltFn(cfg), r) } - - stsClient := sts.NewFromConfig(*options.baseConfig, func(o *sts.Options) { - o.TracerProvider = smithyoteltracing.Adapt(otel.GetTracerProvider()) - }) - cred := stscreds.NewAssumeRoleProvider(stsClient, options.assumeRoleARN, func(aro *stscreds.AssumeRoleOptions) { - if options.assumeRoleExternalID != "" { - aro.ExternalID = aws.String(options.assumeRoleExternalID) + if len(roles) > 0 { + // no point caching every assumed role in the chain, we can just cache + // the last one. + cfg.Credentials = aws.NewCredentialsCache(cfg.Credentials, awsCredentialsCacheOptions) + if _, err := cfg.Credentials.Retrieve(ctx); err != nil { + return aws.Config{}, trace.Wrap(err) } - }) - if _, err := cred.Retrieve(ctx); err != nil { - return aws.Config{}, trace.Wrap(err) } + return cfg, nil +} - opts := buildConfigOptions(region, cred, options) - cfg, err := config.LoadDefaultConfig(ctx, opts...) - return cfg, trace.Wrap(err) +func getAssumeRoleProvider(ctx context.Context, clt stscreds.AssumeRoleAPIClient, role AssumeRole) aws.CredentialsProvider { + slog.DebugContext(ctx, "Initializing AWS session for assumed role", + "assumed_role", role.RoleARN, + ) + return stscreds.NewAssumeRoleProvider(clt, role.RoleARN, func(aro *stscreds.AssumeRoleOptions) { + if role.ExternalID != "" { + aro.ExternalID = aws.String(role.ExternalID) + } + }) } diff --git a/lib/cloud/awsconfig/awsconfig_test.go b/lib/cloud/awsconfig/awsconfig_test.go index 5c0ab10ed6abb..3cb2c4eda3123 100644 --- a/lib/cloud/awsconfig/awsconfig_test.go +++ b/lib/cloud/awsconfig/awsconfig_test.go @@ -18,9 +18,15 @@ package awsconfig import ( "context" + "fmt" + "strings" "testing" + "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/sts" + ststypes "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/gravitational/trace" "github.com/stretchr/testify/require" ) @@ -29,18 +35,60 @@ type mockCredentialProvider struct { cred aws.Credentials } -func (m *mockCredentialProvider) Retrieve(ctx context.Context) (aws.Credentials, error) { +func (m *mockCredentialProvider) Retrieve(_ context.Context) (aws.Credentials, error) { return m.cred, nil } +type mockAssumeRoleAPIClient struct{} + +func (m *mockAssumeRoleAPIClient) AssumeRole(_ context.Context, params *sts.AssumeRoleInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleOutput, error) { + fakeKeyID := fmt.Sprintf("role: %s, externalID: %s", aws.ToString(params.RoleArn), aws.ToString(params.ExternalId)) + return &sts.AssumeRoleOutput{ + AssumedRoleUser: &ststypes.AssumedRoleUser{ + Arn: params.RoleArn, + AssumedRoleId: aws.String("role-id"), + }, + Credentials: &ststypes.Credentials{ + AccessKeyId: aws.String(fakeKeyID), + Expiration: aws.Time(time.Time{}), + SecretAccessKey: aws.String("fake-secret-access-key"), + SessionToken: aws.String("fake-session-token"), + }, + }, nil +} + func TestGetConfigIntegration(t *testing.T) { t.Parallel() + + cache, err := NewCache() + require.NoError(t, err) + tests := []struct { + desc string + Provider + }{ + { + desc: "uncached", + Provider: ProviderFunc(GetConfig), + }, + { + desc: "cached", + Provider: cache, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + testGetConfigIntegration(t, test.Provider) + }) + } +} + +func testGetConfigIntegration(t *testing.T, provider Provider) { dummyIntegration := "integration-test" dummyRegion := "test-region-123" t.Run("without an integration credential provider, must return missing credential provider error", func(t *testing.T) { ctx := context.Background() - _, err := GetConfig(ctx, dummyRegion, WithCredentialsMaybeIntegration(dummyIntegration)) + _, err := provider.GetConfig(ctx, dummyRegion, WithCredentialsMaybeIntegration(dummyIntegration)) require.True(t, trace.IsBadParameter(err), "unexpected error: %v", err) require.ErrorContains(t, err, "missing aws integration credential provider") }) @@ -48,7 +96,7 @@ func TestGetConfigIntegration(t *testing.T) { t.Run("with an integration credential provider, must return the credentials", func(t *testing.T) { ctx := context.Background() - cfg, err := GetConfig(ctx, dummyRegion, + cfg, err := provider.GetConfig(ctx, dummyRegion, WithCredentialsMaybeIntegration(dummyIntegration), WithIntegrationCredentialProvider(func(ctx context.Context, region, integration string) (aws.CredentialsProvider, error) { if region == dummyRegion && integration == dummyIntegration { @@ -66,10 +114,68 @@ func TestGetConfigIntegration(t *testing.T) { require.Equal(t, "foo-bar", creds.SessionToken) }) + t.Run("with an integration credential provider assuming a role, must return assumed role credentials", func(t *testing.T) { + ctx := context.Background() + + cfg, err := provider.GetConfig(ctx, dummyRegion, + WithCredentialsMaybeIntegration(dummyIntegration), + WithIntegrationCredentialProvider(func(ctx context.Context, region, integration string) (aws.CredentialsProvider, error) { + if region == dummyRegion && integration == dummyIntegration { + return &mockCredentialProvider{ + cred: aws.Credentials{ + SessionToken: "foo-bar", + }, + }, nil + } + return nil, trace.NotFound("no creds in region %q with integration %q", region, integration) + }), + WithAssumeRole("roleA", "abc123"), + WithAssumeRoleClientProviderFunc(func(cfg aws.Config) stscreds.AssumeRoleAPIClient { + creds, err := cfg.Credentials.Retrieve(context.Background()) + require.NoError(t, err) + require.Equal(t, "foo-bar", creds.SessionToken) + return &mockAssumeRoleAPIClient{} + }), + ) + require.NoError(t, err) + creds, err := cfg.Credentials.Retrieve(ctx) + require.NoError(t, err) + require.Equal(t, "role: roleA, externalID: abc123", creds.AccessKeyID) + require.Equal(t, "fake-session-token", creds.SessionToken) + }) + + t.Run("with an integration credential provider assuming a role, must limit role chain length", func(t *testing.T) { + ctx := context.Background() + _, err := provider.GetConfig(ctx, dummyRegion, + WithCredentialsMaybeIntegration(dummyIntegration), + WithIntegrationCredentialProvider(func(ctx context.Context, region, integration string) (aws.CredentialsProvider, error) { + if region == dummyRegion && integration == dummyIntegration { + return &mockCredentialProvider{ + cred: aws.Credentials{ + SessionToken: "foo-bar", + }, + }, nil + } + return nil, trace.NotFound("no creds in region %q with integration %q", region, integration) + }), + WithAssumeRole("roleA", "abc123"), + WithAssumeRole("roleB", "abc123"), + WithAssumeRole("roleC", "abc123"), + WithAssumeRoleClientProviderFunc(func(cfg aws.Config) stscreds.AssumeRoleAPIClient { + creds, err := cfg.Credentials.Retrieve(context.Background()) + require.NoError(t, err) + require.Equal(t, "foo-bar", creds.SessionToken) + return &mockAssumeRoleAPIClient{} + }), + ) + require.Error(t, err) + require.ErrorContains(t, err, "role chain contains more than 2 roles") + }) + t.Run("with an integration credential provider, but using an empty integration falls back to ambient credentials", func(t *testing.T) { ctx := context.Background() - _, err := GetConfig(ctx, dummyRegion, + _, err := provider.GetConfig(ctx, dummyRegion, WithCredentialsMaybeIntegration(""), WithIntegrationCredentialProvider(func(ctx context.Context, region, integration string) (aws.CredentialsProvider, error) { require.Fail(t, "this function should not be called") @@ -81,7 +187,7 @@ func TestGetConfigIntegration(t *testing.T) { t.Run("with an integration credential provider, but using ambient credentials", func(t *testing.T) { ctx := context.Background() - _, err := GetConfig(ctx, dummyRegion, + _, err := provider.GetConfig(ctx, dummyRegion, WithAmbientCredentials(), WithIntegrationCredentialProvider(func(ctx context.Context, region, integration string) (aws.CredentialsProvider, error) { require.Fail(t, "this function should not be called") @@ -93,7 +199,7 @@ func TestGetConfigIntegration(t *testing.T) { t.Run("with an integration credential provider, but no credential source", func(t *testing.T) { ctx := context.Background() - _, err := GetConfig(ctx, dummyRegion, + _, err := provider.GetConfig(ctx, dummyRegion, WithIntegrationCredentialProvider(func(ctx context.Context, region, integration string) (aws.CredentialsProvider, error) { require.Fail(t, "this function should not be called") return nil, nil @@ -102,3 +208,16 @@ func TestGetConfigIntegration(t *testing.T) { require.ErrorContains(t, err, "missing credentials source") }) } + +func TestNewCacheKey(t *testing.T) { + roleChain := []AssumeRole{ + {RoleARN: "roleA"}, + {RoleARN: "roleB", ExternalID: "abc123"}, + } + got, err := newCacheKey("integration-name", roleChain...) + require.NoError(t, err) + want := strings.TrimSpace(` +{"integration":"integration-name","role_chain":[{"role_arn":"roleA","external_id":""},{"role_arn":"roleB","external_id":"abc123"}]} +`) + require.Equal(t, want, got) +} diff --git a/lib/cloud/awsconfig/cache.go b/lib/cloud/awsconfig/cache.go new file mode 100644 index 0000000000000..3d664ba04c350 --- /dev/null +++ b/lib/cloud/awsconfig/cache.go @@ -0,0 +1,147 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package awsconfig + +import ( + "context" + "encoding/json" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/lib/utils" +) + +func awsCredentialsCacheOptions(opts *aws.CredentialsCacheOptions) { + // expire early to avoid expiration race. + opts.ExpiryWindow = 2 * time.Minute +} + +// Cache is an AWS config [Provider] that caches credentials by integration and +// role. +type Cache struct { + awsConfigCache *utils.FnCache +} + +// NewCache returns a new [Cache]. +func NewCache() (*Cache, error) { + c, err := utils.NewFnCache(utils.FnCacheConfig{ + TTL: 15 * time.Minute, + ReloadOnErr: true, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return &Cache{ + awsConfigCache: c, + }, nil +} + +// GetConfig returns an [aws.Config] for the given region and options. +func (c *Cache) GetConfig(ctx context.Context, region string, optFns ...OptionsFn) (aws.Config, error) { + opts, err := buildOptions(optFns...) + if err != nil { + return aws.Config{}, trace.Wrap(err) + } + + cfg, err := c.getBaseConfig(ctx, region, opts) + if err != nil { + return aws.Config{}, trace.Wrap(err) + } + cfg, err = c.getConfigForRoleChain(ctx, cfg, opts) + if err != nil { + return aws.Config{}, trace.Wrap(err) + } + return cfg, nil +} + +func (c *Cache) getBaseConfig(ctx context.Context, region string, opts *options) (aws.Config, error) { + // The AWS SDK combines config loading with default credential chain + // loading. + // We cache the entire config by integration name, which is empty for + // non-integration config, but only use credentials from it on cache hit. + cacheKey, err := newCacheKey(opts.integration) + if err != nil { + return aws.Config{}, trace.Wrap(err) + } + var reloaded bool + cfg, err := utils.FnCacheGet(ctx, c.awsConfigCache, cacheKey, + func(ctx context.Context) (aws.Config, error) { + reloaded = true + cfg, err := getBaseConfig(ctx, region, opts) + return cfg, trace.Wrap(err) + }) + if err != nil { + return aws.Config{}, trace.Wrap(err) + } + + if reloaded { + // If the cache reload func was called, then the config we got back has + // already applied our options so we can return the config itself. + return cfg, nil + } + + // On cache hit we just take the credentials from the cached config. + // Then, we apply those credentials while loading config with current + // options. + cfg, err = loadDefaultConfig(ctx, region, cfg.Credentials, opts) + return cfg, trace.Wrap(err) +} + +func (c *Cache) getConfigForRoleChain(ctx context.Context, cfg aws.Config, opts *options) (aws.Config, error) { + for i, r := range opts.assumeRoles { + // cache credentials by integration and assumed-role chain. + cacheKey, err := newCacheKey(opts.integration, opts.assumeRoles[:i+1]...) + if err != nil { + return aws.Config{}, trace.Wrap(err) + } + credProvider, err := utils.FnCacheGet(ctx, c.awsConfigCache, cacheKey, + func(ctx context.Context) (aws.CredentialsProvider, error) { + clt := opts.assumeRoleClientProvider(cfg) + credProvider := getAssumeRoleProvider(ctx, clt, r) + cc := aws.NewCredentialsCache(credProvider, + awsCredentialsCacheOptions, + ) + if _, err := cc.Retrieve(ctx); err != nil { + return nil, trace.Wrap(err) + } + return cc, nil + }) + if err != nil { + return aws.Config{}, trace.Wrap(err) + } + cfg.Credentials = credProvider + } + return cfg, nil +} + +// newCacheKey returns a cache key for AWS credentials. +// The cache key can be used to get role credentials without calling AWS STS. +// Therefore, we marshal the key as JSON to be sure the input cannot be +// manipulated to retrieve other credentials. +func newCacheKey(integrationName string, roleChain ...AssumeRole) (string, error) { + type configCacheKey struct { + Integration string `json:"integration"` + RoleChain []AssumeRole `json:"role_chain"` + } + out, err := json.Marshal(configCacheKey{ + Integration: integrationName, + RoleChain: roleChain, + }) + return string(out), trace.Wrap(err) +} diff --git a/lib/cloud/awsconfig/provider.go b/lib/cloud/awsconfig/provider.go new file mode 100644 index 0000000000000..fce6dfb407b8d --- /dev/null +++ b/lib/cloud/awsconfig/provider.go @@ -0,0 +1,37 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package awsconfig + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" +) + +// Provider provides an [aws.Config]. +type Provider interface { + // GetConfig returns an [aws.Config] for the given region and options. + GetConfig(ctx context.Context, region string, optFns ...OptionsFn) (aws.Config, error) +} + +// ProviderFunc is a [Provider] adapter for functions. +type ProviderFunc func(ctx context.Context, region string, optFns ...OptionsFn) (aws.Config, error) + +// GetConfig returns an [aws.Config] for the given region and options. +func (fn ProviderFunc) GetConfig(ctx context.Context, region string, optFns ...OptionsFn) (aws.Config, error) { + return fn(ctx, region, optFns...) +} diff --git a/lib/cloud/awstesthelpers/tags.go b/lib/cloud/awstesthelpers/tags.go new file mode 100644 index 0000000000000..5e1f4aa0e0738 --- /dev/null +++ b/lib/cloud/awstesthelpers/tags.go @@ -0,0 +1,45 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package awstesthelpers + +import ( + "maps" + "slices" + + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" +) + +// LabelsToRedshiftTags converts labels into [redshifttypes.Tag] list. +func LabelsToRedshiftTags(labels map[string]string) []redshifttypes.Tag { + keys := slices.Collect(maps.Keys(labels)) + slices.Sort(keys) + + ret := make([]redshifttypes.Tag, 0, len(keys)) + for _, key := range keys { + key := key + value := labels[key] + + ret = append(ret, redshifttypes.Tag{ + Key: &key, + Value: &value, + }) + } + + return ret +} diff --git a/lib/cloud/clients.go b/lib/cloud/clients.go index 54b02d84dc400..99c2deb4001f0 100644 --- a/lib/cloud/clients.go +++ b/lib/cloud/clients.go @@ -53,8 +53,6 @@ import ( "github.com/aws/aws-sdk-go/service/opensearchservice/opensearchserviceiface" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/rds/rdsiface" - "github.com/aws/aws-sdk-go/service/redshift" - "github.com/aws/aws-sdk-go/service/redshift/redshiftiface" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/aws/aws-sdk-go/service/redshiftserverless/redshiftserverlessiface" "github.com/aws/aws-sdk-go/service/s3" @@ -115,8 +113,6 @@ type AWSClients interface { GetAWSSession(ctx context.Context, region string, opts ...AWSOptionsFn) (*awssession.Session, error) // GetAWSRDSClient returns AWS RDS client for the specified region. GetAWSRDSClient(ctx context.Context, region string, opts ...AWSOptionsFn) (rdsiface.RDSAPI, error) - // GetAWSRedshiftClient returns AWS Redshift client for the specified region. - GetAWSRedshiftClient(ctx context.Context, region string, opts ...AWSOptionsFn) (redshiftiface.RedshiftAPI, error) // GetAWSRedshiftServerlessClient returns AWS Redshift Serverless client for the specified region. GetAWSRedshiftServerlessClient(ctx context.Context, region string, opts ...AWSOptionsFn) (redshiftserverlessiface.RedshiftServerlessAPI, error) // GetAWSElastiCacheClient returns AWS ElastiCache client for the specified region. @@ -517,15 +513,6 @@ func (c *cloudClients) GetAWSRDSClient(ctx context.Context, region string, opts return rds.New(session), nil } -// GetAWSRedshiftClient returns AWS Redshift client for the specified region. -func (c *cloudClients) GetAWSRedshiftClient(ctx context.Context, region string, opts ...AWSOptionsFn) (redshiftiface.RedshiftAPI, error) { - session, err := c.GetAWSSession(ctx, region, opts...) - if err != nil { - return nil, trace.Wrap(err) - } - return redshift.New(session), nil -} - // GetAWSRedshiftServerlessClient returns AWS Redshift Serverless client for the specified region. func (c *cloudClients) GetAWSRedshiftServerlessClient(ctx context.Context, region string, opts ...AWSOptionsFn) (redshiftserverlessiface.RedshiftServerlessAPI, error) { session, err := c.GetAWSSession(ctx, region, opts...) @@ -1033,7 +1020,6 @@ var _ Clients = (*TestCloudClients)(nil) type TestCloudClients struct { RDS rdsiface.RDSAPI RDSPerRegion map[string]rdsiface.RDSAPI - Redshift redshiftiface.RedshiftAPI RedshiftServerless redshiftserverlessiface.RedshiftServerlessAPI ElastiCache elasticacheiface.ElastiCacheAPI OpenSearch opensearchserviceiface.OpenSearchServiceAPI @@ -1115,15 +1101,6 @@ func (c *TestCloudClients) GetAWSRDSClient(ctx context.Context, region string, o return c.RDS, nil } -// GetAWSRedshiftClient returns AWS Redshift client for the specified region. -func (c *TestCloudClients) GetAWSRedshiftClient(ctx context.Context, region string, opts ...AWSOptionsFn) (redshiftiface.RedshiftAPI, error) { - _, err := c.GetAWSSession(ctx, region, opts...) - if err != nil { - return nil, trace.Wrap(err) - } - return c.Redshift, nil -} - // GetAWSRedshiftServerlessClient returns AWS Redshift Serverless client for the specified region. func (c *TestCloudClients) GetAWSRedshiftServerlessClient(ctx context.Context, region string, opts ...AWSOptionsFn) (redshiftserverlessiface.RedshiftServerlessAPI, error) { _, err := c.GetAWSSession(ctx, region, opts...) diff --git a/lib/cloud/mocks/aws.go b/lib/cloud/mocks/aws.go index 016634a9e5529..ceb50bd822cc2 100644 --- a/lib/cloud/mocks/aws.go +++ b/lib/cloud/mocks/aws.go @@ -37,8 +37,8 @@ import ( "github.com/gravitational/trace" ) -// STSMock mocks AWS STS API. -type STSMock struct { +// STSClientV1 mocks AWS STS API for AWS SDK v1. +type STSClientV1 struct { stsiface.STSAPI ARN string URL *url.URL @@ -47,36 +47,36 @@ type STSMock struct { mu sync.Mutex } -func (m *STSMock) GetAssumedRoleARNs() []string { +func (m *STSClientV1) GetAssumedRoleARNs() []string { m.mu.Lock() defer m.mu.Unlock() return m.assumedRoleARNs } -func (m *STSMock) GetAssumedRoleExternalIDs() []string { +func (m *STSClientV1) GetAssumedRoleExternalIDs() []string { m.mu.Lock() defer m.mu.Unlock() return m.assumedRoleExternalIDs } -func (m *STSMock) ResetAssumeRoleHistory() { +func (m *STSClientV1) ResetAssumeRoleHistory() { m.mu.Lock() defer m.mu.Unlock() m.assumedRoleARNs = nil m.assumedRoleExternalIDs = nil } -func (m *STSMock) GetCallerIdentityWithContext(aws.Context, *sts.GetCallerIdentityInput, ...request.Option) (*sts.GetCallerIdentityOutput, error) { +func (m *STSClientV1) GetCallerIdentityWithContext(aws.Context, *sts.GetCallerIdentityInput, ...request.Option) (*sts.GetCallerIdentityOutput, error) { return &sts.GetCallerIdentityOutput{ Arn: aws.String(m.ARN), }, nil } -func (m *STSMock) AssumeRole(in *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) { +func (m *STSClientV1) AssumeRole(in *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) { return m.AssumeRoleWithContext(context.Background(), in) } -func (m *STSMock) AssumeRoleWithContext(ctx aws.Context, in *sts.AssumeRoleInput, _ ...request.Option) (*sts.AssumeRoleOutput, error) { +func (m *STSClientV1) AssumeRoleWithContext(ctx aws.Context, in *sts.AssumeRoleInput, _ ...request.Option) (*sts.AssumeRoleOutput, error) { m.mu.Lock() defer m.mu.Unlock() if !slices.Contains(m.assumedRoleARNs, aws.StringValue(in.RoleArn)) { @@ -94,7 +94,7 @@ func (m *STSMock) AssumeRoleWithContext(ctx aws.Context, in *sts.AssumeRoleInput }, nil } -func (m *STSMock) GetCallerIdentityRequest(req *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { +func (m *STSClientV1) GetCallerIdentityRequest(req *sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) { return &request.Request{ HTTPRequest: &http.Request{ Header: http.Header{}, diff --git a/lib/cloud/mocks/aws_config.go b/lib/cloud/mocks/aws_config.go new file mode 100644 index 0000000000000..7edadf80a9e20 --- /dev/null +++ b/lib/cloud/mocks/aws_config.go @@ -0,0 +1,42 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package mocks + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + + "github.com/gravitational/teleport/lib/cloud/awsconfig" +) + +type AWSConfigProvider struct { + STSClient *STSClient +} + +func (f *AWSConfigProvider) GetConfig(ctx context.Context, region string, optFns ...awsconfig.OptionsFn) (aws.Config, error) { + stsClt := f.STSClient + if stsClt == nil { + stsClt = &STSClient{} + } + optFns = append(optFns, awsconfig.WithAssumeRoleClientProviderFunc( + newAssumeRoleClientProviderFunc(stsClt), + )) + return awsconfig.GetConfig(ctx, region, optFns...) +} diff --git a/lib/cloud/mocks/aws_redshift.go b/lib/cloud/mocks/aws_redshift.go index 1e4855d249fd5..d485d1dcc5a5e 100644 --- a/lib/cloud/mocks/aws_redshift.go +++ b/lib/cloud/mocks/aws_redshift.go @@ -19,74 +19,62 @@ package mocks import ( + "context" "fmt" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/redshift" - "github.com/aws/aws-sdk-go/service/redshift/redshiftiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - libcloudaws "github.com/gravitational/teleport/lib/cloud/aws" + "github.com/gravitational/teleport/lib/cloud/awstesthelpers" ) -// RedshiftMock mocks AWS Redshift API. -type RedshiftMock struct { - redshiftiface.RedshiftAPI - Clusters []*redshift.Cluster +type RedshiftClient struct { + Unauth bool + + Clusters []redshifttypes.Cluster GetClusterCredentialsOutput *redshift.GetClusterCredentialsOutput GetClusterCredentialsWithIAMOutput *redshift.GetClusterCredentialsWithIAMOutput } -func (m *RedshiftMock) GetClusterCredentialsWithContext(aws.Context, *redshift.GetClusterCredentialsInput, ...request.Option) (*redshift.GetClusterCredentialsOutput, error) { - if m.GetClusterCredentialsOutput == nil { - return nil, trace.AccessDenied("access denied") +func (m *RedshiftClient) DescribeClusters(_ context.Context, input *redshift.DescribeClustersInput, _ ...func(*redshift.Options)) (*redshift.DescribeClustersOutput, error) { + if m.Unauth { + return nil, trace.AccessDenied("unauthorized") } - return m.GetClusterCredentialsOutput, nil -} - -func (m *RedshiftMock) GetClusterCredentialsWithIAMWithContext(aws.Context, *redshift.GetClusterCredentialsWithIAMInput, ...request.Option) (*redshift.GetClusterCredentialsWithIAMOutput, error) { - if m.GetClusterCredentialsWithIAMOutput == nil { - return nil, trace.AccessDenied("access denied") - } - return m.GetClusterCredentialsWithIAMOutput, nil -} -func (m *RedshiftMock) DescribeClustersWithContext(ctx aws.Context, input *redshift.DescribeClustersInput, options ...request.Option) (*redshift.DescribeClustersOutput, error) { - if aws.StringValue(input.ClusterIdentifier) == "" { + if aws.ToString(input.ClusterIdentifier) == "" { return &redshift.DescribeClustersOutput{ Clusters: m.Clusters, }, nil } for _, cluster := range m.Clusters { - if aws.StringValue(cluster.ClusterIdentifier) == aws.StringValue(input.ClusterIdentifier) { + if aws.ToString(cluster.ClusterIdentifier) == aws.ToString(input.ClusterIdentifier) { return &redshift.DescribeClustersOutput{ - Clusters: []*redshift.Cluster{cluster}, + Clusters: []redshifttypes.Cluster{cluster}, }, nil } } - return nil, trace.NotFound("cluster %v not found", aws.StringValue(input.ClusterIdentifier)) + return nil, trace.NotFound("cluster %v not found", aws.ToString(input.ClusterIdentifier)) } -func (m *RedshiftMock) DescribeClustersPagesWithContext(ctx aws.Context, input *redshift.DescribeClustersInput, fn func(*redshift.DescribeClustersOutput, bool) bool, options ...request.Option) error { - fn(&redshift.DescribeClustersOutput{ - Clusters: m.Clusters, - }, true) - return nil -} - -// RedshiftMockUnauth is a mock Redshift client that returns access denied to each call. -type RedshiftMockUnauth struct { - redshiftiface.RedshiftAPI +func (m *RedshiftClient) GetClusterCredentials(context.Context, *redshift.GetClusterCredentialsInput, ...func(*redshift.Options)) (*redshift.GetClusterCredentialsOutput, error) { + if m.Unauth || m.GetClusterCredentialsOutput == nil { + return nil, trace.AccessDenied("access denied") + } + return m.GetClusterCredentialsOutput, nil } -func (m *RedshiftMockUnauth) DescribeClustersWithContext(ctx aws.Context, input *redshift.DescribeClustersInput, options ...request.Option) (*redshift.DescribeClustersOutput, error) { - return nil, trace.AccessDenied("unauthorized") +func (m *RedshiftClient) GetClusterCredentialsWithIAM(context.Context, *redshift.GetClusterCredentialsWithIAMInput, ...func(*redshift.Options)) (*redshift.GetClusterCredentialsWithIAMOutput, error) { + if m.Unauth || m.GetClusterCredentialsWithIAMOutput == nil { + return nil, trace.AccessDenied("access denied") + } + return m.GetClusterCredentialsWithIAMOutput, nil } -// RedshiftGetClusterCredentialsOutput return a sample redshift.GetClusterCredentialsOutput. +// RedshiftGetClusterCredentialsOutput return a sample [redshift.GetClusterCredentialsOutput]. func RedshiftGetClusterCredentialsOutput(user, password string, clock clockwork.Clock) *redshift.GetClusterCredentialsOutput { if clock == nil { clock = clockwork.NewRealClock() @@ -99,7 +87,7 @@ func RedshiftGetClusterCredentialsOutput(user, password string, clock clockwork. } // RedshiftGetClusterCredentialsWithIAMOutput return a sample -// redshift.GetClusterCredentialsWithIAMeOutput. +// [redshift.GetClusterCredentialsWithIAMOutput]. func RedshiftGetClusterCredentialsWithIAMOutput(user, password string, clock clockwork.Clock) *redshift.GetClusterCredentialsWithIAMOutput { if clock == nil { clock = clockwork.NewRealClock() @@ -111,20 +99,19 @@ func RedshiftGetClusterCredentialsWithIAMOutput(user, password string, clock clo } } -// RedshiftCluster returns a sample redshift.Cluster. -func RedshiftCluster(name, region string, labels map[string]string, opts ...func(*redshift.Cluster)) *redshift.Cluster { - cluster := &redshift.Cluster{ +func RedshiftCluster(name, region string, labels map[string]string, opts ...func(*redshifttypes.Cluster)) redshifttypes.Cluster { + cluster := redshifttypes.Cluster{ ClusterIdentifier: aws.String(name), ClusterNamespaceArn: aws.String(fmt.Sprintf("arn:aws:redshift:%s:123456789012:namespace:%s", region, name)), ClusterStatus: aws.String("available"), - Endpoint: &redshift.Endpoint{ + Endpoint: &redshifttypes.Endpoint{ Address: aws.String(fmt.Sprintf("%v.aabbccdd.%v.redshift.amazonaws.com", name, region)), - Port: aws.Int64(5439), + Port: aws.Int32(5439), }, - Tags: libcloudaws.LabelsToTags[redshift.Tag](labels), + Tags: awstesthelpers.LabelsToRedshiftTags(labels), } for _, opt := range opts { - opt(cluster) + opt(&cluster) } return cluster } diff --git a/lib/cloud/mocks/aws_s3.go b/lib/cloud/mocks/aws_s3.go index f68b76d294963..8c49ea4471f07 100644 --- a/lib/cloud/mocks/aws_s3.go +++ b/lib/cloud/mocks/aws_s3.go @@ -35,6 +35,7 @@ type S3Mock struct { BucketPolicyStatus map[string]*s3.PolicyStatus BucketACL map[string][]*s3.Grant BucketTags map[string][]*s3.Tag + BucketLocations map[string]string } func (m *S3Mock) ListBucketsWithContext(_ aws.Context, _ *s3.ListBucketsInput, _ ...request.Option) (*s3.ListBucketsOutput, error) { @@ -94,3 +95,16 @@ func (m *S3Mock) GetBucketTaggingWithContext(_ aws.Context, input *s3.GetBucketT TagSet: tags, }, nil } + +func (m *S3Mock) GetBucketLocationWithContext(_ aws.Context, input *s3.GetBucketLocationInput, _ ...request.Option) (*s3.GetBucketLocationOutput, error) { + if aws.StringValue(input.Bucket) == "" { + return nil, trace.BadParameter("incorrect bucket name") + } + location, ok := m.BucketLocations[aws.StringValue(input.Bucket)] + if !ok { + return nil, trace.NotFound("bucket %v not found", aws.StringValue(input.Bucket)) + } + return &s3.GetBucketLocationOutput{ + LocationConstraint: aws.String(location), + }, nil +} diff --git a/lib/cloud/mocks/aws_sts.go b/lib/cloud/mocks/aws_sts.go new file mode 100644 index 0000000000000..713de480ebf86 --- /dev/null +++ b/lib/cloud/mocks/aws_sts.go @@ -0,0 +1,111 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package mocks + +import ( + "context" + "slices" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/sts" + ststypes "github.com/aws/aws-sdk-go-v2/service/sts/types" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/lib/cloud/awsconfig" +) + +// STSClient mocks the AWS STS API for AWS SDK v1 and v2. +// Callers can use it in tests for both the v1 and v2 interfaces. +// This is useful when some services still use SDK v1 while others use v2 SDK, +// so that all assumed roles can be recorded in one place. +// For example: +// +// clt := &STSClient{} +// a.stsClientV1 = &clt.STSClientV1 +// b.stsClientV2 = clt +// ... +// gotRoles := clt.GetAssumedRoleARNs() // returns roles that were assumed with either v1 or v2 client. +type STSClient struct { + STSClientV1 + + // credentialProvider is only set when a chain of assumed roles is used. + credentialProvider aws.CredentialsProvider + // recordFn records the role and external ID when a role is assumed. + // It is only set when a chain of assumed roles is used, so that all assumed + // roles can be centralized and observed in tests. + recordFn func(roleARN, externalID string) +} + +func (m *STSClient) AssumeRole(ctx context.Context, in *sts.AssumeRoleInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleOutput, error) { + // Retrieve credentials if we have a credential provider, so that all + // assume-role providers in a role chain are triggered to call AssumeRole. + if m.credentialProvider != nil { + _, err := m.credentialProvider.Retrieve(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + } + m.record(aws.ToString(in.RoleArn), aws.ToString(in.ExternalId)) + + expiry := time.Now().Add(60 * time.Minute) + return &sts.AssumeRoleOutput{ + Credentials: &ststypes.Credentials{ + AccessKeyId: in.RoleArn, + SecretAccessKey: aws.String("secret"), + SessionToken: aws.String("token"), + Expiration: &expiry, + }, + }, nil +} + +// record is a helper function that records the role ARN and external ID for an +// assumed role. +// It delegates to the configured recordFn, if it has one, so that all assumed +// role recordings are centralized for observation in tests. +func (m *STSClient) record(roleARN, externalID string) { + if m.recordFn != nil { + m.recordFn(roleARN, externalID) + return + } + m.STSClientV1.mu.Lock() + defer m.STSClientV1.mu.Unlock() + if !slices.Contains(m.assumedRoleARNs, roleARN) { + m.assumedRoleARNs = append(m.assumedRoleARNs, roleARN) + m.assumedRoleExternalIDs = append(m.assumedRoleExternalIDs, externalID) + } +} + +func newAssumeRoleClientProviderFunc(base *STSClient) awsconfig.AssumeRoleClientProviderFunc { + return func(cfg aws.Config) stscreds.AssumeRoleAPIClient { + if cfg.Credentials != nil { + if _, ok := cfg.Credentials.(*stscreds.AssumeRoleProvider); ok { + // Create a new fake client linked to the old one. + // Only do this for AssumeRoleProvider to avoid attempting + // to load the real credential chain. + return &STSClient{ + credentialProvider: cfg.Credentials, + recordFn: base.record, + } + } + } + return base + } +} diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 929bb757a85ca..5d42d9f4aa8a4 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -2753,6 +2753,14 @@ func Configure(clf *CommandLineFlags, cfg *servicecfg.Config, legacyAppFlags boo cfg.Proxy.QUICProxyPeering = true } + if rawPeriod := os.Getenv("TELEPORT_UNSTABLE_AGENT_ROLLOUT_SYNC_PERIOD"); rawPeriod != "" { + period, err := time.ParseDuration(rawPeriod) + if err != nil { + return trace.Wrap(err, "invalid agent rollout period %q", rawPeriod) + } + cfg.Auth.AgentRolloutControllerSyncPeriod = period + } + return nil } diff --git a/lib/devicetrust/native/device_windows.go b/lib/devicetrust/native/device_windows.go index 575e238af5623..3cde66a79de00 100644 --- a/lib/devicetrust/native/device_windows.go +++ b/lib/devicetrust/native/device_windows.go @@ -30,13 +30,13 @@ import ( "time" "github.com/google/go-attestation/attest" - "github.com/gravitational/teleport" "github.com/gravitational/trace" "github.com/yusufpapurcu/wmi" "golang.org/x/sync/errgroup" "golang.org/x/sys/windows" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/gravitational/teleport" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" "github.com/gravitational/teleport/lib/windowsexec" ) diff --git a/lib/events/api.go b/lib/events/api.go index 25f87abf6ac7a..b1dda9a2b6db7 100644 --- a/lib/events/api.go +++ b/lib/events/api.go @@ -205,6 +205,8 @@ const ( AccessRequestUpdateEvent = "access_request.update" // AccessRequestReviewEvent is emitted when a review is applied to a request. AccessRequestReviewEvent = "access_request.review" + // AccessRequestExpirEvent is emitted when an access request expires. + AccessRequestExpireEvent = "access_request.expire" // AccessRequestDeleteEvent is emitted when a new access request is deleted. AccessRequestDeleteEvent = "access_request.delete" // AccessRequestResourceSearch is emitted when a user searches for diff --git a/lib/events/athena/athena.go b/lib/events/athena/athena.go index 7541523ea01bd..601cfba96abc6 100644 --- a/lib/events/athena/athena.go +++ b/lib/events/athena/athena.go @@ -538,7 +538,11 @@ func (l *Log) SearchSessionEvents(ctx context.Context, req events.SearchSessionE } func (l *Log) Close() error { - return trace.Wrap(l.consumerCloser.Close()) + // consumerCloser is nil when consumer is disabled. + if l.consumerCloser != nil { + return trace.Wrap(l.consumerCloser.Close()) + } + return nil } func (l *Log) IsConsumerDisabled() bool { diff --git a/lib/events/auditlog.go b/lib/events/auditlog.go index 3570171f40996..274c3c65c56a6 100644 --- a/lib/events/auditlog.go +++ b/lib/events/auditlog.go @@ -509,9 +509,23 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID e := make(chan error, 1) c := make(chan apievents.AuditEvent) + sessionStartCh := make(chan apievents.AuditEvent, 1) + if startCb, err := sessionStartCallbackFromContext(ctx); err == nil { + go func() { + evt, ok := <-sessionStartCh + if !ok { + startCb(nil, trace.NotFound("session start event not found")) + return + } + + startCb(evt, nil) + }() + } + rawSession, err := os.CreateTemp(l.playbackDir, string(sessionID)+".stream.tar.*") if err != nil { e <- trace.Wrap(trace.ConvertSystemError(err), "creating temporary stream file") + close(sessionStartCh) return c, e } // The file is still perfectly usable after unlinking it, and the space it's @@ -528,6 +542,7 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID if err := os.Remove(rawSession.Name()); err != nil { _ = rawSession.Close() e <- trace.Wrap(trace.ConvertSystemError(err), "removing temporary stream file") + close(sessionStartCh) return c, e } @@ -538,6 +553,7 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID err = trace.NotFound("a recording for session %v was not found", sessionID) } e <- trace.Wrap(err) + close(sessionStartCh) return c, e } l.log.DebugContext(ctx, "Downloaded session to a temporary file for streaming.", @@ -547,6 +563,8 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID go func() { defer rawSession.Close() + defer close(sessionStartCh) + // this shouldn't be necessary as the position should be already 0 (Download // takes an io.WriterAt), but it's better to be safe than sorry if _, err := rawSession.Seek(0, io.SeekStart); err != nil { @@ -557,6 +575,7 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID protoReader := NewProtoReader(rawSession) defer protoReader.Close() + firstEvent := true for { if ctx.Err() != nil { e <- trace.Wrap(ctx.Err()) @@ -573,6 +592,11 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID return } + if firstEvent { + sessionStartCh <- event + firstEvent = false + } + if event.GetIndex() >= startIndex { select { case c <- event: @@ -667,3 +691,39 @@ func (l *AuditLog) periodicSpaceMonitor() { } } } + +// streamSessionEventsContextKey represent context keys used by +// StreamSessionEvents function. +type streamSessionEventsContextKey string + +const ( + // sessionStartCallbackContextKey is the context key used to store the + // session start callback function. + sessionStartCallbackContextKey streamSessionEventsContextKey = "session-start" +) + +// SessionStartCallback is the function used when streaming reaches the start +// event. If any error, such as session not found, the event will be nil, and +// the error will be set. +type SessionStartCallback func(startEvent apievents.AuditEvent, err error) + +// ContextWithSessionStartCallback returns a context.Context containing a +// session start event callback. +func ContextWithSessionStartCallback(ctx context.Context, cb SessionStartCallback) context.Context { + return context.WithValue(ctx, sessionStartCallbackContextKey, cb) +} + +// sessionStartCallbackFromContext returns the session start callback from +// context.Context. +func sessionStartCallbackFromContext(ctx context.Context) (SessionStartCallback, error) { + if ctx == nil { + return nil, trace.BadParameter("context is nil") + } + + cb, ok := ctx.Value(sessionStartCallbackContextKey).(SessionStartCallback) + if !ok { + return nil, trace.BadParameter("session start callback function was not found in the context") + } + + return cb, nil +} diff --git a/lib/events/auditlog_test.go b/lib/events/auditlog_test.go index b76d27a0ee36a..416373e3e6951 100644 --- a/lib/events/auditlog_test.go +++ b/lib/events/auditlog_test.go @@ -154,6 +154,137 @@ func TestConcurrentStreaming(t *testing.T) { } } +func TestStreamSessionEvents(t *testing.T) { + uploader := eventstest.NewMemoryUploader() + alog, err := events.NewAuditLog(events.AuditLogConfig{ + DataDir: t.TempDir(), + Clock: clockwork.NewFakeClock(), + ServerID: "remote", + UploadHandler: uploader, + }) + require.NoError(t, err) + t.Cleanup(func() { alog.Close() }) + + ctx := context.Background() + sid := session.NewID() + sessionEvents := []apievents.AuditEvent{ + &apievents.DatabaseSessionStart{ + Metadata: apievents.Metadata{ + Type: events.DatabaseSessionStartEvent, + Code: events.DatabaseSessionStartCode, + Index: 0, + }, + SessionMetadata: apievents.SessionMetadata{ + SessionID: sid.String(), + }, + }, + &apievents.DatabaseSessionEnd{ + Metadata: apievents.Metadata{ + Type: events.DatabaseSessionEndEvent, + Code: events.DatabaseSessionEndCode, + Index: 1, + }, + SessionMetadata: apievents.SessionMetadata{ + SessionID: sid.String(), + }, + }, + } + + streamer, err := events.NewProtoStreamer(events.ProtoStreamerConfig{ + Uploader: uploader, + }) + require.NoError(t, err) + stream, err := streamer.CreateAuditStream(ctx, sid) + require.NoError(t, err) + for _, event := range sessionEvents { + require.NoError(t, stream.RecordEvent(ctx, eventstest.PrepareEvent(event))) + } + require.NoError(t, stream.Complete(ctx)) + + type callbackResult struct { + event apievents.AuditEvent + err error + } + + t.Run("Success", func(t *testing.T) { + for name, withCallback := range map[string]bool{ + "WithCallback": true, + "WithoutCallback": false, + } { + t.Run(name, func(t *testing.T) { + streamCtx, cancel := context.WithCancel(ctx) + defer cancel() + + callbackCh := make(chan callbackResult, 1) + if withCallback { + streamCtx = events.ContextWithSessionStartCallback(streamCtx, func(ae apievents.AuditEvent, err error) { + callbackCh <- callbackResult{ae, err} + }) + } + + ch, _ := alog.StreamSessionEvents(streamCtx, sid, 0) + for _, event := range sessionEvents { + select { + case receivedEvent := <-ch: + require.NotNil(t, receivedEvent) + require.Equal(t, event.GetCode(), receivedEvent.GetCode()) + require.Equal(t, event.GetType(), receivedEvent.GetType()) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to receive session event %q but got nothing", event.GetType()) + } + } + + if withCallback { + select { + case res := <-callbackCh: + require.NoError(t, res.err) + require.Equal(t, sessionEvents[0].GetCode(), res.event.GetCode()) + require.Equal(t, sessionEvents[0].GetType(), res.event.GetType()) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to receive callback result but got nothing") + } + } + }) + } + }) + + t.Run("Error", func(t *testing.T) { + for name, withCallback := range map[string]bool{ + "WithCallback": true, + "WithoutCallback": false, + } { + t.Run(name, func(t *testing.T) { + streamCtx, cancel := context.WithCancel(ctx) + defer cancel() + + callbackCh := make(chan callbackResult, 1) + if withCallback { + streamCtx = events.ContextWithSessionStartCallback(streamCtx, func(ae apievents.AuditEvent, err error) { + callbackCh <- callbackResult{ae, err} + }) + } + + _, errCh := alog.StreamSessionEvents(streamCtx, session.ID("random"), 0) + select { + case err := <-errCh: + require.Error(t, err) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to get error while stream but got nothing") + } + + if withCallback { + select { + case res := <-callbackCh: + require.Error(t, res.err) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to receive callback result but got nothing") + } + } + }) + } + }) +} + func TestExternalLog(t *testing.T) { m := &eventstest.MockAuditLog{ Emitter: &eventstest.MockRecorderEmitter{}, diff --git a/lib/events/codes.go b/lib/events/codes.go index db007a8db3154..de7a7744b24bc 100644 --- a/lib/events/codes.go +++ b/lib/events/codes.go @@ -363,6 +363,8 @@ const ( AccessRequestDeleteCode = "T5003I" // AccessRequestResourceSearchCode is the access request resource search code. AccessRequestResourceSearchCode = "T5004I" + // AccessRequestExpireCode is the access request expires code. + AccessRequestExpireCode = "T5005I" // ResetPasswordTokenCreateCode is the token create event code. ResetPasswordTokenCreateCode = "T6000I" diff --git a/lib/events/dynamic.go b/lib/events/dynamic.go index 782d742e059eb..de975792b0db7 100644 --- a/lib/events/dynamic.go +++ b/lib/events/dynamic.go @@ -282,6 +282,8 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) { e = &events.SessionConnect{} case AccessRequestDeleteEvent: e = &events.AccessRequestDelete{} + case AccessRequestExpireEvent: + e = &events.AccessRequestExpire{} case CertificateCreateEvent: e = &events.CertificateCreate{} case RenewableCertificateGenerationMismatchEvent: diff --git a/lib/events/dynamoevents/dynamoevents.go b/lib/events/dynamoevents/dynamoevents.go index 3f3c46ac874b6..5c2036336b278 100644 --- a/lib/events/dynamoevents/dynamoevents.go +++ b/lib/events/dynamoevents/dynamoevents.go @@ -42,6 +42,8 @@ import ( autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/applicationautoscaling/types" "github.com/aws/aws-sdk-go-v2/service/dynamodb" dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + legacydynamo "github.com/aws/aws-sdk-go/service/dynamodb" + "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" "github.com/aws/smithy-go" "github.com/aws/smithy-go/tracing/smithyoteltracing" "github.com/google/uuid" @@ -693,6 +695,24 @@ type checkpointKey struct { EventKey string `json:"event_key,omitempty"` } +// legacyCheckpointKey is the old checkpoint key returned by older auth versions. Used to decode +// checkpoints originating from old auths. Commonly we don't bother supporting pagination/cursors +// across teleport versions since the benefit of doing so is usually minimal, but this value is used +// as on-disk state by long running event export operations, and so must be supported. +// +// DELETE IN: 19.0.0 +type legacyCheckpointKey struct { + // The date that the Dynamo iterator corresponds to. + Date string `json:"date,omitempty"` + + // A DynamoDB query iterator. Allows us to resume a partial query. + Iterator map[string]*legacydynamo.AttributeValue `json:"iterator,omitempty"` + + // EventKey is a derived identifier for an event used for resuming + // sub-page breaks due to size constraints. + EventKey string `json:"event_key,omitempty"` +} + // SearchEvents is a flexible way to find events. // // Event types to filter can be specified and pagination is handled by an iterator key that allows @@ -940,11 +960,49 @@ func getCheckpointFromStartKey(startKey string) (checkpointKey, error) { } // If a checkpoint key is provided, unmarshal it so we can work with its parts. if err := json.Unmarshal([]byte(startKey), &checkpoint); err != nil { + // attempt to decode as legacy format. + if checkpoint, err = getCheckpointFromLegacyStartKey(startKey); err == nil { + return checkpoint, nil + } return checkpointKey{}, trace.Wrap(err) } return checkpoint, nil } +// getCheckpointFromLegacyStartKey is a helper function that decodes a legacy checkpoint key +// into the new format. The old format used raw dynamo attribute values for the iterator, where +// the new format uses a json-serialized map with bare values. +// +// DELETE IN: 19.0.0 +func getCheckpointFromLegacyStartKey(startKey string) (checkpointKey, error) { + var checkpoint legacyCheckpointKey + if startKey == "" { + return checkpointKey{}, nil + } + // If a checkpoint key is provided, unmarshal it so we can work with its parts. + if err := json.Unmarshal([]byte(startKey), &checkpoint); err != nil { + return checkpointKey{}, trace.Wrap(err) + } + + // decode the dynamo attrs into the go map repr common to the old and new formats. + m := make(map[string]any) + if err := dynamodbattribute.UnmarshalMap(checkpoint.Iterator, &m); err != nil { + return checkpointKey{}, trace.Wrap(err) + } + + // encode the map into json, making it equivalent to the new format. + iterator, err := json.Marshal(m) + if err != nil { + return checkpointKey{}, trace.Wrap(err) + } + + return checkpointKey{ + Date: checkpoint.Date, + Iterator: string(iterator), + EventKey: checkpoint.EventKey, + }, nil +} + func getExprFilter(filter searchEventsFilter) *string { var filterConds []string if len(filter.eventTypes) > 0 { diff --git a/lib/events/dynamoevents/dynamoevents_test.go b/lib/events/dynamoevents/dynamoevents_test.go index 0e868ac837a0c..28eb81c1f4653 100644 --- a/lib/events/dynamoevents/dynamoevents_test.go +++ b/lib/events/dynamoevents/dynamoevents_test.go @@ -33,6 +33,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/google/go-cmp/cmp" "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -666,3 +667,18 @@ func TestEndpoints(t *testing.T) { }) } } + +func TestStartKeyBackCompat(t *testing.T) { + const ( + oldStartKey = `{"date":"2023-04-27","iterator":{"CreatedAt":{"B":null,"BOOL":null,"BS":null,"L":null,"M":null,"N":"1682583778","NS":null,"NULL":null,"S":null,"SS":null},"CreatedAtDate":{"B":null,"BOOL":null,"BS":null,"L":null,"M":null,"N":null,"NS":null,"NULL":null,"S":"2023-04-27","SS":null},"EventIndex":{"B":null,"BOOL":null,"BS":null,"L":null,"M":null,"N":"0","NS":null,"NULL":null,"S":null,"SS":null},"SessionID":{"B":null,"BOOL":null,"BS":null,"L":null,"M":null,"N":null,"NS":null,"NULL":null,"S":"4bc51fd7-4f0c-47ee-b9a5-da621fbdbabb","SS":null}}}` + newStartKey = `{"date":"2023-04-27","iterator":"{\"CreatedAt\":1682583778,\"CreatedAtDate\":\"2023-04-27\",\"EventIndex\":0,\"SessionID\":\"4bc51fd7-4f0c-47ee-b9a5-da621fbdbabb\"}"}` + ) + + oldCP, err := getCheckpointFromStartKey(oldStartKey) + require.NoError(t, err) + + newCP, err := getCheckpointFromStartKey(newStartKey) + require.NoError(t, err) + + require.Empty(t, cmp.Diff(oldCP, newCP)) +} diff --git a/lib/events/events_test.go b/lib/events/events_test.go index 811db8338831e..98ac93c037575 100644 --- a/lib/events/events_test.go +++ b/lib/events/events_test.go @@ -248,6 +248,7 @@ var eventsMap = map[string]apievents.AuditEvent{ WorkloadIdentityCreateEvent: &apievents.WorkloadIdentityCreate{}, WorkloadIdentityUpdateEvent: &apievents.WorkloadIdentityUpdate{}, WorkloadIdentityDeleteEvent: &apievents.WorkloadIdentityDelete{}, + AccessRequestExpireEvent: &apievents.AccessRequestExpire{}, } // TestJSON tests JSON marshal events diff --git a/lib/events/filesessions/fileasync.go b/lib/events/filesessions/fileasync.go index 27b14f4408fd7..62d96fb0a593a 100644 --- a/lib/events/filesessions/fileasync.go +++ b/lib/events/filesessions/fileasync.go @@ -291,7 +291,7 @@ func (u *Uploader) Scan(ctx context.Context) (*ScanStats, error) { stats.Started++ } if stats.Scanned > 0 { - u.log.DebugContext(ctx, "Session recording scan completed ", "scanned", stats.Scanned, "started", stats.Started, "corupted", stats.Corrupted, "upload_dir", u.cfg.ScanDir) + u.log.DebugContext(ctx, "Session recording scan completed ", "scanned", stats.Scanned, "started", stats.Started, "corrupted", stats.Corrupted, "upload_dir", u.cfg.ScanDir) } return &stats, nil } diff --git a/lib/events/s3sessions/s3handler.go b/lib/events/s3sessions/s3handler.go index f4492d27337a9..6b9da90ca8db6 100644 --- a/lib/events/s3sessions/s3handler.go +++ b/lib/events/s3sessions/s3handler.go @@ -42,6 +42,7 @@ import ( "go.opentelemetry.io/otel" "github.com/gravitational/teleport" + apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" @@ -423,7 +424,11 @@ func (h *Handler) fromPath(p string) session.ID { // ensureBucket makes sure bucket exists, and if it does not, creates it func (h *Handler) ensureBucket(ctx context.Context) error { - _, err := h.client.HeadBucket(ctx, &s3.HeadBucketInput{ + // Use a short timeout for the HeadBucket call in case it takes too long, in + // #50747 this call would hang. + shortCtx, cancel := context.WithTimeout(ctx, apidefaults.DefaultIOTimeout) + defer cancel() + _, err := h.client.HeadBucket(shortCtx, &s3.HeadBucketInput{ Bucket: aws.String(h.Bucket), }) err = awsutils.ConvertS3Error(err) @@ -434,7 +439,7 @@ func (h *Handler) ensureBucket(ctx context.Context) error { case trace.IsBadParameter(err): return trace.Wrap(err) case !trace.IsNotFound(err): - h.logger.ErrorContext(ctx, "Failed to ensure that S3 bucket exists. S3 session uploads may fail. If you've set up the bucket already and gave Teleport write-only access, feel free to ignore this error.", "bucket", h.Bucket, "error", err) + h.logger.ErrorContext(ctx, "Failed to ensure that S3 bucket exists. This is expected if External Audit Storage is enabled or if Teleport has write-only access to the bucket, otherwise S3 session uploads may fail.", "bucket", h.Bucket, "error", err) return nil } diff --git a/lib/events/stream.go b/lib/events/stream.go index 0f00327d16116..14817cc152830 100644 --- a/lib/events/stream.go +++ b/lib/events/stream.go @@ -574,7 +574,7 @@ func (w *sliceWriter) receiveAndUpload() error { slog.DebugContext(w.proto.completeCtx, "Inactivity timer ticked and exceeded threshold but have no data. Nothing to do.", "tick", now, "inactivity_period", inactivityPeriod) } } else { - slog.DebugContext(w.proto.completeCtx, "Inactivity timer ticked and did not exceeded threshold. Resetting ticker.", "tick", now, "inactiity_period", inactivityPeriod, "next_tick", w.proto.cfg.InactivityFlushPeriod-inactivityPeriod) + slog.DebugContext(w.proto.completeCtx, "Inactivity timer ticked and did not exceeded threshold. Resetting ticker.", "tick", now, "inactivity_period", inactivityPeriod, "next_tick", w.proto.cfg.InactivityFlushPeriod-inactivityPeriod) flushCh = clock.After(w.proto.cfg.InactivityFlushPeriod - inactivityPeriod) } case event := <-w.proto.eventsCh: diff --git a/lib/httplib/csrf/csrf.go b/lib/httplib/csrf/csrf.go index 493ce98903200..87bca31f089c7 100644 --- a/lib/httplib/csrf/csrf.go +++ b/lib/httplib/csrf/csrf.go @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +// Package csrf is used to protect against login CSRF in Teleport's SSO flows. package csrf import ( @@ -28,35 +29,24 @@ import ( "github.com/gravitational/teleport/lib/utils" ) -const ( - // CookieName is the name of the CSRF cookie. It's prefixed with "__Host-" as - // an additional defense in depth measure. It makes sure it is sent from a - // secure page (HTTPS), won't be sent to subdomains, and the path attribute - // is set to /. - CookieName = "__Host-grv_csrf" - // HeaderName is the default HTTP request header to inspect. - HeaderName = "X-CSRF-Token" - // FormFieldName is the default form field to inspect. - FormFieldName = "csrf_token" - // tokenLenBytes is CSRF token length in bytes. - tokenLenBytes = 32 - // defaultMaxAge is the default MaxAge for cookies. - defaultMaxAge = 0 -) +// CookieName is the name of the CSRF cookie used to protect against login +// CSRF in Teleport's SSO flows. +// +// It's prefixed with "__Host-" as an additional defense in depth measure. +// This makes sure the cookie is sent from a secure page (HTTPS), +// won't be sent to subdomains, and the path attribute is set to /. +const CookieName = "__Host-grv_csrf" -// GenerateToken generates a random CSRF token. -func GenerateToken() (string, error) { - return utils.CryptoRandomHex(tokenLenBytes) -} +// tokenLenBytes is the length of a raw CSRF token prior to encoding +const tokenLenBytes = 32 -// AddCSRFProtection adds CSRF token into the user session via secure cookie, -// it implements "double submit cookie" approach to check against CSRF attacks -// https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet#Double_Submit_Cookie +// AddCSRFProtection adds CSRF token into the user session via a secure cookie. +// This CSRF token is used to protect against login CSRF in Teleport's SSO flows. func AddCSRFProtection(w http.ResponseWriter, r *http.Request) (string, error) { token, err := ExtractTokenFromCookie(r) // if there was an error retrieving the token, the token doesn't exist if err != nil || len(token) == 0 { - token, err = GenerateToken() + token, err = utils.CryptoRandomHex(tokenLenBytes) if err != nil { return "", trace.Wrap(err) } @@ -65,36 +55,6 @@ func AddCSRFProtection(w http.ResponseWriter, r *http.Request) (string, error) { return token, nil } -// VerifyHTTPHeader checks if HTTP header value matches the cookie. -func VerifyHTTPHeader(r *http.Request) error { - token := r.Header.Get(HeaderName) - if len(token) == 0 { - return trace.BadParameter("cannot retrieve CSRF token from HTTP header %q", HeaderName) - } - - err := VerifyToken(token, r) - if err != nil { - return trace.Wrap(err) - } - - return nil -} - -// VerifyFormField checks if HTTP form value matches the cookie. -func VerifyFormField(r *http.Request) error { - token := r.FormValue(FormFieldName) - if len(token) == 0 { - return trace.BadParameter("cannot retrieve CSRF token from form field %q", FormFieldName) - } - - err := VerifyToken(token, r) - if err != nil { - return trace.Wrap(err) - } - - return nil -} - // VerifyToken validates given token based on HTTP request cookie func VerifyToken(token string, r *http.Request) error { realToken, err := ExtractTokenFromCookie(r) @@ -112,7 +72,7 @@ func VerifyToken(token string, r *http.Request) error { return trace.Wrap(err, "unable to decode cookie CSRF token") } - if !compareTokens(decodedTokenA, decodedTokenB) { + if subtle.ConstantTimeCompare(decodedTokenA, decodedTokenB) != 1 { return trace.BadParameter("CSRF tokens do not match") } @@ -129,7 +89,7 @@ func ExtractTokenFromCookie(r *http.Request) (string, error) { return cookie.Value, nil } -// decode decodes a cookie using base64. +// decode decodes a hex-encoded CSRF token. func decode(token string) ([]byte, error) { decoded, err := hex.DecodeString(token) if err != nil { @@ -143,17 +103,11 @@ func decode(token string) ([]byte, error) { return decoded, nil } -// compareTokens securely (constant-time) compares CSRF tokens -func compareTokens(a, b []byte) bool { - return subtle.ConstantTimeCompare(a, b) == 1 -} - // save stores encoded CSRF token in the session cookie. func save(encodedToken string, w http.ResponseWriter) string { cookie := &http.Cookie{ - Name: CookieName, - Value: encodedToken, - MaxAge: defaultMaxAge, + Name: CookieName, + Value: encodedToken, // Set SameSite to none so browsers preserve gravitational CSRF cookie // while processing SSO providers redirects. SameSite: http.SameSiteNoneMode, diff --git a/lib/integrations/awsoidc/credprovider/credentialscache.go b/lib/integrations/awsoidc/credprovider/credentialscache.go index 2711d0126b2ce..ad1db35e94bea 100644 --- a/lib/integrations/awsoidc/credprovider/credentialscache.go +++ b/lib/integrations/awsoidc/credprovider/credentialscache.go @@ -62,12 +62,21 @@ type CredentialsCache struct { roleARN arn.ARN integration string - // generateOIDCTokenFn is dynamically set after auth is initialized. - generateOIDCTokenFn GenerateOIDCTokenFn - - // initialized communicates (via closing channel) that generateOIDCTokenFn is set. - initialized chan struct{} - closeInitialized func() + // generateOIDCTokenFn can be dynamically set after creating the credential + // cache, this is a workaround for a dependency cycle where audit storage + // depends on the credential cache, the auth server depends on audit + // storage, and the credential cache depends on the auth server for a + // GenerateOIDCTokenFn. + generateOIDCTokenFn GenerateOIDCTokenFn + generateOIDCTokenFnMu sync.Mutex + // gotGenerateOIDCTokenFn communicates (via closing channel) that + // generateOIDCTokenFn is set. + gotGenerateOIDCTokenFn chan struct{} + closeGotGenerateOIDCTokenFn func() + // allowRetrieveBeforeInit allows the Retrieve method to return an error if + // [gotGenerateOIDCTokenFn] has not been closed yet, instead of waiting for it to be + // closed. + allowRetrieveBeforeInit bool // gotFirstCredsOrErr communicates (via closing channel) that the first // credsOrErr has been set. @@ -92,6 +101,15 @@ type CredentialsCacheOptions struct { // with AWS STSClient stscreds.AssumeRoleWithWebIdentityAPIClient + // GenerateOIDCTokenFn is a function that should return a valid, signed JWT for + // authenticating to AWS via OIDC. + GenerateOIDCTokenFn GenerateOIDCTokenFn + + // AllowRetrieveBeforeInit allows the Retrieve method to return with an + // error before the cache has been initialized, instead of waiting for the + // first credentials to be generated. + AllowRetrieveBeforeInit bool + // Log is the logger to use. A default will be supplied if no logger is // explicitly set Log *slog.Logger @@ -124,36 +142,59 @@ func NewCredentialsCache(options CredentialsCacheOptions) (*CredentialsCache, er return nil, trace.Wrap(err, "creating credentials cache") } - initialized := make(chan struct{}) + gotGenerateOIDCTokenFn := make(chan struct{}) + closeGotGenerateOIDCTokenFn := sync.OnceFunc(func() { close(gotGenerateOIDCTokenFn) }) + if options.GenerateOIDCTokenFn != nil { + closeGotGenerateOIDCTokenFn() + } + gotFirstCredsOrErr := make(chan struct{}) + closeGotFirstCredsOrErr := sync.OnceFunc(func() { close(gotFirstCredsOrErr) }) return &CredentialsCache{ - roleARN: options.RoleARN, - integration: options.Integration, - log: options.Log.With("integration", options.Integration), - initialized: initialized, - closeInitialized: sync.OnceFunc(func() { close(initialized) }), - gotFirstCredsOrErr: gotFirstCredsOrErr, - closeGotFirstCredsOrErr: sync.OnceFunc(func() { close(gotFirstCredsOrErr) }), - credsOrErr: credsOrErr{err: errNotReady}, - clock: options.Clock, - stsClient: options.STSClient, + roleARN: options.RoleARN, + integration: options.Integration, + generateOIDCTokenFn: options.GenerateOIDCTokenFn, + gotGenerateOIDCTokenFn: gotGenerateOIDCTokenFn, + closeGotGenerateOIDCTokenFn: closeGotGenerateOIDCTokenFn, + allowRetrieveBeforeInit: options.AllowRetrieveBeforeInit, + log: options.Log.With("integration", options.Integration), + gotFirstCredsOrErr: gotFirstCredsOrErr, + closeGotFirstCredsOrErr: closeGotFirstCredsOrErr, + credsOrErr: credsOrErr{err: errNotReady}, + clock: options.Clock, + stsClient: options.STSClient, }, nil } +// SetGenerateOIDCTokenFn can be used to set a GenerateOIDCTokenFn after +// creating the credential cache, when dependencies require the credential cache +// to be created before a valid GenerateOIDCTokenFn can be created. func (cc *CredentialsCache) SetGenerateOIDCTokenFn(fn GenerateOIDCTokenFn) { + cc.generateOIDCTokenFnMu.Lock() + defer cc.generateOIDCTokenFnMu.Unlock() cc.generateOIDCTokenFn = fn - cc.closeInitialized() + close(cc.gotGenerateOIDCTokenFn) +} + +// getGenerateOIDCTokenFn must not be called before [cc.gotGenerateOIDCTokenFn] +// has been closed, or it will return nil. +func (cc *CredentialsCache) getGenerateOIDCTokenFn() GenerateOIDCTokenFn { + cc.generateOIDCTokenFnMu.Lock() + defer cc.generateOIDCTokenFnMu.Unlock() + return cc.generateOIDCTokenFn } // Retrieve implements [aws.CredentialsProvider] and returns the latest cached // credentials, or an error if no credentials have been generated yet or the // last generated credentials have expired. func (cc *CredentialsCache) Retrieve(ctx context.Context) (aws.Credentials, error) { - select { - case <-cc.gotFirstCredsOrErr: - case <-ctx.Done(): - return aws.Credentials{}, ctx.Err() + if !cc.allowRetrieveBeforeInit { + select { + case <-cc.gotFirstCredsOrErr: + case <-ctx.Done(): + return aws.Credentials{}, ctx.Err() + } } creds, err := cc.retrieve(ctx) return creds, trace.Wrap(err) @@ -169,9 +210,9 @@ func (cc *CredentialsCache) retrieve(ctx context.Context) (aws.Credentials, erro } func (cc *CredentialsCache) Run(ctx context.Context) { - // Wait for initialized signal before running loop. + // Wait for a generateOIDCTokenFn before running loop. select { - case <-cc.initialized: + case <-cc.gotGenerateOIDCTokenFn: case <-ctx.Done(): cc.log.DebugContext(ctx, "Context canceled before initialized.") return @@ -241,7 +282,7 @@ func (cc *CredentialsCache) refresh(ctx context.Context) (aws.Credentials, error defer cc.log.InfoContext(ctx, "Exiting AWS credentials refresh") cc.log.InfoContext(ctx, "Generating Token") - oidcToken, err := cc.generateOIDCTokenFn(ctx, cc.integration) + oidcToken, err := cc.getGenerateOIDCTokenFn()(ctx, cc.integration) if err != nil { cc.log.ErrorContext(ctx, "Token generation failed", errorValue(err)) return aws.Credentials{}, trace.Wrap(err) diff --git a/lib/integrations/awsoidc/credprovider/credentialscache_test.go b/lib/integrations/awsoidc/credprovider/credentialscache_test.go index 6384bed0b8db0..e0a70a3bb973d 100644 --- a/lib/integrations/awsoidc/credprovider/credentialscache_test.go +++ b/lib/integrations/awsoidc/credprovider/credentialscache_test.go @@ -20,7 +20,6 @@ import ( "context" "errors" "sync" - "sync/atomic" "testing" "time" @@ -29,19 +28,18 @@ import ( ststypes "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/google/uuid" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/utils" ) type fakeSTSClient struct { clock clockwork.Clock err error sync.Mutex - called int32 } func (f *fakeSTSClient) setError(err error) { @@ -57,7 +55,6 @@ func (f *fakeSTSClient) getError() error { } func (f *fakeSTSClient) AssumeRoleWithWebIdentity(ctx context.Context, params *sts.AssumeRoleWithWebIdentityInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleWithWebIdentityOutput, error) { - atomic.AddInt32(&f.called, 1) if err := f.getError(); err != nil { return nil, err } @@ -75,8 +72,6 @@ func (f *fakeSTSClient) AssumeRoleWithWebIdentity(ctx context.Context, params *s } func TestCredentialsCache(t *testing.T) { - logrus.SetLevel(logrus.DebugLevel) - ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -98,6 +93,9 @@ func TestCredentialsCache(t *testing.T) { STSClient: stsClient, Integration: "test", Clock: clock, + GenerateOIDCTokenFn: func(ctx context.Context, integration string) (string, error) { + return uuid.NewString(), nil + }, }) require.NoError(t, err) require.NotNil(t, cacheUnderTest) @@ -110,12 +108,6 @@ func TestCredentialsCache(t *testing.T) { clock.Advance(d) } - // Set the GenerateOIDCTokenFn to a dumb faked function. - cacheUnderTest.SetGenerateOIDCTokenFn( - func(ctx context.Context, integration string) (string, error) { - return uuid.NewString(), nil - }) - checkRetrieveCredentials := func(t require.TestingT, expectErr error) { _, err := cacheUnderTest.Retrieve(ctx) assert.ErrorIs(t, err, expectErr) @@ -227,3 +219,50 @@ func TestCredentialsCache(t *testing.T) { } }) } + +func TestCredentialsCacheRetrieveBeforeInit(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clock := clockwork.NewFakeClock() + stsClient := &fakeSTSClient{ + clock: clock, + } + cache, err := NewCredentialsCache(CredentialsCacheOptions{ + STSClient: stsClient, + Integration: "test", + Clock: clock, + AllowRetrieveBeforeInit: true, + }) + require.NoError(t, err) + + utils.RunTestBackgroundTask(ctx, t, &utils.TestBackgroundTask{ + Name: "cache.Run", + Task: func(ctx context.Context) error { + cache.Run(ctx) + return nil + }, + Terminate: func() error { + cancel() + return nil + }, + }) + + // cache.Retrieve should return immediately with errNotReady if + // SetGenerateOIDCTokenFn has not been called yet. + _, err = cache.Retrieve(ctx) + require.ErrorIs(t, err, errNotReady) + + // The GenerateOIDCTokenFn can be set after the cache has been initialized. + cache.SetGenerateOIDCTokenFn(func(ctx context.Context, integration string) (string, error) { + return uuid.NewString(), nil + }) + // WaitForFirstCredsOrErr should usually be called after + // SetGenerateOIDCTokenFn to make sure credentials are ready before they + // will be relied upon. + cache.WaitForFirstCredsOrErr(ctx) + // Now cache.Retrieve should not return an error. + creds, err := cache.Retrieve(ctx) + require.NoError(t, err) + require.NotEmpty(t, creds.SecretAccessKey) +} diff --git a/lib/integrations/awsoidc/credprovider/integration_config_provider.go b/lib/integrations/awsoidc/credprovider/integration_config_provider.go index 204ab121cc133..76ed003113588 100644 --- a/lib/integrations/awsoidc/credprovider/integration_config_provider.go +++ b/lib/integrations/awsoidc/credprovider/integration_config_provider.go @@ -34,32 +34,17 @@ import ( ) // Options represents additional options for configuring the AWS credentials provider. -type Options struct { - // WaitForFirstInit indicates whether to wait for the initial credential - // generation before returning from CreateAWSConfigForIntegration. - WaitForFirstInit bool -} +// There are currently no options but this type is still referenced from +// teleport.e. +type Options struct{} // Option is a function that modifies the Options struct for the AWS configuration. type Option func(*Options) -// WithWaitForFirstInit configures the provider to wait until the first set of -// credentials is generated before proceeding. This is useful in cases where -// immediate credential availability is necessary. -func WithWaitForFirstInit(wait bool) Option { - return func(o *Options) { - o.WaitForFirstInit = wait - } -} - // CreateAWSConfigForIntegration returns a new AWS credentials provider that // uses the AWS OIDC integration to generate temporary credentials. // The provider will periodically refresh the credentials before they expire. func CreateAWSConfigForIntegration(ctx context.Context, config Config, option ...Option) (*aws.Config, error) { - options := Options{} - for _, opt := range option { - opt(&options) - } if err := config.checkAndSetDefaults(); err != nil { return nil, trace.Wrap(err) } @@ -76,10 +61,6 @@ func CreateAWSConfigForIntegration(ctx context.Context, config Config, option .. } go credCache.Run(ctx) - if options.WaitForFirstInit { - credCache.WaitForFirstCredsOrErr(ctx) - } - awsCfg, err := newAWSConfig(ctx, config.Region, awsConfig.WithCredentialsProvider(credCache)) if err != nil { return nil, trace.Wrap(err) @@ -152,17 +133,17 @@ func newAWSCredCache(ctx context.Context, cfg Config, stsClient stscreds.AssumeR credCache, err := NewCredentialsCache( CredentialsCacheOptions{ - Log: cfg.Logger, - Clock: cfg.Clock, - STSClient: stsClient, - RoleARN: roleARN, - Integration: cfg.IntegrationName, + Log: cfg.Logger, + Clock: cfg.Clock, + STSClient: stsClient, + RoleARN: roleARN, + Integration: cfg.IntegrationName, + GenerateOIDCTokenFn: cfg.AWSOIDCTokenGenerator.GenerateAWSOIDCToken, }, ) if err != nil { return nil, trace.Wrap(err, "creating OIDC credentials cache") } - credCache.SetGenerateOIDCTokenFn(cfg.AWSOIDCTokenGenerator.GenerateAWSOIDCToken) return credCache, nil } diff --git a/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go b/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go index 03af684ce602b..7a9e42adb3d5f 100644 --- a/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go +++ b/lib/integrations/awsoidc/credprovider/integration_config_provider_test.go @@ -19,7 +19,6 @@ package credprovider import ( "context" "crypto" - "sync/atomic" "testing" "github.com/gravitational/trace" @@ -55,24 +54,6 @@ func TestCreateAWSConfigForIntegration(t *testing.T) { STSClient: stsClient, }) require.NoError(t, err) - require.Equal(t, int32(0), atomic.LoadInt32(&stsClient.called)) - - creds, err := config.Credentials.Retrieve(ctx) - require.NoError(t, err) - require.NotEmpty(t, creds.SecretAccessKey) - }) - - t.Run("should init creds before retrieve call", func(t *testing.T) { - stsClient := &fakeSTSClient{clock: clockwork.NewFakeClock()} - config, err := CreateAWSConfigForIntegration(ctx, Config{ - Region: awsRegion, - IntegrationName: integrationName, - IntegrationGetter: deps, - AWSOIDCTokenGenerator: deps, - STSClient: stsClient, - }, WithWaitForFirstInit(true)) - require.NoError(t, err) - require.Equal(t, int32(1), atomic.LoadInt32(&stsClient.called)) creds, err := config.Credentials.Retrieve(ctx) require.NoError(t, err) @@ -88,19 +69,10 @@ type depsMock struct { proxies []types.Server } -func (d *depsMock) GenerateOIDCTokenFn(ctx context.Context, integration string) (string, error) { - token, err := awsoidc.GenerateAWSOIDCToken(ctx, d, d, awsoidc.GenerateAWSOIDCTokenRequest{ - Integration: integrationName, - Username: testUser, - Subject: types.IntegrationAWSOIDCSubject, - }) - return token, trace.Wrap(err) -} - func (d *depsMock) GenerateAWSOIDCToken(ctx context.Context, integration string) (string, error) { token, err := awsoidc.GenerateAWSOIDCToken(ctx, d, d, awsoidc.GenerateAWSOIDCTokenRequest{ Integration: integration, - Username: "test-user", + Username: testUser, Subject: types.IntegrationAWSOIDCSubject, }) return token, trace.Wrap(err) diff --git a/lib/integrations/awsoidc/testdata/TestAccessGraphAWSIAMConfigOuput.golden b/lib/integrations/awsoidc/testdata/TestAccessGraphAWSIAMConfigOuput.golden index 488cea7437994..82e937184fe78 100644 --- a/lib/integrations/awsoidc/testdata/TestAccessGraphAWSIAMConfigOuput.golden +++ b/lib/integrations/awsoidc/testdata/TestAccessGraphAWSIAMConfigOuput.golden @@ -32,6 +32,8 @@ PutRolePolicy: { "s3:ListBucket", "s3:GetBucketLocation", "s3:GetBucketTagging", + "s3:GetBucketPolicyStatus", + "s3:GetBucketAcl", "iam:ListUsers", "iam:GetUser", "iam:ListRoles", diff --git a/lib/integrations/diagnostics/profile.go b/lib/integrations/diagnostics/profile.go index 5d185a271c8bd..f34466d749aac 100644 --- a/lib/integrations/diagnostics/profile.go +++ b/lib/integrations/diagnostics/profile.go @@ -17,6 +17,8 @@ package diagnostics import ( + "context" + "log/slog" "os" "path/filepath" "runtime" @@ -26,7 +28,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" ) // Profile captures various Go pprof profiles and writes @@ -34,6 +35,7 @@ import ( // with the same epoch time so that profiles can easily be associated // as being captured from the same call. func Profile(dir string) error { + ctx := context.Background() if err := os.MkdirAll(dir, 0o755); err != nil { return trace.Wrap(err, "creating profile directory %v", dir) } @@ -69,37 +71,37 @@ func Profile(dir string) error { } defer blockFile.Close() - logrus.Debugf("capturing trace profile to %s", traceFile.Name()) + slog.DebugContext(ctx, "capturing trace profile", "file", traceFile.Name()) if err := runtimetrace.Start(traceFile); err != nil { return trace.Wrap(err, "capturing trace profile") } - logrus.Debugf("capturing cpu profile to %s", cpuFile.Name()) + slog.DebugContext(ctx, "capturing cpu profile", "file", cpuFile.Name()) if err := pprof.StartCPUProfile(cpuFile); err != nil { return trace.Wrap(err, "capturing cpu profile") } defer func() { - logrus.Debugf("capturing goroutine profile to %s", cpuFile.Name()) + slog.DebugContext(ctx, "capturing goroutine profile", "file", cpuFile.Name()) if err := pprof.Lookup("goroutine").WriteTo(goroutineFile, 0); err != nil { - logrus.WithError(err).Warn("failed to capture goroutine profile") + slog.WarnContext(ctx, "failed to capture goroutine profile", "error", err) } - logrus.Debugf("capturing block profile to %s", cpuFile.Name()) + slog.DebugContext(ctx, "capturing block profile", "file", cpuFile.Name()) if err := pprof.Lookup("block").WriteTo(blockFile, 0); err != nil { - logrus.WithError(err).Warn("failed to capture block profile") + slog.WarnContext(ctx, "failed to capture block profile", "error", err) } runtime.GC() - logrus.Debugf("capturing heap profile to %s", cpuFile.Name()) + slog.DebugContext(ctx, "capturing heap profile", "file", cpuFile.Name()) if err := pprof.WriteHeapProfile(heapFile); err != nil { - logrus.WithError(err).Warn("failed to capture heap profile") + slog.WarnContext(ctx, "failed to capture heap profile", "error", err) } pprof.StopCPUProfile() diff --git a/lib/integrations/externalauditstorage/configurator.go b/lib/integrations/externalauditstorage/configurator.go index 96c16c9dde133..739ee9d7342a3 100644 --- a/lib/integrations/externalauditstorage/configurator.go +++ b/lib/integrations/externalauditstorage/configurator.go @@ -27,7 +27,6 @@ import ( "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials/stscreds" "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/aws/aws-sdk-go/aws/credentials" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -218,6 +217,10 @@ func newConfigurator(ctx context.Context, spec *externalauditstorage.ExternalAud RoleARN: awsRoleARN, STSClient: options.stsClient, Clock: options.clock, + // SetGenerateOIDCTokenFn will be called later, until then we must allow + // credentialsCache.Retrieve to return errors instead of blocking auth + // startup. + AllowRetrieveBeforeInit: true, }) if err != nil { return nil, trace.Wrap(err) @@ -263,13 +266,6 @@ func (p *Configurator) CredentialsProvider() aws.CredentialsProvider { return p.credentialsCache } -// CredentialsProviderSDKV1 returns a credentials.ProviderWithContext that can be used to -// authenticate with the customer AWS account via the configured AWS OIDC -// integration with aws-sdk-go. -func (p *Configurator) CredentialsProviderSDKV1() credentials.ProviderWithContext { - return &v1Adapter{cc: p.credentialsCache} -} - // WaitForFirstCredentials waits for the internal credentials cache to finish // fetching its first credentials (or getting an error attempting to do so). // This can be called after SetGenerateOIDCTokenFn to make sure any returned @@ -278,37 +274,3 @@ func (p *Configurator) CredentialsProviderSDKV1() credentials.ProviderWithContex func (p *Configurator) WaitForFirstCredentials(ctx context.Context) { p.credentialsCache.WaitForFirstCredsOrErr(ctx) } - -// v1Adapter wraps the credentialsCache to implement -// [credentials.ProviderWithContext] used by aws-sdk-go (v1). -type v1Adapter struct { - cc *credprovider.CredentialsCache -} - -var _ credentials.ProviderWithContext = (*v1Adapter)(nil) - -// RetrieveWithContext returns cached credentials. -func (a *v1Adapter) RetrieveWithContext(ctx context.Context) (credentials.Value, error) { - credsV2, err := a.cc.Retrieve(ctx) - if err != nil { - return credentials.Value{}, trace.Wrap(err) - } - - return credentials.Value{ - AccessKeyID: credsV2.AccessKeyID, - SecretAccessKey: credsV2.SecretAccessKey, - SessionToken: credsV2.SessionToken, - ProviderName: credsV2.Source, - }, nil -} - -// Retrieve returns cached credentials. -func (a *v1Adapter) Retrieve() (credentials.Value, error) { - return a.RetrieveWithContext(context.Background()) -} - -// IsExpired always returns true in order to opt out of AWS SDK credential -// caching. Retrieve(WithContext) already returns cached credentials. -func (a *v1Adapter) IsExpired() bool { - return true -} diff --git a/lib/integrations/externalauditstorage/configurator_test.go b/lib/integrations/externalauditstorage/configurator_test.go index ba86e5f8e0c27..631fae9337aed 100644 --- a/lib/integrations/externalauditstorage/configurator_test.go +++ b/lib/integrations/externalauditstorage/configurator_test.go @@ -30,7 +30,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/google/uuid" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -179,7 +178,6 @@ func TestConfiguratorIsUsed(t *testing.T) { } func TestCredentialsCache(t *testing.T) { - logrus.SetLevel(logrus.DebugLevel) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -233,17 +231,12 @@ func TestCredentialsCache(t *testing.T) { }) provider := c.CredentialsProvider() - providerV1 := c.CredentialsProviderSDKV1() checkRetrieveCredentials := func(t require.TestingT, expectErr error) { - _, err = providerV1.RetrieveWithContext(ctx) - assert.ErrorIs(t, err, expectErr) _, err := provider.Retrieve(ctx) assert.ErrorIs(t, err, expectErr) } checkRetrieveCredentialsWithExpiry := func(t require.TestingT, expectExpiry time.Time) { - _, err = providerV1.RetrieveWithContext(ctx) - assert.NoError(t, err) creds, err := provider.Retrieve(ctx) assert.NoError(t, err) if err == nil { diff --git a/lib/integrations/externalauditstorage/error_counter.go b/lib/integrations/externalauditstorage/error_counter.go index 1cc0f650c90f9..7525cd5631ffb 100644 --- a/lib/integrations/externalauditstorage/error_counter.go +++ b/lib/integrations/externalauditstorage/error_counter.go @@ -29,7 +29,6 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" auditlogpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/auditlog/v1" @@ -38,6 +37,7 @@ import ( apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/session" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const ( @@ -53,7 +53,7 @@ const ( syncInterval = 30 * time.Second ) -var log = logrus.WithField(teleport.ComponentKey, "ExternalAuditStorage") +var log = logutils.NewPackageLogger(teleport.ComponentKey, "ExternalAuditStorage") // ClusterAlertService abstracts a service providing Upsert and Delete // operations for cluster alerts. @@ -189,16 +189,25 @@ func (c *ErrorCounter) sync(ctx context.Context) { types.WithAlertLabel(types.AlertOnLogin, "yes"), types.WithAlertLabel(types.AlertVerbPermit, "external_audit_storage:create")) if err != nil { - log.Infof("ErrorCounter failed to create cluster alert %s: %s", newAlert.name, err) + log.InfoContext(ctx, "ErrorCounter failed to create cluster alert", + "alert_name", newAlert.name, + "error", err, + ) continue } if err := c.alertService.UpsertClusterAlert(ctx, alert); err != nil { - log.Infof("ErrorCounter failed to upsert cluster alert %s: %s", newAlert.name, err) + log.InfoContext(ctx, "ErrorCounter failed to upsert cluster alert", + "alert_name", newAlert.name, + "error", err, + ) } } for _, alertToClear := range allAlertActions.clearAlerts { if err := c.alertService.DeleteClusterAlert(ctx, alertToClear); err != nil && !trace.IsNotFound(err) { - log.Infof("ErrorCounter failed to delete cluster alert %s: %s", alertToClear, err) + log.InfoContext(ctx, "ErrorCounter failed to delete cluster alert", + "alert_name", alertToClear, + "error", err, + ) } } } diff --git a/lib/kube/grpc/grpc.go b/lib/kube/grpc/grpc.go index 67f17fc4d5079..bbd982cd12da0 100644 --- a/lib/kube/grpc/grpc.go +++ b/lib/kube/grpc/grpc.go @@ -21,11 +21,11 @@ package kubev1 import ( "context" "errors" + "log/slog" "slices" "github.com/gravitational/trace" "github.com/gravitational/trace/trail" - "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -85,7 +85,7 @@ type Config struct { // Authz authenticates user. Authz authz.Authorizer // Log is the logger function. - Log logrus.FieldLogger + Log *slog.Logger // Emitter is used to emit audit events. Emitter apievents.Emitter // Component name to include in log output. @@ -139,9 +139,9 @@ func (c *Config) CheckAndSetDefaults() error { c.Component = "kube.grpc" } if c.Log == nil { - c.Log = logrus.New() + c.Log = slog.Default() } - c.Log = c.Log.WithFields(logrus.Fields{teleport.ComponentKey: c.Component}) + c.Log = c.Log.With(teleport.ComponentKey, c.Component) return nil } diff --git a/lib/kube/kubeconfig/kubeconfig.go b/lib/kube/kubeconfig/kubeconfig.go index 30e587da834d1..dc9f8d92833f1 100644 --- a/lib/kube/kubeconfig/kubeconfig.go +++ b/lib/kube/kubeconfig/kubeconfig.go @@ -21,13 +21,13 @@ package kubeconfig import ( "bytes" + "context" "fmt" "os" "path/filepath" "strings" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/exp/maps" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/clientcmd" @@ -36,11 +36,10 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) -var log = logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentKubeClient, -}) +var log = logutils.NewPackageLogger(teleport.ComponentKey, teleport.ComponentKubeClient) const ( // teleportKubeClusterNameExtension is the name of the extension that @@ -268,7 +267,7 @@ func UpdateConfig(path string, v Values, storeAllCAs bool, fs ConfigFS) error { } else if !trace.IsBadParameter(err) { return trace.Wrap(err) } - log.WithError(err).Warn("Kubernetes integration is not supported when logging in with a hardware private key.") + log.WarnContext(context.Background(), "Kubernetes integration is not supported when logging in with a hardware private key", "error", err) } return SaveConfig(path, *config, fs) @@ -493,7 +492,7 @@ func PathFromEnv() string { var configPath string if len(parts) > 0 { configPath = parts[0] - log.Debugf("Using kubeconfig from environment: %q.", configPath) + log.DebugContext(context.Background(), "Using kubeconfig from environment", "config_path", configPath) } return configPath diff --git a/lib/kube/proxy/auth.go b/lib/kube/proxy/auth.go index 16ee58685e1a2..61842aca77bf0 100644 --- a/lib/kube/proxy/auth.go +++ b/lib/kube/proxy/auth.go @@ -23,12 +23,12 @@ import ( "context" "crypto/tls" "fmt" + "log/slog" "net" "net/http" "net/url" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" authzapi "k8s.io/api/authorization/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilnet "k8s.io/apimachinery/pkg/util/net" @@ -77,11 +77,11 @@ func (f *Forwarder) getKubeDetails(ctx context.Context) error { kubeClusterName := f.cfg.KubeClusterName tpClusterName := f.cfg.ClusterName - f.log. - WithField("kubeconfigPath", kubeconfigPath). - WithField("kubeClusterName", kubeClusterName). - WithField("serviceType", serviceType). - Debug("Reading Kubernetes details.") + f.log.DebugContext(ctx, "Reading Kubernetes details", + "kubeconfig_path", kubeconfigPath, + "kube_cluster_name", kubeClusterName, + "service_type", serviceType, + ) // Proxy service should never have creds, forwards to kube service if serviceType == ProxyService { @@ -100,7 +100,7 @@ func (f *Forwarder) getKubeDetails(ctx context.Context) error { case KubeService: return trace.BadParameter("no Kubernetes credentials found; Kubernetes_service requires either a valid kubeconfig_file or to run inside of a Kubernetes pod") case LegacyProxyService: - f.log.Debugf("Could not load Kubernetes credentials. This proxy will still handle Kubernetes requests for trusted teleport clusters or Kubernetes nodes in this teleport cluster") + f.log.DebugContext(ctx, "Could not load Kubernetes credentials. This proxy will still handle Kubernetes requests for trusted teleport clusters or Kubernetes nodes in this teleport cluster") } return nil } @@ -124,14 +124,20 @@ func (f *Forwarder) getKubeDetails(ctx context.Context) error { for cluster, clientCfg := range cfg.Contexts { clusterCreds, err := extractKubeCreds(ctx, serviceType, cluster, clientCfg, f.log, f.cfg.CheckImpersonationPermissions) if err != nil { - f.log.WithError(err).Warnf("failed to load credentials for cluster %q.", cluster) + f.log.WarnContext(ctx, "failed to load credentials for cluster", + "cluster", cluster, + "error", err, + ) continue } kubeCluster, err := types.NewKubernetesClusterV3(types.Metadata{ Name: cluster, }, types.KubernetesClusterSpecV3{}) if err != nil { - f.log.WithError(err).Warnf("failed to create KubernetesClusterV3 from credentials for cluster %q.", cluster) + f.log.WarnContext(ctx, "failed to create KubernetesClusterV3 from credentials for cluster", + "cluster", cluster, + "error", err, + ) continue } @@ -139,13 +145,16 @@ func (f *Forwarder) getKubeDetails(ctx context.Context) error { clusterDetailsConfig{ cluster: kubeCluster, kubeCreds: clusterCreds, - log: f.log.WithField("cluster", kubeCluster.GetName()), + log: f.log.With("cluster", kubeCluster.GetName()), checker: f.cfg.CheckImpersonationPermissions, component: serviceType, clock: f.cfg.Clock, }) if err != nil { - f.log.WithError(err).Warnf("Failed to create cluster details for cluster %q.", cluster) + f.log.WarnContext(ctx, "Failed to create cluster details for cluster", + "cluster", cluster, + "error", err, + ) return trace.Wrap(err) } f.clusterDetails[cluster] = details @@ -153,10 +162,10 @@ func (f *Forwarder) getKubeDetails(ctx context.Context) error { return nil } -func extractKubeCreds(ctx context.Context, component string, cluster string, clientCfg *rest.Config, log logrus.FieldLogger, checkPermissions servicecfg.ImpersonationPermissionsChecker) (*staticKubeCreds, error) { - log = log.WithField("cluster", cluster) +func extractKubeCreds(ctx context.Context, component string, cluster string, clientCfg *rest.Config, log *slog.Logger, checkPermissions servicecfg.ImpersonationPermissionsChecker) (*staticKubeCreds, error) { + log = log.With("cluster", cluster) - log.Debug("Checking Kubernetes impersonation permissions.") + log.DebugContext(ctx, "Checking Kubernetes impersonation permissions") client, err := kubernetes.NewForConfig(clientCfg) if err != nil { return nil, trace.Wrap(err, "failed to generate Kubernetes client for cluster %q", cluster) @@ -165,9 +174,11 @@ func extractKubeCreds(ctx context.Context, component string, cluster string, cli // For each loaded cluster, check impersonation permissions. This // check only logs when permissions are not configured, but does not fail startup. if err := checkPermissions(ctx, cluster, client.AuthorizationV1().SelfSubjectAccessReviews()); err != nil { - log.WithError(err).Warning("Failed to test the necessary Kubernetes permissions. The target Kubernetes cluster may be down or have misconfigured RBAC. This teleport instance will still handle Kubernetes requests towards this Kubernetes cluster.") + log.WarnContext(ctx, "Failed to test the necessary Kubernetes permissions. The target Kubernetes cluster may be down or have misconfigured RBAC. This teleport instance will still handle Kubernetes requests towards this Kubernetes cluster.", + "error", err, + ) } else { - log.Debug("Have all necessary Kubernetes impersonation permissions.") + log.DebugContext(ctx, "Have all necessary Kubernetes impersonation permissions") } targetAddr, err := parseKubeHost(clientCfg.Host) @@ -192,7 +203,7 @@ func extractKubeCreds(ctx context.Context, component string, cluster string, cli return nil, trace.Wrap(err, "failed to generate transport from kubeconfig: %v", err) } - log.Debug("Initialized Kubernetes credentials") + log.DebugContext(ctx, "Initialized Kubernetes credentials") return &staticKubeCreds{ tlsConfig: tlsConfig, transportConfig: transportConfig, diff --git a/lib/kube/proxy/auth_test.go b/lib/kube/proxy/auth_test.go index 1263a0e8b9ad2..9d8269297f8f1 100644 --- a/lib/kube/proxy/auth_test.go +++ b/lib/kube/proxy/auth_test.go @@ -140,7 +140,6 @@ func TestGetKubeCreds(t *testing.T) { rbacSupportedTypes[allowedResourcesKey{apiGroup: "resources.teleport.dev", resourceKind: "teleportroles"}] = utils.KubeCustomResource rbacSupportedTypes[allowedResourcesKey{apiGroup: "resources.teleport.dev", resourceKind: "teleportroles/status"}] = utils.KubeCustomResource - logger := utils.NewLoggerForTests() ctx := context.TODO() const teleClusterName = "teleport-cluster" dir := t.TempDir() @@ -351,7 +350,7 @@ current-context: foo CheckImpersonationPermissions: tt.impersonationCheck, Clock: clockwork.NewFakeClock(), }, - log: logger, + log: utils.NewSlogLoggerForTests(), } err := fwd.getKubeDetails(ctx) tt.assertErr(t, err) diff --git a/lib/kube/proxy/cluster_details.go b/lib/kube/proxy/cluster_details.go index c949494ac982d..1a66ce0562978 100644 --- a/lib/kube/proxy/cluster_details.go +++ b/lib/kube/proxy/cluster_details.go @@ -21,6 +21,7 @@ package proxy import ( "context" "encoding/base64" + "log/slog" "strings" "sync" "time" @@ -29,7 +30,6 @@ import ( "github.com/aws/aws-sdk-go/service/eks" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/version" @@ -91,7 +91,7 @@ type clusterDetailsConfig struct { // cluster is the cluster to create a proxied cluster for. cluster types.KubeCluster // log is the logger to use. - log *logrus.Entry + log *slog.Logger // checker is the permissions checker to use. checker servicecfg.ImpersonationPermissionsChecker // resourceMatchers is the list of resource matchers to match the cluster against @@ -135,7 +135,7 @@ func newClusterDetails(ctx context.Context, cfg clusterDetailsConfig) (_ *kubeDe // Create the codec factory and the list of supported types for RBAC. codecFactory, rbacSupportedTypes, gvkSupportedRes, err := newClusterSchemaBuilder(cfg.log, creds.getKubeClient()) if err != nil { - cfg.log.WithError(err).Warn("Failed to create cluster schema. Possibly the cluster is offline.") + cfg.log.WarnContext(ctx, "Failed to create cluster schema, the cluster may be offline", "error", err) // If the cluster is offline, we will not be able to create the codec factory // and the list of supported types for RBAC. // We mark the cluster as offline and continue to create the kubeDetails but @@ -145,7 +145,7 @@ func newClusterDetails(ctx context.Context, cfg clusterDetailsConfig) (_ *kubeDe kubeVersion, err := creds.getKubeClient().Discovery().ServerVersion() if err != nil { - cfg.log.WithError(err).Warn("Failed to get Kubernetes cluster version. Possibly the cluster is offline.") + cfg.log.WarnContext(ctx, "Failed to get Kubernetes cluster version, the cluster may be offline", "error", err) } ctx, cancel := context.WithCancel(ctx) @@ -198,13 +198,13 @@ func newClusterDetails(ctx context.Context, cfg clusterDetailsConfig) (_ *kubeDe } else { refreshDelay.Inc() } - cfg.log.WithError(err).Error("Failed to update cluster schema") + cfg.log.ErrorContext(ctx, "Failed to update cluster schema", "error", err) continue } kubeVersion, err := creds.getKubeClient().Discovery().ServerVersion() if err != nil { - cfg.log.WithError(err).Warn("Failed to get Kubernetes cluster version. Possibly the cluster is offline.") + cfg.log.WarnContext(ctx, "Failed to get Kubernetes cluster version, the cluster may be offline", "error", err) } // Restore details refresh delay to the default value, in case previously cluster was offline. @@ -389,7 +389,7 @@ func getAWSClientRestConfig(cloudClients cloud.Clients, clock clockwork.Clock, r // getStaticCredentialsFromKubeconfig loads a kubeconfig from the cluster and returns the access credentials for the cluster. // If the config defines multiple contexts, it will pick one (the order is not guaranteed). -func getStaticCredentialsFromKubeconfig(ctx context.Context, component KubeServiceType, cluster types.KubeCluster, log *logrus.Entry, checker servicecfg.ImpersonationPermissionsChecker) (*staticKubeCreds, error) { +func getStaticCredentialsFromKubeconfig(ctx context.Context, component KubeServiceType, cluster types.KubeCluster, log *slog.Logger, checker servicecfg.ImpersonationPermissionsChecker) (*staticKubeCreds, error) { config, err := clientcmd.Load(cluster.GetKubeconfig()) if err != nil { return nil, trace.WrapWithMessage(err, "unable to parse kubeconfig for cluster %q", cluster.GetName()) diff --git a/lib/kube/proxy/cluster_details_test.go b/lib/kube/proxy/cluster_details_test.go index 116a575bc4143..9b52a695752da 100644 --- a/lib/kube/proxy/cluster_details_test.go +++ b/lib/kube/proxy/cluster_details_test.go @@ -26,7 +26,6 @@ import ( "time" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/version" @@ -34,12 +33,12 @@ import ( "k8s.io/client-go/kubernetes" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/utils" ) func TestNewClusterDetails(t *testing.T) { t.Parallel() ctx := context.Background() - log := logrus.New().WithContext(ctx) getClusterDetailsConfig := func(c clockwork.FakeClock) (clusterDetailsConfig, *clusterDetailsClientSet) { client := &clusterDetailsClientSet{} @@ -48,7 +47,7 @@ func TestNewClusterDetails(t *testing.T) { kubeClient: client, }, cluster: &types.KubernetesClusterV3{}, - log: log, + log: utils.NewSlogLoggerForTests(), clock: c, }, client } diff --git a/lib/kube/proxy/ephemeral_containers.go b/lib/kube/proxy/ephemeral_containers.go index 1c9ae08e417a4..61947055c0067 100644 --- a/lib/kube/proxy/ephemeral_containers.go +++ b/lib/kube/proxy/ephemeral_containers.go @@ -83,7 +83,7 @@ func (f *Forwarder) ephemeralContainers(authCtx *authContext, w http.ResponseWri if err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to create cluster session: %v.", err) + f.log.ErrorContext(req.Context(), "Failed to create cluster session", "error", err) return nil, trace.Wrap(err) } // sess.Close cancels the connection monitor context to release it sooner. @@ -101,7 +101,7 @@ func (f *Forwarder) ephemeralContainers(authCtx *authContext, w http.ResponseWri if err := f.setupForwardingHeaders(sess, req, true /* withImpersonationHeaders */); err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to set up forwarding headers: %v.", err) + f.log.ErrorContext(req.Context(), "Failed to set up forwarding headers", "error", err) return nil, trace.Wrap(err) } if !sess.isLocalKubernetesCluster { diff --git a/lib/kube/proxy/forwarder.go b/lib/kube/proxy/forwarder.go index b3df6d6c0b153..aeed6d7c631ac 100644 --- a/lib/kube/proxy/forwarder.go +++ b/lib/kube/proxy/forwarder.go @@ -41,7 +41,6 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/julienschmidt/httprouter" - "github.com/sirupsen/logrus" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" oteltrace "go.opentelemetry.io/otel/trace" kubeerrors "k8s.io/apimachinery/pkg/api/errors" @@ -86,6 +85,7 @@ import ( "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/sshca" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // KubeServiceType specifies a Teleport service type which can forward Kubernetes requests @@ -157,7 +157,7 @@ type ForwarderConfig struct { // PROXYSigner is used to sign PROXY headers for securely propagating client IP address PROXYSigner multiplexer.PROXYHeaderSigner // log is the logger function - log logrus.FieldLogger + log *slog.Logger // TracerProvider is used to create tracers capable // of starting spans. TracerProvider oteltrace.TracerProvider @@ -272,7 +272,7 @@ func (f *ForwarderConfig) CheckAndSetDefaults() error { f.KubeClusterName = f.ClusterName } if f.log == nil { - f.log = logrus.New() + f.log = slog.Default() } return nil } @@ -347,7 +347,7 @@ func NewForwarder(cfg ForwarderConfig) (*Forwarder, error) { fwd.router = instrumentHTTPHandler(fwd.cfg.KubeServiceType, router) if cfg.ClusterOverride != "" { - fwd.log.Debugf("Cluster override is set, forwarder will send all requests to remote cluster %v.", cfg.ClusterOverride) + fwd.log.DebugContext(closeCtx, "Cluster override is set, forwarder will send all requests to remote cluster", "cluster_override", cfg.ClusterOverride) } if len(cfg.KubeClusterName) > 0 || len(cfg.KubeconfigPath) > 0 || cfg.KubeServiceType != KubeService { if err := fwd.getKubeDetails(cfg.Context); err != nil { @@ -363,7 +363,7 @@ func NewForwarder(cfg ForwarderConfig) (*Forwarder, error) { // however some requests like exec sessions it intercepts and records. type Forwarder struct { mu sync.Mutex - log logrus.FieldLogger + log *slog.Logger router http.Handler cfg ForwarderConfig // activeRequests is a map used to serialize active CSR requests to the auth server @@ -540,7 +540,7 @@ func (f *Forwarder) authenticate(req *http.Request) (*authContext, error) { var isRemoteUser bool userTypeI, err := authz.UserFromContext(ctx) if err != nil { - f.log.WithError(err).Warn("error getting user from context") + f.log.WarnContext(ctx, "error getting user from context", "error", err) return nil, trace.AccessDenied(accessDeniedMsg) } switch userTypeI.(type) { @@ -549,10 +549,12 @@ func (f *Forwarder) authenticate(req *http.Request) (*authContext, error) { case authz.RemoteUser: isRemoteUser = true case authz.BuiltinRole: - f.log.Warningf("Denying proxy access to unauthenticated user of type %T - this can sometimes be caused by inadvertently using an HTTP load balancer instead of a TCP load balancer on the Kubernetes port.", userTypeI) + f.log.WarnContext(ctx, "Denying proxy access to unauthenticated user - this can sometimes be caused by inadvertently using an HTTP load balancer instead of a TCP load balancer on the Kubernetes port", + "user_type", logutils.TypeAttr(userTypeI), + ) return nil, trace.AccessDenied(accessDeniedMsg) default: - f.log.Warningf("Denying proxy access to unsupported user type: %T.", userTypeI) + f.log.WarnContext(ctx, "Denying proxy access to unsupported user type", "user_type", logutils.TypeAttr(userTypeI)) return nil, trace.AccessDenied(accessDeniedMsg) } @@ -563,7 +565,7 @@ func (f *Forwarder) authenticate(req *http.Request) (*authContext, error) { authContext, err := f.setupContext(ctx, *userContext, req, isRemoteUser) if err != nil { - f.log.WithError(err).Warn("Unable to setup context.") + f.log.WarnContext(ctx, "Unable to setup context", "error", err) if trace.IsAccessDenied(err) { return nil, trace.AccessDenied(accessDeniedMsg) } @@ -726,7 +728,7 @@ func (f *Forwarder) formatStatusResponseError(rw http.ResponseWriter, respErr er } data, err := runtime.Encode(globalKubeCodecs.LegacyCodec(), status) if err != nil { - f.log.Warningf("Failed encoding error into kube Status object: %v", err) + f.log.WarnContext(f.ctx, "Failed encoding error into kube Status object", "error", err) trace.WriteError(rw, respErr) return } @@ -737,7 +739,7 @@ func (f *Forwarder) formatStatusResponseError(rw http.ResponseWriter, respErr er // has prevented the request from succeeding`` instead of the correct reason. rw.WriteHeader(trace.ErrorToCode(respErr)) if _, err := rw.Write(data); err != nil { - f.log.Warningf("Failed writing kube error response body: %v", err) + f.log.WarnContext(f.ctx, "Failed writing kube error response body", "error", err) } } @@ -919,7 +921,7 @@ func (f *Forwarder) emitAuditEvent(req *http.Request, sess *clusterSession, stat r.populateEvent(event) if err := f.cfg.AuthClient.EmitAuditEvent(f.ctx, event); err != nil { - f.log.WithError(err).Warn("Failed to emit event.") + f.log.WarnContext(f.ctx, "Failed to emit event", "error", err) } } @@ -1033,13 +1035,17 @@ func (f *Forwarder) authorize(ctx context.Context, actx *authContext) error { if actx.teleportCluster.isRemote { // Authorization for a remote kube cluster will happen on the remote // end (by their proxy), after that cluster has remapped used roles. - f.log.WithField("auth_context", actx.String()).Debug("Skipping authorization for a remote kubernetes cluster name") + f.log.DebugContext(ctx, "Skipping authorization for a remote kubernetes cluster name", + "auth_context", logutils.StringerAttr(actx), + ) return nil } if actx.kubeClusterName == "" { // This should only happen for remote clusters (filtered above), but // check and report anyway. - f.log.WithField("auth_context", actx.String()).Debug("Skipping authorization due to unknown kubernetes cluster name") + f.log.DebugContext(ctx, "Skipping authorization due to unknown kubernetes cluster name", + "auth_context", logutils.StringerAttr(actx), + ) return nil } @@ -1135,7 +1141,9 @@ func (f *Forwarder) authorize(ctx context.Context, actx *authContext) error { return nil } if actx.kubeClusterName == f.cfg.ClusterName { - f.log.WithField("auth_context", actx.String()).Debug("Skipping authorization for proxy-based kubernetes cluster,") + f.log.DebugContext(ctx, "Skipping authorization for proxy-based kubernetes cluster", + "auth_context", logutils.StringerAttr(actx), + ) return nil } return trace.AccessDenied(notFoundMessage) @@ -1168,7 +1176,7 @@ func (f *Forwarder) join(ctx *authContext, w http.ResponseWriter, req *http.Requ joinSessionsInFlightGauge.WithLabelValues(f.cfg.KubeServiceType).Inc() defer joinSessionsInFlightGauge.WithLabelValues(f.cfg.KubeServiceType).Dec() - f.log.Debugf("Join %v.", req.URL.String()) + f.log.DebugContext(req.Context(), "Joining session", "join_url", logutils.StringerAttr(req.URL)) sess, err := f.newClusterSession(req.Context(), *ctx) if err != nil { @@ -1241,7 +1249,11 @@ func (f *Forwarder) join(ctx *authContext, w http.ResponseWriter, req *http.Requ close(closeC) if _, err := session.leave(party.ID); err != nil { - f.log.WithError(err).Debugf("Participant %q was unable to leave session %s", party.ID, session.id) + f.log.DebugContext(req.Context(), "Participant was unable to leave session", + "participant_id", party.ID, + "session_id", session.id, + "error", err, + ) } wg.Wait() @@ -1249,7 +1261,7 @@ func (f *Forwarder) join(ctx *authContext, w http.ResponseWriter, req *http.Requ }(); err != nil { writeErr := ws.WriteControl(gwebsocket.CloseMessage, gwebsocket.FormatCloseMessage(gwebsocket.CloseInternalServerErr, err.Error()), time.Now().Add(time.Second*10)) if writeErr != nil { - f.log.WithError(writeErr).Warn("Failed to send early-exit websocket close message.") + f.log.WarnContext(req.Context(), "Failed to send early-exit websocket close message", "error", writeErr) } } @@ -1337,7 +1349,7 @@ func (f *Forwarder) remoteJoin(ctx *authContext, w http.ResponseWriter, req *htt } defer wsSource.Close() - wsProxy(f.log, wsSource, wsTarget) + wsProxy(req.Context(), f.log, wsSource, wsTarget) return nil, nil } @@ -1362,7 +1374,7 @@ func (f *Forwarder) getSessionHostID(ctx context.Context, authCtx *authContext, // wsProxy proxies a websocket connection between two clusters transparently to allow for // remote joins. -func wsProxy(log logrus.FieldLogger, wsSource *gwebsocket.Conn, wsTarget *gwebsocket.Conn) { +func wsProxy(ctx context.Context, log *slog.Logger, wsSource *gwebsocket.Conn, wsTarget *gwebsocket.Conn) { errS := make(chan error, 1) errT := make(chan error, 1) wg := &sync.WaitGroup{} @@ -1416,7 +1428,7 @@ func wsProxy(log logrus.FieldLogger, wsSource *gwebsocket.Conn, wsTarget *gwebso var websocketErr *gwebsocket.CloseError if errors.As(err, &websocketErr) && websocketErr.Code == gwebsocket.CloseAbnormalClosure { - log.WithError(err).Debugf("websocket proxy: Error when copying from %s to %s", from, to) + log.DebugContext(ctx, "websocket proxying failed", "src", from, "target", to, "error", err) } wg.Wait() } @@ -1496,7 +1508,7 @@ func (f *Forwarder) execNonInteractive(ctx *authContext, req *http.Request, _ ht } if err := f.cfg.Emitter.EmitAuditEvent(f.ctx, sessionStartEvent); err != nil { - f.log.WithError(err).Warn("Failed to emit event.") + f.log.WarnContext(f.ctx, "Failed to emit event", "error", err) return trace.Wrap(err) } @@ -1518,7 +1530,7 @@ func (f *Forwarder) execNonInteractive(ctx *authContext, req *http.Request, _ ht defer func() { if err := f.cfg.Emitter.EmitAuditEvent(f.ctx, execEvent); err != nil { - f.log.WithError(err).Warn("Failed to emit exec event.") + f.log.WarnContext(f.ctx, "Failed to emit exec event", "error", err) } sessionEndEvent := &apievents.SessionEnd{ @@ -1541,7 +1553,7 @@ func (f *Forwarder) execNonInteractive(ctx *authContext, req *http.Request, _ ht } if err := f.cfg.Emitter.EmitAuditEvent(f.ctx, sessionEndEvent); err != nil { - f.log.WithError(err).Warn("Failed to emit session end event.") + f.log.WarnContext(f.ctx, "Failed to emit session end event", "error", err) } }() @@ -1550,7 +1562,7 @@ func (f *Forwarder) execNonInteractive(ctx *authContext, req *http.Request, _ ht execEvent.Code = events.ExecFailureCode execEvent.Error, execEvent.ExitCode = exitCode(err) - f.log.WithError(err).Warning("Failed creating executor.") + f.log.WarnContext(f.ctx, "Failed creating executor", "error", err) return trace.Wrap(err) } @@ -1560,7 +1572,7 @@ func (f *Forwarder) execNonInteractive(ctx *authContext, req *http.Request, _ ht execEvent.Code = events.ExecFailureCode execEvent.Error, execEvent.ExitCode = exitCode(err) - f.log.WithError(err).Warning("Executor failed while streaming.") + f.log.WarnContext(f.ctx, "Executor failed while streaming", "error", err) return trace.Wrap(err) } @@ -1629,10 +1641,10 @@ func (f *Forwarder) exec(authCtx *authContext, w http.ResponseWriter, req *http. ) defer span.End() - f.log.Debugf("Exec %v.", req.URL.String()) + f.log.DebugContext(ctx, "Starting exec", "exec_url", logutils.StringerAttr(req.URL)) defer func() { if err != nil { - f.log.WithError(err).Debug("Exec request failed") + f.log.DebugContext(ctx, "Exec request failed", "error", err) } }() @@ -1640,7 +1652,7 @@ func (f *Forwarder) exec(authCtx *authContext, w http.ResponseWriter, req *http. if err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to create cluster session: %v.", err) + f.log.ErrorContext(ctx, "Failed to create cluster session", "error", err) return nil, trace.Wrap(err) } // sess.Close cancels the connection monitor context to release it sooner. @@ -1702,7 +1714,11 @@ func (f *Forwarder) exec(authCtx *authContext, w http.ResponseWriter, req *http. err = <-party.closeC if _, errLeave := session.leave(party.ID); errLeave != nil { - f.log.WithError(errLeave).Debugf("Participant %q was unable to leave session %s", party.ID, session.id) + f.log.DebugContext(ctx, "Participant was unable to leave session", + "participant_id", party.ID, + "session_id", session.id, + "error", errLeave, + ) } return trace.Wrap(err) @@ -1714,13 +1730,13 @@ func (f *Forwarder) exec(authCtx *authContext, w http.ResponseWriter, req *http. func (f *Forwarder) remoteExec(req *http.Request, sess *clusterSession, proxy *remoteCommandProxy) error { executor, err := f.getExecutor(sess, req) if err != nil { - f.log.WithError(err).Warning("Failed creating executor.") + f.log.WarnContext(req.Context(), "Failed creating executor", "error", err) return trace.Wrap(err) } streamOptions := proxy.options() err = executor.StreamWithContext(req.Context(), streamOptions) if err != nil { - f.log.WithError(err).Warning("Executor failed while streaming.") + f.log.WarnContext(req.Context(), "Executor failed while streaming", "error", err) } return trace.Wrap(err) @@ -1745,12 +1761,15 @@ func (f *Forwarder) portForward(authCtx *authContext, w http.ResponseWriter, req ) defer span.End() - f.log.Debugf("Port forward: %v. req headers: %v.", req.URL.String(), req.Header) + f.log.DebugContext(ctx, "Handling port forward request", + "request_url", logutils.StringerAttr(req.URL), + "request_headers", req.Header, + ) sess, err := f.newClusterSession(ctx, *authCtx) if err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to create cluster session: %v.", err) + f.log.ErrorContext(ctx, "Failed to create cluster session", "error", err) return nil, trace.Wrap(err) } // sess.Close cancels the connection monitor context to release it sooner. @@ -1765,7 +1784,7 @@ func (f *Forwarder) portForward(authCtx *authContext, w http.ResponseWriter, req } if err := f.setupForwardingHeaders(sess, req, true /* withImpersonationHeaders */); err != nil { - f.log.Debugf("DENIED Port forward: %v.", req.URL.String()) + f.log.DebugContext(ctx, "DENIED Port forward", "request_url", logutils.StringerAttr(req.URL)) return nil, trace.Wrap(err) } @@ -1805,7 +1824,7 @@ func (f *Forwarder) portForward(authCtx *authContext, w http.ResponseWriter, req portForward.Code = events.PortForwardFailureCode } if err := f.cfg.Emitter.EmitAuditEvent(f.ctx, portForward); err != nil { - f.log.WithError(err).Warn("Failed to emit event.") + f.log.WarnContext(ctx, "Failed to emit event", "error", err) } } defer func() { @@ -1829,7 +1848,7 @@ func (f *Forwarder) portForward(authCtx *authContext, w http.ResponseWriter, req }, } if err := f.cfg.Emitter.EmitAuditEvent(f.ctx, portForward); err != nil { - f.log.WithError(err).Warn("Failed to emit event.") + f.log.WarnContext(ctx, "Failed to emit event", "error", err) } } }() @@ -1847,12 +1866,12 @@ func (f *Forwarder) portForward(authCtx *authContext, w http.ResponseWriter, req pingPeriod: f.cfg.ConnPingPeriod, idleTimeout: sess.clientIdleTimeout, } - f.log.Debugf("Starting %v.", request) + f.log.DebugContext(ctx, "Starting port forwarding", "request", request) err = runPortForwarding(request) if err != nil { return nil, trace.Wrap(err) } - f.log.Debugf("Done %v.", request) + f.log.DebugContext(ctx, "Completed port forwarding", "request", request) return nil, nil } @@ -2057,11 +2076,11 @@ func (f *Forwarder) catchAll(authCtx *authContext, w http.ResponseWriter, req *h req = req.WithContext(ctx) defer span.End() - sess, err := f.newClusterSession(req.Context(), *authCtx) + sess, err := f.newClusterSession(ctx, *authCtx) if err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to create cluster session: %v.", err) + f.log.ErrorContext(ctx, "Failed to create cluster session", "error", err) return nil, trace.Wrap(err) } // sess.Close cancels the connection monitor context to release it sooner. @@ -2079,7 +2098,7 @@ func (f *Forwarder) catchAll(authCtx *authContext, w http.ResponseWriter, req *h if err := f.setupForwardingHeaders(sess, req, true /* withImpersonationHeaders */); err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to set up forwarding headers: %v.", err) + f.log.ErrorContext(ctx, "Failed to set up forwarding headers", "error", err) return nil, trace.Wrap(err) } @@ -2151,7 +2170,10 @@ func (f *Forwarder) getWebsocketRestConfig(sess *clusterSession, req *http.Reque } func (f *Forwarder) getWebsocketExecutor(sess *clusterSession, req *http.Request) (remotecommand.Executor, error) { - f.log.Debugf("Creating websocket remote executor for request %s %s", req.Method, req.RequestURI) + f.log.DebugContext(req.Context(), "Creating websocket remote executor for request", + "request_method", req.Method, + "request_uri", req.RequestURI, + ) cfg, err := f.getWebsocketRestConfig(sess, req) if err != nil { return nil, trace.Wrap(err, "unable to create websocket executor") @@ -2190,7 +2212,10 @@ func (f *Forwarder) getExecutor(sess *clusterSession, req *http.Request) (remote } func (f *Forwarder) getSPDYExecutor(sess *clusterSession, req *http.Request) (remotecommand.Executor, error) { - f.log.Debugf("Creating SPDY remote executor for request %s %s", req.Method, req.RequestURI) + f.log.DebugContext(req.Context(), "Creating SPDY remote executor for request", + "request_method", req.Method, + "request_uri", req.RequestURI, + ) tlsConfig, useImpersonation, err := f.getTLSConfig(sess) if err != nil { @@ -2381,11 +2406,9 @@ func (s *clusterSession) monitorConn(conn net.Conn, err error, hostID string) (n Context: s.connCtx, TeleportUser: s.User.GetName(), ServerID: s.parent.cfg.HostID, - // TODO(tross) update this to use the child logger - // once Forwarder is converted to use a slog.Logger - Logger: slog.Default(), - Emitter: s.parent.cfg.AuthClient, - EmitterContext: s.parent.ctx, + Logger: s.parent.log, + Emitter: s.parent.cfg.AuthClient, + EmitterContext: s.parent.ctx, }) if err != nil { tc.CloseWithCause(err) @@ -2462,7 +2485,7 @@ func (f *Forwarder) newClusterSession(ctx context.Context, authCtx authContext) } func (f *Forwarder) newClusterSessionRemoteCluster(ctx context.Context, authCtx authContext) (*clusterSession, error) { - f.log.Debugf("Forwarding kubernetes session for %v to remote cluster.", authCtx) + f.log.DebugContext(ctx, "Forwarding kubernetes session to remote cluster", "auth_context", logutils.StringerAttr(authCtx)) connCtx, cancel := context.WithCancelCause(ctx) return &clusterSession{ parent: f, @@ -2511,7 +2534,7 @@ func (f *Forwarder) newClusterSessionLocal(ctx context.Context, authCtx authCont return nil, trace.Wrap(err) } connCtx, cancel := context.WithCancelCause(ctx) - f.log.Debugf("Handling kubernetes session for %v using local credentials.", authCtx) + f.log.DebugContext(ctx, "Handling kubernetes session using local credentials", "auth_context", logutils.StringerAttr(authCtx)) return &clusterSession{ parent: f, authContext: authCtx, @@ -2550,8 +2573,7 @@ func (f *Forwarder) makeSessionForwarder(sess *clusterSession) (*reverseproxy.Fo opts := []reverseproxy.Option{ reverseproxy.WithFlushInterval(100 * time.Millisecond), reverseproxy.WithRoundTripper(transport), - // TODO(tross): convert this to use f.log once it has been converted to use slog - reverseproxy.WithLogger(slog.Default()), + reverseproxy.WithLogger(f.log), reverseproxy.WithErrorHandler(f.formatForwardResponseError), } if sess.isLocalKubernetesCluster { diff --git a/lib/kube/proxy/forwarder_test.go b/lib/kube/proxy/forwarder_test.go index ef10408506f5d..860746ab97213 100644 --- a/lib/kube/proxy/forwarder_test.go +++ b/lib/kube/proxy/forwarder_test.go @@ -40,7 +40,6 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/julienschmidt/httprouter" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" kubeerrors "k8s.io/apimachinery/pkg/api/errors" @@ -132,7 +131,7 @@ func TestAuthenticate(t *testing.T) { }, } f := &Forwarder{ - log: logrus.NewEntry(logrus.New()), + log: utils.NewSlogLoggerForTests(), cfg: ForwarderConfig{ ClusterName: "local", CachingAuthClient: ap, @@ -1112,7 +1111,7 @@ func newMockForwader(ctx context.Context, t *testing.T) *Forwarder { require.NoError(t, err) return &Forwarder{ - log: logrus.NewEntry(logrus.New()), + log: utils.NewSlogLoggerForTests(), router: httprouter.New(), cfg: ForwarderConfig{ Keygen: testauthority.New(), @@ -1316,7 +1315,7 @@ func (m *mockWatcher) Done() <-chan struct{} { func newTestForwarder(ctx context.Context, cfg ForwarderConfig) *Forwarder { return &Forwarder{ - log: logrus.NewEntry(logrus.New()), + log: utils.NewSlogLoggerForTests(), router: httprouter.New(), cfg: cfg, activeRequests: make(map[string]context.Context), @@ -1676,7 +1675,7 @@ func TestForwarderTLSConfigCAs(t *testing.T) { return x509.NewCertPool(), nil }, }, - log: logrus.NewEntry(logrus.New()), + log: utils.NewSlogLoggerForTests(), ctx: context.Background(), } diff --git a/lib/kube/proxy/kube_creds.go b/lib/kube/proxy/kube_creds.go index 19fd6edb2bd69..fd7e367d5f8cf 100644 --- a/lib/kube/proxy/kube_creds.go +++ b/lib/kube/proxy/kube_creds.go @@ -21,13 +21,13 @@ package proxy import ( "context" "crypto/tls" + "log/slog" "net/http" "sync" "time" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/transport" @@ -151,7 +151,7 @@ type dynamicKubeCreds struct { ctx context.Context renewTicker clockwork.Ticker staticCreds *staticKubeCreds - log logrus.FieldLogger + log *slog.Logger closeC chan struct{} client dynamicCredsClient checker servicecfg.ImpersonationPermissionsChecker @@ -164,7 +164,7 @@ type dynamicKubeCreds struct { // dynamicCredsConfig contains configuration for dynamicKubeCreds. type dynamicCredsConfig struct { kubeCluster types.KubeCluster - log logrus.FieldLogger + log *slog.Logger client dynamicCredsClient checker servicecfg.ImpersonationPermissionsChecker clock clockwork.Clock @@ -224,7 +224,7 @@ func newDynamicKubeCreds(ctx context.Context, cfg dynamicCredsConfig) (*dynamicK return case <-dyn.renewTicker.Chan(): if err := dyn.renewClientset(cfg.kubeCluster); err != nil { - logrus.WithError(err).Warnf("Unable to renew cluster %q credentials.", cfg.kubeCluster.GetName()) + cfg.log.WarnContext(ctx, "Unable to renew cluster credentials", "cluster", cfg.kubeCluster.GetName(), "error", err) } } } diff --git a/lib/kube/proxy/kube_creds_test.go b/lib/kube/proxy/kube_creds_test.go index ef8950691c0a1..ca4f1bd4b58e0 100644 --- a/lib/kube/proxy/kube_creds_test.go +++ b/lib/kube/proxy/kube_creds_test.go @@ -30,19 +30,19 @@ import ( "github.com/aws/aws-sdk-go/service/eks" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" authztypes "k8s.io/client-go/kubernetes/typed/authorization/v1" "k8s.io/client-go/rest" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/api/utils" + apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/cloud" "github.com/gravitational/teleport/lib/cloud/azure" "github.com/gravitational/teleport/lib/cloud/gcp" "github.com/gravitational/teleport/lib/cloud/mocks" "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/utils" ) // Test_DynamicKubeCreds tests the dynamic kube credrentials generator for @@ -54,7 +54,6 @@ func Test_DynamicKubeCreds(t *testing.T) { t.Parallel() var ( fakeClock = clockwork.NewFakeClock() - log = logrus.New() notify = make(chan struct{}, 1) ttl = 14 * time.Minute ) @@ -106,7 +105,7 @@ func Test_DynamicKubeCreds(t *testing.T) { Host: "sts.amazonaws.com", Path: "/?Action=GetCallerIdentity&Version=2011-06-15", } - sts := &mocks.STSMock{ + sts := &mocks.STSClientV1{ // u is used to presign the request // here we just verify the pre-signed request includes this url. URL: u, @@ -303,7 +302,7 @@ func Test_DynamicKubeCreds(t *testing.T) { ) error { return nil }, - log: log, + log: utils.NewSlogLoggerForTests(), kubeCluster: tt.args.cluster, client: tt.args.client, initialRenewInterval: ttl / 2, @@ -332,8 +331,8 @@ func Test_DynamicKubeCreds(t *testing.T) { } require.NoError(t, got.close()) - require.Equal(t, tt.wantAssumedRole, utils.Deduplicate(sts.GetAssumedRoleARNs())) - require.Equal(t, tt.wantExternalIds, utils.Deduplicate(sts.GetAssumedRoleExternalIDs())) + require.Equal(t, tt.wantAssumedRole, apiutils.Deduplicate(sts.GetAssumedRoleARNs())) + require.Equal(t, tt.wantExternalIds, apiutils.Deduplicate(sts.GetAssumedRoleExternalIDs())) sts.ResetAssumeRoleHistory() }) } diff --git a/lib/kube/proxy/portforward_spdy.go b/lib/kube/proxy/portforward_spdy.go index 561750239250d..1745536fc44f1 100644 --- a/lib/kube/proxy/portforward_spdy.go +++ b/lib/kube/proxy/portforward_spdy.go @@ -19,6 +19,7 @@ package proxy import ( "context" "fmt" + "log/slog" "net" "net/http" "strconv" @@ -26,7 +27,6 @@ import ( "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/util/httpstream" spdystream "k8s.io/apimachinery/pkg/util/httpstream/spdy" @@ -91,10 +91,10 @@ func runPortForwardingHTTPStreams(req portForwardRequest) error { defer conn.Close() h := &portForwardProxy{ - Entry: log.WithFields(log.Fields{ - teleport.ComponentKey: teleport.Component(teleport.ComponentProxyKube), - events.RemoteAddr: req.httpRequest.RemoteAddr, - }), + logger: slog.With( + teleport.ComponentKey, teleport.Component(teleport.ComponentProxyKube), + events.RemoteAddr, req.httpRequest.RemoteAddr, + ), portForwardRequest: req, sourceConn: conn, streamChan: streamChan, @@ -104,7 +104,7 @@ func runPortForwardingHTTPStreams(req portForwardRequest) error { } defer h.Close() - h.Debugf("Setting port forwarding streaming connection idle timeout to %s.", req.idleTimeout) + h.logger.DebugContext(req.context, "Setting port forwarding streaming connection idle timeout", "idle_timeout", req.idleTimeout) conn.SetIdleTimeout(req.idleTimeout) h.run() @@ -149,7 +149,7 @@ func httpStreamReceived(ctx context.Context, streams chan httpstream.Stream) fun // portForwardProxy is capable of processing multiple port forward // requests over a single httpstream.Connection. type portForwardProxy struct { - *log.Entry + logger *slog.Logger portForwardRequest sourceConn httpstream.Connection streamChan chan httpstream.Stream @@ -200,7 +200,7 @@ func (h *portForwardProxy) forwardStreamPair(p *httpStreamPair, remotePort int64 go func() { defer wg.Done() if err := utils.ProxyConn(h.context, p.errorStream, targetErrorStream); err != nil { - h.WithError(err).Debugf("Unable to proxy portforward error-stream.") + h.logger.DebugContext(h.context, "Unable to proxy portforward error-stream", "error", err) } }() @@ -222,14 +222,14 @@ func (h *portForwardProxy) forwardStreamPair(p *httpStreamPair, remotePort int64 go func() { defer wg.Done() if err := utils.ProxyConn(h.context, p.dataStream, targetDataStream); err != nil { - h.WithError(err).Debugf("Unable to proxy portforward data-stream.") + h.logger.DebugContext(h.context, "Unable to proxy portforward data-stream", "error", err) } }() - h.Debugf("Streams have been created, Waiting for copy to complete.") + h.logger.DebugContext(h.context, "Streams have been created, Waiting for copy to complete") // wait for the copies to complete before returning. wg.Wait() - h.Debugf("Port forwarding pair completed.") + h.logger.DebugContext(h.context, "Port forwarding pair completed") return nil } @@ -241,11 +241,11 @@ func (h *portForwardProxy) getStreamPair(requestID string) (*httpStreamPair, boo defer h.streamPairsLock.Unlock() if p, ok := h.streamPairs[requestID]; ok { - log.Debugf("Request %s, found existing stream pair", requestID) + h.logger.DebugContext(h.context, "Found existing stream pair for request", "request_id", requestID) return p, false } - h.Debugf("Request %s, creating new stream pair.", requestID) + h.logger.DebugContext(h.context, "Creating new stream pair for request", "request_id", requestID) p := newPortForwardPair(requestID) h.streamPairs[requestID] = p @@ -261,9 +261,9 @@ func (h *portForwardProxy) monitorStreamPair(p *httpStreamPair) { defer timeC.Stop() select { case <-timeC.C: - h.Errorf("Request %s, timed out waiting for streams.", p.requestID) + h.logger.ErrorContext(h.context, "Request timed out waiting for streams", "request_id", p.requestID) case <-p.complete: - h.Debugf("Request %s, successfully received error and data streams.", p.requestID) + h.logger.DebugContext(h.context, "Request successfully received error and data streams", "request_id", p.requestID) } h.removeStreamPair(p.requestID) } @@ -296,23 +296,24 @@ func (h *portForwardProxy) requestID(stream httpstream.Stream) (string, error) { // streams, invoking portForward for each complete stream pair. The loop exits // when the httpstream.Connection is closed. func (h *portForwardProxy) run() { - h.Debugf("Waiting for port forward streams.") + h.logger.DebugContext(h.context, "Waiting for port forward streams") for { select { case <-h.context.Done(): - h.Debugf("Context is closing, returning.") + h.logger.DebugContext(h.context, "Context is closing, returning") return case <-h.sourceConn.CloseChan(): - h.Debugf("Upgraded connection closed.") + h.logger.DebugContext(h.context, "Upgraded connection closed") return case stream := <-h.streamChan: requestID, err := h.requestID(stream) if err != nil { - h.Warningf("Failed to parse request id: %v.", err) + h.logger.WarnContext(h.context, "Failed to parse request id", "error", err) return } + streamType := stream.Headers().Get(StreamType) - h.Debugf("Received new stream %v of type %v.", requestID, streamType) + h.logger.DebugContext(h.context, "Received new stream", "request_id", requestID, "stream_type", streamType) p, created := h.getStreamPair(requestID) if created { @@ -336,13 +337,15 @@ func (h *portForwardProxy) portForward(p *httpStreamPair) { portString := p.dataStream.Headers().Get(PortHeader) port, _ := strconv.ParseInt(portString, 10, 32) - h.Debugf("Forwarding port %v -> %v.", p.requestID, portString) + logger := h.logger.With("request_id", p.requestID, "port", portString) + + logger.DebugContext(h.context, "Forwarding port") if err := h.forwardStreamPair(p, port); err != nil { - h.WithError(err).Debugf("Error forwarding port %v -> %v.", p.requestID, portString) + logger.DebugContext(h.context, "Error forwarding port", "error", err) return } - h.Debugf("Completed forwarding port %v -> %v.", p.requestID, portString) + h.logger.DebugContext(h.context, "Completed forwarding port") } // httpStreamPair represents the error and data streams for a port diff --git a/lib/kube/proxy/portforward_test.go b/lib/kube/proxy/portforward_test.go index b923ca591129e..7dc7a147e22b7 100644 --- a/lib/kube/proxy/portforward_test.go +++ b/lib/kube/proxy/portforward_test.go @@ -31,7 +31,6 @@ import ( "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" kubeerrors "k8s.io/apimachinery/pkg/api/errors" @@ -43,6 +42,7 @@ import ( "k8s.io/client-go/transport/spdy" testingkubemock "github.com/gravitational/teleport/lib/kube/proxy/testing/kube_server" + "github.com/gravitational/teleport/lib/utils" ) func TestPortForwardKubeService(t *testing.T) { @@ -269,7 +269,6 @@ type portForwarder interface { // connection, it will leak memory. func TestPortForwardProxy_run_connsClosed(t *testing.T) { t.Parallel() - logger := log.NewEntry(&log.Logger{Out: io.Discard}) const ( reqID = "reqID" // portHeaderValue is the value of the port header in the stream. @@ -285,7 +284,7 @@ func TestPortForwardProxy_run_connsClosed(t *testing.T) { context: context.Background(), onPortForward: func(addr string, success bool) {}, }, - Entry: logger, + logger: utils.NewSlogLoggerForTests(), sourceConn: sourceConn, targetConn: targetConn, streamChan: make(chan httpstream.Stream), diff --git a/lib/kube/proxy/portforward_websocket.go b/lib/kube/proxy/portforward_websocket.go index af1cb04ab525a..c2cb4cb6c97a9 100644 --- a/lib/kube/proxy/portforward_websocket.go +++ b/lib/kube/proxy/portforward_websocket.go @@ -23,13 +23,13 @@ import ( "encoding/binary" "fmt" "io" + "log/slog" "net/http" "strings" "sync" gwebsocket "github.com/gorilla/websocket" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/util/httpstream" spdystream "k8s.io/apimachinery/pkg/util/httpstream/spdy" "k8s.io/apimachinery/pkg/util/httpstream/wsstream" @@ -148,10 +148,10 @@ func runPortForwardingWebSocket(req portForwardRequest) error { podName: req.podName, targetConn: targetConn, onPortForward: req.onPortForward, - FieldLogger: logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.Component(teleport.ComponentProxyKube), - events.RemoteAddr: req.httpRequest.RemoteAddr, - }), + logger: slog.With( + teleport.ComponentKey, teleport.Component(teleport.ComponentProxyKube), + events.RemoteAddr, req.httpRequest.RemoteAddr, + ), context: req.context, } // run the portforward request until termination. @@ -213,8 +213,8 @@ type websocketPortforwardHandler struct { podName string targetConn httpstream.Connection onPortForward portForwardCallback - logrus.FieldLogger - context context.Context + logger *slog.Logger + context context.Context } // run invokes the targetConn SPDY connection and copies the client data into @@ -237,10 +237,12 @@ func (h *websocketPortforwardHandler) run() { // portForward copies the client and upstream streams. func (h *websocketPortforwardHandler) portForward(p *websocketChannelPair) { - h.Debugf("Forwarding port %v -> %v.", p.requestID, p.port) + logger := h.logger.With("request_id", p.requestID, "port", p.port) + + logger.DebugContext(h.context, "Forwarding port") h.forwardStreamPair(p) - h.Debugf("Completed forwarding port %v -> %v.", p.requestID, p.port) + logger.DebugContext(h.context, "Completed forwarding port") } func (h *websocketPortforwardHandler) forwardStreamPair(p *websocketChannelPair) { @@ -269,7 +271,7 @@ func (h *websocketPortforwardHandler) forwardStreamPair(p *websocketChannelPair) go func() { defer wg.Done() if err := utils.ProxyConn(h.context, p.errorStream, targetErrorStream); err != nil { - h.WithError(err).Debugf("Unable to proxy portforward error-stream.") + h.logger.DebugContext(h.context, "Unable to proxy portforward error-stream", "error", err) } }() @@ -292,15 +294,15 @@ func (h *websocketPortforwardHandler) forwardStreamPair(p *websocketChannelPair) go func() { defer wg.Done() if err := utils.ProxyConn(h.context, p.dataStream, targetDataStream); err != nil { - h.WithError(err).Debugf("Unable to proxy portforward data-stream.") + h.logger.DebugContext(h.context, "Unable to proxy portforward data-stream", "error", err) } }() - h.Debugf("Streams have been created, Waiting for copy to complete.") + h.logger.DebugContext(h.context, "Streams have been created, Waiting for copy to complete") // Wait until every goroutine exits. wg.Wait() - h.Debugf("Port forwarding pair completed.") + h.logger.DebugContext(h.context, "Port forwarding pair completed") } // runPortForwardingTunneledHTTPStreams handles a port-forwarding request that uses SPDY protocol @@ -341,10 +343,10 @@ func runPortForwardingTunneledHTTPStreams(req portForwardRequest) error { defer conn.Close() h := &portForwardProxy{ - Entry: logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.Component(teleport.ComponentProxyKube), - events.RemoteAddr: req.httpRequest.RemoteAddr, - }), + logger: slog.With( + teleport.ComponentKey, teleport.Component(teleport.ComponentProxyKube), + events.RemoteAddr, req.httpRequest.RemoteAddr, + ), portForwardRequest: req, sourceConn: spdyConn, streamChan: streamChan, @@ -354,7 +356,7 @@ func runPortForwardingTunneledHTTPStreams(req portForwardRequest) error { } defer h.Close() - h.Debugf("Setting port forwarding streaming connection idle timeout to %s.", req.idleTimeout) + h.logger.DebugContext(context.Background(), "Setting port forwarding streaming connection idle timeout to", "idle_timeout", req.idleTimeout) spdyConn.SetIdleTimeout(req.idleTimeout) h.run() diff --git a/lib/kube/proxy/remotecommand.go b/lib/kube/proxy/remotecommand.go index 09a9c868b43ca..2cd03c870d70b 100644 --- a/lib/kube/proxy/remotecommand.go +++ b/lib/kube/proxy/remotecommand.go @@ -22,12 +22,12 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "strings" "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -75,7 +75,7 @@ func (req remoteCommandRequest) eventPodMeta(ctx context.Context, creds kubeCred // here shouldn't prevent a session from starting. pod, err := creds.getKubeClient().CoreV1().Pods(req.podNamespace).Get(ctx, req.podName, metav1.GetOptions{}) if err != nil { - log.WithError(err).Debugf("Failed fetching pod from kubernetes API; skipping additional metadata on the audit event") + slog.DebugContext(ctx, "Failed fetching pod from kubernetes API; skipping additional metadata on the audit event", "error", err) return meta } meta.KubernetesNodeName = pod.Spec.NodeName @@ -121,7 +121,7 @@ func upgradeRequestToRemoteCommandProxy(req remoteCommandRequest, exec func(*rem err = nil } if err := proxy.sendStatus(err); err != nil { - log.Warningf("Failed to send status: %v", err) + slog.WarnContext(req.context, "Failed to send status", "error", err) } // return rsp=nil, err=nil to indicate that the request has been handled // by the hijacked connection. If we return an error, the request will be @@ -162,10 +162,10 @@ func createSPDYStreams(req remoteCommandRequest) (*remoteCommandProxy, error) { var handler protocolHandler switch protocol { case "": - log.Warningf("Client did not request protocol negotiation.") + slog.WarnContext(ctx, "Client did not request protocol negotiation") fallthrough case StreamProtocolV4Name: - log.Infof("Negotiated protocol %v.", protocol) + slog.InfoContext(ctx, "Negotiated protocol", "protocol", protocol) handler = &v4ProtocolHandler{} default: err = trace.BadParameter("protocol %v is not supported. upgrade the client", protocol) @@ -357,7 +357,7 @@ func (t *termQueue) handleResizeEvents(stream io.Reader) { size := remotecommand.TerminalSize{} if err := decoder.Decode(&size); err != nil { if !errors.Is(err, io.EOF) { - log.Warningf("Failed to decode resize event: %v", err) + slog.WarnContext(t.done, "Failed to decode resize event", "error", err) } t.cancel() return @@ -412,7 +412,7 @@ WaitForStreams: remoteProxy.resizeStream = stream go waitStreamReply(stopCtx, stream.replySent, replyChan) default: - log.Warningf("Ignoring unexpected stream type: %q", streamType) + slog.WarnContext(stopCtx, "Ignoring unexpected stream type", "stream_type", streamType) } case <-replyChan: receivedStreams++ diff --git a/lib/kube/proxy/resource_deletecollection.go b/lib/kube/proxy/resource_deletecollection.go index f73c5f2043426..c3d828f05a6e6 100644 --- a/lib/kube/proxy/resource_deletecollection.go +++ b/lib/kube/proxy/resource_deletecollection.go @@ -21,10 +21,10 @@ package proxy import ( "context" "io" + "log/slog" "net/http" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" oteltrace "go.opentelemetry.io/otel/trace" appsv1 "k8s.io/api/apps/v1" @@ -44,6 +44,7 @@ import ( "github.com/gravitational/teleport/lib/kube/proxy/responsewriters" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/slices" ) // deleteResourcesCollection calls listResources method to list the resources the user @@ -174,7 +175,7 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe items, err := deleteResources( params, types.KindKubePod, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.CoreV1().Pods(namespace).Delete(ctx, name, deleteOptions)) }, @@ -182,12 +183,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *corev1.SecretList: items, err := deleteResources( params, types.KindKubeSecret, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.CoreV1().Secrets(namespace).Delete(ctx, name, deleteOptions)) }, @@ -195,12 +196,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *corev1.ConfigMapList: items, err := deleteResources( params, types.KindKubeConfigmap, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.CoreV1().ConfigMaps(namespace).Delete(ctx, name, deleteOptions)) }, @@ -208,12 +209,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *corev1.NamespaceList: items, err := deleteResources( params, types.KindKubeNamespace, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, _ string) error { return trace.Wrap(client.CoreV1().Namespaces().Delete(ctx, name, deleteOptions)) }, @@ -221,12 +222,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *corev1.ServiceAccountList: items, err := deleteResources( params, types.KindKubeServiceAccount, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.CoreV1().ServiceAccounts(namespace).Delete(ctx, name, deleteOptions)) }, @@ -234,12 +235,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *corev1.PersistentVolumeList: items, err := deleteResources( params, types.KindKubePersistentVolume, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, _ string) error { return trace.Wrap(client.CoreV1().PersistentVolumes().Delete(ctx, name, deleteOptions)) }, @@ -247,13 +248,13 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *corev1.PersistentVolumeClaimList: items, err := deleteResources( params, types.KindKubePersistentVolumeClaim, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.CoreV1().PersistentVolumeClaims(namespace).Delete(ctx, name, deleteOptions)) }, @@ -261,12 +262,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *appsv1.DeploymentList: items, err := deleteResources( params, types.KindKubeDeployment, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.AppsV1().Deployments(namespace).Delete(ctx, name, deleteOptions)) }, @@ -274,12 +275,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *appsv1.ReplicaSetList: items, err := deleteResources( params, types.KindKubeReplicaSet, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.AppsV1().ReplicaSets(namespace).Delete(ctx, name, deleteOptions)) }, @@ -287,13 +288,13 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *appsv1.StatefulSetList: items, err := deleteResources( params, types.KindKubeStatefulset, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.AppsV1().StatefulSets(namespace).Delete(ctx, name, deleteOptions)) }, @@ -301,12 +302,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *appsv1.DaemonSetList: items, err := deleteResources( params, types.KindKubeDaemonSet, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.AppsV1().DaemonSets(namespace).Delete(ctx, name, deleteOptions)) }, @@ -314,13 +315,13 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *authv1.ClusterRoleList: items, err := deleteResources( params, types.KindKubeClusterRole, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, _ string) error { return trace.Wrap(client.RbacV1().ClusterRoles().Delete(ctx, name, deleteOptions)) }, @@ -328,12 +329,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *authv1.RoleList: items, err := deleteResources( params, types.KindKubeRole, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.RbacV1().Roles(namespace).Delete(ctx, name, deleteOptions)) }, @@ -341,12 +342,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *authv1.ClusterRoleBindingList: items, err := deleteResources( params, types.KindKubeClusterRoleBinding, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, _ string) error { return trace.Wrap(client.RbacV1().ClusterRoleBindings().Delete(ctx, name, deleteOptions)) }, @@ -354,12 +355,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *authv1.RoleBindingList: items, err := deleteResources( params, types.KindKubeRoleBinding, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.RbacV1().RoleBindings(namespace).Delete(ctx, name, deleteOptions)) }, @@ -367,12 +368,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *batchv1.CronJobList: items, err := deleteResources( params, types.KindKubeCronjob, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.BatchV1().CronJobs(namespace).Delete(ctx, name, deleteOptions)) }, @@ -380,12 +381,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *batchv1.JobList: items, err := deleteResources( params, types.KindKubeJob, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.BatchV1().Jobs(namespace).Delete(ctx, name, deleteOptions)) }, @@ -393,12 +394,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *certificatesv1.CertificateSigningRequestList: items, err := deleteResources( params, types.KindKubeCertificateSigningRequest, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, _ string) error { return trace.Wrap(client.CertificatesV1().CertificateSigningRequests().Delete(ctx, name, deleteOptions)) }, @@ -406,12 +407,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *networkingv1.IngressList: items, err := deleteResources( params, types.KindKubeIngress, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.NetworkingV1().Ingresses(namespace).Delete(ctx, name, deleteOptions)) }, @@ -419,12 +420,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *extensionsv1beta1.IngressList: items, err := deleteResources( params, types.KindKubeIngress, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.ExtensionsV1beta1().Ingresses(namespace).Delete(ctx, name, deleteOptions)) }, @@ -432,12 +433,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *extensionsv1beta1.DaemonSetList: items, err := deleteResources( params, types.KindKubeDaemonSet, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.ExtensionsV1beta1().DaemonSets(namespace).Delete(ctx, name, deleteOptions)) }, @@ -445,12 +446,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *extensionsv1beta1.DeploymentList: items, err := deleteResources( params, types.KindKubeDeployment, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.ExtensionsV1beta1().Deployments(namespace).Delete(ctx, name, deleteOptions)) }, @@ -458,12 +459,12 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) case *extensionsv1beta1.ReplicaSetList: items, err := deleteResources( params, types.KindKubeReplicaSet, - arrayToPointerArray(o.Items), + slices.ToPointers(o.Items), func(ctx context.Context, client kubernetes.Interface, name, namespace string) error { return trace.Wrap(client.ExtensionsV1beta1().ReplicaSets(namespace).Delete(ctx, name, deleteOptions)) }, @@ -471,7 +472,7 @@ func (f *Forwarder) handleDeleteCollectionReq(req *http.Request, sess *clusterSe if err != nil { return internalErrStatus, trace.Wrap(err) } - o.Items = pointerArrayToArray(items) + o.Items = slices.FromPointers(items) default: return internalErrStatus, trace.BadParameter("unexpected type %T", obj) } @@ -566,7 +567,7 @@ func (f *Forwarder) handleDeleteCustomResourceCollection(w http.ResponseWriter, type deleteResourcesCommonParams struct { ctx context.Context - log logrus.FieldLogger + log *slog.Logger authCtx *authContext header http.Header kubeDetails *kubeDetails diff --git a/lib/kube/proxy/resource_filters.go b/lib/kube/proxy/resource_filters.go index 735aab9648f9d..d52322d751651 100644 --- a/lib/kube/proxy/resource_filters.go +++ b/lib/kube/proxy/resource_filters.go @@ -20,12 +20,13 @@ package proxy import ( "bytes" + "context" "io" + "log/slog" "mime" "net/http" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" certificatesv1 "k8s.io/api/certificates/v1" @@ -42,6 +43,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/kube/proxy/responsewriters" "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/slices" ) // newResourceFilterer creates a wrapper function that once executed creates @@ -50,7 +52,7 @@ import ( // - deniedResources: excluded if (namespace,name) matches an entry even if it matches // the allowedResources's list. // - allowedResources: excluded if (namespace,name) not match a single entry. -func newResourceFilterer(kind, verb string, codecs *serializer.CodecFactory, allowedResources, deniedResources []types.KubernetesResource, log logrus.FieldLogger) responsewriters.FilterWrapper { +func newResourceFilterer(kind, verb string, codecs *serializer.CodecFactory, allowedResources, deniedResources []types.KubernetesResource, log *slog.Logger) responsewriters.FilterWrapper { // If the list of allowed resources contains a wildcard and no deniedResources, then we // don't need to filter anything. if containsWildcard(allowedResources) && len(deniedResources) == 0 { @@ -113,7 +115,7 @@ type resourceFilterer struct { // deniedResources is the list of kubernetes resources the user must not access. deniedResources []types.KubernetesResource // log is the logger. - log logrus.FieldLogger + log *slog.Logger // kind is the type of the resource. kind string // verb is the kube API verb based on HTTP verb. @@ -150,25 +152,6 @@ func (d *resourceFilterer) FilterBuffer(buf []byte, output io.Writer) error { return d.encode(obj, output) } -func arrayToPointerArray[T any](arr []T) []*T { - out := make([]*T, len(arr)) - for i := range arr { - out[i] = &arr[i] - } - return out -} - -func pointerArrayToArray[T any](arr []*T) []T { - out := make([]T, len(arr)) - for i := range arr { - if arr[i] == nil { - continue - } - out[i] = *arr[i] - } - return out -} - // FilterObj receives a runtime.Object type and filters the resources on it // based on allowed and denied rules. // After filtering them, the obj is manipulated to hold the filtered information. @@ -176,6 +159,8 @@ func pointerArrayToArray[T any](arr []*T) []T { // with the object. // The isListObj boolean returned indicates if the object is a list of resources. func (d *resourceFilterer) FilterObj(obj runtime.Object) (isAllowed bool, isList bool, err error) { + ctx := context.Background() + switch o := obj.(type) { case *metav1.Status: // Status object is returned when the Kubernetes API returns an error and @@ -184,376 +169,376 @@ func (d *resourceFilterer) FilterObj(obj runtime.Object) (isAllowed bool, isList case *corev1.Pod: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.PodList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.Secret: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.SecretList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.ConfigMap: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.ConfigMapList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.Namespace: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.NamespaceList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.Service: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.ServiceList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.Endpoints: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.EndpointsList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.ServiceAccount: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.ServiceAccountList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.Node: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.NodeList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.PersistentVolume: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.PersistentVolumeList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *corev1.PersistentVolumeClaim: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *corev1.PersistentVolumeClaimList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *appsv1.Deployment: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *appsv1.DeploymentList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *appsv1.ReplicaSet: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *appsv1.ReplicaSetList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *appsv1.StatefulSet: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *appsv1.StatefulSetList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *appsv1.DaemonSet: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *appsv1.DaemonSetList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *authv1.ClusterRole: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *authv1.ClusterRoleList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *authv1.Role: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *authv1.RoleList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *authv1.ClusterRoleBinding: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *authv1.ClusterRoleBindingList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *authv1.RoleBinding: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *authv1.RoleBindingList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *batchv1.CronJob: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *batchv1.CronJobList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *batchv1.Job: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *batchv1.JobList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *certificatesv1.CertificateSigningRequest: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *certificatesv1.CertificateSigningRequestList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *networkingv1.Ingress: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *networkingv1.IngressList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *extensionsv1beta1.Ingress: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *extensionsv1beta1.IngressList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *extensionsv1beta1.DaemonSet: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *extensionsv1beta1.DaemonSetList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *extensionsv1beta1.Deployment: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *extensionsv1beta1.DeploymentList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil case *extensionsv1beta1.ReplicaSet: result, err := filterResource(d.kind, d.verb, o, d.allowedResources, d.deniedResources) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil case *extensionsv1beta1.ReplicaSetList: - o.Items = pointerArrayToArray( + o.Items = slices.FromPointers( filterResourceList( d.kind, d.verb, - arrayToPointerArray(o.Items), d.allowedResources, d.deniedResources, d.log), + slices.ToPointers(o.Items), d.allowedResources, d.deniedResources, d.log), ) return len(o.Items) > 0, true, nil @@ -569,7 +554,7 @@ func (d *resourceFilterer) FilterObj(obj runtime.Object) (isAllowed bool, isList d.allowedResources, d.deniedResources, ) if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expressions within kubernetes_resources.") + d.log.WarnContext(ctx, "Unable to compile regex expressions within kubernetes_resources", "error", err) } // if err is not nil or result is false, we should not include it. return result, false, nil @@ -632,13 +617,13 @@ func (d *resourceFilterer) encode(obj runtime.Object, w io.Writer) error { } // filterResourceList excludes resources the user should not have access to. -func filterResourceList[T kubeObjectInterface](kind, verb string, originalList []T, allowed, denied []types.KubernetesResource, log logrus.FieldLogger) []T { +func filterResourceList[T kubeObjectInterface](kind, verb string, originalList []T, allowed, denied []types.KubernetesResource, log *slog.Logger) []T { filteredList := make([]T, 0, len(originalList)) for _, resource := range originalList { if result, err := filterResource(kind, verb, resource, allowed, denied); err == nil && result { filteredList = append(filteredList, resource) } else if err != nil { - log.WithError(err).Warnf("Unable to compile regex expressions within kubernetes_resources.") + slog.WarnContext(context.Background(), "Unable to compile regex expressions within kubernetes_resources", "error", err) } } return filteredList @@ -686,7 +671,7 @@ func (d *resourceFilterer) filterMetaV1Table(table *metav1.Table, allowedResourc if result, err := matchKubernetesResource(resource, allowedResources, deniedResources); err == nil && result { resources = append(resources, *row) } else if err != nil { - d.log.WithError(err).Warn("Unable to compile regex expression.") + d.log.WarnContext(context.Background(), "Unable to compile regex expression", "error", err) } } table.Rows = resources @@ -799,7 +784,7 @@ func filterBuffer(filterWrapper responsewriters.FilterWrapper, src *responsewrit // filterUnstructuredList filters the unstructured list object to exclude resources // that the user must not have access to. // The filtered list is re-assigned to `obj.Object["items"]`. -func filterUnstructuredList(verb string, obj *unstructured.Unstructured, allowed, denied []types.KubernetesResource, log logrus.FieldLogger) (hasElems bool) { +func filterUnstructuredList(verb string, obj *unstructured.Unstructured, allowed, denied []types.KubernetesResource, log *slog.Logger) (hasElems bool) { const ( itemsKey = "items" ) @@ -809,7 +794,7 @@ func filterUnstructuredList(verb string, obj *unstructured.Unstructured, allowed objList, err := obj.ToList() if err != nil { // This should never happen, but if it does, we should log it. - log.WithError(err).Warnf("Unable to convert unstructured object to list.") + slog.WarnContext(context.Background(), "Unable to convert unstructured object to list", "error", err) return false } @@ -822,7 +807,7 @@ func filterUnstructuredList(verb string, obj *unstructured.Unstructured, allowed ); result { filteredList = append(filteredList, resource.Object) } else if err != nil { - log.WithError(err).Warnf("Unable to compile regex expressions within kubernetes_resources.") + slog.WarnContext(context.Background(), "Unable to compile regex expressions within kubernetes_resources", "error", err) } } obj.Object[itemsKey] = filteredList diff --git a/lib/kube/proxy/resource_filters_test.go b/lib/kube/proxy/resource_filters_test.go index 3d49563712b81..d193513247345 100644 --- a/lib/kube/proxy/resource_filters_test.go +++ b/lib/kube/proxy/resource_filters_test.go @@ -30,7 +30,6 @@ import ( "text/template" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" @@ -42,10 +41,11 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/kube/proxy/responsewriters" + "github.com/gravitational/teleport/lib/utils" + tslices "github.com/gravitational/teleport/lib/utils/slices" ) func Test_filterBuffer(t *testing.T) { - log := logrus.New() type objectAndAPI struct { obj string api string @@ -175,7 +175,7 @@ func Test_filterBuffer(t *testing.T) { buf, decompress := newMemoryResponseWriter(t, data.Bytes(), tt.args.contentEncoding) - err = filterBuffer(newResourceFilterer(r, types.KubeVerbList, &globalKubeCodecs, allowedResources, nil, log), buf) + err = filterBuffer(newResourceFilterer(r, types.KubeVerbList, &globalKubeCodecs, allowedResources, nil, utils.NewSlogLoggerForTests()), buf) require.NoError(t, err) // Decompress the buffer to compare the result. @@ -188,43 +188,43 @@ func Test_filterBuffer(t *testing.T) { var resources []string switch o := obj.(type) { case *corev1.SecretList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *appsv1.DeploymentList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *appsv1.DaemonSetList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *appsv1.StatefulSetList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *authv1.RoleBindingList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *batchv1.CronJobList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *batchv1.JobList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *corev1.PodList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *corev1.ConfigMapList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *corev1.ServiceAccountList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *appsv1.ReplicaSetList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *corev1.ServiceList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *corev1.PersistentVolumeClaimList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *authv1.RoleList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *networkingv1.IngressList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *extensionsv1beta1.IngressList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *extensionsv1beta1.DaemonSetList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *extensionsv1beta1.ReplicaSetList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *extensionsv1beta1.DeploymentList: - resources = collectResourcesFromResponse(arrayToPointerArray(o.Items)) + resources = collectResourcesFromResponse(tslices.ToPointers(o.Items)) case *metav1.Table: for i := range o.Rows { row := &(o.Rows[i]) diff --git a/lib/kube/proxy/resource_list.go b/lib/kube/proxy/resource_list.go index d0401a600fe5d..f892c91bb8eb5 100644 --- a/lib/kube/proxy/resource_list.go +++ b/lib/kube/proxy/resource_list.go @@ -237,7 +237,7 @@ func (f *Forwarder) sendEphemeralContainerEvents(done <-chan struct{}, req *http podName, ) if err != nil { - f.log.WithError(err).Warn("error getting user ephemeral containers") + f.log.WarnContext(req.Context(), "error getting user ephemeral containers", "error", err) return } @@ -247,7 +247,7 @@ func (f *Forwarder) sendEphemeralContainerEvents(done <-chan struct{}, req *http } evt, err := f.getPatchedPodEvent(req.Context(), sess, wc) if err != nil { - f.log.WithError(err).Warn("error pushing pod event") + f.log.WarnContext(req.Context(), "error pushing pod event", "error", err) continue } sentDebugContainers[wc.Spec.ContainerName] = struct{}{} diff --git a/lib/kube/proxy/resource_rbac_test.go b/lib/kube/proxy/resource_rbac_test.go index 9ee1f0b931824..faebea646681c 100644 --- a/lib/kube/proxy/resource_rbac_test.go +++ b/lib/kube/proxy/resource_rbac_test.go @@ -32,7 +32,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/uuid" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -51,6 +50,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/kube/proxy/responsewriters" testingkubemock "github.com/gravitational/teleport/lib/kube/proxy/testing/kube_server" + "github.com/gravitational/teleport/lib/utils" ) func TestListPodRBAC(t *testing.T) { @@ -518,8 +518,6 @@ func TestListPodRBAC(t *testing.T) { func TestWatcherResponseWriter(t *testing.T) { defaultNamespace := "default" devNamespace := "dev" - log := logrus.New() - log.SetLevel(logrus.DebugLevel) t.Parallel() statusErr := &metav1.Status{ TypeMeta: metav1.TypeMeta{ @@ -633,7 +631,7 @@ func TestWatcherResponseWriter(t *testing.T) { t.Run(tt.name, func(t *testing.T) { userReader, userWriter := io.Pipe() negotiator := newClientNegotiator(&globalKubeCodecs) - filterWrapper := newResourceFilterer(types.KindKubePod, types.KubeVerbWatch, &globalKubeCodecs, tt.args.allowed, tt.args.denied, log) + filterWrapper := newResourceFilterer(types.KindKubePod, types.KubeVerbWatch, &globalKubeCodecs, tt.args.allowed, tt.args.denied, utils.NewSlogLoggerForTests()) // watcher parses the data written into itself and if the user is allowed to // receive the update, it writes the event into target. watcher, err := responsewriters.NewWatcherResponseWriter(newFakeResponseWriter(userWriter) /*target*/, negotiator, filterWrapper) diff --git a/lib/kube/proxy/response_rewriter.go b/lib/kube/proxy/response_rewriter.go index 7fccfe0eb5132..1060762c16b87 100644 --- a/lib/kube/proxy/response_rewriter.go +++ b/lib/kube/proxy/response_rewriter.go @@ -87,7 +87,7 @@ func (f *Forwarder) rewriteResponseForbidden(s *clusterSession) func(r *http.Res newClientNegotiator(&globalKubeCodecs), ) if err != nil { - f.log.WithError(err).Error("Failed to create encoder") + f.log.ErrorContext(r.Request.Context(), "Failed to create encoder", "error", err) return nil } @@ -107,7 +107,7 @@ func (f *Forwarder) rewriteResponseForbidden(s *clusterSession) func(r *http.Res // Encode the new response. if err = encoder.Encode(status, b); err != nil { - f.log.WithError(err).Error("Failed to encode response") + f.log.ErrorContext(r.Request.Context(), "Failed to encode response", "error", err) return trace.Wrap(err) } diff --git a/lib/kube/proxy/roundtrip.go b/lib/kube/proxy/roundtrip.go index 3630f3e898dd7..7fb1bf9c8517a 100644 --- a/lib/kube/proxy/roundtrip.go +++ b/lib/kube/proxy/roundtrip.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net" "net/http" "net/url" @@ -31,7 +32,6 @@ import ( "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -90,7 +90,7 @@ type roundTripperConfig struct { // headers instead of relying on the certificate to transport it. useIdentityForwarding bool // log specifies the logger. - log log.FieldLogger + log *slog.Logger proxier func(*http.Request) (*url.URL, error) } diff --git a/lib/kube/proxy/scheme.go b/lib/kube/proxy/scheme.go index 85d80739ada50..0d88a0fdaef9c 100644 --- a/lib/kube/proxy/scheme.go +++ b/lib/kube/proxy/scheme.go @@ -19,11 +19,12 @@ package proxy import ( + "context" "errors" + "log/slog" "strings" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/exp/maps" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -113,7 +114,7 @@ type gvkSupportedResources map[gvkSupportedResourcesKey]*schema.GroupVersionKind // This schema includes all well-known Kubernetes types and all namespaced // custom resources. // It also returns a map of resources that we support RBAC restrictions for. -func newClusterSchemaBuilder(log logrus.FieldLogger, client kubernetes.Interface) (*serializer.CodecFactory, rbacSupportedResources, gvkSupportedResources, error) { +func newClusterSchemaBuilder(log *slog.Logger, client kubernetes.Interface) (*serializer.CodecFactory, rbacSupportedResources, gvkSupportedResources, error) { kubeScheme := runtime.NewScheme() kubeCodecs := serializer.NewCodecFactory(kubeScheme) supportedResources := maps.Clone(defaultRBACResources) @@ -135,7 +136,10 @@ func newClusterSchemaBuilder(log logrus.FieldLogger, client kubernetes.Interface // reachable. // In this case, we still want to register the other resources that are // available in the cluster. - log.WithError(err).Debugf("Failed to discover some API groups: %v", maps.Keys(discoveryErr.Groups)) + log.DebugContext(context.Background(), "Failed to discover some API groups", + "groups", maps.Keys(discoveryErr.Groups), + "error", err, + ) case err != nil: return nil, nil, nil, trace.Wrap(err) } diff --git a/lib/kube/proxy/scheme_test.go b/lib/kube/proxy/scheme_test.go index d13dbbc94df8f..ae7075db251f4 100644 --- a/lib/kube/proxy/scheme_test.go +++ b/lib/kube/proxy/scheme_test.go @@ -21,17 +21,18 @@ package proxy import ( "testing" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" + + "github.com/gravitational/teleport/lib/utils" ) // TestNewClusterSchemaBuilder tests that newClusterSchemaBuilder doesn't panic // when it's given types already registered in the global scheme. func Test_newClusterSchemaBuilder(t *testing.T) { - _, _, _, err := newClusterSchemaBuilder(logrus.StandardLogger(), &clientSet{}) + _, _, _, err := newClusterSchemaBuilder(utils.NewSlogLoggerForTests(), &clientSet{}) require.NoError(t, err) } diff --git a/lib/kube/proxy/self_subject_reviews.go b/lib/kube/proxy/self_subject_reviews.go index 2130cfdaed034..25fb264a0db38 100644 --- a/lib/kube/proxy/self_subject_reviews.go +++ b/lib/kube/proxy/self_subject_reviews.go @@ -63,7 +63,7 @@ func (f *Forwarder) selfSubjectAccessReviews(authCtx *authContext, w http.Respon if err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to create cluster session: %v.", err) + f.log.ErrorContext(req.Context(), "Failed to create cluster session", "error", err) return nil, trace.Wrap(err) } // sess.Close cancels the connection monitor context to release it sooner. @@ -91,7 +91,7 @@ func (f *Forwarder) selfSubjectAccessReviews(authCtx *authContext, w http.Respon if err := f.setupForwardingHeaders(sess, req, true /* withImpersonationHeaders */); err != nil { // This error goes to kubernetes client and is not visible in the logs // of the teleport server if not logged here. - f.log.Errorf("Failed to set up forwarding headers: %v.", err) + f.log.ErrorContext(req.Context(), "Failed to set up forwarding headers", "error", err) return nil, trace.Wrap(err) } rw := httplib.NewResponseStatusRecorder(w) diff --git a/lib/kube/proxy/server.go b/lib/kube/proxy/server.go index 6f05f2a13a22f..6ac466746b51f 100644 --- a/lib/kube/proxy/server.go +++ b/lib/kube/proxy/server.go @@ -29,7 +29,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/net/http2" "github.com/gravitational/teleport" @@ -68,7 +67,7 @@ type TLSServerConfig struct { // ConnectedProxyGetter gets the proxies teleport is connected to. ConnectedProxyGetter *reversetunnel.ConnectedProxyGetter // Log is the logger. - Log logrus.FieldLogger + Log *slog.Logger // Selectors is a list of resource monitor selectors. ResourceMatchers []services.ResourceMatcher // OnReconcile is called after each kube_cluster resource reconciliation. @@ -134,7 +133,7 @@ func (c *TLSServerConfig) CheckAndSetDefaults() error { } if c.Log == nil { - c.Log = logrus.New() + c.Log = slog.Default() } if c.CloudClients == nil { cloudClients, err := cloud.NewClients() @@ -180,7 +179,7 @@ type TLSServer struct { monitoredKubeClusters monitoredKubeClusters // reconcileCh triggers reconciliation of proxied kube_clusters. reconcileCh chan struct{} - log *logrus.Entry + log *slog.Logger } // NewTLSServer returns new unstarted TLS server @@ -188,9 +187,7 @@ func NewTLSServer(cfg TLSServerConfig) (*TLSServer, error) { if err := cfg.CheckAndSetDefaults(); err != nil { return nil, trace.Wrap(err) } - log := cfg.Log.WithFields(logrus.Fields{ - teleport.ComponentKey: cfg.Component, - }) + log := cfg.Log.With(teleport.ComponentKey, cfg.Component) // limiter limits requests by frequency and amount of simultaneous // connections per client limiter, err := limiter.NewLimiter(cfg.LimiterConfig) @@ -422,8 +419,7 @@ func (t *TLSServer) close(ctx context.Context) error { // and server's GetConfigForClient reloads the list of trusted // local and remote certificate authorities func (t *TLSServer) GetConfigForClient(info *tls.ClientHelloInfo) (*tls.Config, error) { - // TODO(tross): remove slog.Default once the TLSServer is updated to use a slog.Logger - return authclient.WithClusterCAs(t.TLS, t.AccessPoint, t.ClusterName, slog.Default())(info) + return authclient.WithClusterCAs(t.TLS, t.AccessPoint, t.ClusterName, t.log)(info) } // GetServerInfo returns a services.Server object for heartbeats (aka @@ -523,7 +519,7 @@ func (t *TLSServer) startHeartbeat(name string) error { func (t *TLSServer) getRotationState() types.Rotation { rotation, err := t.TLSServerConfig.GetRotation(types.RoleKube) if err != nil && !trace.IsNotFound(err) { - t.log.WithError(err).Warn("Failed to get rotation state.") + t.log.WarnContext(t.closeContext, "Failed to get rotation state", "error", err) } if rotation != nil { return *rotation @@ -539,14 +535,14 @@ func (t *TLSServer) startStaticClustersHeartbeat() error { // proxy_service will pretend to also be kube_server. if t.KubeServiceType == KubeService || t.KubeServiceType == LegacyProxyService { - t.log.Debugf("Starting kubernetes_service heartbeats for %q", t.Component) + t.log.DebugContext(t.closeContext, "Starting kubernetes_service heartbeats") for _, cluster := range t.fwd.kubeClusters() { if err := t.startHeartbeat(cluster.GetName()); err != nil { return trace.Wrap(err) } } } else { - t.log.Debug("No local kube credentials on proxy, will not start kubernetes_service heartbeats") + t.log.DebugContext(t.closeContext, "No local kube credentials on proxy, will not start kubernetes_service heartbeats") } return nil diff --git a/lib/kube/proxy/server_test.go b/lib/kube/proxy/server_test.go index 091f33599c96f..a5a28823080ed 100644 --- a/lib/kube/proxy/server_test.go +++ b/lib/kube/proxy/server_test.go @@ -35,7 +35,6 @@ import ( "time" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/client/proto" @@ -45,6 +44,7 @@ import ( testingkubemock "github.com/gravitational/teleport/lib/kube/proxy/testing/kube_server" "github.com/gravitational/teleport/lib/reversetunnel" "github.com/gravitational/teleport/lib/tlsca" + "github.com/gravitational/teleport/lib/utils" ) func TestServeConfigureError(t *testing.T) { @@ -111,10 +111,9 @@ func TestMTLSClientCAs(t *testing.T) { } hostCert := genCert(t, "localhost", "localhost", "127.0.0.1", "::1") userCert := genCert(t, "user") - log := logrus.New() srv := &TLSServer{ TLSServerConfig: TLSServerConfig{ - Log: log, + Log: utils.NewSlogLoggerForTests(), ForwarderConfig: ForwarderConfig{ ClusterName: mainClusterName, }, @@ -125,7 +124,7 @@ func TestMTLSClientCAs(t *testing.T) { }, GetRotation: func(role types.SystemRole) (*types.Rotation, error) { return &types.Rotation{}, nil }, }, - log: logrus.NewEntry(log), + log: utils.NewSlogLoggerForTests(), } lis, err := net.Listen("tcp", "localhost:0") @@ -207,7 +206,7 @@ func TestGetServerInfo(t *testing.T) { srv := &TLSServer{ TLSServerConfig: TLSServerConfig{ - Log: logrus.New(), + Log: utils.NewSlogLoggerForTests(), ForwarderConfig: ForwarderConfig{ Clock: clockwork.NewFakeClock(), ClusterName: "kube-cluster", diff --git a/lib/kube/proxy/sess.go b/lib/kube/proxy/sess.go index bbf2f308a1a25..f017b59dbd851 100644 --- a/lib/kube/proxy/sess.go +++ b/lib/kube/proxy/sess.go @@ -22,6 +22,7 @@ import ( "context" "fmt" "io" + "log/slog" "net/http" "path" "reflect" @@ -33,7 +34,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/julienschmidt/httprouter" - log "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -55,6 +55,7 @@ import ( tsession "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const sessionRecorderID = "session-recorder" @@ -378,7 +379,7 @@ type session struct { // This is used for audit trails. partiesHistorical map[uuid.UUID]*party - log *log.Entry + log *slog.Logger io *srv.TermManager @@ -440,8 +441,8 @@ type session struct { // newSession creates a new session in pending mode. func newSession(ctx authContext, forwarder *Forwarder, req *http.Request, params httprouter.Params, initiator *party, sess *clusterSession) (*session, error) { id := uuid.New() - log := forwarder.log.WithField("session", id.String()) - log.Debug("Creating session") + log := forwarder.log.With("session", id.String()) + log.DebugContext(req.Context(), "Creating session") var policySets []*types.SessionTrackerPolicySet roles := ctx.Checker.Roles() @@ -502,7 +503,7 @@ func newSession(ctx authContext, forwarder *Forwarder, req *http.Request, params if _, open := <-s.io.TerminateNotifier(); open { err := s.Close() if err != nil { - s.log.Errorf("Failed to close session: %v.", err) + s.log.ErrorContext(req.Context(), "Failed to close session", "error", err) } } }() @@ -518,24 +519,33 @@ func newSession(ctx authContext, forwarder *Forwarder, req *http.Request, params // It is used to properly handle client disconnections. func (s *session) disconnectPartyOnErr(idString string, err error) { if idString == sessionRecorderID { - s.log.Error("Failed to write to session recorder, closing session.") + s.log.ErrorContext(s.sess.connCtx, "Failed to write to session recorder, closing session") s.Close() return } id, uuidParseErr := uuid.Parse(idString) if uuidParseErr != nil { - s.log.WithError(uuidParseErr).Errorf("Unable to decode %q into a UUID.", idString) + s.log.ErrorContext(s.sess.connCtx, "Unable to decode party id", + "party_id", idString, + "error", uuidParseErr, + ) return } wasActive, leaveErr := s.leave(id) if leaveErr != nil { - s.log.WithError(leaveErr).Errorf("Failed to disconnect party %v from the session.", idString) + s.log.ErrorContext(s.sess.connCtx, "Failed to disconnect party from the session", + "party_id", idString, + "error", leaveErr, + ) } if wasActive { // log the error only if it was the reason for the user disconnection. - s.log.Errorf("Encountered error: %v with party %v. Disconnecting them from the session.", err, idString) + s.log.ErrorContext(s.sess.connCtx, "Encountered error with party, disconnecting them from the session", + "error", err, + "party_id", idString, + ) } } @@ -551,11 +561,14 @@ func (s *session) checkPresence() error { } if participant.Mode == string(types.SessionModeratorMode) && time.Now().UTC().After(participant.LastActive.Add(PresenceMaxDifference)) { - s.log.Debugf("Participant %v is not active, kicking.", participant.ID) + s.log.DebugContext(s.sess.connCtx, "Participant is not active, kicking", "participant_id", participant.ID) id, _ := uuid.Parse(participant.ID) _, err := s.unlockedLeave(id) if err != nil { - s.log.WithError(err).Warnf("Failed to kick participant %v for inactivity.", participant.ID) + s.log.WarnContext(s.sess.connCtx, "Failed to kick participant for inactivity", + "participant_id", participant.ID, + "error", err, + ) } } } @@ -569,11 +582,14 @@ func (s *session) launch(ephemeralContainerStatus *corev1.ContainerStatus) (retu defer func() { err := s.Close() if err != nil { - s.log.WithError(err).Errorf("Failed to close session: %v", s.id) + s.log.ErrorContext(s.req.Context(), "Failed to close session", + "session_id", s.id, + "error", err, + ) } }() - s.log.Debugf("Launching session: %v", s.id) + s.log.DebugContext(s.req.Context(), "Launching session", "session_id", s.id) q := s.req.URL.Query() namespace := s.params.ByName("podNamespace") @@ -603,7 +619,7 @@ func (s *session) launch(ephemeralContainerStatus *corev1.ContainerStatus) (retu if returnErr != nil { s.setTerminationErr(returnErr) s.reportErrorToSessionRecorder(returnErr) - s.log.WithError(returnErr).Warning("Executor failed while streaming.") + s.log.WarnContext(s.req.Context(), "Executor failed while streaming", "error", returnErr) } // call onFinished to emit the session.end and exec events. // onFinished is never nil. @@ -640,13 +656,13 @@ func (s *session) launch(ephemeralContainerStatus *corev1.ContainerStatus) (retu }) if err == nil { if err := s.recorder.RecordEvent(s.forwarder.ctx, sessionStartEvent); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to record session start event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to record session start event", "error", err) } if err := s.emitter.EmitAuditEvent(s.forwarder.ctx, sessionStartEvent.GetAuditEvent()); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to emit session start event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to emit session start event", "error", err) } } else { - s.forwarder.log.WithError(err).Warn("Failed to set up session start event - event will not be recorded") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to set up session start event - event will not be recorded", "error", err) } s.weakEventsWaiter.Add(1) @@ -660,21 +676,21 @@ func (s *session) launch(ephemeralContainerStatus *corev1.ContainerStatus) (retu s.BroadcastMessage("Session expired, closing...") err := s.Close() if err != nil { - s.log.WithError(err).Error("Failed to close session") + s.log.ErrorContext(s.forwarder.ctx, "Failed to close session", "error", err) } case <-s.closeC: } }() if err = s.tracker.UpdateState(s.forwarder.ctx, types.SessionState_SessionStateRunning); err != nil { - s.log.WithError(err).Warn("Failed to set tracker state to running") + s.log.WarnContext(s.forwarder.ctx, "Failed to set tracker state to running", "error", err) } var executor remotecommand.Executor executor, err = s.forwarder.getExecutor(s.sess, s.req) if err != nil { - s.log.WithError(err).Warning("Failed creating executor.") + s.log.WarnContext(s.forwarder.ctx, "Failed creating executor", "error", err) return trace.Wrap(err) } @@ -759,7 +775,10 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, eventPodMeta } err := p.Client.resize(termSize.size) if err != nil { - s.log.WithError(err).Errorf("Failed to resize client: %v", id.String()) + s.log.ErrorContext(s.forwarder.ctx, "Failed to resize participant", + "party_id", id.String(), + "error", err, + ) } } @@ -789,10 +808,10 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, eventPodMeta // Report the updated window size to the event log (this is so the sessions // can be replayed correctly). if err := s.recorder.RecordEvent(s.forwarder.ctx, resizeEvent); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to emit terminal resize event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to emit terminal resize event", "error", err) } } else { - s.forwarder.log.WithError(err).Warn("Failed to set up terminal resize event - event will not be recorded") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to set up terminal resize event - event will not be recorded", "error", err) } } } else { @@ -844,7 +863,7 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, eventPodMeta } if err := s.emitter.EmitAuditEvent(s.forwarder.ctx, execEvent); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to emit exec event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to emit exec event", "error", err) } sessionDataEvent := &apievents.SessionData{ @@ -864,7 +883,7 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, eventPodMeta } if err := s.emitter.EmitAuditEvent(s.forwarder.ctx, sessionDataEvent); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to emit session data event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to emit session data event", "error", err) } sessionEndEvent, err := s.recorder.PrepareSessionEvent(&apievents.SessionEnd{ @@ -888,13 +907,13 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, eventPodMeta }) if err == nil { if err := s.recorder.RecordEvent(s.forwarder.ctx, sessionEndEvent); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to record session end event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to record session end event", "error", err) } if err := s.emitter.EmitAuditEvent(s.forwarder.ctx, sessionEndEvent.GetAuditEvent()); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to emit session end event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to emit session end event", "error", err) } } else { - s.forwarder.log.WithError(err).Warn("Failed to set up session end event - event will not be recorded") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to set up session end event - event will not be recorded", "error", err) } } @@ -934,9 +953,9 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, eventPodMeta case <-ticker.C: err := s.checkPresence() if err != nil { - s.log.WithError(err).Error("Failed to check presence, closing session as a security measure") + s.log.ErrorContext(s.forwarder.ctx, "Failed to check presence, closing session as a security measure", "error", err) if err := s.Close(); err != nil { - s.log.WithError(err).Error("Failed to close session") + s.log.ErrorContext(s.forwarder.ctx, "Failed to close session", "error", err) } return } @@ -969,7 +988,7 @@ func (s *session) join(p *party, emitJoinEvent bool) error { return trace.AccessDenied("The requested session is not active") } - s.log.Debugf("Tracking participant: %s", p.ID) + s.log.DebugContext(s.forwarder.ctx, "Tracking participant", "participant_id", p.ID) participant := &types.Participant{ ID: p.ID.String(), User: p.Ctx.User.GetName(), @@ -989,7 +1008,7 @@ func (s *session) join(p *party, emitJoinEvent bool) error { recentWrites := s.io.GetRecentHistory() if _, err := p.Client.stdoutStream().Write(recentWrites); err != nil { - s.log.Warnf("Failed to write history to client: %v.", err) + s.log.WarnContext(s.forwarder.ctx, "Failed to write history to participant", "error", err) } s.BroadcastMessage("User %v joined the session with participant mode: %v.", p.Ctx.User.GetName(), p.Mode) @@ -1009,7 +1028,10 @@ func (s *session) join(p *party, emitJoinEvent bool) error { // other parties' terminals and no discrepancies are present. if lastQueueSize := s.terminalSizeQueue.getLastSize(); lastQueueSize != nil { if err := p.Client.resize(lastQueueSize); err != nil { - s.log.WithError(err).Errorf("Failed to resize client: %v", stringID) + s.log.ErrorContext(s.forwarder.ctx, "Failed to resize participant", + "participant_id", stringID, + "error", err, + ) } } @@ -1022,7 +1044,10 @@ func (s *session) join(p *party, emitJoinEvent bool) error { if p.Ctx.User.GetName() != s.ctx.User.GetName() { err := srv.MsgParticipantCtrls(p.Client.stdoutStream(), p.Mode) if err != nil { - s.log.Errorf("Could not send intro message to participant: %v", err) + s.log.ErrorContext(s.forwarder.ctx, "Could not send intro message to participant", + "error", err, + "participant_id", stringID, + ) } } @@ -1036,10 +1061,10 @@ func (s *session) join(p *party, emitJoinEvent bool) error { case <-c: s.setTerminationErr(sessionTerminatedByModeratorErr) go func() { - s.log.Debugf("Received force termination request") + s.log.DebugContext(s.forwarder.ctx, "Received force termination request") err := s.Close() if err != nil { - s.log.Errorf("Failed to close session: %v.", err) + s.log.ErrorContext(s.forwarder.ctx, "Failed to close session", "error", err) } }() case <-s.closeC: @@ -1064,11 +1089,11 @@ func (s *session) join(p *party, emitJoinEvent bool) error { // we must inform all parties that the session is closing. s.setTerminationErrUnlocked(err) s.reportErrorToSessionRecorder(err) - s.log.WithError(err).Warning("Executor failed while creating ephemeral pod.") + s.log.WarnContext(s.forwarder.ctx, "Executor failed while creating ephemeral pod", "error", err) go func() { err := s.Close() if err != nil { - s.log.WithError(err).Error("Failed to close session") + s.log.ErrorContext(s.forwarder.ctx, "Failed to close session", "error", err) } }() return trace.Wrap(err) @@ -1076,7 +1101,7 @@ func (s *session) join(p *party, emitJoinEvent bool) error { go func() { if err := s.launch(startedEphemeralCont); err != nil { - s.log.WithError(err).Warning("Failed to launch Kubernetes session.") + s.log.WarnContext(s.forwarder.ctx, "Failed to launch Kubernetes session", "error", err) } }() } else if len(s.parties) == 1 { @@ -1098,7 +1123,7 @@ func (s *session) join(p *party, emitJoinEvent bool) error { // types.SessionState_SessionStatePending marks a session that is waiting for // a moderator to rejoin. if err := s.tracker.UpdateState(s.forwarder.ctx, types.SessionState_SessionStateRunning); err != nil { - s.log.Warnf("Failed to set tracker state to %v", types.SessionState_SessionStateRunning) + s.log.WarnContext(s.forwarder.ctx, "Failed to update tracker to running state") } } return nil @@ -1141,7 +1166,7 @@ func (s *session) createEphemeralContainer() (*corev1.ContainerStatus, error) { return nil, trace.Wrap(err) } - s.log.Debugf("Creating ephemeral container %s on pod %s", container, podName) + s.log.DebugContext(s.forwarder.ctx, "Creating ephemeral container on pod", "container", container, "pod", podName) containerStatus, err := s.patchAndWaitForPodEphemeralContainer(s.forwarder.ctx, &initUser.Ctx, s.req.Header, waitingCont) return containerStatus, trace.Wrap(err) } @@ -1179,7 +1204,7 @@ func (s *session) emitSessionJoinEvent(p *party) { } if err := s.emitter.EmitAuditEvent(s.forwarder.ctx, sessionJoinEvent); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to emit event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to emit event", "error", err) } } @@ -1228,10 +1253,10 @@ func (s *session) unlockedLeave(id uuid.UUID) (bool, error) { } if err := s.emitter.EmitAuditEvent(s.forwarder.ctx, sessionLeaveEvent); err != nil { - s.forwarder.log.WithError(err).Warn("Failed to emit event.") + s.forwarder.log.WarnContext(s.forwarder.ctx, "Failed to emit event", "error", err) } - s.log.Debugf("No longer tracking participant: %v", party.ID) + s.log.DebugContext(s.forwarder.ctx, "No longer tracking participant", "participant_id", party.ID) err := s.tracker.RemoveParticipant(s.forwarder.ctx, party.ID.String()) if err != nil { errs = append(errs, trace.Wrap(err)) @@ -1246,7 +1271,7 @@ func (s *session) unlockedLeave(id uuid.UUID) (bool, error) { // close session err := s.Close() if err != nil { - s.log.WithError(err).Errorf("Failed to close session") + s.log.ErrorContext(s.forwarder.ctx, "Failed to close session", "error", err) } }() return true, trace.NewAggregate(errs...) @@ -1267,7 +1292,7 @@ func (s *session) unlockedLeave(id uuid.UUID) (bool, error) { if options.OnLeaveAction == types.OnSessionLeaveTerminate { go func() { if err := s.Close(); err != nil { - s.log.WithError(err).Errorf("Failed to close session") + s.log.ErrorContext(s.forwarder.ctx, "Failed to close session", "error", err) } }() return true, nil @@ -1277,7 +1302,7 @@ func (s *session) unlockedLeave(id uuid.UUID) (bool, error) { s.io.Off() s.BroadcastMessage("Session paused, Waiting for required participants...") if err := s.tracker.UpdateState(s.forwarder.ctx, types.SessionState_SessionStatePending); err != nil { - s.log.Warnf("Failed to set tracker state to %v", types.SessionState_SessionStatePending) + s.log.WarnContext(s.forwarder.ctx, "Failed to set tracker state to pending") } go func() { @@ -1335,7 +1360,7 @@ func (s *session) Close() error { // Once tracker is closed parties cannot join the session. // check session.join for logic. if err := s.tracker.Close(s.forwarder.ctx); err != nil { - s.log.WithError(err).Debug("Failed to close session tracker") + s.log.DebugContext(s.forwarder.ctx, "Failed to close session tracker", "error", err) } s.mu.Lock() terminationErr := s.terminationErr @@ -1345,7 +1370,7 @@ func (s *session) Close() error { } recorder := s.recorder s.mu.Unlock() - s.log.Debugf("Closing session %v.", s.id.String()) + s.log.DebugContext(s.forwarder.ctx, "Closing session", "session_id", logutils.StringerAttr(s.id)) close(s.closeC) // Wait until every party leaves the session and emits the session leave // event before closing the recorder - if available. @@ -1405,18 +1430,18 @@ func (s *session) trackSession(p *party, policySet []*types.SessionTrackerPolicy InitialCommand: command, } - s.log.Debug("Creating session tracker") + s.log.DebugContext(ctx, "Creating session tracker") sessionTrackerService := s.forwarder.cfg.AuthClient tracker, err := srv.NewSessionTracker(ctx, trackerSpec, sessionTrackerService) switch { // there was an error creating the tracker for a moderated session - terminate the session case err != nil && s.accessEvaluator.IsModerated(): - s.log.WithError(err).Warn("Failed to create session tracker, unable to proceed for moderated session") + s.log.WarnContext(ctx, "Failed to create session tracker, unable to proceed for moderated session", "error", err) return trace.Wrap(err) // there was an error creating the tracker for a non-moderated session - permit the session with a local tracker case err != nil && !s.accessEvaluator.IsModerated(): - s.log.Warn("Failed to create session tracker, proceeding with local session tracker for non-moderated session") + s.log.WarnContext(ctx, "Failed to create session tracker, proceeding with local session tracker for non-moderated session") localTracker, err := srv.NewSessionTracker(ctx, trackerSpec, nil) // this error means there are problems with the trackerSpec, we need to return it @@ -1435,7 +1460,7 @@ func (s *session) trackSession(p *party, policySet []*types.SessionTrackerPolicy go func() { if err := s.tracker.UpdateExpirationLoop(s.forwarder.ctx, s.forwarder.cfg.Clock); err != nil { - s.log.WithError(err).Warn("Failed to update session tracker expiration") + s.log.WarnContext(ctx, "Failed to update session tracker expiration", "error", err) } }() @@ -1549,7 +1574,7 @@ func (s *session) retrieveAlreadyStoppedPodLogs(namespace, podName, container st func (s *session) retrieveEphemeralContainerCommand(ctx context.Context, username, containerName string) []string { containers, err := s.forwarder.getUserEphemeralContainersForPod(ctx, username, s.ctx.kubeClusterName, s.podNamespace, s.podName) if err != nil { - s.log.WithError(err).Warn("Failed to retrieve ephemeral containers") + s.log.WarnContext(ctx, "Failed to retrieve ephemeral containers", "error", err) return nil } if len(containers) == 0 { @@ -1569,7 +1594,7 @@ func (s *session) retrieveEphemeralContainerCommand(ctx context.Context, usernam newClientNegotiator(s.sess.codecFactory), ) if err != nil { - s.log.WithError(err).Warn("Failed to create encoder and decoder") + s.log.WarnContext(ctx, "Failed to create encoder and decoder", "error", err) return nil } pod, _, err := s.forwarder.mergeEphemeralPatchWithCurrentPod( @@ -1585,7 +1610,7 @@ func (s *session) retrieveEphemeralContainerCommand(ctx context.Context, usernam }, ) if err != nil { - s.log.WithError(err).Warn("Failed to merge ephemeral patch with current pod") + s.log.WarnContext(ctx, "Failed to merge ephemeral patch with current pod", "error", err) return nil } for _, ephemeral := range pod.Spec.EphemeralContainers { diff --git a/lib/kube/proxy/sess_test.go b/lib/kube/proxy/sess_test.go index 469fe2c4df27a..2c87ec555845d 100644 --- a/lib/kube/proxy/sess_test.go +++ b/lib/kube/proxy/sess_test.go @@ -34,13 +34,11 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/remotecommand" - "github.com/gravitational/teleport" kubewaitingcontainerpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/kubewaitingcontainer/v1" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" @@ -49,6 +47,7 @@ import ( "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/events" testingkubemock "github.com/gravitational/teleport/lib/kube/proxy/testing/kube_server" + "github.com/gravitational/teleport/lib/utils" ) func TestSessionEndError(t *testing.T) { @@ -284,7 +283,7 @@ func Test_session_trackSession(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sess := &session{ - log: logrus.New().WithField(teleport.ComponentKey, "test"), + log: utils.NewSlogLoggerForTests(), id: uuid.New(), req: &http.Request{ URL: &url.URL{ diff --git a/lib/kube/proxy/streamproto/proto.go b/lib/kube/proxy/streamproto/proto.go index e2ee646dfe2b7..605f8e00748c4 100644 --- a/lib/kube/proxy/streamproto/proto.go +++ b/lib/kube/proxy/streamproto/proto.go @@ -19,16 +19,17 @@ package streamproto import ( + "context" "errors" "fmt" "io" + "log/slog" "sync" "sync/atomic" "time" "github.com/gorilla/websocket" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "k8s.io/client-go/tools/remotecommand" "github.com/gravitational/teleport/api/types" @@ -170,7 +171,7 @@ func (s *SessionStream) readTask() { ty, data, err := s.conn.ReadMessage() if err != nil { if !errors.Is(err, io.EOF) && !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseAbnormalClosure, websocket.CloseNoStatusReceived) { - log.WithError(err).Warn("Failed to read message from websocket") + slog.WarnContext(context.Background(), "Failed to read message from websocket", "error", err) } var closeErr *websocket.CloseError @@ -293,7 +294,7 @@ func (s *SessionStream) Close() error { if atomic.CompareAndSwapInt32(&s.closed, 0, 1) { err := s.conn.WriteMessage(websocket.CloseMessage, []byte{}) if err != nil { - log.Warnf("Failed to gracefully close websocket connection: %v", err) + slog.WarnContext(context.Background(), "Failed to gracefully close websocket connection", "error", err) } t := time.NewTimer(time.Second * 5) defer t.Stop() diff --git a/lib/kube/proxy/testing/kube_server/kube_mock.go b/lib/kube/proxy/testing/kube_server/kube_mock.go index 332d7327c6d69..0bdc27eeb4d67 100644 --- a/lib/kube/proxy/testing/kube_server/kube_mock.go +++ b/lib/kube/proxy/testing/kube_server/kube_mock.go @@ -26,6 +26,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/http/httptest" "strings" @@ -36,7 +37,6 @@ import ( gwebsocket "github.com/gorilla/websocket" "github.com/gravitational/trace" "github.com/julienschmidt/httprouter" - log "github.com/sirupsen/logrus" "golang.org/x/net/http2" v1 "k8s.io/api/authorization/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -151,7 +151,7 @@ type KubeUpgradeRequests struct { type KubeMockServer struct { router *httprouter.Router - log *log.Entry + log *slog.Logger server *httptest.Server TLS *tls.Config URL string @@ -178,7 +178,7 @@ type KubeMockServer struct { func NewKubeAPIMock(opts ...Option) (*KubeMockServer, error) { s := &KubeMockServer{ router: httprouter.New(), - log: log.NewEntry(log.New()), + log: slog.Default(), deletedResources: make(map[deletedResource][]string), version: &apimachineryversion.Info{ Major: "1", @@ -273,7 +273,7 @@ func (s *KubeMockServer) writeResponseError(rw http.ResponseWriter, respErr erro status = status.DeepCopy() data, err := runtime.Encode(kubeCodecs.LegacyCodec(), status) if err != nil { - s.log.Warningf("Failed encoding error into kube Status object: %v", err) + s.log.WarnContext(context.Background(), "Failed encoding error into kube Status object", "error", err) trace.WriteError(rw, respErr) return } @@ -283,7 +283,7 @@ func (s *KubeMockServer) writeResponseError(rw http.ResponseWriter, respErr erro // embedded. rw.WriteHeader(int(status.Code)) if _, err := rw.Write(data); err != nil { - s.log.Warningf("Failed writing kube error response body: %v", err) + s.log.WarnContext(context.Background(), "Failed writing kube error response body", "error", err) } } @@ -323,13 +323,13 @@ func (s *KubeMockServer) exec(w http.ResponseWriter, req *http.Request, p httpro if request.stdout { if _, err := proxy.stdoutStream.Write([]byte(request.containerName + "\n")); err != nil { - s.log.WithError(err).Errorf("unable to send to stdout") + s.log.ErrorContext(request.context, "unable to send to stdout", "error", err) } } if request.stderr { if _, err := proxy.stderrStream.Write([]byte(request.containerName + "\n")); err != nil { - s.log.WithError(err).Errorf("unable to send to stderr") + s.log.ErrorContext(request.context, "unable to send to stderr", "error", err) } } @@ -341,7 +341,7 @@ func (s *KubeMockServer) exec(w http.ResponseWriter, req *http.Request, p httpro if errors.Is(err, io.EOF) && n == 0 { break } else if err != nil && n == 0 { - s.log.WithError(err).Errorf("unable to receive from stdin") + s.log.ErrorContext(request.context, "unable to receive from stdin", "error", err) break } @@ -359,13 +359,13 @@ func (s *KubeMockServer) exec(w http.ResponseWriter, req *http.Request, p httpro if request.stdout { if _, err := proxy.stdoutStream.Write(buffer); err != nil { - s.log.WithError(err).Errorf("unable to send to stdout") + s.log.ErrorContext(request.context, "unable to send to stdout", "error", err) } } if request.stderr { if _, err := proxy.stderrStream.Write(buffer); err != nil { - s.log.WithError(err).Errorf("unable to send to stdout") + s.log.ErrorContext(request.context, "unable to send to stdout", "error", err) } } @@ -536,10 +536,10 @@ func createSPDYStreams(req remoteCommandRequest) (*remoteCommandProxy, error) { var handler protocolHandler switch protocol { case "": - log.Warningf("Client did not request protocol negotiation.") + slog.WarnContext(req.context, "Client did not request protocol negotiation.") fallthrough case StreamProtocolV4Name: - log.Infof("Negotiated protocol %v.", protocol) + slog.InfoContext(req.context, "Negotiated protocol", "protocol", protocol) handler = &v4ProtocolHandler{} default: return nil, trace.BadParameter("protocol %v is not supported. upgrade the client", protocol) @@ -641,7 +641,7 @@ func (t *termQueue) handleResizeEvents(stream io.Reader) { size := remotecommand.TerminalSize{} if err := decoder.Decode(&size); err != nil { if !errors.Is(err, io.EOF) { - log.Warningf("Failed to decode resize event: %v", err) + slog.WarnContext(t.done, "Failed to decode resize event", "error", err) } t.cancel() return @@ -696,7 +696,7 @@ WaitForStreams: remoteProxy.resizeStream = stream go waitStreamReply(stopCtx, stream.replySent, replyChan) default: - log.Warningf("Ignoring unexpected stream type: %q", streamType) + slog.WarnContext(stopCtx, "Ignoring unexpected stream type", "stream_type", streamType) } case <-replyChan: receivedStreams++ diff --git a/lib/kube/proxy/utils_testing.go b/lib/kube/proxy/utils_testing.go index 462638df203c4..4f3f596ec0a82 100644 --- a/lib/kube/proxy/utils_testing.go +++ b/lib/kube/proxy/utils_testing.go @@ -35,7 +35,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "k8s.io/client-go/kubernetes" @@ -66,6 +65,7 @@ import ( "github.com/gravitational/teleport/lib/services" sessPkg "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/tlsca" + "github.com/gravitational/teleport/lib/utils" ) type TestContext struct { @@ -226,8 +226,6 @@ func SetupTestContext(ctx context.Context, t *testing.T, cfg TestConfig) *TestCo require.NoError(t, err) testCtx.kubeProxyListener, err = net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err) - log := logrus.New() - log.SetLevel(logrus.DebugLevel) inventoryHandle := inventory.NewDownstreamHandle(client.InventoryControlStream, proto.UpstreamInventoryHello{ ServerID: testCtx.HostID, @@ -281,7 +279,7 @@ func SetupTestContext(ctx context.Context, t *testing.T, cfg TestConfig) *TestCo GetRotation: func(role types.SystemRole) (*types.Rotation, error) { return &types.Rotation{}, nil }, ResourceMatchers: cfg.ResourceMatchers, OnReconcile: cfg.OnReconcile, - Log: log, + Log: utils.NewSlogLoggerForTests(), InventoryHandle: inventoryHandle, }) require.NoError(t, err) @@ -358,7 +356,7 @@ func SetupTestContext(ctx context.Context, t *testing.T, cfg TestConfig) *TestCo LimiterConfig: limiter.Config{ MaxConnections: 1000, }, - Log: log, + Log: utils.NewSlogLoggerForTests(), InventoryHandle: inventoryHandle, GetRotation: func(role types.SystemRole) (*types.Rotation, error) { return &types.Rotation{}, nil diff --git a/lib/kube/proxy/watcher.go b/lib/kube/proxy/watcher.go index 24e52e2d9c923..56bea639d5260 100644 --- a/lib/kube/proxy/watcher.go +++ b/lib/kube/proxy/watcher.go @@ -20,7 +20,6 @@ package proxy import ( "context" - "log/slog" "sync" "time" @@ -37,7 +36,7 @@ import ( // kubernetes clusters according to the up-to-date list of kube_cluster resources. func (s *TLSServer) startReconciler(ctx context.Context) (err error) { if len(s.ResourceMatchers) == 0 || s.KubeServiceType != KubeService { - s.log.Debug("Not initializing Kube Cluster resource watcher.") + s.log.DebugContext(ctx, "Not initializing Kube Cluster resource watcher") return nil } s.reconciler, err = services.NewReconciler(services.ReconcilerConfig[types.KubeCluster]{ @@ -47,8 +46,7 @@ func (s *TLSServer) startReconciler(ctx context.Context) (err error) { OnCreate: s.onCreate, OnUpdate: s.onUpdate, OnDelete: s.onDelete, - // TODO(tross): update to use the server logger once it has been converted to slog - Logger: slog.With("kind", types.KindKubernetesCluster), + Logger: s.log.With("kind", types.KindKubernetesCluster), }) if err != nil { return trace.Wrap(err) @@ -71,16 +69,16 @@ func (s *TLSServer) startReconciler(ctx context.Context) (err error) { select { case <-reconcileTicker.C: if err := s.reconciler.Reconcile(ctx); err != nil { - s.log.WithError(err).Error("Failed to reconcile.") + s.log.ErrorContext(ctx, "Failed to reconcile", "error", err) } case <-s.reconcileCh: if err := s.reconciler.Reconcile(ctx); err != nil { - s.log.WithError(err).Error("Failed to reconcile.") + s.log.ErrorContext(ctx, "Failed to reconcile", "error", err) } else if s.OnReconcile != nil { s.OnReconcile(s.fwd.kubeClusters()) } case <-ctx.Done(): - s.log.Debug("Reconciler done.") + s.log.DebugContext(ctx, "Reconciler done") return } } @@ -92,16 +90,15 @@ func (s *TLSServer) startReconciler(ctx context.Context) (err error) { // registers/unregisters the proxied Kube Cluster accordingly. func (s *TLSServer) startKubeClusterResourceWatcher(ctx context.Context) (*services.GenericWatcher[types.KubeCluster, readonly.KubeCluster], error) { if len(s.ResourceMatchers) == 0 || s.KubeServiceType != KubeService { - s.log.Debug("Not initializing Kube Cluster resource watcher.") + s.log.DebugContext(ctx, "Not initializing Kube Cluster resource watcher") return nil, nil } - s.log.Debug("Initializing Kube Cluster resource watcher.") + s.log.DebugContext(ctx, "Initializing Kube Cluster resource watcher") watcher, err := services.NewKubeClusterWatcher(ctx, services.KubeClusterWatcherConfig{ ResourceWatcherConfig: services.ResourceWatcherConfig{ Component: s.Component, - // TODO(tross): update this once converted to use slog - // Logger: s.log, - Client: s.AccessPoint, + Logger: s.log, + Client: s.AccessPoint, }, KubernetesClusterGetter: s.AccessPoint, }) @@ -120,7 +117,7 @@ func (s *TLSServer) startKubeClusterResourceWatcher(ctx context.Context) (*servi return } case <-ctx.Done(): - s.log.Debug("Kube Cluster resource watcher done.") + s.log.DebugContext(ctx, "Kube Cluster resource watcher done") return } } diff --git a/lib/multiplexer/test/ping.pb.go b/lib/multiplexer/test/ping.pb.go index a78fd7c1a9e71..94cbf8e67b1b9 100644 --- a/lib/multiplexer/test/ping.pb.go +++ b/lib/multiplexer/test/ping.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.1 // protoc (unknown) // source: teleport/lib/multiplexer/test/ping.proto diff --git a/lib/multiplexer/tls.go b/lib/multiplexer/tls.go index 36fa5f138481a..e7fd8f16a2038 100644 --- a/lib/multiplexer/tls.go +++ b/lib/multiplexer/tls.go @@ -114,7 +114,7 @@ func (l *TLSListener) Serve() error { tlsConn, ok := conn.(*tls.Conn) if !ok { conn.Close() - l.log.LogAttrs(l.context, slog.LevelError, "Recevied a non-TLS connection", + l.log.LogAttrs(l.context, slog.LevelError, "Received a non-TLS connection", slog.Any("src_addr", logutils.StringerAttr(conn.RemoteAddr())), slog.Any("dst_addr", logutils.StringerAttr(conn.LocalAddr())), slog.Any("conn_type", logutils.TypeAttr(conn)), diff --git a/lib/multiplexer/web.go b/lib/multiplexer/web.go index d80ac8f570e07..e21db4d5c4065 100644 --- a/lib/multiplexer/web.go +++ b/lib/multiplexer/web.go @@ -120,7 +120,7 @@ func (l *WebListener) Serve() error { tlsConn, ok := conn.(*tls.Conn) if !ok { - l.log.LogAttrs(l.context, slog.LevelError, "Recevied a non-TLS connection", + l.log.LogAttrs(l.context, slog.LevelError, "Received a non-TLS connection", slog.Any("src_addr", logutils.StringerAttr(conn.RemoteAddr())), slog.Any("dst_addr", logutils.StringerAttr(conn.LocalAddr())), slog.Any("conn_type", logutils.TypeAttr(conn)), diff --git a/lib/reversetunnel/agent.go b/lib/reversetunnel/agent.go index 4bd870c3418d6..cc96acdc5a352 100644 --- a/lib/reversetunnel/agent.go +++ b/lib/reversetunnel/agent.go @@ -577,7 +577,7 @@ func (a *agent) handleDrainChannels() error { continue } - a.logger.DebugContext(a.ctx, "Received trransport request", "channel_type", nch.ChannelType()) + a.logger.DebugContext(a.ctx, "Received transport request", "channel_type", nch.ChannelType()) ch, req, err := nch.Accept() if err != nil { a.logger.WarnContext(a.ctx, "Failed to accept transport request", "error", err) diff --git a/lib/reversetunnel/agentpool.go b/lib/reversetunnel/agentpool.go index c4ea000758570..b57f0748cfe72 100644 --- a/lib/reversetunnel/agentpool.go +++ b/lib/reversetunnel/agentpool.go @@ -774,7 +774,7 @@ func (c *agentPoolRuntimeConfig) updateRemote(ctx context.Context, addr *utils.N if c.remoteTLSRoutingEnabled { c.tlsRoutingConnUpgradeRequired = client.IsALPNConnUpgradeRequired(ctx, addr.Addr, lib.IsInsecureDevMode()) slog.DebugContext(ctx, "ALPN upgrade required for remote cluster", - "remot_addr", addr.Addr, + "remote_addr", addr.Addr, "conn_upgrade_required", c.tlsRoutingConnUpgradeRequired, ) } diff --git a/lib/service/certreloader.go b/lib/service/certreloader.go index 3a391427d828e..11da660b6b2d7 100644 --- a/lib/service/certreloader.go +++ b/lib/service/certreloader.go @@ -22,11 +22,11 @@ import ( "context" "crypto/tls" "crypto/x509" + "log/slog" "sync" "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/service/servicecfg" @@ -44,7 +44,7 @@ type CertReloaderConfig struct { // CertReloader periodically reloads a list of cert key-pair paths. // This allows new certificates to be used without a full reload of Teleport. type CertReloader struct { - *log.Entry + logger *slog.Logger // cfg is the certificate reloader configuration. cfg CertReloaderConfig @@ -57,17 +57,15 @@ type CertReloader struct { // NewCertReloader initializes a new certificate reloader. func NewCertReloader(cfg CertReloaderConfig) *CertReloader { return &CertReloader{ - Entry: log.WithFields(log.Fields{ - teleport.ComponentKey: teleport.Component(teleport.ComponentProxy, "certreloader"), - }), - cfg: cfg, + logger: slog.With(teleport.ComponentKey, teleport.Component(teleport.ComponentProxy, "certreloader")), + cfg: cfg, } } // Run tries to load certificates and then spawns the certificate reloader. func (c *CertReloader) Run(ctx context.Context) error { // Synchronously load initially configured certificates. - if err := c.loadCertificates(); err != nil { + if err := c.loadCertificates(ctx); err != nil { return trace.Wrap(err) } @@ -78,9 +76,9 @@ func (c *CertReloader) Run(ctx context.Context) error { // Spawn the certificate reloader. go func() { - c.Infof("Starting periodic reloading of certificate key pairs every %s.", c.cfg.KeyPairsReloadInterval) + c.logger.InfoContext(ctx, "Starting periodic reloading of certificate key pairs", "reload_interval", c.cfg.KeyPairsReloadInterval) defer func() { - c.Info("Stopped periodic reloading of certificate key pairs.") + c.logger.InfoContext(ctx, "Stopped periodic reloading of certificate key pairs") }() t := time.NewTicker(c.cfg.KeyPairsReloadInterval) @@ -88,8 +86,8 @@ func (c *CertReloader) Run(ctx context.Context) error { for { select { case <-t.C: - if err := c.loadCertificates(); err != nil { - c.WithError(err).Warn("Failed to load certificates") + if err := c.loadCertificates(ctx); err != nil { + c.logger.WarnContext(ctx, "Failed to load certificates", "error", err) } case <-ctx.Done(): return @@ -102,10 +100,13 @@ func (c *CertReloader) Run(ctx context.Context) error { // loadCertificates loads certificate keys pairs. // It returns an error if any of the certificate key pairs fails to load. // If any of the key pairs fails to load, none of the certificates are updated. -func (c *CertReloader) loadCertificates() error { +func (c *CertReloader) loadCertificates(ctx context.Context) error { certs := make([]tls.Certificate, 0, len(c.cfg.KeyPairs)) for _, pair := range c.cfg.KeyPairs { - c.Debugf("Loading TLS certificate %v and key %v.", pair.Certificate, pair.PrivateKey) + c.logger.DebugContext(ctx, "Loading TLS certificate", + "public_key", pair.Certificate, + "private_key", pair.PrivateKey, + ) certificate, err := tls.LoadX509KeyPair(pair.Certificate, pair.PrivateKey) if err != nil { diff --git a/lib/service/certreloader_test.go b/lib/service/certreloader_test.go index 0c40c91831687..af424eb3648bf 100644 --- a/lib/service/certreloader_test.go +++ b/lib/service/certreloader_test.go @@ -182,7 +182,7 @@ func TestCertReloader(t *testing.T) { tc.certsUpdate(t, certs) // Perform cert reload. - err = certReloader.loadCertificates() + err = certReloader.loadCertificates(ctx) tc.certsReloadErrorAssert(t, err) // Perform certs assert, passing in the certs before & after the update. diff --git a/lib/service/kubernetes.go b/lib/service/kubernetes.go index c642da43f4669..f25b46e42da4c 100644 --- a/lib/service/kubernetes.go +++ b/lib/service/kubernetes.go @@ -240,7 +240,7 @@ func (process *TeleportProcess) initKubernetesService(logger *slog.Logger, conn StaticLabels: cfg.Kube.StaticLabels, DynamicLabels: dynLabels, CloudLabels: process.cloudLabels, - Log: process.log.WithField(teleport.ComponentKey, teleport.Component(teleport.ComponentKube, process.id)), + Log: process.logger.With(teleport.ComponentKey, teleport.Component(teleport.ComponentKube, process.id)), PROXYProtocolMode: multiplexer.PROXYProtocolOff, // Kube service doesn't need to process unsigned PROXY headers. InventoryHandle: process.inventoryHandle, }) diff --git a/lib/service/service.go b/lib/service/service.go index ebdb99b6a282b..51d171f3737b3 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -149,6 +149,7 @@ import ( secretsscannerproxy "github.com/gravitational/teleport/lib/secretsscanner/proxy" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/services/expiry" "github.com/gravitational/teleport/lib/services/local" "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/srv/alpnproxy" @@ -631,9 +632,6 @@ type TeleportProcess struct { // during in-process reloads. id string - // log is a process-local logrus.Entry. - // Deprecated: use logger instead. - log logrus.FieldLogger // logger is a process-local slog.Logger. logger *slog.Logger @@ -1031,13 +1029,13 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { } if len(cfg.FileDescriptors) == 0 { - cfg.FileDescriptors, err = importFileDescriptors(cfg.Log) + cfg.FileDescriptors, err = importFileDescriptors(cfg.Logger) if err != nil { return nil, trace.Wrap(err) } } - supervisor := NewSupervisor(processID, cfg.Log) + supervisor := NewSupervisor(processID, cfg.Logger) storage, err := storage.NewProcessStorage(supervisor.ExitContext(), filepath.Join(cfg.DataDir, teleport.ComponentProcess)) if err != nil { return nil, trace.Wrap(err) @@ -1183,7 +1181,6 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { storage: storage, rotationCache: rotationCache, id: processID, - log: cfg.Log, logger: cfg.Logger, cloudLabels: cloudLabels, TracingProvider: tracing.NoopProvider(), @@ -2477,6 +2474,25 @@ func (process *TeleportProcess) initAuthService() error { return trace.Wrap(authServer.MonitorSystemTime(process.GracefulExitContext())) }) + expiry, err := expiry.New(&expiry.Config{ + Log: logger.With( + teleport.ComponentKey, teleport.Component(teleport.ComponentAuth, "expiry_service"), + ), + Emitter: authServer, + AccessPoint: authServer.Services, + Clock: process.Clock, + HostID: cfg.HostUUID, + }) + if err != nil { + return trace.Wrap(err) + } + process.RegisterFunc("auth.expiry", func() error { + if err := expiry.Run(process.GracefulExitContext()); err != nil { + logger.ErrorContext(process.GracefulExitContext(), "expiry starting", "error", err) + } + return trace.Wrap(err) + }) + // execute this when process is asked to exit: process.OnExit("auth.shutdown", func(payload any) { // The listeners have to be closed here, because if shutdown @@ -4369,11 +4385,162 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { return trace.Wrap(err) } + // We register the shutdown function before starting the services because we want to run it even if we encounter an + // error and return early. Some of the registered services don't watch if the context is Done (e.g. proxy.web). + // In case of error, if we don't run "proxy.shutdown", those registered services will run ad vitam aeternam and + // Supervisor.Wait() won't return. + var ( + tsrv reversetunnelclient.Server + peerClient *peer.Client + peerQUICTransport *quic.Transport + rcWatcher *reversetunnel.RemoteClusterTunnelManager + peerServer *peer.Server + peerQUICServer *peerquic.Server + webServer *web.Server + minimalWebServer *web.Server + sshProxy *regular.Server + sshGRPCServer *grpc.Server + kubeServer *kubeproxy.TLSServer + grpcServerPublic *grpc.Server + grpcServerMTLS *grpc.Server + alpnServer *alpnproxy.Proxy + reverseTunnelALPNServer *alpnproxy.Proxy + clientTLSConfigGenerator *auth.ClientTLSConfigGenerator + ) + + defer func() { + // execute this when process is asked to exit: + process.OnExit("proxy.shutdown", func(payload interface{}) { + // Close the listeners at the beginning of shutdown, because we are not + // really guaranteed to be capable to serve new requests if we're + // halfway through a shutdown, and double closing a listener is fine. + listeners.Close() + if payload == nil { + logger.InfoContext(process.ExitContext(), "Shutting down immediately") + if tsrv != nil { + warnOnErr(process.ExitContext(), tsrv.Close(), logger) + } + if rcWatcher != nil { + warnOnErr(process.ExitContext(), rcWatcher.Close(), logger) + } + if peerServer != nil { + warnOnErr(process.ExitContext(), peerServer.Close(), logger) + } + if peerQUICServer != nil { + warnOnErr(process.ExitContext(), peerQUICServer.Close(), logger) + } + if webServer != nil { + warnOnErr(process.ExitContext(), webServer.Close(), logger) + } + if minimalWebServer != nil { + warnOnErr(process.ExitContext(), minimalWebServer.Close(), logger) + } + if peerClient != nil { + warnOnErr(process.ExitContext(), peerClient.Stop(), logger) + } + if sshProxy != nil { + warnOnErr(process.ExitContext(), sshProxy.Close(), logger) + } + if sshGRPCServer != nil { + sshGRPCServer.Stop() + } + if kubeServer != nil { + warnOnErr(process.ExitContext(), kubeServer.Close(), logger) + } + if grpcServerPublic != nil { + grpcServerPublic.Stop() + } + if grpcServerMTLS != nil { + grpcServerMTLS.Stop() + } + if alpnServer != nil { + warnOnErr(process.ExitContext(), alpnServer.Close(), logger) + } + if reverseTunnelALPNServer != nil { + warnOnErr(process.ExitContext(), reverseTunnelALPNServer.Close(), logger) + } + + if clientTLSConfigGenerator != nil { + clientTLSConfigGenerator.Close() + } + } else { + logger.InfoContext(process.ExitContext(), "Shutting down gracefully") + ctx := payloadContext(payload) + if tsrv != nil { + warnOnErr(ctx, tsrv.DrainConnections(ctx), logger) + } + if sshProxy != nil { + warnOnErr(ctx, sshProxy.Shutdown(ctx), logger) + } + if sshGRPCServer != nil { + sshGRPCServer.GracefulStop() + } + if webServer != nil { + warnOnErr(ctx, webServer.Shutdown(ctx), logger) + } + if minimalWebServer != nil { + warnOnErr(ctx, minimalWebServer.Shutdown(ctx), logger) + } + if tsrv != nil { + warnOnErr(ctx, tsrv.Shutdown(ctx), logger) + } + if rcWatcher != nil { + warnOnErr(ctx, rcWatcher.Close(), logger) + } + if peerServer != nil { + warnOnErr(ctx, peerServer.Shutdown(), logger) + } + if peerQUICServer != nil { + warnOnErr(ctx, peerQUICServer.Shutdown(ctx), logger) + } + if peerClient != nil { + peerClient.Shutdown(ctx) + } + if kubeServer != nil { + warnOnErr(ctx, kubeServer.Shutdown(ctx), logger) + } + if grpcServerPublic != nil { + grpcServerPublic.GracefulStop() + } + if grpcServerMTLS != nil { + grpcServerMTLS.GracefulStop() + } + if alpnServer != nil { + warnOnErr(ctx, alpnServer.Close(), logger) + } + if reverseTunnelALPNServer != nil { + warnOnErr(ctx, reverseTunnelALPNServer.Close(), logger) + } + + // Explicitly deleting proxy heartbeats helps the behavior of + // reverse tunnel agents during rollouts, as otherwise they'll keep + // trying to reach proxies until the heartbeats expire. + if services.ShouldDeleteServerHeartbeatsOnShutdown(ctx) { + if err := conn.Client.DeleteProxy(ctx, process.Config.HostUUID); err != nil { + if !trace.IsNotFound(err) { + logger.WarnContext(ctx, "Failed to delete heartbeat", "error", err) + } else { + logger.DebugContext(ctx, "Failed to delete heartbeat", "error", err) + } + } + } + + if clientTLSConfigGenerator != nil { + clientTLSConfigGenerator.Close() + } + } + if peerQUICTransport != nil { + _ = peerQUICTransport.Close() + _ = peerQUICTransport.Conn.Close() + } + warnOnErr(process.ExitContext(), asyncEmitter.Close(), logger) + warnOnErr(process.ExitContext(), conn.Close(), logger) + logger.InfoContext(process.ExitContext(), "Exited") + }) + }() + // register SSH reverse tunnel server that accepts connections // from remote teleport nodes - var tsrv reversetunnelclient.Server - var peerClient *peer.Client - var peerQUICTransport *quic.Transport if !process.Config.Proxy.DisableReverseTunnel { if listeners.proxyPeer != nil { if process.Config.Proxy.QUICProxyPeering { @@ -4514,8 +4681,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { // Register web proxy server alpnHandlerForWeb := &alpnproxy.ConnectionHandlerWrapper{} - var webServer *web.Server - var minimalWebServer *web.Server if !process.Config.Proxy.DisableWebService { var fs http.FileSystem @@ -4759,8 +4924,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { } var peerAddrString string - var peerServer *peer.Server - var peerQUICServer *peerquic.Server if !process.Config.Proxy.DisableReverseTunnel && listeners.proxyPeer != nil { peerAddr, err := process.Config.Proxy.PublicPeerAddr() if err != nil { @@ -4853,7 +5016,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { logger.InfoContext(process.ExitContext(), "advertising proxy peering QUIC support") } - sshProxy, err := regular.New( + sshProxy, err = regular.New( process.ExitContext(), cfg.SSH.Addr, cfg.Hostname, @@ -4916,7 +5079,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { } // clientTLSConfigGenerator pre-generates specialized per-cluster client TLS config values - clientTLSConfigGenerator, err := auth.NewClientTLSConfigGenerator(auth.ClientTLSConfigGeneratorConfig{ + clientTLSConfigGenerator, err = auth.NewClientTLSConfigGenerator(auth.ClientTLSConfigGeneratorConfig{ TLS: sshGRPCTLSConfig, ClusterName: clusterName, PermitRemoteClusters: true, @@ -4937,7 +5100,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { return trace.Wrap(err) } - sshGRPCServer := grpc.NewServer( + sshGRPCServer = grpc.NewServer( grpc.ChainUnaryInterceptor( interceptors.GRPCServerUnaryErrorInterceptor, //nolint:staticcheck // SA1019. There is a data race in the stats.Handler that is replacing @@ -5021,7 +5184,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { rcWatchLog := process.logger.With(teleport.ComponentKey, teleport.Component(teleport.ComponentReverseTunnelAgent, process.id)) // Create and register reverse tunnel AgentPool. - rcWatcher, err := reversetunnel.NewRemoteClusterTunnelManager(reversetunnel.RemoteClusterTunnelManagerConfig{ + rcWatcher, err = reversetunnel.NewRemoteClusterTunnelManager(reversetunnel.RemoteClusterTunnelManagerConfig{ HostUUID: conn.HostID(), AuthClient: conn.Client, AccessPoint: accessPoint, @@ -5050,7 +5213,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { return nil }) - var kubeServer *kubeproxy.TLSServer if listeners.kube != nil && !process.Config.Proxy.DisableReverseTunnel { authorizer, err := authz.NewAuthorizer(authz.AuthorizerOpts{ ClusterName: clusterName, @@ -5119,7 +5281,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { AccessPoint: accessPoint, GetRotation: process.GetRotation, OnHeartbeat: process.OnHeartbeat(component), - Log: process.log.WithField(teleport.ComponentKey, teleport.Component(teleport.ComponentReverseTunnelServer, process.id)), + Log: process.logger.With(teleport.ComponentKey, teleport.Component(teleport.ComponentReverseTunnelServer, process.id)), IngressReporter: ingressReporter, KubernetesServersWatcher: kubeServerWatcher, PROXYProtocolMode: cfg.Proxy.PROXYProtocolMode, @@ -5268,10 +5430,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { } } - var ( - grpcServerPublic *grpc.Server - grpcServerMTLS *grpc.Server - ) if alpnRouter != nil { grpcServerPublic, err = process.initPublicGRPCServer(proxyLimiter, conn, listeners.grpcPublic) if err != nil { @@ -5298,8 +5456,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { } } - var alpnServer *alpnproxy.Proxy - var reverseTunnelALPNServer *alpnproxy.Proxy if !cfg.Proxy.DisableTLS && !cfg.Proxy.DisableALPNSNIListener && listeners.web != nil { authDialerService := alpnproxyauth.NewAuthProxyDialerService( tsrv, @@ -5362,123 +5518,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { } } - // execute this when process is asked to exit: - process.OnExit("proxy.shutdown", func(payload interface{}) { - // Close the listeners at the beginning of shutdown, because we are not - // really guaranteed to be capable to serve new requests if we're - // halfway through a shutdown, and double closing a listener is fine. - listeners.Close() - if payload == nil { - logger.InfoContext(process.ExitContext(), "Shutting down immediately.") - if tsrv != nil { - warnOnErr(process.ExitContext(), tsrv.Close(), logger) - } - warnOnErr(process.ExitContext(), rcWatcher.Close(), logger) - if peerServer != nil { - warnOnErr(process.ExitContext(), peerServer.Close(), logger) - } - if peerQUICServer != nil { - warnOnErr(process.ExitContext(), peerQUICServer.Close(), logger) - } - if webServer != nil { - warnOnErr(process.ExitContext(), webServer.Close(), logger) - } - if minimalWebServer != nil { - warnOnErr(process.ExitContext(), minimalWebServer.Close(), logger) - } - if peerClient != nil { - warnOnErr(process.ExitContext(), peerClient.Stop(), logger) - } - warnOnErr(process.ExitContext(), sshProxy.Close(), logger) - sshGRPCServer.Stop() - if kubeServer != nil { - warnOnErr(process.ExitContext(), kubeServer.Close(), logger) - } - if grpcServerPublic != nil { - grpcServerPublic.Stop() - } - if grpcServerMTLS != nil { - grpcServerMTLS.Stop() - } - if alpnServer != nil { - warnOnErr(process.ExitContext(), alpnServer.Close(), logger) - } - if reverseTunnelALPNServer != nil { - warnOnErr(process.ExitContext(), reverseTunnelALPNServer.Close(), logger) - } - - if clientTLSConfigGenerator != nil { - clientTLSConfigGenerator.Close() - } - } else { - logger.InfoContext(process.ExitContext(), "Shutting down gracefully.") - ctx := payloadContext(payload) - if tsrv != nil { - warnOnErr(ctx, tsrv.DrainConnections(ctx), logger) - } - warnOnErr(ctx, sshProxy.Shutdown(ctx), logger) - sshGRPCServer.GracefulStop() - if webServer != nil { - warnOnErr(ctx, webServer.Shutdown(ctx), logger) - } - if minimalWebServer != nil { - warnOnErr(ctx, minimalWebServer.Shutdown(ctx), logger) - } - if tsrv != nil { - warnOnErr(ctx, tsrv.Shutdown(ctx), logger) - } - warnOnErr(ctx, rcWatcher.Close(), logger) - if peerServer != nil { - warnOnErr(ctx, peerServer.Shutdown(), logger) - } - if peerQUICServer != nil { - warnOnErr(ctx, peerQUICServer.Shutdown(ctx), logger) - } - if peerClient != nil { - peerClient.Shutdown(ctx) - } - if kubeServer != nil { - warnOnErr(ctx, kubeServer.Shutdown(ctx), logger) - } - if grpcServerPublic != nil { - grpcServerPublic.GracefulStop() - } - if grpcServerMTLS != nil { - grpcServerMTLS.GracefulStop() - } - if alpnServer != nil { - warnOnErr(ctx, alpnServer.Close(), logger) - } - if reverseTunnelALPNServer != nil { - warnOnErr(ctx, reverseTunnelALPNServer.Close(), logger) - } - - // Explicitly deleting proxy heartbeats helps the behavior of - // reverse tunnel agents during rollouts, as otherwise they'll keep - // trying to reach proxies until the heartbeats expire. - if services.ShouldDeleteServerHeartbeatsOnShutdown(ctx) { - if err := conn.Client.DeleteProxy(ctx, process.Config.HostUUID); err != nil { - if !trace.IsNotFound(err) { - logger.WarnContext(ctx, "Failed to delete heartbeat.", "error", err) - } else { - logger.DebugContext(ctx, "Failed to delete heartbeat.", "error", err) - } - } - } - - if clientTLSConfigGenerator != nil { - clientTLSConfigGenerator.Close() - } - } - if peerQUICTransport != nil { - _ = peerQUICTransport.Close() - _ = peerQUICTransport.Conn.Close() - } - warnOnErr(process.ExitContext(), asyncEmitter.Close(), logger) - warnOnErr(process.ExitContext(), conn.Close(), logger) - logger.InfoContext(process.ExitContext(), "Exited.") - }) - return nil } @@ -6748,7 +6787,7 @@ func (process *TeleportProcess) initSecureGRPCServer(cfg initSecureGRPCServerCfg kubeServer, err := kubegrpc.New(kubegrpc.Config{ AccessPoint: cfg.accessPoint, Authz: authorizer, - Log: process.log, + Log: process.logger, Emitter: cfg.emitter, KubeProxyAddr: cfg.kubeProxyAddr.String(), ClusterName: clusterName, diff --git a/lib/service/service_test.go b/lib/service/service_test.go index 2b7de675666c9..264a9c47c1281 100644 --- a/lib/service/service_test.go +++ b/lib/service/service_test.go @@ -518,7 +518,6 @@ func TestAthenaAuditLogSetup(t *testing.T) { exitContext: context.Background(), }, backend: backend, - log: utils.NewLoggerForTests(), logger: utils.NewSlogLoggerForTests(), } @@ -920,7 +919,7 @@ func TestSetupProxyTLSConfig(t *testing.T) { process := TeleportProcess{ Config: cfg, // Setting Supervisor so that `ExitContext` can be called. - Supervisor: NewSupervisor("process-id", cfg.Log), + Supervisor: NewSupervisor("process-id", cfg.Logger), } tls, err := process.setupProxyTLSConfig( &Connector{}, @@ -1291,7 +1290,7 @@ func TestProxyGRPCServers(t *testing.T) { // Create a new Teleport process to initialize the gRPC servers with KubeProxy // enabled. process := &TeleportProcess{ - Supervisor: NewSupervisor(hostID, utils.NewLoggerForTests()), + Supervisor: NewSupervisor(hostID, utils.NewSlogLoggerForTests()), Config: &servicecfg.Config{ Proxy: servicecfg.ProxyConfig{ Kube: servicecfg.KubeProxyConfig{ @@ -1299,7 +1298,6 @@ func TestProxyGRPCServers(t *testing.T) { }, }, }, - log: utils.NewLoggerForTests(), logger: utils.NewSlogLoggerForTests(), } diff --git a/lib/service/signals.go b/lib/service/signals.go index 8bb32f5675f04..976e0a27beed8 100644 --- a/lib/service/signals.go +++ b/lib/service/signals.go @@ -23,6 +23,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net" "os" "os/exec" @@ -31,7 +32,6 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/lib/defaults" @@ -414,7 +414,7 @@ func (process *TeleportProcess) ExportFileDescriptors() ([]*servicecfg.FileDescr } // importFileDescriptors imports file descriptors from environment if there are any -func importFileDescriptors(log logrus.FieldLogger) ([]*servicecfg.FileDescriptor, error) { +func importFileDescriptors(log *slog.Logger) ([]*servicecfg.FileDescriptor, error) { // These files may be passed in by the parent process filesString := os.Getenv(teleportFilesEnvVar) os.Unsetenv(teleportFilesEnvVar) @@ -428,7 +428,7 @@ func importFileDescriptors(log logrus.FieldLogger) ([]*servicecfg.FileDescriptor } if len(files) != 0 { - log.Infof("Child has been passed files: %v", files) + log.InfoContext(context.Background(), "Child has been passed files", "file_descriptors", files) } return files, nil diff --git a/lib/service/supervisor.go b/lib/service/supervisor.go index 1108f82f584f3..4c06277023628 100644 --- a/lib/service/supervisor.go +++ b/lib/service/supervisor.go @@ -22,15 +22,16 @@ import ( "context" "errors" "fmt" + "log/slog" "sync" "time" "github.com/gravitational/trace" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/observability/metrics" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // Supervisor implements the simple service logic - registering @@ -165,11 +166,11 @@ type LocalSupervisor struct { id string // log specifies the logger - log logrus.FieldLogger + log *slog.Logger } // NewSupervisor returns new instance of initialized supervisor -func NewSupervisor(id string, parentLog logrus.FieldLogger) Supervisor { +func NewSupervisor(id string, parentLog *slog.Logger) Supervisor { ctx := context.TODO() closeContext, cancel := context.WithCancel(ctx) @@ -196,7 +197,7 @@ func NewSupervisor(id string, parentLog logrus.FieldLogger) Supervisor { gracefulExitContext: gracefulExitContext, signalGracefulExit: signalGracefulExit, - log: parentLog.WithField(teleport.ComponentKey, teleport.Component(teleport.ComponentProcess, id)), + log: parentLog.With(teleport.ComponentKey, teleport.Component(teleport.ComponentProcess, id)), } go srv.fanOut() return srv @@ -214,7 +215,7 @@ func (e *Event) String() string { } func (s *LocalSupervisor) Register(srv Service) { - s.log.WithField("service", srv.Name()).Debug("Adding service to supervisor.") + s.log.DebugContext(s.closeContext, "Adding service to supervisor", "service", srv.Name()) s.Lock() defer s.Unlock() s.services = append(s.services, srv) @@ -246,17 +247,17 @@ func (s *LocalSupervisor) RegisterCriticalFunc(name string, fn Func) { // RemoveService removes service from supervisor tracking list func (s *LocalSupervisor) RemoveService(srv Service) error { - l := s.log.WithField("service", srv.Name()) + l := s.log.With("service", srv.Name()) s.Lock() defer s.Unlock() for i, el := range s.services { if el == srv { s.services = append(s.services[:i], s.services[i+1:]...) - l.Debug("Service is completed and removed.") + l.DebugContext(s.closeContext, "Service is completed and removed") return nil } } - l.Warning("Service is completed but not found.") + l.WarnContext(s.closeContext, "Service is completed but not found") return trace.NotFound("service %v is not found", srv) } @@ -302,15 +303,15 @@ func (s *LocalSupervisor) serve(srv Service) { defer metricsServicesRunning.WithLabelValues(label).Dec() } - l := s.log.WithField("service", srv.Name()) - l.Debug("Service has started.") + l := s.log.With("service", srv.Name()) + l.DebugContext(s.closeContext, "Service has started") err := srv.Serve() if err != nil { if errors.Is(err, ErrTeleportExited) { - l.Info("Teleport process has shut down.") + l.InfoContext(s.closeContext, "Teleport process has shut down") } else { if s.ExitContext().Err() == nil { - l.WithError(err).Warning("Teleport process has exited with error.") + l.WarnContext(s.closeContext, "Teleport process has exited with error", "error", err) } s.BroadcastEvent(Event{ Name: ServiceExitedWithErrorEvent, @@ -327,7 +328,7 @@ func (s *LocalSupervisor) Start() error { s.state = stateStarted if len(s.services) == 0 { - s.log.Warning("Supervisor has no services to run. Exiting.") + s.log.WarnContext(s.closeContext, "Supervisor has no services to run - exiting") return nil } @@ -408,7 +409,7 @@ func (s *LocalSupervisor) BroadcastEvent(event Event) { // Log all events other than recovered events to prevent the logs from // being flooded. if event.String() != TeleportOKEvent { - s.log.WithField("event", event.String()).Debug("Broadcasting event.") + s.log.DebugContext(s.closeContext, "Broadcasting event", "event", logutils.StringerAttr(&event)) } go func() { @@ -430,12 +431,12 @@ func (s *LocalSupervisor) BroadcastEvent(event Event) { return } }(mappedEvent) - s.log.WithFields(logrus.Fields{ - "in": event.String(), - "out": m.String(), - }).Debug("Broadcasting mapped event.") + s.log.DebugContext(s.closeContext, "Broadcasting mapped event", + "in", logutils.StringerAttr(&event), + "out", logutils.StringerAttr(m), + ) } else if err != nil { - s.log.Debugf("Teleport not yet ready: %v", err) + s.log.DebugContext(s.closeContext, "Teleport not yet ready", "error", err) } } } diff --git a/lib/services/access_checker.go b/lib/services/access_checker.go index 1d3639bc742c8..acea13514adea 100644 --- a/lib/services/access_checker.go +++ b/lib/services/access_checker.go @@ -22,13 +22,13 @@ import ( "cmp" "context" "fmt" + "log/slog" "net" "slices" "strings" "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "github.com/gravitational/teleport/api/constants" @@ -39,6 +39,7 @@ import ( "github.com/gravitational/teleport/lib/services/readonly" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // AccessChecker interface checks access to resources based on roles, traits, @@ -406,10 +407,11 @@ func (a *accessChecker) checkAllowedResources(r AccessCheckable) error { return nil } - // Note: logging in this function only happens in debug mode. This is because + // Note: logging in this function only happens in trace mode. This is because // adding logging to this function (which is called on every resource returned // by the backend) can slow down this function by 50x for large clusters! - isDebugEnabled, debugf := rbacDebugLogger() + ctx := context.Background() + isLoggingEnabled := rbacLogger.Enabled(ctx, logutils.TraceLevel) for _, resourceID := range a.info.AllowedResourceIDs { if resourceID.ClusterName == a.localCluster && @@ -421,23 +423,33 @@ func (a *accessChecker) checkAllowedResources(r AccessCheckable) error { (resourceID.Kind == r.GetKind() || (slices.Contains(types.KubernetesResourcesKinds, resourceID.Kind) && r.GetKind() == types.KindKubernetesCluster)) && resourceID.Name == r.GetName() { // Allowed to access this resource by resource ID, move on to role checks. - if isDebugEnabled { - debugf("Matched allowed resource ID %q", types.ResourceIDToString(resourceID)) + + if isLoggingEnabled { + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Matched allowed resource ID", + slog.String("resource_id", types.ResourceIDToString(resourceID)), + ) } + return nil } } - if isDebugEnabled { + if isLoggingEnabled { allowedResources, err := types.ResourceIDsToString(a.info.AllowedResourceIDs) if err != nil { return trace.Wrap(err) } - err = trace.AccessDenied("access to %v denied, %q not in allowed resource IDs %s", + + slog.LogAttrs(ctx, logutils.TraceLevel, "Access to resource denied, not in allowed resource IDs", + slog.String("resource_kind", r.GetKind()), + slog.String("resource_name", r.GetName()), + slog.Any("allowed_resources", allowedResources), + ) + + return trace.AccessDenied("access to %v denied, %q not in allowed resource IDs %s", r.GetKind(), r.GetName(), allowedResources) - debugf("Access denied: %v", err) - return err } + return trace.AccessDenied("access to %v denied, not in allowed resource IDs", r.GetKind()) } @@ -875,10 +887,11 @@ func (a *accessChecker) CheckAccessToRemoteCluster(rc types.RemoteCluster) error return trace.AccessDenied("access to cluster denied") } - // Note: logging in this function only happens in debug mode, this is because + // Note: logging in this function only happens in trace mode, this is because // adding logging to this function (which is called on every server returned // by GetRemoteClusters) can slow down this function by 50x for large clusters! - isDebugEnabled, debugf := rbacDebugLogger() + ctx := context.Background() + isLoggingEnabled := rbacLogger.Enabled(ctx, logutils.TraceLevel) rcLabels := rc.GetMetadata().Labels @@ -898,8 +911,10 @@ func (a *accessChecker) CheckAccessToRemoteCluster(rc types.RemoteCluster) error } if !usesLabels && len(rcLabels) == 0 { - debugf("Grant access to cluster %v - no role in %v uses cluster labels and the cluster is not labeled.", - rc.GetName(), a.RoleNames()) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Grant access to cluster - no role uses cluster labels and the cluster is not labeled", + slog.String("cluster_name", rc.GetName()), + slog.Any("roles", a.RoleNames()), + ) return nil } @@ -907,21 +922,24 @@ func (a *accessChecker) CheckAccessToRemoteCluster(rc types.RemoteCluster) error // the deny role set prohibits access. var errs []error for _, role := range a.RoleSet { - matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, rc, isDebugEnabled) + matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, a.info.Traits, rc, isLoggingEnabled) if err != nil { return trace.Wrap(err) } if matchLabels { // This condition avoids formatting calls on large scale. - debugf("Access to cluster %v denied, deny rule in %v matched; match(%s)", - rc.GetName(), role.GetName(), labelsMessage) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Access to cluster denied, deny rule matched", + slog.String("cluster", rc.GetName()), + slog.String("role", role.GetName()), + slog.String("label_message", labelsMessage), + ) return trace.AccessDenied("access to cluster denied") } } // Check allow rules: label has to match in any role in the role set to be granted access. for _, role := range a.RoleSet { - matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, rc, isDebugEnabled) + matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, a.info.Traits, rc, isLoggingEnabled) if err != nil { return trace.Wrap(err) } @@ -929,22 +947,32 @@ func (a *accessChecker) CheckAccessToRemoteCluster(rc types.RemoteCluster) error if err != nil { return trace.Wrap(err) } - debugf("Check access to role(%v) rc(%v, labels=%v) matchLabels=%v, msg=%v, err=%v allow=%v rcLabels=%v", - role.GetName(), rc.GetName(), rcLabels, matchLabels, labelsMessage, err, labelMatchers, rcLabels) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Check access to role", + slog.String("role", role.GetName()), + slog.String("cluster", rc.GetName()), + slog.Any("cluster_labels", rcLabels), + slog.Any("match_labels", matchLabels), + slog.String("labels_message", labelsMessage), + slog.Any("error", err), + slog.Any("allow", labelMatchers), + ) if err != nil { return trace.Wrap(err) } if matchLabels { return nil } - if isDebugEnabled { + if isLoggingEnabled { deniedError := trace.AccessDenied("role=%v, match(%s)", role.GetName(), labelsMessage) errs = append(errs, deniedError) } } - debugf("Access to cluster %v denied, no allow rule matched; %v", rc.GetName(), errs) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Access to cluster denied, no allow rule matched", + slog.String("cluster", rc.GetName()), + slog.Any("error", errs), + ) return trace.AccessDenied("access to cluster denied") } @@ -1095,7 +1123,10 @@ func (a *accessChecker) HostUsers(s types.Server) (*HostUsersInfo, error) { fmt.Fprintf(b, "%s=%v ", shell, roles) } - log.Warnf("Host user shell resolution is ambiguous due to conflicting roles. %q will be used, but consider unifying roles around a single shell. Current shell assignments: %s", shell, b) + slog.WarnContext(context.Background(), "Host user shell resolution is ambiguous due to conflicting roles, consider unifying roles around a single shell", + "selected_shell", shell, + "shell_assignments", b, + ) } for _, role := range a.RoleSet { @@ -1247,8 +1278,11 @@ func AccessInfoFromRemoteCertificate(cert *ssh.Certificate, roleMap types.RoleMa if len(roles) == 0 { return nil, trace.AccessDenied("no roles mapped for user with remote roles %v", unmappedRoles) } - log.Debugf("Mapped remote roles %v to local roles %v and traits %v.", - unmappedRoles, roles, traits) + slog.DebugContext(context.Background(), "Mapped remote roles to local roles and traits", + "remote_roles", unmappedRoles, + "local_roles", roles, + "traits", traits, + ) allowedResourceIDs, err := ExtractAllowedResourcesFromCert(cert) if err != nil { @@ -1281,10 +1315,10 @@ func AccessInfoFromLocalIdentity(identity tlsca.Identity, access UserGetter) (*A return nil, trace.Wrap(err) } - log.Warnf("Failed to find roles in x509 identity for %v. Fetching "+ - "from backend. If the identity provider allows username changes, this can "+ - "potentially allow an attacker to change the role of the existing user.", - identity.Username) + const msg = "Failed to find roles in x509 identity. Fetching " + + "from backend. If the identity provider allows username changes, this can " + + "potentially allow an attacker to change the role of the existing user." + slog.WarnContext(context.Background(), msg, "username", identity.Username) roles = u.GetRoles() traits = u.GetTraits() } @@ -1335,8 +1369,12 @@ func AccessInfoFromRemoteIdentity(identity tlsca.Identity, roleMap types.RoleMap if len(roles) == 0 { return nil, trace.AccessDenied("no roles mapped for remote user %q from cluster %q with remote roles %v", identity.Username, identity.TeleportCluster, unmappedRoles) } - log.Debugf("Mapped roles %v of remote user %q to local roles %v and traits %v.", - unmappedRoles, identity.Username, roles, traits) + slog.DebugContext(context.Background(), "Mapped roles of remote user to local roles and traits", + "remote_roles", unmappedRoles, + "user", identity.Username, + "local_roles", roles, + "traits", traits, + ) allowedResourceIDs := identity.AllowedResourceIDs diff --git a/lib/services/access_request.go b/lib/services/access_request.go index 67da35cde7381..2d5c407f05e48 100644 --- a/lib/services/access_request.go +++ b/lib/services/access_request.go @@ -41,6 +41,7 @@ import ( apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" "github.com/gravitational/teleport/lib/utils/parse" "github.com/gravitational/teleport/lib/utils/typical" ) @@ -2170,10 +2171,9 @@ func (m *RequestValidator) pruneResourceRequestRoles( for _, resourceID := range resourceIDs { if resourceID.ClusterName != localClusterName { - _, debugf := rbacDebugLogger() - debugf("Requested resource %q is in a foreign cluster, unable to prune roles. "+ - `All available "search_as_roles" will be requested.`, - types.ResourceIDToString(resourceID)) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, `Requested resource is in a foreign cluster, unable to prune roles - All available "search_as_roles" will be requested`, + slog.Any("requested_resources", types.ResourceIDToString(resourceID)), + ) return roles, nil } } diff --git a/lib/services/database.go b/lib/services/database.go index a151f2956fc6f..9a3aba1ad9560 100644 --- a/lib/services/database.go +++ b/lib/services/database.go @@ -21,6 +21,7 @@ package services import ( "context" "errors" + "log/slog" "net" "net/netip" "net/url" @@ -28,7 +29,6 @@ import ( "strings" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/mongo/readpref" "go.mongodb.org/mongo-driver/x/mongo/driver/connstring" @@ -277,7 +277,11 @@ func validateMongoDB(db types.Database) error { // DNS errors here by replacing the scheme and then ParseAndValidate again // to validate as much as we can. if isDNSError(err) { - log.Warnf("MongoDB database %q (connection string %q) failed validation with DNS error: %v.", db.GetName(), db.GetURI(), err) + slog.WarnContext(context.Background(), "MongoDB database %q (connection string %q) failed validation with DNS error", + "database_name", db.GetName(), + "database_uri", db.GetURI(), + "error", err, + ) connString, err = connstring.ParseAndValidate(strings.Replace( db.GetURI(), diff --git a/lib/services/expiry/expiry.go b/lib/services/expiry/expiry.go new file mode 100644 index 0000000000000..4b14a3b687f63 --- /dev/null +++ b/lib/services/expiry/expiry.go @@ -0,0 +1,248 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package expiry + +import ( + "context" + "log/slog" + "time" + + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + + "github.com/gravitational/teleport/api/client/proto" + "github.com/gravitational/teleport/api/types" + apievents "github.com/gravitational/teleport/api/types/events" + "github.com/gravitational/teleport/api/utils/retryutils" + "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/utils/interval" +) + +var ( + // scanInterval is the interval at which the expiry checker scans for access requests. + scanInterval = time.Minute * 5 + + // pendingRequestGracePeriod is the grace period used when checking a pending request's expiry + // as the expiry time may be extended on approval. + pendingRequestGracePeriod = time.Second * 40 +) + +const ( + semaphoreName = "auth.expiry" + semaphoreExpiration = time.Minute * 5 + semaphoreJitter = time.Minute + + // minPageDelay is the minimum delay between processing each page of access requests. + minPageDelay = time.Millisecond * 200 + accessRequestPageLimit = 100 + // maxExpiresPerCycle is an arbitrary limit on the number of requests to expire per cycle + // to prevent any one auth server holding the lease for more than a couple of minutes. + maxExpiresPerCycle = 120 +) + +// Config provides configuration for the expiry server. +type Config struct { + // Log is the logger. + Log *slog.Logger + // Emitter is an events emitter, used to submit discrete events. + Emitter apievents.Emitter + // AccessPoint is a expiry access point. + AccessPoint authclient.ExpiryAccessPoint + // Clock is the clock used to calculate expiry. + Clock clockwork.Clock + // HostID is a unique ID of this host. + HostID string +} + +// CheckAndSetDefaults checks required fields and sets default values. +func (c *Config) CheckAndSetDefaults() error { + if c.Emitter == nil { + return trace.BadParameter("no Emitter configured for expiry") + } + if c.AccessPoint == nil { + return trace.BadParameter("no AccessPoint configured for expiry") + } + if c.Clock == nil { + c.Clock = clockwork.NewRealClock() + } + return nil +} + +// Service is a expiry service. +type Service struct { + *Config +} + +// New initializes a expiry service +func New(cfg *Config) (*Service, error) { + if err := cfg.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + + s := &Service{ + Config: cfg, + } + return s, nil +} + +func (s *Service) getSemaphoreConfig() services.SemaphoreLockConfigWithRetry { + return services.SemaphoreLockConfigWithRetry{ + SemaphoreLockConfig: services.SemaphoreLockConfig{ + Service: s.AccessPoint, + Params: types.AcquireSemaphoreRequest{ + SemaphoreKind: types.KindAccessRequest, + SemaphoreName: semaphoreName, + MaxLeases: 1, + Holder: s.HostID, + }, + Expiry: semaphoreExpiration, + Clock: s.Clock, + }, + Retry: retryutils.LinearConfig{ + Clock: s.Clock, + First: time.Second, + Step: semaphoreExpiration / 2, + Max: semaphoreExpiration, + Jitter: retryutils.DefaultJitter, + }, + } +} + +// Run starts the expiry service. +func (s *Service) Run(ctx context.Context) error { + semCfg := s.getSemaphoreConfig() + + poll := interval.New(interval.Config{ + Duration: scanInterval, + FirstDuration: retryutils.FullJitter(scanInterval), + Jitter: retryutils.SeventhJitter, + Clock: s.Clock, + }) + defer poll.Stop() + + for { + lease, err := services.AcquireSemaphoreLockWithRetry(ctx, semCfg) + if err != nil { + s.Log.WarnContext(ctx, "error acquiring semaphore", "error", err) + } else { + if err := s.processRequests(ctx); err != nil { + s.Log.WarnContext(ctx, "error processing access requests", "error", err) + } + lease.Stop() + if err := lease.Wait(); err != nil { + s.Log.WarnContext(ctx, "error cleaning up semaphore", "error", err) + } + } + select { + case <-ctx.Done(): + return nil + case <-poll.Next(): + } + } +} +func (s *Service) processRequests(ctx context.Context) error { + requestsExpired := 0 + nextPageStart := "" + for { + var page []*types.AccessRequestV3 + var err error + // Use time at read when calculating expiry of requests. + readTime := s.Clock.Now() + page, nextPageStart, err = s.getNextPageOfAccessRequests(ctx, nextPageStart) + if err != nil { + return trace.Wrap(err) + } + if len(page) == 0 { + return nil + } + for _, req := range page { + if !s.shouldExpire(req, readTime) { + continue + } + requestsExpired++ + s.Log.InfoContext(ctx, "expiring access request", "request", req.GetName()) + if err := s.expireRequest(ctx, req); err != nil { + s.Log.WarnContext(ctx, "error expiring access request", "error", err) + continue + } + if requestsExpired >= maxExpiresPerCycle { + return nil + } + } + select { + case <-ctx.Done(): + return nil + case <-s.Clock.After(retryutils.SeventhJitter(minPageDelay)): + } + } +} + +func (s *Service) getNextPageOfAccessRequests(ctx context.Context, startKey string) ([]*types.AccessRequestV3, string, error) { + req := &proto.ListAccessRequestsRequest{ + Sort: proto.AccessRequestSort_DEFAULT, + Limit: accessRequestPageLimit, + StartKey: startKey, + } + resp, err := s.AccessPoint.ListAccessRequests(ctx, req) + if err != nil { + return nil, "", trace.Wrap(err) + } + + return resp.AccessRequests, resp.NextKey, nil +} + +func (s *Service) shouldExpire(req types.AccessRequest, readTime time.Time) bool { + expires := req.Expiry() + // Add grace period for pending access requests as expiry time may be extended on approval. + if req.GetState() == types.RequestState_PENDING { + expires = expires.Add(pendingRequestGracePeriod) + } + return readTime.After(expires) +} + +func (s *Service) expireRequest(ctx context.Context, req types.AccessRequest) error { + expiry := req.Expiry() + event := &apievents.AccessRequestExpire{ + Metadata: apievents.Metadata{ + Type: events.AccessRequestExpireEvent, + Code: events.AccessRequestExpireCode, + }, + ResourceMetadata: apievents.ResourceMetadata{ + Expires: req.GetAccessExpiry(), + }, + RequestID: req.GetName(), + ResourceExpiry: &expiry, + } + // Emit expiry event before deletion as event we know event is expired here + // but the deletion may fail. + if err := s.Emitter.EmitAuditEvent(ctx, event); err != nil { + return trace.Wrap(err) + } + + if err := s.AccessPoint.DeleteAccessRequest(ctx, req.GetName()); err != nil { + if trace.IsNotFound(err) { + s.Log.InfoContext(ctx, "access request was already deleted", "request", req.GetName()) + return nil + } + return trace.Wrap(err) + } + return nil +} diff --git a/lib/services/expiry/expiry_test.go b/lib/services/expiry/expiry_test.go new file mode 100644 index 0000000000000..c9ce9a076a99a --- /dev/null +++ b/lib/services/expiry/expiry_test.go @@ -0,0 +1,94 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package expiry + +import ( + "context" + "testing" + "time" + + "github.com/google/uuid" + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/events/eventstest" + "github.com/gravitational/teleport/lib/utils" +) + +func TestExpiry(t *testing.T) { + clock := clockwork.NewFakeClock() + + authServer, err := auth.NewTestAuthServer(auth.TestAuthServerConfig{ + Dir: t.TempDir(), + Clock: clock, + AuthPreferenceSpec: &types.AuthPreferenceSpecV2{ + SecondFactor: constants.SecondFactorOn, + Webauthn: &types.Webauthn{ + RPID: "localhost", + }, + }, + }) + + require.NoError(t, err) + t.Cleanup(func() { authServer.Close() }) + + logger := utils.NewSlogLoggerForTests() + mockEmitter := &eventstest.MockRecorderEmitter{} + cfg := &Config{ + Log: logger, + Emitter: mockEmitter, + AccessPoint: authServer.AuthServer, + Clock: clock, + } + + ctx := context.Background() + + expiry, err := New(cfg) + require.NoError(t, err) + + scanInterval = time.Second + pendingRequestGracePeriod = time.Second + + go func() { + err := expiry.Run(ctx) + require.NoError(t, err) + }() + + req1Name := uuid.New().String() + req1, err := types.NewAccessRequest(req1Name, "someUser", "someRole") + require.NoError(t, err) + req1.SetExpiry(clock.Now().Add(scanInterval)) + err = authServer.AuthServer.CreateAccessRequest(ctx, req1) + require.NoError(t, err) + + req2Name := uuid.New().String() + req2, err := types.NewAccessRequest(req2Name, "someUser", "someRole") + require.NoError(t, err) + req2.SetExpiry(clock.Now().Add(scanInterval * 2)) + err = authServer.AuthServer.CreateAccessRequest(ctx, req2) + require.NoError(t, err) + + require.Eventually(t, func() bool { + clock.Advance(scanInterval * 5) + return len(mockEmitter.Events()) == 2 + }, scanInterval*5, time.Second/2) +} diff --git a/lib/services/local/access.go b/lib/services/local/access.go index eb467e76ed815..55f53a1715268 100644 --- a/lib/services/local/access.go +++ b/lib/services/local/access.go @@ -20,11 +20,11 @@ package local import ( "context" + "log/slog" "strings" "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" @@ -36,14 +36,14 @@ import ( // AccessService manages roles type AccessService struct { backend.Backend - log *logrus.Entry + logger *slog.Logger } // NewAccessService returns new access service instance func NewAccessService(backend backend.Backend) *AccessService { return &AccessService{ Backend: backend, - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "AccessService"}), + logger: slog.With(teleport.ComponentKey, "AccessService"), } } @@ -124,7 +124,10 @@ func (s *AccessService) ListRoles(ctx context.Context, req *proto.ListRolesReque services.WithRevision(item.Revision), ) if err != nil { - s.log.Warnf("Failed to unmarshal role at %q: %v", item.Key, err) + s.logger.WarnContext(ctx, "Failed to unmarshal role", + "key", item.Key, + "error", err, + ) continue } diff --git a/lib/services/local/access_list.go b/lib/services/local/access_list.go index 611d9de23f094..955b691d503bc 100644 --- a/lib/services/local/access_list.go +++ b/lib/services/local/access_list.go @@ -27,9 +27,7 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" - "github.com/gravitational/teleport" accesslistv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" @@ -75,7 +73,6 @@ const ( // consistent view to the rest of the Teleport application. It makes no decisions // about granting or withholding list membership. type AccessListService struct { - log logrus.FieldLogger clock clockwork.Clock service *generic.Service[*accesslist.AccessList] memberService *generic.Service[*accesslist.AccessListMember] @@ -144,7 +141,6 @@ func NewAccessListService(b backend.Backend, clock clockwork.Clock, opts ...Serv } return &AccessListService{ - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "access-list:local-service"}), clock: clock, service: service, memberService: memberService, diff --git a/lib/services/local/dynamic_access.go b/lib/services/local/dynamic_access.go index e11aa5a51b16a..6ed3af08de8d1 100644 --- a/lib/services/local/dynamic_access.go +++ b/lib/services/local/dynamic_access.go @@ -20,11 +20,11 @@ package local import ( "context" + "log/slog" "slices" "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" @@ -38,14 +38,14 @@ import ( // DynamicAccessService manages dynamic RBAC type DynamicAccessService struct { backend.Backend - log *logrus.Entry + logger *slog.Logger } // NewDynamicAccessService returns new dynamic access service instance func NewDynamicAccessService(backend backend.Backend) *DynamicAccessService { return &DynamicAccessService{ Backend: backend, - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "DynamicAccess"}), + logger: slog.With(teleport.ComponentKey, "DynamicAccess"), } } @@ -348,7 +348,10 @@ func (s *DynamicAccessService) ListAccessRequests(ctx context.Context, req *prot accessRequest, err := itemToAccessRequest(item) if err != nil { - s.log.Warnf("Failed to unmarshal access request at %q: %v", item.Key, err) + s.logger.WarnContext(ctx, "Failed to unmarshal access request", + "key", item.Key, + "error", err, + ) continue } @@ -448,7 +451,6 @@ func itemFromAccessRequest(req types.AccessRequest) (backend.Item, error) { return backend.Item{ Key: accessRequestKey(req.GetName()), Value: value, - Expires: req.Expiry(), Revision: rev, }, nil } @@ -469,7 +471,6 @@ func itemFromAccessListPromotions(req types.AccessRequest, suggestedItems *types func itemToAccessRequest(item backend.Item, opts ...services.MarshalOption) (*types.AccessRequestV3, error) { opts = append( opts, - services.WithExpires(item.Expires), services.WithRevision(item.Revision), ) req, err := services.UnmarshalAccessRequest( diff --git a/lib/services/local/events.go b/lib/services/local/events.go index 9931b80857500..58e56ada292a4 100644 --- a/lib/services/local/events.go +++ b/lib/services/local/events.go @@ -20,11 +20,11 @@ package local import ( "context" + "log/slog" "strings" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" apidefaults "github.com/gravitational/teleport/api/defaults" @@ -48,14 +48,14 @@ import ( // EventsService implements service to watch for events type EventsService struct { - *logrus.Entry + logger *slog.Logger backend backend.Backend } // NewEventsService returns new events service instance func NewEventsService(b backend.Backend) *EventsService { return &EventsService{ - Entry: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "Events"}), + logger: slog.With(teleport.ComponentKey, "Events"), backend: b, } } @@ -287,13 +287,13 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type if err != nil { return nil, trace.Wrap(err) } - return newWatcher(w, e.Entry, parsers, validKinds), nil + return newWatcher(w, e.logger, parsers, validKinds), nil } -func newWatcher(backendWatcher backend.Watcher, l *logrus.Entry, parsers []resourceParser, kinds []types.WatchKind) *watcher { +func newWatcher(backendWatcher backend.Watcher, l *slog.Logger, parsers []resourceParser, kinds []types.WatchKind) *watcher { w := &watcher{ backendWatcher: backendWatcher, - Entry: l, + logger: l, parsers: parsers, eventsC: make(chan types.Event), kinds: kinds, @@ -303,7 +303,7 @@ func newWatcher(backendWatcher backend.Watcher, l *logrus.Entry, parsers []resou } type watcher struct { - *logrus.Entry + logger *slog.Logger parsers []resourceParser backendWatcher backend.Watcher eventsC chan types.Event @@ -350,7 +350,7 @@ func (w *watcher) forwardEvents() { // node events as well, and there could be no // handler registered for nodes, only for namespaces if !trace.IsNotFound(err) { - w.Warning(trace.DebugReport(err)) + w.logger.WarnContext(context.Background(), "failed parsing event", "error", err) } } for _, c := range converted { @@ -2944,7 +2944,7 @@ func WaitForEvent(ctx context.Context, watcher types.Watcher, m EventMatcher, cl return res, nil } if !trace.IsCompareFailed(err) { - logrus.WithError(err).Debug("Failed to match event.") + slog.DebugContext(ctx, "Failed to match event", "error", err) } case <-watcher.Done(): // Watcher closed, probably due to a network error. diff --git a/lib/services/local/externalauditstorage.go b/lib/services/local/externalauditstorage.go index 96cd050bfd5be..fdb59e827d03a 100644 --- a/lib/services/local/externalauditstorage.go +++ b/lib/services/local/externalauditstorage.go @@ -22,9 +22,7 @@ import ( "context" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" - "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types/externalauditstorage" "github.com/gravitational/teleport/api/types/header" "github.com/gravitational/teleport/lib/backend" @@ -45,14 +43,12 @@ var ( // ExternalAuditStorageService manages External Audit Storage resources in the Backend. type ExternalAuditStorageService struct { backend backend.Backend - logger *logrus.Entry } // NewExternalAuditStorageService returns a new *ExternalAuditStorageService or an error if it fails. func NewExternalAuditStorageService(backend backend.Backend) *ExternalAuditStorageService { return &ExternalAuditStorageService{ backend: backend, - logger: logrus.WithField(teleport.ComponentKey, "ExternalAuditStorage.backend"), } } diff --git a/lib/services/local/externalauditstorage_watcher.go b/lib/services/local/externalauditstorage_watcher.go index 633e8a5c53f3e..f962e2e5cdcce 100644 --- a/lib/services/local/externalauditstorage_watcher.go +++ b/lib/services/local/externalauditstorage_watcher.go @@ -21,11 +21,11 @@ package local import ( "context" "errors" + "log/slog" "sync" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" @@ -38,8 +38,8 @@ import ( type ClusterExternalAuditStorageWatcherConfig struct { // Backend is the storage backend used to create watchers. Backend backend.Backend - // Log is a logger. - Log logrus.FieldLogger + // Logger is a logger. + Logger *slog.Logger // Clock is used to control time. Clock clockwork.Clock // OnChange is the action to take when the cluster ExternalAuditStorage @@ -52,8 +52,8 @@ func (cfg *ClusterExternalAuditStorageWatcherConfig) CheckAndSetDefaults() error if cfg.Backend == nil { return trace.BadParameter("missing parameter Backend") } - if cfg.Log == nil { - cfg.Log = logrus.StandardLogger().WithField(teleport.ComponentKey, "ExternalAuditStorage.watcher") + if cfg.Logger == nil { + cfg.Logger = slog.With(teleport.ComponentKey, "ExternalAuditStorage.watcher") } if cfg.Clock == nil { cfg.Clock = cfg.Backend.Clock() @@ -67,7 +67,7 @@ func (cfg *ClusterExternalAuditStorageWatcherConfig) CheckAndSetDefaults() error // ClusterExternalAuditWatcher is a light weight backend watcher for the cluster external audit resource. type ClusterExternalAuditWatcher struct { backend backend.Backend - log logrus.FieldLogger + logger *slog.Logger clock clockwork.Clock onChange func() retry retryutils.Retry @@ -97,7 +97,7 @@ func NewClusterExternalAuditWatcher(ctx context.Context, cfg ClusterExternalAudi w := &ClusterExternalAuditWatcher{ backend: cfg.Backend, - log: cfg.Log, + logger: cfg.Logger, clock: cfg.Clock, onChange: cfg.OnChange, retry: retry, @@ -137,7 +137,10 @@ func (w *ClusterExternalAuditWatcher) runWatchLoop(ctx context.Context) { startedWaiting := w.clock.Now() select { case t := <-w.retry.After(): - w.log.Warningf("Restarting watch on error after waiting %v. Error: %v.", t.Sub(startedWaiting), err) + w.logger.WarnContext(ctx, "Restarting watch on error", + "backoff", t.Sub(startedWaiting), + "error", err, + ) w.retry.Inc() case <-ctx.Done(): return @@ -156,7 +159,7 @@ func (w *ClusterExternalAuditWatcher) watch(ctx context.Context) error { for { select { case <-watcher.Events(): - w.log.Infof("Detected change to cluster ExternalAuditStorage config") + w.logger.InfoContext(ctx, "Detected change to cluster ExternalAuditStorage config") w.onChange() case w.running <- struct{}{}: case <-watcher.Done(): diff --git a/lib/services/local/headlessauthn_watcher.go b/lib/services/local/headlessauthn_watcher.go index d3ded3d60439c..dee036fa363df 100644 --- a/lib/services/local/headlessauthn_watcher.go +++ b/lib/services/local/headlessauthn_watcher.go @@ -21,12 +21,12 @@ package local import ( "context" "errors" + "log/slog" "sync" "time" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" @@ -51,8 +51,8 @@ var ErrHeadlessAuthenticationWatcherClosed = errors.New("headless authentication type HeadlessAuthenticationWatcherConfig struct { // Backend is the storage backend used to create watchers. Backend backend.Backend - // Log is a logger. - Log logrus.FieldLogger + // Logger is a logger. + Logger *slog.Logger // Clock is used to control time. Clock clockwork.Clock // MaxRetryPeriod is the maximum retry period on failed watchers. @@ -64,9 +64,8 @@ func (cfg *HeadlessAuthenticationWatcherConfig) CheckAndSetDefaults() error { if cfg.Backend == nil { return trace.BadParameter("missing parameter Backend") } - if cfg.Log == nil { - cfg.Log = logrus.StandardLogger() - cfg.Log.WithField("resource-kind", types.KindHeadlessAuthentication) + if cfg.Logger == nil { + cfg.Logger = slog.With("resource_kind", types.KindHeadlessAuthentication) } if cfg.MaxRetryPeriod == 0 { // On watcher failure, we eagerly retry in order to avoid login delays. @@ -159,12 +158,15 @@ func (h *HeadlessAuthenticationWatcher) runWatchLoop(ctx context.Context) { startedWaiting := h.Clock.Now() select { case t := <-h.retry.After(): - h.Log.Warningf("Restarting watch on error after waiting %v. Error: %v.", t.Sub(startedWaiting), err) + h.Logger.WarnContext(ctx, "Restarting watch on error", + "backoff", t.Sub(startedWaiting), + "error", err, + ) h.retry.Inc() case <-ctx.Done(): return case <-h.closed: - h.Log.Debug("Watcher closed. Returning from watch loop.") + h.Logger.DebugContext(ctx, "Watcher closed, terminating watch loop") return } } @@ -191,7 +193,7 @@ func (h *HeadlessAuthenticationWatcher) watch(ctx context.Context) error { case types.OpPut: headlessAuthn, err := unmarshalHeadlessAuthenticationFromItem(&event.Item) if err != nil { - h.Log.WithError(err).Debug("failed to unmarshal headless authentication from put event") + h.Logger.DebugContext(ctx, "failed to unmarshal headless authentication from put event", "error", err) } else { h.notify(headlessAuthn) } diff --git a/lib/services/local/inventory.go b/lib/services/local/inventory.go index 2b488e49e58aa..0eba8cadd618b 100644 --- a/lib/services/local/inventory.go +++ b/lib/services/local/inventory.go @@ -54,11 +54,17 @@ func (s *PresenceService) GetInstances(ctx context.Context, req types.InstanceFi return stream.FilterMap(items, func(item backend.Item) (types.Instance, bool) { instance, err := generic.FastUnmarshal[*types.InstanceV1](item) if err != nil { - s.log.Warnf("Skipping instance at %s, failed to unmarshal: %v", item.Key, err) + s.logger.WarnContext(ctx, "Skipping instance failed to unmarshal", + "key", item.Key, + "error", err, + ) return nil, false } if err := instance.CheckAndSetDefaults(); err != nil { - s.log.Warnf("Skipping instance at %s: %v", item.Key, err) + s.logger.WarnContext(ctx, "Skipping invalid instance", + "key", item.Key, + "error", err, + ) return nil, false } if !req.Match(instance) { diff --git a/lib/services/local/okta.go b/lib/services/local/okta.go index b1ab4e8f3b65d..dfbca6b4d29ee 100644 --- a/lib/services/local/okta.go +++ b/lib/services/local/okta.go @@ -24,9 +24,7 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" - "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/services" @@ -43,7 +41,6 @@ const ( // OktaService manages Okta resources in the Backend. type OktaService struct { - log logrus.FieldLogger clock clockwork.Clock importRuleSvc *generic.Service[types.OktaImportRule] assignmentSvc *generic.Service[types.OktaAssignment] @@ -76,7 +73,6 @@ func NewOktaService(b backend.Backend, clock clockwork.Clock) (*OktaService, err } return &OktaService{ - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "okta:local-service"}), clock: clock, importRuleSvc: importRuleSvc, assignmentSvc: assignmentSvc, diff --git a/lib/services/local/presence.go b/lib/services/local/presence.go index ff95884151b34..520eae0f33c87 100644 --- a/lib/services/local/presence.go +++ b/lib/services/local/presence.go @@ -26,7 +26,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" @@ -45,7 +44,7 @@ import ( // PresenceService records and reports the presence of all components // of the cluster - Nodes, Proxies and SSH nodes type PresenceService struct { - log *logrus.Entry + logger *slog.Logger jitter retryutils.Jitter backend.Backend } @@ -57,7 +56,7 @@ type backendItemToResourceFunc func(item backend.Item) (types.ResourceWithLabels // NewPresenceService returns new presence service instance func NewPresenceService(b backend.Backend) *PresenceService { return &PresenceService{ - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "Presence"}), + logger: slog.With(teleport.ComponentKey, "Presence"), jitter: retryutils.FullJitter, Backend: b, } @@ -160,7 +159,10 @@ func (s *PresenceService) GetServerInfos(ctx context.Context) stream.Stream[type services.WithRevision(item.Revision), ) if err != nil { - s.log.Warnf("Skipping server info at %s, failed to unmarshal: %v", item.Key, err) + s.logger.WarnContext(ctx, "Failed to unmarshal server info", + "key", item.Key, + "error", err, + ) return nil, false } return si, true diff --git a/lib/services/local/saml_idp_service_provider.go b/lib/services/local/saml_idp_service_provider.go index 6b08cf084afd9..eadeb347367ab 100644 --- a/lib/services/local/saml_idp_service_provider.go +++ b/lib/services/local/saml_idp_service_provider.go @@ -22,6 +22,7 @@ import ( "context" "encoding/xml" "fmt" + "log/slog" "net/http" "net/url" "time" @@ -29,7 +30,6 @@ import ( "github.com/crewjam/saml" "github.com/crewjam/saml/samlsp" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" @@ -55,7 +55,7 @@ type SAMLIdPServiceProviderService struct { // backend is used to spawn Plugins storage service so that // it can be queried from the SAML service. backend backend.Backend - log logrus.FieldLogger + logger *slog.Logger httpClient *http.Client } @@ -86,7 +86,7 @@ func NewSAMLIdPServiceProviderService(b backend.Backend, opts ...SAMLIdPOption) samlSPService := &SAMLIdPServiceProviderService{ svc: *svc, backend: b, - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "saml-idp"}), + logger: slog.With(teleport.ComponentKey, "saml-idp"), } for _, opt := range opts { @@ -120,13 +120,17 @@ func (s *SAMLIdPServiceProviderService) CreateSAMLIdPServiceProvider(ctx context if err := services.ValidateSAMLIdPACSURLAndRelayStateInputs(sp); err != nil { // logging instead of returning an error cause we do not want to break cache writes on a cluster // that already has a service provider with unsupported characters/scheme in the acs_url or relay_state. - s.log.Warn(err) + s.logger.WarnContext(ctx, "Provided SAML IdP service provided is invalid", "error", err) } if sp.GetEntityDescriptor() == "" { if err := s.configureEntityDescriptorPerPreset(sp); err != nil { errMsg := fmt.Errorf("failed to configure entity descriptor with the given entity_id %q and acs_url %q: %w", sp.GetEntityID(), sp.GetACSURL(), err) - s.log.Errorf(errMsg.Error()) + s.logger.ErrorContext(ctx, "failed to configure entity descriptor", + "entity_id", sp.GetEntityID(), + "acs_url", sp.GetACSURL(), + "error", err, + ) return trace.BadParameter(errMsg.Error()) } } @@ -166,7 +170,7 @@ func (s *SAMLIdPServiceProviderService) UpdateSAMLIdPServiceProvider(ctx context if err := services.ValidateSAMLIdPACSURLAndRelayStateInputs(sp); err != nil { // logging instead of returning an error cause we do not want to break cache writes on a cluster // that already has a service provider with unsupported characters/scheme in the acs_url or relay_state. - s.log.Warn(err) + s.logger.WarnContext(ctx, "Provided SAML IdP service provided is invalid", "error", err) } // we only verify if the entity ID field in the spec matches with the entity descriptor. @@ -250,7 +254,10 @@ func (s *SAMLIdPServiceProviderService) configureEntityDescriptorPerPreset(sp ty // fetchAndSetEntityDescriptor is expected to return error if it fails // to fetch a valid entity descriptor. if err := s.fetchAndSetEntityDescriptor(sp); err != nil { - s.log.Debugf("Failed to fetch entity descriptor from %q: %v.", sp.GetEntityID(), err) + s.logger.DebugContext(context.Background(), "Failed to fetch entity descriptor", + "entity_id", sp.GetEntityID(), + "error", err, + ) // We aren't interested in checking error type as any occurrence of error // mean entity descriptor was not set. return trace.Wrap(s.generateAndSetEntityDescriptor(sp)) @@ -295,7 +302,10 @@ func (s *SAMLIdPServiceProviderService) fetchAndSetEntityDescriptor(sp types.SAM // generateAndSetEntityDescriptor generates and sets Service Provider entity descriptor // with ACS URL, Entity ID and unspecified NameID format. func (s *SAMLIdPServiceProviderService) generateAndSetEntityDescriptor(sp types.SAMLIdPServiceProvider) error { - s.log.Infof("Generating a default entity_descriptor with entity_id %q and acs_url %q.", sp.GetEntityID(), sp.GetACSURL()) + s.logger.InfoContext(context.Background(), "Generating a default entity_descriptor", + "entity_id", sp.GetEntityID(), + "acs_url", sp.GetACSURL(), + ) acsURL, err := url.Parse(sp.GetACSURL()) if err != nil { @@ -335,7 +345,9 @@ func (s *SAMLIdPServiceProviderService) embedAttributeMapping(sp types.SAMLIdPSe switch attrMapLen := len(sp.GetAttributeMapping()); { case attrMapLen == 0: if teleportSPSSODescriptorIndex == 0 { - s.log.Debugf("No custom attribute mapping values provided for %s. SAML assertion will default to uid and eduPersonAffiliate", sp.GetEntityID()) + s.logger.DebugContext(context.Background(), "No custom attribute mapping values provided,SAML assertion will default to uid and eduPersonAffiliate", + "entity_id", sp.GetEntityID(), + ) return nil } else { // delete Teleport SPSSODescriptor diff --git a/lib/services/local/secreports.go b/lib/services/local/secreports.go index c08ac60fe3a36..36307ce17d528 100644 --- a/lib/services/local/secreports.go +++ b/lib/services/local/secreports.go @@ -23,9 +23,7 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" - "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/secreports" "github.com/gravitational/teleport/lib/backend" @@ -46,7 +44,6 @@ var ( // SecReportsService is the local implementation of the SecReports service. type SecReportsService struct { - log logrus.FieldLogger clock clockwork.Clock auditQuerySvc *generic.Service[*secreports.AuditQuery] securityReportSvc *generic.Service[*secreports.Report] @@ -99,7 +96,6 @@ func NewSecReportsService(backend backend.Backend, clock clockwork.Clock) (*SecR } return &SecReportsService{ - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "secreports:local-service"}), clock: clock, auditQuerySvc: auditQuerySvc, securityReportSvc: securityReportSvc, diff --git a/lib/services/local/session.go b/lib/services/local/session.go index c2fde121ddce7..ce032ca6fc3b2 100644 --- a/lib/services/local/session.go +++ b/lib/services/local/session.go @@ -23,7 +23,6 @@ import ( "slices" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" @@ -315,7 +314,7 @@ func (s *IdentityService) DeleteAllSAMLIdPSessions(ctx context.Context) error { // WebSessions returns the web sessions manager. func (s *IdentityService) WebSessions() types.WebSessionInterface { - return &webSessions{backend: s.Backend, log: s.log} + return &webSessions{backend: s.Backend} } // Get returns the web session state described with req. @@ -423,12 +422,11 @@ func (r *webSessions) listLegacySessions(ctx context.Context) ([]types.WebSessio type webSessions struct { backend backend.Backend - log logrus.FieldLogger } // WebTokens returns the web token manager. func (s *IdentityService) WebTokens() types.WebTokenInterface { - return &webTokens{backend: s.Backend, log: s.log} + return &webTokens{backend: s.Backend} } // Get returns the web token described with req. @@ -504,7 +502,6 @@ func (r *webTokens) DeleteAll(ctx context.Context) error { type webTokens struct { backend backend.Backend - log logrus.FieldLogger } func webSessionKey(sessionID string) backend.Key { diff --git a/lib/services/local/sessiontracker.go b/lib/services/local/sessiontracker.go index ad6fc5e9d06f1..95ec34895d78e 100644 --- a/lib/services/local/sessiontracker.go +++ b/lib/services/local/sessiontracker.go @@ -20,10 +20,10 @@ package local import ( "context" + "log/slog" "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" @@ -160,7 +160,7 @@ func (s *sessionTracker) getActiveSessionTrackers(ctx context.Context, filter *t for _, item := range noExpiry { if err := s.bk.Delete(ctx, item.Key); err != nil { if !trace.IsNotFound(err) { - logrus.WithError(err).Error("Failed to remove stale session tracker") + slog.ErrorContext(ctx, "Failed to remove stale session tracker", "error", err) } } } diff --git a/lib/services/local/status.go b/lib/services/local/status.go index 9af78ee0d4f2b..b6046e9bde34a 100644 --- a/lib/services/local/status.go +++ b/lib/services/local/status.go @@ -20,10 +20,10 @@ package local import ( "context" + "log/slog" "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" @@ -35,13 +35,13 @@ import ( // StatusService manages cluster status info. type StatusService struct { backend.Backend - log logrus.FieldLogger + logger *slog.Logger } func NewStatusService(bk backend.Backend) *StatusService { return &StatusService{ Backend: bk, - log: logrus.WithField(teleport.ComponentKey, "status"), + logger: slog.With(teleport.ComponentKey, "status"), } } @@ -68,7 +68,7 @@ func (s *StatusService) GetClusterAlerts(ctx context.Context, query types.GetClu filtered := alerts[:0] for _, alert := range alerts { if err := alert.CheckAndSetDefaults(); err != nil { - s.log.Warnf("Skipping invalid cluster alert: %v", err) + s.logger.WarnContext(ctx, "Skipping invalid cluster alert", "error", err) } if !query.Match(alert) { diff --git a/lib/services/local/user_login_state.go b/lib/services/local/user_login_state.go index 76e12b6b4c95d..a73c77b238dc4 100644 --- a/lib/services/local/user_login_state.go +++ b/lib/services/local/user_login_state.go @@ -22,9 +22,7 @@ import ( "context" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" - "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/userloginstate" "github.com/gravitational/teleport/lib/backend" @@ -38,7 +36,6 @@ const ( // UserLoginStateService manages user login state resources in the Backend. type UserLoginStateService struct { - log logrus.FieldLogger svc *generic.Service[*userloginstate.UserLoginState] } @@ -56,7 +53,6 @@ func NewUserLoginStateService(b backend.Backend) (*UserLoginStateService, error) } return &UserLoginStateService{ - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "user-login-state:local-service"}), svc: svc, }, nil } diff --git a/lib/services/local/users.go b/lib/services/local/users.go index 760634dfa65b3..1c4790bcda886 100644 --- a/lib/services/local/users.go +++ b/lib/services/local/users.go @@ -26,6 +26,7 @@ import ( "encoding/base64" "encoding/json" "io" + "log/slog" "sort" "strings" "sync" @@ -37,7 +38,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" "golang.org/x/sync/errgroup" @@ -65,43 +65,30 @@ var GlobalSessionDataMaxEntries = 5000 // arbitrary // user accounts as well type IdentityService struct { backend.Backend - log logrus.FieldLogger + logger *slog.Logger bcryptCost int notificationsSvc *NotificationsService } -// TODO(rudream): Rename to NewIdentityService. +// TODO(tross): DELETE ONCE e is updated to use NewIdentityService // NewIdentityServiceV2 returns a new instance of IdentityService object func NewIdentityServiceV2(backend backend.Backend) (*IdentityService, error) { - notificationsSvc, err := NewNotificationsService(backend, backend.Clock()) - if err != nil { - return nil, trace.Wrap(err) - } - - return &IdentityService{ - Backend: backend, - log: logrus.WithField(teleport.ComponentKey, "identity"), - bcryptCost: bcrypt.DefaultCost, - notificationsSvc: notificationsSvc, - }, nil + return NewIdentityService(backend) } -// TODO(rudream): Remove once NewIdentityServiceV2 is merged. // NewIdentityService returns a new instance of IdentityService object -func NewIdentityService(backend backend.Backend) *IdentityService { +func NewIdentityService(backend backend.Backend) (*IdentityService, error) { notificationsSvc, err := NewNotificationsService(backend, backend.Clock()) - - log := logrus.WithField(teleport.ComponentKey, "identity") if err != nil { - log.Warnf("error initializing notifications service with identity service: %v", err) + return nil, trace.Wrap(err) } return &IdentityService{ Backend: backend, - log: log, + logger: slog.With(teleport.ComponentKey, "identity"), bcryptCost: bcrypt.DefaultCost, notificationsSvc: notificationsSvc, - } + }, nil } // NewTestIdentityService returns a new instance of IdentityService object to be @@ -201,7 +188,10 @@ func (s *IdentityService) streamUsersWithSecrets(itemStream stream.Stream[backen collectorStream := stream.FilterMap(itemStream, func(item backend.Item) (collector, bool) { name, suffix, err := splitUsernameAndSuffix(item.Key) if err != nil { - s.log.Warnf("Failed to extract name/suffix for user item at %q: %v", item.Key, err) + s.logger.WarnContext(context.Background(), "Failed to extract name/suffix for user item", + "key", item.Key, + "error", err, + ) return collector{}, false } @@ -242,7 +232,10 @@ func (s *IdentityService) streamUsersWithSecrets(itemStream stream.Stream[backen userStream := stream.FilterMap(collectorStream, func(c collector) (*types.UserV2, bool) { user, err := userFromUserItems(c.name, c.items) if err != nil { - s.log.Warnf("Failed to build user %q from user item aggregator: %v", c.name, err) + s.logger.WarnContext(context.Background(), "Failed to build user from user item aggregator", + "user", c.name, + "error", err, + ) return nil, false } @@ -263,7 +256,10 @@ func (s *IdentityService) streamUsersWithoutSecrets(itemStream stream.Stream[bac user, err := services.UnmarshalUser(item.Value, services.WithRevision(item.Revision)) if err != nil { - s.log.Warnf("Failed to unmarshal user at %q: %v", item.Key, err) + s.logger.WarnContext(context.Background(), "Failed to unmarshal user", + "key", item.Key, + "error", err, + ) return nil, false } @@ -874,6 +870,7 @@ func (s *IdentityService) DeleteUserLoginAttempts(user string) error { // `PasswordState` status flag accordingly. Returns an error if the user doesn't // exist. func (s *IdentityService) UpsertPassword(user string, password []byte) error { + ctx := context.TODO() if user == "" { return trace.BadParameter("missing username") } @@ -891,7 +888,7 @@ func (s *IdentityService) UpsertPassword(user string, password []byte) error { } _, err = s.UpdateAndSwapUser( - context.TODO(), + ctx, user, false, /*withSecrets*/ func(u types.User) (bool, error) { @@ -900,10 +897,10 @@ func (s *IdentityService) UpsertPassword(user string, password []byte) error { }) if err != nil { // Don't let the password state flag change fail the entire operation. - s.log. - WithError(err). - WithField("user", user). - Warn("Failed to set password state") + s.logger.WarnContext(ctx, "Failed to set password state", + "user", user, + "error", err, + ) } return nil @@ -924,7 +921,7 @@ func (s *IdentityService) DeletePassword(ctx context.Context, user string) error } if _, err := s.UpdateAndSwapUser( - context.TODO(), + ctx, user, false, /*withSecrets*/ func(u types.User) (bool, error) { @@ -933,10 +930,10 @@ func (s *IdentityService) DeletePassword(ctx context.Context, user string) error }, ); err != nil { // Don't let the password state flag change fail the entire operation. - s.log. - WithError(err). - WithField("user", user). - Warn("Failed to set password state") + s.logger.WarnContext(ctx, "Failed to set password state", + "user", user, + "error", err, + ) } // Now is the time to return the delete operation, if any. @@ -987,7 +984,7 @@ func (s *IdentityService) UpsertWebauthnLocalAuth(ctx context.Context, user stri // lib/auth/webauthn is prepared to deal with eventual inconsistencies // between "web/users/.../webauthnlocalauth" and "webauthn/users/" keys. if err := s.Delete(ctx, wlaKey); err != nil { - s.log.WithError(err).Warn("Failed to undo WebauthnLocalAuth update") + s.logger.WarnContext(ctx, "Failed to undo WebauthnLocalAuth update", "error", err) } return trace.Wrap(err, "writing webauthn user") } @@ -1208,7 +1205,7 @@ func (s *IdentityService) UpsertMFADevice(ctx context.Context, user string, d *t return trace.Wrap(err) } if err := s.upsertUserStatusMFADevice(ctx, user); err != nil { - s.log.WithError(err).Warn("Unable to update user status after adding MFA device") + s.logger.WarnContext(ctx, "Unable to update user status after adding MFA device", "error", err) } return nil } @@ -1308,7 +1305,7 @@ func (s *IdentityService) buildAndSetWeakestMFADeviceKind(ctx context.Context, u } state, err := s.buildWeakestMFADeviceKind(ctx, user.GetName(), upsertingMFA...) if err != nil { - s.log.WithError(err).Warn("Failed to determine weakest mfa device kind for user") + s.logger.WarnContext(ctx, "Failed to determine weakest mfa device kind for user", "error", err) return } user.SetWeakestDevice(state) @@ -1370,7 +1367,7 @@ func (s *IdentityService) DeleteMFADevice(ctx context.Context, user, id string) return trace.Wrap(err) } if err := s.upsertUserStatusMFADevice(ctx, user); err != nil { - s.log.WithError(err).Warn("Unable to update user status after deleting MFA device") + s.logger.WarnContext(ctx, "Unable to update user status after deleting MFA device", "error", err) } return nil } @@ -1607,10 +1604,10 @@ func (s *IdentityService) GetOIDCConnectors(ctx context.Context, withSecrets boo for _, item := range result.Items { conn, err := services.UnmarshalOIDCConnector(item.Value, services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { - logrus. - WithError(err). - WithField("key", item.Key). - Errorf("Error unmarshaling OIDC Connector") + s.logger.ErrorContext(ctx, "Error unmarshaling OIDC Connector", + "key", item.Key, + "error", err, + ) continue } if !withSecrets { @@ -1775,10 +1772,10 @@ func (s *IdentityService) GetSAMLConnectors(ctx context.Context, withSecrets boo for _, item := range result.Items { conn, err := services.UnmarshalSAMLConnector(item.Value, services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { - logrus. - WithError(err). - WithField("key", item.Key). - Errorf("Error unmarshaling SAML Connector") + s.logger.ErrorContext(ctx, "Error unmarshaling SAML Connector", + "key", item.Key, + "error", err, + ) continue } if !withSecrets { @@ -2016,10 +2013,10 @@ func (s *IdentityService) GetGithubConnectors(ctx context.Context, withSecrets b for _, item := range result.Items { connector, err := services.UnmarshalGithubConnector(item.Value, services.WithRevision(item.Revision)) if err != nil { - logrus. - WithError(err). - WithField("key", item.Key). - Errorf("Error unmarshaling GitHub Connector") + s.logger.ErrorContext(ctx, "Error unmarshaling GitHub Connector", + "key", item.Key, + "error", err, + ) continue } if !withSecrets { diff --git a/lib/services/matchers.go b/lib/services/matchers.go index e625d4ca59fab..84e8ba87bd793 100644 --- a/lib/services/matchers.go +++ b/lib/services/matchers.go @@ -19,10 +19,11 @@ package services import ( + "context" + "log/slog" "slices" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" @@ -115,8 +116,11 @@ func MatchResourceLabels(matchers []ResourceMatcher, labels map[string]string) b } match, _, err := MatchLabels(matcher.Labels, labels) if err != nil { - logrus.WithError(err).Errorf("Failed to match labels %v: %v.", - matcher.Labels, labels) + slog.ErrorContext(context.Background(), "Failed to match labels", + "error", err, + "matcher_labels", matcher.Labels, + "resource_labels", labels, + ) return false } if match { diff --git a/lib/services/parser.go b/lib/services/parser.go index 8d70f0f53b8fe..1ea6b6d7cdd60 100644 --- a/lib/services/parser.go +++ b/lib/services/parser.go @@ -19,14 +19,14 @@ package services import ( + "context" "fmt" - "io" + "log/slog" "slices" "strings" "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/vulcand/predicate" "github.com/vulcand/predicate/builder" @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/wrappers" "github.com/gravitational/teleport/lib/session" + logutils "github.com/gravitational/teleport/lib/utils/log" "github.com/gravitational/teleport/lib/utils/typical" ) @@ -231,34 +232,41 @@ func NewActionsParser(ctx RuleContext) (predicate.Parser, error) { // NewLogActionFn creates logger functions func NewLogActionFn(ctx RuleContext) interface{} { l := &LogAction{ctx: ctx} - writer, ok := ctx.(io.Writer) - if ok && writer != nil { - l.writer = writer - } + return l.Log } // LogAction represents action that will emit log entry // when specified in the actions of a matched rule type LogAction struct { - ctx RuleContext - writer io.Writer + ctx RuleContext } -// Log logs with specified level and formatting string with arguments -func (l *LogAction) Log(level, format string, args ...interface{}) predicate.BoolPredicate { +// Log logs with specified level and message string and attributes +func (l *LogAction) Log(level, msg string, args ...any) predicate.BoolPredicate { return func() bool { - ilevel, err := log.ParseLevel(level) - if err != nil { - ilevel = log.DebugLevel + slevel := slog.LevelDebug + switch strings.ToLower(level) { + case "error": + slevel = slog.LevelError + case "warn", "warning": + slevel = slog.LevelWarn + case "info": + slevel = slog.LevelInfo + case "debug": + slevel = slog.LevelDebug + case "trace": + slevel = logutils.TraceLevel } - var writer io.Writer - if l.writer != nil { - writer = l.writer - } else { - writer = log.StandardLogger().WriterLevel(ilevel) + + ctx := context.Background() + // Expicitly check whether logging is enabled for the level + // to avoid formatting the message if the log won't be sampled. + if !slog.Default().Handler().Enabled(ctx, slevel) { + //nolint:sloglint // msg cannot be constant + slog.Log(context.Background(), slevel, fmt.Sprintf(msg, args...)) } - writer.Write([]byte(fmt.Sprintf(format, args...))) + return true } } diff --git a/lib/services/presets.go b/lib/services/presets.go index ec3f8ad529c9d..9c534d64b1d22 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -19,16 +19,16 @@ package services import ( + "context" + "log/slog" "slices" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/api/types/common" apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/utils" @@ -596,7 +596,11 @@ func NewSystemIdentityCenterAccessRole() types.Role { Description: "Access AWS IAM Identity Center resources", Labels: map[string]string{ types.TeleportInternalResourceType: types.SystemResource, - types.OriginLabel: common.OriginAWSIdentityCenter, + // OriginLabel should not be set to AWS Identity center because: + // - identity center is not the one owning this role, this role + // is part of the Teleport system requirements + // - setting the label to a value not support in older agents + // (v16) will cause them to crash. }, }, Spec: types.RoleSpecV6{ @@ -687,6 +691,11 @@ func bootstrapRoleMetadataLabels() map[string]map[string]string { types.TeleportInternalResourceType: types.SystemResource, types.OriginLabel: types.OriginOkta, }, + // We unset the OriginLabel on the system AWS IC role because this value + // was not supported on v16 agents and this crashes them. + teleport.SystemIdentityCenterAccessRoleName: { + types.TeleportInternalResourceType: types.SystemResource, + }, // Group access, reviewer and requester are intentionally not added here as there may be // existing customer defined roles that have these labels. } @@ -801,7 +810,7 @@ func defaultAllowAccountAssignments(enterprise bool) map[string][]types.Identity // AddRoleDefaults adds default role attributes to a preset role. // Only attributes whose resources are not already defined (either allowing or denying) are added. -func AddRoleDefaults(role types.Role) (types.Role, error) { +func AddRoleDefaults(ctx context.Context, role types.Role) (types.Role, error) { changed := false oldLabels := role.GetAllLabels() @@ -855,7 +864,10 @@ func AddRoleDefaults(role types.Role) (types.Role, error) { continue } - log.Debugf("Adding default allow rule %v for role %q", defaultRule, role.GetName()) + slog.DebugContext(ctx, "Adding default allow rule to role", + "rule", defaultRule, + "role", role.GetName(), + ) rules := role.GetRules(types.Allow) rules = append(rules, defaultRule) role.SetRules(types.Allow, rules) diff --git a/lib/services/presets_test.go b/lib/services/presets_test.go index 3e4f12c5d4084..8e0490c82f714 100644 --- a/lib/services/presets_test.go +++ b/lib/services/presets_test.go @@ -19,6 +19,7 @@ package services import ( + "context" "testing" "github.com/google/go-cmp/cmp" @@ -639,7 +640,7 @@ func TestAddRoleDefaults(t *testing.T) { }) } - role, err := AddRoleDefaults(test.role) + role, err := AddRoleDefaults(context.Background(), test.role) test.expectedErr(t, err) require.Empty(t, cmp.Diff(role, test.expected)) diff --git a/lib/services/role.go b/lib/services/role.go index 37418d27c41a0..b9e3e04d83816 100644 --- a/lib/services/role.go +++ b/lib/services/role.go @@ -35,7 +35,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" jsoniter "github.com/json-iterator/go" - log "github.com/sirupsen/logrus" "github.com/vulcand/predicate" "golang.org/x/crypto/ssh" @@ -51,6 +50,7 @@ import ( "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" awsutils "github.com/gravitational/teleport/lib/utils/aws" + logutils "github.com/gravitational/teleport/lib/utils/log" "github.com/gravitational/teleport/lib/utils/parse" ) @@ -1598,7 +1598,7 @@ func (set RoleSet) CheckGCPServiceAccounts(ttl time.Duration, overrideTTL bool) // //nolint:revive // Because we want this to be IdP. func (set RoleSet) CheckAccessToSAMLIdP(authPref readonly.AuthPreference, state AccessState) error { - _, debugf := rbacDebugLogger() + ctx := context.Background() if authPref != nil { if !authPref.IsSAMLIdPEnabled() { @@ -1607,7 +1607,7 @@ func (set RoleSet) CheckAccessToSAMLIdP(authPref readonly.AuthPreference, state } if state.MFARequired == MFARequiredAlways && !state.MFAVerified { - debugf("Access to SAML IdP denied, cluster requires per-session MFA") + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Access to SAML IdP denied, cluster requires per-session MFA") return trace.Wrap(ErrSessionMFARequired) } @@ -1627,7 +1627,10 @@ func (set RoleSet) CheckAccessToSAMLIdP(authPref readonly.AuthPreference, state } if !mfaAllowed && options.RequireMFAType.IsSessionMFARequired() { - debugf("Access to SAML IdP denied, role %q requires per-session MFA", role.GetName()) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Access to SAML IdP denied, role requires per-session MFA", + slog.String("role", role.GetName()), + ) + return trace.Wrap(ErrSessionMFARequired) } } @@ -2539,19 +2542,7 @@ type AccessCheckable interface { GetAllLabels() map[string]string } -// rbacDebugLogger creates a debug logger for Teleport's RBAC component. -// It also returns a flag indicating whether debug logging is enabled, -// allowing the RBAC system to generate more verbose errors in debug mode. -func rbacDebugLogger() (debugEnabled bool, debugf func(format string, args ...interface{})) { - debugEnabled = log.IsLevelEnabled(log.TraceLevel) - debugf = func(format string, args ...interface{}) {} - - if debugEnabled { - debugf = log.WithField(teleport.ComponentKey, teleport.ComponentRBAC).Tracef - } - - return -} +var rbacLogger = logutils.NewPackageLogger(teleport.ComponentKey, teleport.ComponentRBAC) // resourceRequiresLabelMatching decides if a resource requires lapel matching // when making RBAC access decisions. @@ -2567,13 +2558,18 @@ func resourceRequiresLabelMatching(r AccessCheckable) bool { } func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state AccessState, matchers ...RoleMatcher) error { - // Note: logging in this function only happens in debug mode. This is because + // Note: logging in this function only happens in trace mode. This is because // adding logging to this function (which is called on every resource returned // by the backend) can slow down this function by 50x for large clusters! - isDebugEnabled, debugf := rbacDebugLogger() + ctx := context.Background() + logger := rbacLogger + isLoggingEnabled := logger.Handler().Enabled(ctx, logutils.TraceLevel) + if isLoggingEnabled { + logger.With("resource_kind", r.GetKind(), "resource_name", r.GetName()) + } if !state.MFAVerified && state.MFARequired == MFARequiredAlways { - debugf("Access to %v %q denied, cluster requires per-session MFA", r.GetKind(), r.GetName()) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource denied, cluster requires per-session MFA") return ErrSessionMFARequired } @@ -2601,18 +2597,21 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state continue } if requiresLabelMatching { - matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, traits, r, isDebugEnabled) + matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Deny, role, traits, r, isLoggingEnabled) if err != nil { return trace.Wrap(err) } if matchLabels { - debugf("Access to %v %q denied, deny rule in role %q matched; match(namespace=%v, %s)", - r.GetKind(), r.GetName(), role.GetName(), namespaceMessage, labelsMessage) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource denied, deny rule in role matched", + slog.String("role", role.GetName()), + slog.String("namespace_message", namespaceMessage), + slog.String("label_message", labelsMessage), + ) return trace.AccessDenied("access to %v denied. User does not have permissions. %v", r.GetKind(), additionalDeniedMessage) } } else { - debugf("Role label matching skipped for %v %q", r.GetKind(), r.GetName()) + logger.LogAttrs(ctx, logutils.TraceLevel, "Role label matching skipped") } // Deny rules are greedy on purpose. They will always match if // at least one of the matchers returns true. @@ -2621,8 +2620,10 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state return trace.Wrap(err) } if matchMatchers { - debugf("Access to %v %q denied, deny rule in role %q matched; match(matcher=%v)", - r.GetKind(), r.GetName(), role.GetName(), matchersMessage) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource denied, deny rule in role matched", + slog.String("role", role.GetName()), + slog.Any("matcher_message", matchersMessage), + ) return trace.AccessDenied("access to %v denied. User does not have permissions. %v", r.GetKind(), additionalDeniedMessage) } @@ -2640,7 +2641,7 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state for _, role := range set { matchNamespace, namespaceMessage := MatchNamespace(role.GetNamespaces(types.Allow), namespace) if !matchNamespace { - if isDebugEnabled { + if isLoggingEnabled { errs = append(errs, trace.AccessDenied("role=%v, match(namespace=%v)", role.GetName(), namespaceMessage)) } @@ -2648,20 +2649,20 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state } if requiresLabelMatching { - matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, traits, r, isDebugEnabled) + matchLabels, labelsMessage, err := checkRoleLabelsMatch(types.Allow, role, traits, r, isLoggingEnabled) if err != nil { return trace.Wrap(err) } if !matchLabels { - if isDebugEnabled { + if isLoggingEnabled { errs = append(errs, trace.AccessDenied("role=%v, match(%s)", role.GetName(), labelsMessage)) } continue } } else { - debugf("Role label matching skipped for %v %q", r.GetKind(), r.GetName()) + logger.LogAttrs(ctx, logutils.TraceLevel, "Role label matching skipped for resource") } // Allow rules are not greedy. They will match only if all of the @@ -2671,7 +2672,7 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state return trace.Wrap(err) } if !matchMatchers { - if isDebugEnabled { + if isLoggingEnabled { errs = append(errs, fmt.Errorf("role=%v, match(matchers=%v)", role.GetName(), matchers)) } @@ -2689,37 +2690,43 @@ func (set RoleSet) checkAccess(r AccessCheckable, traits wrappers.Traits, state // ensure the access is permitted. if mfaAllowed && deviceAllowed { - debugf("Access to %v %q granted, allow rule in role %q matched.", - r.GetKind(), r.GetName(), role.GetName()) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource granted, allow rule in role matched", + slog.String("role", role.GetName()), + ) return nil } // MFA verification. if !mfaAllowed && role.GetOptions().RequireMFAType.IsSessionMFARequired() { - debugf("Access to %v %q denied, role %q requires per-session MFA", - r.GetKind(), r.GetName(), role.GetName()) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource denied, role requires per-session MFA", + slog.String("role", role.GetName()), + ) return ErrSessionMFARequired } // Device verification. if !deviceAllowed && role.GetOptions().DeviceTrustMode == constants.DeviceTrustModeRequired { - debugf("Access to %v %q denied, role %q requires a trusted device", - r.GetKind(), r.GetName(), role.GetName()) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource denied, role requires a trusted device", + slog.String("role", role.GetName()), + ) return ErrTrustedDeviceRequired } // Current role allows access, but keep looking for a more restrictive // setting. allowed = true - debugf("Access to %v %q granted, allow rule in role %q matched.", - r.GetKind(), r.GetName(), role.GetName()) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource granted, allow rule in role matched", + slog.String("role", role.GetName()), + ) } if allowed { return nil } - debugf("Access to %v %q denied, no allow rule matched; %v", r.GetKind(), r.GetName(), errs) + logger.LogAttrs(ctx, logutils.TraceLevel, "Access to resource denied, no allow rule matched", + slog.Any("errors", errs), + ) return trace.AccessDenied("access to %v denied. User does not have permissions. %v", r.GetKind(), additionalDeniedMessage) } @@ -3224,6 +3231,8 @@ func (a *accessExplicitlyDenied) Unwrap() error { } func (set RoleSet) checkAccessToRuleImpl(p checkAccessParams) (err error) { + ctx := context.Background() + // Every unknown error, which could be due to a bad role or an expression // that can't parse, should be considered an explicit denial. explicitDeny := true @@ -3247,10 +3256,13 @@ func (set RoleSet) checkAccessToRuleImpl(p checkAccessParams) (err error) { return trace.Wrap(err) } if matched { - log.WithFields(log.Fields{ - teleport.ComponentKey: teleport.ComponentRBAC, - }).Tracef("Access to %v %v in namespace %v denied to %v: deny rule matched.", - p.verb, p.resource, p.namespace, role.GetName()) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Access denied, deny rule matched", + slog.String("verb", p.verb), + slog.String("resource", p.resource), + slog.String("namespace", p.namespace), + slog.String("role", role.GetName()), + ) + return trace.AccessDenied("access denied to perform action %q on %q", p.verb, p.resource) } } @@ -3270,10 +3282,12 @@ func (set RoleSet) checkAccessToRuleImpl(p checkAccessParams) (err error) { } } - log.WithFields(log.Fields{ - teleport.ComponentKey: teleport.ComponentRBAC, - }).Tracef("Access to %v %v in namespace %v denied to %v: no allow rule matched.", - p.verb, p.resource, p.namespace, set) + rbacLogger.LogAttrs(ctx, logutils.TraceLevel, "Access denied, no allow rule matched", + slog.String("verb", p.verb), + slog.String("resource", p.resource), + slog.String("namespace", p.namespace), + slog.Any("set", set), + ) // At this point no deny rule has matched and there are no more unknown // errors, so this is only an implicit denial. diff --git a/lib/services/role_test.go b/lib/services/role_test.go index 7c85f6a07e0a8..13f2cc3f18c72 100644 --- a/lib/services/role_test.go +++ b/lib/services/role_test.go @@ -19,7 +19,6 @@ package services import ( - "bytes" "cmp" "context" "encoding/json" @@ -2180,27 +2179,13 @@ func makeAccessCheckerWithRoleSet(roleSet RoleSet) AccessChecker { return NewAccessCheckerWithRoleSet(accessInfo, "clustername", roleSet) } -// testContext overrides context and captures log writes in action -type testContext struct { - Context - // Buffer captures log writes - buffer *bytes.Buffer -} - -// Write is implemented explicitly to avoid collision -// of String methods when embedding -func (t *testContext) Write(data []byte) (int, error) { - return t.buffer.Write(data) -} - func TestCheckRuleAccess(t *testing.T) { type check struct { - hasAccess bool - verb string - namespace string - rule string - context testContext - matchBuffer string + hasAccess bool + verb string + namespace string + rule string + context Context } testCases := []struct { name string @@ -2320,9 +2305,6 @@ func TestCheckRuleAccess(t *testing.T) { Resources: []string{types.KindSession}, Verbs: []string{types.VerbRead}, Where: `contains(user.spec.traits["group"], "prod")`, - Actions: []string{ - `log("info", "4 - tc match for user %v", user.metadata.name)`, - }, }, }, }, @@ -2333,17 +2315,14 @@ func TestCheckRuleAccess(t *testing.T) { {rule: types.KindSession, verb: types.VerbRead, namespace: apidefaults.Namespace, hasAccess: false}, {rule: types.KindSession, verb: types.VerbList, namespace: apidefaults.Namespace, hasAccess: false}, { - context: testContext{ - buffer: &bytes.Buffer{}, - Context: Context{ - User: &types.UserV2{ - Metadata: types.Metadata{ - Name: "bob", - }, - Spec: types.UserSpecV2{ - Traits: map[string][]string{ - "group": {"dev", "prod"}, - }, + context: Context{ + User: &types.UserV2{ + Metadata: types.Metadata{ + Name: "bob", + }, + Spec: types.UserSpecV2{ + Traits: map[string][]string{ + "group": {"dev", "prod"}, }, }, }, @@ -2354,14 +2333,11 @@ func TestCheckRuleAccess(t *testing.T) { hasAccess: true, }, { - context: testContext{ - buffer: &bytes.Buffer{}, - Context: Context{ - User: &types.UserV2{ - Spec: types.UserSpecV2{ - Traits: map[string][]string{ - "group": {"dev"}, - }, + context: Context{ + User: &types.UserV2{ + Spec: types.UserSpecV2{ + Traits: map[string][]string{ + "group": {"dev"}, }, }, }, @@ -2389,9 +2365,6 @@ func TestCheckRuleAccess(t *testing.T) { Resources: []string{types.KindRole}, Verbs: []string{types.VerbRead}, Where: `equals(resource.metadata.labels["team"], "dev")`, - Actions: []string{ - `log("error", "4 - tc match")`, - }, }, }, }, @@ -2402,13 +2375,10 @@ func TestCheckRuleAccess(t *testing.T) { {rule: types.KindRole, verb: types.VerbRead, namespace: apidefaults.Namespace, hasAccess: false}, {rule: types.KindRole, verb: types.VerbList, namespace: apidefaults.Namespace, hasAccess: false}, { - context: testContext{ - buffer: &bytes.Buffer{}, - Context: Context{ - Resource: &types.RoleV6{ - Metadata: types.Metadata{ - Labels: map[string]string{"team": "dev"}, - }, + context: Context{ + Resource: &types.RoleV6{ + Metadata: types.Metadata{ + Labels: map[string]string{"team": "dev"}, }, }, }, @@ -2439,9 +2409,6 @@ func TestCheckRuleAccess(t *testing.T) { Resources: []string{types.KindRole}, Verbs: []string{types.VerbRead}, Where: `equals(resource.metadata.labels["team"], "dev")`, - Actions: []string{ - `log("info", "matched more specific rule")`, - }, }, }, }, @@ -2450,21 +2417,17 @@ func TestCheckRuleAccess(t *testing.T) { }, checks: []check{ { - context: testContext{ - buffer: &bytes.Buffer{}, - Context: Context{ - Resource: &types.RoleV6{ - Metadata: types.Metadata{ - Labels: map[string]string{"team": "dev"}, - }, + context: Context{ + Resource: &types.RoleV6{ + Metadata: types.Metadata{ + Labels: map[string]string{"team": "dev"}, }, }, }, - rule: types.KindRole, - verb: types.VerbRead, - namespace: apidefaults.Namespace, - hasAccess: true, - matchBuffer: "more specific rule", + rule: types.KindRole, + verb: types.VerbRead, + namespace: apidefaults.Namespace, + hasAccess: true, }, }, }, @@ -2486,9 +2449,6 @@ func TestCheckRuleAccess(t *testing.T) { } else { require.True(t, trace.IsAccessDenied(result), comment) } - if check.matchBuffer != "" { - require.Contains(t, check.context.buffer.String(), check.matchBuffer, comment) - } } } } @@ -2498,7 +2458,7 @@ func TestDefaultImplicitRules(t *testing.T) { hasAccess bool verb string rule string - context testContext + context Context } testCases := []struct { name string diff --git a/lib/services/saml.go b/lib/services/saml.go index ad8e2dccaa36c..ab6988047e0a6 100644 --- a/lib/services/saml.go +++ b/lib/services/saml.go @@ -24,6 +24,7 @@ import ( "crypto/x509/pkix" "encoding/base64" "encoding/xml" + "log/slog" "net/http" "strings" "time" @@ -33,7 +34,6 @@ import ( saml2 "github.com/russellhaering/gosaml2" samltypes "github.com/russellhaering/gosaml2/types" dsig "github.com/russellhaering/goxmldsig" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" @@ -87,7 +87,11 @@ func ValidateSAMLConnector(sc types.SAMLConnector, rg RoleGetter) error { } sc.SetEntityDescriptor(entityDescriptor) - log.Debugf("[SAML] Successfully fetched entity descriptor from %v for connector %v", url, sc.GetName()) + slog.DebugContext(context.Background(), " Successfully fetched entity descriptor for connector", + teleport.ComponentKey, teleport.ComponentSAML, + "entity_descriptor_url", url, + "connector", sc.GetName(), + ) } if ed := sc.GetEntityDescriptor(); ed != "" { @@ -173,9 +177,12 @@ func ValidateSAMLConnector(sc types.SAMLConnector, rg RoleGetter) error { sc.SetMFASettings(mfa) } - log.Debugf("[SAML] SSO: %v", sc.GetSSO()) - log.Debugf("[SAML] Issuer: %v", sc.GetIssuer()) - log.Debugf("[SAML] ACS: %v", sc.GetAssertionConsumerService()) + slog.DebugContext(context.Background(), "connector validated", + teleport.ComponentKey, teleport.ComponentSAML, + "sso", sc.GetSSO(), + "issuer", sc.GetIssuer(), + "acs", sc.GetAssertionConsumerService(), + ) return nil } @@ -271,7 +278,9 @@ func GetSAMLServiceProvider(sc types.SAMLConnector, clock clockwork.Clock) (*sam // Case 1: Only the signing key pair is set. This means that SAML encryption is not expected // and we therefore configure the main key that gets used for all operations as the signing key. // This is done because gosaml2 mandates an encryption key even if not used. - log.Info("No assertion_key_pair was detected. Falling back to signing key for all SAML operations.") + slog.InfoContext(context.Background(), "No assertion_key_pair was detected, falling back to signing key for all SAML operations", + teleport.ComponentKey, teleport.ComponentSAML, + ) keyStore, err = utils.ParseKeyStorePEM(signingKeyPair.PrivateKey, signingKeyPair.Cert) signingKeyStore = keyStore if err != nil { @@ -281,7 +290,9 @@ func GetSAMLServiceProvider(sc types.SAMLConnector, clock clockwork.Clock) (*sam // Case 2: An encryption keypair is configured. This means that encrypted SAML responses are expected. // Since gosaml2 always uses the main key for encryption, we set it to assertion_key_pair. // To handle signing correctly, we now instead set the optional signing key in gosaml2 to signing_key_pair. - log.Info("Detected assertion_key_pair and configured it to decrypt SAML responses.") + slog.InfoContext(context.Background(), "Detected assertion_key_pair and configured it to decrypt SAML responses", + teleport.ComponentKey, teleport.ComponentSAML, + ) keyStore, err = utils.ParseKeyStorePEM(encryptionKeyPair.PrivateKey, encryptionKeyPair.Cert) if err != nil { return nil, trace.Wrap(err, "failed to parse certificate or private key defined in assertion_key_pair") @@ -313,9 +324,7 @@ func GetSAMLServiceProvider(sc types.SAMLConnector, clock clockwork.Clock) (*sam // be used. switch sc.GetProvider() { case teleport.ADFS, teleport.JumpCloud: - log.WithFields(log.Fields{ - teleport.ComponentKey: teleport.ComponentSAML, - }).Debug("Setting ADFS/JumpCloud values.") + slog.DebugContext(context.Background(), "Setting ADFS/JumpCloud values", teleport.ComponentKey, teleport.ComponentSAML) if sp.SignAuthnRequests { sp.SignAuthnRequestsCanonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(dsig.DefaultPrefix) diff --git a/lib/services/saml_idp_service_provider.go b/lib/services/saml_idp_service_provider.go index 3d5882c5e5e67..51c60fa1e807f 100644 --- a/lib/services/saml_idp_service_provider.go +++ b/lib/services/saml_idp_service_provider.go @@ -21,6 +21,7 @@ package services import ( "context" "fmt" + "log/slog" "net/url" "slices" "strings" @@ -28,7 +29,6 @@ import ( "github.com/crewjam/saml" "github.com/crewjam/saml/samlsp" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/utils" @@ -139,7 +139,10 @@ func FilterSAMLEntityDescriptor(ed *saml.EntityDescriptor, quiet bool) error { filtered := slices.DeleteFunc(ed.SPSSODescriptors[i].AssertionConsumerServices, func(acs saml.IndexedEndpoint) bool { if err := ValidateAssertionConsumerService(acs); err != nil { if !quiet { - log.Warnf("AssertionConsumerService binding for entity %q is invalid and will be ignored: %v", ed.EntityID, err) + slog.WarnContext(context.Background(), "AssertionConsumerService binding for entity is invalid and will be ignored", + "entity_id", ed.EntityID, + "error", err, + ) } return true } diff --git a/lib/services/semaphore.go b/lib/services/semaphore.go index 52211acbfa33c..4e1a672423ced 100644 --- a/lib/services/semaphore.go +++ b/lib/services/semaphore.go @@ -20,12 +20,12 @@ package services import ( "context" + "log/slog" "sync" "time" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/retryutils" @@ -200,10 +200,17 @@ func (l *SemaphoreLock) keepAlive(ctx context.Context) { defer cancel() err = l.cfg.Service.CancelSemaphoreLease(cancelContext, lease) if err != nil { - log.Warnf("Failed to cancel semaphore lease %s/%s: %v", lease.SemaphoreKind, lease.SemaphoreName, err) + slog.WarnContext(cancelContext, "Failed to cancel semaphore lease %s/%s: %v", + "semaphore_kind", lease.SemaphoreKind, + "semaphore_name", lease.SemaphoreName, + "error", err, + ) } } else { - log.Errorf("Semaphore lease expired: %s/%s", lease.SemaphoreKind, lease.SemaphoreName) + slog.ErrorContext(context.Background(), "Semaphore lease expired", + "semaphore_kind", lease.SemaphoreKind, + "semaphore_name", lease.SemaphoreName, + ) } }() Outer: @@ -219,7 +226,11 @@ Outer: leaseCancel() // semaphore and/or lease no longer exist; best to log the error // and exit immediately. - log.Warnf("Halting keepalive on semaphore %s/%s early: %v", lease.SemaphoreKind, lease.SemaphoreName, err) + slog.WarnContext(leaseContext, "Halting keepalive on semaphore", + "semaphore_kind", lease.SemaphoreKind, + "semaphore_name", lease.SemaphoreName, + "error", err, + ) nodrop = true return } @@ -233,7 +244,11 @@ Outer: } continue Outer } - log.Debugf("Failed to renew semaphore lease %s/%s: %v", lease.SemaphoreKind, lease.SemaphoreName, err) + slog.DebugContext(leaseContext, "Failed to renew semaphore lease", + "semaphore_kind", lease.SemaphoreKind, + "semaphore_name", lease.SemaphoreName, + "error", err, + ) l.retry.Inc() select { case <-l.retry.After(): diff --git a/lib/services/simple/access_list.go b/lib/services/simple/access_list.go index 9effb4475c973..f8b8b78bdbe44 100644 --- a/lib/services/simple/access_list.go +++ b/lib/services/simple/access_list.go @@ -23,9 +23,7 @@ import ( "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" - "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" "github.com/gravitational/teleport/lib/backend" @@ -46,7 +44,6 @@ const ( // AccessListService is a simple access list backend service for use specifically by the cache. type AccessListService struct { - log logrus.FieldLogger service *generic.Service[*accesslist.AccessList] memberService *generic.Service[*accesslist.AccessListMember] reviewService *generic.Service[*accesslist.Review] @@ -93,7 +90,6 @@ func NewAccessListService(b backend.Backend) (*AccessListService, error) { } return &AccessListService{ - log: logrus.WithFields(logrus.Fields{teleport.ComponentKey: "access-list:simple-service"}), service: service, memberService: memberService, reviewService: reviewService, diff --git a/lib/services/suite/suite.go b/lib/services/suite/suite.go index ad2cb4695b3f2..3c1a445480166 100644 --- a/lib/services/suite/suite.go +++ b/lib/services/suite/suite.go @@ -22,6 +22,7 @@ import ( "context" "crypto/x509/pkix" "fmt" + "log/slog" "sort" "sync" "sync/atomic" @@ -33,7 +34,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "golang.org/x/crypto/bcrypt" protobuf "google.golang.org/protobuf/proto" @@ -2140,7 +2140,7 @@ skiploop: for { select { case event := <-w.Events(): - log.Debugf("Skipping pre-test event: %v", event) + slog.DebugContext(ctx, "Skipping pre-test event", "event", event) continue skiploop default: break skiploop @@ -2215,11 +2215,11 @@ waitLoop: t.Fatalf("Watcher exited with error %v", w.Error()) case event := <-w.Events(): if event.Type != types.OpPut { - log.Debugf("Skipping event %+v", event) + slog.DebugContext(context.Background(), "Skipping event", "event", event) continue } if resource.GetName() != event.Resource.GetName() || resource.GetKind() != event.Resource.GetKind() || resource.GetSubKind() != event.Resource.GetSubKind() { - log.Debugf("Skipping event %v resource %v, expecting %v", event.Type, event.Resource.GetMetadata(), event.Resource.GetMetadata()) + slog.DebugContext(context.Background(), "Skipping event", "event", event) continue waitLoop } require.Empty(t, cmp.Diff(resource, event.Resource)) @@ -2240,7 +2240,10 @@ waitLoop: t.Fatalf("Watcher exited with error %v", w.Error()) case event := <-w.Events(): if event.Type != types.OpDelete { - log.Debugf("Skipping stale event %v %v", event.Type, event.Resource.GetName()) + slog.DebugContext(context.Background(), "Skipping stale event", + "event_type", event.Type, + "resource_name", event.Resource.GetName(), + ) continue } diff --git a/lib/services/traits.go b/lib/services/traits.go index 4c6fa2294dbf1..fb27619f98539 100644 --- a/lib/services/traits.go +++ b/lib/services/traits.go @@ -19,11 +19,12 @@ package services import ( + "context" "fmt" + "log/slog" "regexp" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" @@ -144,13 +145,19 @@ TraitMappingLoop: // show at most maxMismatchedTraitValuesLogged trait values to prevent huge log lines switch l := len(mismatched); { case l > maxMismatchedTraitValuesLogged: - log.WithField("expression", mapping.Value). - WithField("values", mismatched[0:maxMismatchedTraitValuesLogged]). - Debugf("%d trait value(s) did not match (showing first %d values)", len(mismatched), maxMismatchedTraitValuesLogged) + slog. + DebugContext(context.Background(), "trait value(s) did not match (showing first %d values)", + "mismatch_count", len(mismatched), + "max_mismatch_logged", maxMismatchedTraitValuesLogged, + "expression", mapping.Value, + "values", mismatched[0:maxMismatchedTraitValuesLogged], + ) case l > 0: - log.WithField("expression", mapping.Value). - WithField("values", mismatched). - Debugf("%d trait value(s) did not match", len(mismatched)) + slog.DebugContext(context.Background(), "trait value(s) did not match", + "mismatch_count", len(mismatched), + "expression", mapping.Value, + "values", mismatched, + ) } } } diff --git a/lib/services/unified_resource.go b/lib/services/unified_resource.go index 2cf2e1c89995f..1bf8bd95747c0 100644 --- a/lib/services/unified_resource.go +++ b/lib/services/unified_resource.go @@ -20,6 +20,7 @@ package services import ( "context" + "log/slog" "strings" "sync" "time" @@ -28,7 +29,6 @@ import ( "github.com/google/btree" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" @@ -37,6 +37,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" "github.com/gravitational/teleport/lib/utils/pagination" ) @@ -68,9 +69,9 @@ type UnifiedResourceCacheConfig struct { // UnifiedResourceCache contains a representation of all resources that are displayable in the UI type UnifiedResourceCache struct { - rw sync.RWMutex - log *log.Entry - cfg UnifiedResourceCacheConfig + rw sync.RWMutex + logger *slog.Logger + cfg UnifiedResourceCacheConfig // nameTree is a BTree with items sorted by (hostname)/name/type nameTree *btree.BTreeG[*item] // typeTree is a BTree with items sorted by type/(hostname)/name @@ -101,10 +102,8 @@ func NewUnifiedResourceCache(ctx context.Context, cfg UnifiedResourceCacheConfig } m := &UnifiedResourceCache{ - log: log.WithFields(log.Fields{ - teleport.ComponentKey: cfg.Component, - }), - cfg: cfg, + logger: slog.With(teleport.ComponentKey, cfg.Component), + cfg: cfg, nameTree: btree.NewG(cfg.BTreeDegree, func(a, b *item) bool { return a.Less(b) }), @@ -267,7 +266,10 @@ func (c *UnifiedResourceCache) getRange(ctx context.Context, startKey backend.Ke } if len(res) == backend.DefaultRangeLimit { - c.log.Warnf("Range query hit backend limit. (this is a bug!) startKey=%q,limit=%d", startKey, backend.DefaultRangeLimit) + c.logger.WarnContext(ctx, "Range query hit backend limit. (this is a bug!)", + "start_key", startKey, + "range_limit", backend.DefaultRangeLimit, + ) } return res, nextKey, nil @@ -747,7 +749,11 @@ func (c *UnifiedResourceCache) processEventsAndUpdateCurrent(ctx context.Context for _, event := range events { if event.Resource == nil { - c.log.Warnf("Unexpected event: %v.", event) + c.logger.WarnContext(ctx, "Unexpected event", + "event_type", event.Type, + "resource_kind", event.Resource.GetKind(), + "resource_name", event.Resource.GetName(), + ) continue } @@ -775,15 +781,15 @@ func (c *UnifiedResourceCache) processEventsAndUpdateCurrent(ctx context.Context c.putLocked(types.Resource153ToUnifiedResource(unwrapped)) default: - c.log.Warnf("unsupported Resource153 type %T.", unwrapped) + c.logger.WarnContext(ctx, "unsupported Resource153 type", "resource_type", logutils.TypeAttr(unwrapped)) } default: - c.log.Warnf("unsupported Resource type %T.", r) + c.logger.WarnContext(ctx, "unsupported Resource type", "resource_type", logutils.TypeAttr(r)) } default: - c.log.Warnf("unsupported event type %s.", event.Type) + c.logger.WarnContext(ctx, "unsupported event type", "event_type", event.Type) continue } } diff --git a/lib/srv/alpnproxy/forward_proxy.go b/lib/srv/alpnproxy/forward_proxy.go index 1ea1811ff004d..f1fec62a8fab1 100644 --- a/lib/srv/alpnproxy/forward_proxy.go +++ b/lib/srv/alpnproxy/forward_proxy.go @@ -175,7 +175,27 @@ func MatchAllRequests(req *http.Request) bool { // MatchAWSRequests is a MatchFunc that returns true if request is an AWS API // request. func MatchAWSRequests(req *http.Request) bool { - return awsapiutils.IsAWSEndpoint(req.Host) + return awsapiutils.IsAWSEndpoint(req.Host) && + // Avoid proxying SSM session WebSocket requests and let the forward proxy + // send it directly to AWS. + // + // `aws ssm start-session` first calls ssm..amazonaws.com to get + // a stream URL and a token. Then it makes a wss connection with the + // provided token to the provided stream URL. The stream URL looks like: + // wss://ssmmessages.region.amazonaws.com/v1/data-channel/session-id?stream=(input|output) + // + // The wss request currently respects HTTPS_PROXY but does not + // respect local CA bundle we provided thus causing a failure. The + // request is not signed with SigV4 either. + // + // Reference: + // https://github.com/aws/session-manager-plugin/ + !isAWSSSMWebsocketRequest(req) +} + +func isAWSSSMWebsocketRequest(req *http.Request) bool { + return awsapiutils.IsAWSEndpoint(req.Host) && + strings.HasPrefix(req.Host, "ssmmessages.") } // MatchAzureRequests is a MatchFunc that returns true if request is an Azure API diff --git a/lib/srv/alpnproxy/forward_proxy_test.go b/lib/srv/alpnproxy/forward_proxy_test.go index 118d0c3f825f6..c5970e08d9085 100644 --- a/lib/srv/alpnproxy/forward_proxy_test.go +++ b/lib/srv/alpnproxy/forward_proxy_test.go @@ -247,3 +247,43 @@ func TestMatchGCPRequests(t *testing.T) { }) } } + +func TestMatchAWSRequests(t *testing.T) { + makeRequest := func(url string) *http.Request { + // Forward proxy always receives CONNECT requests. + request, err := http.NewRequest("CONNECT", url, nil) + require.NoError(t, err) + return request + } + tests := []struct { + name string + req *http.Request + check require.BoolAssertionFunc + }{ + { + name: "AWS request", + req: makeRequest("http://s3.ca-central-1.amazonaws.com"), + check: require.True, + }, + { + name: "non-AWS request", + req: makeRequest("https://registry.terraform.io"), + check: require.False, + }, + { + name: "SSM API", + req: makeRequest("https://ssm.ca-central-1.amazonaws.com"), + check: require.True, + }, + { + name: "SSM session WebSocket", + req: makeRequest("wss://ssmmessages.ca-central-1.amazonaws.com"), + check: require.False, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.check(t, MatchAWSRequests(tt.req)) + }) + } +} diff --git a/lib/srv/db/access_test.go b/lib/srv/db/access_test.go index 8d415fc8953c0..46c6ca1a19f53 100644 --- a/lib/srv/db/access_test.go +++ b/lib/srv/db/access_test.go @@ -61,6 +61,7 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/authz" clients "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/cloud/mocks" "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" @@ -1629,13 +1630,13 @@ func (c *testContext) startHandlingConnections() { // postgresClient connects to test Postgres through database access as a // specified Teleport user and database account. -func (c *testContext) postgresClient(ctx context.Context, teleportUser, dbService, dbUser, dbName string) (*pgconn.PgConn, error) { - return c.postgresClientWithAddr(ctx, c.mux.DB().Addr().String(), teleportUser, dbService, dbUser, dbName) +func (c *testContext) postgresClient(ctx context.Context, teleportUser, dbService, dbUser, dbName string, opts ...common.ClientOption) (*pgconn.PgConn, error) { + return c.postgresClientWithAddr(ctx, c.mux.DB().Addr().String(), teleportUser, dbService, dbUser, dbName, opts...) } // postgresClientWithAddr is like postgresClient but allows to override connection address. -func (c *testContext) postgresClientWithAddr(ctx context.Context, address, teleportUser, dbService, dbUser, dbName string) (*pgconn.PgConn, error) { - return postgres.MakeTestClient(ctx, common.TestClientConfig{ +func (c *testContext) postgresClientWithAddr(ctx context.Context, address, teleportUser, dbService, dbUser, dbName string, opts ...common.ClientOption) (*pgconn.PgConn, error) { + cfg := common.TestClientConfig{ AuthClient: c.authClient, AuthServer: c.authServer, Address: address, @@ -1647,7 +1648,13 @@ func (c *testContext) postgresClientWithAddr(ctx context.Context, address, telep Username: dbUser, Database: dbName, }, - }) + } + + for _, opt := range opts { + opt(&cfg) + } + + return postgres.MakeTestClient(ctx, cfg) } // postgresClientLocalProxy connects to test Postgres through local ALPN proxy. @@ -2441,6 +2448,8 @@ type agentParams struct { CADownloader CADownloader // CloudClients is the cloud API clients for database service. CloudClients clients.Clients + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider // AWSMatchers is a list of AWS databases matchers. AWSMatchers []types.AWSMatcher // AzureMatchers is a list of Azure databases matchers. @@ -2481,9 +2490,8 @@ func (p *agentParams) setDefaults(c *testContext) { if p.CloudClients == nil { p.CloudClients = &clients.TestCloudClients{ - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, RDS: &mocks.RDSMock{}, - Redshift: &mocks.RedshiftMock{}, RedshiftServerless: &mocks.RedshiftServerlessMock{}, ElastiCache: p.ElastiCache, MemoryDB: p.MemoryDB, @@ -2492,6 +2500,9 @@ func (p *agentParams) setDefaults(c *testContext) { GCPSQL: p.GCPSQL, } } + if p.AWSConfigProvider == nil { + p.AWSConfigProvider = &mocks.AWSConfigProvider{} + } if p.DiscoveryResourceChecker == nil { p.DiscoveryResourceChecker = &fakeDiscoveryResourceChecker{} @@ -2524,10 +2535,11 @@ func (c *testContext) setupDatabaseServer(ctx context.Context, t testing.TB, p a // Create test database auth tokens generator. testAuth, err := newTestAuth(common.AuthConfig{ - AuthClient: c.authClient, - AccessPoint: c.authClient, - Clients: &clients.TestCloudClients{}, - Clock: c.clock, + AuthClient: c.authClient, + AccessPoint: c.authClient, + Clients: &clients.TestCloudClients{}, + Clock: c.clock, + AWSConfigProvider: &mocks.AWSConfigProvider{}, }) require.NoError(t, err) @@ -2596,6 +2608,7 @@ func (c *testContext) setupDatabaseServer(ctx context.Context, t testing.TB, p a OnReconcile: p.OnReconcile, ConnectionMonitor: connMonitor, CloudClients: p.CloudClients, + AWSConfigProvider: p.AWSConfigProvider, AWSMatchers: p.AWSMatchers, AzureMatchers: p.AzureMatchers, ShutdownPollPeriod: 100 * time.Millisecond, diff --git a/lib/srv/db/audit_test.go b/lib/srv/db/audit_test.go index 50eab78e98914..37aa736fad253 100644 --- a/lib/srv/db/audit_test.go +++ b/lib/srv/db/audit_test.go @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport/lib/defaults" libevents "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/events/eventstest" + "github.com/gravitational/teleport/lib/srv/db/common" "github.com/gravitational/teleport/lib/srv/db/postgres" "github.com/gravitational/teleport/lib/srv/db/redis" ) @@ -59,12 +60,14 @@ func TestAuditPostgres(t *testing.T) { requireEvent(t, testCtx, libevents.DatabaseSessionStartFailureCode) // Connect should trigger successful session start event. - psql, err := testCtx.postgresClient(ctx, "alice", "postgres", "postgres", "postgres") + userAgent := "psql" + psql, err := testCtx.postgresClient(ctx, "alice", "postgres", "postgres", "postgres", common.WithUserAgent(userAgent)) require.NoError(t, err) startEvt, ok := requireEvent(t, testCtx, libevents.DatabaseSessionStartCode).(*events.DatabaseSessionStart) require.True(t, ok) require.NotNil(t, startEvt) require.NotZero(t, startEvt.PostgresPID) + require.Equal(t, userAgent, startEvt.ClientMetadata.UserAgent) // Simple query should trigger the query event. _, err = psql.Exec(ctx, "select 1").ReadAll() diff --git a/lib/srv/db/cloud/aws.go b/lib/srv/db/cloud/aws.go index 706581952b9d2..8222599c318a7 100644 --- a/lib/srv/db/cloud/aws.go +++ b/lib/srv/db/cloud/aws.go @@ -75,7 +75,7 @@ func newAWS(ctx context.Context, config awsConfig) (*awsClient, error) { teleport.ComponentKey, "aws", "db", config.database.GetName(), ) - dbConfigurator, err := getDBConfigurator(ctx, logger, config.clients, config.database) + dbConfigurator, err := getDBConfigurator(logger, config.clients, config.database) if err != nil { return nil, trace.Wrap(err) } @@ -102,7 +102,7 @@ type dbIAMAuthConfigurator interface { } // getDBConfigurator returns a database IAM Auth configurator. -func getDBConfigurator(ctx context.Context, logger *slog.Logger, clients cloud.Clients, db types.Database) (dbIAMAuthConfigurator, error) { +func getDBConfigurator(logger *slog.Logger, clients cloud.Clients, db types.Database) (dbIAMAuthConfigurator, error) { if db.IsRDS() { // Only setting for RDS instances and Aurora clusters. return &rdsDBConfigurator{clients: clients, logger: logger}, nil diff --git a/lib/srv/db/cloud/iam_test.go b/lib/srv/db/cloud/iam_test.go index e55c94345fc33..d13d1fc74b86c 100644 --- a/lib/srv/db/cloud/iam_test.go +++ b/lib/srv/db/cloud/iam_test.go @@ -28,7 +28,6 @@ import ( "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/google/uuid" "github.com/gravitational/trace" "github.com/stretchr/testify/require" @@ -59,13 +58,8 @@ func TestAWSIAM(t *testing.T) { DbClusterResourceId: aws.String("cluster-xyz"), } - redshiftCluster := &redshift.Cluster{ - ClusterNamespaceArn: aws.String("arn:aws:redshift:us-east-2:123456789012:namespace:namespace-xyz"), - ClusterIdentifier: aws.String("redshift-cluster-1"), - } - // Configure mocks. - stsClient := &mocks.STSMock{ + stsClient := &mocks.STSClientV1{ ARN: "arn:aws:iam::123456789012:role/test-role", } @@ -74,10 +68,6 @@ func TestAWSIAM(t *testing.T) { DBClusters: []*rds.DBCluster{auroraCluster}, } - redshiftClient := &mocks.RedshiftMock{ - Clusters: []*redshift.Cluster{redshiftCluster}, - } - iamClient := &mocks.IAMMock{} // Setup database resources. @@ -163,10 +153,9 @@ func TestAWSIAM(t *testing.T) { configurator, err := NewIAM(ctx, IAMConfig{ AccessPoint: &mockAccessPoint{}, Clients: &clients.TestCloudClients{ - RDS: rdsClient, - Redshift: redshiftClient, - STS: stsClient, - IAM: iamClient, + RDS: rdsClient, + STS: stsClient, + IAM: iamClient, }, HostID: "host-id", onProcessedTask: func(iamTask, error) { @@ -294,7 +283,7 @@ func TestAWSIAMNoPermissions(t *testing.T) { t.Cleanup(cancel) // Create unauthorized mocks for AWS services. - stsClient := &mocks.STSMock{ + stsClient := &mocks.STSClientV1{ ARN: "arn:aws:iam::123456789012:role/test-role", } // Make configurator. @@ -347,7 +336,6 @@ func TestAWSIAMNoPermissions(t *testing.T) { name: "Redshift cluster", meta: types.AWS{Region: "localhost", AccountID: "123456789012", Redshift: types.Redshift{ClusterID: "redshift-cluster-1"}}, clients: &clients.TestCloudClients{ - Redshift: &mocks.RedshiftMockUnauth{}, IAM: &mocks.IAMErrorMock{ Error: trace.AccessDenied("unauthorized"), }, @@ -371,7 +359,6 @@ func TestAWSIAMNoPermissions(t *testing.T) { name: "IAM UnmodifiableEntityException", meta: types.AWS{Region: "localhost", AccountID: "123456789012", Redshift: types.Redshift{ClusterID: "redshift-cluster-1"}}, clients: &clients.TestCloudClients{ - Redshift: &mocks.RedshiftMockUnauth{}, IAM: &mocks.IAMErrorMock{ Error: awserr.New(iam.ErrCodeUnmodifiableEntityException, "unauthorized", fmt.Errorf("unauthorized")), }, diff --git a/lib/srv/db/cloud/meta.go b/lib/srv/db/cloud/meta.go index 515ff0d83ecbe..031f9fb9dae4c 100644 --- a/lib/srv/db/cloud/meta.go +++ b/lib/srv/db/cloud/meta.go @@ -23,15 +23,15 @@ import ( "log/slog" "strings" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/elasticache/elasticacheiface" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/memorydb/memorydbiface" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/rds/rdsiface" - "github.com/aws/aws-sdk-go/service/redshift" - "github.com/aws/aws-sdk-go/service/redshift/redshiftiface" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/aws/aws-sdk-go/service/redshiftserverless/redshiftserverlessiface" "github.com/gravitational/trace" @@ -39,15 +39,30 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/srv/db/common" discoverycommon "github.com/gravitational/teleport/lib/srv/discovery/common" logutils "github.com/gravitational/teleport/lib/utils/log" ) +// redshiftClient defines a subset of the AWS Redshift client API. +type redshiftClient interface { + redshift.DescribeClustersAPIClient +} + +// redshiftClientProviderFunc provides a [redshiftClient]. +type redshiftClientProviderFunc func(cfg aws.Config, optFns ...func(*redshift.Options)) redshiftClient + // MetadataConfig is the cloud metadata service config. type MetadataConfig struct { // Clients is an interface for retrieving cloud clients. Clients cloud.Clients + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider + + // redshiftClientProviderFn is an internal-only [redshiftClient] provider + // func that is only set in tests. + redshiftClientProviderFn redshiftClientProviderFunc } // Check validates the metadata service config. @@ -59,6 +74,15 @@ func (c *MetadataConfig) Check() error { } c.Clients = cloudClients } + if c.AWSConfigProvider == nil { + return trace.BadParameter("missing AWSConfigProvider") + } + + if c.redshiftClientProviderFn == nil { + c.redshiftClientProviderFn = func(cfg aws.Config, optFns ...func(*redshift.Options)) redshiftClient { + return redshift.NewFromConfig(cfg, optFns...) + } + } return nil } @@ -177,13 +201,14 @@ func (m *Metadata) fetchRDSProxyMetadata(ctx context.Context, database types.Dat // fetchRedshiftMetadata fetches metadata for the provided Redshift database. func (m *Metadata) fetchRedshiftMetadata(ctx context.Context, database types.Database) (*types.AWS, error) { meta := database.GetAWS() - redshift, err := m.cfg.Clients.GetAWSRedshiftClient(ctx, meta.Region, - cloud.WithAssumeRoleFromAWSMeta(meta), - cloud.WithAmbientCredentials(), + awsCfg, err := m.cfg.AWSConfigProvider.GetConfig(ctx, meta.Region, + awsconfig.WithAssumeRole(meta.AssumeRoleARN, meta.ExternalID), + awsconfig.WithAmbientCredentials(), ) if err != nil { return nil, trace.Wrap(err) } + redshift := m.cfg.redshiftClientProviderFn(awsCfg) cluster, err := describeRedshiftCluster(ctx, redshift, meta.Redshift.ClusterID) if err != nil { return nil, trace.Wrap(err) @@ -296,8 +321,8 @@ func describeRDSCluster(ctx context.Context, rdsClient rdsiface.RDSAPI, clusterI } // describeRedshiftCluster returns AWS Redshift cluster for the specified ID. -func describeRedshiftCluster(ctx context.Context, redshiftClient redshiftiface.RedshiftAPI, clusterID string) (*redshift.Cluster, error) { - out, err := redshiftClient.DescribeClustersWithContext(ctx, &redshift.DescribeClustersInput{ +func describeRedshiftCluster(ctx context.Context, clt redshiftClient, clusterID string) (*redshifttypes.Cluster, error) { + out, err := clt.DescribeClusters(ctx, &redshift.DescribeClustersInput{ ClusterIdentifier: aws.String(clusterID), }) if err != nil { @@ -306,7 +331,7 @@ func describeRedshiftCluster(ctx context.Context, redshiftClient redshiftiface.R if len(out.Clusters) != 1 { return nil, trace.BadParameter("expected 1 Redshift cluster for %v, got %+v", clusterID, out.Clusters) } - return out.Clusters[0], nil + return &out.Clusters[0], nil } // describeElastiCacheCluster returns AWS ElastiCache Redis cluster for the @@ -369,7 +394,7 @@ func fetchRDSProxyCustomEndpointMetadata(ctx context.Context, rdsClient rdsiface return nil, trace.Wrap(err) } - rdsProxy, err := describeRDSProxy(ctx, rdsClient, aws.StringValue(rdsProxyEndpoint.DBProxyName)) + rdsProxy, err := describeRDSProxy(ctx, rdsClient, aws.ToString(rdsProxyEndpoint.DBProxyName)) if err != nil { return nil, trace.Wrap(err) } @@ -389,7 +414,7 @@ func describeRDSProxyCustomEndpointAndFindURI(ctx context.Context, rdsClient rds for _, customEndpoint := range out.DBProxyEndpoints { // Double check if it has the same URI in case multiple custom // endpoints have the same name. - if strings.Contains(uri, aws.StringValue(customEndpoint.Endpoint)) { + if strings.Contains(uri, aws.ToString(customEndpoint.Endpoint)) { return customEndpoint, nil } } @@ -408,7 +433,7 @@ func fetchRedshiftServerlessVPCEndpointMetadata(ctx context.Context, client reds if err != nil { return nil, trace.Wrap(err) } - workgroup, err := describeRedshiftServerlessWorkgroup(ctx, client, aws.StringValue(endpoint.WorkgroupName)) + workgroup, err := describeRedshiftServerlessWorkgroup(ctx, client, aws.ToString(endpoint.WorkgroupName)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/srv/db/cloud/meta_test.go b/lib/srv/db/cloud/meta_test.go index c4eb033360f13..9e66a416a2ebb 100644 --- a/lib/srv/db/cloud/meta_test.go +++ b/lib/srv/db/cloud/meta_test.go @@ -22,11 +22,12 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/stretchr/testify/require" @@ -78,8 +79,8 @@ func TestAWSMetadata(t *testing.T) { } // Configure Redshift API mock. - redshift := &mocks.RedshiftMock{ - Clusters: []*redshift.Cluster{ + redshiftClt := &mocks.RedshiftClient{ + Clusters: []redshifttypes.Cluster{ { ClusterNamespaceArn: aws.String("arn:aws:redshift:us-west-1:123456789012:namespace:namespace-id"), ClusterIdentifier: aws.String("redshift-cluster-1"), @@ -116,7 +117,7 @@ func TestAWSMetadata(t *testing.T) { }, } - stsMock := &mocks.STSMock{} + fakeSTS := &mocks.STSClient{} // Configure Redshift Serverless API mock. redshiftServerlessWorkgroup := mocks.RedshiftServerlessWorkgroup("my-workgroup", "us-west-1") @@ -130,12 +131,15 @@ func TestAWSMetadata(t *testing.T) { metadata, err := NewMetadata(MetadataConfig{ Clients: &cloud.TestCloudClients{ RDS: rds, - Redshift: redshift, ElastiCache: elasticache, MemoryDB: memorydb, RedshiftServerless: redshiftServerless, - STS: stsMock, + STS: &fakeSTS.STSClientV1, }, + AWSConfigProvider: &mocks.AWSConfigProvider{ + STSClient: fakeSTS, + }, + redshiftClientProviderFn: newFakeRedshiftClientProvider(redshiftClt), }) require.NoError(t, err) @@ -392,9 +396,9 @@ func TestAWSMetadata(t *testing.T) { err = metadata.Update(ctx, database) require.NoError(t, err) require.Equal(t, test.outAWS, database.GetAWS()) - require.Equal(t, []string{test.inAWS.AssumeRoleARN}, stsMock.GetAssumedRoleARNs()) - require.Equal(t, []string{test.inAWS.ExternalID}, stsMock.GetAssumedRoleExternalIDs()) - stsMock.ResetAssumeRoleHistory() + require.Equal(t, []string{test.inAWS.AssumeRoleARN}, fakeSTS.GetAssumedRoleARNs()) + require.Equal(t, []string{test.inAWS.ExternalID}, fakeSTS.GetAssumedRoleExternalIDs()) + fakeSTS.ResetAssumeRoleHistory() }) } } @@ -404,17 +408,20 @@ func TestAWSMetadata(t *testing.T) { func TestAWSMetadataNoPermissions(t *testing.T) { // Create unauthorized mocks. rds := &mocks.RDSMockUnauth{} - redshift := &mocks.RedshiftMockUnauth{} + redshiftClt := &mocks.RedshiftClient{Unauth: true} - stsMock := &mocks.STSMock{} + fakeSTS := &mocks.STSClient{} // Create metadata fetcher. metadata, err := NewMetadata(MetadataConfig{ Clients: &cloud.TestCloudClients{ - RDS: rds, - Redshift: redshift, - STS: stsMock, + RDS: rds, + STS: &fakeSTS.STSClientV1, + }, + AWSConfigProvider: &mocks.AWSConfigProvider{ + STSClient: fakeSTS, }, + redshiftClientProviderFn: newFakeRedshiftClientProvider(redshiftClt), }) require.NoError(t, err) @@ -480,9 +487,15 @@ func TestAWSMetadataNoPermissions(t *testing.T) { err = metadata.Update(ctx, database) require.NoError(t, err) require.Equal(t, test.meta, database.GetAWS()) - require.Equal(t, []string{test.meta.AssumeRoleARN}, stsMock.GetAssumedRoleARNs()) - require.Equal(t, []string{test.meta.ExternalID}, stsMock.GetAssumedRoleExternalIDs()) - stsMock.ResetAssumeRoleHistory() + require.Equal(t, []string{test.meta.AssumeRoleARN}, fakeSTS.GetAssumedRoleARNs()) + require.Equal(t, []string{test.meta.ExternalID}, fakeSTS.GetAssumedRoleExternalIDs()) + fakeSTS.ResetAssumeRoleHistory() }) } } + +func newFakeRedshiftClientProvider(c redshiftClient) redshiftClientProviderFunc { + return func(cfg aws.Config, optFns ...func(*redshift.Options)) redshiftClient { + return c + } +} diff --git a/lib/srv/db/cloud/resource_checker.go b/lib/srv/db/cloud/resource_checker.go index 14b5393cebcc9..ea38c69abecf4 100644 --- a/lib/srv/db/cloud/resource_checker.go +++ b/lib/srv/db/cloud/resource_checker.go @@ -27,6 +27,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/services" ) @@ -40,6 +41,8 @@ type DiscoveryResourceChecker interface { // DiscoveryResourceCheckerConfig is the config for DiscoveryResourceChecker. type DiscoveryResourceCheckerConfig struct { + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider // ResourceMatchers is a list of database resource matchers. ResourceMatchers []services.ResourceMatcher // Clients is an interface for retrieving cloud clients. @@ -59,6 +62,9 @@ func (c *DiscoveryResourceCheckerConfig) CheckAndSetDefaults() error { } c.Clients = cloudClients } + if c.AWSConfigProvider == nil { + return trace.BadParameter("missing AWSConfigProvider") + } if c.Context == nil { c.Context = context.Background() } diff --git a/lib/srv/db/cloud/resource_checker_url.go b/lib/srv/db/cloud/resource_checker_url.go index 4ca6ed0b0b081..fdc4efdb65fe9 100644 --- a/lib/srv/db/cloud/resource_checker_url.go +++ b/lib/srv/db/cloud/resource_checker_url.go @@ -27,17 +27,25 @@ import ( "slices" "sync" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" "github.com/gravitational/trace" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils" apiawsutils "github.com/gravitational/teleport/api/utils/aws" "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" ) // urlChecker validates the database has the correct URL. type urlChecker struct { + // awsConfigProvider provides [aws.Config] for AWS SDK service clients. + awsConfigProvider awsconfig.Provider + // redshiftClientProviderFn is an internal-only [redshiftClient] provider + // func that is only set in tests. + redshiftClientProviderFn redshiftClientProviderFunc + clients cloud.Clients logger *slog.Logger warnOnError bool @@ -52,6 +60,10 @@ type urlChecker struct { func newURLChecker(cfg DiscoveryResourceCheckerConfig) *urlChecker { return &urlChecker{ + awsConfigProvider: cfg.AWSConfigProvider, + redshiftClientProviderFn: func(cfg aws.Config, optFns ...func(*redshift.Options)) redshiftClient { + return redshift.NewFromConfig(cfg, optFns...) + }, clients: cfg.Clients, logger: cfg.Logger, warnOnError: getWarnOnError(), @@ -121,8 +133,13 @@ func requireDatabaseIsEndpoint(ctx context.Context, database types.Database, isE return trace.Wrap(convIsEndpoint(isEndpoint)(ctx, database)) } -func requireDatabaseAddressPort(database types.Database, wantURLHost *string, wantURLPort *int64) error { - wantURL := fmt.Sprintf("%v:%v", aws.StringValue(wantURLHost), aws.Int64Value(wantURLPort)) +// TODO(gavin): remove the generic type parameter after all callers are migrated from AWS SDK v1 (uses *int64) to SDK v2 (uses *int32). +func requireDatabaseAddressPort[T ~int32 | ~int64](database types.Database, wantURLHost *string, wantURLPort *T) error { + var port int + if wantURLPort != nil { + port = int(*wantURLPort) + } + wantURL := fmt.Sprintf("%v:%v", aws.ToString(wantURLHost), port) if database.GetURI() != wantURL { return trace.BadParameter("expect database URL %q but got %q for database %q", wantURL, database.GetURI(), database.GetName()) } diff --git a/lib/srv/db/cloud/resource_checker_url_aws.go b/lib/srv/db/cloud/resource_checker_url_aws.go index 87a55c4d26f13..336ee197815fb 100644 --- a/lib/srv/db/cloud/resource_checker_url_aws.go +++ b/lib/srv/db/cloud/resource_checker_url_aws.go @@ -32,6 +32,7 @@ import ( apiawsutils "github.com/gravitational/teleport/api/utils/aws" "github.com/gravitational/teleport/lib/cloud" cloudaws "github.com/gravitational/teleport/lib/cloud/aws" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/srv/discovery/common" ) @@ -60,13 +61,23 @@ func (c *urlChecker) checkAWS(describeCheck, basicEndpointCheck checkDatabaseFun } } +const awsPermissionsErrMsg = "" + + "No permissions to describe AWS resource metadata that is needed for validating databases created by Discovery Service. " + + "Basic AWS endpoint validation will be performed instead. For best security, please provide the Database Service with the proper IAM permissions. " + + "Enable --debug mode to see details on which databases require more IAM permissions. See Database Access documentation for more details." + func (c *urlChecker) logAWSAccessDeniedError(ctx context.Context, database types.Database, accessDeniedError error) { c.warnAWSOnce.Do(func() { // TODO(greedy52) add links to doc. - c.logger.WarnContext(ctx, "No permissions to describe AWS resource metadata that is needed for validating databases created by Discovery Service. Basic AWS endpoint validation will be performed instead. For best security, please provide the Database Service with the proper IAM permissions. Enable --debug mode to see details on which databases require more IAM permissions. See Database Access documentation for more details.") + c.logger.WarnContext(ctx, awsPermissionsErrMsg, + "error", accessDeniedError, + ) }) - c.logger.DebugContext(ctx, "No permissions to describe database for URL validation", "database", database.GetName()) + c.logger.DebugContext(ctx, "No permissions to describe database for URL validation", + "database", database.GetName(), + "error", accessDeniedError, + ) } func (c *urlChecker) checkRDS(ctx context.Context, database types.Database) error { @@ -149,13 +160,14 @@ func (c *urlChecker) checkRDSProxyCustomEndpoint(ctx context.Context, database t func (c *urlChecker) checkRedshift(ctx context.Context, database types.Database) error { meta := database.GetAWS() - redshift, err := c.clients.GetAWSRedshiftClient(ctx, meta.Region, - cloud.WithAssumeRoleFromAWSMeta(meta), - cloud.WithAmbientCredentials(), + awsCfg, err := c.awsConfigProvider.GetConfig(ctx, meta.Region, + awsconfig.WithAssumeRole(meta.AssumeRoleARN, meta.ExternalID), + awsconfig.WithAmbientCredentials(), ) if err != nil { return trace.Wrap(err) } + redshift := c.redshiftClientProviderFn(awsCfg) cluster, err := describeRedshiftCluster(ctx, redshift, meta.Redshift.ClusterID) if err != nil { return trace.Wrap(err) diff --git a/lib/srv/db/cloud/resource_checker_url_aws_test.go b/lib/srv/db/cloud/resource_checker_url_aws_test.go index 81928cbd7902c..e8ba24f624c16 100644 --- a/lib/srv/db/cloud/resource_checker_url_aws_test.go +++ b/lib/srv/db/cloud/resource_checker_url_aws_test.go @@ -22,17 +22,18 @@ import ( "context" "testing" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/opensearchservice" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" apiawsutils "github.com/gravitational/teleport/api/utils/aws" "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/cloud/mocks" "github.com/gravitational/teleport/lib/srv/discovery/common" "github.com/gravitational/teleport/lib/utils" @@ -69,7 +70,7 @@ func TestURLChecker_AWS(t *testing.T) { // Redshift. redshiftCluster := mocks.RedshiftCluster("redshift-cluster", region, nil) - redshiftClusterDB, err := common.NewDatabaseFromRedshiftCluster(redshiftCluster) + redshiftClusterDB, err := common.NewDatabaseFromRedshiftCluster(&redshiftCluster) require.NoError(t, err) testCases = append(testCases, redshiftClusterDB) @@ -126,9 +127,6 @@ func TestURLChecker_AWS(t *testing.T) { DBProxies: []*rds.DBProxy{rdsProxy}, DBProxyEndpoints: []*rds.DBProxyEndpoint{rdsProxyCustomEndpoint}, }, - Redshift: &mocks.RedshiftMock{ - Clusters: []*redshift.Cluster{redshiftCluster}, - }, RedshiftServerless: &mocks.RedshiftServerlessMock{ Workgroups: []*redshiftserverless.Workgroup{redshiftServerlessWorkgroup}, Endpoints: []*redshiftserverless.EndpointAccess{redshiftServerlessVPCEndpoint}, @@ -142,41 +140,50 @@ func TestURLChecker_AWS(t *testing.T) { OpenSearch: &mocks.OpenSearchMock{ Domains: []*opensearchservice.DomainStatus{openSearchDomain, openSearchVPCDomain}, }, - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, } mockClientsUnauth := &cloud.TestCloudClients{ RDS: &mocks.RDSMockUnauth{}, - Redshift: &mocks.RedshiftMockUnauth{}, RedshiftServerless: &mocks.RedshiftServerlessMock{Unauth: true}, ElastiCache: &mocks.ElastiCacheMock{Unauth: true}, MemoryDB: &mocks.MemoryDBMock{Unauth: true}, OpenSearch: &mocks.OpenSearchMock{Unauth: true}, - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, } // Test both check methods. // Note that "No permissions" logs should only be printed during the second // group ("basic endpoint check"). methods := []struct { - name string - clients cloud.Clients + name string + clients cloud.Clients + awsConfigProvider awsconfig.Provider + redshiftClient redshiftClient }{ { - name: "API check", - clients: mockClients, + name: "API check", + clients: mockClients, + awsConfigProvider: &mocks.AWSConfigProvider{}, + redshiftClient: &mocks.RedshiftClient{ + Clusters: []redshifttypes.Cluster{redshiftCluster}, + }, }, { - name: "basic endpoint check", - clients: mockClientsUnauth, + name: "basic endpoint check", + clients: mockClientsUnauth, + awsConfigProvider: &mocks.AWSConfigProvider{}, + redshiftClient: &mocks.RedshiftClient{Unauth: true}, }, } for _, method := range methods { t.Run(method.name, func(t *testing.T) { c := newURLChecker(DiscoveryResourceCheckerConfig{ - Clients: method.clients, - Logger: utils.NewSlogLoggerForTests(), + Clients: method.clients, + AWSConfigProvider: method.awsConfigProvider, + Logger: utils.NewSlogLoggerForTests(), }) + c.redshiftClientProviderFn = newFakeRedshiftClientProvider(method.redshiftClient) for _, database := range testCases { t.Run(database.GetName(), func(t *testing.T) { diff --git a/lib/srv/db/common/audit.go b/lib/srv/db/common/audit.go index 64b6bd1de9bd7..4ade27fa861a3 100644 --- a/lib/srv/db/common/audit.go +++ b/lib/srv/db/common/audit.go @@ -146,6 +146,9 @@ func (a *audit) OnSessionStart(ctx context.Context, session *Session, sessionErr Success: true, }, PostgresPID: session.PostgresPID, + ClientMetadata: events.ClientMetadata{ + UserAgent: session.UserAgent, + }, } event.SetTime(session.StartTime) diff --git a/lib/srv/db/common/auth.go b/lib/srv/db/common/auth.go index 43f5df408ab85..e567d82d402e0 100644 --- a/lib/srv/db/common/auth.go +++ b/lib/srv/db/common/auth.go @@ -34,13 +34,13 @@ import ( gcpcredentialspb "cloud.google.com/go/iam/credentials/apiv1/credentialspb" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" "github.com/aws/aws-sdk-go/aws/credentials" v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/rds/rdsutils" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -55,6 +55,7 @@ import ( "github.com/gravitational/teleport/api/utils/retryutils" "github.com/gravitational/teleport/lib/cloud" awslib "github.com/gravitational/teleport/lib/cloud/aws" + "github.com/gravitational/teleport/lib/cloud/awsconfig" libazure "github.com/gravitational/teleport/lib/cloud/azure" "github.com/gravitational/teleport/lib/cloud/gcp" "github.com/gravitational/teleport/lib/cryptosuites" @@ -124,6 +125,15 @@ type AccessPoint interface { GetAuthPreference(ctx context.Context) (types.AuthPreference, error) } +// redshiftClient defines a subset of the AWS Redshift client API. +type redshiftClient interface { + GetClusterCredentialsWithIAM(context.Context, *redshift.GetClusterCredentialsWithIAMInput, ...func(*redshift.Options)) (*redshift.GetClusterCredentialsWithIAMOutput, error) + GetClusterCredentials(context.Context, *redshift.GetClusterCredentialsInput, ...func(*redshift.Options)) (*redshift.GetClusterCredentialsOutput, error) +} + +// redshiftClientProviderFunc provides a [redshiftClient]. +type redshiftClientProviderFunc func(cfg aws.Config, optFns ...func(*redshift.Options)) redshiftClient + // AuthConfig is the database access authenticator configuration. type AuthConfig struct { // AuthClient is the cluster auth client. @@ -136,6 +146,13 @@ type AuthConfig struct { Clock clockwork.Clock // Logger is used for logging. Logger *slog.Logger + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider + + // redshiftClientProviderFn is an internal-only [redshiftClient] provider + // func that defaults to a func that provides a real Redshift client. + // The default is only overridden in tests. + redshiftClientProviderFn redshiftClientProviderFunc } // CheckAndSetDefaults validates the config and sets defaults. @@ -149,23 +166,28 @@ func (c *AuthConfig) CheckAndSetDefaults() error { if c.Clients == nil { return trace.BadParameter("missing Clients") } + if c.AWSConfigProvider == nil { + return trace.BadParameter("missing AWSConfigProvider") + } if c.Clock == nil { c.Clock = clockwork.NewRealClock() } if c.Logger == nil { c.Logger = slog.With(teleport.ComponentKey, "db:auth") } + + if c.redshiftClientProviderFn == nil { + c.redshiftClientProviderFn = func(cfg aws.Config, optFns ...func(*redshift.Options)) redshiftClient { + return redshift.NewFromConfig(cfg, optFns...) + } + } return nil } func (c *AuthConfig) withLogger(getUpdatedLogger func(*slog.Logger) *slog.Logger) AuthConfig { - return AuthConfig{ - AuthClient: c.AuthClient, - AccessPoint: c.AccessPoint, - Clients: c.Clients, - Clock: c.Clock, - Logger: getUpdatedLogger(c.Logger), - } + cfg := *c + cfg.Logger = getUpdatedLogger(c.Logger) + return cfg } // dbAuth provides utilities for creating TLS configurations and @@ -272,18 +294,12 @@ func (a *dbAuth) getRedshiftIAMRoleAuthToken(ctx context.Context, database types return "", "", trace.Wrap(err) } - baseSession, err := a.cfg.Clients.GetAWSSession(ctx, meta.Region, - cloud.WithAssumeRoleFromAWSMeta(meta), - cloud.WithAmbientCredentials(), - ) - if err != nil { - return "", "", trace.Wrap(err) - } // Assume the configured AWS role before assuming the role we need to get the // auth token. This allows cross-account AWS access. - client, err := a.cfg.Clients.GetAWSRedshiftClient(ctx, meta.Region, - cloud.WithChainedAssumeRole(baseSession, roleARN, externalIDForChainedAssumeRole(meta)), - cloud.WithAmbientCredentials(), + awsCfg, err := a.cfg.AWSConfigProvider.GetConfig(ctx, meta.Region, + awsconfig.WithAssumeRole(meta.AssumeRoleARN, meta.ExternalID), + awsconfig.WithAssumeRole(roleARN, externalIDForChainedAssumeRole(meta)), + awsconfig.WithAmbientCredentials(), ) if err != nil { return "", "", trace.AccessDenied(`Could not generate Redshift IAM role auth token: @@ -300,7 +316,8 @@ Make sure that IAM role %q has a trust relationship with Teleport database agent "database_user", databaseUser, "database_name", databaseName, ) - resp, err := client.GetClusterCredentialsWithIAMWithContext(ctx, &redshift.GetClusterCredentialsWithIAMInput{ + client := a.cfg.redshiftClientProviderFn(awsCfg) + resp, err := client.GetClusterCredentialsWithIAM(ctx, &redshift.GetClusterCredentialsWithIAMInput{ ClusterIdentifier: aws.String(meta.Redshift.ClusterID), DbName: aws.String(databaseName), }) @@ -318,14 +335,14 @@ Make sure that IAM role %q has permissions to generate credentials. Here is a sa %v `, err, roleARN, policy) } - return aws.StringValue(resp.DbUser), aws.StringValue(resp.DbPassword), nil + return aws.ToString(resp.DbUser), aws.ToString(resp.DbPassword), nil } func (a *dbAuth) getRedshiftDBUserAuthToken(ctx context.Context, database types.Database, databaseUser string, databaseName string) (string, string, error) { meta := database.GetAWS() - redshiftClient, err := a.cfg.Clients.GetAWSRedshiftClient(ctx, meta.Region, - cloud.WithAssumeRoleFromAWSMeta(meta), - cloud.WithAmbientCredentials(), + awsCfg, err := a.cfg.AWSConfigProvider.GetConfig(ctx, meta.Region, + awsconfig.WithAssumeRole(meta.AssumeRoleARN, meta.ExternalID), + awsconfig.WithAmbientCredentials(), ) if err != nil { return "", "", trace.Wrap(err) @@ -335,7 +352,8 @@ func (a *dbAuth) getRedshiftDBUserAuthToken(ctx context.Context, database types. "database_user", databaseUser, "database_name", databaseName, ) - resp, err := redshiftClient.GetClusterCredentialsWithContext(ctx, &redshift.GetClusterCredentialsInput{ + clt := a.cfg.redshiftClientProviderFn(awsCfg) + resp, err := clt.GetClusterCredentials(ctx, &redshift.GetClusterCredentialsInput{ ClusterIdentifier: aws.String(meta.Redshift.ClusterID), DbUser: aws.String(databaseUser), DbName: aws.String(databaseName), @@ -344,7 +362,7 @@ func (a *dbAuth) getRedshiftDBUserAuthToken(ctx context.Context, database types. AutoCreate: aws.Bool(false), // TODO(r0mant): List of additional groups DbUser will join for the // session. Do we need to let people control this? - DbGroups: []*string{}, + DbGroups: []string{}, }) if err != nil { policy, getPolicyErr := dbiam.GetReadableAWSPolicyDocument(database) @@ -362,7 +380,7 @@ propagate): %v `, err, policy) } - return aws.StringValue(resp.DbUser), aws.StringValue(resp.DbPassword), nil + return aws.ToString(resp.DbUser), aws.ToString(resp.DbPassword), nil } // GetRedshiftServerlessAuthToken generates Redshift Serverless auth token. @@ -422,7 +440,7 @@ Make sure that IAM role %q has permissions to generate credentials. Here is a sa %v `, err, roleARN, policy) } - return aws.StringValue(resp.DbUser), aws.StringValue(resp.DbPassword), nil + return aws.ToString(resp.DbUser), aws.ToString(resp.DbPassword), nil } // GetCloudSQLAuthToken returns authorization token that will be used as a diff --git a/lib/srv/db/common/auth_test.go b/lib/srv/db/common/auth_test.go index c02d8a4984d85..ae136b4d53c46 100644 --- a/lib/srv/db/common/auth_test.go +++ b/lib/srv/db/common/auth_test.go @@ -25,11 +25,14 @@ import ( "errors" "fmt" "net/url" + "os" "testing" "time" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" @@ -43,8 +46,14 @@ import ( "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/tlsca" + "github.com/gravitational/teleport/lib/utils" ) +func TestMain(m *testing.M) { + utils.InitLoggerForTests() + os.Exit(m.Run()) +} + func TestAuthGetAzureCacheForRedisToken(t *testing.T) { t.Parallel() @@ -59,6 +68,7 @@ func TestAuthGetAzureCacheForRedisToken(t *testing.T) { Token: "azure-redis-enterprise-token", }), }, + AWSConfigProvider: &mocks.AWSConfigProvider{}, }) require.NoError(t, err) @@ -106,7 +116,7 @@ func TestAuthGetRedshiftServerlessAuthToken(t *testing.T) { t.Parallel() // setup mock aws sessions. - stsMock := &mocks.STSMock{} + stsMock := &mocks.STSClientV1{} clock := clockwork.NewFakeClock() auth, err := NewAuth(AuthConfig{ Clock: clock, @@ -118,6 +128,7 @@ func TestAuthGetRedshiftServerlessAuthToken(t *testing.T) { GetCredentialsOutput: mocks.RedshiftServerlessGetCredentialsOutput("IAM:some-user", "some-password", clock), }, }, + AWSConfigProvider: &mocks.AWSConfigProvider{}, }) require.NoError(t, err) @@ -137,9 +148,10 @@ func TestAuthGetTLSConfig(t *testing.T) { t.Parallel() auth, err := NewAuth(AuthConfig{ - AuthClient: new(authClientMock), - AccessPoint: new(accessPointMock), - Clients: &cloud.TestCloudClients{}, + AuthClient: new(authClientMock), + AccessPoint: new(accessPointMock), + Clients: &cloud.TestCloudClients{}, + AWSConfigProvider: &mocks.AWSConfigProvider{}, }) require.NoError(t, err) @@ -346,9 +358,10 @@ func TestGetAzureIdentityResourceID(t *testing.T) { } { t.Run(tc.desc, func(t *testing.T) { auth, err := NewAuth(AuthConfig{ - AuthClient: new(authClientMock), - AccessPoint: new(accessPointMock), - Clients: tc.clients, + AuthClient: new(authClientMock), + AccessPoint: new(accessPointMock), + Clients: tc.clients, + AWSConfigProvider: &mocks.AWSConfigProvider{}, }) require.NoError(t, err) @@ -379,6 +392,7 @@ func TestGetAzureIdentityResourceIDCache(t *testing.T) { }, AzureVirtualMachines: libcloudazure.NewVirtualMachinesClientByAPI(virtualMachinesMock), }, + AWSConfigProvider: &mocks.AWSConfigProvider{}, }) require.NoError(t, err) @@ -466,7 +480,7 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { t.Cleanup(cancel) tests := map[string]struct { checkGetAuthFn func(t *testing.T, auth Auth) - checkSTS func(t *testing.T, stsMock *mocks.STSMock) + checkSTS func(t *testing.T, stsMock *mocks.STSClient) }{ "Redshift": { checkGetAuthFn: func(t *testing.T, auth Auth) { @@ -485,7 +499,7 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { require.Equal(t, "IAM:some-user", dbUser) require.Equal(t, "some-password", dbPassword) }, - checkSTS: func(t *testing.T, stsMock *mocks.STSMock) { + checkSTS: func(t *testing.T, stsMock *mocks.STSClient) { t.Helper() require.Contains(t, stsMock.GetAssumedRoleARNs(), "arn:aws:iam::123456789012:role/RedshiftRole") require.Contains(t, stsMock.GetAssumedRoleExternalIDs(), "externalRedshift") @@ -508,9 +522,10 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { require.Equal(t, "IAM:some-role", dbUser) require.Equal(t, "some-password-for-some-role", dbPassword) }, - checkSTS: func(t *testing.T, stsMock *mocks.STSMock) { + checkSTS: func(t *testing.T, stsMock *mocks.STSClient) { t.Helper() require.Contains(t, stsMock.GetAssumedRoleARNs(), "arn:aws:iam::123456789012:role/RedshiftRole") + require.Contains(t, stsMock.GetAssumedRoleARNs(), "arn:aws:iam::123456789012:role/some-role") require.Contains(t, stsMock.GetAssumedRoleExternalIDs(), "externalRedshift") }, }, @@ -530,7 +545,7 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { require.Equal(t, "IAM:some-user", dbUser) require.Equal(t, "some-password", dbPassword) }, - checkSTS: func(t *testing.T, stsMock *mocks.STSMock) { + checkSTS: func(t *testing.T, stsMock *mocks.STSClient) { t.Helper() require.Contains(t, stsMock.GetAssumedRoleARNs(), "arn:aws:iam::123456789012:role/RedshiftServerlessRole") require.Contains(t, stsMock.GetAssumedRoleExternalIDs(), "externalRedshiftServerless") @@ -550,7 +565,7 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { require.NoError(t, err) require.Contains(t, token, "DBUser=some-user") }, - checkSTS: func(t *testing.T, stsMock *mocks.STSMock) { + checkSTS: func(t *testing.T, stsMock *mocks.STSClient) { t.Helper() require.Contains(t, stsMock.GetAssumedRoleARNs(), "arn:aws:iam::123456789012:role/RDSProxyRole") require.Contains(t, stsMock.GetAssumedRoleExternalIDs(), "externalRDSProxy") @@ -578,7 +593,7 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { require.Equal(t, "arn:aws:iam::123456789012:role/RedisRole/20010203/ca-central-1/elasticache/aws4_request", query.Get("X-Amz-Credential")) }, - checkSTS: func(t *testing.T, stsMock *mocks.STSMock) { + checkSTS: func(t *testing.T, stsMock *mocks.STSClient) { t.Helper() require.Contains(t, stsMock.GetAssumedRoleARNs(), "arn:aws:iam::123456789012:role/RedisRole") require.Contains(t, stsMock.GetAssumedRoleExternalIDs(), "externalElastiCacheRedis") @@ -586,23 +601,26 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { }, } - stsMock := &mocks.STSMock{} + fakeSTS := &mocks.STSClient{} clock := clockwork.NewFakeClockAt(time.Date(2001, time.February, 3, 0, 0, 0, 0, time.UTC)) auth, err := NewAuth(AuthConfig{ Clock: clock, AuthClient: new(authClientMock), AccessPoint: new(accessPointMock), Clients: &cloud.TestCloudClients{ - STS: stsMock, + STS: &fakeSTS.STSClientV1, RDS: &mocks.RDSMock{}, - Redshift: &mocks.RedshiftMock{ - GetClusterCredentialsOutput: mocks.RedshiftGetClusterCredentialsOutput("IAM:some-user", "some-password", clock), - GetClusterCredentialsWithIAMOutput: mocks.RedshiftGetClusterCredentialsWithIAMOutput("IAM:some-role", "some-password-for-some-role", clock), - }, RedshiftServerless: &mocks.RedshiftServerlessMock{ GetCredentialsOutput: mocks.RedshiftServerlessGetCredentialsOutput("IAM:some-user", "some-password", clock), }, }, + AWSConfigProvider: &mocks.AWSConfigProvider{ + STSClient: fakeSTS, + }, + redshiftClientProviderFn: newFakeRedshiftClientProvider(&mocks.RedshiftClient{ + GetClusterCredentialsOutput: mocks.RedshiftGetClusterCredentialsOutput("IAM:some-user", "some-password", clock), + GetClusterCredentialsWithIAMOutput: mocks.RedshiftGetClusterCredentialsWithIAMOutput("IAM:some-role", "some-password-for-some-role", clock), + }), }) require.NoError(t, err) @@ -611,7 +629,7 @@ func TestAuthGetAWSTokenWithAssumedRole(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() tt.checkGetAuthFn(t, auth) - tt.checkSTS(t, stsMock) + tt.checkSTS(t, fakeSTS) }) } } @@ -623,7 +641,7 @@ func TestGetAWSIAMCreds(t *testing.T) { for name, tt := range map[string]struct { db types.Database - stsMock *mocks.STSMock + stsMock *mocks.STSClientV1 username string expectedKeyId string expectedAssumedRoles []string @@ -632,7 +650,7 @@ func TestGetAWSIAMCreds(t *testing.T) { }{ "username is full role ARN": { db: newMongoAtlasDatabase(t, types.AWS{}), - stsMock: &mocks.STSMock{}, + stsMock: &mocks.STSClientV1{}, username: "arn:aws:iam::123456789012:role/role-name", expectedKeyId: "arn:aws:iam::123456789012:role/role-name", expectedAssumedRoles: []string{"arn:aws:iam::123456789012:role/role-name"}, @@ -641,7 +659,7 @@ func TestGetAWSIAMCreds(t *testing.T) { }, "username is partial role ARN": { db: newMongoAtlasDatabase(t, types.AWS{}), - stsMock: &mocks.STSMock{ + stsMock: &mocks.STSClientV1{ // This is the role returned by the STS GetCallerIdentity. ARN: "arn:aws:iam::222222222222:role/teleport-service-role", }, @@ -653,7 +671,7 @@ func TestGetAWSIAMCreds(t *testing.T) { }, "unable to fetch account ID": { db: newMongoAtlasDatabase(t, types.AWS{}), - stsMock: &mocks.STSMock{ + stsMock: &mocks.STSClientV1{ ARN: "", }, username: "role/role-name", @@ -664,7 +682,7 @@ func TestGetAWSIAMCreds(t *testing.T) { ExternalID: "123123", AssumeRoleARN: "arn:aws:iam::222222222222:role/teleport-service-role-external", }), - stsMock: &mocks.STSMock{ + stsMock: &mocks.STSClientV1{ ARN: "arn:aws:iam::111111111111:role/teleport-service-role", }, username: "role/role-name", @@ -685,6 +703,7 @@ func TestGetAWSIAMCreds(t *testing.T) { Clients: &cloud.TestCloudClients{ STS: tt.stsMock, }, + AWSConfigProvider: &mocks.AWSConfigProvider{}, }) require.NoError(t, err) @@ -1002,3 +1021,9 @@ func (m *imdsMock) GetID(_ context.Context) (string, error) { func (m *imdsMock) GetType() types.InstanceMetadataType { return m.instanceType } + +func newFakeRedshiftClientProvider(c redshiftClient) redshiftClientProviderFunc { + return func(cfg aws.Config, optFns ...func(*redshift.Options)) redshiftClient { + return c + } +} diff --git a/lib/srv/db/common/errors.go b/lib/srv/db/common/errors.go index a86305d13b8ec..7b8456f5e5b35 100644 --- a/lib/srv/db/common/errors.go +++ b/lib/srv/db/common/errors.go @@ -25,6 +25,7 @@ import ( "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/go-mysql-org/go-mysql/mysql" "github.com/gravitational/trace" @@ -66,12 +67,15 @@ func ConvertError(err error) error { var googleAPIErr *googleapi.Error var awsRequestFailureErr awserr.RequestFailure + var awsRequestFailureErrV2 *awshttp.ResponseError var azResponseErr *azcore.ResponseError var pgError *pgconn.PgError var myError *mysql.MyError switch err := trace.Unwrap(err); { case errors.As(err, &googleAPIErr): return convertGCPError(googleAPIErr) + case errors.As(err, &awsRequestFailureErrV2): + return awslib.ConvertRequestFailureErrorV2(awsRequestFailureErrV2) case errors.As(err, &awsRequestFailureErr): return awslib.ConvertRequestFailureError(awsRequestFailureErr) case errors.As(err, &azResponseErr): diff --git a/lib/srv/db/common/session.go b/lib/srv/db/common/session.go index d7cc951570343..575df62c71f53 100644 --- a/lib/srv/db/common/session.go +++ b/lib/srv/db/common/session.go @@ -68,6 +68,8 @@ type Session struct { StartTime time.Time // PostgresPID is the Postgres backend PID for the session. PostgresPID uint32 + // UserAgent identifies the type of client used on the session. + UserAgent string } // String returns string representation of the session parameters. diff --git a/lib/srv/db/common/test.go b/lib/srv/db/common/test.go index c1be10cb3e8d3..4eb03e85ae7e1 100644 --- a/lib/srv/db/common/test.go +++ b/lib/srv/db/common/test.go @@ -160,6 +160,16 @@ func MakeTestServerTLSConfig(config TestServerConfig) (*tls.Config, error) { }, nil } +// ClientOption represents a database client config option. +type ClientOption func(config *TestClientConfig) + +// WithUserAgent set client user agent. +func WithUserAgent(userAgent string) ClientOption { + return func(config *TestClientConfig) { + config.UserAgent = userAgent + } +} + // TestClientConfig combines parameters for a test Postgres/MySQL client. type TestClientConfig struct { // AuthClient will be used to retrieve trusted CA. @@ -176,6 +186,8 @@ type TestClientConfig struct { PinnedIP string // RouteToDatabase contains database routing information. RouteToDatabase tlsca.RouteToDatabase + // UserAgent contains the client user agent. + UserAgent string } // MakeTestClientTLSCert returns TLS certificate suitable for configuring test diff --git a/lib/srv/db/dynamodb/engine.go b/lib/srv/db/dynamodb/engine.go index 940b0b315a724..d877741dc628b 100644 --- a/lib/srv/db/dynamodb/engine.go +++ b/lib/srv/db/dynamodb/engine.go @@ -30,10 +30,10 @@ import ( "strconv" "strings" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/service/dax" - "github.com/aws/aws-sdk-go/service/dynamodb" - "github.com/aws/aws-sdk-go/service/dynamodbstreams" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/dax" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodbstreams" "github.com/gravitational/trace" "github.com/prometheus/client_golang/prometheus" @@ -43,6 +43,7 @@ import ( "github.com/gravitational/teleport/lib/cloud" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/srv/db/common" "github.com/gravitational/teleport/lib/srv/db/common/role" "github.com/gravitational/teleport/lib/utils" @@ -54,6 +55,7 @@ func NewEngine(ec common.EngineConfig) common.Engine { return &Engine{ EngineConfig: ec, RoundTrippers: make(map[string]http.RoundTripper), + UseFIPS: modules.GetModules().IsBoringBinary(), } } @@ -71,6 +73,8 @@ type Engine struct { RoundTrippers map[string]http.RoundTripper // CredentialsGetter is used to obtain STS credentials. CredentialsGetter libaws.CredentialsGetter + // UseFIPS will ensure FIPS endpoint resolution. + UseFIPS bool } var _ common.Engine = (*Engine)(nil) @@ -194,7 +198,7 @@ func (e *Engine) process(ctx context.Context, req *http.Request, signer *libaws. // emit an audit event regardless of failure, but using the resolved endpoint. var responseStatusCode uint32 defer func() { - e.emitAuditEvent(req, re.URL, responseStatusCode, err) + e.emitAuditEvent(req, re.URL.String(), responseStatusCode, err) }() // try to read, close, and replace the incoming request body. @@ -319,8 +323,8 @@ func (e *Engine) checkAccess(ctx context.Context, sessionCtx *common.Session) er } // getRoundTripper makes an HTTP round tripper with TLS config based on the given URL. -func (e *Engine) getRoundTripper(ctx context.Context, URL string) (http.RoundTripper, error) { - if rt, ok := e.RoundTrippers[URL]; ok { +func (e *Engine) getRoundTripper(ctx context.Context, u *url.URL) (http.RoundTripper, error) { + if rt, ok := e.RoundTrippers[u.String()]; ok { return rt, nil } tlsConfig, err := e.Auth.GetTLSConfig(ctx, e.sessionCtx.GetExpiry(), e.sessionCtx.Database, e.sessionCtx.DatabaseUser) @@ -329,55 +333,136 @@ func (e *Engine) getRoundTripper(ctx context.Context, URL string) (http.RoundTri } // We need to set the ServerName here because the AWS endpoint service prefix is not known in advance, // and the TLS config we got does not set it. - host, err := getURLHostname(URL) - if err != nil { - return nil, trace.Wrap(err) - } - tlsConfig.ServerName = host + tlsConfig.ServerName = u.Hostname() out, err := defaults.Transport() if err != nil { return nil, trace.Wrap(err) } out.TLSClientConfig = tlsConfig - e.RoundTrippers[URL] = out + e.RoundTrippers[u.String()] = out return out, nil } -// resolveEndpoint returns a resolved endpoint for either the configured URI or the AWS target service and region. -func (e *Engine) resolveEndpoint(req *http.Request) (*endpoints.ResolvedEndpoint, error) { - endpointID, err := extractEndpointID(req) +type endpoint struct { + URL *url.URL + SigningName string + SigningRegion string +} + +// resolveEndpoint returns a resolved endpoint for either the configured URI or +// the AWS target service and region. +// For a target operation, the appropriate AWS service resolver is used. +// Targets look like one of DynamoDB_$version.$operation, +// DynamoDBStreams_$version.$operation, or AmazonDAX$version.$operation. +// For example: DynamoDBStreams_20120810.ListStreams +func (e *Engine) resolveEndpoint(req *http.Request) (*endpoint, error) { + target, err := getTargetHeader(req) if err != nil { return nil, trace.Wrap(err) } - opts := func(opts *endpoints.Options) { - opts.ResolveUnknownService = true + + awsMeta := e.sessionCtx.Database.GetAWS() + + var re *endpoint + switch target := strings.ToLower(target); { + case strings.HasPrefix(target, "dynamodbstreams"): + re, err = resolveDynamoDBStreamsEndpoint(req.Context(), awsMeta.Region, e.UseFIPS) + case strings.HasPrefix(target, "dynamodb"): + re, err = resolveDynamoDBEndpoint(req.Context(), awsMeta.Region, awsMeta.AccountID, e.UseFIPS) + case strings.HasPrefix(target, "amazondax"): + re, err = resolveDaxEndpoint(req.Context(), awsMeta.Region, e.UseFIPS) + default: + return nil, trace.BadParameter("DynamoDB API target %q is not recognized", target) } - re, err := endpoints.DefaultResolver().EndpointFor(endpointID, e.sessionCtx.Database.GetAWS().Region, opts) if err != nil { return nil, trace.Wrap(err) } uri := e.sessionCtx.Database.GetURI() - if uri != "" && uri != apiaws.DynamoDBURIForRegion(e.sessionCtx.Database.GetAWS().Region) { + if uri != "" && uri != apiaws.DynamoDBURIForRegion(awsMeta.Region) { + // Add a temporary schema to make a valid URL for url.Parse. + if !strings.Contains(uri, "://") { + uri = "schema://" + uri + } + u, err := url.Parse(uri) + if err != nil { + return nil, trace.Wrap(err) + } // override the resolved endpoint URL with the user-configured URI. - re.URL = uri + re.URL = u } - if !strings.Contains(re.URL, "://") { - re.URL = "https://" + re.URL + // Force HTTPS + re.URL.Scheme = "https" + return re, nil +} + +func resolveDynamoDBStreamsEndpoint(ctx context.Context, region string, useFIPS bool) (*endpoint, error) { + params := dynamodbstreams.EndpointParameters{ + Region: aws.String(region), + UseFIPS: aws.Bool(useFIPS), } - return &re, nil + ep, err := dynamodbstreams.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params) + if err != nil { + return nil, trace.Wrap(err) + } + return &endpoint{ + URL: &ep.URI, + SigningRegion: region, + // DynamoDB Streams uses the same signing name as DynamoDB. + SigningName: "dynamodb", + }, nil } -// rewriteRequest clones a request, modifies the clone to rewrite its URL, and returns the modified request clone. -func rewriteRequest(ctx context.Context, r *http.Request, re *endpoints.ResolvedEndpoint, body []byte) (*http.Request, error) { - resolvedURL, err := url.Parse(re.URL) +func resolveDynamoDBEndpoint(ctx context.Context, region, accountID string, useFIPS bool) (*endpoint, error) { + params := dynamodb.EndpointParameters{ + Region: aws.String(region), + // Preferred means if we have an account ID available, then use an + // account ID based endpoint. + // We should always have the account ID available anyway. + // If we didn't then it would just resolve the regional endpoint like + // dynamodb..amazonaws.com. + // AWS documents that account-based routing provides better request + // performance for some services. + // See: https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html + AccountIdEndpointMode: aws.String(aws.AccountIDEndpointModePreferred), + UseFIPS: aws.Bool(useFIPS), + } + if accountID != "" { + params.AccountId = aws.String(accountID) + } + ep, err := dynamodb.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params) if err != nil { return nil, trace.Wrap(err) } + return &endpoint{ + URL: &ep.URI, + SigningRegion: region, + SigningName: "dynamodb", + }, nil +} + +func resolveDaxEndpoint(ctx context.Context, region string, useFIPS bool) (*endpoint, error) { + params := dax.EndpointParameters{ + Region: aws.String(region), + UseFIPS: aws.Bool(useFIPS), + } + ep, err := dax.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params) + if err != nil { + return nil, trace.Wrap(err) + } + return &endpoint{ + URL: &ep.URI, + SigningRegion: region, + SigningName: "dax", + }, nil +} + +// rewriteRequest clones a request, modifies the clone to rewrite its URL, and returns the modified request clone. +func rewriteRequest(ctx context.Context, r *http.Request, re *endpoint, body []byte) (*http.Request, error) { reqCopy := r.Clone(ctx) // set url and host header to match the resolved endpoint. - reqCopy.URL = resolvedURL - reqCopy.Host = resolvedURL.Host + reqCopy.URL = re.URL + reqCopy.Host = re.URL.Host if body == nil { // no body is fine, skip copying it. return reqCopy, nil @@ -388,42 +473,13 @@ func rewriteRequest(ctx context.Context, r *http.Request, re *endpoints.Resolved return reqCopy, nil } -// extractEndpointID extracts the AWS endpoint ID from the request header X-Amz-Target. -func extractEndpointID(req *http.Request) (string, error) { +// getTargetHeader gets the X-Amz-Target header or returns an error if it is not +// present, as we rely on this header for endpoint resolution. +// See X-Amz-Target: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.LowLevelAPI.html +func getTargetHeader(req *http.Request) (string, error) { target := req.Header.Get(libaws.AmzTargetHeader) if target == "" { return "", trace.BadParameter("missing %q header in http request", libaws.AmzTargetHeader) } - endpointID, err := endpointIDForTarget(target) - return endpointID, trace.Wrap(err) -} - -// endpointIDForTarget converts a target operation into the appropriate the AWS endpoint ID. -// Target looks like one of DynamoDB_$version.$operation, DynamoDBStreams_$version.$operation, AmazonDAX$version.$operation, -// for example: DynamoDBStreams_20120810.ListStreams -// See X-Amz-Target: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.LowLevelAPI.html -func endpointIDForTarget(target string) (string, error) { - t := strings.ToLower(target) - switch { - case strings.HasPrefix(t, "dynamodbstreams"): - return dynamodbstreams.EndpointsID, nil - case strings.HasPrefix(t, "dynamodb"): - return dynamodb.EndpointsID, nil - case strings.HasPrefix(t, "amazondax"): - return dax.EndpointsID, nil - default: - return "", trace.BadParameter("DynamoDB API target %q is not recognized", target) - } -} - -// getURLHostname parses a URL to extract its hostname. -func getURLHostname(uri string) (string, error) { - if !strings.Contains(uri, "://") { - uri = "schema://" + uri - } - parsed, err := url.Parse(uri) - if err != nil { - return "", trace.Wrap(err) - } - return parsed.Hostname(), nil + return target, nil } diff --git a/lib/srv/db/dynamodb/engine_test.go b/lib/srv/db/dynamodb/engine_test.go index faeaf0536ed1d..57b1fe170a409 100644 --- a/lib/srv/db/dynamodb/engine_test.go +++ b/lib/srv/db/dynamodb/engine_test.go @@ -38,7 +38,8 @@ func TestResolveEndpoint(t *testing.T) { desc string target string // from X-Amz-Target in requests region string - wantEndpointID string + useFIPS bool + unsetAccountID bool wantSigningName string wantURL string wantErrMsg string @@ -47,15 +48,21 @@ func TestResolveEndpoint(t *testing.T) { desc: "dynamodb target in us west", target: "DynamoDB_20120810.Scan", region: "us-west-1", - wantEndpointID: "dynamodb", + wantSigningName: "dynamodb", + wantURL: "https://123456789012.ddb.us-west-1.amazonaws.com", + }, + { + desc: "dynamodb target in us west with no account id", + target: "DynamoDB_20120810.Scan", + region: "us-west-1", wantSigningName: "dynamodb", wantURL: "https://dynamodb.us-west-1.amazonaws.com", + unsetAccountID: true, }, { desc: "dynamodb target in china", target: "DynamoDB_20120810.Scan", region: "cn-north-1", - wantEndpointID: "dynamodb", wantSigningName: "dynamodb", wantURL: "https://dynamodb.cn-north-1.amazonaws.com.cn", }, @@ -63,7 +70,6 @@ func TestResolveEndpoint(t *testing.T) { desc: "dynamodb streams target in us west", target: "DynamoDBStreams_20120810.ListStreams", region: "us-west-1", - wantEndpointID: "streams.dynamodb", wantSigningName: "dynamodb", wantURL: "https://streams.dynamodb.us-west-1.amazonaws.com", }, @@ -71,7 +77,6 @@ func TestResolveEndpoint(t *testing.T) { desc: "dynamodb streams target in china", target: "DynamoDBStreams_20120810.ListStreams", region: "cn-north-1", - wantEndpointID: "streams.dynamodb", wantSigningName: "dynamodb", wantURL: "https://streams.dynamodb.cn-north-1.amazonaws.com.cn", }, @@ -79,7 +84,6 @@ func TestResolveEndpoint(t *testing.T) { desc: "dax target in us west", target: "AmazonDAXV3.ListTags", region: "us-west-1", - wantEndpointID: "dax", wantSigningName: "dax", wantURL: "https://dax.us-west-1.amazonaws.com", }, @@ -87,10 +91,33 @@ func TestResolveEndpoint(t *testing.T) { desc: "dax target in china", target: "AmazonDAXV3.ListTags", region: "cn-north-1", - wantEndpointID: "dax", wantSigningName: "dax", wantURL: "https://dax.cn-north-1.amazonaws.com.cn", }, + { + desc: "dynamodb target in us west with FIPS required", + target: "DynamoDB_20120810.Scan", + region: "us-west-1", + wantSigningName: "dynamodb", + wantURL: "https://dynamodb-fips.us-west-1.amazonaws.com", + useFIPS: true, + }, + { + desc: "dynamodb streams target in us west with FIPS required", + target: "DynamoDBStreams_20120810.ListStreams", + region: "us-west-1", + wantSigningName: "dynamodb", + wantURL: "https://streams.dynamodb-fips.us-west-1.amazonaws.com", + useFIPS: true, + }, + { + desc: "dax target in us west with FIPS required", + target: "AmazonDAXV3.ListTags", + region: "us-west-1", + wantSigningName: "dax", + wantURL: "https://dax-fips.us-west-1.amazonaws.com", + useFIPS: true, + }, { desc: "unrecognizable target", target: "DDB.Scan", @@ -105,25 +132,19 @@ func TestResolveEndpoint(t *testing.T) { req := &http.Request{Header: make(http.Header)} req.Header.Set(libaws.AmzTargetHeader, tt.target) - // check that the correct endpoint ID is extracted. - endpointID, err := extractEndpointID(req) - if tt.wantErrMsg != "" { - require.Error(t, err) - require.ErrorContains(t, err, tt.wantErrMsg) - return - } - require.Equal(t, tt.wantEndpointID, endpointID) - // check that the engine resolves the correct URL. db := &types.DatabaseV3{ Spec: types.DatabaseSpecV3{ URI: apiaws.DynamoDBURIForRegion(tt.region), AWS: types.AWS{ Region: tt.region, - AccountID: "12345", + AccountID: "123456789012", }, }, } + if tt.unsetAccountID { + db.Spec.AWS.AccountID = "" + } engine := &Engine{ EngineConfig: common.EngineConfig{ Log: slog.Default(), @@ -131,18 +152,26 @@ func TestResolveEndpoint(t *testing.T) { sessionCtx: &common.Session{ Database: db, }, + UseFIPS: tt.useFIPS, } re, err := engine.resolveEndpoint(req) + if tt.wantErrMsg != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.wantErrMsg) + return + } require.NoError(t, err) - require.Equal(t, tt.wantURL, re.URL) + require.Equal(t, tt.wantURL, re.URL.String()) require.Equal(t, tt.wantSigningName, re.SigningName) + require.Equal(t, tt.region, re.SigningRegion) // now use a custom URI and check that it overrides the resolved URL. db.Spec.URI = "foo.com" re, err = engine.resolveEndpoint(req) require.NoError(t, err) - require.Equal(t, "https://foo.com", re.URL) + require.Equal(t, "https://foo.com", re.URL.String()) require.Equal(t, tt.wantSigningName, re.SigningName) + require.Equal(t, tt.region, re.SigningRegion) }) } } diff --git a/lib/srv/db/dynamodb/test.go b/lib/srv/db/dynamodb/test.go index 8dc31ee1e7379..92e57e4915c60 100644 --- a/lib/srv/db/dynamodb/test.go +++ b/lib/srv/db/dynamodb/test.go @@ -27,10 +27,9 @@ import ( "sync" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/gravitational/trace" "github.com/gravitational/teleport/lib/srv/db/common" @@ -38,7 +37,10 @@ import ( ) // Client alias for easier use. -type Client = dynamodb.DynamoDB +type Client struct { + *dynamodb.Client + HTTPClient *http.Client +} // ClientOptionsParams is a struct for client configuration options. type ClientOptionsParams struct { @@ -51,19 +53,19 @@ type ClientOptions func(*ClientOptionsParams) // MakeTestClient returns DynamoDB client connection according to the provided // parameters. func MakeTestClient(_ context.Context, config common.TestClientConfig, opts ...ClientOptions) (*Client, error) { - provider := session.Must(session.NewSession(&aws.Config{ - Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: credentials.Value{ - AccessKeyID: "fakeClientKeyID", - SecretAccessKey: "fakeClientSecret", - }}), - Region: aws.String("local"), - })) - dynamoClient := dynamodb.New(provider, &aws.Config{ - Endpoint: aws.String("http://" + config.Address), - MaxRetries: aws.Int(0), // disable automatic retries in tests - HTTPClient: &http.Client{Timeout: 5 * time.Second}, + httpClt := &http.Client{Timeout: 5 * time.Second} + dynamoClient := dynamodb.New(dynamodb.Options{ + Region: "local", + Credentials: credentials.NewStaticCredentialsProvider( + "fakeClientKeyID", + "fakeClientSecret", + "", + ), + BaseEndpoint: aws.String("http://" + config.Address), + RetryMaxAttempts: 0, // disable automatic retries in tests + HTTPClient: httpClt, }) - return dynamoClient, nil + return &Client{Client: dynamoClient, HTTPClient: httpClt}, nil } // TestServerOption allows setting test server options. @@ -99,7 +101,7 @@ func NewTestServer(config common.TestServerConfig, opts ...TestServerOption) (*T mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - err := awsutils.VerifyAWSSignature(r, credentials.NewStaticCredentials("AKIDl", "SECRET", "SESSION")) + err := awsutils.VerifyAWSSignatureV2(r, credentials.NewStaticCredentialsProvider("AKIDl", "SECRET", "SESSION")) if err != nil { code := trace.ErrorToCode(err) body, _ := json.Marshal(jsonErr{ diff --git a/lib/srv/db/dynamodb_test.go b/lib/srv/db/dynamodb_test.go index 0ed6355983381..f7a2b259e110b 100644 --- a/lib/srv/db/dynamodb_test.go +++ b/lib/srv/db/dynamodb_test.go @@ -25,9 +25,8 @@ import ( "net/http" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - awsdynamodb "github.com/aws/aws-sdk-go/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/credentials" + awsdynamodb "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" @@ -36,6 +35,7 @@ import ( "github.com/gravitational/teleport/lib/srv/db/common" "github.com/gravitational/teleport/lib/srv/db/dynamodb" awsutils "github.com/gravitational/teleport/lib/utils/aws" + "github.com/gravitational/teleport/lib/utils/aws/migration" ) func registerTestDynamoDBEngine() { @@ -50,7 +50,9 @@ func newTestDynamoDBEngine(ec common.EngineConfig) common.Engine { RoundTrippers: make(map[string]http.RoundTripper), // inject mock AWS credentials. CredentialsGetter: awsutils.NewStaticCredentialsGetter( - credentials.NewStaticCredentials("AKIDl", "SECRET", "SESSION"), + migration.NewCredentialsAdapter( + credentials.NewStaticCredentialsProvider("AKIDl", "SECRET", "SESSION"), + ), ), } } @@ -127,14 +129,14 @@ func TestAccessDynamoDB(t *testing.T) { require.NoError(t, err) // Execute a dynamodb query. - out, err := clt.ListTables(&awsdynamodb.ListTablesInput{}) + out, err := clt.ListTables(ctx, &awsdynamodb.ListTablesInput{}) if test.wantErrMsg != "" { require.Error(t, err) require.ErrorContains(t, err, test.wantErrMsg) return } require.NoError(t, err) - require.ElementsMatch(t, mockTables, aws.StringValueSlice(out.TableNames)) + require.ElementsMatch(t, mockTables, out.TableNames) }) } } @@ -159,7 +161,7 @@ func TestAuditDynamoDB(t *testing.T) { require.NoError(t, err) // Execute a dynamodb query. - _, err = clt.ListTables(&awsdynamodb.ListTablesInput{}) + _, err = clt.ListTables(ctx, &awsdynamodb.ListTablesInput{}) require.Error(t, err) require.ErrorContains(t, err, "access to db denied") requireEvent(t, testCtx, libevents.DatabaseSessionStartFailureCode) @@ -176,21 +178,21 @@ func TestAuditDynamoDB(t *testing.T) { require.NoError(t, err) t.Run("session starts and emits a request event", func(t *testing.T) { - _, err := clt.ListTables(&awsdynamodb.ListTablesInput{}) + _, err := clt.ListTables(ctx, &awsdynamodb.ListTablesInput{}) require.NoError(t, err) requireEvent(t, testCtx, libevents.DatabaseSessionStartCode) requireEvent(t, testCtx, libevents.DynamoDBRequestCode) }) t.Run("session ends when client closes the connection", func(t *testing.T) { - clt.Config.HTTPClient.CloseIdleConnections() + clt.HTTPClient.CloseIdleConnections() requireEvent(t, testCtx, libevents.DatabaseSessionEndCode) }) t.Run("session ends when local proxy closes the connection", func(t *testing.T) { // closing local proxy and canceling the context used to start it should trigger session end event. // without this cancel, the session will not end until the smaller of client_idle_timeout or the testCtx closes. - _, err := clt.ListTables(&awsdynamodb.ListTablesInput{}) + _, err := clt.ListTables(ctx, &awsdynamodb.ListTablesInput{}) require.NoError(t, err) requireEvent(t, testCtx, libevents.DatabaseSessionStartCode) requireEvent(t, testCtx, libevents.DynamoDBRequestCode) diff --git a/lib/srv/db/postgres/engine.go b/lib/srv/db/postgres/engine.go index 5ab9680a1fed6..ec1f95d5fa121 100644 --- a/lib/srv/db/postgres/engine.go +++ b/lib/srv/db/postgres/engine.go @@ -213,6 +213,9 @@ func (e *Engine) handleStartup(client *pgproto3.Backend, sessionCtx *common.Sess sessionCtx.DatabaseName = value case "user": sessionCtx.DatabaseUser = value + // https://www.postgresql.org/docs/17/libpq-connect.html#LIBPQ-CONNECT-APPLICATION-NAME + case "application_name": + sessionCtx.UserAgent = value default: sessionCtx.StartupParameters[key] = value } diff --git a/lib/srv/db/postgres/test.go b/lib/srv/db/postgres/test.go index 955bb39e2c7a2..b94c7b792d275 100644 --- a/lib/srv/db/postgres/test.go +++ b/lib/srv/db/postgres/test.go @@ -58,6 +58,9 @@ func MakeTestClient(ctx context.Context, config common.TestClientConfig) (*pgcon if err != nil { return nil, trace.Wrap(err) } + if config.UserAgent != "" { + pgconnConfig.RuntimeParams["application_name"] = config.UserAgent + } pgConn, err := pgconn.ConnectConfig(ctx, pgconnConfig) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/srv/db/postgres/users.go b/lib/srv/db/postgres/users.go index a902a85298c08..56fb14a2dd9d8 100644 --- a/lib/srv/db/postgres/users.go +++ b/lib/srv/db/postgres/users.go @@ -430,13 +430,14 @@ func (e *Engine) updateAutoUsersRole(ctx context.Context, conn *pgx.Conn, adminU // support WITH INHERIT FALSE or WITH SET FALSE syntax, so we only specify // WITH ADMIN OPTION. // See: https://www.postgresql.org/docs/16/release-16.html - stmt := fmt.Sprintf("grant role %q to %q WITH ADMIN OPTION", teleportAutoUserRole, adminUser) + stmt := fmt.Sprintf("grant %q to %q WITH ADMIN OPTION", teleportAutoUserRole, adminUser) _, err = conn.Exec(ctx, stmt) if err != nil { if !strings.Contains(err.Error(), "cannot be granted back") && !strings.Contains(err.Error(), "already") { e.Log.DebugContext(ctx, "Failed to grant required role to the Teleport database admin, user auto-provisioning may not work until the database admin is granted the role by a superuser", "role", teleportAutoUserRole, "database_admin", adminUser, + "error", err, ) } } diff --git a/lib/srv/db/server.go b/lib/srv/db/server.go index b91245d6898ef..28fcc486bf4db 100644 --- a/lib/srv/db/server.go +++ b/lib/srv/db/server.go @@ -41,6 +41,7 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/authz" clients "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/inventory" @@ -68,6 +69,7 @@ import ( "github.com/gravitational/teleport/lib/srv/db/spanner" "github.com/gravitational/teleport/lib/srv/db/sqlserver" discoverycommon "github.com/gravitational/teleport/lib/srv/discovery/common" + "github.com/gravitational/teleport/lib/srv/discovery/fetchers/db" "github.com/gravitational/teleport/lib/utils" ) @@ -138,6 +140,10 @@ type Config struct { CADownloader CADownloader // CloudClients creates cloud API clients. CloudClients clients.Clients + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider + // AWSDatabaseFetcherFactory provides AWS database fetchers + AWSDatabaseFetcherFactory *db.AWSFetcherFactory // CloudMeta fetches cloud metadata for cloud hosted databases. CloudMeta *cloud.Metadata // CloudIAM configures IAM for cloud hosted databases. @@ -192,12 +198,30 @@ func (c *Config) CheckAndSetDefaults(ctx context.Context) (err error) { } c.CloudClients = cloudClients } + if c.AWSConfigProvider == nil { + provider, err := awsconfig.NewCache() + if err != nil { + return trace.Wrap(err, "unable to create AWS config provider cache") + } + c.AWSConfigProvider = provider + } + if c.AWSDatabaseFetcherFactory == nil { + factory, err := db.NewAWSFetcherFactory(db.AWSFetcherFactoryConfig{ + CloudClients: c.CloudClients, + AWSConfigProvider: c.AWSConfigProvider, + }) + if err != nil { + return trace.Wrap(err) + } + c.AWSDatabaseFetcherFactory = factory + } if c.Auth == nil { c.Auth, err = common.NewAuth(common.AuthConfig{ - AuthClient: c.AuthClient, - AccessPoint: c.AccessPoint, - Clock: c.Clock, - Clients: c.CloudClients, + AuthClient: c.AuthClient, + AccessPoint: c.AccessPoint, + Clock: c.Clock, + Clients: c.CloudClients, + AWSConfigProvider: c.AWSConfigProvider, }) if err != nil { return trace.Wrap(err) @@ -226,7 +250,8 @@ func (c *Config) CheckAndSetDefaults(ctx context.Context) (err error) { } if c.CloudMeta == nil { c.CloudMeta, err = cloud.NewMetadata(cloud.MetadataConfig{ - Clients: c.CloudClients, + Clients: c.CloudClients, + AWSConfigProvider: c.AWSConfigProvider, }) if err != nil { return trace.Wrap(err) @@ -282,9 +307,10 @@ func (c *Config) CheckAndSetDefaults(ctx context.Context) (err error) { if c.discoveryResourceChecker == nil { c.discoveryResourceChecker, err = cloud.NewDiscoveryResourceChecker(cloud.DiscoveryResourceCheckerConfig{ - ResourceMatchers: c.ResourceMatchers, - Clients: c.CloudClients, - Context: ctx, + ResourceMatchers: c.ResourceMatchers, + Clients: c.CloudClients, + AWSConfigProvider: c.AWSConfigProvider, + Context: ctx, }) if err != nil { return trace.Wrap(err) diff --git a/lib/srv/db/watcher.go b/lib/srv/db/watcher.go index f4d7fe86d99ec..2dc1dcb11d35c 100644 --- a/lib/srv/db/watcher.go +++ b/lib/srv/db/watcher.go @@ -110,7 +110,7 @@ func (s *Server) startResourceWatcher(ctx context.Context) (*services.GenericWat // startCloudWatcher starts fetching cloud databases according to the // selectors and register/unregister them appropriately. func (s *Server) startCloudWatcher(ctx context.Context) error { - awsFetchers, err := dbfetchers.MakeAWSFetchers(ctx, s.cfg.CloudClients, s.cfg.AWSMatchers, "" /* discovery config */) + awsFetchers, err := s.cfg.AWSDatabaseFetcherFactory.MakeFetchers(ctx, s.cfg.AWSMatchers, "" /* discovery config */) if err != nil { return trace.Wrap(err) } diff --git a/lib/srv/desktop/rdp/rdpclient/Cargo.toml b/lib/srv/desktop/rdp/rdpclient/Cargo.toml index 7b3eba0019872..54460d2f74ee8 100644 --- a/lib/srv/desktop/rdp/rdpclient/Cargo.toml +++ b/lib/srv/desktop/rdp/rdpclient/Cargo.toml @@ -13,7 +13,7 @@ bitflags = "2.5.0" boring = { git = "https://github.com/gravitational/boring", rev = "99897308abb5976ea05625b8314c24b16eebb01b", optional = true } byteorder = "1.5.0" bytes = "1.9.0" -env_logger = "0.11.5" +env_logger = "0.11.6" ironrdp-cliprdr.workspace = true ironrdp-connector.workspace = true ironrdp-core.workspace = true @@ -44,7 +44,7 @@ picky-asn1-der = "0.5.1" picky-asn1-x509 = "0.14.1" picky-krb = "0.9.2" reqwest = { version = "0.12", default-features = false } -rustls = { version = "0.23.19", default-features = false, features = ["aws-lc-rs"] } +rustls = { version = "0.23.20", default-features = false, features = ["aws-lc-rs"] } [build-dependencies] cbindgen = "0.27.0" diff --git a/lib/srv/desktop/rdp/rdpclient/src/lib.rs b/lib/srv/desktop/rdp/rdpclient/src/lib.rs index 200f1b6a6186a..c82663f704c73 100644 --- a/lib/srv/desktop/rdp/rdpclient/src/lib.rs +++ b/lib/srv/desktop/rdp/rdpclient/src/lib.rs @@ -39,6 +39,7 @@ use rdpdr::tdp::{ }; use std::ffi::CString; use std::fmt::Debug; +use std::io::ErrorKind; use std::os::raw::c_char; use std::ptr; use util::{from_c_string, from_go_array}; @@ -141,12 +142,17 @@ pub unsafe extern "C" fn client_run(cgo_handle: CgoHandle, params: CGOConnectPar None => ptr::null_mut(), }, }, - Err(e) => { error!("client_run failed: {:?}", e); + let message = match e { + client::ClientError::Tcp(io_err) if io_err.kind() == ErrorKind::TimedOut => { + String::from(TIMEOUT_ERROR_MESSAGE) + } + _ => format!("{}", e), + }; CGOResult { err_code: CGOErrCode::ErrCodeFailure, - message: CString::new(format!("{}", e)) + message: CString::new(message) .map(|c| c.into_raw()) .unwrap_or(ptr::null_mut()), } @@ -154,6 +160,13 @@ pub unsafe extern "C" fn client_run(cgo_handle: CgoHandle, params: CGOConnectPar } } +const TIMEOUT_ERROR_MESSAGE: &str = "Connection Timed Out\n\n\ +Teleport could not connect to the host within the timeout period. \ +This could be due to a firewall blocking connections, an overloaded system, \ +or network congestion. To resolve this issue, ensure that the Teleport agent \ +has connectivity to the Windows host.\n\n\ +Use \"nc -vz HOST 3389\" to help debug this issue."; + fn handle_operation(cgo_handle: CgoHandle, ctx: &'static str, f: T) -> CGOErrCode where T: FnOnce(ClientHandle) -> ClientResult<()>, diff --git a/lib/srv/discovery/common/database.go b/lib/srv/discovery/common/database.go index 237c72f2fc76d..8afe335f87fcb 100644 --- a/lib/srv/discovery/common/database.go +++ b/lib/srv/discovery/common/database.go @@ -28,14 +28,14 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v3" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redisenterprise/armredisenterprise" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql" + "github.com/aws/aws-sdk-go-v2/aws" rdstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" - "github.com/aws/aws-sdk-go/aws" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/opensearchservice" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/gravitational/trace" @@ -295,7 +295,7 @@ func NewDatabaseFromRDSInstance(instance *rds.DBInstance) (types.Database, error if err != nil { return nil, trace.Wrap(err) } - protocol, err := rdsEngineToProtocol(aws.StringValue(instance.Engine)) + protocol, err := rdsEngineToProtocol(aws.ToString(instance.Engine)) if err != nil { return nil, trace.Wrap(err) } @@ -304,10 +304,10 @@ func NewDatabaseFromRDSInstance(instance *rds.DBInstance) (types.Database, error setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS instance in %v", metadata.Region), Labels: labelsFromRDSInstance(instance, metadata), - }, aws.StringValue(instance.DBInstanceIdentifier)), + }, aws.ToString(instance.DBInstanceIdentifier)), types.DatabaseSpecV3{ Protocol: protocol, - URI: fmt.Sprintf("%v:%v", aws.StringValue(endpoint.Address), aws.Int64Value(endpoint.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(endpoint.Address), aws.ToInt64(endpoint.Port)), AWS: *metadata, }) } @@ -323,7 +323,7 @@ func NewDatabaseFromRDSV2Instance(instance *rdstypes.DBInstance) (types.Database if err != nil { return nil, trace.Wrap(err) } - protocol, err := rdsEngineToProtocol(aws.StringValue(instance.Engine)) + protocol, err := rdsEngineToProtocol(aws.ToString(instance.Engine)) if err != nil { return nil, trace.Wrap(err) } @@ -331,9 +331,9 @@ func NewDatabaseFromRDSV2Instance(instance *rdstypes.DBInstance) (types.Database uri := "" if instance.Endpoint != nil && instance.Endpoint.Address != nil { if instance.Endpoint.Port != nil { - uri = fmt.Sprintf("%s:%d", aws.StringValue(instance.Endpoint.Address), *instance.Endpoint.Port) + uri = fmt.Sprintf("%s:%d", aws.ToString(instance.Endpoint.Address), *instance.Endpoint.Port) } else { - uri = aws.StringValue(instance.Endpoint.Address) + uri = aws.ToString(instance.Endpoint.Address) } } @@ -341,7 +341,7 @@ func NewDatabaseFromRDSV2Instance(instance *rdstypes.DBInstance) (types.Database setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS instance in %v", metadata.Region), Labels: labelsFromRDSV2Instance(instance, metadata), - }, aws.StringValue(instance.DBInstanceIdentifier)), + }, aws.ToString(instance.DBInstanceIdentifier)), types.DatabaseSpecV3{ Protocol: protocol, URI: uri, @@ -352,7 +352,7 @@ func NewDatabaseFromRDSV2Instance(instance *rdstypes.DBInstance) (types.Database // MetadataFromRDSInstance creates AWS metadata from the provided RDS instance. // It uses aws sdk v2. func MetadataFromRDSV2Instance(rdsInstance *rdstypes.DBInstance) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(rdsInstance.DBInstanceArn)) + parsedARN, err := arn.Parse(aws.ToString(rdsInstance.DBInstanceArn)) if err != nil { return nil, trace.Wrap(err) } @@ -363,10 +363,10 @@ func MetadataFromRDSV2Instance(rdsInstance *rdstypes.DBInstance) (*types.AWS, er Region: parsedARN.Region, AccountID: parsedARN.AccountID, RDS: types.RDS{ - InstanceID: aws.StringValue(rdsInstance.DBInstanceIdentifier), - ClusterID: aws.StringValue(rdsInstance.DBClusterIdentifier), - ResourceID: aws.StringValue(rdsInstance.DbiResourceId), - IAMAuth: aws.BoolValue(rdsInstance.IAMDatabaseAuthenticationEnabled), + InstanceID: aws.ToString(rdsInstance.DBInstanceIdentifier), + ClusterID: aws.ToString(rdsInstance.DBClusterIdentifier), + ResourceID: aws.ToString(rdsInstance.DbiResourceId), + IAMAuth: aws.ToBool(rdsInstance.IAMDatabaseAuthenticationEnabled), Subnets: subnets, VPCID: vpcID, SecurityGroups: rdsSecurityGroupInfo(rdsInstance.VpcSecurityGroups), @@ -378,12 +378,12 @@ func MetadataFromRDSV2Instance(rdsInstance *rdstypes.DBInstance) (*types.AWS, er // It uses aws sdk v2. func labelsFromRDSV2Instance(rdsInstance *rdstypes.DBInstance, meta *types.AWS) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsInstance.Engine) - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsInstance.EngineVersion) + labels[types.DiscoveryLabelEngine] = aws.ToString(rdsInstance.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(rdsInstance.EngineVersion) labels[types.DiscoveryLabelEndpointType] = apiawsutils.RDSEndpointTypeInstance - labels[types.DiscoveryLabelStatus] = aws.StringValue(rdsInstance.DBInstanceStatus) + labels[types.DiscoveryLabelStatus] = aws.ToString(rdsInstance.DBInstanceStatus) if rdsInstance.DBSubnetGroup != nil { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(rdsInstance.DBSubnetGroup.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.ToString(rdsInstance.DBSubnetGroup.VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(rdsInstance.TagList)) } @@ -395,20 +395,20 @@ func NewDatabaseFromRDSV2Cluster(cluster *rdstypes.DBCluster, firstInstance *rds if err != nil { return nil, trace.Wrap(err) } - protocol, err := rdsEngineToProtocol(aws.StringValue(cluster.Engine)) + protocol, err := rdsEngineToProtocol(aws.ToString(cluster.Engine)) if err != nil { return nil, trace.Wrap(err) } uri := "" if cluster.Endpoint != nil && cluster.Port != nil { - uri = fmt.Sprintf("%v:%v", aws.StringValue(cluster.Endpoint), *cluster.Port) + uri = fmt.Sprintf("%v:%v", aws.ToString(cluster.Endpoint), *cluster.Port) } return types.NewDatabaseV3( setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v", metadata.Region), Labels: labelsFromRDSV2Cluster(cluster, metadata, apiawsutils.RDSEndpointTypePrimary, firstInstance), - }, aws.StringValue(cluster.DBClusterIdentifier)), + }, aws.ToString(cluster.DBClusterIdentifier)), types.DatabaseSpecV3{ Protocol: protocol, URI: uri, @@ -421,10 +421,10 @@ func rdsSubnetGroupToNetworkInfo(subnetGroup *rdstypes.DBSubnetGroup) (vpcID str return } - vpcID = aws.StringValue(subnetGroup.VpcId) + vpcID = aws.ToString(subnetGroup.VpcId) subnets = make([]string, 0, len(subnetGroup.Subnets)) for _, s := range subnetGroup.Subnets { - subnetID := aws.StringValue(s.SubnetIdentifier) + subnetID := aws.ToString(s.SubnetIdentifier) if subnetID != "" { subnets = append(subnets, subnetID) } @@ -439,7 +439,7 @@ func rdsSecurityGroupInfo(memberships []rdstypes.VpcSecurityGroupMembership) []s secGroups = make([]string, 0, len(memberships)) } for _, group := range memberships { - groupID := aws.StringValue(group.VpcSecurityGroupId) + groupID := aws.ToString(group.VpcSecurityGroupId) if groupID != "" { secGroups = append(secGroups, groupID) } @@ -451,7 +451,7 @@ func rdsSecurityGroupInfo(memberships []rdstypes.VpcSecurityGroupMembership) []s // It uses aws sdk v2. // An optional [rdstypes.DBInstance] can be passed to fill the network configuration of the Cluster. func MetadataFromRDSV2Cluster(rdsCluster *rdstypes.DBCluster, rdsInstance *rdstypes.DBInstance) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(rdsCluster.DBClusterArn)) + parsedARN, err := arn.Parse(aws.ToString(rdsCluster.DBClusterArn)) if err != nil { return nil, trace.Wrap(err) } @@ -467,9 +467,9 @@ func MetadataFromRDSV2Cluster(rdsCluster *rdstypes.DBCluster, rdsInstance *rdsty Region: parsedARN.Region, AccountID: parsedARN.AccountID, RDS: types.RDS{ - ClusterID: aws.StringValue(rdsCluster.DBClusterIdentifier), - ResourceID: aws.StringValue(rdsCluster.DbClusterResourceId), - IAMAuth: aws.BoolValue(rdsCluster.IAMDatabaseAuthenticationEnabled), + ClusterID: aws.ToString(rdsCluster.DBClusterIdentifier), + ResourceID: aws.ToString(rdsCluster.DbClusterResourceId), + IAMAuth: aws.ToBool(rdsCluster.IAMDatabaseAuthenticationEnabled), Subnets: subnets, VPCID: vpcID, SecurityGroups: rdsSecurityGroupInfo(rdsCluster.VpcSecurityGroups), @@ -481,12 +481,12 @@ func MetadataFromRDSV2Cluster(rdsCluster *rdstypes.DBCluster, rdsInstance *rdsty // It uses aws sdk v2. func labelsFromRDSV2Cluster(rdsCluster *rdstypes.DBCluster, meta *types.AWS, endpointType string, memberInstance *rdstypes.DBInstance) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsCluster.Engine) - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsCluster.EngineVersion) + labels[types.DiscoveryLabelEngine] = aws.ToString(rdsCluster.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(rdsCluster.EngineVersion) labels[types.DiscoveryLabelEndpointType] = endpointType - labels[types.DiscoveryLabelStatus] = aws.StringValue(rdsCluster.Status) + labels[types.DiscoveryLabelStatus] = aws.ToString(rdsCluster.Status) if memberInstance != nil && memberInstance.DBSubnetGroup != nil { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(memberInstance.DBSubnetGroup.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.ToString(memberInstance.DBSubnetGroup.VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(rdsCluster.TagList)) } @@ -497,7 +497,7 @@ func NewDatabaseFromRDSCluster(cluster *rds.DBCluster, memberInstances []*rds.DB if err != nil { return nil, trace.Wrap(err) } - protocol, err := rdsEngineToProtocol(aws.StringValue(cluster.Engine)) + protocol, err := rdsEngineToProtocol(aws.ToString(cluster.Engine)) if err != nil { return nil, trace.Wrap(err) } @@ -505,10 +505,10 @@ func NewDatabaseFromRDSCluster(cluster *rds.DBCluster, memberInstances []*rds.DB setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v", metadata.Region), Labels: labelsFromRDSCluster(cluster, metadata, apiawsutils.RDSEndpointTypePrimary, memberInstances), - }, aws.StringValue(cluster.DBClusterIdentifier)), + }, aws.ToString(cluster.DBClusterIdentifier)), types.DatabaseSpecV3{ Protocol: protocol, - URI: fmt.Sprintf("%v:%v", aws.StringValue(cluster.Endpoint), aws.Int64Value(cluster.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(cluster.Endpoint), aws.ToInt64(cluster.Port)), AWS: *metadata, }) } @@ -519,7 +519,7 @@ func NewDatabaseFromRDSClusterReaderEndpoint(cluster *rds.DBCluster, memberInsta if err != nil { return nil, trace.Wrap(err) } - protocol, err := rdsEngineToProtocol(aws.StringValue(cluster.Engine)) + protocol, err := rdsEngineToProtocol(aws.ToString(cluster.Engine)) if err != nil { return nil, trace.Wrap(err) } @@ -527,10 +527,10 @@ func NewDatabaseFromRDSClusterReaderEndpoint(cluster *rds.DBCluster, memberInsta setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v (%v endpoint)", metadata.Region, apiawsutils.RDSEndpointTypeReader), Labels: labelsFromRDSCluster(cluster, metadata, apiawsutils.RDSEndpointTypeReader, memberInstances), - }, aws.StringValue(cluster.DBClusterIdentifier), apiawsutils.RDSEndpointTypeReader), + }, aws.ToString(cluster.DBClusterIdentifier), apiawsutils.RDSEndpointTypeReader), types.DatabaseSpecV3{ Protocol: protocol, - URI: fmt.Sprintf("%v:%v", aws.StringValue(cluster.ReaderEndpoint), aws.Int64Value(cluster.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(cluster.ReaderEndpoint), aws.ToInt64(cluster.Port)), AWS: *metadata, }) } @@ -541,7 +541,7 @@ func NewDatabasesFromRDSClusterCustomEndpoints(cluster *rds.DBCluster, memberIns if err != nil { return nil, trace.Wrap(err) } - protocol, err := rdsEngineToProtocol(aws.StringValue(cluster.Engine)) + protocol, err := rdsEngineToProtocol(aws.ToString(cluster.Engine)) if err != nil { return nil, trace.Wrap(err) } @@ -551,7 +551,7 @@ func NewDatabasesFromRDSClusterCustomEndpoints(cluster *rds.DBCluster, memberIns for _, endpoint := range cluster.CustomEndpoints { // RDS custom endpoint format: // .cluster-custom-. - endpointDetails, err := apiawsutils.ParseRDSEndpoint(aws.StringValue(endpoint)) + endpointDetails, err := apiawsutils.ParseRDSEndpoint(aws.ToString(endpoint)) if err != nil { errors = append(errors, trace.Wrap(err)) continue @@ -565,16 +565,16 @@ func NewDatabasesFromRDSClusterCustomEndpoints(cluster *rds.DBCluster, memberIns setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v (%v endpoint)", metadata.Region, apiawsutils.RDSEndpointTypeCustom), Labels: labelsFromRDSCluster(cluster, metadata, apiawsutils.RDSEndpointTypeCustom, memberInstances), - }, aws.StringValue(cluster.DBClusterIdentifier), apiawsutils.RDSEndpointTypeCustom, endpointDetails.ClusterCustomEndpointName), + }, aws.ToString(cluster.DBClusterIdentifier), apiawsutils.RDSEndpointTypeCustom, endpointDetails.ClusterCustomEndpointName), types.DatabaseSpecV3{ Protocol: protocol, - URI: fmt.Sprintf("%v:%v", aws.StringValue(endpoint), aws.Int64Value(cluster.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(endpoint), aws.ToInt64(cluster.Port)), AWS: *metadata, // Aurora instances update their certificates upon restart, and thus custom endpoint SAN may not be available right // away. Using primary endpoint instead as server name since it's always available. TLS: types.DatabaseTLS{ - ServerName: aws.StringValue(cluster.Endpoint), + ServerName: aws.ToString(cluster.Endpoint), }, }) if err != nil { @@ -591,7 +591,7 @@ func NewDatabasesFromRDSClusterCustomEndpoints(cluster *rds.DBCluster, memberIns func checkRDSClusterMembers(cluster *rds.DBCluster) (hasWriterInstance, hasReaderInstance bool) { for _, clusterMember := range cluster.DBClusterMembers { if clusterMember != nil { - if aws.BoolValue(clusterMember.IsClusterWriter) { + if aws.ToBool(clusterMember.IsClusterWriter) { hasWriterInstance = true } else { hasReaderInstance = true @@ -692,10 +692,10 @@ func NewDatabaseFromDocumentDBClusterEndpoint(cluster *rds.DBCluster) (types.Dat setAWSDBName(types.Metadata{ Description: fmt.Sprintf("DocumentDB cluster in %v", metadata.Region), Labels: labelsFromDocumentDBCluster(cluster, metadata, endpointType), - }, aws.StringValue(cluster.DBClusterIdentifier)), + }, aws.ToString(cluster.DBClusterIdentifier)), types.DatabaseSpecV3{ Protocol: types.DatabaseProtocolMongoDB, - URI: fmt.Sprintf("%v:%v", aws.StringValue(cluster.Endpoint), aws.Int64Value(cluster.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(cluster.Endpoint), aws.ToInt64(cluster.Port)), AWS: *metadata, }) } @@ -712,10 +712,10 @@ func NewDatabaseFromDocumentDBReaderEndpoint(cluster *rds.DBCluster) (types.Data setAWSDBName(types.Metadata{ Description: fmt.Sprintf("DocumentDB cluster in %v (%v endpoint)", metadata.Region, endpointType), Labels: labelsFromDocumentDBCluster(cluster, metadata, endpointType), - }, aws.StringValue(cluster.DBClusterIdentifier), endpointType), + }, aws.ToString(cluster.DBClusterIdentifier), endpointType), types.DatabaseSpecV3{ Protocol: types.DatabaseProtocolMongoDB, - URI: fmt.Sprintf("%v:%v", aws.StringValue(cluster.ReaderEndpoint), aws.Int64Value(cluster.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(cluster.ReaderEndpoint), aws.ToInt64(cluster.Port)), AWS: *metadata, }) } @@ -726,7 +726,7 @@ func NewDatabaseFromRDSProxy(dbProxy *rds.DBProxy, tags []*rds.Tag) (types.Datab if err != nil { return nil, trace.Wrap(err) } - protocol, port, err := rdsEngineFamilyToProtocolAndPort(aws.StringValue(dbProxy.EngineFamily)) + protocol, port, err := rdsEngineFamilyToProtocolAndPort(aws.ToString(dbProxy.EngineFamily)) if err != nil { return nil, trace.Wrap(err) } @@ -734,10 +734,10 @@ func NewDatabaseFromRDSProxy(dbProxy *rds.DBProxy, tags []*rds.Tag) (types.Datab setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS Proxy in %v", metadata.Region), Labels: labelsFromRDSProxy(dbProxy, metadata, tags), - }, aws.StringValue(dbProxy.DBProxyName)), + }, aws.ToString(dbProxy.DBProxyName)), types.DatabaseSpecV3{ Protocol: protocol, - URI: fmt.Sprintf("%s:%d", aws.StringValue(dbProxy.Endpoint), port), + URI: fmt.Sprintf("%s:%d", aws.ToString(dbProxy.Endpoint), port), AWS: *metadata, }) } @@ -749,7 +749,7 @@ func NewDatabaseFromRDSProxyCustomEndpoint(dbProxy *rds.DBProxy, customEndpoint if err != nil { return nil, trace.Wrap(err) } - protocol, port, err := rdsEngineFamilyToProtocolAndPort(aws.StringValue(dbProxy.EngineFamily)) + protocol, port, err := rdsEngineFamilyToProtocolAndPort(aws.ToString(dbProxy.EngineFamily)) if err != nil { return nil, trace.Wrap(err) } @@ -757,10 +757,10 @@ func NewDatabaseFromRDSProxyCustomEndpoint(dbProxy *rds.DBProxy, customEndpoint setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS Proxy endpoint in %v", metadata.Region), Labels: labelsFromRDSProxyCustomEndpoint(dbProxy, customEndpoint, metadata, tags), - }, aws.StringValue(dbProxy.DBProxyName), aws.StringValue(customEndpoint.DBProxyEndpointName)), + }, aws.ToString(dbProxy.DBProxyName), aws.ToString(customEndpoint.DBProxyEndpointName)), types.DatabaseSpecV3{ Protocol: protocol, - URI: fmt.Sprintf("%s:%d", aws.StringValue(customEndpoint.Endpoint), port), + URI: fmt.Sprintf("%s:%d", aws.ToString(customEndpoint.Endpoint), port), AWS: *metadata, // RDS proxies serve wildcard certificates like this: @@ -773,17 +773,17 @@ func NewDatabaseFromRDSProxyCustomEndpoint(dbProxy *rds.DBProxy, customEndpoint // Using proxy's default endpoint as server name as it should always // succeed. TLS: types.DatabaseTLS{ - ServerName: aws.StringValue(dbProxy.Endpoint), + ServerName: aws.ToString(dbProxy.Endpoint), }, }) } // NewDatabaseFromRedshiftCluster creates a database resource from a Redshift cluster. -func NewDatabaseFromRedshiftCluster(cluster *redshift.Cluster) (types.Database, error) { +func NewDatabaseFromRedshiftCluster(cluster *redshifttypes.Cluster) (types.Database, error) { // Endpoint can be nil while the cluster is being created. Return an error // until the Endpoint is available. if cluster.Endpoint == nil { - return nil, trace.BadParameter("missing endpoint in Redshift cluster %v", aws.StringValue(cluster.ClusterIdentifier)) + return nil, trace.BadParameter("missing endpoint in Redshift cluster %v", aws.ToString(cluster.ClusterIdentifier)) } metadata, err := MetadataFromRedshiftCluster(cluster) @@ -795,10 +795,10 @@ func NewDatabaseFromRedshiftCluster(cluster *redshift.Cluster) (types.Database, setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Redshift cluster in %v", metadata.Region), Labels: labelsFromRedshiftCluster(cluster, metadata), - }, aws.StringValue(cluster.ClusterIdentifier)), + }, aws.ToString(cluster.ClusterIdentifier)), types.DatabaseSpecV3{ Protocol: defaults.ProtocolPostgres, - URI: fmt.Sprintf("%v:%v", aws.StringValue(cluster.Endpoint.Address), aws.Int64Value(cluster.Endpoint.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(cluster.Endpoint.Address), aws.ToInt32(cluster.Endpoint.Port)), AWS: *metadata, }) } @@ -842,7 +842,7 @@ func NewDatabasesFromElastiCacheNodeGroups(cluster *elasticache.ReplicationGroup func NewDatabasesFromElastiCacheReplicationGroup(cluster *elasticache.ReplicationGroup, extraLabels map[string]string) (types.Databases, error) { // Create database using configuration endpoint for Redis with cluster // mode enabled. - if aws.BoolValue(cluster.ClusterEnabled) { + if aws.ToBool(cluster.ClusterEnabled) { database, err := NewDatabaseFromElastiCacheConfigurationEndpoint(cluster, extraLabels) if err != nil { return nil, trace.Wrap(err) @@ -873,9 +873,9 @@ func newElastiCacheDatabase(cluster *elasticache.ReplicationGroup, endpoint *ela return types.NewDatabaseV3(setAWSDBName(types.Metadata{ Description: fmt.Sprintf("ElastiCache cluster in %v (%v endpoint)", metadata.Region, endpointType), Labels: labelsFromMetaAndEndpointType(metadata, endpointType, extraLabels), - }, aws.StringValue(cluster.ReplicationGroupId), suffix...), types.DatabaseSpecV3{ + }, aws.ToString(cluster.ReplicationGroupId), suffix...), types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, - URI: fmt.Sprintf("%v:%v", aws.StringValue(endpoint.Address), aws.Int64Value(endpoint.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(endpoint.Address), aws.ToInt64(endpoint.Port)), AWS: *metadata, }) } @@ -884,7 +884,7 @@ func newElastiCacheDatabase(cluster *elasticache.ReplicationGroup, endpoint *ela func NewDatabasesFromOpenSearchDomain(domain *opensearchservice.DomainStatus, tags []*opensearchservice.Tag) (types.Databases, error) { var databases types.Databases - if aws.StringValue(domain.Endpoint) != "" { + if aws.ToString(domain.Endpoint) != "" { metadata, err := MetadataFromOpenSearchDomain(domain, apiawsutils.OpenSearchDefaultEndpoint) if err != nil { return nil, trace.Wrap(err) @@ -895,10 +895,10 @@ func NewDatabasesFromOpenSearchDomain(domain *opensearchservice.DomainStatus, ta Labels: labelsFromOpenSearchDomain(domain, metadata, apiawsutils.OpenSearchDefaultEndpoint, tags), } - meta = setAWSDBName(meta, aws.StringValue(domain.DomainName)) + meta = setAWSDBName(meta, aws.ToString(domain.DomainName)) spec := types.DatabaseSpecV3{ Protocol: defaults.ProtocolOpenSearch, - URI: fmt.Sprintf("%v:443", aws.StringValue(domain.Endpoint)), + URI: fmt.Sprintf("%v:443", aws.ToString(domain.Endpoint)), AWS: *metadata, } @@ -910,7 +910,7 @@ func NewDatabasesFromOpenSearchDomain(domain *opensearchservice.DomainStatus, ta databases = append(databases, db) } - if domain.DomainEndpointOptions != nil && aws.StringValue(domain.DomainEndpointOptions.CustomEndpoint) != "" { + if domain.DomainEndpointOptions != nil && aws.ToString(domain.DomainEndpointOptions.CustomEndpoint) != "" { metadata, err := MetadataFromOpenSearchDomain(domain, apiawsutils.OpenSearchCustomEndpoint) if err != nil { return nil, trace.Wrap(err) @@ -921,10 +921,10 @@ func NewDatabasesFromOpenSearchDomain(domain *opensearchservice.DomainStatus, ta Labels: labelsFromOpenSearchDomain(domain, metadata, apiawsutils.OpenSearchCustomEndpoint, tags), } - meta = setAWSDBName(meta, aws.StringValue(domain.DomainName), "custom") + meta = setAWSDBName(meta, aws.ToString(domain.DomainName), "custom") spec := types.DatabaseSpecV3{ Protocol: defaults.ProtocolOpenSearch, - URI: fmt.Sprintf("%v:443", aws.StringValue(domain.DomainEndpointOptions.CustomEndpoint)), + URI: fmt.Sprintf("%v:443", aws.ToString(domain.DomainEndpointOptions.CustomEndpoint)), AWS: *metadata, } @@ -948,13 +948,13 @@ func NewDatabasesFromOpenSearchDomain(domain *opensearchservice.DomainStatus, ta } if domain.VPCOptions != nil { - meta.Labels[types.DiscoveryLabelVPCID] = aws.StringValue(domain.VPCOptions.VPCId) + meta.Labels[types.DiscoveryLabelVPCID] = aws.ToString(domain.VPCOptions.VPCId) } - meta = setAWSDBName(meta, aws.StringValue(domain.DomainName), name) + meta = setAWSDBName(meta, aws.ToString(domain.DomainName), name) spec := types.DatabaseSpecV3{ Protocol: defaults.ProtocolOpenSearch, - URI: fmt.Sprintf("%v:443", aws.StringValue(url)), + URI: fmt.Sprintf("%v:443", aws.ToString(url)), AWS: *metadata, } @@ -983,10 +983,10 @@ func NewDatabaseFromMemoryDBCluster(cluster *memorydb.Cluster, extraLabels map[s setAWSDBName(types.Metadata{ Description: fmt.Sprintf("MemoryDB cluster in %v", metadata.Region), Labels: labelsFromMetaAndEndpointType(metadata, endpointType, extraLabels), - }, aws.StringValue(cluster.Name)), + }, aws.ToString(cluster.Name)), types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, - URI: fmt.Sprintf("%v:%v", aws.StringValue(cluster.ClusterEndpoint.Address), aws.Int64Value(cluster.ClusterEndpoint.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(cluster.ClusterEndpoint.Address), aws.ToInt64(cluster.ClusterEndpoint.Port)), AWS: *metadata, }) } @@ -1010,7 +1010,7 @@ func NewDatabaseFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Wo }, metadata.RedshiftServerless.WorkgroupName), types.DatabaseSpecV3{ Protocol: defaults.ProtocolPostgres, - URI: fmt.Sprintf("%v:%v", aws.StringValue(workgroup.Endpoint.Address), aws.Int64Value(workgroup.Endpoint.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(workgroup.Endpoint.Address), aws.ToInt64(workgroup.Endpoint.Port)), AWS: *metadata, }) } @@ -1034,19 +1034,19 @@ func NewDatabaseFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.E }, metadata.RedshiftServerless.WorkgroupName, metadata.RedshiftServerless.EndpointName), types.DatabaseSpecV3{ Protocol: defaults.ProtocolPostgres, - URI: fmt.Sprintf("%v:%v", aws.StringValue(endpoint.Address), aws.Int64Value(endpoint.Port)), + URI: fmt.Sprintf("%v:%v", aws.ToString(endpoint.Address), aws.ToInt64(endpoint.Port)), AWS: *metadata, // Use workgroup's default address as the server name. TLS: types.DatabaseTLS{ - ServerName: aws.StringValue(workgroup.Endpoint.Address), + ServerName: aws.ToString(workgroup.Endpoint.Address), }, }) } // MetadataFromRDSInstance creates AWS metadata from the provided RDS instance. func MetadataFromRDSInstance(rdsInstance *rds.DBInstance) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(rdsInstance.DBInstanceArn)) + parsedARN, err := arn.Parse(aws.ToString(rdsInstance.DBInstanceArn)) if err != nil { return nil, trace.Wrap(err) } @@ -1054,17 +1054,17 @@ func MetadataFromRDSInstance(rdsInstance *rds.DBInstance) (*types.AWS, error) { Region: parsedARN.Region, AccountID: parsedARN.AccountID, RDS: types.RDS{ - InstanceID: aws.StringValue(rdsInstance.DBInstanceIdentifier), - ClusterID: aws.StringValue(rdsInstance.DBClusterIdentifier), - ResourceID: aws.StringValue(rdsInstance.DbiResourceId), - IAMAuth: aws.BoolValue(rdsInstance.IAMDatabaseAuthenticationEnabled), + InstanceID: aws.ToString(rdsInstance.DBInstanceIdentifier), + ClusterID: aws.ToString(rdsInstance.DBClusterIdentifier), + ResourceID: aws.ToString(rdsInstance.DbiResourceId), + IAMAuth: aws.ToBool(rdsInstance.IAMDatabaseAuthenticationEnabled), }, }, nil } // MetadataFromRDSCluster creates AWS metadata from the provided RDS cluster. func MetadataFromRDSCluster(rdsCluster *rds.DBCluster) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(rdsCluster.DBClusterArn)) + parsedARN, err := arn.Parse(aws.ToString(rdsCluster.DBClusterArn)) if err != nil { return nil, trace.Wrap(err) } @@ -1072,9 +1072,9 @@ func MetadataFromRDSCluster(rdsCluster *rds.DBCluster) (*types.AWS, error) { Region: parsedARN.Region, AccountID: parsedARN.AccountID, RDS: types.RDS{ - ClusterID: aws.StringValue(rdsCluster.DBClusterIdentifier), - ResourceID: aws.StringValue(rdsCluster.DbClusterResourceId), - IAMAuth: aws.BoolValue(rdsCluster.IAMDatabaseAuthenticationEnabled), + ClusterID: aws.ToString(rdsCluster.DBClusterIdentifier), + ResourceID: aws.ToString(rdsCluster.DbClusterResourceId), + IAMAuth: aws.ToBool(rdsCluster.IAMDatabaseAuthenticationEnabled), }, }, nil } @@ -1082,7 +1082,7 @@ func MetadataFromRDSCluster(rdsCluster *rds.DBCluster) (*types.AWS, error) { // MetadataFromDocumentDBCluster creates AWS metadata from the provided // DocumentDB cluster. func MetadataFromDocumentDBCluster(cluster *rds.DBCluster, endpointType string) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(cluster.DBClusterArn)) + parsedARN, err := arn.Parse(aws.ToString(cluster.DBClusterArn)) if err != nil { return nil, trace.Wrap(err) } @@ -1090,7 +1090,7 @@ func MetadataFromDocumentDBCluster(cluster *rds.DBCluster, endpointType string) Region: parsedARN.Region, AccountID: parsedARN.AccountID, DocumentDB: types.DocumentDB{ - ClusterID: aws.StringValue(cluster.DBClusterIdentifier), + ClusterID: aws.ToString(cluster.DBClusterIdentifier), EndpointType: endpointType, }, }, nil @@ -1098,7 +1098,7 @@ func MetadataFromDocumentDBCluster(cluster *rds.DBCluster, endpointType string) // MetadataFromRDSProxy creates AWS metadata from the provided RDS Proxy. func MetadataFromRDSProxy(rdsProxy *rds.DBProxy) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(rdsProxy.DBProxyArn)) + parsedARN, err := arn.Parse(aws.ToString(rdsProxy.DBProxyArn)) if err != nil { return nil, trace.Wrap(err) } @@ -1112,14 +1112,14 @@ func MetadataFromRDSProxy(rdsProxy *rds.DBProxy) (*types.AWS, error) { // resource type is "db-proxy" and the resource ID is "prx-xxxyyyzzz". _, resourceID, ok := strings.Cut(parsedARN.Resource, ":") if !ok { - return nil, trace.BadParameter("failed to find resource ID from %v", aws.StringValue(rdsProxy.DBProxyArn)) + return nil, trace.BadParameter("failed to find resource ID from %v", aws.ToString(rdsProxy.DBProxyArn)) } return &types.AWS{ Region: parsedARN.Region, AccountID: parsedARN.AccountID, RDSProxy: types.RDSProxy{ - Name: aws.StringValue(rdsProxy.DBProxyName), + Name: aws.ToString(rdsProxy.DBProxyName), ResourceID: resourceID, }, }, nil @@ -1135,13 +1135,13 @@ func MetadataFromRDSProxyCustomEndpoint(rdsProxy *rds.DBProxy, customEndpoint *r return nil, trace.Wrap(err) } - metadata.RDSProxy.CustomEndpointName = aws.StringValue(customEndpoint.DBProxyEndpointName) + metadata.RDSProxy.CustomEndpointName = aws.ToString(customEndpoint.DBProxyEndpointName) return metadata, nil } // MetadataFromRedshiftCluster creates AWS metadata from the provided Redshift cluster. -func MetadataFromRedshiftCluster(cluster *redshift.Cluster) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(cluster.ClusterNamespaceArn)) +func MetadataFromRedshiftCluster(cluster *redshifttypes.Cluster) (*types.AWS, error) { + parsedARN, err := arn.Parse(aws.ToString(cluster.ClusterNamespaceArn)) if err != nil { return nil, trace.Wrap(err) } @@ -1149,7 +1149,7 @@ func MetadataFromRedshiftCluster(cluster *redshift.Cluster) (*types.AWS, error) Region: parsedARN.Region, AccountID: parsedARN.AccountID, Redshift: types.Redshift{ - ClusterID: aws.StringValue(cluster.ClusterIdentifier), + ClusterID: aws.ToString(cluster.ClusterIdentifier), }, }, nil } @@ -1157,7 +1157,7 @@ func MetadataFromRedshiftCluster(cluster *redshift.Cluster) (*types.AWS, error) // MetadataFromElastiCacheCluster creates AWS metadata for the provided // ElastiCache cluster. func MetadataFromElastiCacheCluster(cluster *elasticache.ReplicationGroup, endpointType string) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(cluster.ARN)) + parsedARN, err := arn.Parse(aws.ToString(cluster.ARN)) if err != nil { return nil, trace.Wrap(err) } @@ -1168,16 +1168,16 @@ func MetadataFromElastiCacheCluster(cluster *elasticache.ReplicationGroup, endpo // messages don't fail. var userGroupIDs []string if len(cluster.UserGroupIds) != 0 { - userGroupIDs = aws.StringValueSlice(cluster.UserGroupIds) + userGroupIDs = aws.ToStringSlice(cluster.UserGroupIds) } return &types.AWS{ Region: parsedARN.Region, AccountID: parsedARN.AccountID, ElastiCache: types.ElastiCache{ - ReplicationGroupID: aws.StringValue(cluster.ReplicationGroupId), + ReplicationGroupID: aws.ToString(cluster.ReplicationGroupId), UserGroupIDs: userGroupIDs, - TransitEncryptionEnabled: aws.BoolValue(cluster.TransitEncryptionEnabled), + TransitEncryptionEnabled: aws.ToBool(cluster.TransitEncryptionEnabled), EndpointType: endpointType, }, }, nil @@ -1185,7 +1185,7 @@ func MetadataFromElastiCacheCluster(cluster *elasticache.ReplicationGroup, endpo // MetadataFromOpenSearchDomain creates AWS metadata for the provided OpenSearch domain. func MetadataFromOpenSearchDomain(domain *opensearchservice.DomainStatus, endpointType string) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(domain.ARN)) + parsedARN, err := arn.Parse(aws.ToString(domain.ARN)) if err != nil { return nil, trace.Wrap(err) } @@ -1194,8 +1194,8 @@ func MetadataFromOpenSearchDomain(domain *opensearchservice.DomainStatus, endpoi Region: parsedARN.Region, AccountID: parsedARN.AccountID, OpenSearch: types.OpenSearch{ - DomainName: aws.StringValue(domain.DomainName), - DomainID: aws.StringValue(domain.DomainId), + DomainName: aws.ToString(domain.DomainName), + DomainID: aws.ToString(domain.DomainId), EndpointType: endpointType, }, }, nil @@ -1204,7 +1204,7 @@ func MetadataFromOpenSearchDomain(domain *opensearchservice.DomainStatus, endpoi // MetadataFromMemoryDBCluster creates AWS metadata for the provided MemoryDB // cluster. func MetadataFromMemoryDBCluster(cluster *memorydb.Cluster, endpointType string) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(cluster.ARN)) + parsedARN, err := arn.Parse(aws.ToString(cluster.ARN)) if err != nil { return nil, trace.Wrap(err) } @@ -1213,9 +1213,9 @@ func MetadataFromMemoryDBCluster(cluster *memorydb.Cluster, endpointType string) Region: parsedARN.Region, AccountID: parsedARN.AccountID, MemoryDB: types.MemoryDB{ - ClusterName: aws.StringValue(cluster.Name), - ACLName: aws.StringValue(cluster.ACLName), - TLSEnabled: aws.BoolValue(cluster.TLSEnabled), + ClusterName: aws.ToString(cluster.Name), + ACLName: aws.ToString(cluster.ACLName), + TLSEnabled: aws.ToBool(cluster.TLSEnabled), EndpointType: endpointType, }, }, nil @@ -1224,7 +1224,7 @@ func MetadataFromMemoryDBCluster(cluster *memorydb.Cluster, endpointType string) // MetadataFromRedshiftServerlessWorkgroup creates AWS metadata for the // provided Redshift Serverless Workgroup. func MetadataFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Workgroup) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(workgroup.WorkgroupArn)) + parsedARN, err := arn.Parse(aws.ToString(workgroup.WorkgroupArn)) if err != nil { return nil, trace.Wrap(err) } @@ -1233,8 +1233,8 @@ func MetadataFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Workg Region: parsedARN.Region, AccountID: parsedARN.AccountID, RedshiftServerless: types.RedshiftServerless{ - WorkgroupName: aws.StringValue(workgroup.WorkgroupName), - WorkgroupID: aws.StringValue(workgroup.WorkgroupId), + WorkgroupName: aws.ToString(workgroup.WorkgroupName), + WorkgroupID: aws.ToString(workgroup.WorkgroupId), }, }, nil } @@ -1242,7 +1242,7 @@ func MetadataFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Workg // MetadataFromRedshiftServerlessVPCEndpoint creates AWS metadata for the // provided Redshift Serverless VPC endpoint. func MetadataFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.EndpointAccess, workgroup *redshiftserverless.Workgroup) (*types.AWS, error) { - parsedARN, err := arn.Parse(aws.StringValue(endpoint.EndpointArn)) + parsedARN, err := arn.Parse(aws.ToString(endpoint.EndpointArn)) if err != nil { return nil, trace.Wrap(err) } @@ -1251,9 +1251,9 @@ func MetadataFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.Endp Region: parsedARN.Region, AccountID: parsedARN.AccountID, RedshiftServerless: types.RedshiftServerless{ - WorkgroupName: aws.StringValue(endpoint.WorkgroupName), - EndpointName: aws.StringValue(endpoint.EndpointName), - WorkgroupID: aws.StringValue(workgroup.WorkgroupId), + WorkgroupName: aws.ToString(endpoint.WorkgroupName), + EndpointName: aws.ToString(endpoint.EndpointName), + WorkgroupID: aws.ToString(workgroup.WorkgroupId), }, }, nil } @@ -1261,15 +1261,15 @@ func MetadataFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.Endp // ExtraElastiCacheLabels returns a list of extra labels for provided // ElastiCache cluster. func ExtraElastiCacheLabels(cluster *elasticache.ReplicationGroup, tags []*elasticache.Tag, allNodes []*elasticache.CacheCluster, allSubnetGroups []*elasticache.CacheSubnetGroup) map[string]string { - replicationGroupID := aws.StringValue(cluster.ReplicationGroupId) + replicationGroupID := aws.ToString(cluster.ReplicationGroupId) subnetGroupName := "" labels := make(map[string]string) // Find any node belongs to this cluster and set engine version label. for _, node := range allNodes { - if aws.StringValue(node.ReplicationGroupId) == replicationGroupID { - subnetGroupName = aws.StringValue(node.CacheSubnetGroupName) - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(node.EngineVersion) + if aws.ToString(node.ReplicationGroupId) == replicationGroupID { + subnetGroupName = aws.ToString(node.CacheSubnetGroupName) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(node.EngineVersion) break } } @@ -1280,8 +1280,8 @@ func ExtraElastiCacheLabels(cluster *elasticache.ReplicationGroup, tags []*elast // accessible within the same VPC. Having a VPC ID label can be very useful // for filtering. for _, subnetGroup := range allSubnetGroups { - if aws.StringValue(subnetGroup.CacheSubnetGroupName) == subnetGroupName { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(subnetGroup.VpcId) + if aws.ToString(subnetGroup.CacheSubnetGroupName) == subnetGroupName { + labels[types.DiscoveryLabelVPCID] = aws.ToString(subnetGroup.VpcId) break } } @@ -1296,12 +1296,12 @@ func ExtraMemoryDBLabels(cluster *memorydb.Cluster, tags []*memorydb.Tag, allSub labels := make(map[string]string) // Engine version. - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(cluster.EngineVersion) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(cluster.EngineVersion) // VPC ID. for _, subnetGroup := range allSubnetGroups { - if aws.StringValue(subnetGroup.Name) == aws.StringValue(cluster.SubnetGroupName) { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(subnetGroup.VpcId) + if aws.ToString(subnetGroup.Name) == aws.ToString(cluster.SubnetGroupName) { + labels[types.DiscoveryLabelVPCID] = aws.ToString(subnetGroup.VpcId) break } } @@ -1423,11 +1423,11 @@ func labelsFromAzurePostgresFlexServer(server *armpostgresqlflexibleservers.Serv // labelsFromRDSInstance creates database labels for the provided RDS instance. func labelsFromRDSInstance(rdsInstance *rds.DBInstance, meta *types.AWS) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsInstance.Engine) - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsInstance.EngineVersion) + labels[types.DiscoveryLabelEngine] = aws.ToString(rdsInstance.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(rdsInstance.EngineVersion) labels[types.DiscoveryLabelEndpointType] = apiawsutils.RDSEndpointTypeInstance if rdsInstance.DBSubnetGroup != nil { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(rdsInstance.DBSubnetGroup.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.ToString(rdsInstance.DBSubnetGroup.VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(rdsInstance.TagList)) } @@ -1435,19 +1435,19 @@ func labelsFromRDSInstance(rdsInstance *rds.DBInstance, meta *types.AWS) map[str // labelsFromRDSCluster creates database labels for the provided RDS cluster. func labelsFromRDSCluster(rdsCluster *rds.DBCluster, meta *types.AWS, endpointType string, memberInstances []*rds.DBInstance) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsCluster.Engine) - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsCluster.EngineVersion) + labels[types.DiscoveryLabelEngine] = aws.ToString(rdsCluster.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(rdsCluster.EngineVersion) labels[types.DiscoveryLabelEndpointType] = endpointType if len(memberInstances) > 0 && memberInstances[0].DBSubnetGroup != nil { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(memberInstances[0].DBSubnetGroup.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.ToString(memberInstances[0].DBSubnetGroup.VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(rdsCluster.TagList)) } func labelsFromDocumentDBCluster(cluster *rds.DBCluster, meta *types.AWS, endpointType string) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[types.DiscoveryLabelEngine] = aws.StringValue(cluster.Engine) - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(cluster.EngineVersion) + labels[types.DiscoveryLabelEngine] = aws.ToString(cluster.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(cluster.EngineVersion) labels[types.DiscoveryLabelEndpointType] = endpointType return addLabels(labels, libcloudaws.TagsToLabels(cluster.TagList)) } @@ -1456,8 +1456,8 @@ func labelsFromDocumentDBCluster(cluster *rds.DBCluster, meta *types.AWS, endpoi func labelsFromRDSProxy(rdsProxy *rds.DBProxy, meta *types.AWS, tags []*rds.Tag) map[string]string { // rds.DBProxy has no TagList. labels := labelsFromAWSMetadata(meta) - labels[types.DiscoveryLabelVPCID] = aws.StringValue(rdsProxy.VpcId) - labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsProxy.EngineFamily) + labels[types.DiscoveryLabelVPCID] = aws.ToString(rdsProxy.VpcId) + labels[types.DiscoveryLabelEngine] = aws.ToString(rdsProxy.EngineFamily) return addLabels(labels, libcloudaws.TagsToLabels(tags)) } @@ -1465,12 +1465,12 @@ func labelsFromRDSProxy(rdsProxy *rds.DBProxy, meta *types.AWS, tags []*rds.Tag) // RDS Proxy custom endpoint. func labelsFromRDSProxyCustomEndpoint(rdsProxy *rds.DBProxy, customEndpoint *rds.DBProxyEndpoint, meta *types.AWS, tags []*rds.Tag) map[string]string { labels := labelsFromRDSProxy(rdsProxy, meta, tags) - labels[types.DiscoveryLabelEndpointType] = aws.StringValue(customEndpoint.TargetRole) + labels[types.DiscoveryLabelEndpointType] = aws.ToString(customEndpoint.TargetRole) return labels } // labelsFromRedshiftCluster creates database labels for the provided Redshift cluster. -func labelsFromRedshiftCluster(cluster *redshift.Cluster, meta *types.AWS) map[string]string { +func labelsFromRedshiftCluster(cluster *redshifttypes.Cluster, meta *types.AWS) map[string]string { labels := labelsFromAWSMetadata(meta) return addLabels(labels, libcloudaws.TagsToLabels(cluster.Tags)) } @@ -1478,9 +1478,9 @@ func labelsFromRedshiftCluster(cluster *redshift.Cluster, meta *types.AWS) map[s func labelsFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Workgroup, meta *types.AWS, tags []*redshiftserverless.Tag) map[string]string { labels := labelsFromAWSMetadata(meta) labels[types.DiscoveryLabelEndpointType] = services.RedshiftServerlessWorkgroupEndpoint - labels[types.DiscoveryLabelNamespace] = aws.StringValue(workgroup.NamespaceName) + labels[types.DiscoveryLabelNamespace] = aws.ToString(workgroup.NamespaceName) if workgroup.Endpoint != nil && len(workgroup.Endpoint.VpcEndpoints) > 0 { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(workgroup.Endpoint.VpcEndpoints[0].VpcId) + labels[types.DiscoveryLabelVPCID] = aws.ToString(workgroup.Endpoint.VpcEndpoints[0].VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(tags)) } @@ -1488,10 +1488,10 @@ func labelsFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Workgro func labelsFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.EndpointAccess, workgroup *redshiftserverless.Workgroup, meta *types.AWS, tags []*redshiftserverless.Tag) map[string]string { labels := labelsFromAWSMetadata(meta) labels[types.DiscoveryLabelEndpointType] = services.RedshiftServerlessVPCEndpoint - labels[types.DiscoveryLabelWorkgroup] = aws.StringValue(endpoint.WorkgroupName) - labels[types.DiscoveryLabelNamespace] = aws.StringValue(workgroup.NamespaceName) + labels[types.DiscoveryLabelWorkgroup] = aws.ToString(endpoint.WorkgroupName) + labels[types.DiscoveryLabelNamespace] = aws.ToString(workgroup.NamespaceName) if endpoint.VpcEndpoint != nil { - labels[types.DiscoveryLabelVPCID] = aws.StringValue(endpoint.VpcEndpoint.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.ToString(endpoint.VpcEndpoint.VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(tags)) } @@ -1509,7 +1509,7 @@ func labelsFromAWSMetadata(meta *types.AWS) map[string]string { func labelsFromOpenSearchDomain(domain *opensearchservice.DomainStatus, meta *types.AWS, endpointType string, tags []*opensearchservice.Tag) map[string]string { labels := labelsFromMetaAndEndpointType(meta, endpointType, libcloudaws.TagsToLabels(tags)) - labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(domain.EngineVersion) + labels[types.DiscoveryLabelEngineVersion] = aws.ToString(domain.EngineVersion) return labels } diff --git a/lib/srv/discovery/common/database_test.go b/lib/srv/discovery/common/database_test.go index 24cb2dacfd483..ab2b45fff24bc 100644 --- a/lib/srv/discovery/common/database_test.go +++ b/lib/srv/discovery/common/database_test.go @@ -27,12 +27,12 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v3" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redisenterprise/armredisenterprise" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql" + "github.com/aws/aws-sdk-go-v2/aws" rdsTypesV2 "github.com/aws/aws-sdk-go-v2/service/rds/types" - "github.com/aws/aws-sdk-go/aws" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/memorydb" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/redshiftserverless" "github.com/google/go-cmp/cmp" "github.com/google/uuid" @@ -1182,14 +1182,14 @@ func TestAzureTagsToLabels(t *testing.T) { // TestDatabaseFromRedshiftCluster tests converting an Redshift cluster to a database resource. func TestDatabaseFromRedshiftCluster(t *testing.T) { t.Run("success", func(t *testing.T) { - cluster := &redshift.Cluster{ + cluster := &redshifttypes.Cluster{ ClusterIdentifier: aws.String("mycluster"), ClusterNamespaceArn: aws.String("arn:aws:redshift:us-east-1:123456789012:namespace:u-u-i-d"), - Endpoint: &redshift.Endpoint{ + Endpoint: &redshifttypes.Endpoint{ Address: aws.String("localhost"), - Port: aws.Int64(5439), + Port: aws.Int32(5439), }, - Tags: []*redshift.Tag{ + Tags: []redshifttypes.Tag{ { Key: aws.String("key"), Value: aws.String("val"), @@ -1231,14 +1231,14 @@ func TestDatabaseFromRedshiftCluster(t *testing.T) { for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { t.Run("success with name override via"+overrideLabel, func(t *testing.T) { - cluster := &redshift.Cluster{ + cluster := &redshifttypes.Cluster{ ClusterIdentifier: aws.String("mycluster"), ClusterNamespaceArn: aws.String("arn:aws:redshift:us-east-1:123456789012:namespace:u-u-i-d"), - Endpoint: &redshift.Endpoint{ + Endpoint: &redshifttypes.Endpoint{ Address: aws.String("localhost"), - Port: aws.Int64(5439), + Port: aws.Int32(5439), }, - Tags: []*redshift.Tag{ + Tags: []redshifttypes.Tag{ { Key: aws.String("key"), Value: aws.String("val"), @@ -1284,7 +1284,7 @@ func TestDatabaseFromRedshiftCluster(t *testing.T) { }) t.Run("missing endpoint", func(t *testing.T) { - _, err := NewDatabaseFromRedshiftCluster(&redshift.Cluster{ + _, err := NewDatabaseFromRedshiftCluster(&redshifttypes.Cluster{ ClusterIdentifier: aws.String("still-creating"), }) require.Error(t, err) diff --git a/lib/srv/discovery/config_test.go b/lib/srv/discovery/config_test.go index 6954a2036a493..615c1852643a8 100644 --- a/lib/srv/discovery/config_test.go +++ b/lib/srv/discovery/config_test.go @@ -50,6 +50,8 @@ func TestConfigCheckAndSetDefaults(t *testing.T) { cfgChange: func(c *Config) {}, postCheckAndSetDefaultsFunc: func(t *testing.T, c *Config) { require.NotNil(t, c.CloudClients) + require.NotNil(t, c.AWSConfigProvider) + require.NotNil(t, c.AWSDatabaseFetcherFactory) require.NotNil(t, c.Log) require.NotNil(t, c.clock) require.NotNil(t, c.TriggerFetchC) diff --git a/lib/srv/discovery/database_watcher.go b/lib/srv/discovery/database_watcher.go index 0ce0c6ef8ef42..b14332a8f9bb4 100644 --- a/lib/srv/discovery/database_watcher.go +++ b/lib/srv/discovery/database_watcher.go @@ -74,17 +74,7 @@ func (s *Server) startDatabaseWatchers() error { TriggerFetchC: s.newDiscoveryConfigChangedSub(), Origin: types.OriginCloud, Clock: s.clock, - PreFetchHookFn: func() { - discoveryConfigs := slices.FilterMapUnique( - s.getAllDatabaseFetchers(), - func(f common.Fetcher) (s string, include bool) { - return f.GetDiscoveryConfigName(), f.GetDiscoveryConfigName() != "" - }, - ) - s.updateDiscoveryConfigStatus(discoveryConfigs...) - - s.awsRDSResourcesStatus.reset() - }, + PreFetchHookFn: s.databaseWatcherIterationStarted, }, ) if err != nil { @@ -151,6 +141,38 @@ func (s *Server) startDatabaseWatchers() error { return nil } +func (s *Server) databaseWatcherIterationStarted() { + allFetchers := s.getAllDatabaseFetchers() + if len(allFetchers) == 0 { + return + } + + s.submitFetchersEvent(allFetchers) + + awsResultGroups := slices.FilterMapUnique( + allFetchers, + func(f common.Fetcher) (awsResourceGroup, bool) { + include := f.GetDiscoveryConfigName() != "" && f.IntegrationName() != "" + resourceGroup := awsResourceGroup{ + discoveryConfigName: f.GetDiscoveryConfigName(), + integration: f.IntegrationName(), + } + return resourceGroup, include + }, + ) + + for _, g := range awsResultGroups { + s.awsRDSResourcesStatus.iterationStarted(g) + } + + discoveryConfigs := slices.FilterMapUnique(awsResultGroups, func(g awsResourceGroup) (s string, include bool) { + return g.discoveryConfigName, true + }) + s.updateDiscoveryConfigStatus(discoveryConfigs...) + + s.awsRDSResourcesStatus.reset() +} + func (s *Server) getAllDatabaseFetchers() []common.Fetcher { allFetchers := make([]common.Fetcher, 0, len(s.databaseFetchers)) @@ -162,8 +184,6 @@ func (s *Server) getAllDatabaseFetchers() []common.Fetcher { allFetchers = append(allFetchers, s.databaseFetchers...) - s.submitFetchersEvent(allFetchers) - return allFetchers } diff --git a/lib/srv/discovery/discovery.go b/lib/srv/discovery/discovery.go index ffb4a76353f59..28690130d51a7 100644 --- a/lib/srv/discovery/discovery.go +++ b/lib/srv/discovery/discovery.go @@ -115,6 +115,10 @@ type gcpInstaller interface { type Config struct { // CloudClients is an interface for retrieving cloud clients. CloudClients cloud.Clients + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider + // AWSDatabaseFetcherFactory provides AWS database fetchers + AWSDatabaseFetcherFactory *db.AWSFetcherFactory // GetEC2Client gets an AWS EC2 client for the given region. GetEC2Client server.EC2ClientGetter // GetSSMClient gets an AWS SSM client for the given region. @@ -219,6 +223,24 @@ kubernetes matchers are present.`) } c.CloudClients = cloudClients } + if c.AWSConfigProvider == nil { + provider, err := awsconfig.NewCache() + if err != nil { + return trace.Wrap(err, "unable to create AWS config provider cache") + } + c.AWSConfigProvider = provider + } + if c.AWSDatabaseFetcherFactory == nil { + factory, err := db.NewAWSFetcherFactory(db.AWSFetcherFactoryConfig{ + CloudClients: c.CloudClients, + AWSConfigProvider: c.AWSConfigProvider, + IntegrationCredentialProviderFn: c.getIntegrationCredentialProviderFn(), + }) + if err != nil { + return trace.Wrap(err) + } + c.AWSDatabaseFetcherFactory = factory + } if c.GetEC2Client == nil { c.GetEC2Client = func(ctx context.Context, region string, opts ...awsconfig.OptionsFn) (ec2.DescribeInstancesAPIClient, error) { cfg, err := c.getAWSConfig(ctx, region, opts...) @@ -290,7 +312,13 @@ kubernetes matchers are present.`) } func (c *Config) getAWSConfig(ctx context.Context, region string, opts ...awsconfig.OptionsFn) (aws.Config, error) { - opts = append(opts, awsconfig.WithIntegrationCredentialProvider(func(ctx context.Context, region, integrationName string) (aws.CredentialsProvider, error) { + opts = append(opts, awsconfig.WithIntegrationCredentialProvider(c.getIntegrationCredentialProviderFn())) + cfg, err := c.AWSConfigProvider.GetConfig(ctx, region, opts...) + return cfg, trace.Wrap(err) +} + +func (c *Config) getIntegrationCredentialProviderFn() awsconfig.IntegrationCredentialProviderFunc { + return func(ctx context.Context, region, integrationName string) (aws.CredentialsProvider, error) { integration, err := c.AccessPoint.GetIntegration(ctx, integrationName) if err != nil { return nil, trace.Wrap(err) @@ -308,9 +336,7 @@ func (c *Config) getAWSConfig(ctx context.Context, region string, opts ...awscon Region: region, }) return cred, trace.Wrap(err) - })) - cfg, err := awsconfig.GetConfig(ctx, region, opts...) - return cfg, trace.Wrap(err) + } } // Server is a discovery server, used to discover cloud resources for @@ -523,18 +549,7 @@ func (s *Server) initAWSWatchers(matchers []types.AWSMatcher) error { s.ctx, s.getAllAWSServerFetchers, s.caRotationCh, server.WithPollInterval(s.PollInterval), server.WithTriggerFetchC(s.newDiscoveryConfigChangedSub()), - server.WithPreFetchHookFn(func() { - discoveryConfigs := libslices.FilterMapUnique( - s.getAllAWSServerFetchers(), - func(f server.Fetcher) (s string, include bool) { - return f.GetDiscoveryConfigName(), f.GetDiscoveryConfigName() != "" - }, - ) - s.updateDiscoveryConfigStatus(discoveryConfigs...) - - s.awsEC2ResourcesStatus.reset() - s.awsEC2Tasks.reset() - }), + server.WithPreFetchHookFn(s.ec2WatcherIterationStarted), ) if err != nil { return trace.Wrap(err) @@ -575,6 +590,38 @@ func (s *Server) initAWSWatchers(matchers []types.AWSMatcher) error { return nil } +func (s *Server) ec2WatcherIterationStarted() { + allFetchers := s.getAllAWSServerFetchers() + if len(allFetchers) == 0 { + return + } + + s.submitFetchEvent(types.CloudAWS, types.AWSMatcherEC2) + + awsResultGroups := libslices.FilterMapUnique( + allFetchers, + func(f server.Fetcher) (awsResourceGroup, bool) { + include := f.GetDiscoveryConfigName() != "" && f.IntegrationName() != "" + resourceGroup := awsResourceGroup{ + discoveryConfigName: f.GetDiscoveryConfigName(), + integration: f.IntegrationName(), + } + return resourceGroup, include + }, + ) + for _, g := range awsResultGroups { + s.awsEC2ResourcesStatus.iterationStarted(g) + } + + discoveryConfigs := libslices.FilterMapUnique(awsResultGroups, func(g awsResourceGroup) (s string, include bool) { + return g.discoveryConfigName, true + }) + s.updateDiscoveryConfigStatus(discoveryConfigs...) + s.awsEC2ResourcesStatus.reset() + + s.awsEC2Tasks.reset() +} + func (s *Server) initKubeAppWatchers(matchers []types.KubernetesMatcher) error { if len(matchers) == 0 { return nil @@ -661,7 +708,7 @@ func (s *Server) databaseFetchersFromMatchers(matchers Matchers, discoveryConfig // AWS awsDatabaseMatchers, _ := splitMatchers(matchers.AWS, db.IsAWSMatcherType) if len(awsDatabaseMatchers) > 0 { - databaseFetchers, err := db.MakeAWSFetchers(s.ctx, s.CloudClients, awsDatabaseMatchers, discoveryConfigName) + databaseFetchers, err := s.AWSDatabaseFetcherFactory.MakeFetchers(s.ctx, awsDatabaseMatchers, discoveryConfigName) if err != nil { return nil, trace.Wrap(err) } @@ -1483,10 +1530,6 @@ func (s *Server) getAllAWSServerFetchers() []server.Fetcher { allFetchers = append(allFetchers, s.staticServerAWSFetchers...) - if len(allFetchers) > 0 { - s.submitFetchEvent(types.CloudAWS, types.AWSMatcherEC2) - } - return allFetchers } diff --git a/lib/srv/discovery/discovery_test.go b/lib/srv/discovery/discovery_test.go index 4bf7685e3cca5..f3c387a475932 100644 --- a/lib/srv/discovery/discovery_test.go +++ b/lib/srv/discovery/discovery_test.go @@ -37,8 +37,11 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v3" awsv2 "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/credentials" "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/redshift" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/aws/aws-sdk-go-v2/service/ssm" ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types" "github.com/aws/aws-sdk-go/aws" @@ -46,7 +49,6 @@ import ( "github.com/aws/aws-sdk-go/service/eks" "github.com/aws/aws-sdk-go/service/eks/eksiface" "github.com/aws/aws-sdk-go/service/rds" - "github.com/aws/aws-sdk-go/service/redshift" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" @@ -85,6 +87,7 @@ import ( "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/srv/discovery/common" + "github.com/gravitational/teleport/lib/srv/discovery/fetchers/db" "github.com/gravitational/teleport/lib/srv/server" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" libutils "github.com/gravitational/teleport/lib/utils" @@ -285,6 +288,26 @@ func TestDiscoveryServer(t *testing.T) { ) require.NoError(t, err) + dcForEC2StatusWithoutMatchName := uuid.NewString() + dcForEC2StatusWithoutMatch, err := discoveryconfig.NewDiscoveryConfig( + header.Metadata{Name: dcForEC2StatusWithoutMatchName}, + discoveryconfig.Spec{ + DiscoveryGroup: defaultDiscoveryGroup, + AWS: []types.AWSMatcher{{ + Types: []string{"ec2"}, + Regions: []string{"eu-central-1"}, + Tags: map[string]utils.Strings{"teleport": {"yes"}}, + SSM: &types.AWSSSM{DocumentName: "document"}, + Params: &types.InstallerParams{ + InstallTeleport: true, + EnrollMode: types.InstallParamEnrollMode_INSTALL_PARAM_ENROLL_MODE_SCRIPT, + }, + Integration: "my-integration", + }}, + }, + ) + require.NoError(t, err) + discoveryConfigForUserTaskEKSTestName := uuid.NewString() discoveryConfigForUserTaskEKSTest, err := discoveryconfig.NewDiscoveryConfig( header.Metadata{Name: discoveryConfigForUserTaskEKSTestName}, @@ -573,6 +596,31 @@ func TestDiscoveryServer(t *testing.T) { }, wantInstalledInstances: []string{"instance-id-1"}, }, + { + name: "no nodes found using DiscoveryConfig and Integration, but DiscoveryConfig Status is still updated", + presentInstances: []types.Server{}, + foundEC2Instances: []ec2types.Instance{}, + ssm: &mockSSMClient{}, + emitter: &mockEmitter{}, + staticMatchers: Matchers{}, + discoveryConfig: dcForEC2StatusWithoutMatch, + wantDiscoveryConfigStatus: &discoveryconfig.Status{ + State: "DISCOVERY_CONFIG_STATE_SYNCING", + ErrorMessage: nil, + DiscoveredResources: 0, + LastSyncTime: fakeClock.Now().UTC(), + IntegrationDiscoveredResources: map[string]*discoveryconfigv1.IntegrationDiscoveredSummary{ + "my-integration": { + AwsEc2: &discoveryconfigv1.ResourcesDiscoveredSummary{ + Found: 0, + Enrolled: 0, + Failed: 0, + }, + }, + }, + }, + wantInstalledInstances: []string{}, + }, { name: "one node found but SSM Run fails and DiscoverEC2 User Task is created", presentInstances: []types.Server{}, @@ -647,7 +695,7 @@ func TestDiscoveryServer(t *testing.T) { foundEC2Instances: []ec2types.Instance{}, ssm: &mockSSMClient{}, cloudClients: &cloud.TestCloudClients{ - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, EKS: &mocks.EKSMock{ Clusters: []*eks.Cluster{ { @@ -1396,7 +1444,7 @@ func TestDiscoveryInCloudKube(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - sts := &mocks.STSMock{} + sts := &mocks.STSClientV1{} testCloudClients := &cloud.TestCloudClients{ STS: sts, @@ -1542,7 +1590,7 @@ func TestDiscoveryServer_New(t *testing.T) { }{ { desc: "no matchers error", - cloudClients: &cloud.TestCloudClients{STS: &mocks.STSMock{}}, + cloudClients: &cloud.TestCloudClients{STS: &mocks.STSClientV1{}}, matchers: Matchers{}, errAssertion: func(t require.TestingT, err error, i ...interface{}) { require.ErrorIs(t, err, &trace.BadParameterError{Message: "no matchers or discovery group configured for discovery"}) @@ -1551,7 +1599,7 @@ func TestDiscoveryServer_New(t *testing.T) { }, { desc: "success with EKS matcher", - cloudClients: &cloud.TestCloudClients{STS: &mocks.STSMock{}, EKS: &mocks.EKSMock{}}, + cloudClients: &cloud.TestCloudClients{STS: &mocks.STSClientV1{}, EKS: &mocks.EKSMock{}}, matchers: Matchers{ AWS: []types.AWSMatcher{ { @@ -1576,7 +1624,7 @@ func TestDiscoveryServer_New(t *testing.T) { { desc: "EKS fetcher is skipped on initialization error (missing region)", cloudClients: &cloud.TestCloudClients{ - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, EKS: &mocks.EKSMock{}, }, matchers: Matchers{ @@ -1965,7 +2013,7 @@ func TestDiscoveryDatabase(t *testing.T) { } testCloudClients := &cloud.TestCloudClients{ - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, RDS: &mocks.RDSMock{ DBInstances: []*rds.DBInstance{awsRDSInstance}, DBEngineVersions: []*rds.DBEngineVersion{ @@ -1973,9 +2021,6 @@ func TestDiscoveryDatabase(t *testing.T) { }, }, MemoryDB: &mocks.MemoryDBMock{}, - Redshift: &mocks.RedshiftMock{ - Clusters: []*redshift.Cluster{awsRedshiftResource}, - }, AzureRedis: azure.NewRedisClientByAPI(&azure.ARMRedisMock{ Servers: []*armredis.ResourceInfo{azRedisResource}, }), @@ -1987,6 +2032,18 @@ func TestDiscoveryDatabase(t *testing.T) { Clusters: []*eks.Cluster{eksAWSResource}, }, } + fakeConfigProvider := &mocks.AWSConfigProvider{} + dbFetcherFactory, err := db.NewAWSFetcherFactory(db.AWSFetcherFactoryConfig{ + AWSConfigProvider: fakeConfigProvider, + CloudClients: testCloudClients, + IntegrationCredentialProviderFn: func(_ context.Context, _, _ string) (awsv2.CredentialsProvider, error) { + return credentials.NewStaticCredentialsProvider("key", "secret", "session"), nil + }, + RedshiftClientProviderFn: newFakeRedshiftClientProvider(&mocks.RedshiftClient{ + Clusters: []redshifttypes.Cluster{*awsRedshiftResource}, + }), + }) + require.NoError(t, err) tcs := []struct { name string @@ -2297,6 +2354,8 @@ func TestDiscoveryDatabase(t *testing.T) { &Config{ IntegrationOnlyCredentials: integrationOnlyCredential, CloudClients: testCloudClients, + AWSDatabaseFetcherFactory: dbFetcherFactory, + AWSConfigProvider: fakeConfigProvider, ClusterFeatures: func() proto.Features { return proto.Features{} }, KubernetesClient: fake.NewSimpleClientset(), AccessPoint: getDiscoveryAccessPoint(tlsServer.Auth(), authClient), @@ -2377,7 +2436,7 @@ func TestDiscoveryDatabaseRemovingDiscoveryConfigs(t *testing.T) { awsRDSInstance, awsRDSDB := makeRDSInstance(t, "aws-rds", "us-west-1", rewriteDiscoveryLabelsParams{discoveryConfigName: dc2Name, discoveryGroup: mainDiscoveryGroup}) testCloudClients := &cloud.TestCloudClients{ - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, RDS: &mocks.RDSMock{ DBInstances: []*rds.DBInstance{awsRDSInstance}, DBEngineVersions: []*rds.DBEngineVersion{ @@ -2561,15 +2620,15 @@ func makeRDSInstance(t *testing.T, name, region string, discoveryParams rewriteD return instance, database } -func makeRedshiftCluster(t *testing.T, name, region string, discoveryParams rewriteDiscoveryLabelsParams) (*redshift.Cluster, types.Database) { +func makeRedshiftCluster(t *testing.T, name, region string, discoveryParams rewriteDiscoveryLabelsParams) (*redshifttypes.Cluster, types.Database) { t.Helper() - cluster := &redshift.Cluster{ + cluster := &redshifttypes.Cluster{ ClusterIdentifier: aws.String(name), ClusterNamespaceArn: aws.String(fmt.Sprintf("arn:aws:redshift:%s:123456789012:namespace:%s", region, name)), ClusterStatus: aws.String("available"), - Endpoint: &redshift.Endpoint{ + Endpoint: &redshifttypes.Endpoint{ Address: aws.String("localhost"), - Port: aws.Int64(5439), + Port: aws.Int32(5439), }, } @@ -3619,3 +3678,9 @@ func newPopulatedGCPProjectsMock() *mockProjectsAPI { }, } } + +func newFakeRedshiftClientProvider(c redshift.DescribeClustersAPIClient) db.RedshiftClientProviderFunc { + return func(cfg awsv2.Config, optFns ...func(*redshift.Options)) db.RedshiftClient { + return c + } +} diff --git a/lib/srv/discovery/fetchers/aws-sync/s3.go b/lib/srv/discovery/fetchers/aws-sync/s3.go index db0eaf41d6caa..a0c81e9d55436 100644 --- a/lib/srv/discovery/fetchers/aws-sync/s3.go +++ b/lib/srv/discovery/fetchers/aws-sync/s3.go @@ -71,28 +71,13 @@ func (a *awsFetcher) fetchS3Buckets(ctx context.Context) ([]*accessgraphv1alpha. } } - region := awsutil.GetKnownRegions()[0] - if len(a.Regions) > 0 { - region = a.Regions[0] - } - - s3Client, err := a.CloudClients.GetAWSS3Client( - ctx, - region, - a.getAWSOptions()..., - ) - if err != nil { - return nil, trace.Wrap(err) - } - rsp, err := s3Client.ListBucketsWithContext( - ctx, - &s3.ListBucketsInput{}, - ) + buckets, getBucketRegion, err := a.listS3Buckets(ctx) if err != nil { return existing.S3Buckets, trace.Wrap(err) } - for _, bucket := range rsp.Buckets { + // Iterate over the buckets and fetch their inline and attached policies. + for _, bucket := range buckets { bucket := bucket eG.Go(func() error { var failedReqs failedRequests @@ -101,54 +86,24 @@ func (a *awsFetcher) fetchS3Buckets(ctx context.Context) ([]*accessgraphv1alpha. return b.Name == aws.ToString(bucket.Name) && b.AccountId == a.AccountID }, ) - policy, err := s3Client.GetBucketPolicyWithContext(ctx, &s3.GetBucketPolicyInput{ - Bucket: bucket.Name, - }) + bucketRegion, err := getBucketRegion(bucket.Name) if err != nil { errs = append(errs, - trace.Wrap(err, "failed to fetch bucket %q inline policy", aws.ToString(bucket.Name)), + trace.Wrap(err), ) failedReqs.policyFailed = true - } - - policyStatus, err := s3Client.GetBucketPolicyStatusWithContext(ctx, &s3.GetBucketPolicyStatusInput{ - Bucket: bucket.Name, - }) - if err != nil { - errs = append(errs, - trace.Wrap(err, "failed to fetch bucket %q policy status", aws.ToString(bucket.Name)), - ) failedReqs.failedPolicyStatus = true - } - - acls, err := s3Client.GetBucketAclWithContext(ctx, &s3.GetBucketAclInput{ - Bucket: bucket.Name, - }) - if err != nil { - errs = append(errs, - trace.Wrap(err, "failed to fetch bucket %q acls policies", aws.ToString(bucket.Name)), - ) failedReqs.failedAcls = true - } - - tagsOutput, err := s3Client.GetBucketTaggingWithContext(ctx, &s3.GetBucketTaggingInput{ - Bucket: bucket.Name, - }) - var awsErr awserr.Error - const noSuchTagSet = "NoSuchTagSet" // error code when there are no tags or the bucket does not support them - if errors.As(err, &awsErr) && awsErr.Code() == noSuchTagSet { - // If there are no tags, set the error to nil. - err = nil - } - if err != nil { - errs = append(errs, - trace.Wrap(err, "failed to fetch bucket %q tags", aws.ToString(bucket.Name)), - ) failedReqs.failedTags = true + newBucket := awsS3Bucket(aws.ToString(bucket.Name), nil, nil, nil, nil, a.AccountID) + collect(mergeS3Protos(existingBucket, newBucket, failedReqs), trace.NewAggregate(errs...)) + return nil } - newBucket := awsS3Bucket(aws.ToString(bucket.Name), policy, policyStatus, acls, tagsOutput, a.AccountID) - collect(mergeS3Protos(existingBucket, newBucket, failedReqs), trace.NewAggregate(errs...)) + details, failedReqs, errsL := a.getS3BucketDetails(ctx, bucket, bucketRegion) + + newBucket := awsS3Bucket(aws.ToString(bucket.Name), details.policy, details.policyStatus, details.acls, details.tags, a.AccountID) + collect(mergeS3Protos(existingBucket, newBucket, failedReqs), trace.NewAggregate(append(errs, errsL...)...)) return nil }) } @@ -212,6 +167,7 @@ type failedRequests struct { failedPolicyStatus bool failedAcls bool failedTags bool + headFailed bool } func mergeS3Protos(existing, new *accessgraphv1alpha.AWSS3BucketV1, failedReqs failedRequests) *accessgraphv1alpha.AWSS3BucketV1 { @@ -237,3 +193,124 @@ func mergeS3Protos(existing, new *accessgraphv1alpha.AWSS3BucketV1, failedReqs f return clone } + +type s3Details struct { + policy *s3.GetBucketPolicyOutput + policyStatus *s3.GetBucketPolicyStatusOutput + acls *s3.GetBucketAclOutput + tags *s3.GetBucketTaggingOutput +} + +func (a *awsFetcher) getS3BucketDetails(ctx context.Context, bucket *s3.Bucket, bucketRegion string) (s3Details, failedRequests, []error) { + var failedReqs failedRequests + var errs []error + var details s3Details + + s3Client, err := a.CloudClients.GetAWSS3Client( + ctx, + bucketRegion, + a.getAWSOptions()..., + ) + if err != nil { + errs = append(errs, + trace.Wrap(err, "failed to create s3 client for bucket %q", aws.ToString(bucket.Name)), + ) + return s3Details{}, + failedRequests{ + headFailed: true, + policyFailed: true, + failedPolicyStatus: true, + failedAcls: true, + failedTags: true, + }, errs + } + + details.policy, err = s3Client.GetBucketPolicyWithContext(ctx, &s3.GetBucketPolicyInput{ + Bucket: bucket.Name, + }) + if err != nil && !isS3BucketPolicyNotFound(err) { + errs = append(errs, + trace.Wrap(err, "failed to fetch bucket %q inline policy", aws.ToString(bucket.Name)), + ) + failedReqs.policyFailed = true + } + + details.policyStatus, err = s3Client.GetBucketPolicyStatusWithContext(ctx, &s3.GetBucketPolicyStatusInput{ + Bucket: bucket.Name, + }) + if err != nil && !isS3BucketPolicyNotFound(err) { + errs = append(errs, + trace.Wrap(err, "failed to fetch bucket %q policy status", aws.ToString(bucket.Name)), + ) + failedReqs.failedPolicyStatus = true + } + + details.acls, err = s3Client.GetBucketAclWithContext(ctx, &s3.GetBucketAclInput{ + Bucket: bucket.Name, + }) + if err != nil { + errs = append(errs, + trace.Wrap(err, "failed to fetch bucket %q acls policies", aws.ToString(bucket.Name)), + ) + failedReqs.failedAcls = true + } + + details.tags, err = s3Client.GetBucketTaggingWithContext(ctx, &s3.GetBucketTaggingInput{ + Bucket: bucket.Name, + }) + if err != nil && !isS3BucketNoTagSet(err) { + errs = append(errs, + trace.Wrap(err, "failed to fetch bucket %q tags", aws.ToString(bucket.Name)), + ) + failedReqs.failedTags = true + } + + return details, failedReqs, errs +} + +func isS3BucketPolicyNotFound(err error) bool { + var awsErr awserr.Error + return errors.As(err, &awsErr) && awsErr.Code() == "NoSuchBucketPolicy" +} + +func isS3BucketNoTagSet(err error) bool { + var awsErr awserr.Error + return errors.As(err, &awsErr) && awsErr.Code() == "NoSuchTagSet" +} + +func (a *awsFetcher) listS3Buckets(ctx context.Context) ([]*s3.Bucket, func(*string) (string, error), error) { + region := awsutil.GetKnownRegions()[0] + if len(a.Regions) > 0 { + region = a.Regions[0] + } + + // use any region to list buckets + s3Client, err := a.CloudClients.GetAWSS3Client( + ctx, + region, + a.getAWSOptions()..., + ) + if err != nil { + return nil, nil, trace.Wrap(err) + } + rsp, err := s3Client.ListBucketsWithContext(ctx, &s3.ListBucketsInput{}) + if err != nil { + return nil, nil, trace.Wrap(err) + } + return rsp.Buckets, + func(bucket *string) (string, error) { + rsp, err := s3Client.GetBucketLocationWithContext( + ctx, + &s3.GetBucketLocationInput{ + Bucket: bucket, + }, + ) + if err != nil { + return "", trace.Wrap(err, "failed to fetch bucket %q region", aws.ToString(bucket)) + } + if rsp.LocationConstraint == nil { + return "us-east-1", nil + } + return aws.ToString(rsp.LocationConstraint), nil + }, nil +} diff --git a/lib/srv/discovery/fetchers/aws-sync/s3_test.go b/lib/srv/discovery/fetchers/aws-sync/s3_test.go index d85a52cd90920..a10ad3bb6985d 100644 --- a/lib/srv/discovery/fetchers/aws-sync/s3_test.go +++ b/lib/srv/discovery/fetchers/aws-sync/s3_test.go @@ -56,6 +56,11 @@ func TestPollAWSS3(t *testing.T) { mockedClients = &cloud.TestCloudClients{ S3: &mocks.S3Mock{ Buckets: s3Buckets(bucketName, otherBucketName, missingBucket), + BucketLocations: map[string]string{ + bucketName: "eu-west-1", + otherBucketName: "eu-west-1", + missingBucket: "eu-west-1", + }, BucketPolicy: map[string]string{ bucketName: "policy", otherBucketName: "otherPolicy", diff --git a/lib/srv/discovery/fetchers/db/aws.go b/lib/srv/discovery/fetchers/db/aws.go index 9ccf26f82b397..f87e0e9a6c443 100644 --- a/lib/srv/discovery/fetchers/db/aws.go +++ b/lib/srv/discovery/fetchers/db/aws.go @@ -23,14 +23,21 @@ import ( "fmt" "log/slog" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" "github.com/gravitational/trace" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/srv/discovery/common" ) +// maxAWSPages is the maximum number of pages to iterate over when fetching aws +// databases. +const maxAWSPages = 10 + // awsFetcherPlugin defines an interface that provides database type specific // functions for use by the common AWS database fetcher. type awsFetcherPlugin interface { @@ -46,6 +53,11 @@ type awsFetcherPlugin interface { type awsFetcherConfig struct { // AWSClients are the AWS API clients. AWSClients cloud.AWSClients + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider + // IntegrationCredentialProviderFn is a required function that provides + // credentials via AWS OIDC integration. + IntegrationCredentialProviderFn awsconfig.IntegrationCredentialProviderFunc // Type is the type of DB matcher, for example "rds", "redshift", etc. Type string // AssumeRole provides a role ARN and ExternalID to assume an AWS role @@ -64,6 +76,9 @@ type awsFetcherConfig struct { // Might be empty when the fetcher is using static matchers: // ie teleport.yaml/discovery_service.. DiscoveryConfigName string + + // redshiftClientProviderFn provides an AWS Redshift client. + redshiftClientProviderFn RedshiftClientProviderFunc } // CheckAndSetDefaults validates the config and sets defaults. @@ -71,6 +86,9 @@ func (cfg *awsFetcherConfig) CheckAndSetDefaults(component string) error { if cfg.AWSClients == nil { return trace.BadParameter("missing parameter AWSClients") } + if cfg.AWSConfigProvider == nil { + return trace.BadParameter("missing AWSConfigProvider") + } if cfg.Type == "" { return trace.BadParameter("missing parameter Type") } @@ -93,6 +111,12 @@ func (cfg *awsFetcherConfig) CheckAndSetDefaults(component string) error { "credentials", credentialsSource, ) } + + if cfg.redshiftClientProviderFn == nil { + cfg.redshiftClientProviderFn = func(cfg aws.Config, optFns ...func(*redshift.Options)) RedshiftClient { + return redshift.NewFromConfig(cfg, optFns...) + } + } return nil } @@ -179,7 +203,3 @@ func (f *awsFetcher) String() string { return fmt.Sprintf("awsFetcher(Type: %v, Region=%v, Labels=%v)", f.cfg.Type, f.cfg.Region, f.cfg.Labels) } - -// maxAWSPages is the maximum number of pages to iterate over when fetching aws -// databases. -const maxAWSPages = 10 diff --git a/lib/srv/discovery/fetchers/db/aws_redshift.go b/lib/srv/discovery/fetchers/db/aws_redshift.go index 7b4b1bfb35315..508cb6e8810f1 100644 --- a/lib/srv/discovery/fetchers/db/aws_redshift.go +++ b/lib/srv/discovery/fetchers/db/aws_redshift.go @@ -21,17 +21,25 @@ package db import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/redshift" - "github.com/aws/aws-sdk-go/service/redshift/redshiftiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/gravitational/trace" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/cloud" libcloudaws "github.com/gravitational/teleport/lib/cloud/aws" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/srv/discovery/common" ) +// RedshiftClientProviderFunc provides a [RedshiftClient]. +type RedshiftClientProviderFunc func(cfg aws.Config, optFns ...func(*redshift.Options)) RedshiftClient + +// RedshiftClient is a subset of the AWS Redshift API. +type RedshiftClient interface { + redshift.DescribeClustersAPIClient +} + // newRedshiftFetcher returns a new AWS fetcher for Redshift databases. func newRedshiftFetcher(cfg awsFetcherConfig) (common.Fetcher, error) { return newAWSFetcher(cfg, &redshiftPlugin{}) @@ -42,32 +50,33 @@ type redshiftPlugin struct{} // GetDatabases returns Redshift databases matching the watcher's selectors. func (f *redshiftPlugin) GetDatabases(ctx context.Context, cfg *awsFetcherConfig) (types.Databases, error) { - redshiftClient, err := cfg.AWSClients.GetAWSRedshiftClient(ctx, cfg.Region, - cloud.WithAssumeRole(cfg.AssumeRole.RoleARN, cfg.AssumeRole.ExternalID), - cloud.WithCredentialsMaybeIntegration(cfg.Integration), + awsCfg, err := cfg.AWSConfigProvider.GetConfig(ctx, cfg.Region, + awsconfig.WithAssumeRole(cfg.AssumeRole.RoleARN, cfg.AssumeRole.ExternalID), + awsconfig.WithCredentialsMaybeIntegration(cfg.Integration), + awsconfig.WithIntegrationCredentialProvider(cfg.IntegrationCredentialProviderFn), ) if err != nil { return nil, trace.Wrap(err) } - clusters, err := getRedshiftClusters(ctx, redshiftClient) + clusters, err := getRedshiftClusters(ctx, cfg.redshiftClientProviderFn(awsCfg)) if err != nil { return nil, trace.Wrap(err) } var databases types.Databases for _, cluster := range clusters { - if !libcloudaws.IsRedshiftClusterAvailable(cluster) { + if !libcloudaws.IsRedshiftClusterAvailable(&cluster) { cfg.Logger.DebugContext(ctx, "Skipping unavailable Redshift cluster", - "cluster", aws.StringValue(cluster.ClusterIdentifier), - "status", aws.StringValue(cluster.ClusterStatus), + "cluster", aws.ToString(cluster.ClusterIdentifier), + "status", aws.ToString(cluster.ClusterStatus), ) continue } - database, err := common.NewDatabaseFromRedshiftCluster(cluster) + database, err := common.NewDatabaseFromRedshiftCluster(&cluster) if err != nil { cfg.Logger.InfoContext(ctx, "Could not convert Redshift cluster to database resource", - "cluster", aws.StringValue(cluster.ClusterIdentifier), + "cluster", aws.ToString(cluster.ClusterIdentifier), "error", err, ) continue @@ -84,17 +93,20 @@ func (f *redshiftPlugin) ComponentShortName() string { // getRedshiftClusters fetches all Reshift clusters using the provided client, // up to the specified max number of pages -func getRedshiftClusters(ctx context.Context, redshiftClient redshiftiface.RedshiftAPI) ([]*redshift.Cluster, error) { - var clusters []*redshift.Cluster - var pageNum int - err := redshiftClient.DescribeClustersPagesWithContext( - ctx, +func getRedshiftClusters(ctx context.Context, clt redshift.DescribeClustersAPIClient) ([]redshifttypes.Cluster, error) { + pager := redshift.NewDescribeClustersPaginator(clt, &redshift.DescribeClustersInput{}, - func(page *redshift.DescribeClustersOutput, lastPage bool) bool { - pageNum++ - clusters = append(clusters, page.Clusters...) - return pageNum <= maxAWSPages + func(dcpo *redshift.DescribeClustersPaginatorOptions) { + dcpo.StopOnDuplicateToken = true }, ) - return clusters, trace.Wrap(libcloudaws.ConvertRequestFailureError(err)) + var clusters []redshifttypes.Cluster + for pageNum := 0; pageNum < maxAWSPages && pager.HasMorePages(); pageNum++ { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, libcloudaws.ConvertRequestFailureErrorV2(err) + } + clusters = append(clusters, page.Clusters...) + } + return clusters, nil } diff --git a/lib/srv/discovery/fetchers/db/aws_redshift_test.go b/lib/srv/discovery/fetchers/db/aws_redshift_test.go index 76fb7898db578..ded47035e96e3 100644 --- a/lib/srv/discovery/fetchers/db/aws_redshift_test.go +++ b/lib/srv/discovery/fetchers/db/aws_redshift_test.go @@ -21,16 +21,21 @@ package db import ( "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/redshift" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" + redshifttypes "github.com/aws/aws-sdk-go-v2/service/redshift/types" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/cloud" "github.com/gravitational/teleport/lib/cloud/mocks" "github.com/gravitational/teleport/lib/srv/discovery/common" ) +func newFakeRedshiftClientProvider(c RedshiftClient) RedshiftClientProviderFunc { + return func(cfg aws.Config, optFns ...func(*redshift.Options)) RedshiftClient { + return c + } +} func TestRedshiftFetcher(t *testing.T) { t.Parallel() @@ -42,30 +47,30 @@ func TestRedshiftFetcher(t *testing.T) { tests := []awsFetcherTest{ { name: "fetch all", - inputClients: &cloud.TestCloudClients{ - Redshift: &mocks.RedshiftMock{ - Clusters: []*redshift.Cluster{redshiftUse1Prod, redshiftUse1Dev}, - }, + fetcherCfg: AWSFetcherFactoryConfig{ + RedshiftClientProviderFn: newFakeRedshiftClientProvider(&mocks.RedshiftClient{ + Clusters: []redshifttypes.Cluster{*redshiftUse1Prod, *redshiftUse1Dev}, + }), }, inputMatchers: makeAWSMatchersForType(types.AWSMatcherRedshift, "us-east-1", wildcardLabels), wantDatabases: types.Databases{redshiftDatabaseUse1Prod, redshiftDatabaseUse1Dev}, }, { name: "fetch prod", - inputClients: &cloud.TestCloudClients{ - Redshift: &mocks.RedshiftMock{ - Clusters: []*redshift.Cluster{redshiftUse1Prod, redshiftUse1Dev}, - }, + fetcherCfg: AWSFetcherFactoryConfig{ + RedshiftClientProviderFn: newFakeRedshiftClientProvider(&mocks.RedshiftClient{ + Clusters: []redshifttypes.Cluster{*redshiftUse1Prod, *redshiftUse1Dev}, + }), }, inputMatchers: makeAWSMatchersForType(types.AWSMatcherRedshift, "us-east-1", envProdLabels), wantDatabases: types.Databases{redshiftDatabaseUse1Prod}, }, { name: "skip unavailable", - inputClients: &cloud.TestCloudClients{ - Redshift: &mocks.RedshiftMock{ - Clusters: []*redshift.Cluster{redshiftUse1Prod, redshiftUse1Unavailable, redshiftUse1UnknownStatus}, - }, + fetcherCfg: AWSFetcherFactoryConfig{ + RedshiftClientProviderFn: newFakeRedshiftClientProvider(&mocks.RedshiftClient{ + Clusters: []redshifttypes.Cluster{*redshiftUse1Prod, *redshiftUse1Unavailable, *redshiftUse1UnknownStatus}, + }), }, inputMatchers: makeAWSMatchersForType(types.AWSMatcherRedshift, "us-east-1", wildcardLabels), wantDatabases: types.Databases{redshiftDatabaseUse1Prod, redshiftDatabaseUnknownStatus}, @@ -74,18 +79,18 @@ func TestRedshiftFetcher(t *testing.T) { testAWSFetchers(t, tests...) } -func makeRedshiftCluster(t *testing.T, region, env string, opts ...func(*redshift.Cluster)) (*redshift.Cluster, types.Database) { +func makeRedshiftCluster(t *testing.T, region, env string, opts ...func(*redshifttypes.Cluster)) (*redshifttypes.Cluster, types.Database) { cluster := mocks.RedshiftCluster(env, region, map[string]string{"env": env}, opts...) - database, err := common.NewDatabaseFromRedshiftCluster(cluster) + database, err := common.NewDatabaseFromRedshiftCluster(&cluster) require.NoError(t, err) common.ApplyAWSDatabaseNameSuffix(database, types.AWSMatcherRedshift) - return cluster, database + return &cluster, database } // withRedshiftStatus returns an option function for makeRedshiftCluster to overwrite status. -func withRedshiftStatus(status string) func(*redshift.Cluster) { - return func(cluster *redshift.Cluster) { +func withRedshiftStatus(status string) func(*redshifttypes.Cluster) { + return func(cluster *redshifttypes.Cluster) { cluster.ClusterStatus = aws.String(status) } } diff --git a/lib/srv/discovery/fetchers/db/db.go b/lib/srv/discovery/fetchers/db/db.go index c3c3cbeec6dbe..3ef56532d90af 100644 --- a/lib/srv/discovery/fetchers/db/db.go +++ b/lib/srv/discovery/fetchers/db/db.go @@ -22,11 +22,14 @@ import ( "context" "log/slog" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/redshift" "github.com/gravitational/trace" "golang.org/x/exp/maps" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/awsconfig" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/srv/discovery/common" ) @@ -64,8 +67,53 @@ func IsAzureMatcherType(matcherType string) bool { return len(makeAzureFetcherFuncs[matcherType]) > 0 } -// MakeAWSFetchers creates new AWS database fetchers. -func MakeAWSFetchers(ctx context.Context, clients cloud.AWSClients, matchers []types.AWSMatcher, discoveryConfigName string) (result []common.Fetcher, err error) { +// AWSFetcherFactoryConfig is the configuration for an [AWSFetcherFactory]. +type AWSFetcherFactoryConfig struct { + // AWSConfigProvider provides [aws.Config] for AWS SDK service clients. + AWSConfigProvider awsconfig.Provider + // CloudClients is an interface for retrieving AWS SDK v1 cloud clients. + CloudClients cloud.AWSClients + // IntegrationCredentialProviderFn is an optional function that provides + // credentials via AWS OIDC integration. + IntegrationCredentialProviderFn awsconfig.IntegrationCredentialProviderFunc + // RedshiftClientProviderFn is an optional function that provides + RedshiftClientProviderFn RedshiftClientProviderFunc +} + +func (c *AWSFetcherFactoryConfig) checkAndSetDefaults() error { + if c.CloudClients == nil { + return trace.BadParameter("missing CloudClients") + } + if c.AWSConfigProvider == nil { + return trace.BadParameter("missing AWSConfigProvider") + } + if c.RedshiftClientProviderFn == nil { + c.RedshiftClientProviderFn = func(cfg aws.Config, optFns ...func(*redshift.Options)) RedshiftClient { + return redshift.NewFromConfig(cfg, optFns...) + } + } + return nil +} + +// AWSFetcherFactory makes AWS database fetchers. +type AWSFetcherFactory struct { + cfg AWSFetcherFactoryConfig +} + +// NewAWSFetcherFactory checks the given config and returns a new fetcher +// provider. +func NewAWSFetcherFactory(cfg AWSFetcherFactoryConfig) (*AWSFetcherFactory, error) { + if err := cfg.checkAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + return &AWSFetcherFactory{ + cfg: cfg, + }, nil +} + +// MakeFetchers returns AWS database fetchers for each matcher given. +func (f *AWSFetcherFactory) MakeFetchers(ctx context.Context, matchers []types.AWSMatcher, discoveryConfigName string) ([]common.Fetcher, error) { + var result []common.Fetcher for _, matcher := range matchers { assumeRole := types.AssumeRole{} if matcher.AssumeRole != nil { @@ -80,13 +128,16 @@ func MakeAWSFetchers(ctx context.Context, clients cloud.AWSClients, matchers []t for _, makeFetcher := range makeFetchers { for _, region := range matcher.Regions { fetcher, err := makeFetcher(awsFetcherConfig{ - AWSClients: clients, - Type: matcherType, - AssumeRole: assumeRole, - Labels: matcher.Tags, - Region: region, - Integration: matcher.Integration, - DiscoveryConfigName: discoveryConfigName, + AWSClients: f.cfg.CloudClients, + Type: matcherType, + AssumeRole: assumeRole, + Labels: matcher.Tags, + Region: region, + Integration: matcher.Integration, + DiscoveryConfigName: discoveryConfigName, + AWSConfigProvider: f.cfg.AWSConfigProvider, + IntegrationCredentialProviderFn: f.cfg.IntegrationCredentialProviderFn, + redshiftClientProviderFn: f.cfg.RedshiftClientProviderFn, }) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/srv/discovery/fetchers/db/helpers_test.go b/lib/srv/discovery/fetchers/db/helpers_test.go index 6063198b71e6d..5feae42c7b367 100644 --- a/lib/srv/discovery/fetchers/db/helpers_test.go +++ b/lib/srv/discovery/fetchers/db/helpers_test.go @@ -53,10 +53,12 @@ func makeAWSMatchersForType(matcherType, region string, tags map[string]string) }} } -func mustMakeAWSFetchers(t *testing.T, clients cloud.AWSClients, matchers []types.AWSMatcher, discoveryConfigName string) []common.Fetcher { +func mustMakeAWSFetchers(t *testing.T, cfg AWSFetcherFactoryConfig, matchers []types.AWSMatcher, discoveryConfigName string) []common.Fetcher { t.Helper() - fetchers, err := MakeAWSFetchers(context.Background(), clients, matchers, discoveryConfigName) + fetcherFactory, err := NewAWSFetcherFactory(cfg) + require.NoError(t, err) + fetchers, err := fetcherFactory.MakeFetchers(context.Background(), matchers, discoveryConfigName) require.NoError(t, err) require.NotEmpty(t, fetchers) @@ -111,6 +113,7 @@ var testAssumeRole = types.AssumeRole{ type awsFetcherTest struct { name string inputClients *cloud.TestCloudClients + fetcherCfg AWSFetcherFactoryConfig inputMatchers []types.AWSMatcher wantDatabases types.Databases } @@ -121,22 +124,30 @@ func testAWSFetchers(t *testing.T, tests ...awsFetcherTest) { t.Helper() for _, test := range tests { test := test - require.Nil(t, test.inputClients.STS, "testAWSFetchers injects an STS mock itself, but test input had already configured it. This is a test configuration error.") - stsMock := &mocks.STSMock{} - test.inputClients.STS = stsMock + fakeSTS := &mocks.STSClient{} + if test.inputClients != nil { + require.Nil(t, test.inputClients.STS, "testAWSFetchers injects an STS mock itself, but test input had already configured it. This is a test configuration error.") + test.inputClients.STS = &fakeSTS.STSClientV1 + } + test.fetcherCfg.CloudClients = test.inputClients + require.Nil(t, test.fetcherCfg.AWSConfigProvider, "testAWSFetchers injects a fake AWSConfigProvider, but the test input had already configured it. This is a test configuration error.") + test.fetcherCfg.AWSConfigProvider = &mocks.AWSConfigProvider{ + STSClient: fakeSTS, + } t.Run(test.name, func(t *testing.T) { t.Helper() - fetchers := mustMakeAWSFetchers(t, test.inputClients, test.inputMatchers, "" /* discovery config */) + fetchers := mustMakeAWSFetchers(t, test.fetcherCfg, test.inputMatchers, "" /* discovery config */) require.ElementsMatch(t, test.wantDatabases, mustGetDatabases(t, fetchers)) }) t.Run(test.name+" with assume role", func(t *testing.T) { t.Helper() + fakeSTS.ResetAssumeRoleHistory() matchers := copyAWSMatchersWithAssumeRole(testAssumeRole, test.inputMatchers...) wantDBs := copyDatabasesWithAWSAssumeRole(testAssumeRole, test.wantDatabases...) - fetchers := mustMakeAWSFetchers(t, test.inputClients, matchers, "" /* discovery config */) + fetchers := mustMakeAWSFetchers(t, test.fetcherCfg, matchers, "" /* discovery config */) require.ElementsMatch(t, wantDBs, mustGetDatabases(t, fetchers)) - require.Equal(t, []string{testAssumeRole.RoleARN}, stsMock.GetAssumedRoleARNs()) - require.Equal(t, []string{testAssumeRole.ExternalID}, stsMock.GetAssumedRoleExternalIDs()) + require.Equal(t, []string{testAssumeRole.RoleARN}, fakeSTS.GetAssumedRoleARNs()) + require.Equal(t, []string{testAssumeRole.ExternalID}, fakeSTS.GetAssumedRoleExternalIDs()) }) } } diff --git a/lib/srv/discovery/kube_integration_watcher.go b/lib/srv/discovery/kube_integration_watcher.go index 859dd13a11949..ffbecf6497359 100644 --- a/lib/srv/discovery/kube_integration_watcher.go +++ b/lib/srv/discovery/kube_integration_watcher.go @@ -76,18 +76,7 @@ func (s *Server) startKubeIntegrationWatchers() error { Interval: s.PollInterval, Origin: types.OriginCloud, TriggerFetchC: s.newDiscoveryConfigChangedSub(), - PreFetchHookFn: func() { - discoveryConfigs := libslices.FilterMapUnique( - s.getKubeIntegrationFetchers(), - func(f common.Fetcher) (s string, include bool) { - return f.GetDiscoveryConfigName(), f.GetDiscoveryConfigName() != "" - }, - ) - s.updateDiscoveryConfigStatus(discoveryConfigs...) - - s.awsEKSResourcesStatus.reset() - s.awsEKSTasks.reset() - }, + PreFetchHookFn: s.kubernetesIntegrationWatcherIterationStarted, }) if err != nil { return trace.Wrap(err) @@ -194,6 +183,38 @@ func (s *Server) startKubeIntegrationWatchers() error { return nil } +func (s *Server) kubernetesIntegrationWatcherIterationStarted() { + allFetchers := s.getKubeIntegrationFetchers() + if len(allFetchers) == 0 { + return + } + + s.submitFetchersEvent(allFetchers) + + awsResultGroups := libslices.FilterMapUnique( + allFetchers, + func(f common.Fetcher) (awsResourceGroup, bool) { + include := f.GetDiscoveryConfigName() != "" && f.IntegrationName() != "" + resourceGroup := awsResourceGroup{ + discoveryConfigName: f.GetDiscoveryConfigName(), + integration: f.IntegrationName(), + } + return resourceGroup, include + }, + ) + for _, g := range awsResultGroups { + s.awsEKSResourcesStatus.iterationStarted(g) + } + + discoveryConfigs := libslices.FilterMapUnique(awsResultGroups, func(g awsResourceGroup) (s string, include bool) { + return g.discoveryConfigName, true + }) + s.updateDiscoveryConfigStatus(discoveryConfigs...) + + s.awsEKSResourcesStatus.reset() + s.awsEKSTasks.reset() +} + func (s *Server) enrollEKSClusters(region, integration, discoveryConfigName string, clusters []types.DiscoveredEKSCluster, agentVersion string, mu *sync.Mutex, enrollingClusters map[string]bool) { mu.Lock() for _, c := range clusters { diff --git a/lib/srv/discovery/kube_integration_watcher_test.go b/lib/srv/discovery/kube_integration_watcher_test.go index bde45665b2a28..423339678ae8d 100644 --- a/lib/srv/discovery/kube_integration_watcher_test.go +++ b/lib/srv/discovery/kube_integration_watcher_test.go @@ -56,19 +56,19 @@ import ( func TestServer_getKubeFetchers(t *testing.T) { eks1, err := fetchers.NewEKSFetcher(fetchers.EKSFetcherConfig{ - ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSMock{}}, + ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSClientV1{}}, FilterLabels: types.Labels{"l1": []string{"v1"}}, Region: "region1", }) require.NoError(t, err) eks2, err := fetchers.NewEKSFetcher(fetchers.EKSFetcherConfig{ - ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSMock{}}, + ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSClientV1{}}, FilterLabels: types.Labels{"l1": []string{"v1"}}, Region: "region1", Integration: "aws1"}) require.NoError(t, err) eks3, err := fetchers.NewEKSFetcher(fetchers.EKSFetcherConfig{ - ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSMock{}}, + ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSClientV1{}}, FilterLabels: types.Labels{"l1": []string{"v1"}}, Region: "region1", Integration: "aws1"}) @@ -314,7 +314,7 @@ func TestDiscoveryKubeIntegrationEKS(t *testing.T) { t.Parallel() testCloudClients := &cloud.TestCloudClients{ - STS: &mocks.STSMock{}, + STS: &mocks.STSClientV1{}, EKS: &mockEKSAPI{ clusters: eksMockClusters[:2], }, diff --git a/lib/srv/discovery/status.go b/lib/srv/discovery/status.go index 2d168c5aea776..2647ce047b07b 100644 --- a/lib/srv/discovery/status.go +++ b/lib/srv/discovery/status.go @@ -296,6 +296,15 @@ func (ars *awsResourcesStatus) incrementFailed(g awsResourceGroup, count int) { ars.awsResourcesResults[g] = groupStats } +func (ars *awsResourcesStatus) iterationStarted(g awsResourceGroup) { + ars.mu.Lock() + defer ars.mu.Unlock() + if ars.awsResourcesResults == nil { + ars.awsResourcesResults = make(map[awsResourceGroup]awsResourceGroupResult) + } + ars.awsResourcesResults[g] = awsResourceGroupResult{} +} + func (ars *awsResourcesStatus) incrementFound(g awsResourceGroup, count int) { ars.mu.Lock() defer ars.mu.Unlock() diff --git a/lib/srv/server/azure_watcher.go b/lib/srv/server/azure_watcher.go index fda04125ae7e1..fb1110247dc0f 100644 --- a/lib/srv/server/azure_watcher.go +++ b/lib/srv/server/azure_watcher.go @@ -121,6 +121,7 @@ type azureFetcherConfig struct { ResourceGroup string AzureClientGetter azureClientGetter DiscoveryConfigName string + Integration string } type azureInstanceFetcher struct { @@ -132,6 +133,7 @@ type azureInstanceFetcher struct { Parameters map[string]string ClientID string DiscoveryConfigName string + Integration string } func newAzureInstanceFetcher(cfg azureFetcherConfig) *azureInstanceFetcher { @@ -142,6 +144,7 @@ func newAzureInstanceFetcher(cfg azureFetcherConfig) *azureInstanceFetcher { ResourceGroup: cfg.ResourceGroup, Labels: cfg.Matcher.ResourceTags, DiscoveryConfigName: cfg.DiscoveryConfigName, + Integration: cfg.Integration, } if cfg.Matcher.Params != nil { @@ -164,6 +167,12 @@ func (f *azureInstanceFetcher) GetDiscoveryConfigName() string { return f.DiscoveryConfigName } +// IntegrationName identifies the integration name whose credentials were used to fetch the resources. +// Might be empty when the fetcher is using ambient credentials. +func (f *azureInstanceFetcher) IntegrationName() string { + return f.Integration +} + // GetInstances fetches all Azure virtual machines matching configured filters. func (f *azureInstanceFetcher) GetInstances(ctx context.Context, _ bool) ([]Instances, error) { client, err := f.AzureClientGetter.GetAzureVirtualMachinesClient(f.Subscription) diff --git a/lib/srv/server/ec2_watcher.go b/lib/srv/server/ec2_watcher.go index cf3bb13a62367..1f81fb3d6952a 100644 --- a/lib/srv/server/ec2_watcher.go +++ b/lib/srv/server/ec2_watcher.go @@ -461,3 +461,9 @@ func (f *ec2InstanceFetcher) GetInstances(ctx context.Context, rotation bool) ([ func (f *ec2InstanceFetcher) GetDiscoveryConfigName() string { return f.DiscoveryConfigName } + +// IntegrationName identifies the integration name whose credentials were used to fetch the resources. +// Might be empty when the fetcher is using ambient credentials. +func (f *ec2InstanceFetcher) IntegrationName() string { + return f.Integration +} diff --git a/lib/srv/server/gcp_watcher.go b/lib/srv/server/gcp_watcher.go index 4b3ddca5ebb98..e3cf33c591d49 100644 --- a/lib/srv/server/gcp_watcher.go +++ b/lib/srv/server/gcp_watcher.go @@ -111,6 +111,7 @@ type gcpFetcherConfig struct { GCPClient gcp.InstancesClient projectsClient gcp.ProjectsClient DiscoveryConfigName string + Integration string } type gcpInstanceFetcher struct { @@ -123,16 +124,19 @@ type gcpInstanceFetcher struct { Parameters map[string]string projectsClient gcp.ProjectsClient DiscoveryConfigName string + Integration string } func newGCPInstanceFetcher(cfg gcpFetcherConfig) *gcpInstanceFetcher { fetcher := &gcpInstanceFetcher{ - GCP: cfg.GCPClient, - Zones: cfg.Matcher.Locations, - ProjectIDs: cfg.Matcher.ProjectIDs, - ServiceAccounts: cfg.Matcher.ServiceAccounts, - Labels: cfg.Matcher.GetLabels(), - projectsClient: cfg.projectsClient, + GCP: cfg.GCPClient, + Zones: cfg.Matcher.Locations, + ProjectIDs: cfg.Matcher.ProjectIDs, + ServiceAccounts: cfg.Matcher.ServiceAccounts, + Labels: cfg.Matcher.GetLabels(), + projectsClient: cfg.projectsClient, + Integration: cfg.Integration, + DiscoveryConfigName: cfg.DiscoveryConfigName, } if cfg.Matcher.Params != nil { fetcher.Parameters = map[string]string{ @@ -152,6 +156,12 @@ func (f *gcpInstanceFetcher) GetDiscoveryConfigName() string { return f.DiscoveryConfigName } +// IntegrationName identifies the integration name whose credentials were used to fetch the resources. +// Might be empty when the fetcher is using ambient credentials. +func (f *gcpInstanceFetcher) IntegrationName() string { + return f.Integration +} + // GetInstances fetches all GCP virtual machines matching configured filters. func (f *gcpInstanceFetcher) GetInstances(ctx context.Context, _ bool) ([]Instances, error) { // Key by project ID, then by zone. diff --git a/lib/srv/server/ssm_install.go b/lib/srv/server/ssm_install.go index 259da98b246e9..6c977a4b38be1 100644 --- a/lib/srv/server/ssm_install.go +++ b/lib/srv/server/ssm_install.go @@ -199,6 +199,10 @@ func (si *SSMInstaller) Run(ctx context.Context, req SSMRunRequest) error { validInstances = instancesState.valid } + if len(validInstances) == 0 { + return nil + } + validInstanceIDs := instanceIDsFrom(validInstances) output, err := req.SSM.SendCommand(ctx, &ssm.SendCommandInput{ DocumentName: aws.String(req.DocumentName), diff --git a/lib/srv/server/watcher.go b/lib/srv/server/watcher.go index 436cd0f128cbc..5b10097b0e045 100644 --- a/lib/srv/server/watcher.go +++ b/lib/srv/server/watcher.go @@ -45,6 +45,9 @@ type Fetcher interface { // GetDiscoveryConfigName returns the DiscoveryConfig name that created this fetcher. // Empty for Fetchers created from `teleport.yaml/discovery_service.aws.` matchers. GetDiscoveryConfigName() string + // IntegrationName identifies the integration name whose credentials were used to fetch the resources. + // Might be empty when the fetcher is using ambient credentials. + IntegrationName() string } // WithTriggerFetchC sets a poll trigger to manual start a resource polling. diff --git a/lib/srv/sess.go b/lib/srv/sess.go index 7942441592ee4..0796af8a58850 100644 --- a/lib/srv/sess.go +++ b/lib/srv/sess.go @@ -393,7 +393,7 @@ func (s *SessionRegistry) OpenExecSession(ctx context.Context, channel ssh.Chann if sessionID.IsZero() { sessionID = rsession.NewID() - scx.Logger.Log(ctx, logutils.TraceLevel, "Session not found, creating a new session", "sessin_id", sessionID) + scx.Logger.Log(ctx, logutils.TraceLevel, "Session not found, creating a new session", "session_id", sessionID) } else { // Use passed session ID. Assist uses this "feature" to record // the execution output. diff --git a/lib/tbot/service_ca_rotation.go b/lib/tbot/service_ca_rotation.go index 6df13528d62a3..c5d51658da245 100644 --- a/lib/tbot/service_ca_rotation.go +++ b/lib/tbot/service_ca_rotation.go @@ -222,7 +222,7 @@ func (s *caRotationService) watchCARotations(ctx context.Context, queueReload fu // We need to debounce here, as multiple events will be received if // the user is rotating multiple CAs at once. - s.log.InfoContext(ctx, "CA Rotation step detected; queueing renewa.") + s.log.InfoContext(ctx, "CA Rotation step detected; queueing renewal") queueReload() case <-watcher.Done(): if err := watcher.Error(); err != nil { diff --git a/lib/utils/log/buffer.go b/lib/utils/log/buffer.go index c158808bd545c..d12ac9e11bab0 100644 --- a/lib/utils/log/buffer.go +++ b/lib/utils/log/buffer.go @@ -21,6 +21,14 @@ func newBuffer() *buffer { return bufPool.Get().(*buffer) } +func (b *buffer) Len() int { + return len(*b) +} + +func (b *buffer) SetLen(n int) { + *b = (*b)[:n] +} + func (b *buffer) Free() { // To reduce peak allocation, return only smaller buffers to the pool. const maxBufferSize = 16 << 10 @@ -49,35 +57,6 @@ func (b *buffer) WriteByte(c byte) error { return nil } -func (b *buffer) WritePosInt(i int) { - b.WritePosIntWidth(i, 0) -} - -// WritePosIntWidth writes non-negative integer i to the buffer, padded on the left -// by zeroes to the given width. Use a width of 0 to omit padding. -func (b *buffer) WritePosIntWidth(i, width int) { - // Cheap integer to fixed-width decimal ASCII. - // Copied from log/log.go. - - if i < 0 { - panic("negative int") - } - - // Assemble decimal in reverse order. - var bb [20]byte - bp := len(bb) - 1 - for i >= 10 || width > 1 { - width-- - q := i / 10 - bb[bp] = byte('0' + i - q*10) - bp-- - i = q - } - // i < 10 - bb[bp] = byte('0' + i) - b.Write(bb[bp:]) -} - func (b *buffer) String() string { return string(*b) } diff --git a/lib/utils/log/formatter_test.go b/lib/utils/log/formatter_test.go index 39c717df3425d..e11a9f63620fb 100644 --- a/lib/utils/log/formatter_test.go +++ b/lib/utils/log/formatter_test.go @@ -45,7 +45,7 @@ import ( "github.com/gravitational/teleport" ) -const message = "Adding diagnostic debugging handlers.\t To connect with profiler, use `go tool pprof diag_addr`." +const message = "Adding diagnostic debugging handlers.\t To connect with profiler, use go tool pprof diag_addr." var ( logErr = errors.New("the quick brown fox jumped really high") @@ -76,7 +76,6 @@ func TestOutput(t *testing.T) { loc, err := time.LoadLocation("Africa/Cairo") require.NoError(t, err, "failed getting timezone") clock := clockwork.NewFakeClockAt(time.Now().In(loc)) - formattedNow := clock.Now().UTC().Format(time.RFC3339) t.Run("text", func(t *testing.T) { // fieldsRegex matches all the key value pairs emitted after the message and before the caller. All fields are @@ -88,7 +87,7 @@ func TestOutput(t *testing.T) { // 2) the message // 3) the fields // 4) the caller - outputRegex := regexp.MustCompile("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z)(\\s+.*)(\".*diag_addr`\\.\")(.*)(\\slog/formatter_test.go:\\d{3})") + outputRegex := regexp.MustCompile(`(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)(\s+.*)(".*diag_addr\.")(.*)(\slog/formatter_test.go:\d{3})`) tests := []struct { name string @@ -149,7 +148,7 @@ func TestOutput(t *testing.T) { EnableColors: true, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { - a.Value = slog.StringValue(formattedNow) + a.Value = slog.TimeValue(clock.Now().UTC()) } return a }, @@ -188,7 +187,7 @@ func TestOutput(t *testing.T) { // Match level, and component: DEBU [TEST] assert.Empty(t, cmp.Diff(logrusMatches[2], slogMatches[2]), "level, and component to be identical") - // Match the log message: "Adding diagnostic debugging handlers.\t To connect with profiler, use `go tool pprof diag_addr`.\n" + // Match the log message: "Adding diagnostic debugging handlers.\t To connect with profiler, use go tool pprof diag_addr.\n" assert.Empty(t, cmp.Diff(logrusMatches[3], slogMatches[3]), "expected output messages to be identical") // The last matches are the caller information assert.Equal(t, fmt.Sprintf(" log/formatter_test.go:%d", logrusTestLogLineNumber), logrusMatches[5]) @@ -461,7 +460,13 @@ func TestConcurrentOutput(t *testing.T) { wg.Add(1) go func(i int) { defer wg.Done() - logger.InfoContext(ctx, "Teleport component entered degraded state", "component", i) + logger.InfoContext(ctx, "Teleport component entered degraded state", + slog.Int("component", i), + slog.Group("group", + slog.String("test", "123"), + slog.String("animal", "llama"), + ), + ) }(i) } wg.Wait() diff --git a/lib/utils/log/handle_state.go b/lib/utils/log/handle_state.go new file mode 100644 index 0000000000000..c60132b28e48e --- /dev/null +++ b/lib/utils/log/handle_state.go @@ -0,0 +1,352 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package log + +import ( + "encoding" + "fmt" + "log/slog" + "reflect" + "strconv" + "sync" + "time" + + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + + "github.com/gravitational/teleport" +) + +// handleState adapted from go/src/log/slog/handler.go +type handleState struct { + h *SlogTextHandler + buf *buffer + freeBuf bool // should buf be freed? + prefix *buffer // for text: key prefix + groups *[]string // pool-allocated slice of active groups, for ReplaceAttr +} + +var groupPool = sync.Pool{New: func() any { + s := make([]string, 0, 10) + return &s +}} + +func (s *handleState) free() { + if s.freeBuf { + s.buf.Free() + } + if gs := s.groups; gs != nil { + *gs = (*gs)[:0] + groupPool.Put(gs) + } + s.prefix.Free() +} + +func (s *handleState) openGroups() { + for _, n := range s.h.groups[s.h.nOpenGroups:] { + s.openGroup(n) + } +} + +// openGroup starts a new group of attributes +// with the given name. +func (s *handleState) openGroup(name string) { + s.prefix.WriteString(name) + s.prefix.WriteByte('.') + + // Collect group names for ReplaceAttr. + if s.groups != nil { + *s.groups = append(*s.groups, name) + } +} + +// closeGroup ends the group with the given name. +func (s *handleState) closeGroup(name string) { + *s.prefix = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */] + + if s.groups != nil { + *s.groups = (*s.groups)[:len(*s.groups)-1] + } +} + +// appendAttrs appends the slice of Attrs. +// It reports whether something was appended. +func (s *handleState) appendAttrs(as []slog.Attr) bool { + nonEmpty := false + for _, a := range as { + if s.appendAttr(a) { + nonEmpty = true + } + } + return nonEmpty +} + +// appendAttr appends the Attr's key and value. +// It handles replacement and checking for an empty key. +// It reports whether something was appended. +func (s *handleState) appendAttr(a slog.Attr) bool { + a.Value = a.Value.Resolve() + if rep := s.h.cfg.ReplaceAttr; rep != nil && a.Value.Kind() != slog.KindGroup { + var gs []string + if s.groups != nil { + gs = *s.groups + } + // a.Value is resolved before calling ReplaceAttr, so the user doesn't have to. + a = rep(gs, a) + // The ReplaceAttr function may return an unresolved Attr. + a.Value = a.Value.Resolve() + } + // Elide empty Attrs. + if a.Equal(slog.Attr{}) { + return false + } + + // Handle nested attributes from within component fields. + if a.Key == teleport.ComponentFields { + nonEmpty := false + switch fields := a.Value.Any().(type) { + case map[string]any: + for k, v := range fields { + if s.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + return nonEmpty + case logrus.Fields: + for k, v := range fields { + if s.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + return nonEmpty + } + } + + // Handle special cases before formatting. + if a.Value.Kind() == slog.KindAny { + switch v := a.Value.Any().(type) { + case *slog.Source: + a.Value = slog.StringValue(fmt.Sprintf(" %s:%d", v.File, v.Line)) + case trace.Error: + a.Value = slog.StringValue("[" + v.DebugReport() + "]") + case error: + a.Value = slog.StringValue(fmt.Sprintf("[%v]", v)) + } + } + + if a.Value.Kind() == slog.KindGroup { + attrs := a.Value.Group() + // Output only non-empty groups. + if len(attrs) > 0 { + // The group may turn out to be empty even though it has attrs (for + // example, ReplaceAttr may delete all the attrs). + // So remember where we are in the buffer, to restore the position + // later if necessary. + pos := s.buf.Len() + // Inline a group with an empty key. + if a.Key != "" { + s.openGroup(a.Key) + } + if !s.appendAttrs(attrs) { + s.buf.SetLen(pos) + return false + } + if a.Key != "" { + s.closeGroup(a.Key) + } + } + + return true + } + + if a.Value.Kind() == slog.KindString && a.Key != slog.LevelKey { + val := a.Value.String() + if needsQuoting(val) { + a.Value = slog.StringValue(strconv.Quote(val)) + } + } + + s.appendKey(a.Key) + + // Write the log key directly to avoid quoting + // color formatting that exists. + if a.Key == slog.LevelKey { + s.buf.WriteString(a.Value.String()) + } else { + s.appendValue(a.Value) + } + + return true +} + +func (s *handleState) appendError(err error) { + s.appendString(fmt.Sprintf("!ERROR:%v", err)) +} + +func (s *handleState) appendKey(key string) { + if s.buf.Len() > 0 { + s.buf.WriteString(" ") + } + + // These keys should not be included in the output to match + // the behavior of the lorgus formatter. + if key == slog.TimeKey || + key == teleport.ComponentKey || + key == slog.LevelKey || + key == CallerField || + key == slog.MessageKey || + key == slog.SourceKey { + return + } + + if s.prefix != nil && len(*s.prefix) > 0 { + // TODO: optimize by avoiding allocation. + s.appendString(string(*s.prefix) + key) + } else { + s.appendString(key) + } + + s.buf.WriteByte(':') +} + +func (s *handleState) appendString(str string) { + if str == "" { + return + } + + if needsQuoting(str) { + *s.buf = strconv.AppendQuote(*s.buf, str) + } else { + s.buf.WriteString(str) + } +} + +func (s *handleState) appendValue(v slog.Value) { + defer func() { + if r := recover(); r != nil { + // If it panics with a nil pointer, the most likely cases are + // an encoding.TextMarshaler or error fails to guard against nil, + // in which case "" seems to be the feasible choice. + // + // Adapted from the code in fmt/print.go. + if v := reflect.ValueOf(v.Any()); v.Kind() == reflect.Pointer && v.IsNil() { + s.appendString("") + return + } + + // Otherwise just print the original panic message. + s.appendString(fmt.Sprintf("!PANIC: %v", r)) + } + }() + + if err := appendTextValue(s, v); err != nil { + s.appendError(err) + } +} + +func (s *handleState) appendTime(t time.Time) { + *s.buf = appendRFC3339Millis(*s.buf, t) +} + +func (s *handleState) appendNonBuiltIns(r slog.Record) { + // preformatted Attrs + if pfa := s.h.preformatted; len(pfa) > 0 { + s.buf.WriteString(" ") + s.buf.Write(pfa) + } + // Attrs in Record -- unlike the built-in ones, they are in groups started + // from WithGroup. + // If the record has no Attrs, don't output any groups. + if r.NumAttrs() > 0 { + s.prefix.WriteString(s.h.groupPrefix) + // The group may turn out to be empty even though it has attrs (for + // example, ReplaceAttr may delete all the attrs). + // So remember where we are in the buffer, to restore the position + // later if necessary. + pos := s.buf.Len() + s.openGroups() + empty := true + r.Attrs(func(a slog.Attr) bool { + // The component is handled by the top level handler. + if a.Key == teleport.ComponentKey { + return true + } + if s.appendAttr(a) { + empty = false + } + return true + }) + if empty { + s.buf.SetLen(pos) + } + } +} + +func byteSlice(a any) ([]byte, bool) { + if bs, ok := a.([]byte); ok { + return bs, true + } + // Like Printf's %s, we allow both the slice type and the byte element type to be named. + t := reflect.TypeOf(a) + if t != nil && t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { + return reflect.ValueOf(a).Bytes(), true + } + return nil, false +} + +func appendTextValue(s *handleState, v slog.Value) error { + switch v.Kind() { + case slog.KindString: + s.appendString(v.String()) + case slog.KindTime: + s.appendTime(v.Time()) + case slog.KindAny: + if tm, ok := v.Any().(encoding.TextMarshaler); ok { + data, err := tm.MarshalText() + if err != nil { + return err + } + // TODO: avoid the conversion to string. + s.appendString(string(data)) + return nil + } + if bs, ok := byteSlice(v.Any()); ok { + // As of Go 1.19, this only allocates for strings longer than 32 bytes. + s.buf.WriteString(strconv.Quote(string(bs))) + return nil + } + s.appendString(fmt.Sprintf("%+v", v.Any())) + case slog.KindInt64: + *s.buf = strconv.AppendInt(*s.buf, v.Int64(), 10) + case slog.KindUint64: + *s.buf = strconv.AppendUint(*s.buf, v.Uint64(), 10) + case slog.KindFloat64: + *s.buf = strconv.AppendFloat(*s.buf, v.Float64(), 'g', -1, 64) + case slog.KindBool: + *s.buf = strconv.AppendBool(*s.buf, v.Bool()) + case slog.KindDuration: + *s.buf = append(*s.buf, v.Duration().String()...) + case slog.KindGroup: + *s.buf = fmt.Append(*s.buf, v.Group()) + case slog.KindLogValuer: + *s.buf = fmt.Append(*s.buf, v.Any()) + default: + panic(fmt.Sprintf("bad kind: %s", v.Kind())) + } + return nil +} + +func appendRFC3339Millis(b []byte, t time.Time) []byte { + // Format according to time.RFC3339Nano since it is highly optimized, + // but truncate it to use millisecond resolution. + // Unfortunately, that format trims trailing 0s, so add 1/10 millisecond + // to guarantee that there are exactly 4 digits after the period. + const prefixLen = len("2006-01-02T15:04:05.000") + n := len(b) + t = t.Truncate(time.Millisecond).Add(time.Millisecond / 10) + b = t.AppendFormat(b, time.RFC3339Nano) + b = append(b[:n+prefixLen], b[n+prefixLen+1:]...) // drop the 4th digit + return b +} diff --git a/lib/utils/log/logrus_formatter.go b/lib/utils/log/logrus_formatter.go index 87b3bff3bdc24..a21d922adf809 100644 --- a/lib/utils/log/logrus_formatter.go +++ b/lib/utils/log/logrus_formatter.go @@ -145,7 +145,7 @@ func (tf *TextFormatter) Format(e *logrus.Entry) ([]byte, error) { // write timestamp first if enabled if tf.timestampEnabled { - writeTimeRFC3339(w.b, e.Time) + *w.b = appendRFC3339Millis(*w.b, e.Time.Round(0)) } for _, field := range tf.ExtraFields { diff --git a/lib/utils/log/slog.go b/lib/utils/log/slog.go new file mode 100644 index 0000000000000..b1b0678ec5487 --- /dev/null +++ b/lib/utils/log/slog.go @@ -0,0 +1,131 @@ +/* + * Teleport + * Copyright (C) 2023 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package log + +import ( + "context" + "fmt" + "log/slog" + "reflect" + "strings" + + oteltrace "go.opentelemetry.io/otel/trace" +) + +const ( + // TraceLevel is the logging level when set to Trace verbosity. + TraceLevel = slog.LevelDebug - 1 + + // TraceLevelText is the text representation of Trace verbosity. + TraceLevelText = "TRACE" +) + +// DiscardHandler is a [slog.Handler] that discards all messages. It +// is more efficient than a [slog.Handler] which outputs to [io.Discard] since +// it performs zero formatting. +// TODO(tross): Use slog.DiscardHandler once upgraded to Go 1.24. +type DiscardHandler struct{} + +func (dh DiscardHandler) Enabled(context.Context, slog.Level) bool { return false } +func (dh DiscardHandler) Handle(context.Context, slog.Record) error { return nil } +func (dh DiscardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return dh } +func (dh DiscardHandler) WithGroup(name string) slog.Handler { return dh } + +func addTracingContextToRecord(ctx context.Context, r *slog.Record) { + const ( + traceID = "trace_id" + spanID = "span_id" + ) + + span := oteltrace.SpanFromContext(ctx) + if span == nil { + return + } + + spanContext := span.SpanContext() + if spanContext.HasTraceID() { + r.AddAttrs(slog.String(traceID, spanContext.TraceID().String())) + } + + if spanContext.HasSpanID() { + r.AddAttrs(slog.String(spanID, spanContext.SpanID().String())) + } +} + +// getCaller retrieves source information from the attribute +// and returns the file and line of the caller. The file is +// truncated from the absolute path to package/filename. +func getCaller(s *slog.Source) (file string, line int) { + count := 0 + idx := strings.LastIndexFunc(s.File, func(r rune) bool { + if r == '/' { + count++ + } + + return count == 2 + }) + file = s.File[idx+1:] + line = s.Line + + return file, line +} + +type stringerAttr struct { + fmt.Stringer +} + +// StringerAttr creates a [slog.LogValuer] that will defer to +// the provided [fmt.Stringer]. All slog attributes are always evaluated, +// even if the log event is discarded due to the configured log level. +// A text [slog.Handler] will try to defer evaluation if the attribute is a +// [fmt.Stringer], however, the JSON [slog.Handler] only defers to [json.Marshaler]. +// This means that to defer evaluation and creation of the string representation, +// the object must implement [fmt.Stringer] and [json.Marshaler], otherwise additional +// and unwanted values may be emitted if the logger is configured to use JSON +// instead of text. This wrapping mechanism allows a value that implements [fmt.Stringer], +// to be guaranteed to be lazily constructed and always output the same +// content regardless of the output format. +func StringerAttr(s fmt.Stringer) slog.LogValuer { + return stringerAttr{Stringer: s} +} + +func (s stringerAttr) LogValue() slog.Value { + if s.Stringer == nil { + return slog.StringValue("") + } + return slog.StringValue(s.Stringer.String()) +} + +type typeAttr struct { + val any +} + +// TypeAttr creates a lazily evaluated log value that presents the pretty type name of a value +// as a string. It is roughly equivalent to the '%T' format option, and should only perform +// reflection in the event that logs are actually being generated. +func TypeAttr(val any) slog.LogValuer { + return typeAttr{val} +} + +func (a typeAttr) LogValue() slog.Value { + if t := reflect.TypeOf(a.val); t != nil { + return slog.StringValue(t.String()) + } + return slog.StringValue("nil") +} diff --git a/lib/utils/log/slog_handler.go b/lib/utils/log/slog_handler.go deleted file mode 100644 index 14363bca8584e..0000000000000 --- a/lib/utils/log/slog_handler.go +++ /dev/null @@ -1,692 +0,0 @@ -/* - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package log - -import ( - "context" - "fmt" - "io" - "log/slog" - "reflect" - "runtime" - "slices" - "strconv" - "strings" - "sync" - "time" - - "github.com/gravitational/trace" - "github.com/sirupsen/logrus" - oteltrace "go.opentelemetry.io/otel/trace" - - "github.com/gravitational/teleport" -) - -// TraceLevel is the logging level when set to Trace verbosity. -const TraceLevel = slog.LevelDebug - 1 - -// TraceLevelText is the text representation of Trace verbosity. -const TraceLevelText = "TRACE" - -// DiscardHandler is a [slog.Handler] that discards all messages. It -// is more efficient than a [slog.Handler] which outputs to [io.Discard] since -// it performs zero formatting. -// TODO(tross): Use slog.DiscardHandler once upgraded to Go 1.24. -type DiscardHandler struct{} - -func (dh DiscardHandler) Enabled(context.Context, slog.Level) bool { return false } -func (dh DiscardHandler) Handle(context.Context, slog.Record) error { return nil } -func (dh DiscardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return dh } -func (dh DiscardHandler) WithGroup(name string) slog.Handler { return dh } - -// SlogTextHandler is a [slog.Handler] that outputs messages in a textual -// manner as configured by the Teleport configuration. -type SlogTextHandler struct { - cfg SlogTextHandlerConfig - // withCaller indicates whether the location the log was emitted from - // should be included in the output message. - withCaller bool - // withTimestamp indicates whether the times that the log was emitted at - // should be included in the output message. - withTimestamp bool - // component is the Teleport subcomponent that emitted the log. - component string - // preformatted data from previous calls to WithGroup and WithAttrs. - preformatted []byte - // groupPrefix is for the text handler only. - // It holds the prefix for groups that were already pre-formatted. - // A group will appear here when a call to WithGroup is followed by - // a call to WithAttrs. - groupPrefix buffer - // groups passed in via WithGroup and WithAttrs. - groups []string - // nOpenGroups the number of groups opened in preformatted. - nOpenGroups int - - // mu protects out - it needs to be a pointer so that all cloned - // SlogTextHandler returned from WithAttrs and WithGroup share the - // same mutex. Otherwise, output may be garbled since each clone - // will use its own copy of the mutex to protect out. See - // https://github.com/golang/go/issues/61321 for more details. - mu *sync.Mutex - out io.Writer -} - -// SlogTextHandlerConfig allow the SlogTextHandler functionality -// to be tweaked. -type SlogTextHandlerConfig struct { - // Level is the minimum record level that will be logged. - Level slog.Leveler - // EnableColors allows the level to be printed in color. - EnableColors bool - // Padding to use for various components. - Padding int - // ConfiguredFields are fields explicitly set by users to be included in - // the output message. If there are any entries configured, they will be honored. - // If empty, the default fields will be populated and included in the output. - ConfiguredFields []string - // ReplaceAttr is called to rewrite each non-group attribute before - // it is logged. - ReplaceAttr func(groups []string, a slog.Attr) slog.Attr -} - -// NewSlogTextHandler creates a SlogTextHandler that writes messages to w. -func NewSlogTextHandler(w io.Writer, cfg SlogTextHandlerConfig) *SlogTextHandler { - if cfg.Padding == 0 { - cfg.Padding = defaultComponentPadding - } - - handler := SlogTextHandler{ - cfg: cfg, - withCaller: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField), - withTimestamp: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField), - out: w, - mu: &sync.Mutex{}, - } - - if handler.cfg.ConfiguredFields == nil { - handler.cfg.ConfiguredFields = defaultFormatFields - } - - return &handler -} - -// Enabled returns whether the provided level will be included in output. -func (s *SlogTextHandler) Enabled(ctx context.Context, level slog.Level) bool { - minLevel := slog.LevelInfo - if s.cfg.Level != nil { - minLevel = s.cfg.Level.Level() - } - return level >= minLevel -} - -func (s *SlogTextHandler) appendAttr(buf []byte, a slog.Attr) []byte { - if rep := s.cfg.ReplaceAttr; rep != nil && a.Value.Kind() != slog.KindGroup { - var gs []string - if s.groups != nil { - gs = s.groups - } - // Resolve before calling ReplaceAttr, so the user doesn't have to. - a.Value = a.Value.Resolve() - a = rep(gs, a) - } - - // Resolve the Attr's value before doing anything else. - a.Value = a.Value.Resolve() - // Ignore empty Attrs. - if a.Equal(slog.Attr{}) { - return buf - } - - switch a.Value.Kind() { - case slog.KindString: - value := a.Value.String() - if a.Key == slog.TimeKey { - buf = fmt.Append(buf, value) - break - } - - if a.Key == teleport.ComponentFields { - switch fields := a.Value.Any().(type) { - case map[string]any: - for k, v := range fields { - buf = s.appendAttr(buf, slog.Any(k, v)) - } - case logrus.Fields: - for k, v := range fields { - buf = s.appendAttr(buf, slog.Any(k, v)) - } - } - } - - if needsQuoting(value) { - if a.Key == teleport.ComponentKey || a.Key == slog.LevelKey || a.Key == CallerField || a.Key == slog.MessageKey { - if len(buf) > 0 { - buf = fmt.Append(buf, " ") - } - } else { - if len(buf) > 0 { - buf = fmt.Append(buf, " ") - } - buf = fmt.Appendf(buf, "%s%s:", s.groupPrefix, a.Key) - } - buf = strconv.AppendQuote(buf, value) - break - } - - if a.Key == teleport.ComponentKey || a.Key == slog.LevelKey || a.Key == CallerField || a.Key == slog.MessageKey { - if len(buf) > 0 { - buf = fmt.Append(buf, " ") - } - buf = fmt.Appendf(buf, "%s", a.Value.String()) - break - } - - buf = fmt.Appendf(buf, " %s%s:%s", s.groupPrefix, a.Key, a.Value.String()) - case slog.KindGroup: - attrs := a.Value.Group() - // Ignore empty groups. - if len(attrs) == 0 { - return buf - } - // If the key is non-empty, write it out and indent the rest of the attrs. - // Otherwise, inline the attrs. - if a.Key != "" { - s.groupPrefix = fmt.Append(s.groupPrefix, a.Key) - s.groupPrefix = fmt.Append(s.groupPrefix, ".") - } - for _, ga := range attrs { - buf = s.appendAttr(buf, ga) - } - if a.Key != "" { - s.groupPrefix = s.groupPrefix[:len(s.groupPrefix)-len(a.Key)-1 /* for keyComponentSep */] - if s.groups != nil { - s.groups = (s.groups)[:len(s.groups)-1] - } - } - default: - switch err := a.Value.Any().(type) { - case trace.Error: - buf = fmt.Appendf(buf, " error:[%v]", err.DebugReport()) - case error: - buf = fmt.Appendf(buf, " error:[%v]", a.Value) - default: - buf = fmt.Appendf(buf, " %s:%s", a.Key, a.Value) - } - } - return buf -} - -// writeTimeRFC3339 writes the time in [time.RFC3339Nano] to the buffer. -// This takes half the time of [time.Time.AppendFormat]. Adapted from -// go/src/log/slog/handler.go. -func writeTimeRFC3339(buf *buffer, t time.Time) { - year, month, day := t.Date() - buf.WritePosIntWidth(year, 4) - buf.WriteByte('-') - buf.WritePosIntWidth(int(month), 2) - buf.WriteByte('-') - buf.WritePosIntWidth(day, 2) - buf.WriteByte('T') - hour, min, sec := t.Clock() - buf.WritePosIntWidth(hour, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(min, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(sec, 2) - _, offsetSeconds := t.Zone() - if offsetSeconds == 0 { - buf.WriteByte('Z') - } else { - offsetMinutes := offsetSeconds / 60 - if offsetMinutes < 0 { - buf.WriteByte('-') - offsetMinutes = -offsetMinutes - } else { - buf.WriteByte('+') - } - buf.WritePosIntWidth(offsetMinutes/60, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(offsetMinutes%60, 2) - } -} - -// Handle formats the provided record and writes the output to the -// destination. -func (s *SlogTextHandler) Handle(ctx context.Context, r slog.Record) error { - buf := newBuffer() - defer buf.Free() - - addTracingContextToRecord(ctx, &r) - - if s.withTimestamp && !r.Time.IsZero() { - if s.cfg.ReplaceAttr != nil { - *buf = s.appendAttr(*buf, slog.Time(slog.TimeKey, r.Time)) - } else { - writeTimeRFC3339(buf, r.Time) - } - } - - // Processing fields in this manner allows users to - // configure the level and component position in the output. - // This matches the behavior of the original logrus. All other - // fields location in the output message are static. - for _, field := range s.cfg.ConfiguredFields { - switch field { - case LevelField: - var color int - var level string - switch r.Level { - case TraceLevel: - level = "TRACE" - color = gray - case slog.LevelDebug: - level = "DEBUG" - color = gray - case slog.LevelInfo: - level = "INFO" - color = blue - case slog.LevelWarn: - level = "WARN" - color = yellow - case slog.LevelError: - level = "ERROR" - color = red - case slog.LevelError + 1: - level = "FATAL" - color = red - default: - color = blue - level = r.Level.String() - } - - if !s.cfg.EnableColors { - color = noColor - } - - level = padMax(level, defaultLevelPadding) - if color == noColor { - *buf = s.appendAttr(*buf, slog.String(slog.LevelKey, level)) - } else { - *buf = fmt.Appendf(*buf, " \u001B[%dm%s\u001B[0m", color, level) - } - case ComponentField: - // If a component is provided with the attributes, it should be used instead of - // the component set on the handler. Note that if there are multiple components - // specified in the arguments, the one with the lowest index is used and the others are ignored. - // In the example below, the resulting component in the message output would be "alpaca". - // - // logger := logger.With(teleport.ComponentKey, "fish") - // logger.InfoContext(ctx, "llama llama llama", teleport.ComponentKey, "alpaca", "foo", 123, teleport.ComponentKey, "shark") - component := s.component - r.Attrs(func(attr slog.Attr) bool { - if attr.Key == teleport.ComponentKey { - component = fmt.Sprintf("[%v]", attr.Value) - component = strings.ToUpper(padMax(component, s.cfg.Padding)) - if component[len(component)-1] != ' ' { - component = component[:len(component)-1] + "]" - } - - return false - } - - return true - }) - - *buf = s.appendAttr(*buf, slog.String(teleport.ComponentKey, component)) - default: - if _, ok := knownFormatFields[field]; !ok { - return trace.BadParameter("invalid log format key: %v", field) - } - } - } - - *buf = s.appendAttr(*buf, slog.String(slog.MessageKey, r.Message)) - - // Insert preformatted attributes just after built-in ones. - *buf = append(*buf, s.preformatted...) - if r.NumAttrs() > 0 { - if len(s.groups) > 0 { - for _, n := range s.groups[s.nOpenGroups:] { - s.groupPrefix = fmt.Append(s.groupPrefix, n) - s.groupPrefix = fmt.Append(s.groupPrefix, ".") - } - } - - r.Attrs(func(a slog.Attr) bool { - // Skip adding any component attrs since they are processed above. - if a.Key == teleport.ComponentKey { - return true - } - - *buf = s.appendAttr(*buf, a) - return true - }) - } - - if r.PC != 0 && s.withCaller { - fs := runtime.CallersFrames([]uintptr{r.PC}) - f, _ := fs.Next() - - src := &slog.Source{ - Function: f.Function, - File: f.File, - Line: f.Line, - } - - file, line := getCaller(src) - *buf = fmt.Appendf(*buf, " %s:%d", file, line) - } - - buf.WriteByte('\n') - - s.mu.Lock() - defer s.mu.Unlock() - _, err := s.out.Write(*buf) - return err -} - -// WithAttrs clones the current handler with the provided attributes -// added to any existing attributes. The values are preformatted here -// so that they do not need to be formatted per call to Handle. -func (s *SlogTextHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - if len(attrs) == 0 { - return s - } - s2 := *s - // Force an append to copy the underlying arrays. - s2.preformatted = slices.Clip(s.preformatted) - s2.groups = slices.Clip(s.groups) - - // Add all groups from WithGroup that haven't already been added to the prefix. - if len(s.groups) > 0 { - for _, n := range s.groups[s.nOpenGroups:] { - s2.groupPrefix = fmt.Append(s2.groupPrefix, n) - s2.groupPrefix = fmt.Append(s2.groupPrefix, ".") - } - } - - // Now all groups have been opened. - s2.nOpenGroups = len(s2.groups) - - component := s.component - - // Pre-format the attributes. - for _, a := range attrs { - switch a.Key { - case teleport.ComponentKey: - component = fmt.Sprintf("[%v]", a.Value.String()) - component = strings.ToUpper(padMax(component, s.cfg.Padding)) - if component[len(component)-1] != ' ' { - component = component[:len(component)-1] + "]" - } - case teleport.ComponentFields: - switch fields := a.Value.Any().(type) { - case map[string]any: - for k, v := range fields { - s2.appendAttr(s2.preformatted, slog.Any(k, v)) - } - case logrus.Fields: - for k, v := range fields { - s2.preformatted = s2.appendAttr(s2.preformatted, slog.Any(k, v)) - } - } - default: - s2.preformatted = s2.appendAttr(s2.preformatted, a) - } - } - - s2.component = component - // Remember how many opened groups are in preformattedAttrs, - // so we don't open them again when we handle a Record. - s2.nOpenGroups = len(s2.groups) - return &s2 -} - -// WithGroup opens a new group. -func (s *SlogTextHandler) WithGroup(name string) slog.Handler { - if name == "" { - return s - } - - s2 := *s - s2.groups = append(s2.groups, name) - return &s2 -} - -// SlogJSONHandlerConfig allow the SlogJSONHandler functionality -// to be tweaked. -type SlogJSONHandlerConfig struct { - // Level is the minimum record level that will be logged. - Level slog.Leveler - // ConfiguredFields are fields explicitly set by users to be included in - // the output message. If there are any entries configured, they will be honored. - // If empty, the default fields will be populated and included in the output. - ConfiguredFields []string - // ReplaceAttr is called to rewrite each non-group attribute before - // it is logged. - ReplaceAttr func(groups []string, a slog.Attr) slog.Attr -} - -// SlogJSONHandler is a [slog.Handler] that outputs messages in a json -// format per the config file. -type SlogJSONHandler struct { - *slog.JSONHandler -} - -// NewSlogJSONHandler creates a SlogJSONHandler that outputs to w. -func NewSlogJSONHandler(w io.Writer, cfg SlogJSONHandlerConfig) *SlogJSONHandler { - withCaller := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField) - withComponent := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, ComponentField) - withTimestamp := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField) - - return &SlogJSONHandler{ - JSONHandler: slog.NewJSONHandler(w, &slog.HandlerOptions{ - AddSource: true, - Level: cfg.Level, - ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { - switch a.Key { - case teleport.ComponentKey: - if !withComponent { - return slog.Attr{} - } - if a.Value.Kind() != slog.KindString { - return a - } - - a.Key = ComponentField - case slog.LevelKey: - // The slog.JSONHandler will inject "level" Attr. - // However, this lib's consumer might add an Attr using the same key ("level") and we end up with two records named "level". - // We must check its type before assuming this was injected by the slog.JSONHandler. - lvl, ok := a.Value.Any().(slog.Level) - if !ok { - return a - } - - var level string - switch lvl { - case TraceLevel: - level = "trace" - case slog.LevelDebug: - level = "debug" - case slog.LevelInfo: - level = "info" - case slog.LevelWarn: - level = "warning" - case slog.LevelError: - level = "error" - case slog.LevelError + 1: - level = "fatal" - default: - level = strings.ToLower(lvl.String()) - } - - a.Value = slog.StringValue(level) - case slog.TimeKey: - if !withTimestamp { - return slog.Attr{} - } - - // The slog.JSONHandler will inject "time" Attr. - // However, this lib's consumer might add an Attr using the same key ("time") and we end up with two records named "time". - // We must check its type before assuming this was injected by the slog.JSONHandler. - if a.Value.Kind() != slog.KindTime { - return a - } - - t := a.Value.Time() - if t.IsZero() { - return a - } - - a.Key = TimestampField - a.Value = slog.StringValue(t.Format(time.RFC3339)) - case slog.MessageKey: - // The slog.JSONHandler will inject "msg" Attr. - // However, this lib's consumer might add an Attr using the same key ("msg") and we end up with two records named "msg". - // We must check its type before assuming this was injected by the slog.JSONHandler. - if a.Value.Kind() != slog.KindString { - return a - } - a.Key = messageField - case slog.SourceKey: - if !withCaller { - return slog.Attr{} - } - - // The slog.JSONHandler will inject "source" Attr when AddSource is true. - // However, this lib's consumer might add an Attr using the same key ("source") and we end up with two records named "source". - // We must check its type before assuming this was injected by the slog.JSONHandler. - s, ok := a.Value.Any().(*slog.Source) - if !ok { - return a - } - - file, line := getCaller(s) - a = slog.String(CallerField, fmt.Sprintf("%s:%d", file, line)) - } - - // Convert [slog.KindAny] values that are backed by an [error] or [fmt.Stringer] - // to strings so that only the message is output instead of a json object. The kind is - // first checked to avoid allocating an interface for the values stored inline - // in [slog.Attr]. - if a.Value.Kind() == slog.KindAny { - if err, ok := a.Value.Any().(error); ok { - a.Value = slog.StringValue(err.Error()) - } - - if stringer, ok := a.Value.Any().(fmt.Stringer); ok { - a.Value = slog.StringValue(stringer.String()) - } - } - - return a - }, - }), - } -} - -const ( - traceID = "trace_id" - spanID = "span_id" -) - -func addTracingContextToRecord(ctx context.Context, r *slog.Record) { - span := oteltrace.SpanFromContext(ctx) - if span == nil { - return - } - - spanContext := span.SpanContext() - if spanContext.HasTraceID() { - r.AddAttrs(slog.String(traceID, spanContext.TraceID().String())) - } - - if spanContext.HasSpanID() { - r.AddAttrs(slog.String(spanID, spanContext.SpanID().String())) - } -} - -func (j *SlogJSONHandler) Handle(ctx context.Context, r slog.Record) error { - addTracingContextToRecord(ctx, &r) - return j.JSONHandler.Handle(ctx, r) -} - -// getCaller retrieves source information from the attribute -// and returns the file and line of the caller. The file is -// truncated from the absolute path to package/filename. -func getCaller(s *slog.Source) (file string, line int) { - count := 0 - idx := strings.LastIndexFunc(s.File, func(r rune) bool { - if r == '/' { - count++ - } - - return count == 2 - }) - file = s.File[idx+1:] - line = s.Line - - return file, line -} - -type stringerAttr struct { - fmt.Stringer -} - -// StringerAttr creates a [slog.LogValuer] that will defer to -// the provided [fmt.Stringer]. All slog attributes are always evaluated, -// even if the log event is discarded due to the configured log level. -// A text [slog.Handler] will try to defer evaluation if the attribute is a -// [fmt.Stringer], however, the JSON [slog.Handler] only defers to [json.Marshaler]. -// This means that to defer evaluation and creation of the string representation, -// the object must implement [fmt.Stringer] and [json.Marshaler], otherwise additional -// and unwanted values may be emitted if the logger is configured to use JSON -// instead of text. This wrapping mechanism allows a value that implements [fmt.Stringer], -// to be guaranteed to be lazily constructed and always output the same -// content regardless of the output format. -func StringerAttr(s fmt.Stringer) slog.LogValuer { - return stringerAttr{Stringer: s} -} - -func (s stringerAttr) LogValue() slog.Value { - if s.Stringer == nil { - return slog.StringValue("") - } - return slog.StringValue(s.Stringer.String()) -} - -type typeAttr struct { - val any -} - -// TypeAttr creates a lazily evaluated log value that presents the pretty type name of a value -// as a string. It is roughly equivalent to the '%T' format option, and should only perform -// reflection in the event that logs are actually being generated. -func TypeAttr(val any) slog.LogValuer { - return typeAttr{val} -} - -func (a typeAttr) LogValue() slog.Value { - if t := reflect.TypeOf(a.val); t != nil { - return slog.StringValue(t.String()) - } - return slog.StringValue("nil") -} diff --git a/lib/utils/log/slog_handler_test.go b/lib/utils/log/slog_handler_test.go index 20876b6c4df1c..a75d57fc66994 100644 --- a/lib/utils/log/slog_handler_test.go +++ b/lib/utils/log/slog_handler_test.go @@ -22,13 +22,11 @@ import ( "bytes" "context" "encoding/json" - "fmt" "log/slog" "regexp" "strings" "testing" "testing/slogtest" - "time" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/assert" @@ -41,16 +39,17 @@ import ( func TestSlogTextHandler(t *testing.T) { t.Parallel() clock := clockwork.NewFakeClock() - now := clock.Now().UTC().Format(time.RFC3339) + now := clock.Now().UTC() // Create a handler that doesn't report the caller and automatically sets // the time to whatever time the fake clock has in UTC time. Since the timestamp // is not important for this test overriding, it allows the regex to be simpler. var buf bytes.Buffer h := NewSlogTextHandler(&buf, SlogTextHandlerConfig{ + ConfiguredFields: []string{LevelField, ComponentField, TimestampField}, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { - a.Value = slog.StringValue(now) + a.Value = slog.TimeValue(now) } return a }, @@ -62,8 +61,7 @@ func TestSlogTextHandler(t *testing.T) { // Group 2: verbosity level of output // Group 3: message contents // Group 4: additional attributes - regex := fmt.Sprintf("^(?:(%s)?)\\s?([A-Z]{4})\\s+(\\w+)(?:\\s(.*))?$", now) - lineRegex := regexp.MustCompile(regex) + lineRegex := regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)?\s?([A-Z]{4})\s+(\w+)(?:\s(.*))?$`) results := func() []map[string]any { var ms []map[string]any @@ -75,7 +73,7 @@ func TestSlogTextHandler(t *testing.T) { var m map[string]any matches := lineRegex.FindSubmatch(line) if len(matches) == 0 { - assert.Failf(t, "log output did not match regular expression", "regex: %s output: %s", regex, string(line)) + assert.Failf(t, "log output did not match regular expression", "regex: %s output: %s", lineRegex.String(), string(line)) ms = append(ms, m) continue } diff --git a/lib/utils/log/slog_json_handler.go b/lib/utils/log/slog_json_handler.go new file mode 100644 index 0000000000000..f5c3ec4062b09 --- /dev/null +++ b/lib/utils/log/slog_json_handler.go @@ -0,0 +1,167 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package log + +import ( + "context" + "fmt" + "io" + "log/slog" + "slices" + "strings" + "time" + + "github.com/gravitational/teleport" +) + +// SlogJSONHandlerConfig allows the SlogJSONHandler functionality +// to be tweaked. +type SlogJSONHandlerConfig struct { + // Level is the minimum record level that will be logged. + Level slog.Leveler + // ConfiguredFields are fields explicitly set by users to be included in + // the output message. If there are any entries configured, they will be honored. + // If empty, the default fields will be populated and included in the output. + ConfiguredFields []string + // ReplaceAttr is called to rewrite each non-group attribute before + // it is logged. + ReplaceAttr func(groups []string, a slog.Attr) slog.Attr +} + +// SlogJSONHandler is a [slog.Handler] that outputs messages in a json +// format per the config file. +type SlogJSONHandler struct { + *slog.JSONHandler +} + +// NewSlogJSONHandler creates a SlogJSONHandler that outputs to w. +func NewSlogJSONHandler(w io.Writer, cfg SlogJSONHandlerConfig) *SlogJSONHandler { + withCaller := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField) + withComponent := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, ComponentField) + withTimestamp := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField) + + return &SlogJSONHandler{ + JSONHandler: slog.NewJSONHandler(w, &slog.HandlerOptions{ + AddSource: true, + Level: cfg.Level, + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + switch a.Key { + case teleport.ComponentKey: + if !withComponent { + return slog.Attr{} + } + if a.Value.Kind() != slog.KindString { + return a + } + + a.Key = ComponentField + case slog.LevelKey: + // The slog.JSONHandler will inject "level" Attr. + // However, this lib's consumer might add an Attr using the same key ("level") and we end up with two records named "level". + // We must check its type before assuming this was injected by the slog.JSONHandler. + lvl, ok := a.Value.Any().(slog.Level) + if !ok { + return a + } + + var level string + switch lvl { + case TraceLevel: + level = "trace" + case slog.LevelDebug: + level = "debug" + case slog.LevelInfo: + level = "info" + case slog.LevelWarn: + level = "warning" + case slog.LevelError: + level = "error" + case slog.LevelError + 1: + level = "fatal" + default: + level = strings.ToLower(lvl.String()) + } + + a.Value = slog.StringValue(level) + case slog.TimeKey: + if !withTimestamp { + return slog.Attr{} + } + + // The slog.JSONHandler will inject "time" Attr. + // However, this lib's consumer might add an Attr using the same key ("time") and we end up with two records named "time". + // We must check its type before assuming this was injected by the slog.JSONHandler. + if a.Value.Kind() != slog.KindTime { + return a + } + + t := a.Value.Time() + if t.IsZero() { + return a + } + + a.Key = TimestampField + a.Value = slog.StringValue(t.Format(time.RFC3339)) + case slog.MessageKey: + // The slog.JSONHandler will inject "msg" Attr. + // However, this lib's consumer might add an Attr using the same key ("msg") and we end up with two records named "msg". + // We must check its type before assuming this was injected by the slog.JSONHandler. + if a.Value.Kind() != slog.KindString { + return a + } + a.Key = messageField + case slog.SourceKey: + if !withCaller { + return slog.Attr{} + } + + // The slog.JSONHandler will inject "source" Attr when AddSource is true. + // However, this lib's consumer might add an Attr using the same key ("source") and we end up with two records named "source". + // We must check its type before assuming this was injected by the slog.JSONHandler. + s, ok := a.Value.Any().(*slog.Source) + if !ok { + return a + } + + file, line := getCaller(s) + a = slog.String(CallerField, fmt.Sprintf("%s:%d", file, line)) + } + + // Convert [slog.KindAny] values that are backed by an [error] or [fmt.Stringer] + // to strings so that only the message is output instead of a json object. The kind is + // first checked to avoid allocating an interface for the values stored inline + // in [slog.Attr]. + if a.Value.Kind() == slog.KindAny { + if err, ok := a.Value.Any().(error); ok { + a.Value = slog.StringValue(err.Error()) + } + + if stringer, ok := a.Value.Any().(fmt.Stringer); ok { + a.Value = slog.StringValue(stringer.String()) + } + } + + return a + }, + }), + } +} + +func (j *SlogJSONHandler) Handle(ctx context.Context, r slog.Record) error { + addTracingContextToRecord(ctx, &r) + return j.JSONHandler.Handle(ctx, r) +} diff --git a/lib/utils/log/slog_text_handler.go b/lib/utils/log/slog_text_handler.go new file mode 100644 index 0000000000000..b3bc4900ac64c --- /dev/null +++ b/lib/utils/log/slog_text_handler.go @@ -0,0 +1,359 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package log + +import ( + "context" + "fmt" + "io" + "log/slog" + "runtime" + "slices" + "strings" + "sync" + + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + + "github.com/gravitational/teleport" +) + +// SlogTextHandler is a [slog.Handler] that outputs messages in a textual +// manner as configured by the Teleport configuration. +type SlogTextHandler struct { + cfg SlogTextHandlerConfig + // withCaller indicates whether the location the log was emitted from + // should be included in the output message. + withCaller bool + // withTimestamp indicates whether the times that the log was emitted at + // should be included in the output message. + withTimestamp bool + // component is the Teleport subcomponent that emitted the log. + component string + // preformatted data from previous calls to WithGroup and WithAttrs. + preformatted []byte + // groupPrefix is for the text handler only. + // It holds the prefix for groups that were already pre-formatted. + // A group will appear here when a call to WithGroup is followed by + // a call to WithAttrs. + groupPrefix string + // groups passed in via WithGroup and WithAttrs. + groups []string + // nOpenGroups the number of groups opened in preformatted. + nOpenGroups int + + // mu protects out - it needs to be a pointer so that all cloned + // SlogTextHandler returned from WithAttrs and WithGroup share the + // same mutex. Otherwise, output may be garbled since each clone + // will use its own copy of the mutex to protect out. See + // https://github.com/golang/go/issues/61321 for more details. + mu *sync.Mutex + out io.Writer +} + +// SlogTextHandlerConfig allow the SlogTextHandler functionality +// to be tweaked. +type SlogTextHandlerConfig struct { + // Level is the minimum record level that will be logged. + Level slog.Leveler + // EnableColors allows the level to be printed in color. + EnableColors bool + // Padding to use for various components. + Padding int + // ConfiguredFields are fields explicitly set by users to be included in + // the output message. If there are any entries configured, they will be honored. + // If empty, the default fields will be populated and included in the output. + ConfiguredFields []string + // ReplaceAttr is called to rewrite each non-group attribute before + // it is logged. + ReplaceAttr func(groups []string, a slog.Attr) slog.Attr +} + +// NewSlogTextHandler creates a SlogTextHandler that writes messages to w. +func NewSlogTextHandler(w io.Writer, cfg SlogTextHandlerConfig) *SlogTextHandler { + if cfg.Padding == 0 { + cfg.Padding = defaultComponentPadding + } + + handler := SlogTextHandler{ + cfg: cfg, + withCaller: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField), + withTimestamp: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField), + out: w, + mu: &sync.Mutex{}, + } + + if handler.cfg.ConfiguredFields == nil { + handler.cfg.ConfiguredFields = defaultFormatFields + } + + return &handler +} + +// Enabled returns whether the provided level will be included in output. +func (s *SlogTextHandler) Enabled(ctx context.Context, level slog.Level) bool { + minLevel := slog.LevelInfo + if s.cfg.Level != nil { + minLevel = s.cfg.Level.Level() + } + return level >= minLevel +} + +func (s *SlogTextHandler) newHandleState(buf *buffer, freeBuf bool) handleState { + state := handleState{ + h: s, + buf: buf, + freeBuf: freeBuf, + prefix: newBuffer(), + } + if s.cfg.ReplaceAttr != nil { + state.groups = groupPool.Get().(*[]string) + *state.groups = append(*state.groups, s.groups[:s.nOpenGroups]...) + } + return state +} + +// Handle formats the provided record and writes the output to the +// destination. +func (s *SlogTextHandler) Handle(ctx context.Context, r slog.Record) error { + state := s.newHandleState(newBuffer(), true) + defer state.free() + + addTracingContextToRecord(ctx, &r) + + // Built-in attributes. They are not in a group. + stateGroups := state.groups + state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups. + rep := s.cfg.ReplaceAttr + + if s.withTimestamp && !r.Time.IsZero() { + if rep == nil { + state.appendKey(slog.TimeKey) + state.appendTime(r.Time.Round(0)) + } else { + state.appendAttr(slog.Time(slog.TimeKey, r.Time.Round(0))) + } + } + + // Processing fields in this manner allows users to + // configure the level and component position in the output. + // This matches the behavior of the original logrus. All other + // fields location in the output message are static. + for _, field := range s.cfg.ConfiguredFields { + switch field { + case LevelField: + var color int + var level string + switch r.Level { + case TraceLevel: + level = "TRACE" + color = gray + case slog.LevelDebug: + level = "DEBUG" + color = gray + case slog.LevelInfo: + level = "INFO" + color = blue + case slog.LevelWarn: + level = "WARN" + color = yellow + case slog.LevelError: + level = "ERROR" + color = red + case slog.LevelError + 1: + level = "FATAL" + color = red + default: + color = blue + level = r.Level.String() + } + + if !s.cfg.EnableColors { + color = noColor + } + + level = padMax(level, defaultLevelPadding) + if color != noColor { + level = fmt.Sprintf("\u001B[%dm%s\u001B[0m", color, level) + } + + if rep == nil { + state.appendKey(slog.LevelKey) + // Write the level directly to stat to avoid quoting + // color formatting that exists. + state.buf.WriteString(level) + } else { + state.appendAttr(slog.String(slog.LevelKey, level)) + } + case ComponentField: + // If a component is provided with the attributes, it should be used instead of + // the component set on the handler. Note that if there are multiple components + // specified in the arguments, the one with the lowest index is used and the others are ignored. + // In the example below, the resulting component in the message output would be "alpaca". + // + // logger := logger.With(teleport.ComponentKey, "fish") + // logger.InfoContext(ctx, "llama llama llama", teleport.ComponentKey, "alpaca", "foo", 123, teleport.ComponentKey, "shark") + component := s.component + r.Attrs(func(attr slog.Attr) bool { + if attr.Key != teleport.ComponentKey { + return true + } + component = fmt.Sprintf("[%v]", attr.Value) + component = strings.ToUpper(padMax(component, s.cfg.Padding)) + if component[len(component)-1] != ' ' { + component = component[:len(component)-1] + "]" + } + + return false + }) + + if rep == nil { + state.appendKey(teleport.ComponentKey) + state.appendString(component) + } else { + state.appendAttr(slog.String(teleport.ComponentKey, component)) + } + default: + if _, ok := knownFormatFields[field]; !ok { + return trace.BadParameter("invalid log format key: %v", field) + } + } + } + + if rep == nil { + state.appendKey(slog.MessageKey) + state.appendString(r.Message) + } else { + state.appendAttr(slog.String(slog.MessageKey, r.Message)) + } + + state.groups = stateGroups // Restore groups passed to ReplaceAttrs. + state.appendNonBuiltIns(r) + + if r.PC != 0 && s.withCaller { + fs := runtime.CallersFrames([]uintptr{r.PC}) + f, _ := fs.Next() + + src := slog.Source{ + Function: f.Function, + File: f.File, + Line: f.Line, + } + src.File, src.Line = getCaller(&src) + + if rep == nil { + state.appendKey(slog.SourceKey) + state.appendString(fmt.Sprintf("%s:%d", src.File, src.Line)) + } else { + state.appendAttr(slog.Any(slog.SourceKey, &src)) + } + + } + + state.buf.WriteByte('\n') + + s.mu.Lock() + defer s.mu.Unlock() + _, err := s.out.Write(*state.buf) + return err +} + +func (s *SlogTextHandler) clone() *SlogTextHandler { + // We can't use assignment because we can't copy the mutex. + return &SlogTextHandler{ + cfg: s.cfg, + withCaller: s.withCaller, + withTimestamp: s.withTimestamp, + component: s.component, + preformatted: slices.Clip(s.preformatted), + groupPrefix: s.groupPrefix, + groups: slices.Clip(s.groups), + nOpenGroups: s.nOpenGroups, + out: s.out, + mu: s.mu, // mutex shared among all clones of this handler + } +} + +// WithAttrs clones the current handler with the provided attributes +// added to any existing attributes. The values are preformatted here +// so that they do not need to be formatted per call to Handle. +func (s *SlogTextHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + if len(attrs) == 0 { + return s + } + + s2 := s.clone() + // Pre-format the attributes as an optimization. + state := s2.newHandleState((*buffer)(&s2.preformatted), false) + defer state.free() + state.prefix.WriteString(s.groupPrefix) + + // Remember the position in the buffer, in case all attrs are empty. + pos := state.buf.Len() + state.openGroups() + + nonEmpty := false + for _, a := range attrs { + switch a.Key { + case teleport.ComponentKey: + component := fmt.Sprintf("[%v]", a.Value.String()) + component = strings.ToUpper(padMax(component, s.cfg.Padding)) + if component[len(component)-1] != ' ' { + component = component[:len(component)-1] + "]" + } + s2.component = component + case teleport.ComponentFields: + switch fields := a.Value.Any().(type) { + case map[string]any: + for k, v := range fields { + if state.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + case logrus.Fields: + for k, v := range fields { + if state.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + } + default: + if state.appendAttr(a) { + nonEmpty = true + } + } + } + + if !nonEmpty { + state.buf.SetLen(pos) + } else { + // Remember the new prefix for later keys. + s2.groupPrefix = state.prefix.String() + // Remember how many opened groups are in preformattedAttrs, + // so we don't open them again when we handle a Record. + s2.nOpenGroups = len(s2.groups) + } + + return s2 +} + +// WithGroup opens a new group. +func (s *SlogTextHandler) WithGroup(name string) slog.Handler { + s2 := s.clone() + s2.groups = append(s2.groups, name) + return s2 +} diff --git a/lib/utils/slices/slices.go b/lib/utils/slices/slices.go index c3d3fcb0a4496..3c33c0baf8710 100644 --- a/lib/utils/slices/slices.go +++ b/lib/utils/slices/slices.go @@ -18,21 +18,42 @@ package slices -import ( - "cmp" - "slices" -) - // FilterMapUnique applies a function to all elements of a slice and collects them. // The function returns the value to collect and whether the current element should be included. -// Returned values are sorted and deduplicated. -func FilterMapUnique[T any, S cmp.Ordered](ts []T, fn func(T) (s S, include bool)) []S { +// Returned values are deduplicated. +func FilterMapUnique[T any, S comparable](ts []T, fn func(T) (s S, include bool)) []S { ss := make([]S, 0, len(ts)) + seen := make(map[S]struct{}, len(ts)) for _, t := range ts { if s, include := fn(t); include { - ss = append(ss, s) + if _, found := seen[s]; !found { + seen[s] = struct{}{} + ss = append(ss, s) + } + } + } + + return ss +} + +// ToPointers converts a slice of values to a slice of pointers to those values +func ToPointers[T any](in []T) []*T { + out := make([]*T, len(in)) + for i := range in { + out[i] = &in[i] + } + return out +} + +// FromPointers converts a slice of pointers to values to a slice of values. +// Nil pointers are converted to zero-values. +func FromPointers[T any](in []*T) []T { + out := make([]T, len(in)) + for i := range in { + if in[i] == nil { + continue } + out[i] = *in[i] } - slices.Sort(ss) - return slices.Compact(ss) + return out } diff --git a/lib/utils/slices/slices_test.go b/lib/utils/slices/slices_test.go index 6591880c0ff27..1f031d21eca49 100644 --- a/lib/utils/slices/slices_test.go +++ b/lib/utils/slices/slices_test.go @@ -19,11 +19,16 @@ package slices import ( + "strings" "testing" "github.com/stretchr/testify/require" ) +type aType struct { + fieldA string +} + func TestFilterMapUnique(t *testing.T) { for _, tt := range []struct { name string @@ -69,4 +74,21 @@ func TestFilterMapUnique(t *testing.T) { require.Equal(t, tt.expected, got) }) } + + t.Run("structs", func(t *testing.T) { + input := []aType{ + {"+a"}, + {"+b"}, + {"+b"}, + {"b"}, + {"z"}, + } + withPlusPrefix := func(a aType) (string, bool) { + return a.fieldA, strings.HasPrefix(a.fieldA, "+") + } + got := FilterMapUnique(input, withPlusPrefix) + + expected := []string{"+a", "+b"} + require.Equal(t, expected, got) + }) } diff --git a/lib/utils/typical/cached_parser.go b/lib/utils/typical/cached_parser.go index 27b61defd31bf..e8e11347d87a1 100644 --- a/lib/utils/typical/cached_parser.go +++ b/lib/utils/typical/cached_parser.go @@ -90,7 +90,7 @@ func (c *CachedParser[TEnv, TResult]) Parse(expression string) (Expression[TEnv, } if evicted := c.cache.Add(expression, parsedExpr); evicted && c.evictedCount.Add(1)%logAfterEvictions == 0 { c.logger.InfoContext(context.Background(), "entries have been evicted from the predicate expression cache, consider increasing TELEPORT_EXPRESSION_CACHE_SIZE", - "eviticiton_count", logAfterEvictions, + "eviction_count", logAfterEvictions, ) } return parsedExpr, nil diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 9a7a0ac625be8..d8d620ce73ac6 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -657,10 +657,10 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*APIHandler, error) { h.logger.WarnContext(r.Context(), "Failed to generate CSRF token", "error", err) } - session, err := h.authenticateWebSession(w, r) - if err != nil { - h.logger.DebugContext(r.Context(), "Could not authenticate", "error", err) - } + // Ignore errors here, as unauthenticated requests for index.html are common - the user might + // not have logged in yet, or their session may have expired. + // The web app will show them the login page in this case. + session, _ := h.authenticateWebSession(w, r) session.XCSRF = csrfToken httplib.SetNoCacheHeaders(w.Header()) @@ -724,7 +724,7 @@ type webSession struct { } func (h *Handler) authenticateWebSession(w http.ResponseWriter, r *http.Request) (webSession, error) { - ctx, err := h.AuthenticateRequest(w, r, false) + ctx, err := h.AuthenticateRequest(w, r, false /* validate bearer token */) if err != nil { return webSession{}, trace.Wrap(err) } @@ -1997,6 +1997,7 @@ func (h *Handler) githubLoginWeb(w http.ResponseWriter, r *http.Request, p httpr } response, err := h.cfg.ProxyClient.CreateGithubAuthRequest(r.Context(), types.GithubAuthRequest{ + CSRFToken: req.CSRFToken, ConnectorID: req.ConnectorID, CreateWebSession: true, ClientRedirectURL: req.ClientRedirectURL, @@ -4398,6 +4399,8 @@ type ClusterWebsocketHandler func(w http.ResponseWriter, r *http.Request, p http // WithClusterAuth wraps a ClusterHandler to ensure that a request is authenticated to this proxy // (the same as WithAuth), as well as to grab the remoteSite (which can represent this local cluster // or a remote trusted cluster) as specified by the ":site" url parameter. +// +// WithClusterAuth also provides CSRF protection by requiring the bearer token to be present. func (h *Handler) WithClusterAuth(fn ClusterHandler) httprouter.Handle { return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { sctx, site, err := h.authenticateRequestWithCluster(w, r, p) @@ -4693,6 +4696,7 @@ func (h *Handler) WithMetaRedirect(fn redirectHandlerFunc) httprouter.Handle { // WithAuth ensures that a request is authenticated. // Authenticated requests require both a session cookie as well as a bearer token. +// WithAuth also provides CSRF protection by requiring the bearer token to be present. func (h *Handler) WithAuth(fn ContextHandler) httprouter.Handle { return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { sctx, err := h.AuthenticateRequest(w, r, true /* check bearer token */) @@ -4705,6 +4709,10 @@ func (h *Handler) WithAuth(fn ContextHandler) httprouter.Handle { // WithSession ensures that the request provides a session cookie. // It does not check for a bearer token. +// +// WithSession does not provide CSRF protection, so it should only +// be used for non-state-changing requests or when other CSRF mitigations +// are applied. func (h *Handler) WithSession(fn ContextHandler) httprouter.Handle { return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { sctx, err := h.AuthenticateRequest(w, r, false /* check bearer token */) @@ -5045,6 +5053,8 @@ type SSORequestParams struct { // ConnectorID identifies the SSO connector to use to log in, from // the connector_id query parameter. ConnectorID string + // CSRFToken is used to protect against login-CSRF in SSO flows. + CSRFToken string } // ParseSSORequestParams extracts the SSO request parameters from an http.Request, @@ -5077,9 +5087,15 @@ func ParseSSORequestParams(r *http.Request) (*SSORequestParams, error) { return nil, trace.BadParameter("missing connector_id query parameter") } + csrfToken, err := csrf.ExtractTokenFromCookie(r) + if err != nil { + return nil, trace.Wrap(err) + } + return &SSORequestParams{ ClientRedirectURL: clientRedirectURL, ConnectorID: connectorID, + CSRFToken: csrfToken, }, nil } @@ -5109,6 +5125,11 @@ type SSOCallbackResponse struct { // error is returned. func SSOSetWebSessionAndRedirectURL(w http.ResponseWriter, r *http.Request, response *SSOCallbackResponse, verifyCSRF bool) error { if verifyCSRF { + // Make sure that the CSRF token provided in this request matches + // the token that was used to initiate this SSO attempt. + // + // This ensures that an attacker cannot perform a login CSRF attack + // in order to get the victim to log in to the incorrect account. if err := csrf.VerifyToken(response.CSRFToken, r); err != nil { return trace.Wrap(err) } diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 2816f2f92b7bb..b220d593bab5b 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -117,6 +117,7 @@ import ( "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/events/eventstest" "github.com/gravitational/teleport/lib/httplib" + "github.com/gravitational/teleport/lib/httplib/csrf" "github.com/gravitational/teleport/lib/inventory" kubeproxy "github.com/gravitational/teleport/lib/kube/proxy" "github.com/gravitational/teleport/lib/limiter" @@ -6100,6 +6101,8 @@ func TestChangeUserAuthentication_settingDefaultClusterAuthPreference(t *testing func TestParseSSORequestParams(t *testing.T) { t.Parallel() + token := "someMeaninglessTokenString" + tests := []struct { name, url string wantErr bool @@ -6111,6 +6114,7 @@ func TestParseSSORequestParams(t *testing.T) { expected: &SSORequestParams{ ClientRedirectURL: "https://localhost:8080/web/cluster/im-a-cluster-name/nodes?search=tunnel&sort=hostname:asc", ConnectorID: "oidc", + CSRFToken: token, }, }, { @@ -6119,6 +6123,7 @@ func TestParseSSORequestParams(t *testing.T) { expected: &SSORequestParams{ ClientRedirectURL: "https://localhost:8080/web/cluster/im-a-cluster-name/nodes?search=tunnel&sort=hostname:asc", ConnectorID: "github", + CSRFToken: token, }, }, { @@ -6127,6 +6132,7 @@ func TestParseSSORequestParams(t *testing.T) { expected: &SSORequestParams{ ClientRedirectURL: "https://localhost:8080/web/cluster/im-a-cluster-name/apps?query=search(%22watermelon%22%2C%20%22this%22)%20%26%26%20labels%5B%22unique-id%22%5D%20%3D%3D%20%22hi%22&sort=name:asc", ConnectorID: "saml", + CSRFToken: token, }, }, { @@ -6146,6 +6152,11 @@ func TestParseSSORequestParams(t *testing.T) { req, err := http.NewRequest("", tc.url, nil) require.NoError(t, err) + req.AddCookie(&http.Cookie{ + Name: csrf.CookieName, + Value: token, + }) + params, err := ParseSSORequestParams(req) switch { diff --git a/lib/web/databases.go b/lib/web/databases.go index f84beeb4ef7a8..57ad42e67b0d3 100644 --- a/lib/web/databases.go +++ b/lib/web/databases.go @@ -466,7 +466,6 @@ func (h *Handler) dbConnect( } stream := terminal.NewStream(ctx, terminal.StreamConfig{WS: ws}) - defer stream.Close() replConn, alpnConn := net.Pipe() sess := &databaseInteractiveSession{ @@ -486,11 +485,22 @@ func (h *Handler) dbConnect( } defer sess.Close() + // Don't close the terminal stream on session error, as it would also + // cause the underlying connection to be closed. This will prevent the + // middleware from properly writing the error into the WebSocket connection. if err := sess.Run(); err != nil { log.ErrorContext(ctx, "Database interactive session exited with error", "error", err) return nil, trace.Wrap(err) } + // TODO(gabrielcorado): Right now, if we send a close message the UI closes + // the terminal without giving the chance for users to review the session. + // Once this gets solved, we should send the close message here. + + if err := stream.Close(); err != nil { + log.ErrorContext(ctx, "Unable to close web socket terminal stream", "error", err) + } + return nil, nil } @@ -617,7 +627,7 @@ func (s *databaseInteractiveSession) Run() error { func (s *databaseInteractiveSession) Close() error { s.replConn.Close() - return s.ws.Close() + return nil } // issueCerts performs the MFA (if required) and generate the user session diff --git a/lib/web/desktop.go b/lib/web/desktop.go index 63a6bdee24508..29534c637276c 100644 --- a/lib/web/desktop.go +++ b/lib/web/desktop.go @@ -225,7 +225,7 @@ func (h *Handler) createDesktopConnection( return sendTDPError(err) } for _, msg := range withheld { - log.DebugContext(ctx, "Sending withheld message", "messgage", logutils.TypeAttr(msg)) + log.DebugContext(ctx, "Sending withheld message", "message", logutils.TypeAttr(msg)) if err := tdpConn.WriteMessage(msg); err != nil { return sendTDPError(err) } diff --git a/lib/web/integrations.go b/lib/web/integrations.go index 6705afcf8dc48..d0feb628b8a0c 100644 --- a/lib/web/integrations.go +++ b/lib/web/integrations.go @@ -20,6 +20,7 @@ package web import ( "context" + "log/slog" "net/http" "net/url" "slices" @@ -220,7 +221,14 @@ func (h *Handler) integrationStats(w http.ResponseWriter, r *http.Request, p htt return nil, trace.Wrap(err) } - summary, err := collectAWSOIDCAutoDiscoverStats(r.Context(), ig, clt.DiscoveryConfigClient()) + req := collectIntegrationStatsRequest{ + logger: h.logger, + integration: ig, + discoveryConfigLister: clt.DiscoveryConfigClient(), + databaseGetter: clt, + awsOIDCClient: clt.IntegrationAWSOIDCClient(), + } + summary, err := collectIntegrationStats(r.Context(), req) if err != nil { return nil, trace.Wrap(err) } @@ -228,44 +236,46 @@ func (h *Handler) integrationStats(w http.ResponseWriter, r *http.Request, p htt return summary, nil } -func collectAWSOIDCAutoDiscoverStats( - ctx context.Context, - integration types.Integration, - clt interface { - ListDiscoveryConfigs(ctx context.Context, pageSize int, nextToken string) ([]*discoveryconfig.DiscoveryConfig, string, error) - }, -) (ui.IntegrationWithSummary, error) { - var ret ui.IntegrationWithSummary +type collectIntegrationStatsRequest struct { + logger *slog.Logger + integration types.Integration + discoveryConfigLister discoveryConfigLister + databaseGetter databaseGetter + awsOIDCClient deployedDatabaseServiceLister +} - uiIg, err := ui.MakeIntegration(integration) +func collectIntegrationStats(ctx context.Context, req collectIntegrationStatsRequest) (*ui.IntegrationWithSummary, error) { + ret := &ui.IntegrationWithSummary{} + + uiIg, err := ui.MakeIntegration(req.integration) if err != nil { - return ret, err + return nil, err } ret.Integration = uiIg var nextPage string for { - discoveryConfigs, nextToken, err := clt.ListDiscoveryConfigs(ctx, 0, nextPage) + discoveryConfigs, nextToken, err := req.discoveryConfigLister.ListDiscoveryConfigs(ctx, 0, nextPage) if err != nil { - return ret, trace.Wrap(err) + return nil, trace.Wrap(err) } for _, dc := range discoveryConfigs { - discoveredResources, ok := dc.Status.IntegrationDiscoveredResources[integration.GetName()] + discoveredResources, ok := dc.Status.IntegrationDiscoveredResources[req.integration.GetName()] if !ok { continue } - if matchers := rulesWithIntegration(dc, types.AWSMatcherEC2, integration.GetName()); matchers != 0 { + if matchers := rulesWithIntegration(dc, types.AWSMatcherEC2, req.integration.GetName()); matchers != 0 { ret.AWSEC2.RulesCount += matchers mergeResourceTypeSummary(&ret.AWSEC2, dc.Status.LastSyncTime, discoveredResources.AwsEc2) } - if matchers := rulesWithIntegration(dc, types.AWSMatcherRDS, integration.GetName()); matchers != 0 { + if matchers := rulesWithIntegration(dc, types.AWSMatcherRDS, req.integration.GetName()); matchers != 0 { ret.AWSRDS.RulesCount += matchers mergeResourceTypeSummary(&ret.AWSRDS, dc.Status.LastSyncTime, discoveredResources.AwsRds) } - if matchers := rulesWithIntegration(dc, types.AWSMatcherEKS, integration.GetName()); matchers != 0 { + if matchers := rulesWithIntegration(dc, types.AWSMatcherEKS, req.integration.GetName()); matchers != 0 { ret.AWSEKS.RulesCount += matchers mergeResourceTypeSummary(&ret.AWSEKS, dc.Status.LastSyncTime, discoveredResources.AwsEks) } @@ -277,8 +287,17 @@ func collectAWSOIDCAutoDiscoverStats( nextPage = nextToken } - // TODO(marco): add total number of ECS Database Services. - ret.AWSRDS.ECSDatabaseServiceCount = 0 + regions, err := fetchRelevantAWSRegions(ctx, req.databaseGetter, req.discoveryConfigLister) + if err != nil { + return nil, trace.Wrap(err) + } + + services, err := listDeployedDatabaseServices(ctx, req.logger, req.integration.GetName(), regions, req.awsOIDCClient) + if err != nil { + return nil, trace.Wrap(err) + } + + ret.AWSRDS.ECSDatabaseServiceCount = len(services) return ret, nil } diff --git a/lib/web/integrations_test.go b/lib/web/integrations_test.go index 9719de251d0ec..0ffd4a139c3f7 100644 --- a/lib/web/integrations_test.go +++ b/lib/web/integrations_test.go @@ -27,12 +27,15 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" + "github.com/gravitational/teleport/api/client/proto" discoveryconfigv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1" + integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/discoveryconfig" "github.com/gravitational/teleport/api/types/header" "github.com/gravitational/teleport/lib/services" libui "github.com/gravitational/teleport/lib/ui" + "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/web/ui" ) @@ -97,6 +100,8 @@ func TestIntegrationsCreateWithAudience(t *testing.T) { func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) { ctx := context.Background() + logger := utils.NewSlogLoggerForTests() + integrationName := "my-integration" integration, err := types.NewIntegrationAWSOIDC( types.Metadata{Name: integrationName}, @@ -106,14 +111,33 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) { ) require.NoError(t, err) + deployedServiceCommand := buildCommandDeployedDatabaseService(t, true, types.Labels{"vpc": []string{"vpc1", "vpc2"}}) + deployedDatabaseServicesClient := &mockDeployedDatabaseServices{ + integration: "my-integration", + servicesPerRegion: map[string][]*integrationv1.DeployedDatabaseService{ + "us-west-2": dummyDeployedDatabaseServices(1, deployedServiceCommand), + }, + } + t.Run("without discovery configs, returns just the integration", func(t *testing.T) { - clt := &mockDiscoveryConfigsGetter{ + clt := &mockRelevantAWSRegionsClient{ + databaseServices: &proto.ListResourcesResponse{ + Resources: []*proto.PaginatedResource{}, + }, + databases: make([]types.Database, 0), discoveryConfigs: make([]*discoveryconfig.DiscoveryConfig, 0), } - gotSummary, err := collectAWSOIDCAutoDiscoverStats(ctx, integration, clt) + req := collectIntegrationStatsRequest{ + logger: logger, + integration: integration, + discoveryConfigLister: clt, + databaseGetter: clt, + awsOIDCClient: deployedDatabaseServicesClient, + } + gotSummary, err := collectIntegrationStats(ctx, req) require.NoError(t, err) - expectedSummary := ui.IntegrationWithSummary{ + expectedSummary := &ui.IntegrationWithSummary{ Integration: &ui.Integration{ Name: integrationName, SubKind: "aws-oidc", @@ -145,7 +169,7 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) { Spec: discoveryconfig.Spec{AWS: []types.AWSMatcher{{ Integration: integrationName, Types: []string{"rds"}, - Regions: []string{"us-east-1", "us-east-2"}, + Regions: []string{"us-east-1", "us-east-2", "us-west-2"}, }}}, Status: discoveryconfig.Status{ LastSyncTime: syncTime, @@ -173,17 +197,26 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) { }, }, } - clt := &mockDiscoveryConfigsGetter{ + clt := &mockRelevantAWSRegionsClient{ discoveryConfigs: []*discoveryconfig.DiscoveryConfig{ dcForEC2, dcForRDS, dcForEKS, }, + databaseServices: &proto.ListResourcesResponse{}, + databases: make([]types.Database, 0), } - gotSummary, err := collectAWSOIDCAutoDiscoverStats(ctx, integration, clt) + req := collectIntegrationStatsRequest{ + logger: logger, + integration: integration, + discoveryConfigLister: clt, + databaseGetter: clt, + awsOIDCClient: deployedDatabaseServicesClient, + } + gotSummary, err := collectIntegrationStats(ctx, req) require.NoError(t, err) - expectedSummary := ui.IntegrationWithSummary{ + expectedSummary := &ui.IntegrationWithSummary{ Integration: &ui.Integration{ Name: integrationName, SubKind: "aws-oidc", @@ -197,10 +230,11 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) { DiscoverLastSync: &syncTime, }, AWSRDS: ui.ResourceTypeSummary{ - RulesCount: 2, + RulesCount: 3, ResourcesFound: 2, ResourcesEnrollmentFailed: 1, ResourcesEnrollmentSuccess: 1, + ECSDatabaseServiceCount: 1, DiscoverLastSync: &syncTime, }, AWSEKS: ui.ResourceTypeSummary{ @@ -220,7 +254,7 @@ func TestCollectAutoDiscoveryRules(t *testing.T) { integrationName := "my-integration" t.Run("without discovery configs, returns no rules", func(t *testing.T) { - clt := &mockDiscoveryConfigsGetter{ + clt := &mockRelevantAWSRegionsClient{ discoveryConfigs: make([]*discoveryconfig.DiscoveryConfig, 0), } @@ -287,7 +321,7 @@ func TestCollectAutoDiscoveryRules(t *testing.T) { Tags: types.Labels{"*": []string{"*"}}, }}}, } - clt := &mockDiscoveryConfigsGetter{ + clt := &mockRelevantAWSRegionsClient{ discoveryConfigs: []*discoveryconfig.DiscoveryConfig{ dcForEC2, dcForRDS, @@ -350,11 +384,3 @@ func TestCollectAutoDiscoveryRules(t *testing.T) { require.ElementsMatch(t, expectedRules, got.Rules) }) } - -type mockDiscoveryConfigsGetter struct { - discoveryConfigs []*discoveryconfig.DiscoveryConfig -} - -func (m *mockDiscoveryConfigsGetter) ListDiscoveryConfigs(ctx context.Context, pageSize int, nextToken string) ([]*discoveryconfig.DiscoveryConfig, string, error) { - return m.discoveryConfigs, "", nil -} diff --git a/package.json b/package.json index 68e1b1f60eea5..e8b5ab7246759 100644 --- a/package.json +++ b/package.json @@ -35,50 +35,50 @@ }, "devDependencies": { "@gravitational/build": "workspace:*", - "@storybook/addon-actions": "^8.4.6", - "@storybook/addon-controls": "^8.4.6", - "@storybook/addon-toolbars": "^8.4.6", - "@storybook/components": "^8.4.6", - "@storybook/preview-api": "^8.4.6", - "@storybook/react": "^8.4.6", - "@storybook/react-vite": "^8.4.6", - "@storybook/test-runner": "^0.20.0", + "@storybook/addon-actions": "^8.4.7", + "@storybook/addon-controls": "^8.4.7", + "@storybook/addon-toolbars": "^8.4.7", + "@storybook/components": "^8.4.7", + "@storybook/preview-api": "^8.4.7", + "@storybook/react": "^8.4.7", + "@storybook/react-vite": "^8.4.7", + "@storybook/test-runner": "^0.21.0", "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.0.0", + "@testing-library/react": "^16.1.0", "@testing-library/user-event": "^14.5.2", "@types/history": "^4.7.11", "@types/jest": "^29.5.14", - "@types/node": "^20.17.9", + "@types/node": "^20.17.11", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/react-highlight": "^0.12.8", "@types/react-router": "^5.1.20", "@types/react-router-dom": "^5.1.1", - "@types/react-transition-group": "^4.4.11", + "@types/react-transition-group": "^4.4.12", "@types/wicg-file-system-access": "^2023.10.5", - "cspell": "^8.16.1", + "cspell": "^8.17.1", "jest": "^29.7.0", "jsdom-testing-mocks": "^1.13.1", - "msw": "^2.6.6", + "msw": "^2.7.0", "msw-storybook-addon": "^2.0.4", - "playwright": "^1.49.0", - "prettier": "^3.4.1", + "playwright": "^1.49.1", + "prettier": "^3.4.2", "react-select-event": "^5.5.1", - "storybook": "^8.4.6", + "storybook": "^8.4.7", "typescript": "^5.7.2", - "vite": "^5.4.8" + "vite": "^6.0.6" }, "dependencies": { - "@codemirror/autocomplete": "^6.18.3", + "@codemirror/autocomplete": "^6.18.4", "@codemirror/lang-sql": "^6.8.0", - "@codemirror/view": "^6.35.0", - "@grpc/grpc-js": "1.12.2", + "@codemirror/view": "^6.36.1", + "@grpc/grpc-js": "1.12.5", "@lezer/highlight": "^1.2.1", "@nivo/bar": "^0.88.0", "@protobuf-ts/runtime": "^2.9.4", "@protobuf-ts/runtime-rpc": "^2.9.4", - "@uiw/codemirror-themes": "^4.23.6", - "@uiw/react-codemirror": "^4.23.6", + "@uiw/codemirror-themes": "^4.23.7", + "@uiw/react-codemirror": "^4.23.7", "d3-scale": "^4.0.2", "d3-time-format": "^4.1.0", "date-fns": "^2.28.0", @@ -91,7 +91,7 @@ "react-is": "^18.3.1", "react-router": "5.3.4", "react-router-dom": "5.3.4", - "react-select": "^5.8.3", + "react-select": "^5.9.0", "react-transition-group": "^4.4.5", "styled-components": "^6.1.13", "tslib": "^2.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb96cf6b79d40..7a8312c994213 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,17 +14,17 @@ importers: .: dependencies: '@codemirror/autocomplete': - specifier: ^6.18.3 - version: 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3) + specifier: ^6.18.4 + version: 6.18.4 '@codemirror/lang-sql': specifier: ^6.8.0 - version: 6.8.0(@codemirror/view@6.35.0) + version: 6.8.0 '@codemirror/view': - specifier: ^6.35.0 - version: 6.35.0 + specifier: ^6.36.1 + version: 6.36.1 '@grpc/grpc-js': - specifier: 1.12.2 - version: 1.12.2 + specifier: 1.12.5 + version: 1.12.5 '@lezer/highlight': specifier: ^1.2.1 version: 1.2.1 @@ -38,11 +38,11 @@ importers: specifier: ^2.9.4 version: 2.9.4 '@uiw/codemirror-themes': - specifier: ^4.23.6 - version: 4.23.6(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0) + specifier: ^4.23.7 + version: 4.23.7(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1) '@uiw/react-codemirror': - specifier: ^4.23.6 - version: 4.23.6(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^4.23.7 + version: 4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) d3-scale: specifier: ^4.0.2 version: 4.0.2 @@ -80,8 +80,8 @@ importers: specifier: 5.3.4 version: 5.3.4(react@18.3.1) react-select: - specifier: ^5.8.3 - version: 5.8.3(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^5.9.0 + version: 5.9.0(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-transition-group: specifier: ^4.4.5 version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -99,35 +99,35 @@ importers: specifier: workspace:* version: link:web/packages/build '@storybook/addon-actions': - specifier: ^8.4.6 - version: 8.4.6(storybook@8.4.6(prettier@3.4.1)) + specifier: ^8.4.7 + version: 8.4.7(storybook@8.4.7(prettier@3.4.2)) '@storybook/addon-controls': - specifier: ^8.4.6 - version: 8.4.6(storybook@8.4.6(prettier@3.4.1)) + specifier: ^8.4.7 + version: 8.4.7(storybook@8.4.7(prettier@3.4.2)) '@storybook/addon-toolbars': - specifier: ^8.4.6 - version: 8.4.6(storybook@8.4.6(prettier@3.4.1)) + specifier: ^8.4.7 + version: 8.4.7(storybook@8.4.7(prettier@3.4.2)) '@storybook/components': - specifier: ^8.4.6 - version: 8.4.6(storybook@8.4.6(prettier@3.4.1)) + specifier: ^8.4.7 + version: 8.4.7(storybook@8.4.7(prettier@3.4.2)) '@storybook/preview-api': - specifier: ^8.4.6 - version: 8.4.6(storybook@8.4.6(prettier@3.4.1)) + specifier: ^8.4.7 + version: 8.4.7(storybook@8.4.7(prettier@3.4.2)) '@storybook/react': - specifier: ^8.4.6 - version: 8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.6(prettier@3.4.1))(typescript@5.7.2) + specifier: ^8.4.7 + version: 8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2) '@storybook/react-vite': - specifier: ^8.4.6 - version: 8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.23.0)(storybook@8.4.6(prettier@3.4.1))(typescript@5.7.2)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)) + specifier: ^8.4.7 + version: 8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.29.1)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)) '@storybook/test-runner': - specifier: ^0.20.0 - version: 0.20.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0)(storybook@8.4.6(prettier@3.4.1)) + specifier: ^0.21.0 + version: 0.21.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0)(storybook@8.4.7(prettier@3.4.2)) '@testing-library/jest-dom': specifier: ^6.6.3 version: 6.6.3 '@testing-library/react': - specifier: ^16.0.0 - version: 16.0.1(@testing-library/dom@10.1.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^16.1.0 + version: 16.1.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@testing-library/user-event': specifier: ^14.5.2 version: 14.5.2(@testing-library/dom@10.1.0) @@ -138,8 +138,8 @@ importers: specifier: ^29.5.14 version: 29.5.14 '@types/node': - specifier: ^20.17.9 - version: 20.17.9 + specifier: ^20.17.11 + version: 20.17.11 '@types/react': specifier: ^18.3.12 version: 18.3.12 @@ -156,44 +156,44 @@ importers: specifier: ^5.1.1 version: 5.3.3 '@types/react-transition-group': - specifier: ^4.4.11 - version: 4.4.11 + specifier: ^4.4.12 + version: 4.4.12(@types/react@18.3.12) '@types/wicg-file-system-access': specifier: ^2023.10.5 version: 2023.10.5 cspell: - specifier: ^8.16.1 - version: 8.16.1 + specifier: ^8.17.1 + version: 8.17.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + version: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) jsdom-testing-mocks: specifier: ^1.13.1 version: 1.13.1 msw: - specifier: ^2.6.6 - version: 2.6.6(@types/node@20.17.9)(typescript@5.7.2) + specifier: ^2.7.0 + version: 2.7.0(@types/node@20.17.11)(typescript@5.7.2) msw-storybook-addon: specifier: ^2.0.4 - version: 2.0.4(msw@2.6.6(@types/node@20.17.9)(typescript@5.7.2)) + version: 2.0.4(msw@2.7.0(@types/node@20.17.11)(typescript@5.7.2)) playwright: - specifier: ^1.49.0 - version: 1.49.0 + specifier: ^1.49.1 + version: 1.49.1 prettier: - specifier: ^3.4.1 - version: 3.4.1 + specifier: ^3.4.2 + version: 3.4.2 react-select-event: specifier: ^5.5.1 version: 5.5.1 storybook: - specifier: ^8.4.6 - version: 8.4.6(prettier@3.4.1) + specifier: ^8.4.7 + version: 8.4.7(prettier@3.4.2) typescript: specifier: ^5.7.2 version: 5.7.2 vite: - specifier: ^5.4.8 - version: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + specifier: ^6.0.6 + version: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) e/web/teleport: {} @@ -206,53 +206,53 @@ importers: specifier: ^7.26.0 version: 7.26.0(@babel/core@7.26.0) '@babel/preset-react': - specifier: ^7.25.9 - version: 7.25.9(@babel/core@7.26.0) + specifier: ^7.26.3 + version: 7.26.3(@babel/core@7.26.0) '@babel/preset-typescript': specifier: ^7.26.0 version: 7.26.0(@babel/core@7.26.0) '@eslint/js': - specifier: ^9.16.0 - version: 9.16.0 + specifier: ^9.17.0 + version: 9.17.0 + '@ianvs/prettier-plugin-sort-imports': + specifier: ^4.4.0 + version: 4.4.0(prettier@3.4.2) '@swc/core': - specifier: ^1.9.3 - version: 1.9.3 + specifier: ^1.10.4 + version: 1.10.4 '@swc/plugin-styled-components': - specifier: ^5.0.0 - version: 5.0.0 + specifier: ^6.0.2 + version: 6.0.2 '@types/jsdom': specifier: ^21.1.7 version: 21.1.7 '@vitejs/plugin-react-swc': specifier: ^3.7.2 - version: 3.7.2(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)) + version: 3.7.2(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)) babel-plugin-styled-components: specifier: ^2.1.4 version: 2.1.4(@babel/core@7.26.0)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) eslint: - specifier: ^9.16.0 - version: 9.16.0 - eslint-plugin-import: - specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0) + specifier: ^9.17.0 + version: 9.17.0 eslint-plugin-jest: - specifier: ^28.9.0 - version: 28.9.0(@typescript-eslint/eslint-plugin@8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0))(typescript@5.7.2) + specifier: ^28.10.0 + version: 28.10.0(@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0))(typescript@5.7.2) eslint-plugin-jest-dom: specifier: ^5.5.0 - version: 5.5.0(@testing-library/dom@10.1.0)(eslint@9.16.0) + version: 5.5.0(@testing-library/dom@10.1.0)(eslint@9.17.0) eslint-plugin-react: - specifier: ^7.37.2 - version: 7.37.2(eslint@9.16.0) + specifier: ^7.37.3 + version: 7.37.3(eslint@9.17.0) eslint-plugin-react-hooks: - specifier: ^5.0.0 - version: 5.0.0(eslint@9.16.0) + specifier: ^5.1.0 + version: 5.1.0(eslint@9.17.0) eslint-plugin-testing-library: specifier: ^7.1.1 - version: 7.1.1(eslint@9.16.0)(typescript@5.7.2) + version: 7.1.1(eslint@9.17.0)(typescript@5.7.2) globals: - specifier: ^15.13.0 - version: 15.13.0 + specifier: ^15.14.0 + version: 15.14.0 jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 @@ -263,17 +263,17 @@ importers: specifier: ^25.0.1 version: 25.0.1 rollup-plugin-visualizer: - specifier: ^5.12.0 - version: 5.12.0(rollup@4.23.0) + specifier: ^5.13.1 + version: 5.13.1(rollup@4.29.1) typescript-eslint: - specifier: ^8.18.0 - version: 8.18.0(eslint@9.16.0)(typescript@5.7.2) + specifier: ^8.19.0 + version: 8.19.0(eslint@9.17.0)(typescript@5.7.2) vite-plugin-wasm: - specifier: ^3.3.0 - version: 3.3.0(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)) + specifier: ^3.4.1 + version: 3.4.1(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)) vite-tsconfig-paths: - specifier: ^5.1.3 - version: 5.1.3(typescript@5.7.2)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)) + specifier: ^5.1.4 + version: 5.1.4(typescript@5.7.2)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)) web/packages/design: dependencies: @@ -306,8 +306,8 @@ importers: specifier: ^0.15.0 version: 0.15.0(@xterm/xterm@5.5.0) ace-builds: - specifier: 1.36.5 - version: 1.36.5 + specifier: 1.37.1 + version: 1.37.1 events: specifier: 3.3.0 version: 3.3.0 @@ -327,41 +327,41 @@ importers: specifier: 1.9.0 version: 1.9.0 '@opentelemetry/context-zone': - specifier: 1.28.0 - version: 1.28.0(@opentelemetry/api@1.9.0) + specifier: 1.30.0 + version: 1.30.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': - specifier: ^1.28.0 - version: 1.28.0(@opentelemetry/api@1.9.0) + specifier: ^1.30.0 + version: 1.30.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-http': - specifier: 0.55.0 - version: 0.55.0(@opentelemetry/api@1.9.0) + specifier: 0.57.0 + version: 0.57.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': - specifier: 0.55.0 - version: 0.55.0(@opentelemetry/api@1.9.0) + specifier: 0.57.0 + version: 0.57.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-document-load': - specifier: 0.42.0 - version: 0.42.0(@opentelemetry/api@1.9.0) + specifier: 0.44.0 + version: 0.44.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-fetch': - specifier: 0.55.0 - version: 0.55.0(@opentelemetry/api@1.9.0) + specifier: 0.57.0 + version: 0.57.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-user-interaction': - specifier: 0.42.0 - version: 0.42.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7) + specifier: 0.44.0 + version: 0.44.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7) '@opentelemetry/instrumentation-xml-http-request': - specifier: 0.55.0 - version: 0.55.0(@opentelemetry/api@1.9.0) + specifier: 0.57.0 + version: 0.57.0(@opentelemetry/api@1.9.0) '@opentelemetry/propagator-b3': - specifier: 1.28.0 - version: 1.28.0(@opentelemetry/api@1.9.0) + specifier: 1.30.0 + version: 1.30.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': - specifier: 1.28.0 - version: 1.28.0(@opentelemetry/api@1.9.0) + specifier: 1.30.0 + version: 1.30.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': - specifier: 1.28.0 - version: 1.28.0(@opentelemetry/api@1.9.0) + specifier: 1.30.0 + version: 1.30.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-web': - specifier: 1.28.0 - version: 1.28.0(@opentelemetry/api@1.9.0) + specifier: 1.30.0 + version: 1.30.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': specifier: 1.28.0 version: 1.28.0 @@ -412,8 +412,8 @@ importers: web/packages/teleterm: dependencies: '@grpc/grpc-js': - specifier: 1.12.2 - version: 1.12.2 + specifier: 1.12.5 + version: 1.12.5 '@types/which': specifier: ^3.0.4 version: 3.0.4 @@ -456,7 +456,7 @@ importers: version: link:../shared '@protobuf-ts/grpc-transport': specifier: ^2.9.4 - version: 2.9.4(@grpc/grpc-js@1.12.2) + version: 2.9.4(@grpc/grpc-js@1.12.5) '@types/node-forge': specifier: ^1.3.11 version: 1.3.11 @@ -480,7 +480,7 @@ importers: version: 25.1.8(electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8)) electron-vite: specifier: ^2.3.0 - version: 2.3.0(@swc/core@1.9.3)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)) + version: 2.3.0(@swc/core@1.10.4)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)) events: specifier: 3.3.0 version: 3.3.0 @@ -492,19 +492,19 @@ importers: version: 2.5.2 react-dnd: specifier: ^14.0.4 - version: 14.0.5(@types/node@20.17.9)(@types/react@18.3.12)(react@18.3.1) + version: 14.0.5(@types/node@20.17.11)(@types/react@18.3.12)(react@18.3.1) react-dnd-html5-backend: specifier: ^14.0.2 version: 14.1.0 whatwg-url: - specifier: ^14.0.0 - version: 14.0.0 + specifier: ^14.1.0 + version: 14.1.0 zod: - specifier: ^3.23.8 - version: 3.23.8 + specifier: ^3.24.1 + version: 3.24.1 zod-to-json-schema: - specifier: ^3.23.5 - version: 3.23.5(zod@3.23.8) + specifier: ^3.24.1 + version: 3.24.1(zod@3.24.1) packages: @@ -1114,8 +1114,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/preset-react@7.25.9': - resolution: {integrity: sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==} + '@babel/preset-react@7.26.3': + resolution: {integrity: sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1154,13 +1154,8 @@ packages: '@bundled-es-modules/tough-cookie@0.1.6': resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} - '@codemirror/autocomplete@6.18.3': - resolution: {integrity: sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==} - peerDependencies: - '@codemirror/language': ^6.0.0 - '@codemirror/state': ^6.0.0 - '@codemirror/view': ^6.0.0 - '@lezer/common': ^1.0.0 + '@codemirror/autocomplete@6.18.4': + resolution: {integrity: sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA==} '@codemirror/commands@6.7.1': resolution: {integrity: sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==} @@ -1177,41 +1172,41 @@ packages: '@codemirror/search@6.5.3': resolution: {integrity: sha512-M1nGnpUTlOqp0Ywn6V30T8eFcuNFIDfx4+0ja5Wag+qQpL/HZgsIZ7FpE6qZatPziakgj+UXyZTrTUditrkwIQ==} - '@codemirror/state@6.4.1': - resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==} + '@codemirror/state@6.5.0': + resolution: {integrity: sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==} '@codemirror/theme-one-dark@6.1.2': resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==} - '@codemirror/view@6.35.0': - resolution: {integrity: sha512-I0tYy63q5XkaWsJ8QRv5h6ves7kvtrBWjBcnf/bzohFJQc5c14a1AQRdE8QpPF9eMp5Mq2FMm59TCj1gDfE7kw==} + '@codemirror/view@6.36.1': + resolution: {integrity: sha512-miD1nyT4m4uopZaDdO2uXU/LLHliKNYL9kB1C1wJHrunHLm/rpkb5QVSokqgw9hFqEZakrdlb/VGWX8aYZTslQ==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - '@cspell/cspell-bundled-dicts@8.16.1': - resolution: {integrity: sha512-EkbtoYpmiN9YPfcOoPcMnIrJBZh13mun64jPyyaYhrPPToiU5+CisZ7ZKUBGnqNaatuciMUxwIudhanQJ7Yhnw==} + '@cspell/cspell-bundled-dicts@8.17.1': + resolution: {integrity: sha512-HmkXS5uX4bk/XxsRS4Q+zRvhgRa81ddGiR2/Xfag9MIi5L5UnEJ4g21EpmIlXkMxYrTu2fp69SZFss5NfcFF9Q==} engines: {node: '>=18'} - '@cspell/cspell-json-reporter@8.16.1': - resolution: {integrity: sha512-ue1paJ2OE2BjIBQHXFMHnFqJL5xMrE/TLveOntDSCKJw7edCGP4XJA6Q0ZfUgR/ZAP3SYKNPkajEWbDTMfG+XA==} + '@cspell/cspell-json-reporter@8.17.1': + resolution: {integrity: sha512-EV9Xkh42Xw3aORvDZfxusICX91DDbqQpYdGKBdPGuhgxWOUYYZKpLXsHCmDkhruMPo2m5gDh++/OqjLRPZofKQ==} engines: {node: '>=18'} - '@cspell/cspell-pipe@8.16.1': - resolution: {integrity: sha512-6N+QZ3y65JRgGrQhZHmaBHESR+nC0J8nySGaYKclit8yk3jLZ/ORw9aoSGIj+dMPzImkNEDh+C1B1zdV4X8W6A==} + '@cspell/cspell-pipe@8.17.1': + resolution: {integrity: sha512-uhC99Ox+OH3COSgShv4fpVHiotR70dNvAOSkzRvKVRzV6IGyFnxHjmyVVPEV0dsqzVLxltwYTqFhwI+UOwm45A==} engines: {node: '>=18'} - '@cspell/cspell-resolver@8.16.1': - resolution: {integrity: sha512-CfVI2JFMwh9/n1QuU9niEONbYCX1XGKqmyCcHQUzAapSqGzbAmFrRFnvyKwNL+mmy1bxli9EZV8f5vBco26f9Q==} + '@cspell/cspell-resolver@8.17.1': + resolution: {integrity: sha512-XEK2ymTdQNgsV3ny60VkKzWskbICl4zNXh/DbxsoRXHqIRg43MXFpTNkEJ7j873EqdX7BU4opQQ+5D4stWWuhQ==} engines: {node: '>=18'} - '@cspell/cspell-service-bus@8.16.1': - resolution: {integrity: sha512-URaralJKcdHZH/Lr25L28GJo2Ub07adHPPhOL83BvmPyGkboehmz5arjNrgQFwS+IvGjHLdp5uzEJd0xyeHGdw==} + '@cspell/cspell-service-bus@8.17.1': + resolution: {integrity: sha512-2sFWQtMEWZ4tdz7bw0bAx4NaV1t0ynGfjpuKWdQppsJFKNb+ZPZZ6Ah1dC13AdRRMZaG194kDRFwzNvRaCgWkQ==} engines: {node: '>=18'} - '@cspell/cspell-types@8.16.1': - resolution: {integrity: sha512-B8bHlBaDSMDMEq++H8qO9osKUkzWUrP4CgWQyRqlXZ9EOdnJ469Tp1wghcQ7DezII3aXYrHiVKsUYY9VvjkhIg==} + '@cspell/cspell-types@8.17.1': + resolution: {integrity: sha512-NJbov7Jp57fh8addoxesjb8atg/APQfssCH5Q9uZuHBN06wEJDgs7fhfE48bU+RBViC9gltblsYZzZZQKzHYKg==} engines: {node: '>=18'} '@cspell/dict-ada@4.0.5': @@ -1226,8 +1221,8 @@ packages: '@cspell/dict-bash@4.1.8': resolution: {integrity: sha512-I2CM2pTNthQwW069lKcrVxchJGMVQBzru2ygsHCwgidXRnJL/NTjAPOFTxN58Jc1bf7THWghfEDyKX/oyfc0yg==} - '@cspell/dict-companies@3.1.7': - resolution: {integrity: sha512-ncVs/efuAkP1/tLDhWbXukBjgZ5xOUfe03neHMWsE8zvXXc5+Lw6TX5jaJXZLOoES/f4j4AhRE20jsPCF5pm+A==} + '@cspell/dict-companies@3.1.12': + resolution: {integrity: sha512-99FxBNdLOQc3nVQ663Xh7JqDLbIy/AdqOecQ5bk3HpmXpSkoDvTT7XCUU5nQZvmFBrrQlXFKlRRYjLfTEOUDdA==} '@cspell/dict-cpp@6.0.2': resolution: {integrity: sha512-yw5eejWvY4bAnc6LUA44m4WsFwlmgPt2uMSnO7QViGMBDuoeopMma4z9XYvs4lSjTi8fIJs/A1YDfM9AVzb8eg==} @@ -1283,8 +1278,8 @@ packages: '@cspell/dict-fullstack@3.2.3': resolution: {integrity: sha512-62PbndIyQPH11mAv0PyiyT0vbwD0AXEocPpHlCHzfb5v9SspzCCbzQ/LIBiFmyRa+q5LMW35CnSVu6OXdT+LKg==} - '@cspell/dict-gaming-terms@1.0.8': - resolution: {integrity: sha512-7OL0zTl93WFWhhtpXFrtm9uZXItC3ncAs8d0iQDMMFVNU1rBr6raBNxJskxE5wx2Ant12fgI66ZGVagXfN+yfA==} + '@cspell/dict-gaming-terms@1.0.10': + resolution: {integrity: sha512-LJBUSg2ErWi5+QQysFKFwRvq09zAswteIKdAM/g06NpSiPT+SoIeRNKnnASmvuQQSFS427EwgKKtJ3723n2SFQ==} '@cspell/dict-git@3.0.3': resolution: {integrity: sha512-LSxB+psZ0qoj83GkyjeEH/ZViyVsGEF/A6BAo8Nqc0w0HjD2qX/QR4sfA6JHUgQ3Yi/ccxdK7xNIo67L2ScW5A==} @@ -1339,8 +1334,8 @@ packages: '@cspell/dict-node@5.0.5': resolution: {integrity: sha512-7NbCS2E8ZZRZwlLrh2sA0vAk9n1kcTUiRp/Nia8YvKaItGXLfxYqD2rMQ3HpB1kEutal6hQLVic3N2Yi1X7AaA==} - '@cspell/dict-npm@5.1.16': - resolution: {integrity: sha512-9z0YLQIu88pC6BOMbHUYksXoSyhzwWvZa9XVvN/1rGMov1kTgStr2QDvsBpECk3AIamzZnahKIyUvJrr+rOkYg==} + '@cspell/dict-npm@5.1.21': + resolution: {integrity: sha512-AHqgbnBPwdMUF6jV/vyf5lz1+9MpmQn8h2E/Px0jHYFri4VTZ5TNBa40NaTNC/L/U/ggbVQTSoBnqZ6rLFwGCg==} '@cspell/dict-php@4.0.13': resolution: {integrity: sha512-P6sREMZkhElzz/HhXAjahnICYIqB/HSGp1EhZh+Y6IhvC15AzgtDP8B8VYCIsQof6rPF1SQrFwunxOv8H1e2eg==} @@ -1366,8 +1361,8 @@ packages: '@cspell/dict-scala@5.0.6': resolution: {integrity: sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww==} - '@cspell/dict-software-terms@4.1.18': - resolution: {integrity: sha512-QhOQ3qVFr2Y+uDw2SH15klVNU2S07ecFhG+2gpTO/K4Kuaui3INbVrzHOKW41ofP3ghv9y2TkUUtOP5KfddP8A==} + '@cspell/dict-software-terms@4.2.2': + resolution: {integrity: sha512-cgteXRzx2W/Ug7QSdFJrVxLES7krrZEjZ9J6sXRWOsVYFpgu2Gup8NKmjKOZ8NTnCjHQFrMnbmKdv56q9Kwixw==} '@cspell/dict-sql@2.1.8': resolution: {integrity: sha512-dJRE4JV1qmXTbbGm6WIcg1knmR6K5RXnQxF4XHs5HA3LAjc/zf77F95i5LC+guOGppVF6Hdl66S2UyxT+SAF3A==} @@ -1387,20 +1382,20 @@ packages: '@cspell/dict-vue@3.0.3': resolution: {integrity: sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA==} - '@cspell/dynamic-import@8.16.1': - resolution: {integrity: sha512-mEfdeS1kFKpJoDsQ8wW6PxO3+ncYuZCWCASR0trbzZDduzO2RcogMUgzP99obHtYbgXadw94qcQWXB8OYTPSwg==} + '@cspell/dynamic-import@8.17.1': + resolution: {integrity: sha512-XQtr2olYOtqbg49E+8SISd6I5DzfxmsKINDn0ZgaTFeLalnNdF3ewDU4gOEbApIzGffRa1mW9t19MsiVrznSDw==} engines: {node: '>=18.0'} - '@cspell/filetypes@8.16.1': - resolution: {integrity: sha512-zpbNg3n26muR1jdMbylw5YsaVGyS9LU5Lfy20gU7RygAk6kFyx3Yz4C84EihBGQHy2gVEsEeyCCxk+R8RXuPZA==} + '@cspell/filetypes@8.17.1': + resolution: {integrity: sha512-AxYw6j7EPYtDFAFjwybjFpMc9waXQzurfBXmEVfQ5RQRlbylujLZWwR6GnMqofeNg4oGDUpEjcAZFrgdkvMQlA==} engines: {node: '>=18'} - '@cspell/strong-weak-map@8.16.1': - resolution: {integrity: sha512-jJQS05wg2iUkLKnPR8NEq3LqvqHWKnvUDFoPwaJzYw6ol/O4yi/lv+Me9+XCPrgjpnAz+8APhWkhrR/O71R1Bw==} + '@cspell/strong-weak-map@8.17.1': + resolution: {integrity: sha512-8cY3vLAKdt5gQEMM3Gr57BuQ8sun2NjYNh9qTdrctC1S9gNC7XzFghTYAfHSWR4VrOUcMFLO/izMdsc1KFvFOA==} engines: {node: '>=18'} - '@cspell/url@8.16.1': - resolution: {integrity: sha512-kGlr7Wdo4xJpXKal/Gqo3Ll5Is7ptlIlLZOB/hzR6R53Fw4N6SdipTDIeHHqC15p2AXTEG6TSNdhk9dA50LY6w==} + '@cspell/url@8.17.1': + resolution: {integrity: sha512-LMvReIndW1ckvemElfDgTt282fb2C3C/ZXfsm0pJsTV5ZmtdelCHwzmgSBmY5fDr7D66XDp8EurotSE0K6BTvw==} engines: {node: '>=18.0'} '@dabh/diagnostics@2.0.2': @@ -1496,8 +1491,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.24.0': - resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1508,8 +1503,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.24.0': - resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1520,8 +1515,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.24.0': - resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -1532,8 +1527,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.24.0': - resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -1544,8 +1539,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.24.0': - resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -1556,8 +1551,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.24.0': - resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1568,8 +1563,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.24.0': - resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -1580,8 +1575,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.0': - resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -1592,8 +1587,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.24.0': - resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -1604,8 +1599,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.24.0': - resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -1616,8 +1611,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.24.0': - resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -1628,8 +1623,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.24.0': - resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -1640,8 +1635,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.24.0': - resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -1652,8 +1647,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.24.0': - resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1664,8 +1659,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.24.0': - resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1676,8 +1671,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.24.0': - resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1688,26 +1683,32 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.24.0': - resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.0': - resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.0': - resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1718,8 +1719,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.0': - resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1730,8 +1731,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.24.0': - resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1742,8 +1743,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.24.0': - resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1754,8 +1755,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.24.0': - resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1766,8 +1767,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.24.0': - resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1794,8 +1795,8 @@ packages: resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.16.0': - resolution: {integrity: sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==} + '@eslint/js@9.17.0': + resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.5': @@ -1818,8 +1819,8 @@ packages: '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - '@grpc/grpc-js@1.12.2': - resolution: {integrity: sha512-bgxdZmgTrJZX50OjyVwz3+mNEnCTNkh3cIqGPWVNeW9jX6bn1ZkU80uPd+67/ZpIJIjRQ9qaHCjhavyoWYxumg==} + '@grpc/grpc-js@1.12.5': + resolution: {integrity: sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==} engines: {node: '>=12.10.0'} '@grpc/proto-loader@0.7.13': @@ -1853,6 +1854,15 @@ packages: resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} engines: {node: '>=18.18'} + '@ianvs/prettier-plugin-sort-imports@4.4.0': + resolution: {integrity: sha512-f4/e+/ANGk3tHuwRW0uh2YuBR50I4h1ZjGQ+5uD8sWfinHTivQsnieR5cz24t8M6Vx4rYvZ5v/IEKZhYpzQm9Q==} + peerDependencies: + '@vue/compiler-sfc': 2.7.x || 3.x + prettier: 2 || 3 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + '@inquirer/confirm@5.0.2': resolution: {integrity: sha512-KJLUHOaKnNCYzwVbryj3TNBxyZIrr56fR5N45v6K9IPrbT6B7DcudBMfylkV1A8PUdJE15mybkEQyp2/ZUpxUA==} engines: {node: '>=18'} @@ -2005,6 +2015,9 @@ packages: resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==} engines: {node: '>= 10.0.0'} + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@mswjs/interceptors@0.37.1': resolution: {integrity: sha512-SvE+tSpcX884RJrPCskXxoS965Ky/pYABDEhWW6oeSRhpUDLrS5nTvT5n1LLSDVDYvty4imVmXsy+3/ROVuknA==} engines: {node: '>=18'} @@ -2077,120 +2090,116 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@opentelemetry/api-logs@0.55.0': - resolution: {integrity: sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==} + '@opentelemetry/api-logs@0.57.0': + resolution: {integrity: sha512-l1aJ30CXeauVYaI+btiynHpw341LthkMTv3omi1VJDX14werY2Wmv9n1yudMsq9HuY0m8PvXEVX4d8zxEb+WRg==} engines: {node: '>=14'} '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-zone-peer-dep@1.28.0': - resolution: {integrity: sha512-tGtZ+N/QlMpZmHlSpANJsCBsx04FAF7t1nvyocG+Hr1r9rEJFPbdJ23pRDPYdpHX7D5UislKEz3mP+kfsP9p/A==} + '@opentelemetry/context-zone-peer-dep@1.30.0': + resolution: {integrity: sha512-5T3cM1f6hnHUodbZK6X61wtRKT4q0dmITJolwXSN5XA55WLpkTY0D6u4WhNYDybvOd6/nWxsSVeACUyMa22few==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - zone.js: ^0.10.2 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^0.14.0 + zone.js: ^0.10.2 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^0.14.0 || ^0.15.0 - '@opentelemetry/context-zone@1.28.0': - resolution: {integrity: sha512-fMgtDVpEs64S5bKqOJxXk/PJ4H5H5RlFfFbnpUmChbzsZBFtWBWUGyCBb7tcDTYDLGL6kGpq2n/CLj6zvnP8gQ==} + '@opentelemetry/context-zone@1.30.0': + resolution: {integrity: sha512-iN/l3LJUST1XXTLLoZionIfaBxgMXUnnmbAs4DJa50xHVDn9qvQUfeI0WCfPHetfAUyC0oAYlUycXEtTZE3XIQ==} engines: {node: '>=14'} - '@opentelemetry/core@1.28.0': - resolution: {integrity: sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==} + '@opentelemetry/core@1.30.0': + resolution: {integrity: sha512-Q/3u/K73KUjTCnFUP97ZY+pBjQ1kPEgjOfXj/bJl8zW7GbXdkw6cwuyZk6ZTXkVgCBsYRYUzx4fvYK1jxdb9MA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/exporter-trace-otlp-http@0.55.0': - resolution: {integrity: sha512-lMiNic63EVHpW+eChmLD2CieDmwQBFi72+LFbh8+5hY0ShrDGrsGP/zuT5MRh7M/vM/UZYO/2A/FYd7CMQGR7A==} + '@opentelemetry/exporter-trace-otlp-http@0.57.0': + resolution: {integrity: sha512-BJl35PSkwoMlGEOrzjCG1ih6zqZoAZJIR4xyqSKC2BqPtwuRjID0vWBaEdP9xrxxJTEIEQw+gEY/0pUgicX0ew==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-document-load@0.42.0': - resolution: {integrity: sha512-/YfGcdl3oJBXktuWSpUU+IrF6V0haudEMqJZvpPkbzVgHJ5I3W1+1REGimmsU4e5PvwHKt/jm3pne7PBSb4WyA==} + '@opentelemetry/instrumentation-document-load@0.44.0': + resolution: {integrity: sha512-PccD8BSqhevp9E2Y3EUSelnIMPUbBJtWIEfJSChG17yKwDcf3U8zI8uupJMXXlPwZLyDXe/0zeIhPW0BfdHMiA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-fetch@0.55.0': - resolution: {integrity: sha512-wkybQE85HzInYX2csZ4UuMlCIMCyGGHcNqL9TcoZgAZC2EuXFReTsLytoszknhcaX+P7UT9Ee3915t8KC6XP4w==} + '@opentelemetry/instrumentation-fetch@0.57.0': + resolution: {integrity: sha512-z9ivzkoxkZqkjRW6zSywGdnQL2Nmb9WAiW1Pk9klH9BWCGWqfMRjrmaota9eb+izrmYwTcrzsZiQoq2twCpbSg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-user-interaction@0.42.0': - resolution: {integrity: sha512-cRlvT5/Dw4xNr0LWQlNsXP3o9NFj4kHz5TJOka0XCgSrHHoL9/zGbPiSQml8SCD7MS4OmeBYUCn7MJ25/h1WMQ==} + '@opentelemetry/instrumentation-user-interaction@0.44.0': + resolution: {integrity: sha512-GngN5ZKNbbzr4ebdqZHGv2I52+UNSVFKYUvRnnLy8Ei7zPJOzpRkFHhwAfYt8rr+MQgtKs5VB4VrVILr7f/N1w==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - zone.js: ^0.11.4 || ^0.13.0 || ^0.14.0 + zone.js: ^0.11.4 || ^0.13.0 || ^0.14.0 || ^0.15.0 - '@opentelemetry/instrumentation-xml-http-request@0.55.0': - resolution: {integrity: sha512-Wlz4LzDpBFxHpb24RAM6RoiGspJ7J16ux0Xw5KVLtK3zzpQaxMYEVF0XQNC61WJJa3tRNt3XRjiQ8mrXJZxVQg==} + '@opentelemetry/instrumentation-xml-http-request@0.57.0': + resolution: {integrity: sha512-kSVsw/dXrx0/ac5gsO/M9aTyawiqM6g0tnXhdGajugLJghYozY7QC6KoIMf3AxmcAcXKDCWWMZpYX1DVimJkRA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.55.0': - resolution: {integrity: sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ==} + '@opentelemetry/instrumentation@0.57.0': + resolution: {integrity: sha512-qIKp+tSCLqofneUWRc5XHtr9jHIq0N0BJfaJamM9gjEFO8sthV4SDXDGNOSAx16PxkbrQJ5/AxMPAGCXl8W/Hg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.55.0': - resolution: {integrity: sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==} + '@opentelemetry/otlp-exporter-base@0.57.0': + resolution: {integrity: sha512-QQl4Ngm3D6H8SDO0EM642ncTxjRsf/HDq7+IWIA0eaEK/NTsJeQ3iYJiZj3F4jkALnvyeM1kkwd+DHtqxTBx9Q==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-transformer@0.55.0': - resolution: {integrity: sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==} + '@opentelemetry/otlp-transformer@0.57.0': + resolution: {integrity: sha512-yHX7sdwkdAmSa6Jbi3caSLDWy0PCHS1pKQeKz8AIWSyQqL7IojHKgdk9A+7eRd98Z1n9YTdwWSWLnObvIqhEhQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/propagator-b3@1.28.0': - resolution: {integrity: sha512-Q7HVDIMwhN5RxL4bECMT4BdbyYSAKkC6U/RGn4NpO/cbqP6ZRg+BS7fPo/pGZi2w8AHfpIGQFXQmE8d2PC5xxQ==} + '@opentelemetry/propagator-b3@1.30.0': + resolution: {integrity: sha512-lcobQQmd+hLdtxJJKu/i51lNXmF1PJJ7Y9B97ciHRVQuMI260vSZG7Uf4Zg0fqR8PB+fT/7rnlDwS0M7QldZQQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/resources@1.28.0': - resolution: {integrity: sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==} + '@opentelemetry/resources@1.30.0': + resolution: {integrity: sha512-5mGMjL0Uld/99t7/pcd7CuVtJbkARckLVuiOX84nO8RtLtIz0/J6EOHM2TGvPZ6F4K+XjUq13gMx14w80SVCQg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/sdk-logs@0.55.0': - resolution: {integrity: sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==} + '@opentelemetry/sdk-logs@0.57.0': + resolution: {integrity: sha512-6Kbxdu/QE9LWH7+WSLmYo3DjAq+c55TiCLXiXu6b/2m2muy5SyOG2m0MrGqetyRpfYSSbIqHmJoqNVTN3+2a9g==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-metrics@1.28.0': - resolution: {integrity: sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==} + '@opentelemetry/sdk-metrics@1.30.0': + resolution: {integrity: sha512-5kcj6APyRMvv6dEIP5plz2qfJAD4OMipBRT11u/pa1a68rHKI2Ln+iXVkAGKgx8o7CXbD7FdPypTUY88ZQgP4Q==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@1.28.0': - resolution: {integrity: sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==} + '@opentelemetry/sdk-trace-base@1.30.0': + resolution: {integrity: sha512-RKQDaDIkV7PwizmHw+rE/FgfB2a6MBx+AEVVlAHXRG1YYxLiBpPX2KhmoB99R5vA4b72iJrjle68NDWnbrE9Dg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/sdk-trace-web@1.28.0': - resolution: {integrity: sha512-/QOIrJc/A/caKbA9voLua4isf///cjQKB6gomEzX2fL18TBqZhIkm9k2DpjlbtrQoYCJDZ9x7Phrec22aQGpQw==} + '@opentelemetry/sdk-trace-web@1.30.0': + resolution: {integrity: sha512-tSsPbaOQqmkfSkRkMnv1T8au2hwlv3v5ZUGmRwc7zIL1hokhZKg5qhqTsvrWvRENlZ7+J9+cXZFKIMNKHodyhQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/semantic-conventions@1.27.0': - resolution: {integrity: sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==} - engines: {node: '>=14'} - '@opentelemetry/semantic-conventions@1.28.0': resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} engines: {node: '>=14'} @@ -2285,89 +2294,101 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.23.0': - resolution: {integrity: sha512-8OR+Ok3SGEMsAZispLx8jruuXw0HVF16k+ub2eNXKHDmdxL4cf9NlNpAzhlOhNyXzKDEJuFeq0nZm+XlNb1IFw==} + '@rollup/rollup-android-arm-eabi@4.29.1': + resolution: {integrity: sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.23.0': - resolution: {integrity: sha512-rEFtX1nP8gqmLmPZsXRMoLVNB5JBwOzIAk/XAcEPuKrPa2nPJ+DuGGpfQUR0XjRm8KjHfTZLpWbKXkA5BoFL3w==} + '@rollup/rollup-android-arm64@4.29.1': + resolution: {integrity: sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.23.0': - resolution: {integrity: sha512-ZbqlMkJRMMPeapfaU4drYHns7Q5MIxjM/QeOO62qQZGPh9XWziap+NF9fsqPHT0KzEL6HaPspC7sOwpgyA3J9g==} + '@rollup/rollup-darwin-arm64@4.29.1': + resolution: {integrity: sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.23.0': - resolution: {integrity: sha512-PfmgQp78xx5rBCgn2oYPQ1rQTtOaQCna0kRaBlc5w7RlA3TDGGo7m3XaptgitUZ54US9915i7KeVPHoy3/W8tA==} + '@rollup/rollup-darwin-x64@4.29.1': + resolution: {integrity: sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.23.0': - resolution: {integrity: sha512-WAeZfAAPus56eQgBioezXRRzArAjWJGjNo/M+BHZygUcs9EePIuGI1Wfc6U/Ki+tMW17FFGvhCfYnfcKPh18SA==} + '@rollup/rollup-freebsd-arm64@4.29.1': + resolution: {integrity: sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.29.1': + resolution: {integrity: sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.29.1': + resolution: {integrity: sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.23.0': - resolution: {integrity: sha512-v7PGcp1O5XKZxKX8phTXtmJDVpE20Ub1eF6w9iMmI3qrrPak6yR9/5eeq7ziLMrMTjppkkskXyxnmm00HdtXjA==} + '@rollup/rollup-linux-arm-musleabihf@4.29.1': + resolution: {integrity: sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.23.0': - resolution: {integrity: sha512-nAbWsDZ9UkU6xQiXEyXBNHAKbzSAi95H3gTStJq9UGiS1v+YVXwRHcQOQEF/3CHuhX5BVhShKoeOf6Q/1M+Zhg==} + '@rollup/rollup-linux-arm64-gnu@4.29.1': + resolution: {integrity: sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.23.0': - resolution: {integrity: sha512-5QT/Di5FbGNPaVw8hHO1wETunwkPuZBIu6W+5GNArlKHD9fkMHy7vS8zGHJk38oObXfWdsuLMogD4sBySLJ54g==} + '@rollup/rollup-linux-arm64-musl@4.29.1': + resolution: {integrity: sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': - resolution: {integrity: sha512-Sefl6vPyn5axzCsO13r1sHLcmPuiSOrKIImnq34CBurntcJ+lkQgAaTt/9JkgGmaZJ+OkaHmAJl4Bfd0DmdtOQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.29.1': + resolution: {integrity: sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': + resolution: {integrity: sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.23.0': - resolution: {integrity: sha512-o4QI2KU/QbP7ZExMse6ULotdV3oJUYMrdx3rBZCgUF3ur3gJPfe8Fuasn6tia16c5kZBBw0aTmaUygad6VB/hQ==} + '@rollup/rollup-linux-riscv64-gnu@4.29.1': + resolution: {integrity: sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.23.0': - resolution: {integrity: sha512-+bxqx+V/D4FGrpXzPGKp/SEZIZ8cIW3K7wOtcJAoCrmXvzRtmdUhYNbgd+RztLzfDEfA2WtKj5F4tcbNPuqgeg==} + '@rollup/rollup-linux-s390x-gnu@4.29.1': + resolution: {integrity: sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.23.0': - resolution: {integrity: sha512-I/eXsdVoCKtSgK9OwyQKPAfricWKUMNCwJKtatRYMmDo5N859tbO3UsBw5kT3dU1n6ZcM1JDzPRSGhAUkxfLxw==} + '@rollup/rollup-linux-x64-gnu@4.29.1': + resolution: {integrity: sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.23.0': - resolution: {integrity: sha512-4ZoDZy5ShLbbe1KPSafbFh1vbl0asTVfkABC7eWqIs01+66ncM82YJxV2VtV3YVJTqq2P8HMx3DCoRSWB/N3rw==} + '@rollup/rollup-linux-x64-musl@4.29.1': + resolution: {integrity: sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.23.0': - resolution: {integrity: sha512-+5Ky8dhft4STaOEbZu3/NU4QIyYssKO+r1cD3FzuusA0vO5gso15on7qGzKdNXnc1gOrsgCqZjRw1w+zL4y4hQ==} + '@rollup/rollup-win32-arm64-msvc@4.29.1': + resolution: {integrity: sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.23.0': - resolution: {integrity: sha512-0SPJk4cPZQhq9qA1UhIRumSE3+JJIBBjtlGl5PNC///BoaByckNZd53rOYD0glpTkYFBQSt7AkMeLVPfx65+BQ==} + '@rollup/rollup-win32-ia32-msvc@4.29.1': + resolution: {integrity: sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.23.0': - resolution: {integrity: sha512-lqCK5GQC8fNo0+JvTSxcG7YB1UKYp8yrNLhsArlvPWN+16ovSZgoehlVHg6X0sSWPUkpjRBR5TuR12ZugowZ4g==} + '@rollup/rollup-win32-x64-msvc@4.29.1': + resolution: {integrity: sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==} cpu: [x64] os: [win32] - '@rtsao/scc@1.1.0': - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -2390,44 +2411,44 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@storybook/addon-actions@8.4.6': - resolution: {integrity: sha512-vbplwjMj7UXbdzoFhQkqFHLQAPJX8OVGTM9Q+yjuWDHViaKKUlgRWp0jclT7aIDNJQU2a6wJbTimHgJeF16Vhg==} + '@storybook/addon-actions@8.4.7': + resolution: {integrity: sha512-mjtD5JxcPuW74T6h7nqMxWTvDneFtokg88p6kQ5OnC1M259iAXb//yiSZgu/quunMHPCXSiqn4FNOSgASTSbsA==} peerDependencies: - storybook: ^8.4.6 + storybook: ^8.4.7 - '@storybook/addon-controls@8.4.6': - resolution: {integrity: sha512-70pEGWh0C2g8s0DYsISElOzsMbQS6p/K9iU5EqfotDF+hvEqstjsV/bTbR5f3OK4vR/7Gxamk7j8RVd14Nql6A==} + '@storybook/addon-controls@8.4.7': + resolution: {integrity: sha512-377uo5IsJgXLnQLJixa47+11V+7Wn9KcDEw+96aGCBCfLbWNH8S08tJHHnSu+jXg9zoqCAC23MetntVp6LetHA==} peerDependencies: - storybook: ^8.4.6 + storybook: ^8.4.7 - '@storybook/addon-toolbars@8.4.6': - resolution: {integrity: sha512-+Xao/uGa8FnYsyUiREUkYXWNysm3Aba8tL/Bwd+HufHtdiKJGa9lrXaC7VLCqBUaEjwqM3aaPwqEWIROsthmPQ==} + '@storybook/addon-toolbars@8.4.7': + resolution: {integrity: sha512-OSfdv5UZs+NdGB+nZmbafGUWimiweJ/56gShlw8Neo/4jOJl1R3rnRqqY7MYx8E4GwoX+i3GF5C3iWFNQqlDcw==} peerDependencies: - storybook: ^8.4.6 + storybook: ^8.4.7 - '@storybook/builder-vite@8.4.6': - resolution: {integrity: sha512-PyJsaEPyuRFFEplpNUi+nbuJd7d1DC2dAZjpsaHTXyqg5iPIbkIgsbCJLUDeIXnUDqM/utjmMpN0sQKJuhIc6w==} + '@storybook/builder-vite@8.4.7': + resolution: {integrity: sha512-LovyXG5VM0w7CovI/k56ZZyWCveQFVDl0m7WwetpmMh2mmFJ+uPQ35BBsgTvTfc8RHi+9Q3F58qP1MQSByXi9g==} peerDependencies: - storybook: ^8.4.6 + storybook: ^8.4.7 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - '@storybook/components@8.4.6': - resolution: {integrity: sha512-9tKSJJCyFT5RZMRGyozTBJkr9C9Yfk1nuOE9XbDEE1Z+3/IypKR9+iwc5mfNBStDNY+rxtYWNLKBb5GPR2yhzA==} + '@storybook/components@8.4.7': + resolution: {integrity: sha512-uyJIcoyeMWKAvjrG9tJBUCKxr2WZk+PomgrgrUwejkIfXMO76i6jw9BwLa0NZjYdlthDv30r9FfbYZyeNPmF0g==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/core@8.4.6': - resolution: {integrity: sha512-WeojVtHy0/t50tzw/15S+DLzKsj8BN9yWdo3vJMvm+nflLFvfq1XvD9WGOWeaFp8E/o3AP+4HprXG0r42KEJtA==} + '@storybook/core@8.4.7': + resolution: {integrity: sha512-7Z8Z0A+1YnhrrSXoKKwFFI4gnsLbWzr8fnDCU6+6HlDukFYh8GHRcZ9zKfqmy6U3hw2h8H5DrHsxWfyaYUUOoA==} peerDependencies: prettier: ^2 || ^3 peerDependenciesMeta: prettier: optional: true - '@storybook/csf-plugin@8.4.6': - resolution: {integrity: sha512-JDIT0czC4yMgKGNf39KTZr3zm5MusAZdn6LBrTfvWb7CrTCR4iVHa4lp2yb7EJk41vHsBec0QUYDDuiFH/vV0g==} + '@storybook/csf-plugin@8.4.7': + resolution: {integrity: sha512-Fgogplu4HImgC+AYDcdGm1rmL6OR1rVdNX1Be9C/NEXwOCpbbBwi0BxTf/2ZxHRk9fCeaPEcOdP5S8QHfltc1g==} peerDependencies: - storybook: ^8.4.6 + storybook: ^8.4.7 '@storybook/csf@0.1.12': resolution: {integrity: sha512-9/exVhabisyIVL0VxTCxo01Tdm8wefIXKXfltAPTSr8cbLn5JAxGQ6QV3mjdecLGEOucfoVhAKtJfVHxEK1iqw==} @@ -2435,40 +2456,40 @@ packages: '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} - '@storybook/manager-api@8.4.6': - resolution: {integrity: sha512-TsXlQ5m5rTl2KNT9icPFyy822AqXrx1QplZBt/L7cFn7SpqQKDeSta21FH7MG0piAvzOweXebVSqKngJ6cCWWQ==} + '@storybook/manager-api@8.4.7': + resolution: {integrity: sha512-ELqemTviCxAsZ5tqUz39sDmQkvhVAvAgiplYy9Uf15kO0SP2+HKsCMzlrm2ue2FfkUNyqbDayCPPCB0Cdn/mpQ==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/preview-api@8.4.6': - resolution: {integrity: sha512-LbD+lR1FGvWaJBXteVx5xdgs1x1D7tyidBg2CsW2ex+cP0iJ176JgjPfutZxlWOfQnhfRYNnJ3WKoCIfxFOTKA==} + '@storybook/preview-api@8.4.7': + resolution: {integrity: sha512-0QVQwHw+OyZGHAJEXo6Knx+6/4er7n2rTDE5RYJ9F2E2Lg42E19pfdLlq2Jhoods2Xrclo3wj6GWR//Ahi39Eg==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/react-dom-shim@8.4.6': - resolution: {integrity: sha512-f7RM8GO++fqMxbjNdEzeGS1P821jXuwRnAraejk5hyjB5SqetauFxMwoFYEYfJXPaLX2qIubnIJ78hdJ/IBaEA==} + '@storybook/react-dom-shim@8.4.7': + resolution: {integrity: sha512-6bkG2jvKTmWrmVzCgwpTxwIugd7Lu+2btsLAqhQSzDyIj2/uhMNp8xIMr/NBDtLgq3nomt9gefNa9xxLwk/OMg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.4.6 + storybook: ^8.4.7 - '@storybook/react-vite@8.4.6': - resolution: {integrity: sha512-bVoYj3uJRz0SknK2qN3vBVSoEXsvyARQLuHjP9eX0lWBd9XSxZinmVbexPdD0OeJYcJIdmbli2/Gw7/hu5CjFA==} + '@storybook/react-vite@8.4.7': + resolution: {integrity: sha512-iiY9iLdMXhDnilCEVxU6vQsN72pW3miaf0WSenOZRyZv3HdbpgOxI0qapOS0KCyRUnX9vTlmrSPTMchY4cAeOg==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.4.6 + storybook: ^8.4.7 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - '@storybook/react@8.4.6': - resolution: {integrity: sha512-QAT23beoYNLhFGAXPimtuMErvpcI7eZbZ4AlLqW1fhiTZrRYw06cjC1bs9H3tODMcHH9LS5p3Wz9b29jtV2XGw==} + '@storybook/react@8.4.7': + resolution: {integrity: sha512-nQ0/7i2DkaCb7dy0NaT95llRVNYWQiPIVuhNfjr1mVhEP7XD090p0g7eqUmsx8vfdHh2BzWEo6CoBFRd3+EXxw==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.4.6 + '@storybook/test': 8.4.7 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.4.6 + storybook: ^8.4.7 typescript: '>= 4.2.x' peerDependenciesMeta: '@storybook/test': @@ -2476,15 +2497,15 @@ packages: typescript: optional: true - '@storybook/test-runner@0.20.0': - resolution: {integrity: sha512-0cSNW61/XYg8jtcRlk8qc5fJa8qKSq7tvSpvikwS7zcXkKgfu6fbGX56ErxnvidmyCyXjMe/j0UHzLACzc2nTg==} + '@storybook/test-runner@0.21.0': + resolution: {integrity: sha512-aG2QvKXSIjMN1CA9PJK/esnidZWgt1gAkfo9Kqf8+NqBSsmP/2GyL5vxu1lkRFO/4qCv5JenNZ5Uj6ie4b2oag==} engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/theming@8.4.6': - resolution: {integrity: sha512-q7vDPN/mgj7cXIVQ9R1/V75hrzNgKkm2G0LjMo57//9/djQ+7LxvBsR1iScbFIRSEqppvMiBFzkts+2uXidySA==} + '@storybook/theming@8.4.7': + resolution: {integrity: sha512-99rgLEjf7iwfSEmdqlHkSG3AyLcK0sfExcr0jnc6rLiAkBhzuIsvcHjjUwkR210SOCgXqBPW0ZA6uhnuyppHLw==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 @@ -2527,68 +2548,68 @@ packages: '@styled-system/variant@5.1.5': resolution: {integrity: sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==} - '@swc/core-darwin-arm64@1.9.3': - resolution: {integrity: sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w==} + '@swc/core-darwin-arm64@1.10.4': + resolution: {integrity: sha512-sV/eurLhkjn/197y48bxKP19oqcLydSel42Qsy2zepBltqUx+/zZ8+/IS0Bi7kaWVFxerbW1IPB09uq8Zuvm3g==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.9.3': - resolution: {integrity: sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ==} + '@swc/core-darwin-x64@1.10.4': + resolution: {integrity: sha512-gjYNU6vrAUO4+FuovEo9ofnVosTFXkF0VDuo1MKPItz6e2pxc2ale4FGzLw0Nf7JB1sX4a8h06CN16/pLJ8Q2w==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.9.3': - resolution: {integrity: sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ==} + '@swc/core-linux-arm-gnueabihf@1.10.4': + resolution: {integrity: sha512-zd7fXH5w8s+Sfvn2oO464KDWl+ZX1MJiVmE4Pdk46N3PEaNwE0koTfgx2vQRqRG4vBBobzVvzICC3618WcefOA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.9.3': - resolution: {integrity: sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g==} + '@swc/core-linux-arm64-gnu@1.10.4': + resolution: {integrity: sha512-+UGfoHDxsMZgFD3tABKLeEZHqLNOkxStu+qCG7atGBhS4Slri6h6zijVvf4yI5X3kbXdvc44XV/hrP/Klnui2A==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.9.3': - resolution: {integrity: sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==} + '@swc/core-linux-arm64-musl@1.10.4': + resolution: {integrity: sha512-cDDj2/uYsOH0pgAnDkovLZvKJpFmBMyXkxEG6Q4yw99HbzO6QzZ5HDGWGWVq/6dLgYKlnnmpjZCPPQIu01mXEg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.9.3': - resolution: {integrity: sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==} + '@swc/core-linux-x64-gnu@1.10.4': + resolution: {integrity: sha512-qJXh9D6Kf5xSdGWPINpLGixAbB5JX8JcbEJpRamhlDBoOcQC79dYfOMEIxWPhTS1DGLyFakAx2FX/b2VmQmj0g==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.9.3': - resolution: {integrity: sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==} + '@swc/core-linux-x64-musl@1.10.4': + resolution: {integrity: sha512-A76lIAeyQnHCVt0RL/pG+0er8Qk9+acGJqSZOZm67Ve3B0oqMd871kPtaHBM0BW3OZAhoILgfHW3Op9Q3mx3Cw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.9.3': - resolution: {integrity: sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==} + '@swc/core-win32-arm64-msvc@1.10.4': + resolution: {integrity: sha512-e6j5kBu4fIY7fFxFxnZI0MlEovRvp50Lg59Fw+DVbtqHk3C85dckcy5xKP+UoXeuEmFceauQDczUcGs19SRGSQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.9.3': - resolution: {integrity: sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA==} + '@swc/core-win32-ia32-msvc@1.10.4': + resolution: {integrity: sha512-RSYHfdKgNXV/amY5Tqk1EWVsyQnhlsM//jeqMLw5Fy9rfxP592W9UTumNikNRPdjI8wKKzNMXDb1U29tQjN0dg==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.9.3': - resolution: {integrity: sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ==} + '@swc/core-win32-x64-msvc@1.10.4': + resolution: {integrity: sha512-1ujYpaqfqNPYdwKBlvJnOqcl+Syn3UrQ4XE0Txz6zMYgyh6cdU6a3pxqLqIUSJ12MtXRA9ZUhEz1ekU3LfLWXw==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.9.3': - resolution: {integrity: sha512-oRj0AFePUhtatX+BscVhnzaAmWjpfAeySpM1TCbxA1rtBDeH/JDhi5yYzAKneDYtVtBvA7ApfeuzhMC9ye4xSg==} + '@swc/core@1.10.4': + resolution: {integrity: sha512-ut3zfiTLORMxhr6y/GBxkHmzcGuVpwJYX4qyXWuBKkpw/0g0S5iO1/wW7RnLnZbAi8wS/n0atRZoaZlXWBkeJg==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -2605,8 +2626,8 @@ packages: peerDependencies: '@swc/core': '*' - '@swc/plugin-styled-components@5.0.0': - resolution: {integrity: sha512-c9WCV2hU4OxfczzeABNFwkLftAovP7IeHPX0nxqu1HMn4x/T6MjWoJ22hspBv32NpUwGlvIgRG3SyHRHE80enw==} + '@swc/plugin-styled-components@6.0.2': + resolution: {integrity: sha512-9Qy2r4BabF5eQo5nR/AX7zAUuC73gq5oZW2nd4DVNSkVDrnEesRbzwoNHMJdAKw0Q3Qv1lLTi7O6E6n6hGAEKA==} '@swc/types@0.1.17': resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} @@ -2623,15 +2644,15 @@ packages: resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/react@16.0.1': - resolution: {integrity: sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==} + '@testing-library/react@16.1.0': + resolution: {integrity: sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==} engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 - '@types/react-dom': ^18.0.0 - react: ^18.0.0 - react-dom: ^18.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -2741,9 +2762,6 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/keyv@4.2.0': resolution: {integrity: sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw==} deprecated: This is a stub types definition. keyv provides its own type definitions, so you do not need this installed. @@ -2754,8 +2772,8 @@ packages: '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - '@types/node@20.17.9': - resolution: {integrity: sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==} + '@types/node@20.17.11': + resolution: {integrity: sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -2778,8 +2796,10 @@ packages: '@types/react-router@5.1.20': resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} - '@types/react-transition-group@4.4.11': - resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' '@types/react@18.3.12': resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} @@ -2847,55 +2867,55 @@ packages: '@types/yauzl@2.10.0': resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} - '@typescript-eslint/eslint-plugin@8.18.0': - resolution: {integrity: sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==} + '@typescript-eslint/eslint-plugin@8.19.0': + resolution: {integrity: sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.18.0': - resolution: {integrity: sha512-hgUZ3kTEpVzKaK3uNibExUYm6SKKOmTU2BOxBSvOYwtJEPdVQ70kZJpPjstlnhCHcuc2WGfSbpKlb/69ttyN5Q==} + '@typescript-eslint/parser@8.19.0': + resolution: {integrity: sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.18.0': - resolution: {integrity: sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==} + '@typescript-eslint/scope-manager@8.19.0': + resolution: {integrity: sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.18.0': - resolution: {integrity: sha512-er224jRepVAVLnMF2Q7MZJCq5CsdH2oqjP4dT7K6ij09Kyd+R21r7UVJrF0buMVdZS5QRhDzpvzAxHxabQadow==} + '@typescript-eslint/type-utils@8.19.0': + resolution: {integrity: sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.18.0': - resolution: {integrity: sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==} + '@typescript-eslint/types@8.19.0': + resolution: {integrity: sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.18.0': - resolution: {integrity: sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==} + '@typescript-eslint/typescript-estree@8.19.0': + resolution: {integrity: sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.18.0': - resolution: {integrity: sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==} + '@typescript-eslint/utils@8.19.0': + resolution: {integrity: sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.18.0': - resolution: {integrity: sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==} + '@typescript-eslint/visitor-keys@8.19.0': + resolution: {integrity: sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@uiw/codemirror-extensions-basic-setup@4.23.6': - resolution: {integrity: sha512-bvtq8IOvdkLJMhoJBRGPEzU51fMpPDwEhcAHp9xCR05MtbIokQgsnLXrmD1aZm6e7s/3q47H+qdSfAAkR5MkLA==} + '@uiw/codemirror-extensions-basic-setup@4.23.7': + resolution: {integrity: sha512-9/2EUa1Lck4kFKkR2BkxlZPpgD/EWuKHnOlysf1yHKZGraaZmZEaUw+utDK4QcuJc8Iz097vsLz4f4th5EU27g==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' @@ -2905,15 +2925,15 @@ packages: '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' - '@uiw/codemirror-themes@4.23.6': - resolution: {integrity: sha512-0dpuLQW+V6zrKvfvor/eo71V3tpr2L2Hsu8QZAdtSzksjWABxTOzH3ShaBRxCEsrz6sU9sa9o7ShwBMMDz59bQ==} + '@uiw/codemirror-themes@4.23.7': + resolution: {integrity: sha512-UNf1XOx1hG9OmJnrtT86PxKcdcwhaNhbrcD+nsk8WxRJ3n5c8nH6euDvgVPdVLPwbizsaQcZTILACgA/FjRpVg==} peerDependencies: '@codemirror/language': '>=6.0.0' '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' - '@uiw/react-codemirror@4.23.6': - resolution: {integrity: sha512-caYKGV6TfGLRV1HHD3p0G3FiVzKL1go7wes5XT2nWjB0+dTdyzyb81MKRSacptgZcotujfNO6QXn65uhETRAMw==} + '@uiw/react-codemirror@4.23.7': + resolution: {integrity: sha512-Nh/0P6W+kWta+ARp9YpnKPD9ick5teEnwmtNoPQnyd6NPv0EQP3Ui4YmRVNj1nkUEo+QjrAUaEfcejJ2up/HZA==} peerDependencies: '@babel/runtime': '>=7.11.0' '@codemirror/state': '>=6.0.0' @@ -2972,8 +2992,8 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - ace-builds@1.36.5: - resolution: {integrity: sha512-mZ5KVanRT6nLRDLqtG/1YQQLX/gZVC/v526cm1Ru/MTSlrbweSmqv2ZT0d2GaHpJq035MwCMIrj+LgDAUnDXrg==} + ace-builds@1.37.1: + resolution: {integrity: sha512-6/jxFucA1z1C3hgLlVkTE5/znZ+iYvD301vfwtybiMc3k76IDykliCD0xh/eYZMJUfsJtaOQHZ2AJO5ey0PHWw==} acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} @@ -3107,8 +3127,8 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} array-includes@3.1.8: @@ -3122,24 +3142,16 @@ packages: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} - array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} engines: {node: '>= 0.4'} array.prototype.tosorted@1.1.4: resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} engines: {node: '>= 0.4'} - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} assert-plus@1.0.0: @@ -3333,8 +3345,16 @@ packages: resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} engines: {node: '>=8'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + call-bind-apply-helpers@1.0.1: + resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.3: + resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} callsites@3.1.0: @@ -3562,42 +3582,42 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - cspell-config-lib@8.16.1: - resolution: {integrity: sha512-ohbSi9sI14rMdFc2g17ogObGGkd/x6zUVOzCH1nEOefC9yJYYfsvaMHqdhk0rOjvmF95j5OK4dm5oid+DKQcpw==} + cspell-config-lib@8.17.1: + resolution: {integrity: sha512-x1S7QWprgUcwuwiJB1Ng0ZTBC4G50qP9qQyg/aroMkcdMsHfk26E8jUGRPNt4ftHFzS4YMhwtXuJQ9IgRUuNPA==} engines: {node: '>=18'} - cspell-dictionary@8.16.1: - resolution: {integrity: sha512-NL/vwf5SjtkWWaEUh+0dogKdEU4UuepJaNh36FX8W1CFtQXj7yEs45x4K7/Fp+pn/4AT7Qe7WpSSWi9z5GcqKg==} + cspell-dictionary@8.17.1: + resolution: {integrity: sha512-zSl9l3wii+x16yc2NVZl/+CMLeLBAiuEd5YoFkOYPcbTJnfPwdjMNcj71u7wBvNJ+qwbF+kGbutEt15yHW3NBw==} engines: {node: '>=18'} - cspell-gitignore@8.16.1: - resolution: {integrity: sha512-Gg8qvFc8wr1D7TvB+GSfT1jyrUoUmPiG3WdOnQnxOSYKJesOiVvNxLv7YXRFkcUKG1VU6XDUkpb/uzKh3k2rKw==} + cspell-gitignore@8.17.1: + resolution: {integrity: sha512-bk727Zf4FBCjm9Mwvyreyhgjwe+YhPQEW7PldkHiinKd+Irfez4s8GXLQb1EgV0UpvViqaqBqLmngjZdS30BTA==} engines: {node: '>=18'} hasBin: true - cspell-glob@8.16.1: - resolution: {integrity: sha512-EukaXFaUrgrY9G4bB2PguzpkAoOq6ai9acLl6gWD+6DgVEwkLqPmCWjsFJA0MaqVp9QvPsIfCy4KCnx35csG/g==} + cspell-glob@8.17.1: + resolution: {integrity: sha512-cUwM5auSt0RvLX7UkP2GEArJRWc85l51B1voArl+3ZIKeMZwcJpJgN3qvImtF8yRTZwYeYCs1sgsihb179q+mg==} engines: {node: '>=18'} - cspell-grammar@8.16.1: - resolution: {integrity: sha512-7IRYa0O1xfK2HVbhGSpOPPt5HlP2ZHRHtdLU2iOvMSCkh0cSPERu++kdprvcaOf7E7koo0P+bxHSprcYbU/agg==} + cspell-grammar@8.17.1: + resolution: {integrity: sha512-H5tLcBuW7aUj9L0rR+FSbnWPEsWb8lWppHVidtqw9Ll1CUHWOZC9HTB2RdrhJZrsz/8DJbM2yNbok0Xt0VAfdw==} engines: {node: '>=18'} hasBin: true - cspell-io@8.16.1: - resolution: {integrity: sha512-25MOQfy7EhdVeoNUW/+jyb5ArDYSLbaFwVToakHtLGuYk9cW8q8MAHq1W9GzW06wXswT2sQsRvaozmIOTDIOnw==} + cspell-io@8.17.1: + resolution: {integrity: sha512-liIOsblt7oVItifzRAbuxiYrwlgw1VOqKppMxVKtYoAn2VUuuEpjCj6jLWpoTqSszR/38o7ChsHY1LHakhJZmw==} engines: {node: '>=18'} - cspell-lib@8.16.1: - resolution: {integrity: sha512-Gn1vJcyhYe78iB+9dms8rnfgDEfJgYocXapFPTOcZV3EUWKcV4wyCiHdbK3j2ElLXmPuSPg4eZSlxxk8ITD0Aw==} + cspell-lib@8.17.1: + resolution: {integrity: sha512-66n83Q7bK5tnvkDH7869/pBY/65AKmZVfCOAlsbhJn3YMDbNHFCHR0d1oNMlqG+n65Aco89VGwYfXxImZY+/mA==} engines: {node: '>=18'} - cspell-trie-lib@8.16.1: - resolution: {integrity: sha512-T86nszsjQjyZ35dOWk7qN17Hem0cVeXJ4D1v/gIG+Y0Umo7dBW7AwmTvUy8iMFAra29cSdgRH+yk6q1qdpA+ZA==} + cspell-trie-lib@8.17.1: + resolution: {integrity: sha512-13WNa5s75VwOjlGzWprmfNbBFIfXyA7tYYrbV+LugKkznyNZJeJPojHouEudcLq3SYb2Q6tJ7qyWcuT5bR9qPA==} engines: {node: '>=18'} - cspell@8.16.1: - resolution: {integrity: sha512-ILuCjnY3JPY2oO62PodTQD6n3DGTKTwB+IU1tE9EC6EP2Xw6z3Ir+hO2DO6QlRUmZlGrkGMek5U06nNmztt4eA==} + cspell@8.17.1: + resolution: {integrity: sha512-D0lw8XTXrTycNzOn5DkfPJNUT00X53OgvFDm+0SzhBr1r+na8LEh3CnQ6zKYVU0fL0x8vU82vs4jmGjDho9mPg==} engines: {node: '>=18'} hasBin: true @@ -3701,30 +3721,22 @@ packages: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} engines: {node: '>= 0.4'} - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} date-fns@2.28.0: resolution: {integrity: sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==} engines: {node: '>=0.11'} - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -3872,6 +3884,10 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -3953,20 +3969,20 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + es-abstract@1.23.8: + resolution: {integrity: sha512-lfab8IzDn6EpI1ibZakcgS6WsfEBiB+43cuJo+wgylx1xKXf+Sp+YR3vFuQwC/u3sxYwV8Cxe3B0DpVUu/WiJQ==} engines: {node: '>= 0.4'} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.0: - resolution: {integrity: sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==} + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} es-object-atoms@1.0.0: @@ -3980,8 +3996,8 @@ packages: es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} es6-error@4.1.1: @@ -3997,8 +4013,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.24.0: - resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} hasBin: true @@ -4023,40 +4039,6 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-module-utils@2.12.0: - resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-import@2.31.0: - resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint-plugin-jest-dom@5.5.0: resolution: {integrity: sha512-CRlXfchTr7EgC3tDI7MGHY6QjdJU5Vv2RPaeeGtkXUHnKZf04kgzMPIJUXt4qKCvYWVVIEo9ut9Oq1vgXAykEA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6', yarn: '>=1'} @@ -4067,8 +4049,8 @@ packages: '@testing-library/dom': optional: true - eslint-plugin-jest@28.9.0: - resolution: {integrity: sha512-rLu1s1Wf96TgUUxSw6loVIkNtUjq1Re7A9QdCCHSohnvXEBAjuL420h0T/fMmkQlNsQP2GhQzEUpYHPfxBkvYQ==} + eslint-plugin-jest@28.10.0: + resolution: {integrity: sha512-hyMWUxkBH99HpXT3p8hc7REbEZK3D+nk8vHXGgpB+XXsi0gO4PxMSP+pjfUzb67GnV9yawV9a53eUmcde1CCZA==} engines: {node: ^16.10.0 || ^18.12.0 || >=20.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -4080,14 +4062,14 @@ packages: jest: optional: true - eslint-plugin-react-hooks@5.0.0: - resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} + eslint-plugin-react-hooks@5.1.0: + resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react@7.37.2: - resolution: {integrity: sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==} + eslint-plugin-react@7.37.3: + resolution: {integrity: sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 @@ -4110,8 +4092,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.16.0: - resolution: {integrity: sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==} + eslint@9.17.0: + resolution: {integrity: sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -4353,8 +4335,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -4377,14 +4359,18 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + get-intrinsic@1.2.6: + resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==} engines: {node: '>= 0.4'} get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + get-proto@1.0.0: + resolution: {integrity: sha512-TtLgOcKaF1nMP2ijJnITkE4nRhbpshHhmzKiuhmSniiwWzovoqwqQ8rNuhf0mXJOqIY5iU+QkUe0CkJYrLsG9w==} + engines: {node: '>= 0.4'} + get-stdin@9.0.0: resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} engines: {node: '>=12'} @@ -4397,8 +4383,8 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} glob-parent@5.1.2: @@ -4446,8 +4432,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.13.0: - resolution: {integrity: sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==} + globals@15.14.0: + resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} engines: {node: '>=18'} globalthis@1.0.4: @@ -4457,8 +4443,8 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - gopd@1.1.0: - resolution: {integrity: sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} got@11.8.6: @@ -4493,12 +4479,12 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} has-tostringtag@1.0.2: @@ -4642,8 +4628,8 @@ packages: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} internmap@1.0.1: @@ -4661,8 +4647,8 @@ packages: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} is-arrayish@0.2.1: @@ -4675,11 +4661,12 @@ packages: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + is-boolean-object@1.2.1: + resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} engines: {node: '>= 0.4'} is-callable@1.2.7: @@ -4694,12 +4681,12 @@ packages: resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} is-docker@2.2.1: @@ -4711,8 +4698,9 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -4737,18 +4725,15 @@ packages: is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} - is-number-object@1.0.6: - resolution: {integrity: sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} is-number@7.0.0: @@ -4758,31 +4743,32 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} - is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} is-typedarray@1.0.0: @@ -4792,14 +4778,17 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.1.0: + resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} + engines: {node: '>= 0.4'} - is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} is-windows@0.2.0: resolution: {integrity: sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==} @@ -4873,8 +4862,8 @@ packages: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} - iterator.prototype@1.1.3: - resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==} + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} jackspeak@3.4.2: @@ -5126,10 +5115,6 @@ packages: json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -5277,6 +5262,10 @@ packages: resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} engines: {node: '>=10'} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} @@ -5397,8 +5386,8 @@ packages: peerDependencies: msw: ^2.0.0 - msw@2.6.6: - resolution: {integrity: sha512-npfIIVRHKQX3Lw4aLWX4wBh+lQwpqdZNyJYB5K/+ktK8NhtkdsTxGK7WDrgknozcVyRI7TOqY6yBS9j2FTR+YQ==} + msw@2.7.0: + resolution: {integrity: sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -5496,15 +5485,16 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + engines: {node: '>= 0.4'} object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} object.entries@1.1.8: @@ -5515,12 +5505,8 @@ packages: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} once@1.4.0: @@ -5552,6 +5538,10 @@ packages: outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -5665,13 +5655,13 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - playwright-core@1.49.0: - resolution: {integrity: sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==} + playwright-core@1.49.1: + resolution: {integrity: sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==} engines: {node: '>=18'} hasBin: true - playwright@1.49.0: - resolution: {integrity: sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==} + playwright@1.49.1: + resolution: {integrity: sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==} engines: {node: '>=18'} hasBin: true @@ -5694,16 +5684,16 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.4.1: - resolution: {integrity: sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==} + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} engines: {node: '>=14'} hasBin: true @@ -5845,11 +5835,11 @@ packages: react-select-event@5.5.1: resolution: {integrity: sha512-goAx28y0+iYrbqZA2FeRTreHHs/ZtSuKxtA+J5jpKT5RHPCbVZJ4MqACfPnWyFXsEec+3dP5bCrNTxIX8oYe9A==} - react-select@5.8.3: - resolution: {integrity: sha512-lVswnIq8/iTj1db7XCG74M/3fbGB6ZaluCzvwPGT5ZOjCdL/k0CLWhEK0vCBLuU5bHTEf6Gj8jtSvi+3v+tO1w==} + react-select@5.9.0: + resolution: {integrity: sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} @@ -5886,8 +5876,8 @@ packages: redux@4.1.2: resolution: {integrity: sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==} - reflect.getprototypeof@1.0.4: - resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + reflect.getprototypeof@1.0.9: + resolution: {integrity: sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==} engines: {node: '>= 0.4'} regenerate-unicode-properties@10.2.0: @@ -5903,8 +5893,8 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + regexp.prototype.flags@1.5.3: + resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} engines: {node: '>= 0.4'} regexpu-core@6.2.0: @@ -6009,18 +5999,21 @@ packages: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} - rollup-plugin-visualizer@5.12.0: - resolution: {integrity: sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==} - engines: {node: '>=14'} + rollup-plugin-visualizer@5.13.1: + resolution: {integrity: sha512-vMg8i6BprL8aFm9DKvL2c8AwS8324EgymYQo9o6E26wgVvwMhsJxS37aNL6ZsU7X9iAcMYwdME7gItLfG5fwJg==} + engines: {node: '>=18'} hasBin: true peerDependencies: + rolldown: 1.x rollup: 2.x || 3.x || 4.x peerDependenciesMeta: + rolldown: + optional: true rollup: optional: true - rollup@4.23.0: - resolution: {integrity: sha512-vXB4IT9/KLDrS2WRXmY22sVB2wTsTwkpxjB8Q3mnakTENcYw3FRmfdYDy/acNmls+lHmDazgrRjK/yQ6hQAtwA==} + rollup@4.29.1: + resolution: {integrity: sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6033,8 +6026,8 @@ packages: rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} safe-buffer@5.1.2: @@ -6043,8 +6036,12 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} safe-stable-stringify@2.3.1: @@ -6108,8 +6105,20 @@ packages: shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} signal-exit@3.0.7: @@ -6211,8 +6220,8 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - storybook@8.4.6: - resolution: {integrity: sha512-J6juZSZT2u3PUW0QZYZZYxBq6zU5O0OrkSgkMXGMg/QrS9to9IHmt4FjEMEyACRbXo8POcB/fSXa3VpGe7bv3g==} + storybook@8.4.7: + resolution: {integrity: sha512-RP/nMJxiWyFc8EVMH5gp20ID032Wvk+Yr3lmKidoegto5Iy+2dVQnUoElZb2zpbVXNHWakGuAkfI0dY1Hfp/vw==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -6242,19 +6251,20 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} @@ -6449,9 +6459,6 @@ packages: typescript: optional: true - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -6490,27 +6497,27 @@ packages: resolution: {integrity: sha512-RPYt6dKyemXJe7I6oNstcH24myUGSReicxcHTvCLgzm4e0n8y05dGvcGB15/SoPRBmhlMthWQ9pvKyL81ko8nQ==} engines: {node: '>=16'} - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - typescript-eslint@8.18.0: - resolution: {integrity: sha512-Xq2rRjn6tzVpAyHr3+nmSg1/9k9aIHnJ2iZeOH7cfGOWqTkXTm3kwpQglEuLGdNrYvPF+2gtAs+/KF5rjVo+WQ==} + typescript-eslint@8.19.0: + resolution: {integrity: sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -6521,8 +6528,9 @@ packages: engines: {node: '>=14.17'} hasBin: true - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} @@ -6579,11 +6587,11 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - use-isomorphic-layout-effect@1.1.2: - resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + use-isomorphic-layout-effect@1.2.0: + resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} peerDependencies: '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -6616,35 +6624,40 @@ packages: resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} engines: {node: '>=0.6.0'} - vite-plugin-wasm@3.3.0: - resolution: {integrity: sha512-tVhz6w+W9MVsOCHzxo6SSMSswCeIw4HTrXEi6qL3IRzATl83jl09JVO1djBqPSwfjgnpVHNLYcaMbaDX5WB/pg==} + vite-plugin-wasm@3.4.1: + resolution: {integrity: sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==} peerDependencies: - vite: ^2 || ^3 || ^4 || ^5 + vite: ^2 || ^3 || ^4 || ^5 || ^6 - vite-tsconfig-paths@5.1.3: - resolution: {integrity: sha512-0bz+PDlLpGfP2CigeSKL9NFTF1KtXkeHGZSSaGQSuPZH77GhoiQaA8IjYgOaynSuwlDTolSUEU0ErVvju3NURg==} + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} peerDependencies: vite: '*' peerDependenciesMeta: vite: optional: true - vite@5.4.8: - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.0.6: + resolution: {integrity: sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -6659,6 +6672,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vscode-languageserver-textdocument@1.0.12: resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} @@ -6723,25 +6740,27 @@ packages: resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} engines: {node: '>=12'} - whatwg-url@14.0.0: - resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + whatwg-url@14.1.0: + resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==} engines: {node: '>=18'} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} - which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} engines: {node: '>= 0.4'} - which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - which-typed-array@1.1.16: - resolution: {integrity: sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==} + which-typed-array@1.1.18: + resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} engines: {node: '>= 0.4'} which@1.3.1: @@ -6878,13 +6897,13 @@ packages: resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} engines: {node: '>= 10'} - zod-to-json-schema@3.23.5: - resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==} + zod-to-json-schema@3.24.1: + resolution: {integrity: sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==} peerDependencies: - zod: ^3.23.3 + zod: ^3.24.1 - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.24.1: + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} zone.js@0.14.7: resolution: {integrity: sha512-0w6DGkX2BPuiK/NLf+4A8FLE43QwBfuqz2dVgi/40Rj1WmqUskCqj329O/pwrqFJLG5X8wkeG2RhIAro441xtg==} @@ -7652,7 +7671,7 @@ snapshots: '@babel/types': 7.26.0 esutils: 2.0.3 - '@babel/preset-react@7.25.9(@babel/core@7.26.0)': + '@babel/preset-react@7.26.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.25.9 @@ -7717,35 +7736,33 @@ snapshots: '@types/tough-cookie': 4.0.5 tough-cookie: 4.1.4 - '@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3)': + '@codemirror/autocomplete@6.18.4': dependencies: '@codemirror/language': 6.10.6 - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 '@lezer/common': 1.2.3 '@codemirror/commands@6.7.1': dependencies: '@codemirror/language': 6.10.6 - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 '@lezer/common': 1.2.3 - '@codemirror/lang-sql@6.8.0(@codemirror/view@6.35.0)': + '@codemirror/lang-sql@6.8.0': dependencies: - '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3) + '@codemirror/autocomplete': 6.18.4 '@codemirror/language': 6.10.6 - '@codemirror/state': 6.4.1 + '@codemirror/state': 6.5.0 '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 - transitivePeerDependencies: - - '@codemirror/view' '@codemirror/language@6.10.6': dependencies: - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 @@ -7753,40 +7770,42 @@ snapshots: '@codemirror/lint@6.4.2': dependencies: - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 crelt: 1.0.6 '@codemirror/search@6.5.3': dependencies: - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 crelt: 1.0.6 - '@codemirror/state@6.4.1': {} + '@codemirror/state@6.5.0': + dependencies: + '@marijn/find-cluster-break': 1.0.2 '@codemirror/theme-one-dark@6.1.2': dependencies: '@codemirror/language': 6.10.6 - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 '@lezer/highlight': 1.2.1 - '@codemirror/view@6.35.0': + '@codemirror/view@6.36.1': dependencies: - '@codemirror/state': 6.4.1 + '@codemirror/state': 6.5.0 style-mod: 4.1.2 w3c-keyname: 2.2.8 '@colors/colors@1.6.0': {} - '@cspell/cspell-bundled-dicts@8.16.1': + '@cspell/cspell-bundled-dicts@8.17.1': dependencies: '@cspell/dict-ada': 4.0.5 '@cspell/dict-al': 1.0.3 '@cspell/dict-aws': 4.0.7 '@cspell/dict-bash': 4.1.8 - '@cspell/dict-companies': 3.1.7 + '@cspell/dict-companies': 3.1.12 '@cspell/dict-cpp': 6.0.2 '@cspell/dict-cryptocurrencies': 5.0.3 '@cspell/dict-csharp': 4.0.5 @@ -7804,7 +7823,7 @@ snapshots: '@cspell/dict-fonts': 4.0.3 '@cspell/dict-fsharp': 1.0.4 '@cspell/dict-fullstack': 3.2.3 - '@cspell/dict-gaming-terms': 1.0.8 + '@cspell/dict-gaming-terms': 1.0.10 '@cspell/dict-git': 3.0.3 '@cspell/dict-golang': 6.0.17 '@cspell/dict-google': 1.0.4 @@ -7821,7 +7840,7 @@ snapshots: '@cspell/dict-markdown': 2.0.7(@cspell/dict-css@4.0.16)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.10)(@cspell/dict-typescript@3.1.11) '@cspell/dict-monkeyc': 1.0.9 '@cspell/dict-node': 5.0.5 - '@cspell/dict-npm': 5.1.16 + '@cspell/dict-npm': 5.1.21 '@cspell/dict-php': 4.0.13 '@cspell/dict-powershell': 5.0.13 '@cspell/dict-public-licenses': 2.0.11 @@ -7830,7 +7849,7 @@ snapshots: '@cspell/dict-ruby': 5.0.7 '@cspell/dict-rust': 4.0.10 '@cspell/dict-scala': 5.0.6 - '@cspell/dict-software-terms': 4.1.18 + '@cspell/dict-software-terms': 4.2.2 '@cspell/dict-sql': 2.1.8 '@cspell/dict-svelte': 1.0.5 '@cspell/dict-swift': 2.0.4 @@ -7838,19 +7857,19 @@ snapshots: '@cspell/dict-typescript': 3.1.11 '@cspell/dict-vue': 3.0.3 - '@cspell/cspell-json-reporter@8.16.1': + '@cspell/cspell-json-reporter@8.17.1': dependencies: - '@cspell/cspell-types': 8.16.1 + '@cspell/cspell-types': 8.17.1 - '@cspell/cspell-pipe@8.16.1': {} + '@cspell/cspell-pipe@8.17.1': {} - '@cspell/cspell-resolver@8.16.1': + '@cspell/cspell-resolver@8.17.1': dependencies: global-directory: 4.0.1 - '@cspell/cspell-service-bus@8.16.1': {} + '@cspell/cspell-service-bus@8.17.1': {} - '@cspell/cspell-types@8.16.1': {} + '@cspell/cspell-types@8.17.1': {} '@cspell/dict-ada@4.0.5': {} @@ -7860,7 +7879,7 @@ snapshots: '@cspell/dict-bash@4.1.8': {} - '@cspell/dict-companies@3.1.7': {} + '@cspell/dict-companies@3.1.12': {} '@cspell/dict-cpp@6.0.2': {} @@ -7898,7 +7917,7 @@ snapshots: '@cspell/dict-fullstack@3.2.3': {} - '@cspell/dict-gaming-terms@1.0.8': {} + '@cspell/dict-gaming-terms@1.0.10': {} '@cspell/dict-git@3.0.3': {} @@ -7937,7 +7956,7 @@ snapshots: '@cspell/dict-node@5.0.5': {} - '@cspell/dict-npm@5.1.16': {} + '@cspell/dict-npm@5.1.21': {} '@cspell/dict-php@4.0.13': {} @@ -7957,7 +7976,7 @@ snapshots: '@cspell/dict-scala@5.0.6': {} - '@cspell/dict-software-terms@4.1.18': {} + '@cspell/dict-software-terms@4.2.2': {} '@cspell/dict-sql@2.1.8': {} @@ -7971,15 +7990,16 @@ snapshots: '@cspell/dict-vue@3.0.3': {} - '@cspell/dynamic-import@8.16.1': + '@cspell/dynamic-import@8.17.1': dependencies: + '@cspell/url': 8.17.1 import-meta-resolve: 4.1.0 - '@cspell/filetypes@8.16.1': {} + '@cspell/filetypes@8.17.1': {} - '@cspell/strong-weak-map@8.16.1': {} + '@cspell/strong-weak-map@8.17.1': {} - '@cspell/url@8.16.1': {} + '@cspell/url@8.17.1': {} '@dabh/diagnostics@2.0.2': dependencies: @@ -8142,147 +8162,150 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.24.0': + '@esbuild/aix-ppc64@0.24.2': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.24.0': + '@esbuild/android-arm64@0.24.2': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.24.0': + '@esbuild/android-arm@0.24.2': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.24.0': + '@esbuild/android-x64@0.24.2': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.24.0': + '@esbuild/darwin-arm64@0.24.2': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.24.0': + '@esbuild/darwin-x64@0.24.2': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.24.0': + '@esbuild/freebsd-arm64@0.24.2': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.24.0': + '@esbuild/freebsd-x64@0.24.2': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.24.0': + '@esbuild/linux-arm64@0.24.2': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.24.0': + '@esbuild/linux-arm@0.24.2': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.24.0': + '@esbuild/linux-ia32@0.24.2': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.24.0': + '@esbuild/linux-loong64@0.24.2': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.24.0': + '@esbuild/linux-mips64el@0.24.2': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.24.0': + '@esbuild/linux-ppc64@0.24.2': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.24.0': + '@esbuild/linux-riscv64@0.24.2': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.24.0': + '@esbuild/linux-s390x@0.24.2': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.24.0': + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.24.0': + '@esbuild/netbsd-x64@0.24.2': optional: true - '@esbuild/openbsd-arm64@0.24.0': + '@esbuild/openbsd-arm64@0.24.2': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.24.0': + '@esbuild/openbsd-x64@0.24.2': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.24.0': + '@esbuild/sunos-x64@0.24.2': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.24.0': + '@esbuild/win32-arm64@0.24.2': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.24.0': + '@esbuild/win32-ia32@0.24.2': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.24.0': + '@esbuild/win32-x64@0.24.2': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.16.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.17.0)': dependencies: - eslint: 9.16.0 + eslint: 9.17.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -8313,7 +8336,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.16.0': {} + '@eslint/js@9.17.0': {} '@eslint/object-schema@2.1.5': {} @@ -8334,7 +8357,7 @@ snapshots: '@gar/promisify@1.1.3': {} - '@grpc/grpc-js@1.12.2': + '@grpc/grpc-js@1.12.5': dependencies: '@grpc/proto-loader': 0.7.13 '@js-sdsl/ordered-map': 4.4.2 @@ -8365,16 +8388,27 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} - '@inquirer/confirm@5.0.2(@types/node@20.17.9)': + '@ianvs/prettier-plugin-sort-imports@4.4.0(prettier@3.4.2)': + dependencies: + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + prettier: 3.4.2 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + '@inquirer/confirm@5.0.2(@types/node@20.17.11)': dependencies: - '@inquirer/core': 10.1.0(@types/node@20.17.9) - '@inquirer/type': 3.0.1(@types/node@20.17.9) - '@types/node': 20.17.9 + '@inquirer/core': 10.1.0(@types/node@20.17.11) + '@inquirer/type': 3.0.1(@types/node@20.17.11) + '@types/node': 20.17.11 - '@inquirer/core@10.1.0(@types/node@20.17.9)': + '@inquirer/core@10.1.0(@types/node@20.17.11)': dependencies: '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@20.17.9) + '@inquirer/type': 3.0.1(@types/node@20.17.11) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -8387,9 +8421,9 @@ snapshots: '@inquirer/figures@1.0.8': {} - '@inquirer/type@3.0.1(@types/node@20.17.9)': + '@inquirer/type@3.0.1(@types/node@20.17.11)': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@isaacs/cliui@8.0.2': dependencies: @@ -8413,7 +8447,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -8426,14 +8460,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.3.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + jest-config: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -8462,7 +8496,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -8480,7 +8514,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.17.9 + '@types/node': 20.17.11 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -8502,7 +8536,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.17.9 + '@types/node': 20.17.11 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -8572,15 +8606,15 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/yargs': 17.0.33 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.4.2(typescript@5.7.2)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.4.2(typescript@5.7.2)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1))': dependencies: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.7.2) - vite: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + vite: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) optionalDependencies: typescript: 5.7.2 @@ -8632,6 +8666,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@marijn/find-cluster-break@1.0.2': {} + '@mswjs/interceptors@0.37.1': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -8776,82 +8812,82 @@ snapshots: '@open-draft/until@2.1.0': {} - '@opentelemetry/api-logs@0.55.0': + '@opentelemetry/api-logs@0.57.0': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api@1.9.0': {} - '@opentelemetry/context-zone-peer-dep@1.28.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7)': + '@opentelemetry/context-zone-peer-dep@1.30.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7)': dependencies: '@opentelemetry/api': 1.9.0 zone.js: 0.14.7 - '@opentelemetry/context-zone@1.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-zone@1.30.0(@opentelemetry/api@1.9.0)': dependencies: - '@opentelemetry/context-zone-peer-dep': 1.28.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7) + '@opentelemetry/context-zone-peer-dep': 1.30.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7) zone.js: 0.14.7 transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/core@1.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.27.0 + '@opentelemetry/semantic-conventions': 1.28.0 - '@opentelemetry/exporter-trace-otlp-http@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/exporter-trace-otlp-http@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-document-load@0.42.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-document-load@0.44.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-web': 1.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-web': 1.30.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-fetch@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-fetch@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-web': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.27.0 + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-web': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-user-interaction@0.42.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7)': + '@opentelemetry/instrumentation-user-interaction@0.44.0(@opentelemetry/api@1.9.0)(zone.js@0.14.7)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-web': 1.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-web': 1.30.0(@opentelemetry/api@1.9.0) zone.js: 0.14.7 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-xml-http-request@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-xml-http-request@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-web': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.27.0 + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-web': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.55.0 + '@opentelemetry/api-logs': 0.57.0 '@types/shimmer': 1.2.0 import-in-the-middle: 1.8.1 require-in-the-middle: 7.3.0 @@ -8860,71 +8896,69 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/otlp-exporter-base@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-exporter-base@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/otlp-transformer@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.55.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api-logs': 0.57.0 + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.0(@opentelemetry/api@1.9.0) protobufjs: 7.4.0 - '@opentelemetry/propagator-b3@1.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/propagator-b3@1.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources@1.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/resources@1.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.27.0 + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 - '@opentelemetry/sdk-logs@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-logs@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.55.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api-logs': 0.57.0 + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics@1.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-metrics@1.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base@1.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.27.0 + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 - '@opentelemetry/sdk-trace-web@1.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-trace-web@1.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.27.0 - - '@opentelemetry/semantic-conventions@1.27.0': {} + '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 '@opentelemetry/semantic-conventions@1.28.0': {} '@pkgjs/parseargs@0.11.0': optional: true - '@protobuf-ts/grpc-transport@2.9.4(@grpc/grpc-js@1.12.2)': + '@protobuf-ts/grpc-transport@2.9.4(@grpc/grpc-js@1.12.5)': dependencies: - '@grpc/grpc-js': 1.12.2 + '@grpc/grpc-js': 1.12.5 '@protobuf-ts/runtime': 2.9.4 '@protobuf-ts/runtime-rpc': 2.9.4 @@ -8995,63 +9029,70 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@rollup/pluginutils@5.1.3(rollup@4.23.0)': + '@rollup/pluginutils@5.1.3(rollup@4.29.1)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.23.0 + rollup: 4.29.1 + + '@rollup/rollup-android-arm-eabi@4.29.1': + optional: true + + '@rollup/rollup-android-arm64@4.29.1': + optional: true - '@rollup/rollup-android-arm-eabi@4.23.0': + '@rollup/rollup-darwin-arm64@4.29.1': optional: true - '@rollup/rollup-android-arm64@4.23.0': + '@rollup/rollup-darwin-x64@4.29.1': optional: true - '@rollup/rollup-darwin-arm64@4.23.0': + '@rollup/rollup-freebsd-arm64@4.29.1': optional: true - '@rollup/rollup-darwin-x64@4.23.0': + '@rollup/rollup-freebsd-x64@4.29.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.23.0': + '@rollup/rollup-linux-arm-gnueabihf@4.29.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.23.0': + '@rollup/rollup-linux-arm-musleabihf@4.29.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.23.0': + '@rollup/rollup-linux-arm64-gnu@4.29.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.23.0': + '@rollup/rollup-linux-arm64-musl@4.29.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': + '@rollup/rollup-linux-loongarch64-gnu@4.29.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.23.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.23.0': + '@rollup/rollup-linux-riscv64-gnu@4.29.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.23.0': + '@rollup/rollup-linux-s390x-gnu@4.29.1': optional: true - '@rollup/rollup-linux-x64-musl@4.23.0': + '@rollup/rollup-linux-x64-gnu@4.29.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.23.0': + '@rollup/rollup-linux-x64-musl@4.29.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.23.0': + '@rollup/rollup-win32-arm64-msvc@4.29.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.23.0': + '@rollup/rollup-win32-ia32-msvc@4.29.1': optional: true - '@rtsao/scc@1.1.0': {} + '@rollup/rollup-win32-x64-msvc@4.29.1': + optional: true '@sideway/address@4.1.5': dependencies: @@ -9073,45 +9114,45 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.0 - '@storybook/addon-actions@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/addon-actions@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.3.1 - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) uuid: 9.0.1 - '@storybook/addon-controls@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/addon-controls@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) ts-dedent: 2.2.0 - '@storybook/addon-toolbars@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/addon-toolbars@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) - '@storybook/builder-vite@8.4.6(storybook@8.4.6(prettier@3.4.1))(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1))': + '@storybook/builder-vite@8.4.7(storybook@8.4.7(prettier@3.4.2))(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1))': dependencies: - '@storybook/csf-plugin': 8.4.6(storybook@8.4.6(prettier@3.4.1)) + '@storybook/csf-plugin': 8.4.7(storybook@8.4.7(prettier@3.4.2)) browser-assert: 1.2.1 - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) ts-dedent: 2.2.0 - vite: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + vite: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) - '@storybook/components@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/components@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) - '@storybook/core@8.4.6(prettier@3.4.1)': + '@storybook/core@8.4.7(prettier@3.4.2)': dependencies: '@storybook/csf': 0.1.12 better-opn: 3.0.2 browser-assert: 1.2.1 - esbuild: 0.24.0 - esbuild-register: 3.6.0(esbuild@0.24.0) + esbuild: 0.24.2 + esbuild-register: 3.6.0(esbuild@0.24.2) jsdoc-type-pratt-parser: 4.1.0 process: 0.11.10 recast: 0.23.9 @@ -9119,15 +9160,15 @@ snapshots: util: 0.12.5 ws: 8.18.0 optionalDependencies: - prettier: 3.4.1 + prettier: 3.4.2 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@storybook/csf-plugin@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/csf-plugin@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) unplugin: 1.16.0 '@storybook/csf@0.1.12': @@ -9136,56 +9177,56 @@ snapshots: '@storybook/global@5.0.0': {} - '@storybook/manager-api@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/manager-api@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) - '@storybook/preview-api@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/preview-api@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) - '@storybook/react-dom-shim@8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.6(prettier@3.4.1))': + '@storybook/react-dom-shim@8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@3.4.2))': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) - '@storybook/react-vite@8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.23.0)(storybook@8.4.6(prettier@3.4.1))(typescript@5.7.2)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1))': + '@storybook/react-vite@8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.29.1)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.4.2(typescript@5.7.2)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)) - '@rollup/pluginutils': 5.1.3(rollup@4.23.0) - '@storybook/builder-vite': 8.4.6(storybook@8.4.6(prettier@3.4.1))(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)) - '@storybook/react': 8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.6(prettier@3.4.1))(typescript@5.7.2) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.4.2(typescript@5.7.2)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)) + '@rollup/pluginutils': 5.1.3(rollup@4.29.1) + '@storybook/builder-vite': 8.4.7(storybook@8.4.7(prettier@3.4.2))(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)) + '@storybook/react': 8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2) find-up: 5.0.0 magic-string: 0.30.14 react: 18.3.1 react-docgen: 7.1.0 react-dom: 18.3.1(react@18.3.1) resolve: 1.22.8 - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) tsconfig-paths: 4.2.0 - vite: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + vite: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) transitivePeerDependencies: - '@storybook/test' - rollup - supports-color - typescript - '@storybook/react@8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.6(prettier@3.4.1))(typescript@5.7.2)': + '@storybook/react@8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2)': dependencies: - '@storybook/components': 8.4.6(storybook@8.4.6(prettier@3.4.1)) + '@storybook/components': 8.4.7(storybook@8.4.7(prettier@3.4.2)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.4.6(storybook@8.4.6(prettier@3.4.1)) - '@storybook/preview-api': 8.4.6(storybook@8.4.6(prettier@3.4.1)) - '@storybook/react-dom-shim': 8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.6(prettier@3.4.1)) - '@storybook/theming': 8.4.6(storybook@8.4.6(prettier@3.4.1)) + '@storybook/manager-api': 8.4.7(storybook@8.4.7(prettier@3.4.2)) + '@storybook/preview-api': 8.4.7(storybook@8.4.7(prettier@3.4.2)) + '@storybook/react-dom-shim': 8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@3.4.2)) + '@storybook/theming': 8.4.7(storybook@8.4.7(prettier@3.4.2)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) optionalDependencies: typescript: 5.7.2 - '@storybook/test-runner@0.20.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0)(storybook@8.4.6(prettier@3.4.1))': + '@storybook/test-runner@0.21.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0)(storybook@8.4.7(prettier@3.4.2))': dependencies: '@babel/core': 7.26.0 '@babel/generator': 7.26.2 @@ -9193,20 +9234,20 @@ snapshots: '@babel/types': 7.26.0 '@jest/types': 29.6.3 '@storybook/csf': 0.1.12 - '@swc/core': 1.9.3 - '@swc/jest': 0.2.37(@swc/core@1.9.3) + '@swc/core': 1.10.4 + '@swc/jest': 0.2.37(@swc/core@1.10.4) expect-playwright: 0.8.0 - jest: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + jest: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) jest-circus: 29.7.0(babel-plugin-macros@3.1.0) jest-environment-node: 29.7.0 jest-junit: 16.0.0 - jest-playwright-preset: 4.0.0(jest-circus@29.7.0(babel-plugin-macros@3.1.0))(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0)) + jest-playwright-preset: 4.0.0(jest-circus@29.7.0(babel-plugin-macros@3.1.0))(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0)) jest-runner: 29.7.0 jest-serializer-html: 7.1.0 - jest-watch-typeahead: 2.2.2(jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0)) + jest-watch-typeahead: 2.2.2(jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0)) nyc: 15.1.0 - playwright: 1.49.0 - storybook: 8.4.6(prettier@3.4.1) + playwright: 1.49.1 + storybook: 8.4.7(prettier@3.4.2) transitivePeerDependencies: - '@swc/helpers' - '@types/node' @@ -9216,9 +9257,9 @@ snapshots: - supports-color - ts-node - '@storybook/theming@8.4.6(storybook@8.4.6(prettier@3.4.1))': + '@storybook/theming@8.4.7(storybook@8.4.7(prettier@3.4.2))': dependencies: - storybook: 8.4.6(prettier@3.4.1) + storybook: 8.4.7(prettier@3.4.2) '@styled-system/background@5.1.2': dependencies: @@ -9271,62 +9312,62 @@ snapshots: '@styled-system/core': 5.1.2 '@styled-system/css': 5.1.5 - '@swc/core-darwin-arm64@1.9.3': + '@swc/core-darwin-arm64@1.10.4': optional: true - '@swc/core-darwin-x64@1.9.3': + '@swc/core-darwin-x64@1.10.4': optional: true - '@swc/core-linux-arm-gnueabihf@1.9.3': + '@swc/core-linux-arm-gnueabihf@1.10.4': optional: true - '@swc/core-linux-arm64-gnu@1.9.3': + '@swc/core-linux-arm64-gnu@1.10.4': optional: true - '@swc/core-linux-arm64-musl@1.9.3': + '@swc/core-linux-arm64-musl@1.10.4': optional: true - '@swc/core-linux-x64-gnu@1.9.3': + '@swc/core-linux-x64-gnu@1.10.4': optional: true - '@swc/core-linux-x64-musl@1.9.3': + '@swc/core-linux-x64-musl@1.10.4': optional: true - '@swc/core-win32-arm64-msvc@1.9.3': + '@swc/core-win32-arm64-msvc@1.10.4': optional: true - '@swc/core-win32-ia32-msvc@1.9.3': + '@swc/core-win32-ia32-msvc@1.10.4': optional: true - '@swc/core-win32-x64-msvc@1.9.3': + '@swc/core-win32-x64-msvc@1.10.4': optional: true - '@swc/core@1.9.3': + '@swc/core@1.10.4': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.17 optionalDependencies: - '@swc/core-darwin-arm64': 1.9.3 - '@swc/core-darwin-x64': 1.9.3 - '@swc/core-linux-arm-gnueabihf': 1.9.3 - '@swc/core-linux-arm64-gnu': 1.9.3 - '@swc/core-linux-arm64-musl': 1.9.3 - '@swc/core-linux-x64-gnu': 1.9.3 - '@swc/core-linux-x64-musl': 1.9.3 - '@swc/core-win32-arm64-msvc': 1.9.3 - '@swc/core-win32-ia32-msvc': 1.9.3 - '@swc/core-win32-x64-msvc': 1.9.3 + '@swc/core-darwin-arm64': 1.10.4 + '@swc/core-darwin-x64': 1.10.4 + '@swc/core-linux-arm-gnueabihf': 1.10.4 + '@swc/core-linux-arm64-gnu': 1.10.4 + '@swc/core-linux-arm64-musl': 1.10.4 + '@swc/core-linux-x64-gnu': 1.10.4 + '@swc/core-linux-x64-musl': 1.10.4 + '@swc/core-win32-arm64-msvc': 1.10.4 + '@swc/core-win32-ia32-msvc': 1.10.4 + '@swc/core-win32-x64-msvc': 1.10.4 '@swc/counter@0.1.3': {} - '@swc/jest@0.2.37(@swc/core@1.9.3)': + '@swc/jest@0.2.37(@swc/core@1.10.4)': dependencies: '@jest/create-cache-key-function': 29.7.0 - '@swc/core': 1.9.3 + '@swc/core': 1.10.4 '@swc/counter': 0.1.3 jsonc-parser: 3.3.1 - '@swc/plugin-styled-components@5.0.0': + '@swc/plugin-styled-components@6.0.2': dependencies: '@swc/counter': 0.1.3 @@ -9359,7 +9400,7 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react@16.0.1(@testing-library/dom@10.1.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@testing-library/react@16.1.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.26.0 '@testing-library/dom': 10.1.0 @@ -9402,7 +9443,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 4.2.0 - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/responselike': 1.0.0 '@types/cookie@0.6.0': {} @@ -9441,11 +9482,11 @@ snapshots: '@types/fs-extra@9.0.13': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/history@4.7.11': {} @@ -9468,20 +9509,18 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 '@types/jsdom@21.1.7': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} - '@types/keyv@4.2.0': dependencies: keyv: 4.5.4 @@ -9490,9 +9529,9 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 - '@types/node@20.17.9': + '@types/node@20.17.11': dependencies: undici-types: 6.19.8 @@ -9500,7 +9539,7 @@ snapshots: '@types/plist@3.0.2': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 xmlbuilder: 15.1.1 optional: true @@ -9525,7 +9564,7 @@ snapshots: '@types/history': 4.7.11 '@types/react': 18.3.12 - '@types/react-transition-group@4.4.11': + '@types/react-transition-group@4.4.12(@types/react@18.3.12)': dependencies: '@types/react': 18.3.12 @@ -9538,7 +9577,7 @@ snapshots: '@types/responselike@1.0.0': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/shimmer@1.2.0': {} @@ -9554,12 +9593,12 @@ snapshots: '@types/tar-fs@2.0.4': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/tar-stream': 2.2.2 '@types/tar-stream@2.2.2': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/tough-cookie@4.0.5': {} @@ -9572,7 +9611,7 @@ snapshots: '@types/wait-on@5.3.4': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/webidl-conversions@7.0.1': {} @@ -9592,18 +9631,18 @@ snapshots: '@types/yauzl@2.10.0': dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 optional: true - '@typescript-eslint/eslint-plugin@8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2)': + '@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - '@typescript-eslint/scope-manager': 8.18.0 - '@typescript-eslint/type-utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.18.0 - eslint: 9.16.0 + '@typescript-eslint/parser': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.19.0 + '@typescript-eslint/type-utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.19.0 + eslint: 9.17.0 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -9612,40 +9651,40 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2)': + '@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2)': dependencies: - '@typescript-eslint/scope-manager': 8.18.0 - '@typescript-eslint/types': 8.18.0 - '@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.18.0 + '@typescript-eslint/scope-manager': 8.19.0 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.19.0 debug: 4.3.7 - eslint: 9.16.0 + eslint: 9.17.0 typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.18.0': + '@typescript-eslint/scope-manager@8.19.0': dependencies: - '@typescript-eslint/types': 8.18.0 - '@typescript-eslint/visitor-keys': 8.18.0 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/visitor-keys': 8.19.0 - '@typescript-eslint/type-utils@8.18.0(eslint@9.16.0)(typescript@5.7.2)': + '@typescript-eslint/type-utils@8.19.0(eslint@9.17.0)(typescript@5.7.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2) - '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) + '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) + '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) debug: 4.3.7 - eslint: 9.16.0 + eslint: 9.17.0 ts-api-utils: 1.3.0(typescript@5.7.2) typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.18.0': {} + '@typescript-eslint/types@8.19.0': {} - '@typescript-eslint/typescript-estree@8.18.0(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@8.19.0(typescript@5.7.2)': dependencies: - '@typescript-eslint/types': 8.18.0 - '@typescript-eslint/visitor-keys': 8.18.0 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/visitor-keys': 8.19.0 debug: 4.3.7 fast-glob: 3.3.2 is-glob: 4.0.3 @@ -9656,47 +9695,47 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.18.0(eslint@9.16.0)(typescript@5.7.2)': + '@typescript-eslint/utils@8.19.0(eslint@9.17.0)(typescript@5.7.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.16.0) - '@typescript-eslint/scope-manager': 8.18.0 - '@typescript-eslint/types': 8.18.0 - '@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2) - eslint: 9.16.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.17.0) + '@typescript-eslint/scope-manager': 8.19.0 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) + eslint: 9.17.0 typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.18.0': + '@typescript-eslint/visitor-keys@8.19.0': dependencies: - '@typescript-eslint/types': 8.18.0 + '@typescript-eslint/types': 8.19.0 eslint-visitor-keys: 4.2.0 - '@uiw/codemirror-extensions-basic-setup@4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)': + '@uiw/codemirror-extensions-basic-setup@4.23.7(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.7.1)(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)': dependencies: - '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3) + '@codemirror/autocomplete': 6.18.4 '@codemirror/commands': 6.7.1 '@codemirror/language': 6.10.6 '@codemirror/lint': 6.4.2 '@codemirror/search': 6.5.3 - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 - '@uiw/codemirror-themes@4.23.6(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)': + '@uiw/codemirror-themes@4.23.7(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)': dependencies: '@codemirror/language': 6.10.6 - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 - '@uiw/react-codemirror@4.23.6(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@uiw/react-codemirror@4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.26.0 '@codemirror/commands': 6.7.1 - '@codemirror/state': 6.4.1 + '@codemirror/state': 6.5.0 '@codemirror/theme-one-dark': 6.1.2 - '@codemirror/view': 6.35.0 - '@uiw/codemirror-extensions-basic-setup': 4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0) - codemirror: 6.0.1(@lezer/common@1.2.3) + '@codemirror/view': 6.36.1 + '@uiw/codemirror-extensions-basic-setup': 4.23.7(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.7.1)(@codemirror/language@6.10.6)(@codemirror/lint@6.4.2)(@codemirror/search@6.5.3)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1) + codemirror: 6.0.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -9705,10 +9744,10 @@ snapshots: - '@codemirror/lint' - '@codemirror/search' - '@vitejs/plugin-react-swc@3.7.2(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1))': + '@vitejs/plugin-react-swc@3.7.2(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1))': dependencies: - '@swc/core': 1.9.3 - vite: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + '@swc/core': 1.10.4 + vite: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) transitivePeerDependencies: - '@swc/helpers' @@ -9744,7 +9783,7 @@ snapshots: abbrev@1.1.1: {} - ace-builds@1.36.5: {} + ace-builds@1.37.1: {} acorn-globals@7.0.1: dependencies: @@ -9927,72 +9966,55 @@ snapshots: aria-query@5.3.2: {} - array-buffer-byte-length@1.0.1: + array-buffer-byte-length@1.0.2: dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 + call-bound: 1.0.3 + is-array-buffer: 3.0.5 array-includes@3.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - is-string: 1.0.7 + get-intrinsic: 1.2.6 + is-string: 1.1.1 array-timsort@1.0.3: {} array.prototype.findlast@1.2.5: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - array.prototype.findlastindex@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.flat@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.flatmap@1.3.2: + array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-shim-unscopables: 1.0.2 array.prototype.tosorted@1.1.4: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - arraybuffer.prototype.slice@1.0.3: + arraybuffer.prototype.slice@1.0.4: dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 + get-intrinsic: 1.2.6 + is-array-buffer: 3.0.5 assert-plus@1.0.0: optional: true @@ -10289,14 +10311,23 @@ snapshots: package-hash: 4.0.0 write-file-atomic: 3.0.3 - call-bind@1.0.7: + call-bind-apply-helpers@1.0.1: dependencies: - es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-define-property: 1.0.1 + get-intrinsic: 1.2.6 set-function-length: 1.2.2 + call-bound@1.0.3: + dependencies: + call-bind-apply-helpers: 1.0.1 + get-intrinsic: 1.2.6 + callsites@3.1.0: {} camelcase@5.3.1: {} @@ -10382,17 +10413,15 @@ snapshots: co@4.6.0: {} - codemirror@6.0.1(@lezer/common@1.2.3): + codemirror@6.0.1: dependencies: - '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3) + '@codemirror/autocomplete': 6.18.4 '@codemirror/commands': 6.7.1 '@codemirror/language': 6.10.6 '@codemirror/lint': 6.4.2 '@codemirror/search': 6.5.3 - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 - transitivePeerDependencies: - - '@lezer/common' + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.1 collect-v8-coverage@1.0.2: {} @@ -10501,13 +10530,13 @@ snapshots: buffer: 5.7.1 optional: true - create-jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0): + create-jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + jest-config: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -10529,59 +10558,59 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - cspell-config-lib@8.16.1: + cspell-config-lib@8.17.1: dependencies: - '@cspell/cspell-types': 8.16.1 + '@cspell/cspell-types': 8.17.1 comment-json: 4.2.5 yaml: 2.6.1 - cspell-dictionary@8.16.1: + cspell-dictionary@8.17.1: dependencies: - '@cspell/cspell-pipe': 8.16.1 - '@cspell/cspell-types': 8.16.1 - cspell-trie-lib: 8.16.1 + '@cspell/cspell-pipe': 8.17.1 + '@cspell/cspell-types': 8.17.1 + cspell-trie-lib: 8.17.1 fast-equals: 5.0.1 - cspell-gitignore@8.16.1: + cspell-gitignore@8.17.1: dependencies: - '@cspell/url': 8.16.1 - cspell-glob: 8.16.1 - cspell-io: 8.16.1 + '@cspell/url': 8.17.1 + cspell-glob: 8.17.1 + cspell-io: 8.17.1 find-up-simple: 1.0.0 - cspell-glob@8.16.1: + cspell-glob@8.17.1: dependencies: - '@cspell/url': 8.16.1 + '@cspell/url': 8.17.1 micromatch: 4.0.8 - cspell-grammar@8.16.1: + cspell-grammar@8.17.1: dependencies: - '@cspell/cspell-pipe': 8.16.1 - '@cspell/cspell-types': 8.16.1 + '@cspell/cspell-pipe': 8.17.1 + '@cspell/cspell-types': 8.17.1 - cspell-io@8.16.1: + cspell-io@8.17.1: dependencies: - '@cspell/cspell-service-bus': 8.16.1 - '@cspell/url': 8.16.1 + '@cspell/cspell-service-bus': 8.17.1 + '@cspell/url': 8.17.1 - cspell-lib@8.16.1: + cspell-lib@8.17.1: dependencies: - '@cspell/cspell-bundled-dicts': 8.16.1 - '@cspell/cspell-pipe': 8.16.1 - '@cspell/cspell-resolver': 8.16.1 - '@cspell/cspell-types': 8.16.1 - '@cspell/dynamic-import': 8.16.1 - '@cspell/filetypes': 8.16.1 - '@cspell/strong-weak-map': 8.16.1 - '@cspell/url': 8.16.1 + '@cspell/cspell-bundled-dicts': 8.17.1 + '@cspell/cspell-pipe': 8.17.1 + '@cspell/cspell-resolver': 8.17.1 + '@cspell/cspell-types': 8.17.1 + '@cspell/dynamic-import': 8.17.1 + '@cspell/filetypes': 8.17.1 + '@cspell/strong-weak-map': 8.17.1 + '@cspell/url': 8.17.1 clear-module: 4.1.2 comment-json: 4.2.5 - cspell-config-lib: 8.16.1 - cspell-dictionary: 8.16.1 - cspell-glob: 8.16.1 - cspell-grammar: 8.16.1 - cspell-io: 8.16.1 - cspell-trie-lib: 8.16.1 + cspell-config-lib: 8.17.1 + cspell-dictionary: 8.17.1 + cspell-glob: 8.17.1 + cspell-grammar: 8.17.1 + cspell-io: 8.17.1 + cspell-trie-lib: 8.17.1 env-paths: 3.0.0 fast-equals: 5.0.1 gensequence: 7.0.0 @@ -10591,27 +10620,27 @@ snapshots: vscode-uri: 3.0.8 xdg-basedir: 5.1.0 - cspell-trie-lib@8.16.1: + cspell-trie-lib@8.17.1: dependencies: - '@cspell/cspell-pipe': 8.16.1 - '@cspell/cspell-types': 8.16.1 + '@cspell/cspell-pipe': 8.17.1 + '@cspell/cspell-types': 8.17.1 gensequence: 7.0.0 - cspell@8.16.1: + cspell@8.17.1: dependencies: - '@cspell/cspell-json-reporter': 8.16.1 - '@cspell/cspell-pipe': 8.16.1 - '@cspell/cspell-types': 8.16.1 - '@cspell/dynamic-import': 8.16.1 - '@cspell/url': 8.16.1 + '@cspell/cspell-json-reporter': 8.17.1 + '@cspell/cspell-pipe': 8.17.1 + '@cspell/cspell-types': 8.17.1 + '@cspell/dynamic-import': 8.17.1 + '@cspell/url': 8.17.1 chalk: 5.3.0 chalk-template: 1.1.0 commander: 12.1.0 - cspell-dictionary: 8.16.1 - cspell-gitignore: 8.16.1 - cspell-glob: 8.16.1 - cspell-io: 8.16.1 - cspell-lib: 8.16.1 + cspell-dictionary: 8.17.1 + cspell-gitignore: 8.17.1 + cspell-glob: 8.17.1 + cspell-io: 8.17.1 + cspell-lib: 8.17.1 fast-json-stable-stringify: 2.1.0 file-entry-cache: 9.1.0 get-stdin: 9.0.0 @@ -10715,32 +10744,28 @@ snapshots: data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 + whatwg-url: 14.1.0 - data-view-buffer@1.0.1: + data-view-buffer@1.0.2: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 - data-view-byte-length@1.0.1: + data-view-byte-length@1.0.2: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 - data-view-byte-offset@1.0.0: + data-view-byte-offset@1.0.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 date-fns@2.28.0: {} - debug@3.2.7: - dependencies: - ms: 2.1.3 - debug@4.3.7: dependencies: ms: 2.1.3 @@ -10773,9 +10798,9 @@ snapshots: define-data-property@1.1.4: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - gopd: 1.1.0 + gopd: 1.2.0 define-lazy-prop@2.0.0: {} @@ -10887,6 +10912,12 @@ snapshots: dotenv@16.4.5: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + eastasianwidth@0.2.0: {} ejs@3.1.10: @@ -10935,7 +10966,7 @@ snapshots: electron-to-chromium@1.5.67: {} - electron-vite@2.3.0(@swc/core@1.9.3)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)): + electron-vite@2.3.0(@swc/core@1.10.4)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)): dependencies: '@babel/core': 7.26.0 '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) @@ -10943,16 +10974,16 @@ snapshots: esbuild: 0.21.5 magic-string: 0.30.14 picocolors: 1.1.1 - vite: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + vite: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) optionalDependencies: - '@swc/core': 1.9.3 + '@swc/core': 1.10.4 transitivePeerDependencies: - supports-color electron@33.2.1: dependencies: '@electron/get': 2.0.2 - '@types/node': 20.17.9 + '@types/node': 20.17.11 extract-zip: 2.0.1 transitivePeerDependencies: - supports-color @@ -10990,78 +11021,80 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.23.3: + es-abstract@1.23.8: dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 + call-bind: 1.0.8 + call-bound: 1.0.3 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.0.0 es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.2.6 + get-symbol-description: 1.1.0 globalthis: 1.0.4 - gopd: 1.1.0 + gopd: 1.2.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.0 + math-intrinsics: 1.1.0 + object-inspect: 1.13.3 object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.3 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.16 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.18 - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 + es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-iterator-helpers@1.2.0: + es-iterator-helpers@1.2.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-errors: 1.3.0 es-set-tostringtag: 2.0.3 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.6 globalthis: 1.0.4 - gopd: 1.1.0 + gopd: 1.2.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - iterator.prototype: 1.1.3 - safe-array-concat: 1.1.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 es-object-atoms@1.0.0: dependencies: @@ -11069,7 +11102,7 @@ snapshots: es-set-tostringtag@2.0.3: dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.6 has-tostringtag: 1.0.2 hasown: 2.0.2 @@ -11077,18 +11110,18 @@ snapshots: dependencies: hasown: 2.0.2 - es-to-primitive@1.2.1: + es-to-primitive@1.3.0: dependencies: is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 + is-date-object: 1.1.0 + is-symbol: 1.1.1 es6-error@4.1.1: {} - esbuild-register@3.6.0(esbuild@0.24.0): + esbuild-register@3.6.0(esbuild@0.24.2): dependencies: debug: 4.3.7 - esbuild: 0.24.0 + esbuild: 0.24.2 transitivePeerDependencies: - supports-color @@ -11118,32 +11151,33 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.24.0: + esbuild@0.24.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.24.0 - '@esbuild/android-arm': 0.24.0 - '@esbuild/android-arm64': 0.24.0 - '@esbuild/android-x64': 0.24.0 - '@esbuild/darwin-arm64': 0.24.0 - '@esbuild/darwin-x64': 0.24.0 - '@esbuild/freebsd-arm64': 0.24.0 - '@esbuild/freebsd-x64': 0.24.0 - '@esbuild/linux-arm': 0.24.0 - '@esbuild/linux-arm64': 0.24.0 - '@esbuild/linux-ia32': 0.24.0 - '@esbuild/linux-loong64': 0.24.0 - '@esbuild/linux-mips64el': 0.24.0 - '@esbuild/linux-ppc64': 0.24.0 - '@esbuild/linux-riscv64': 0.24.0 - '@esbuild/linux-s390x': 0.24.0 - '@esbuild/linux-x64': 0.24.0 - '@esbuild/netbsd-x64': 0.24.0 - '@esbuild/openbsd-arm64': 0.24.0 - '@esbuild/openbsd-x64': 0.24.0 - '@esbuild/sunos-x64': 0.24.0 - '@esbuild/win32-arm64': 0.24.0 - '@esbuild/win32-ia32': 0.24.0 - '@esbuild/win32-x64': 0.24.0 + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 escalade@3.2.0: {} @@ -11161,103 +11195,56 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.15.1 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint@9.16.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - eslint: 9.16.0 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.16.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint@9.16.0) - hasown: 2.0.2 - is-core-module: 2.15.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - string.prototype.trimend: 1.0.8 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-jest-dom@5.5.0(@testing-library/dom@10.1.0)(eslint@9.16.0): + eslint-plugin-jest-dom@5.5.0(@testing-library/dom@10.1.0)(eslint@9.17.0): dependencies: '@babel/runtime': 7.26.0 - eslint: 9.16.0 + eslint: 9.17.0 requireindex: 1.2.0 optionalDependencies: '@testing-library/dom': 10.1.0 - eslint-plugin-jest@28.9.0(@typescript-eslint/eslint-plugin@8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0))(typescript@5.7.2): + eslint-plugin-jest@28.10.0(@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0))(typescript@5.7.2): dependencies: - '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - eslint: 9.16.0 + '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + eslint: 9.17.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2) - jest: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + '@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2) + jest: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-react-hooks@5.0.0(eslint@9.16.0): + eslint-plugin-react-hooks@5.1.0(eslint@9.17.0): dependencies: - eslint: 9.16.0 + eslint: 9.17.0 - eslint-plugin-react@7.37.2(eslint@9.16.0): + eslint-plugin-react@7.37.3(eslint@9.17.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.2 + array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.0 - eslint: 9.16.0 + es-iterator-helpers: 1.2.1 + eslint: 9.17.0 estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.2.1 minimatch: 3.1.2 object.entries: 1.1.8 object.fromentries: 2.0.8 - object.values: 1.2.0 + object.values: 1.2.1 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.11 + string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-testing-library@7.1.1(eslint@9.16.0)(typescript@5.7.2): + eslint-plugin-testing-library@7.1.1(eslint@9.17.0)(typescript@5.7.2): dependencies: - '@typescript-eslint/scope-manager': 8.18.0 - '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - eslint: 9.16.0 + '@typescript-eslint/scope-manager': 8.19.0 + '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + eslint: 9.17.0 transitivePeerDependencies: - supports-color - typescript @@ -11271,14 +11258,14 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.16.0: + eslint@9.17.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.16.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.17.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.1 '@eslint/core': 0.9.1 '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.16.0 + '@eslint/js': 9.17.0 '@eslint/plugin-kit': 0.2.4 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 @@ -11547,12 +11534,14 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.6: + function.prototype.name@1.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-abstract: 1.23.3 functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 functions-have-names@1.2.3: {} @@ -11573,16 +11562,26 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.2.4: + get-intrinsic@1.2.6: dependencies: + call-bind-apply-helpers: 1.0.1 + dunder-proto: 1.0.1 + es-define-property: 1.0.1 es-errors: 1.3.0 + es-object-atoms: 1.0.0 function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + gopd: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 + math-intrinsics: 1.1.0 get-package-type@0.1.0: {} + get-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.0.0 + get-stdin@9.0.0: {} get-stream@5.2.0: @@ -11591,11 +11590,11 @@ snapshots: get-stream@6.0.1: {} - get-symbol-description@1.0.2: + get-symbol-description@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.6 glob-parent@5.1.2: dependencies: @@ -11661,18 +11660,16 @@ snapshots: globals@14.0.0: {} - globals@15.13.0: {} + globals@15.14.0: {} globalthis@1.0.4: dependencies: define-properties: 1.2.1 - gopd: 1.1.0 + gopd: 1.2.0 globrex@0.1.2: {} - gopd@1.1.0: - dependencies: - get-intrinsic: 1.2.4 + gopd@1.2.0: {} got@11.8.6: dependencies: @@ -11704,15 +11701,17 @@ snapshots: has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 - has-proto@1.0.3: {} + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 - has-symbols@1.0.3: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 has-unicode@2.0.1: {} @@ -11865,11 +11864,11 @@ snapshots: ini@4.1.1: {} - internal-slot@1.0.7: + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 - side-channel: 1.0.6 + side-channel: 1.1.0 internmap@1.0.1: {} @@ -11882,13 +11881,14 @@ snapshots: is-arguments@1.1.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 has-tostringtag: 1.0.2 - is-array-buffer@3.0.4: + is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 is-arrayish@0.2.1: {} @@ -11898,13 +11898,13 @@ snapshots: dependencies: has-tostringtag: 1.0.2 - is-bigint@1.0.4: + is-bigint@1.1.0: dependencies: has-bigints: 1.0.2 - is-boolean-object@1.1.2: + is-boolean-object@1.2.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 has-tostringtag: 1.0.2 is-callable@1.2.7: {} @@ -11917,21 +11917,24 @@ snapshots: dependencies: hasown: 2.0.2 - is-data-view@1.0.1: + is-data-view@1.0.2: dependencies: - is-typed-array: 1.1.13 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 + is-typed-array: 1.1.15 - is-date-object@1.0.5: + is-date-object@1.1.0: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 is-docker@2.2.1: {} is-extglob@2.1.1: {} - is-finalizationregistry@1.0.2: + is-finalizationregistry@1.1.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 is-fullwidth-code-point@3.0.0: {} @@ -11949,59 +11952,63 @@ snapshots: is-lambda@1.0.1: {} - is-map@2.0.2: {} - - is-negative-zero@2.0.3: {} + is-map@2.0.3: {} is-node-process@1.2.0: {} - is-number-object@1.0.6: + is-number-object@1.1.1: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 is-number@7.0.0: {} is-potential-custom-element-name@1.0.1: {} - is-regex@1.1.4: + is-regex@1.2.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 + gopd: 1.2.0 has-tostringtag: 1.0.2 + hasown: 2.0.2 - is-set@2.0.2: {} + is-set@2.0.3: {} - is-shared-array-buffer@1.0.3: + is-shared-array-buffer@1.0.4: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 is-stream@2.0.1: {} - is-string@1.0.7: + is-string@1.1.1: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 - is-symbol@1.0.4: + is-symbol@1.1.1: dependencies: - has-symbols: 1.0.3 + call-bound: 1.0.3 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 - is-typed-array@1.1.13: + is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.16 + which-typed-array: 1.1.18 is-typedarray@1.0.0: {} is-unicode-supported@0.1.0: {} - is-weakmap@2.0.1: {} + is-weakmap@2.0.2: {} - is-weakref@1.0.2: + is-weakref@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 - is-weakset@2.0.2: + is-weakset@2.0.4: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 is-windows@0.2.0: {} @@ -12088,12 +12095,13 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - iterator.prototype@1.1.3: + iterator.prototype@1.1.5: dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.4 + define-data-property: 1.1.4 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.6 + get-proto: 1.0.0 + has-symbols: 1.1.0 set-function-name: 2.0.2 jackspeak@3.4.2: @@ -12126,7 +12134,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3(babel-plugin-macros@3.1.0) @@ -12146,16 +12154,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0): + jest-cli@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0): dependencies: '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + create-jest: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) exit: 0.1.2 import-local: 3.0.3 - jest-config: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + jest-config: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -12165,7 +12173,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0): + jest-config@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0): dependencies: '@babel/core': 7.26.0 '@jest/test-sequencer': 29.7.0 @@ -12190,7 +12198,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -12220,7 +12228,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 20.17.9 + '@types/node': 20.17.11 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -12234,7 +12242,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -12246,7 +12254,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.17.9 + '@types/node': 20.17.11 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -12292,19 +12300,19 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 jest-util: 29.7.0 - jest-playwright-preset@4.0.0(jest-circus@29.7.0(babel-plugin-macros@3.1.0))(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0)): + jest-playwright-preset@4.0.0(jest-circus@29.7.0(babel-plugin-macros@3.1.0))(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0)): dependencies: expect-playwright: 0.8.0 - jest: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + jest: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) jest-circus: 29.7.0(babel-plugin-macros@3.1.0) jest-environment-node: 29.7.0 jest-process-manager: 0.4.0 jest-runner: 29.7.0 nyc: 15.1.0 - playwright-core: 1.49.0 + playwright-core: 1.49.1 rimraf: 3.0.2 uuid: 8.3.2 transitivePeerDependencies: @@ -12359,7 +12367,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -12387,7 +12395,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 chalk: 4.1.2 cjs-module-lexer: 1.4.1 collect-v8-coverage: 1.0.2 @@ -12442,7 +12450,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 chalk: 4.1.2 ci-info: 3.3.0 graceful-fs: 4.2.11 @@ -12457,11 +12465,11 @@ snapshots: leven: 3.1.0 pretty-format: 29.7.0 - jest-watch-typeahead@2.2.2(jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0)): + jest-watch-typeahead@2.2.2(jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0)): dependencies: ansi-escapes: 6.2.1 chalk: 5.3.0 - jest: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + jest: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -12472,7 +12480,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.9 + '@types/node': 20.17.11 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -12486,17 +12494,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0): + jest@29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0): dependencies: '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) '@jest/types': 29.6.3 import-local: 3.0.3 - jest-cli: 29.7.0(@types/node@20.17.9)(babel-plugin-macros@3.1.0) + jest-cli: 29.7.0(@types/node@20.17.11)(babel-plugin-macros@3.1.0) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -12584,7 +12592,7 @@ snapshots: webidl-conversions: 7.0.0 whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 + whatwg-url: 14.1.0 ws: 8.18.0 xml-name-validator: 5.0.0 transitivePeerDependencies: @@ -12605,10 +12613,6 @@ snapshots: json-stringify-safe@5.0.1: optional: true - json5@1.0.2: - dependencies: - minimist: 1.2.8 - json5@2.2.3: {} jsonc-parser@3.3.1: {} @@ -12626,7 +12630,7 @@ snapshots: jsx-ast-utils@3.2.1: dependencies: array-includes: 3.1.8 - object.assign: 4.1.5 + object.assign: 4.1.7 keyv@4.5.4: dependencies: @@ -12762,6 +12766,8 @@ snapshots: escape-string-regexp: 4.0.0 optional: true + math-intrinsics@1.1.0: {} + memoize-one@6.0.0: {} merge-stream@2.0.0: {} @@ -12856,28 +12862,28 @@ snapshots: ms@2.1.3: {} - msw-storybook-addon@2.0.4(msw@2.6.6(@types/node@20.17.9)(typescript@5.7.2)): + msw-storybook-addon@2.0.4(msw@2.7.0(@types/node@20.17.11)(typescript@5.7.2)): dependencies: is-node-process: 1.2.0 - msw: 2.6.6(@types/node@20.17.9)(typescript@5.7.2) + msw: 2.7.0(@types/node@20.17.11)(typescript@5.7.2) - msw@2.6.6(@types/node@20.17.9)(typescript@5.7.2): + msw@2.7.0(@types/node@20.17.11)(typescript@5.7.2): dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.0.2(@types/node@20.17.9) + '@inquirer/confirm': 5.0.2(@types/node@20.17.11) '@mswjs/interceptors': 0.37.1 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 '@types/statuses': 2.0.5 - chalk: 4.1.2 graphql: 16.9.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 path-to-regexp: 6.3.0 + picocolors: 1.1.1 strict-event-emitter: 0.5.1 type-fest: 4.29.0 yargs: 17.7.2 @@ -12995,39 +13001,36 @@ snapshots: object-assign@4.1.1: {} - object-inspect@1.13.1: {} + object-inspect@1.13.3: {} object-keys@1.1.1: {} - object.assign@4.1.5: + object.assign@4.1.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - has-symbols: 1.0.3 + es-object-atoms: 1.0.0 + has-symbols: 1.1.0 object-keys: 1.1.1 object.entries@1.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-object-atoms: 1.0.0 object.fromentries@2.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-object-atoms: 1.0.0 - object.groupby@1.0.3: + object.values@1.2.1: dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - - object.values@1.2.0: - dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 es-object-atoms: 1.0.0 @@ -13074,6 +13077,12 @@ snapshots: outvariant@1.4.3: {} + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.2.6 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + p-cancelable@2.1.1: {} p-limit@2.3.0: @@ -13169,11 +13178,11 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.49.0: {} + playwright-core@1.49.1: {} - playwright@1.49.0: + playwright@1.49.1: dependencies: - playwright-core: 1.49.0 + playwright-core: 1.49.1 optionalDependencies: fsevents: 2.3.2 @@ -13197,7 +13206,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.4.47: + postcss@8.4.49: dependencies: nanoid: 3.3.8 picocolors: 1.1.1 @@ -13205,7 +13214,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.4.1: {} + prettier@3.4.2: {} pretty-format@27.5.1: dependencies: @@ -13259,7 +13268,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.17.9 + '@types/node': 20.17.11 long: 5.2.3 proxy-from-env@1.1.0: {} @@ -13294,7 +13303,7 @@ snapshots: dependencies: dnd-core: 14.0.1 - react-dnd@14.0.5(@types/node@20.17.9)(@types/react@18.3.12)(react@18.3.1): + react-dnd@14.0.5(@types/node@20.17.11)(@types/react@18.3.12)(react@18.3.1): dependencies: '@react-dnd/invariant': 2.0.0 '@react-dnd/shallowequal': 2.0.0 @@ -13303,7 +13312,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 18.3.1 optionalDependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 '@types/react': 18.3.12 react-docgen-typescript@2.2.2(typescript@5.7.2): @@ -13369,19 +13378,19 @@ snapshots: dependencies: '@testing-library/dom': 10.1.0 - react-select@5.8.3(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-select@5.9.0(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.26.0 '@emotion/cache': 11.13.5 '@emotion/react': 11.13.5(@types/react@18.3.12)(react@18.3.1) '@floating-ui/dom': 1.6.12 - '@types/react-transition-group': 4.4.11 + '@types/react-transition-group': 4.4.12(@types/react@18.3.12) memoize-one: 6.0.0 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.12)(react@18.3.1) + use-isomorphic-layout-effect: 1.2.0(@types/react@18.3.12)(react@18.3.1) transitivePeerDependencies: - '@types/react' - supports-color @@ -13442,14 +13451,16 @@ snapshots: dependencies: '@babel/runtime': 7.26.0 - reflect.getprototypeof@1.0.4: + reflect.getprototypeof@1.0.9: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 - get-intrinsic: 1.2.4 - globalthis: 1.0.4 - which-builtin-type: 1.1.3 + dunder-proto: 1.0.1 + es-abstract: 1.23.8 + es-errors: 1.3.0 + get-intrinsic: 1.2.6 + gopd: 1.2.0 + which-builtin-type: 1.2.1 regenerate-unicode-properties@10.2.0: dependencies: @@ -13463,9 +13474,9 @@ snapshots: dependencies: '@babel/runtime': 7.26.0 - regexp.prototype.flags@1.5.2: + regexp.prototype.flags@1.5.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 @@ -13571,35 +13582,38 @@ snapshots: sprintf-js: 1.1.3 optional: true - rollup-plugin-visualizer@5.12.0(rollup@4.23.0): + rollup-plugin-visualizer@5.13.1(rollup@4.29.1): dependencies: open: 8.4.2 - picomatch: 2.3.1 + picomatch: 4.0.2 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.23.0 + rollup: 4.29.1 - rollup@4.23.0: + rollup@4.29.1: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.23.0 - '@rollup/rollup-android-arm64': 4.23.0 - '@rollup/rollup-darwin-arm64': 4.23.0 - '@rollup/rollup-darwin-x64': 4.23.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.23.0 - '@rollup/rollup-linux-arm-musleabihf': 4.23.0 - '@rollup/rollup-linux-arm64-gnu': 4.23.0 - '@rollup/rollup-linux-arm64-musl': 4.23.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.23.0 - '@rollup/rollup-linux-riscv64-gnu': 4.23.0 - '@rollup/rollup-linux-s390x-gnu': 4.23.0 - '@rollup/rollup-linux-x64-gnu': 4.23.0 - '@rollup/rollup-linux-x64-musl': 4.23.0 - '@rollup/rollup-win32-arm64-msvc': 4.23.0 - '@rollup/rollup-win32-ia32-msvc': 4.23.0 - '@rollup/rollup-win32-x64-msvc': 4.23.0 + '@rollup/rollup-android-arm-eabi': 4.29.1 + '@rollup/rollup-android-arm64': 4.29.1 + '@rollup/rollup-darwin-arm64': 4.29.1 + '@rollup/rollup-darwin-x64': 4.29.1 + '@rollup/rollup-freebsd-arm64': 4.29.1 + '@rollup/rollup-freebsd-x64': 4.29.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.29.1 + '@rollup/rollup-linux-arm-musleabihf': 4.29.1 + '@rollup/rollup-linux-arm64-gnu': 4.29.1 + '@rollup/rollup-linux-arm64-musl': 4.29.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.29.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.29.1 + '@rollup/rollup-linux-riscv64-gnu': 4.29.1 + '@rollup/rollup-linux-s390x-gnu': 4.29.1 + '@rollup/rollup-linux-x64-gnu': 4.29.1 + '@rollup/rollup-linux-x64-musl': 4.29.1 + '@rollup/rollup-win32-arm64-msvc': 4.29.1 + '@rollup/rollup-win32-ia32-msvc': 4.29.1 + '@rollup/rollup-win32-x64-msvc': 4.29.1 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} @@ -13612,22 +13626,28 @@ snapshots: dependencies: tslib: 2.8.1 - safe-array-concat@1.1.2: + safe-array-concat@1.1.3: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.6 + has-symbols: 1.1.0 isarray: 2.0.5 safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} - safe-regex-test@1.0.3: + safe-push-apply@1.0.0: dependencies: - call-bind: 1.0.7 es-errors: 1.3.0 - is-regex: 1.1.4 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + is-regex: 1.2.1 safe-stable-stringify@2.3.1: {} @@ -13666,8 +13686,8 @@ snapshots: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.1.0 + get-intrinsic: 1.2.6 + gopd: 1.2.0 has-property-descriptors: 1.0.2 set-function-name@2.0.2: @@ -13687,12 +13707,33 @@ snapshots: shimmer@1.2.1: {} - side-channel@1.0.6: + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + + side-channel-map@1.0.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + get-intrinsic: 1.2.6 + object-inspect: 1.13.3 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.6 + object-inspect: 1.13.3 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 signal-exit@3.0.7: {} @@ -13790,11 +13831,11 @@ snapshots: statuses@2.0.1: {} - storybook@8.4.6(prettier@3.4.1): + storybook@8.4.7(prettier@3.4.2): dependencies: - '@storybook/core': 8.4.6(prettier@3.4.1) + '@storybook/core': 8.4.7(prettier@3.4.2) optionalDependencies: - prettier: 3.4.1 + prettier: 3.4.2 transitivePeerDependencies: - bufferutil - supports-color @@ -13832,42 +13873,47 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string.prototype.matchall@4.0.11: + string.prototype.matchall@4.0.12: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-errors: 1.3.0 es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - gopd: 1.1.0 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - regexp.prototype.flags: 1.5.2 + get-intrinsic: 1.2.6 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.3 set-function-name: 2.0.2 - side-channel: 1.0.6 + side-channel: 1.1.0 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 - string.prototype.trim@1.2.9: + string.prototype.trim@1.2.10: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 + define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.8 es-object-atoms: 1.0.0 + has-property-descriptors: 1.0.2 - string.prototype.trimend@1.0.8: + string.prototype.trimend@1.0.9: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 es-object-atoms: 1.0.0 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-object-atoms: 1.0.0 @@ -14083,13 +14129,6 @@ snapshots: optionalDependencies: typescript: 5.7.2 - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -14117,60 +14156,61 @@ snapshots: type-fest@4.29.0: {} - typed-array-buffer@1.0.2: + typed-array-buffer@1.0.3: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-typed-array: 1.1.13 + is-typed-array: 1.1.15 - typed-array-byte-length@1.0.1: + typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.1.0 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 - typed-array-byte-offset@1.0.2: + typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.1.0 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.9 - typed-array-length@1.0.6: + typed-array-length@1.0.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.1.0 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + is-typed-array: 1.1.15 possible-typed-array-names: 1.0.0 + reflect.getprototypeof: 1.0.9 typedarray-to-buffer@3.1.5: dependencies: is-typedarray: 1.0.0 - typescript-eslint@8.18.0(eslint@9.16.0)(typescript@5.7.2): + typescript-eslint@8.19.0(eslint@9.17.0)(typescript@5.7.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.16.0)(typescript@5.7.2))(eslint@9.16.0)(typescript@5.7.2) - '@typescript-eslint/parser': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - '@typescript-eslint/utils': 8.18.0(eslint@9.16.0)(typescript@5.7.2) - eslint: 9.16.0 + '@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/parser': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + eslint: 9.17.0 typescript: 5.7.2 transitivePeerDependencies: - supports-color typescript@5.7.2: {} - unbox-primitive@1.0.2: + unbox-primitive@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 undici-types@6.19.8: {} @@ -14219,7 +14259,7 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - use-isomorphic-layout-effect@1.1.2(@types/react@18.3.12)(react@18.3.1): + use-isomorphic-layout-effect@1.2.0(@types/react@18.3.12)(react@18.3.1): dependencies: react: 18.3.1 optionalDependencies: @@ -14234,8 +14274,8 @@ snapshots: inherits: 2.0.4 is-arguments: 1.1.1 is-generator-function: 1.0.10 - is-typed-array: 1.1.13 - which-typed-array: 1.1.16 + is-typed-array: 1.1.15 + which-typed-array: 1.1.18 uuid@8.3.2: {} @@ -14256,30 +14296,31 @@ snapshots: extsprintf: 1.4.1 optional: true - vite-plugin-wasm@3.3.0(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)): + vite-plugin-wasm@3.4.1(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)): dependencies: - vite: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + vite: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) - vite-tsconfig-paths@5.1.3(typescript@5.7.2)(vite@5.4.8(@types/node@20.17.9)(terser@5.31.1)): + vite-tsconfig-paths@5.1.4(typescript@5.7.2)(vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1)): dependencies: debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.1.0(typescript@5.7.2) optionalDependencies: - vite: 5.4.8(@types/node@20.17.9)(terser@5.31.1) + vite: 6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1) transitivePeerDependencies: - supports-color - typescript - vite@5.4.8(@types/node@20.17.9)(terser@5.31.1): + vite@6.0.6(@types/node@20.17.11)(terser@5.31.1)(yaml@2.6.1): dependencies: - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.23.0 + esbuild: 0.24.2 + postcss: 8.4.49 + rollup: 4.29.1 optionalDependencies: - '@types/node': 20.17.9 + '@types/node': 20.17.11 fsevents: 2.3.3 terser: 5.31.1 + yaml: 2.6.1 vscode-languageserver-textdocument@1.0.12: {} @@ -14344,49 +14385,51 @@ snapshots: tr46: 3.0.0 webidl-conversions: 7.0.0 - whatwg-url@14.0.0: + whatwg-url@14.1.0: dependencies: tr46: 5.0.0 webidl-conversions: 7.0.0 - which-boxed-primitive@1.0.2: + which-boxed-primitive@1.1.1: dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.6 - is-string: 1.0.7 - is-symbol: 1.0.4 + is-bigint: 1.1.0 + is-boolean-object: 1.2.1 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 - which-builtin-type@1.1.3: + which-builtin-type@1.2.1: dependencies: - function.prototype.name: 1.1.6 + call-bound: 1.0.3 + function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 + is-regex: 1.2.1 + is-weakref: 1.1.0 isarray: 2.0.5 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.16 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.18 - which-collection@1.0.1: + which-collection@1.0.2: dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 which-module@2.0.1: {} - which-typed-array@1.1.16: + which-typed-array@1.1.18: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 for-each: 0.3.3 - gopd: 1.1.0 + gopd: 1.2.0 has-tostringtag: 1.0.2 which@1.3.1: @@ -14529,10 +14572,10 @@ snapshots: compress-commons: 4.1.2 readable-stream: 3.6.2 - zod-to-json-schema@3.23.5(zod@3.23.8): + zod-to-json-schema@3.24.1(zod@3.24.1): dependencies: - zod: 3.23.8 + zod: 3.24.1 - zod@3.23.8: {} + zod@3.24.1: {} zone.js@0.14.7: {} diff --git a/rfd/0016-dynamic-configuration.md b/rfd/0016-dynamic-configuration.md index 0b941d55c00bb..892f6e5fe1683 100644 --- a/rfd/0016-dynamic-configuration.md +++ b/rfd/0016-dynamic-configuration.md @@ -65,7 +65,7 @@ ensure backward compatibility with the already established workflows. #### Choice 2.A In this option, dynamic-configuration resources are understood to exist only -if they have been comitted as a result of having been specified in static +if they have been committed as a result of having been specified in static configuration or via `tctl create`. 3. The command `tctl get cap` would therefore return an error saying diff --git a/rfd/0047-drop-vendor.md b/rfd/0047-drop-vendor.md index 3a5fffd925bd8..b79f282a1d15f 100644 --- a/rfd/0047-drop-vendor.md +++ b/rfd/0047-drop-vendor.md @@ -97,7 +97,7 @@ which is difficult to maintain in a cross-platform way, and has broken [gopls integration](https://github.com/gravitational/teleport/blob/30effc1f08b6a699772ff22f79ebe756fe1a1e34/Makefile#L942-L952) a common tool used in Go development environments. -Lastly, there is no guarantee that the code comitted to vendor actually +Lastly, there is no guarantee that the code committed to vendor actually reflects the contents of go.mod. The onus is on the developer to remember to run `make update-vendor` and commit the results after making changes to dependencies. This has created several cases of confusing build results amongst diff --git a/rfd/0055-webui-ss-paginate-filter.md b/rfd/0055-webui-ss-paginate-filter.md index c4ee111769b43..d674ae9fc0daa 100644 --- a/rfd/0055-webui-ss-paginate-filter.md +++ b/rfd/0055-webui-ss-paginate-filter.md @@ -77,7 +77,7 @@ This technique does not support sorting, but provides faster performance and is The web UI will not request for the entire list of resources upfront, but will provide a user with a `fetch more` button if a user desires to see the next page if any. -We can branch off into two functions with current `ListResources` based on if sorting was requested (orting will be disabled for `tsh`, so that `tsh` performance will not be affected): +We can branch off into two functions with current `ListResources` based on if sorting was requested (sorting will be disabled for `tsh`, so that `tsh` performance will not be affected): - `listResources` (keeps current behavior) - `listResourcesWithSort` diff --git a/rfd/0067-desktop-access-file-system-sharing.md b/rfd/0067-desktop-access-file-system-sharing.md index f1870de298c93..7ef8b2fd30905 100644 --- a/rfd/0067-desktop-access-file-system-sharing.md +++ b/rfd/0067-desktop-access-file-system-sharing.md @@ -490,7 +490,7 @@ requested by RDP. For files, `last_modified` is the last modified time of the file as specified by the [`mtime`](https://www.makeuseof.com/linux-file-timestamps/), in milliseconds since the [UNIX epoch](https://en.wikipedia.org/wiki/Unix_time). For directories, `last_modified` should also be set to the -[directory's `mtime`](https://stackoverflow.com/a/3620704/6277051) when suchinformation is available. If such information is unavailable for a directory, such as +[directory's `mtime`](https://stackoverflow.com/a/3620704/6277051) when such information is available. If such information is unavailable for a directory, such as in a browser environment, this value should be assigned the UNIX epoch itself (0). For files, `size` is the size of the file in bytes. For directories, `size` is not the total size of the contents of the diff --git a/rfd/0073-discover.md b/rfd/0073-discover.md index 5d9e00944b958..21c094fb534d0 100644 --- a/rfd/0073-discover.md +++ b/rfd/0073-discover.md @@ -202,7 +202,7 @@ auto-discovery by going through the following flow: 3. The selected agent will perform initial discovery according to the provided filters. This can be implemented by providing an API for the web UI to create a "discovery request" which agents will watch. -4. The agent will attempt to fullfill the discovery request and will report +4. The agent will attempt to fulfill the discovery request and will report errors, e.g. insufficient IAM policy, to the user. This can be implemented by filling out a Status field on the agent's resource spec. 5. If successful, the UI wizard will display all resources matching the diff --git a/rfd/0083-machine-id-host-certs.md b/rfd/0083-machine-id-host-certs.md index 72ab3dfae912c..892471357c882 100644 --- a/rfd/0083-machine-id-host-certs.md +++ b/rfd/0083-machine-id-host-certs.md @@ -165,7 +165,7 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? This is nearly identical to the usual ssh TOFU message, save for the easy-to-miss "Certificate invalid: expired" message. Users are likely conditioned to accept this, and if that happens the expired or invalid host key -will be comitted to their `known_hosts` permanently, after which the "expired" +will be committed to their `known_hosts` permanently, after which the "expired" message will not be shown again. We'll need to document this caveat along with a workaround (e.g. a diff --git a/rfd/0084-license-expiration-warnings.md b/rfd/0084-license-expiration-warnings.md index 1baa850f6fc9f..6c5bd48080227 100644 --- a/rfd/0084-license-expiration-warnings.md +++ b/rfd/0084-license-expiration-warnings.md @@ -9,7 +9,7 @@ state: implemented ## Required approvers - Engineering: `@r0mant` -- Product: `@klizentas && @xinding33` +- Product: `@klizhentas && @xinding33` - Security: `@reedloden` ## What diff --git a/rfd/0089-merge-webapps.md b/rfd/0089-merge-webapps.md index 224c9ff34ce71..01e4b0f4f1224 100644 --- a/rfd/0089-merge-webapps.md +++ b/rfd/0089-merge-webapps.md @@ -187,7 +187,7 @@ build systems to successfully build Teleport. - [ ] Remove `/webassets` submodule - This submodule is no longer required as the web UI will be built on-demand. - The folder will remain as the output location of the on-demand build but - will not be comitted. + will not be committed. - [ ] Clone the [Webapps repository](https://github.com/gravitational/webapps) into the Teleport root. [Maintaining their respective git histories](https://stackoverflow.com/questions/13040958/merge-two-git-repositories-without-breaking-file-history) - [ ] This will need to be done for every respective version branch (v9, v10, v11) diff --git a/rfd/0122-moderated-file-transfers.md b/rfd/0122-moderated-file-transfers.md index 14e730dfb64c4..98e1de235c377 100644 --- a/rfd/0122-moderated-file-transfers.md +++ b/rfd/0122-moderated-file-transfers.md @@ -136,7 +136,7 @@ OnApprove: 3. We can then use a policy checker to see if the approvers fulfill any moderation policy on the original requester. We can treat this check the same as the `checkIfStart` conditional for opening a session. If this comes back true, we notify the original requester with an event containing the ID of the `FileTransferRequest` Once the client receives this final "approved" message, we can automatically send a "normal" SFTP request (over HTTP) with two new optional params, `sessionID` and `commandRequestId` (similar to the new optional `webauthn` param in this same request). The benefits of using the normal SFTP request is that we can conditionally choose to skip this entire approval process flow for non-moderated sessions. -If the session is not moderated, just send the SFTP request as usual. If it is, do the song and dance perscribed above. +If the session is not moderated, just send the SFTP request as usual. If it is, do the song and dance prescribed above. ### Updated file transfer api handler diff --git a/rfd/0133-connect-my-computer.md b/rfd/0133-connect-my-computer.md index 69cdc7964da44..a15bcdc2b7a9c 100644 --- a/rfd/0133-connect-my-computer.md +++ b/rfd/0133-connect-my-computer.md @@ -417,7 +417,7 @@ agent. This follows from the behavior described in the “Downloading the agent #### Log rotation -The MVP is not going to perform any extra log rotation beyond what the Teleport agent offerts out of +The MVP is not going to perform any extra log rotation beyond what the Teleport agent offers out of the box which is creating a new log file every 24 hours. The logs are thus stored in the same directory as the data directory of the agent and are removed together with the agent. diff --git a/rfd/0143-external-k8s-joining.md b/rfd/0143-external-k8s-joining.md index 0e1ab83eda266..306719be3c468 100644 --- a/rfd/0143-external-k8s-joining.md +++ b/rfd/0143-external-k8s-joining.md @@ -294,7 +294,7 @@ traced back to a specific Kubernetes pod. ## Alternatives -### Introducing a seperate `kubernetes_remote` join method +### Introducing a separate `kubernetes_remote` join method One alternative implementation was to introduce a new `kubernetes_remote` join method that would use a bi-di gRPC RPC to create a challenge and response flow diff --git a/rfd/0187-redesign-enroll-new-resource-page.md b/rfd/0187-redesign-enroll-new-resource-page.md new file mode 100644 index 0000000000000..4a7c047260674 --- /dev/null +++ b/rfd/0187-redesign-enroll-new-resource-page.md @@ -0,0 +1,130 @@ +--- +authors: Dave Sudia (david.sudia@goteleport.com) +state: draft +--- + +# RFD 0187 - Redesign Enroll New Resource Page + +## Required Approvers + +* Engineering: @r0mant +* Product: @xinding33 + +## Overview + +### Problem + +* The current “Enroll a Resource” flow is confusing for new users. + * This slows down PoVs and trials hurts their success by making it harder for new user to get started proving out how successful Teleport will be in meeting their needs. +* There are overlapping ways of enrolling a resource that we do not differentiate +enough for the user. Example: if I have an ubuntu server running in EC2, do I pick the Ubuntu flow or the EC2 Auto-discovery? + * As a new user I may want to simply see how the experience of adding and logging into a server works. I don’t know that picking the latter will start me on a very complex path that requires lots of permissions. + * Or I may need to see how Teleport works at scale before extending a trial into a PoV and don’t know the latter would better suit my needs. +* The highest drop off in the resource enrollment process is on the first page of a guide. This might be because people click on a guide and then realize it is not what they’re looking for. +* The current page’s search function is not accurate enough; it doesn’t cover all the terms that users may search for (e.g., searching “SSH” does not find relevant options). +* The lack of a filter system combined with the limited search makes it challenging for users to find the right options for their needs quickly. The goal is to make it easier for users to locate the correct resource type, and correct level of complexity. + +### Other data/context + +* Because of the above issues (and issues with individual guides), for assisted PoVs, the SE team now recommends people avoid the Enroll a Resource page, and go straight to the docs. +* Data for the above was collected from interviews with users and reports/interviews with the SE team + +### Appetite/Resources +This project aims to be scoped to be feasible for one full-stack engineer with support from design and product input, aiming for a completion timeline of approximately 6 weeks. + +## Solution + +### Hypothesis + +By providing an improved experience for picking options on the Enroll a Resource page, users will be able to find relevant guides more easily, and new users in particular will be better equipped to determine if Teleport is a good solution for their needs. An improved enrollment process will improve PoV and trial success rates. + +### Use Cases/Requirements +* As an admin + * I need to find the correct enrollment guide based on the resource type I want to protect + * I want to filter options by resource type (e.g. db, server, k8s) and cloud provider/hosting platform (e.g., AWS, Azure, self-hosted) + * I want a clear path for both simpler and more complex configurations, i.e. for Day 1 (initial setup/trial) and Day 2 (larger scale) operations +* As a new user to the product + * I want the platform to help me determine what kind of resources I should protect based on common usage or best practices + * I want clear, simple paths to follow that align with my current organizational needs + * I want to have an idea of how complex each setup is before committing to it +* As a Teleporter + * I want to know if users’ use of search increases or decreases + * I want to know if users use the filters + * I want to know which guides are most-accessed during trial and PoV periods to determine what to highlight for new users + +### Rabbit Holes + +We could go really granular on filters, which would require creating and maintaining more identifiers on each resource panel. We should minimize the filter list, maybe by mirroring the current dashboard view options. Doing a guided questionnaire came up during brainstorming. How/where/when to access that and how long/complex it would be could blow out scope. If scope needs to be cut, it could be cut there. + +### Out of Bounds + +This project does not intend to overhaul existing resource-specific documentation or setup guides but aims to streamline how users access and begin these guides. There are issues with individual guides that contribute to PoV/trial failure, but enhancements to setup guides fall under other RFDs. + +## Outlines/Sketches +The [FigJam board](https://www.figma.com/board/65tcBiTgE9B9j05NVO669b/Untitled?node-id=0-1&t=T5ukhCY6H8x3Jxud-1) has original notes. + +#### User Interface Changes +* Implement “pinned” enrollment guides. Guides that are good for getting started will be pinned by default for all new clusters. + * Users can unpin when they don’t want those at the top anymore. + * Users can pin/unpin additional cards as desired +* Add filters + * Hosting method/platform (AWS/GCP/Azure/Self-Hosted) + * Resource type (DB/Server/Kubernetes/Desktop/Application) +* Improve search to capture more relevant terms (i.e. “SSH”, “k8s”, linux distros) +* Collapse Linux options into a unified category, simplifying the selection process for users who may not need to distinguish between distributions at the initial stage + * Make searchable by distro (ubuntu, redhat, centos, debian, etc) + +## Value + +### Opportunity + +We know that the shorter a PoV process the higher our win rate. Improving the resource enrollment flow with a focus on helping new users get to the right guides (whether that is a test of a quick setup or of a scale solution) will hopefully lead to higher conversion rates for PoVs and trials. + +### Measuring Success + +* Monitor overall improvements in conversion rates from initial interest (e.g., entering the “Enroll a Resource” page) to starting active sessions. +* Set target metrics for reducing drop-off rates on the first page of guides (where we have the most drop off) + +## Implementation + +### Design +* [Link to page design](https://www.figma.com/design/uLevdNsEnIvvLDSZ9sQqXM/Discover-Access?node-id=2730-11748&t=xsYYolkkJeUJAhGi-4) +* [Link to resource card states](https://www.figma.com/design/Gpjs9vjhzUKF1GDbeG9JGE/Application-Design-System?node-id=18913-20082&t=teeY2YmoBg0jphJi-4) + +Some notes: +* The resource cards that will be pinned by default are below. +* **Every** resource card should have a second line with additional contextual info—precise details to come. +* We cut filtering by scale/complexity and by guided/docs during design phase (and they're now deleted from the filters list above). +* Pinned items appear both at the top of the page as a different-style card AND in the flow of the page. +* Not necessarily included in this scope of work, but I also updated and expanded our AWS service icons in the design library and exported to Google Drive. + +#### Specs + +##### Default pinned cards +* Linux +* Self-Hosted Kubernetes +* Web Application +* Connect My Computer + +##### New Card Content - only changed/updated cards +* Linux Server + * SSH +* Connect My Computer + * Teleport Desktop App +* Dynamic Database Registration + * Self-Hosted +* SAML Application + * Teleport as IDP +* Web Application + * HTTP Proxy +* MongoDB Atlas + * Database as a Service +* Snowflake + * Database as a Service +* AWS CLI/Console Access + * Amazon Web Services (AWS) +* Grafana SAML + * Teleport as IDP + +### Engineering +To be added to by engineering team. diff --git a/rfd/cspell.json b/rfd/cspell.json index 13952ad731c8b..9982219bada5e 100644 --- a/rfd/cspell.json +++ b/rfd/cspell.json @@ -322,7 +322,6 @@ "cmpopts", "cockroachdb", "codingllama", - "comitted", "committerdate", "commonfolk", "compat", @@ -395,7 +394,6 @@ "fspmarshall", "ftruncate", "fullchain", - "fullfill", "fxamacker", "gcpxyz", "germaine", @@ -465,7 +463,6 @@ "keypresses", "keyv", "kimlisa", - "klizentas", "klizhentas", "kubeconfig", "kubeconfigs", @@ -554,7 +551,6 @@ "objc", "octocats", "offboarding", - "offerts", "olekukonko", "omitempty", "oncall", @@ -568,7 +564,6 @@ "opentelemetry", "oqzt", "orapki", - "orting", "osascript", "otel", "otelaws", @@ -583,7 +578,6 @@ "partman", "passwordless", "pchar", - "perscribed", "pgbouncer", "pgconn", "pgoutput", @@ -677,7 +671,6 @@ "selfsubjectaccessreviews", "selfsubjectrulesreviews", "sendmsg", - "seperate", "serviceaccount", "serviceaccounts", "servicecfg", @@ -734,7 +727,6 @@ "subselection", "subselects", "subtests", - "suchinformation", "sudoer", "sudoersfile", "supercede", diff --git a/tool/common/common.go b/tool/common/common.go index 456a17f4992d3..0a7a13946e554 100644 --- a/tool/common/common.go +++ b/tool/common/common.go @@ -23,11 +23,11 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "sort" "strings" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/constants" @@ -36,6 +36,7 @@ import ( "github.com/gravitational/teleport/lib/asciitable" libevents "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // ExitCodeError wraps an exit code as an error. @@ -85,7 +86,7 @@ func (e *SessionsCollection) WriteText(w io.Writer) error { target = session.DatabaseName timestamp = session.GetTime().Format(constants.HumanDateFormatSeconds) default: - log.Warn(trace.BadParameter("unsupported event type: expected SessionEnd, WindowsDesktopSessionEnd or DatabaseSessionEnd: got: %T", event)) + slog.WarnContext(context.Background(), "unsupported event type: expected SessionEnd, WindowsDesktopSessionEnd or DatabaseSessionEnd", "event_type", logutils.TypeAttr(event)) continue } diff --git a/tool/common/fido2/fido2.go b/tool/common/fido2/fido2.go index f9899224ac8b4..4a9b227d30617 100644 --- a/tool/common/fido2/fido2.go +++ b/tool/common/fido2/fido2.go @@ -25,6 +25,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "os" "github.com/alecthomas/kingpin/v2" @@ -32,7 +33,6 @@ import ( "github.com/go-webauthn/webauthn/protocol/webauthncbor" "github.com/go-webauthn/webauthn/protocol/webauthncose" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" ) @@ -161,7 +161,10 @@ func (c *AttobjCommand) Run() error { cert, err := x509.ParseCertificate(certDER) if err != nil { - log.WithError(err).Warnf("Failed to parse X.509 from x5c[%v], continuing", i) + slog.WarnContext(context.Background(), "Failed to parse X.509 from x5c, continuing", + "index", i, + "error", err, + ) continue } diff --git a/tool/tbot/init.go b/tool/tbot/init.go index ab59ef70adbac..4b7f493bea8df 100644 --- a/tool/tbot/init.go +++ b/tool/tbot/init.go @@ -451,7 +451,7 @@ func onInit(globals *cli.GlobalArgs, init *cli.InitCommand) error { } } if target == nil { - return trace.NotFound("Could not find specified destination %q", init.InitDir) + return trace.NotFound("Initial directory %q must match a destination directory from the configuration file or --destination-dir parameter", init.InitDir) } } diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go index 8800f3aca7f72..ef290ce83ab8b 100644 --- a/tool/tctl/common/auth_command.go +++ b/tool/tctl/common/auth_command.go @@ -22,6 +22,7 @@ import ( "context" "fmt" "io" + "log/slog" "net/url" "os" "strings" @@ -31,7 +32,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - log "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/durationpb" "github.com/gravitational/teleport" @@ -996,7 +996,7 @@ func (a *AuthCommand) generateUserKeys(ctx context.Context, clusterAPI certifica // If we're in multiplexed mode get SNI name for kube from single multiplexed proxy addr kubeTLSServerName := "" if proxyListenerMode == types.ProxyListenerMode_Multiplex { - log.Debug("Using Proxy SNI for kube TLS server name") + slog.DebugContext(ctx, "Using Proxy SNI for kube TLS server name") u, err := parseURL(a.proxyAddr) if err != nil { return trace.Wrap(err) @@ -1008,7 +1008,7 @@ func (a *AuthCommand) generateUserKeys(ctx context.Context, clusterAPI certifica expires, err := keyRing.TeleportTLSCertValidBefore() if err != nil { - log.WithError(err).Warn("Failed to check TTL validity") + slog.WarnContext(ctx, "Failed to check TTL validity", "error", err) // err swallowed on purpose } else if reqExpiry.Sub(expires) > time.Minute { maxAllowedTTL := time.Until(expires).Round(time.Second) @@ -1165,7 +1165,11 @@ func (a *AuthCommand) checkProxyAddr(ctx context.Context, clusterAPI certificate _, err := utils.ParseAddr(addr) if err != nil { - log.Warningf("Invalid public address on the proxy %q: %q: %v.", p.GetName(), addr, err) + slog.WarnContext(ctx, "Invalid public address on the proxy", + "proxy", p.GetName(), + "public_address", addr, + "error", err, + ) continue } @@ -1178,7 +1182,11 @@ func (a *AuthCommand) checkProxyAddr(ctx context.Context, clusterAPI certificate }, ) if err != nil { - log.Warningf("Unable to ping proxy public address on the proxy %q: %q: %v.", p.GetName(), addr, err) + slog.WarnContext(ctx, "Unable to ping proxy public address on the proxy", + "proxy", p.GetName(), + "public_address", addr, + "error", err, + ) continue } diff --git a/tool/tctl/common/auth_rotate_command.go b/tool/tctl/common/auth_rotate_command.go index b0c5f3b31f5c4..d63a6ad914c68 100644 --- a/tool/tctl/common/auth_rotate_command.go +++ b/tool/tctl/common/auth_rotate_command.go @@ -37,7 +37,6 @@ import ( "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "golang.org/x/term" apiclient "github.com/gravitational/teleport/api/client" @@ -1288,7 +1287,6 @@ func setupLoggers(logWriter io.Writer) { logWriter, logutils.SlogTextHandlerConfig{EnableColors: true}, ))) - logrus.StandardLogger().SetOutput(logWriter) } func setupMFAPrompt(client *authclient.Client, pingResp proto.PingResponse, promptWriter io.Writer) { diff --git a/tool/tctl/common/bots_command.go b/tool/tctl/common/bots_command.go index 6a5de45f5afb5..1cd290cb1bcd2 100644 --- a/tool/tctl/common/bots_command.go +++ b/tool/tctl/common/bots_command.go @@ -25,6 +25,7 @@ import ( "errors" "fmt" "io" + "log/slog" "maps" "os" "strings" @@ -34,7 +35,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/google/uuid" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/fieldmaskpb" "github.com/gravitational/teleport" @@ -260,7 +260,7 @@ func (c *BotsCommand) AddBot(ctx context.Context, client *authclient.Client) err roles := splitEntries(c.botRoles) if len(roles) == 0 { - log.Warning("No roles specified. The bot will not be able to produce outputs until a role is added to the bot.") + slog.WarnContext(ctx, "No roles specified - the bot will not be able to produce outputs until a role is added to the bot") } var token types.ProvisionToken if c.tokenID == "" { @@ -386,7 +386,7 @@ func (c *BotsCommand) LockBot(ctx context.Context, client *authclient.Client) er // updateBotLogins applies updates from CLI arguments to a bot's logins trait, // updating the field mask if any updates were made. -func (c *BotsCommand) updateBotLogins(bot *machineidv1pb.Bot, mask *fieldmaskpb.FieldMask) error { +func (c *BotsCommand) updateBotLogins(ctx context.Context, bot *machineidv1pb.Bot, mask *fieldmaskpb.FieldMask) error { traits := map[string][]string{} for _, t := range bot.Spec.GetTraits() { traits[t.Name] = t.Values @@ -419,15 +419,15 @@ func (c *BotsCommand) updateBotLogins(bot *machineidv1pb.Bot, mask *fieldmaskpb. desiredLoginsArray := utils.StringsSliceFromSet(desiredLogins) if maps.Equal(currentLogins, desiredLogins) { - log.Infof("Logins will be left unchanged: %+v", desiredLoginsArray) + slog.InfoContext(ctx, "Logins will be left unchanged", "logins", desiredLoginsArray) return nil } - log.Infof("Desired logins for bot %q: %+v", c.botName, desiredLoginsArray) + slog.InfoContext(ctx, "Desired logins for bot", "bot", c.botName, "logins", desiredLoginsArray) if len(desiredLogins) == 0 { delete(traits, constants.TraitLogins) - log.Infof("Removing logins trait from bot user") + slog.InfoContext(ctx, "Removing logins trait from bot user") } else { traits[constants.TraitLogins] = desiredLoginsArray } @@ -477,11 +477,11 @@ func (c *BotsCommand) updateBotRoles(ctx context.Context, client clientRoleGette desiredRolesArray := utils.StringsSliceFromSet(desiredRoles) if maps.Equal(currentRoles, desiredRoles) { - log.Infof("Roles will be left unchanged: %+v", desiredRolesArray) + slog.InfoContext(ctx, "Roles will be left unchanged", "roles", desiredRolesArray) return nil } - log.Infof("Desired roles for bot %q: %+v", c.botName, desiredRolesArray) + slog.InfoContext(ctx, "Desired roles for bot", "bot", c.botName, "roles", desiredRolesArray) // Validate roles (server does not do this yet). for roleName := range desiredRoles { @@ -510,7 +510,7 @@ func (c *BotsCommand) UpdateBot(ctx context.Context, client *authclient.Client) } if c.setLogins != "" || c.addLogins != "" { - if err := c.updateBotLogins(bot, fieldMask); err != nil { + if err := c.updateBotLogins(ctx, bot, fieldMask); err != nil { return trace.Wrap(err) } } @@ -522,7 +522,7 @@ func (c *BotsCommand) UpdateBot(ctx context.Context, client *authclient.Client) } if len(fieldMask.Paths) == 0 { - log.Infof("No changes requested, nothing to do.") + slog.InfoContext(ctx, "No changes requested, nothing to do") return nil } @@ -534,7 +534,7 @@ func (c *BotsCommand) UpdateBot(ctx context.Context, client *authclient.Client) return trace.Wrap(err) } - log.Infof("Bot %q has been updated. Roles will take effect on its next renewal.", c.botName) + slog.InfoContext(ctx, "Bot has been updated, roles will take effect on its next renewal", "bot", c.botName) return nil } diff --git a/tool/tctl/common/bots_command_test.go b/tool/tctl/common/bots_command_test.go index 5d31e84e80961..ee8fba1cba112 100644 --- a/tool/tctl/common/bots_command_test.go +++ b/tool/tctl/common/bots_command_test.go @@ -123,7 +123,7 @@ func TestUpdateBotLogins(t *testing.T) { setLogins: tt.set, } - err = cmd.updateBotLogins(bot, fieldMask) + err = cmd.updateBotLogins(context.Background(), bot, fieldMask) tt.assert(t, bot, fieldMask, err) }) } diff --git a/tool/tctl/common/config/global.go b/tool/tctl/common/config/global.go index 458905d752e96..0b357db8e848b 100644 --- a/tool/tctl/common/config/global.go +++ b/tool/tctl/common/config/global.go @@ -19,6 +19,7 @@ package config import ( + "context" "errors" "io/fs" "log/slog" @@ -26,7 +27,6 @@ import ( "runtime" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/constants" @@ -65,13 +65,13 @@ type GlobalCLIFlags struct { // The returned authclient.Config has the credentials needed to dial the auth // server. func ApplyConfig(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authclient.Config, error) { + ctx := context.TODO() // --debug flag if ccf.Debug { cfg.Debug = ccf.Debug utils.InitLogger(utils.LoggingForCLI, slog.LevelDebug) - log.Debugf("Debug logging has been enabled.") + slog.DebugContext(ctx, "Debug logging has been enabled") } - cfg.Log = log.StandardLogger() cfg.Logger = slog.Default() if cfg.Version == "" { @@ -126,9 +126,9 @@ func ApplyConfig(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authclient.Confi if !localAuthSvcConf { // Try profile or identity file. if fileConf == nil { - log.Debug("no config file, loading auth config via extension") + slog.DebugContext(ctx, "no config file, loading auth config via extension") } else { - log.Debug("auth_service disabled in config file, loading auth config via extension") + slog.DebugContext(ctx, "auth_service disabled in config file, loading auth config via extension") } authConfig, err := LoadConfigFromProfile(ccf, cfg) if err == nil { diff --git a/tool/tctl/common/config/profile.go b/tool/tctl/common/config/profile.go index f39d85bf859fc..6e5da64ba1f84 100644 --- a/tool/tctl/common/config/profile.go +++ b/tool/tctl/common/config/profile.go @@ -19,11 +19,12 @@ package config import ( + "context" "errors" + "log/slog" "time" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/metadata" @@ -33,10 +34,12 @@ import ( "github.com/gravitational/teleport/lib/client/identityfile" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // LoadConfigFromProfile applies config from ~/.tsh/ profile if it's present func LoadConfigFromProfile(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authclient.Config, error) { + ctx := context.TODO() proxyAddr := "" if len(ccf.AuthServerAddr) != 0 { proxyAddr = ccf.AuthServerAddr[0] @@ -68,7 +71,10 @@ func LoadConfigFromProfile(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authcl } c := client.MakeDefaultConfig() - log.WithFields(log.Fields{"proxy": profile.ProxyURL.String(), "user": profile.Username}).Debugf("Found profile.") + slog.DebugContext(ctx, "Found profile", + "proxy", logutils.StringerAttr(&profile.ProxyURL), + "user", profile.Username, + ) if err := c.LoadProfile(clientStore, proxyAddr); err != nil { return nil, trace.Wrap(err) } @@ -106,7 +112,7 @@ func LoadConfigFromProfile(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authcl if err != nil { return nil, trace.Wrap(err) } - log.Debugf("Setting auth server to web proxy %v.", webProxyAddr) + slog.DebugContext(ctx, "Setting auth server to web proxy", "web_proxy_addr", webProxyAddr) cfg.SetAuthServerAddress(*webProxyAddr) } authConfig.AuthServers = cfg.AuthServerAddresses() diff --git a/tool/tctl/common/devices.go b/tool/tctl/common/devices.go index f8bfc9412ab60..3924c4a798b32 100644 --- a/tool/tctl/common/devices.go +++ b/tool/tctl/common/devices.go @@ -21,6 +21,7 @@ package common import ( "context" "fmt" + "log/slog" "sort" "time" @@ -28,7 +29,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/gravitational/trace/trail" - log "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/timestamppb" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" @@ -490,10 +490,11 @@ func (c *canOperateOnCurrentDevice) setCurrentDevice() (bool, error) { c.osType = cdd.OsType c.assetTag = cdd.SerialNumber - log.Debugf( - "Running device command against current device: %q/%v", - c.assetTag, - devicetrust.FriendlyOSType(c.osType), + slog.DebugContext( + context.Background(), + "Running device command against current device", + "asset_tag", c.assetTag, + "os_type", devicetrust.FriendlyOSType(c.osType), ) return true, nil } diff --git a/tool/tctl/common/kube_command.go b/tool/tctl/common/kube_command.go index ec5f3f9138f33..b0e2f69afe373 100644 --- a/tool/tctl/common/kube_command.go +++ b/tool/tctl/common/kube_command.go @@ -137,10 +137,11 @@ helm repo update --set proxyAddr={{.auth_server}} \ --set authToken={{.token}} \ --create-namespace \ - --namespace=teleport-agent - + --namespace=teleport-agent \ + --version={{.version}} + Please note: - + - This invitation token will expire in {{.minutes}} minutes. - {{.auth_server}} must be reachable from Kubernetes cluster. - The token is usable in a standalone Linux server with kubernetes_service. diff --git a/tool/tctl/common/loadtest_command.go b/tool/tctl/common/loadtest_command.go index fb9075af180fd..3fa9f58063f90 100644 --- a/tool/tctl/common/loadtest_command.go +++ b/tool/tctl/common/loadtest_command.go @@ -33,7 +33,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/google/uuid" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" auditlogpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/auditlog/v1" @@ -186,7 +185,7 @@ func (c *LoadtestCommand) NodeHeartbeats(ctx context.Context, client *authclient return } if err != nil { - log.Debugf("Failed to upsert node: %v", err) + slog.DebugContext(ctx, "Failed to upsert node", "error", err) select { case errch <- err: default: diff --git a/tool/tctl/common/loginrule/command.go b/tool/tctl/common/loginrule/command.go index 022c389d88dd7..15a7f9521db54 100644 --- a/tool/tctl/common/loginrule/command.go +++ b/tool/tctl/common/loginrule/command.go @@ -22,11 +22,11 @@ import ( "context" "errors" "io" + "log/slog" "os" "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" kyaml "k8s.io/apimachinery/pkg/util/yaml" "github.com/gravitational/teleport" @@ -141,7 +141,7 @@ func (t *testCommand) run(ctx context.Context, c *authclient.Client) error { } if len(t.inputResourceFiles) > 0 { - logrus.Debugf("Loaded %d login rule(s) from input resource files", len(loginRules)) + slog.DebugContext(ctx, "Loaded login rule(s) from input resource files", "login_rule_count", len(loginRules)) } traits, err := parseTraitsFile(t.inputTraitsFile) diff --git a/tool/tctl/common/node_command.go b/tool/tctl/common/node_command.go index f07021e2daa1e..0be12463b8184 100644 --- a/tool/tctl/common/node_command.go +++ b/tool/tctl/common/node_command.go @@ -22,6 +22,7 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "os" "strings" "text/template" @@ -29,7 +30,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client" @@ -202,7 +202,7 @@ func (c *NodeCommand) Invite(ctx context.Context, client *authclient.Client) err pingResponse, err := client.Ping(ctx) if err != nil { - log.Debugf("unable to ping auth client: %s.", err.Error()) + slog.DebugContext(ctx, "unable to ping auth client", "error", err) } if err == nil && pingResponse.GetServerFeatures().Cloud { diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index dd9d5ea13af20..23749bc14c528 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "io" + "log/slog" "math" "os" "slices" @@ -34,7 +35,6 @@ import ( "github.com/crewjam/saml/samlsp" "github.com/gravitational/trace" "github.com/gravitational/trace/trail" - log "github.com/sirupsen/logrus" "google.golang.org/protobuf/encoding/protojson" kyaml "k8s.io/apimachinery/pkg/util/yaml" @@ -71,6 +71,7 @@ import ( "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" commonclient "github.com/gravitational/teleport/tool/tctl/common/client" clusterconfigrec "github.com/gravitational/teleport/tool/tctl/common/clusterconfig" tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config" @@ -409,6 +410,9 @@ func (rc *ResourceCommand) createTrustedCluster(ctx context.Context, client *aut return trace.AlreadyExists("trusted cluster %q already exists", name) } + //nolint:staticcheck // SA1019. UpsertTrustedCluster is deprecated but will + // continue being supported for tctl clients. + // TODO(bernardjkim) consider using UpsertTrustedClusterV2 in VX.0.0 out, err := client.UpsertTrustedCluster(ctx, tc) if err != nil { // If force is used and UpsertTrustedCluster returns trace.AlreadyExists, @@ -1365,7 +1369,10 @@ func (rc *ResourceCommand) createSAMLIdPServiceProvider(ctx context.Context, cli // issue warning about unsupported ACS bindings. if err := services.FilterSAMLEntityDescriptor(ed, false /* quiet */); err != nil { - log.Warnf("Entity descriptor for SAML IdP service provider %q contains unsupported ACS bindings: %v", sp.GetEntityID(), err) + slog.WarnContext(ctx, "Entity descriptor for SAML IdP service provider contains unsupported ACS bindings", + "entity_id", sp.GetEntityID(), + "error", err, + ) } } @@ -2246,7 +2253,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient authorities, err := client.GetCertAuthorities(ctx, caType, rc.withSecrets) if err != nil { if trace.IsBadParameter(err) { - log.Warnf("failed to get certificate authority: %v; skipping", err) + slog.WarnContext(ctx, "failed to get certificate authority; skipping", "error", err) continue } return nil, trace.Wrap(err) @@ -2284,7 +2291,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient for _, r := range page { srv, ok := r.ResourceWithLabels.(types.Server) if !ok { - log.Warnf("expected types.Server but received unexpected type %T", r) + slog.WarnContext(ctx, "expected types.Server but received unexpected type", "resource_type", logutils.TypeAttr(r)) continue } diff --git a/tool/tctl/common/token_command.go b/tool/tctl/common/token_command.go index 3f530af05d7f3..c08ee1c122602 100644 --- a/tool/tctl/common/token_command.go +++ b/tool/tctl/common/token_command.go @@ -23,7 +23,9 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "os" + "slices" "sort" "strings" "text/template" @@ -32,7 +34,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/ghodss/yaml" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" @@ -141,6 +142,7 @@ func (c *TokensCommand) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCL c.tokenList = tokens.Command("ls", "List node and user invitation tokens.") c.tokenList.Flag("format", "Output format, 'text', 'json' or 'yaml'").EnumVar(&c.format, formats...) c.tokenList.Flag("with-secrets", "Do not redact join tokens").BoolVar(&c.withSecrets) + c.tokenList.Flag("labels", labelHelp).StringVar(&c.labels) if c.stdout == nil { c.stdout = os.Stdout @@ -285,6 +287,7 @@ func (c *TokensCommand) Add(ctx context.Context, client *authclient.Client) erro "token": token, "minutes": c.ttl.Minutes(), "set_roles": setRoles, + "version": proxies[0].GetTeleportVersion(), }) case roles.Include(types.RoleApp): proxies, err := client.GetProxies() @@ -345,7 +348,7 @@ func (c *TokensCommand) Add(ctx context.Context, client *authclient.Client) erro pingResponse, err := client.Ping(ctx) if err != nil { - log.Debugf("unable to ping auth client: %s.", err.Error()) + slog.DebugContext(ctx, "unable to ping auth client", "error", err) } if err == nil && pingResponse.GetServerFeatures().Cloud { @@ -385,10 +388,26 @@ func (c *TokensCommand) Del(ctx context.Context, client *authclient.Client) erro // List is called to execute "tokens ls" command. func (c *TokensCommand) List(ctx context.Context, client *authclient.Client) error { + labels, err := libclient.ParseLabelSpec(c.labels) + if err != nil { + return trace.Wrap(err) + } + tokens, err := client.GetTokens(ctx) if err != nil { return trace.Wrap(err) } + + tokens = slices.DeleteFunc(tokens, func(token types.ProvisionToken) bool { + tokenLabels := token.GetMetadata().Labels + for k, v := range labels { + if tokenLabels[k] != v { + return true + } + } + return false + }) + if len(tokens) == 0 && c.format == teleport.Text { fmt.Fprintln(c.stdout, "No active tokens found.") return nil diff --git a/tool/tctl/common/token_command_test.go b/tool/tctl/common/token_command_test.go index aef8e3556b21e..afc84f963d4a4 100644 --- a/tool/tctl/common/token_command_test.go +++ b/tool/tctl/common/token_command_test.go @@ -82,58 +82,60 @@ func TestTokens(t *testing.T) { clt := testenv.MakeDefaultAuthClient(t, process) // Test all output formats of "tokens add". - t.Run("add", func(t *testing.T) { - buf, err := runTokensCommand(t, clt, []string{"add", "--type=node"}) - require.NoError(t, err) - require.True(t, strings.HasPrefix(buf.String(), "The invite token:")) + buf, err := runTokensCommand(t, clt, []string{"add", "--type=node"}) + require.NoError(t, err) + require.True(t, strings.HasPrefix(buf.String(), "The invite token:")) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.Text}) - require.NoError(t, err) - require.Equal(t, 1, strings.Count(buf.String(), "\n")) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.Text}) + require.NoError(t, err) + require.Equal(t, 1, strings.Count(buf.String(), "\n")) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.JSON}) - require.NoError(t, err) - out := mustDecodeJSON[addedToken](t, buf) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.JSON}) + require.NoError(t, err) + out := mustDecodeJSON[addedToken](t, buf) - require.Len(t, out.Roles, 2) - require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) - require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) + require.Len(t, out.Roles, 2) + require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) + require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.YAML}) - require.NoError(t, err) - out = mustDecodeYAML[addedToken](t, buf) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.YAML}) + require.NoError(t, err) + out = mustDecodeYAML[addedToken](t, buf) - require.Len(t, out.Roles, 2) - require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) - require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) + require.Len(t, out.Roles, 2) + require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) + require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=kube"}) - require.NoError(t, err) - require.Contains(t, buf.String(), `--set roles="kube\,app\,discovery"`, - "Command print out should include setting kube, app and discovery roles for helm install.") - }) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=kube", "--labels=foo=bar"}) + require.NoError(t, err) + require.Contains(t, buf.String(), `--set roles="kube\,app\,discovery"`, + "Command print out should include setting kube, app and discovery roles for helm install.") // Test all output formats of "tokens ls". - t.Run("ls", func(t *testing.T) { - buf, err := runTokensCommand(t, clt, []string{"ls"}) - require.NoError(t, err) - require.True(t, strings.HasPrefix(buf.String(), "Token ")) - require.Equal(t, 7, strings.Count(buf.String(), "\n")) // account for header lines - - buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.Text}) - require.NoError(t, err) - require.Equal(t, 5, strings.Count(buf.String(), "\n")) - - buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.JSON}) - require.NoError(t, err) - jsonOut := mustDecodeJSON[[]listedToken](t, buf) - require.Len(t, jsonOut, 5) - - buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.YAML}) - require.NoError(t, err) - yamlOut := []listedToken{} - mustDecodeYAMLDocuments(t, buf, &yamlOut) - require.Len(t, yamlOut, 5) - require.Equal(t, jsonOut, yamlOut) - }) + buf, err = runTokensCommand(t, clt, []string{"ls"}) + require.NoError(t, err) + require.True(t, strings.HasPrefix(buf.String(), "Token ")) + require.Equal(t, 7, strings.Count(buf.String(), "\n")) // account for header lines + + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.Text}) + require.NoError(t, err) + require.Equal(t, 5, strings.Count(buf.String(), "\n")) + + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.JSON}) + require.NoError(t, err) + jsonOut := mustDecodeJSON[[]listedToken](t, buf) + require.Len(t, jsonOut, 5) + + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.YAML}) + require.NoError(t, err) + yamlOut := []listedToken{} + mustDecodeYAMLDocuments(t, buf, &yamlOut) + require.Len(t, yamlOut, 5) + require.Equal(t, jsonOut, yamlOut) + + // Test filtering by label + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.JSON, "--labels=foo=bar"}) + require.NoError(t, err) + jsonOut = mustDecodeJSON[[]listedToken](t, buf) + require.Len(t, jsonOut, 1) } diff --git a/tool/tctl/common/user_command.go b/tool/tctl/common/user_command.go index 83fe2f7e56643..9325335b9b4aa 100644 --- a/tool/tctl/common/user_command.go +++ b/tool/tctl/common/user_command.go @@ -23,6 +23,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/url" "os" "strconv" @@ -31,7 +32,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/constants" @@ -482,7 +482,11 @@ func (u *UserCommand) Update(ctx context.Context, client *authclient.Client) err for _, roleName := range user.GetRoles() { if _, err := client.GetRole(ctx, roleName); err != nil { - log.Warnf("Error checking role %q when upserting user %q: %v", roleName, user.GetName(), err) + slog.WarnContext(ctx, "Error checking role when upserting user", + "role", roleName, + "user", user.GetName(), + "error", err, + ) } } if _, err := client.UpsertUser(ctx, user); err != nil { diff --git a/tool/tctl/sso/configure/command.go b/tool/tctl/sso/configure/command.go index 77e4a75e6aa9e..18e11c337ccdc 100644 --- a/tool/tctl/sso/configure/command.go +++ b/tool/tctl/sso/configure/command.go @@ -20,11 +20,11 @@ package configure import ( "context" + "log/slog" "os" "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/auth/authclient" @@ -40,7 +40,7 @@ type SSOConfigureCommand struct { Config *servicecfg.Config ConfigureCmd *kingpin.CmdClause AuthCommands []*AuthKindCommand - Logger *logrus.Entry + Logger *slog.Logger } type AuthKindCommand struct { @@ -52,7 +52,7 @@ type AuthKindCommand struct { // argument parsing func (cmd *SSOConfigureCommand) Initialize(app *kingpin.Application, flags *tctlcfg.GlobalCLIFlags, cfg *servicecfg.Config) { cmd.Config = cfg - cmd.Logger = cfg.Log.WithField(teleport.ComponentKey, teleport.ComponentClient) + cmd.Logger = cfg.Logger.With(teleport.ComponentKey, teleport.ComponentClient) sso := app.Command("sso", "A family of commands for configuring and testing auth connectors (SSO).") cmd.ConfigureCmd = sso.Command("configure", "Create auth connector configuration.") @@ -67,10 +67,12 @@ func (cmd *SSOConfigureCommand) TryRun(ctx context.Context, selectedCommand stri // the default tctl logging behavior is to ignore all logs, unless --debug is present. // we want different behavior: log messages as normal, but with compact format (no time, no caller info). if !cmd.Config.Debug { - formatter := logutils.NewDefaultTextFormatter(utils.IsTerminal(os.Stderr)) - formatter.FormatCaller = func() (caller string) { return "" } - cmd.Logger.Logger.SetFormatter(formatter) - cmd.Logger.Logger.SetOutput(os.Stderr) + cmd.Logger = slog.New(logutils.NewSlogTextHandler(os.Stderr, logutils.SlogTextHandlerConfig{ + Level: cmd.Config.GetLogLevel(), + EnableColors: utils.IsTerminal(os.Stderr), + ConfiguredFields: []string{logutils.LevelField, logutils.ComponentField}, + })) + } client, closeFn, err := clientFunc(ctx) if err != nil { diff --git a/tool/tctl/sso/configure/github.go b/tool/tctl/sso/configure/github.go index 8369e7fe93fbd..09287ea6ebf7b 100644 --- a/tool/tctl/sso/configure/github.go +++ b/tool/tctl/sso/configure/github.go @@ -21,11 +21,11 @@ package configure import ( "context" "fmt" + "log/slog" "os" "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth/authclient" @@ -83,7 +83,7 @@ Examples: The values for --secret and --id are provided by GitHub. > tctl sso configure gh ... | tctl sso test - + Generate the configuration and immediately test it using "tctl sso test" command.`) preset := &AuthKindCommand{ @@ -104,7 +104,7 @@ func ghRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.Github } if spec.RedirectURL == "" { - spec.RedirectURL = ResolveCallbackURL(cmd.Logger, clt, "RedirectURL", "https://%v/v1/webapi/github/callback") + spec.RedirectURL = ResolveCallbackURL(ctx, cmd.Logger, clt, "RedirectURL", "https://%v/v1/webapi/github/callback") } connector, err := types.NewGithubConnector(flags.connectorName, *spec) @@ -115,13 +115,13 @@ func ghRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.Github } // ResolveCallbackURL deals with common pattern of resolving callback URL for IdP to use. -func ResolveCallbackURL(logger *logrus.Entry, clt *authclient.Client, fieldName string, callbackPattern string) string { +func ResolveCallbackURL(ctx context.Context, logger *slog.Logger, clt *authclient.Client, fieldName string, callbackPattern string) string { var callbackURL string - logger.Infof("%v empty, resolving automatically.", fieldName) + logger.InfoContext(ctx, "resolving callback url automatically", "field_name", fieldName) proxies, err := clt.GetProxies() if err != nil { - logger.WithError(err).Error("unable to get proxy list.") + logger.ErrorContext(ctx, "unable to get proxy list", "error", err) } // find first proxy with public addr @@ -135,17 +135,17 @@ func ResolveCallbackURL(logger *logrus.Entry, clt *authclient.Client, fieldName // check if successfully set. if callbackURL == "" { - logger.Warnf("Unable to fill %v automatically, cluster's public address unknown.", fieldName) + logger.WarnContext(ctx, "Unable to resolve callback url automatically, cluster's public address unknown", "field_name", fieldName) } else { - logger.Infof("%v set to %q", fieldName, callbackURL) + logger.InfoContext(ctx, "resolved callback url", "field_name", fieldName, "callback_url", callbackURL) } return callbackURL } -func specCheckRoles(ctx context.Context, logger *logrus.Entry, spec *types.GithubConnectorSpecV3, ignoreMissingRoles bool, clt *authclient.Client) error { +func specCheckRoles(ctx context.Context, logger *slog.Logger, spec *types.GithubConnectorSpecV3, ignoreMissingRoles bool, clt *authclient.Client) error { allRoles, err := clt.GetRoles(ctx) if err != nil { - logger.WithError(err).Warn("Unable to get roles list. Skipping teams-to-roles sanity checks.") + logger.WarnContext(ctx, "Unable to get roles list, skipping teams-to-roles sanity checks", "error", err) return nil } @@ -161,7 +161,10 @@ func specCheckRoles(ctx context.Context, logger *logrus.Entry, spec *types.Githu _, found := roleMap[role] if !found { if ignoreMissingRoles { - logger.Warnf("teams-to-roles references non-existing role: %q. Available roles: %v.", role, roleNames) + logger.WarnContext(ctx, "teams-to-roles references non-existing role", + "non_existent_role", role, + "available_roles", roleNames, + ) } else { return trace.BadParameter("teams-to-roles references non-existing role: %v. Correct the mapping, or add --ignore-missing-roles to ignore this error. Available roles: %v.", role, roleNames) } diff --git a/tool/tctl/sso/configure/oidc.go b/tool/tctl/sso/configure/oidc.go index c783d29192381..b846f87f1d1fc 100644 --- a/tool/tctl/sso/configure/oidc.go +++ b/tool/tctl/sso/configure/oidc.go @@ -19,6 +19,7 @@ package configure import ( "context" "fmt" + "log/slog" "net/url" "os" "strings" @@ -26,7 +27,6 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/coreos/go-oidc/v3/oidc" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" @@ -43,7 +43,7 @@ type oidcPreset struct { description string display string issuerURL string - modifySpec func(logger *logrus.Entry, spec *types.OIDCConnectorSpecV3) error + modifySpec func(ctx context.Context, logger *slog.Logger, spec *types.OIDCConnectorSpecV3) error } type oidcPresetList []oidcPreset @@ -73,7 +73,7 @@ var oidcPresets = oidcPresetList([]oidcPreset{ description: "Google Workspace", display: "Google", issuerURL: "https://accounts.google.com", - modifySpec: func(logger *logrus.Entry, spec *types.OIDCConnectorSpecV3) error { + modifySpec: func(ctx context.Context, logger *slog.Logger, spec *types.OIDCConnectorSpecV3) error { if !strings.HasSuffix(spec.ClientID, ".apps.googleusercontent.com") { return trace.BadParameter(`For Google Workspace the client ID have to use format ".apps.googleusercontent.com", got %q instead. Set full value with --id=... or shorthand with --google-id=...`, spec.ClientID) } @@ -95,14 +95,14 @@ var oidcPresets = oidcPresetList([]oidcPreset{ description: "GitLab", display: "GitLab", issuerURL: "https://gitlab.com", - modifySpec: func(logger *logrus.Entry, spec *types.OIDCConnectorSpecV3) error { + modifySpec: func(ctx context.Context, logger *slog.Logger, spec *types.OIDCConnectorSpecV3) error { switch spec.Prompt { case "none": break case "": spec.Prompt = "none" default: - logger.Warnf("GitLab requires the 'prompt' parameter to be set to 'none', but %q found", spec.Prompt) + logger.WarnContext(ctx, "GitLab 'prompt' parameter was not set to required value of 'none'", "prompt", spec.Prompt) } return nil @@ -114,13 +114,13 @@ var oidcPresets = oidcPresetList([]oidcPreset{ description: "Okta", display: "Okta", issuerURL: "https://oktaice.okta.com", - modifySpec: func(logger *logrus.Entry, spec *types.OIDCConnectorSpecV3) error { + modifySpec: func(ctx context.Context, logger *slog.Logger, spec *types.OIDCConnectorSpecV3) error { if spec.Provider == "" { spec.Provider = teleport.Okta } if spec.Provider != teleport.Okta { - logger.Warnf("Okta requires %q provider, but %q found.", teleport.Okta, spec.Provider) + logger.WarnContext(ctx, "Configured provider was not okta", "provider", spec.Provider) } return nil @@ -186,12 +186,12 @@ Examples: > tctl sso configure oidc -n myauth -r groups,admin,access,editor,auditor -r group,developer,access --secret IDP_SECRET --id CLIENT_ID --issuer-url https://idp.example.com - Generate OIDC auth connector configuration called 'myauth'. Two mappings from OIDC claims to roles are defined: + Generate OIDC auth connector configuration called 'myauth'. Two mappings from OIDC claims to roles are defined: - members of 'admin' group will receive 'access', 'editor' and 'auditor' roles. - members of 'developer' group will receive 'access' role. The values for --secret, --id and --issuer-url are provided by IdP. - + > tctl sso configure oidc --preset okta --scope groups -r groups,okta-admin,access,editor,auditor --secret IDP_SECRET --id CLIENT_ID --issuer-url dev-123456.oktapreview.com Generate OIDC auth connector with Okta preset, enabled 'groups' scope, mapping group 'okta-admin' to roles 'access', 'editor', 'auditor'. @@ -202,7 +202,7 @@ Examples: Generate OIDC auth connector with Google preset. Service account credentials are set to be loaded from /var/lib/teleport/gacc.json with --google-acc-uri. > tctl sso configure oidc ... | tctl sso test - + Generate the configuration and immediately test it using "tctl sso test" command.`, presets)) preset := &AuthKindCommand{ @@ -237,7 +237,7 @@ func oidcRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.OIDC // automatically switch to 'google' preset if google-specific flags are set. if flags.chosenPreset == "" { if spec.GoogleAdminEmail != "" || spec.GoogleServiceAccount != "" || spec.GoogleServiceAccountURI != "" { - cmd.Logger.Infof("Google-specific flags detected, enabling preset %q.", presetGoogle) + cmd.Logger.InfoContext(ctx, "Google-specific flags detected, enabling google preset") flags.chosenPreset = presetGoogle } } @@ -258,7 +258,7 @@ func oidcRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.OIDC } if p.modifySpec != nil { - if err := p.modifySpec(cmd.Logger, spec); err != nil { + if err := p.modifySpec(ctx, cmd.Logger, spec); err != nil { return trace.Wrap(err) } } @@ -280,7 +280,7 @@ func oidcRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.OIDC switch strings.ToLower(parse.Scheme) { case "": spec.IssuerURL = "https://" + spec.IssuerURL - cmd.Logger.Infof("Missing scheme for issuer URL. Defaulting to %q. New value: %q", "https://", spec.IssuerURL) + cmd.Logger.InfoContext(ctx, "Missing scheme for issuer URL, using https", "issuer_url", spec.IssuerURL) case "https": break default: @@ -290,7 +290,7 @@ func oidcRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.OIDC // verify .well-known/openid-configuration is reachable if _, err := oidc.NewProvider(ctx, spec.IssuerURL); err != nil { if cmd.Config.Debug { - cmd.Logger.WithError(err).Warnf("Failed to load .well-known/openid-configuration for issuer URL %q", spec.IssuerURL) + cmd.Logger.WarnContext(ctx, "Failed to load .well-known/openid-configuration for issuer URL", "issuer_url", spec.IssuerURL, "error", err) } return trace.BadParameter("Failed to load .well-known/openid-configuration for issuer URL %q. Check expected --issuer-url against IdP configuration. Rerun with --debug to see the error.", spec.IssuerURL) } @@ -301,7 +301,7 @@ func oidcRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.OIDC allRoles, err := clt.GetRoles(ctx) if err != nil { - cmd.Logger.WithError(err).Warn("unable to get roles list. Skipping attributes_to_roles sanity checks.") + cmd.Logger.WarnContext(ctx, "unable to get roles list, skipping attributes_to_roles sanity checks", "error", err) } else { roleMap := map[string]bool{} var roleNames []string @@ -315,7 +315,7 @@ func oidcRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.OIDC _, found := roleMap[role] if !found { if flags.ignoreMissingRoles { - cmd.Logger.Warnf("claims-to-roles references non-existing role: %q. Available roles: %v.", role, roleNames) + cmd.Logger.WarnContext(ctx, "claims-to-roles references non-existing role", "role", role, "available_roles", roleNames) } else { return trace.BadParameter("claims-to-roles references non-existing role: %v. Correct the mapping, or add --ignore-missing-roles to ignore this error. Available roles: %v.", role, roleNames) } @@ -325,7 +325,7 @@ func oidcRunFunc(ctx context.Context, cmd *SSOConfigureCommand, spec *types.OIDC } if len(spec.RedirectURLs) == 0 { - spec.RedirectURLs = []string{ResolveCallbackURL(cmd.Logger, clt, "RedirectURLs", "https://%v/v1/webapi/oidc/callback")} + spec.RedirectURLs = []string{ResolveCallbackURL(ctx, cmd.Logger, clt, "RedirectURLs", "https://%v/v1/webapi/oidc/callback")} } connector, err := types.NewOIDCConnector(flags.connectorName, *spec) diff --git a/tool/tctl/sso/configure/saml.go b/tool/tctl/sso/configure/saml.go index fa9fa26b8e044..ee48f76885859 100644 --- a/tool/tctl/sso/configure/saml.go +++ b/tool/tctl/sso/configure/saml.go @@ -19,12 +19,12 @@ package configure import ( "context" "fmt" + "log/slog" "net/url" "os" "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" @@ -132,9 +132,9 @@ Presets: Examples: - > tctl sso configure saml -n myauth -r groups,admin,access,editor,auditor -r groups,developer,access -e entity-desc.xml + > tctl sso configure saml -n myauth -r groups,admin,access,editor,auditor -r groups,developer,access -e entity-desc.xml - Generate SAML auth connector configuration called 'myauth'. Two mappings from SAML attributes to roles are defined: + Generate SAML auth connector configuration called 'myauth'. Two mappings from SAML attributes to roles are defined: - members of 'admin' group will receive 'access', 'editor' and 'auditor' roles. - members of 'developer' group will receive 'access' role. The IdP metadata will be read from 'entity-desc.xml' file. @@ -147,7 +147,7 @@ Examples: > tctl sso configure saml -p okta -r groups,developer,access -e entity-desc.xml | tctl sso test - + Generate the configuration and immediately test it using "tctl sso test" command. `, presets)) @@ -197,7 +197,7 @@ func samlRunFunc( allRoles, err := clt.GetRoles(ctx) if err != nil { - cmd.Logger.WithError(err).Warn("unable to get roles list. Skipping attributes-to-roles sanity checks.") + cmd.Logger.WarnContext(ctx, "unable to get roles list, skipping attributes-to-roles sanity checks", "error", err) } else { roleMap := map[string]bool{} var roleNames []string @@ -211,7 +211,7 @@ func samlRunFunc( _, found := roleMap[role] if !found { if flags.ignoreMissingRoles { - cmd.Logger.Warnf("attributes-to-roles references non-existing role: %q. Available roles: %v.", role, roleNames) + cmd.Logger.WarnContext(ctx, "attributes-to-roles references non-existing role", "role", role, "available_roles", roleNames) } else { return trace.BadParameter("attributes-to-roles references non-existing role: %v. Correct the mapping, or add --ignore-missing-roles to ignore this error. Available roles: %v.", role, roleNames) } @@ -241,12 +241,12 @@ func samlRunFunc( } if spec.AssertionConsumerService == "" { - spec.AssertionConsumerService = ResolveCallbackURL(cmd.Logger, clt, "ACS", "https://%v/v1/webapi/saml/acs/"+flags.connectorName) + spec.AssertionConsumerService = ResolveCallbackURL(ctx, cmd.Logger, clt, "ACS", "https://%v/v1/webapi/saml/acs/"+flags.connectorName) } // figure out the actual meaning of entityDescriptorFlag. Can be: URL, file, plain XML. if flags.entityDescriptorFlag != "" { - if err = processEntityDescriptorFlag(spec, flags.entityDescriptorFlag, cmd.Logger); err != nil { + if err = processEntityDescriptorFlag(ctx, spec, flags.entityDescriptorFlag, cmd.Logger); err != nil { return trace.Wrap(err) } } @@ -278,20 +278,20 @@ func keyPairFromFlags(flags types.AsymmetricKeyPair) *types.AsymmetricKeyPair { return &flags } -func processEntityDescriptorFlag(spec *types.SAMLConnectorSpecV2, entityDescriptorFlag string, log *logrus.Entry) error { +func processEntityDescriptorFlag(ctx context.Context, spec *types.SAMLConnectorSpecV2, entityDescriptorFlag string, log *slog.Logger) error { var err error // case: URL var parsedURL *url.URL if parsedURL, err = url.Parse(entityDescriptorFlag); err == nil && parsedURL.Scheme != "" { spec.EntityDescriptorURL = entityDescriptorFlag - log.Infof("Entity descriptor looks like URL, entity_descriptor_url set to %q.", spec.EntityDescriptorURL) + log.InfoContext(ctx, "Using entity descriptor URL", "entity_descriptor_url", spec.EntityDescriptorURL) return nil } if parsedURL.Scheme == "" { - log.Infof("Cannot parse entity descriptor as URL, missing scheme: %q.", entityDescriptorFlag) + log.InfoContext(ctx, "entity descriptor URL missing scheme", "entity_descriptor", entityDescriptorFlag) } else { - log.WithError(err).Infof("Cannot parse entity descriptor as URL: %q.", entityDescriptorFlag) + log.InfoContext(ctx, "invalid entity descriptor URL", "entity_descriptor", entityDescriptorFlag, "error", err) } // case: file @@ -301,18 +301,18 @@ func processEntityDescriptorFlag(spec *types.SAMLConnectorSpecV2, entityDescript return trace.WrapWithMessage(err, "Validating entity descriptor from file %q failed. Check that XML is valid or download the file directly.", entityDescriptorFlag) } spec.EntityDescriptor = string(bytes) - log.Infof("Entity descriptor read from file %q.", entityDescriptorFlag) + log.InfoContext(ctx, "Entity descriptor read from file", "file", entityDescriptorFlag) return nil } - log.WithError(err).Infof("Cannot read entity descriptor from file: %q.", entityDescriptorFlag) + log.InfoContext(ctx, "Cannot read entity descriptor from file", "file", entityDescriptorFlag, "error", err) // case: verbatim XML if err = validateEntityDescriptor([]byte(entityDescriptorFlag), spec.Cert); err == nil { spec.EntityDescriptor = entityDescriptorFlag - log.Infof("Entity descriptor is valid XML, EntityDescriptor set to flag value.") + log.InfoContext(ctx, "Entity descriptor is valid XML, EntityDescriptor set to flag value") return nil } - log.WithError(trace.Unwrap(err)).Infof("Cannot parse entity descriptor as verbatim XML: %q.", entityDescriptorFlag) + log.InfoContext(ctx, "Cannot parse entity descriptor as verbatim XML", "entity_descriptor", entityDescriptorFlag, "error", err) return trace.Errorf("failed to process -e/--entity-descriptor flag. Valid values: XML file, URL, verbatim XML") } diff --git a/tool/tctl/sso/configure/saml_test.go b/tool/tctl/sso/configure/saml_test.go index ec9e6be0ca9f3..5187efbe4d5bd 100644 --- a/tool/tctl/sso/configure/saml_test.go +++ b/tool/tctl/sso/configure/saml_test.go @@ -17,15 +17,16 @@ package configure import ( - "io" + "context" + "log/slog" "os" "path/filepath" "testing" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" + logutils "github.com/gravitational/teleport/lib/utils/log" ) func Test_processEntityDescriptorFlag(t *testing.T) { @@ -59,7 +60,6 @@ V115UGOwvjOOxmOFbYBn865SHgMndFtr tests := []struct { name string entityDescriptor string - log *logrus.Entry wantErr bool want types.SAMLConnectorSpecV2 @@ -99,13 +99,10 @@ V115UGOwvjOOxmOFbYBn865SHgMndFtr }, } - log := logrus.New() - log.Out = io.Discard - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { spec := types.SAMLConnectorSpecV2{} - err := processEntityDescriptorFlag(&spec, tt.entityDescriptor, logrus.NewEntry(log)) + err := processEntityDescriptorFlag(context.Background(), &spec, tt.entityDescriptor, slog.New(logutils.DiscardHandler{})) if tt.wantErr { require.Error(t, err) } else { diff --git a/tool/teleport/common/sftp.go b/tool/teleport/common/sftp.go index 4d3e46f4decf7..2f81da0466c15 100644 --- a/tool/teleport/common/sftp.go +++ b/tool/teleport/common/sftp.go @@ -21,11 +21,13 @@ package common import ( "bufio" "bytes" + "context" "encoding/json" "errors" "fmt" "io" "io/fs" + "log/slog" "os" "os/user" "path" @@ -37,7 +39,6 @@ import ( "github.com/gogo/protobuf/jsonpb" "github.com/gravitational/trace" "github.com/pkg/sftp" - log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" "github.com/gravitational/teleport" @@ -45,7 +46,6 @@ import ( "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/srv" - "github.com/gravitational/teleport/lib/utils" ) const ( @@ -89,7 +89,7 @@ type allowedOps struct { // sftpHandler provides handlers for a SFTP server. type sftpHandler struct { - logger *log.Entry + logger *slog.Logger allowed *allowedOps // mtx protects files @@ -121,7 +121,7 @@ func (t *trackedFile) Close() error { return t.file.Close() } -func newSFTPHandler(logger *log.Entry, req *srv.FileTransferRequest, events chan<- apievents.AuditEvent) (*sftpHandler, error) { +func newSFTPHandler(logger *slog.Logger, req *srv.FileTransferRequest, events chan<- apievents.AuditEvent) (*sftpHandler, error) { var allowed *allowedOps if req != nil { allowed = &allowedOps{ @@ -460,6 +460,7 @@ func (s *sftpHandler) Lstat(req *sftp.Request) (sftp.ListerAt, error) { } func (s *sftpHandler) sendSFTPEvent(req *sftp.Request, reqErr error) { + ctx := context.TODO() event := &apievents.SFTP{ Metadata: apievents.Metadata{ Type: events.SFTPEvent, @@ -532,13 +533,13 @@ func (s *sftpHandler) sendSFTPEvent(req *sftp.Request, reqErr error) { } event.Action = apievents.SFTPAction_LINK default: - s.logger.Warnf("Unknown SFTP request %q", req.Method) + s.logger.WarnContext(ctx, "Unknown SFTP request", "request", req.Method) return } wd, err := os.Getwd() if err != nil { - s.logger.WithError(err).Warn("Failed to get working dir.") + s.logger.WarnContext(ctx, "Failed to get working dir", "error", err) } event.WorkingDirectory = wd @@ -569,7 +570,7 @@ func (s *sftpHandler) sendSFTPEvent(req *sftp.Request, reqErr error) { } } if reqErr != nil { - s.logger.Debugf("%s: %v", req.Method, reqErr) + s.logger.DebugContext(ctx, "failed handling SFTP request", "request", req.Method, "error", reqErr) // If possible, strip the filename from the error message. The // path will be included in audit events already, no need to // make the error message longer than it needs to be. @@ -605,8 +606,7 @@ func onSFTP() error { defer auditFile.Close() // Ensure the parent process will receive log messages from us - l := utils.NewLogger() - logger := l.WithField(teleport.ComponentKey, teleport.ComponentSubsystemSFTP) + logger := slog.With(teleport.ComponentKey, teleport.ComponentSubsystemSFTP) currentUser, err := user.Current() if err != nil { @@ -653,6 +653,7 @@ func onSFTP() error { } sftpSrv := sftp.NewRequestServer(ch, handler, sftp.WithStartDirectory(currentUser.HomeDir)) + ctx := context.TODO() // Start a goroutine to marshal and send audit events to the parent // process to avoid blocking the SFTP connection on event handling done := make(chan struct{}) @@ -662,13 +663,13 @@ func onSFTP() error { for event := range sftpEvents { oneOfEvent, err := apievents.ToOneOf(event) if err != nil { - logger.WithError(err).Warn("Failed to convert SFTP event to OneOf.") + logger.WarnContext(ctx, "Failed to convert SFTP event to OneOf", "error", err) continue } buf.Reset() if err := m.Marshal(&buf, oneOfEvent); err != nil { - logger.WithError(err).Warn("Failed to marshal SFTP event.") + logger.WarnContext(ctx, "Failed to marshal SFTP event", "error", err) continue } @@ -677,7 +678,7 @@ func onSFTP() error { buf.WriteByte(0x0) _, err = io.Copy(auditFile, &buf) if err != nil { - logger.WithError(err).Warn("Failed to send SFTP event to parent.") + logger.WarnContext(ctx, "Failed to send SFTP event to parent", "error", err) } } diff --git a/tool/teleport/common/teleport_test.go b/tool/teleport/common/teleport_test.go index 62f77dc4eccca..7b1292f1e625c 100644 --- a/tool/teleport/common/teleport_test.go +++ b/tool/teleport/common/teleport_test.go @@ -20,13 +20,14 @@ package common import ( "bytes" + "context" "fmt" + "log/slog" "os" "path/filepath" "testing" "github.com/gravitational/trace" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" @@ -84,7 +85,7 @@ func TestTeleportMain(t *testing.T) { require.True(t, conf.SSH.Enabled) require.True(t, conf.Proxy.Enabled) require.Equal(t, os.Stdout, conf.Console) - require.Equal(t, log.ErrorLevel, log.GetLevel()) + require.True(t, slog.Default().Handler().Enabled(context.Background(), slog.LevelError)) }) t.Run("RolesFlag", func(t *testing.T) { @@ -125,7 +126,7 @@ func TestTeleportMain(t *testing.T) { require.True(t, conf.SSH.Enabled) require.False(t, conf.Auth.Enabled) require.False(t, conf.Proxy.Enabled) - require.Equal(t, log.DebugLevel, conf.Log.GetLevel()) + require.True(t, slog.Default().Handler().Enabled(context.Background(), slog.LevelDebug)) require.Equal(t, "hvostongo.example.org", conf.Hostname) token, err := conf.Token() diff --git a/tool/teleport/common/usage.go b/tool/teleport/common/usage.go index 85f429350313b..bc9ce79d18f0f 100644 --- a/tool/teleport/common/usage.go +++ b/tool/teleport/common/usage.go @@ -47,16 +47,16 @@ const ( --uri="http://localhost:8080" \ --labels=group=dev Same as the above, but the app server runs with "group=dev" label which only - allows access to users with the role "group=dev".` + allows access to users with the application label "group: dev" in an assigned role.` dbUsageExamples = ` > teleport db start --token=xyz --auth-server=proxy.example.com:3080 \ --name="example-db" \ --protocol="postgres" \ --uri="localhost:5432" -Starts a database server that proxies PostgreSQL database "example-db" running -at localhost:5432. The database must be configured with Teleport CA and key -pair issued by "tctl auth sign --format=db". + Starts a database server that proxies PostgreSQL database "example-db" running + at localhost:5432. The database must be configured with Teleport CA and key + pair issued by "tctl auth sign --format=db". > teleport db start --token=xyz --auth-server=proxy.example.com:3080 \ --name="aurora-db" \ @@ -64,8 +64,9 @@ pair issued by "tctl auth sign --format=db". --uri="example.cluster-abcdefghij.us-west-1.rds.amazonaws.com:3306" \ --aws-region=us-west-1 \ --labels=env=aws -Starts a database server that proxies Aurora MySQL database running in AWS -region us-west-1 which only allows access to users with the role "env=aws".` + Starts a database server that proxies Aurora MySQL database running in AWS + region us-west-1 which only allows access to users with the database label + "env: aws" in an assigned role.` systemdInstallExamples = ` > teleport install systemd diff --git a/tool/teleport/testenv/test_server.go b/tool/teleport/testenv/test_server.go index 5d4607223c292..3e034d9fdf0d6 100644 --- a/tool/teleport/testenv/test_server.go +++ b/tool/teleport/testenv/test_server.go @@ -417,7 +417,7 @@ func SetupTrustedCluster(ctx context.Context, t *testing.T, rootServer, leafServ rootProxyTunnelAddr, err := rootServer.ProxyTunnelAddr() require.NoError(t, err) - tc, err := types.NewTrustedCluster("root-cluster", types.TrustedClusterSpecV2{ + tc, err := types.NewTrustedCluster(rootServer.Config.Auth.ClusterName.GetClusterName(), types.TrustedClusterSpecV2{ Enabled: true, Token: StaticToken, ProxyAddress: rootProxyAddr.String(), @@ -431,7 +431,7 @@ func SetupTrustedCluster(ctx context.Context, t *testing.T, rootServer, leafServ }) require.NoError(t, err) - _, err = leafServer.GetAuthServer().UpsertTrustedCluster(ctx, tc) + _, err = leafServer.GetAuthServer().UpsertTrustedClusterV2(ctx, tc) require.NoError(t, err) require.EventuallyWithT(t, func(t *assert.CollectT) { diff --git a/tool/tsh/common/access_request_test.go b/tool/tsh/common/access_request_test.go index 7f25c5157149b..9b4c5bafff653 100644 --- a/tool/tsh/common/access_request_test.go +++ b/tool/tsh/common/access_request_test.go @@ -200,7 +200,7 @@ func TestAccessRequestSearch(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - homePath, _ := mustLogin(t, s, tc.args.teleportCluster) + homePath, _ := mustLoginLegacy(t, s, tc.args.teleportCluster) captureStdout := new(bytes.Buffer) err := Run( context.Background(), diff --git a/tool/tsh/common/aliases.go b/tool/tsh/common/aliases.go index 946842bbb28ba..a6cce4e892287 100644 --- a/tool/tsh/common/aliases.go +++ b/tool/tsh/common/aliases.go @@ -29,6 +29,8 @@ import ( "github.com/google/shlex" "github.com/gravitational/trace" + + logutils "github.com/gravitational/teleport/lib/utils/log" ) // tshAliasEnvKey is an env variable storing the aliases that, so far, has been expanded, and should not be expanded again. @@ -205,7 +207,7 @@ func (ar *aliasRunner) runAliasCommand(ctx context.Context, currentExecPath, exe // if execPath is our path, skip re-execution and run main directly instead. // this makes for better error messages in case of failures. if execPath == currentExecPath { - log.Debugf("Self re-exec command: tsh %v.", arguments) + logger.DebugContext(ctx, "tsh re-exec command", "arguments", arguments) return trace.Wrap(ar.runTshMain(ctx, arguments)) } @@ -214,7 +216,7 @@ func (ar *aliasRunner) runAliasCommand(ctx context.Context, currentExecPath, exe cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - log.Debugf("Running external command: %v", cmd) + logger.DebugContext(ctx, "Running external command", "command", logutils.StringerAttr(cmd)) err = ar.runExternalCommand(cmd) if err == nil { return nil diff --git a/tool/tsh/common/app.go b/tool/tsh/common/app.go index 15e391c3959ec..e01935cb05212 100644 --- a/tool/tsh/common/app.go +++ b/tool/tsh/common/app.go @@ -40,6 +40,7 @@ import ( "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // onAppLogin implements "tsh apps login" command. @@ -78,14 +79,23 @@ func onAppLogin(cf *CLIConf) error { } defer clusterClient.Close() + if err := validateTargetPort(app, int(cf.TargetPort)); err != nil { + return trace.Wrap(err) + } + rootClient, err := clusterClient.ConnectToRootCluster(cf.Context) if err != nil { return trace.Wrap(err) } + routeToApp := appInfo.RouteToApp + if cf.TargetPort != 0 { + routeToApp.TargetPort = uint32(cf.TargetPort) + } + appCertParams := client.ReissueParams{ RouteToCluster: tc.SiteName, - RouteToApp: appInfo.RouteToApp, + RouteToApp: routeToApp, AccessRequests: appInfo.profile.ActiveRequests.AccessRequests, } @@ -98,7 +108,7 @@ func onAppLogin(cf *CLIConf) error { return trace.Wrap(err) } - if err := printAppCommand(cf, tc, app, appInfo.RouteToApp); err != nil { + if err := printAppCommand(cf, tc, app, routeToApp); err != nil { return trace.Wrap(err) } @@ -151,7 +161,7 @@ func printAppCommand(cf *CLIConf, tc *client.TeleportClient, app types.Applicati cmd.Stderr = cf.Stderr() cmd.Stdout = output - log.Debugf("Running automatic az login: %v", cmd.String()) + logger.DebugContext(cf.Context, "Running automatic az login", "command", logutils.StringerAttr(cmd)) err := cf.RunCommand(cmd) if err != nil { return trace.Wrap(err, "failed to automatically login with `az login` using identity %q; run with --debug for details", routeToApp.AzureIdentity) @@ -169,8 +179,14 @@ func printAppCommand(cf *CLIConf, tc *client.TeleportClient, app types.Applicati }) case app.IsTCP(): + appNameWithOptionalTargetPort := app.GetName() + if routeToApp.TargetPort != 0 { + appNameWithOptionalTargetPort = fmt.Sprintf("%s:%d", app.GetName(), routeToApp.TargetPort) + } + return tcpAppLoginTemplate.Execute(output, map[string]string{ - "appName": app.GetName(), + "appName": app.GetName(), + "appNameWithOptionalTargetPort": appNameWithOptionalTargetPort, }) case localProxyRequiredForApp(tc): @@ -231,7 +247,7 @@ Then connect to the application through this proxy: // tcpAppLoginTemplate is the message that gets printed to a user upon successful // login into a TCP application. var tcpAppLoginTemplate = template.Must(template.New("").Parse( - `Logged into TCP app {{.appName}}. Start the local TCP proxy for it: + `Logged into TCP app {{.appNameWithOptionalTargetPort}}. Start the local TCP proxy for it: tsh proxy app {{.appName}} @@ -319,7 +335,9 @@ func onAppLogout(cf *CLIConf) error { // remove generated local files for the provided app. err := utils.RemoveFileIfExist(profile.AppLocalCAPath(tc.SiteName, app.Name)) if err != nil { - log.WithError(err).Warnf("Failed to remove %v", profile.AppLocalCAPath(tc.SiteName, app.Name)) + logger.WarnContext(cf.Context, "Failed to clean up app session", + "error", err, + "profile", profile.AppLocalCAPath(tc.SiteName, app.Name)) } } @@ -577,7 +595,7 @@ func (a *appInfo) pickCloudAppLogin(cf *CLIConf, logins []string) error { if err != nil { return trace.Wrap(err) } - log.Debugf("Azure identity is %q", azureIdentity) + logger.DebugContext(cf.Context, "Retrieved azure identity", "azure_identity", azureIdentity) a.AzureIdentity = azureIdentity case a.app.IsGCP(): @@ -585,7 +603,7 @@ func (a *appInfo) pickCloudAppLogin(cf *CLIConf, logins []string) error { if err != nil { return trace.Wrap(err) } - log.Debugf("GCP service account is %q", gcpServiceAccount) + logger.DebugContext(cf.Context, "Retrieved GCP service account", "service_account", gcpServiceAccount) a.GCPServiceAccount = gcpServiceAccount } @@ -608,10 +626,6 @@ type appInfo struct { profile *client.ProfileStatus } -func (a *appInfo) appLocalCAPath(cluster string) string { - return a.profile.AppLocalCAPath(cluster, a.RouteToApp.Name) -} - // GetApp returns the cached app or fetches it using the app route and // caches the result. func (a *appInfo) GetApp(ctx context.Context, clt apiclient.GetResourcesClient) (types.Application, error) { @@ -649,7 +663,7 @@ func getApp(ctx context.Context, clt apiclient.GetResourcesClient, name string) appServer, ok := res.Resources[0].ResourceWithLabels.(types.AppServer) if !ok { - log.Warnf("expected types.AppServer but received unexpected type %T", res.Resources[0].ResourceWithLabels) + logger.WarnContext(ctx, "expected types.AppServer but received unexpected type", "resource_type", logutils.TypeAttr(res.Resources[0].ResourceWithLabels)) return nil, nil, trace.NotFound("app %q not found, use `tsh apps ls` to see registered apps", name) } diff --git a/tool/tsh/common/app_aws.go b/tool/tsh/common/app_aws.go index aaa6adeec6b01..74d575fb3ab0f 100644 --- a/tool/tsh/common/app_aws.go +++ b/tool/tsh/common/app_aws.go @@ -24,7 +24,6 @@ import ( "io" "os" "os/exec" - "strings" "sync" "github.com/aws/aws-sdk-go-v2/aws" @@ -40,6 +39,7 @@ import ( "github.com/gravitational/teleport/lib/srv/alpnproxy" "github.com/gravitational/teleport/lib/tlsca" awsutils "github.com/gravitational/teleport/lib/utils/aws" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const ( @@ -52,11 +52,6 @@ func onAWS(cf *CLIConf) error { return trace.Wrap(err) } - if shouldUseAWSEndpointURLMode(cf) { - log.Debugf("Forcing endpoint URL mode for AWS command %q.", cf.AWSCommandArgs) - cf.AWSEndpointURLMode = true - } - err = awsApp.StartLocalProxies(cf.Context) if err != nil { return trace.Wrap(err) @@ -64,7 +59,7 @@ func onAWS(cf *CLIConf) error { defer func() { if err := awsApp.Close(); err != nil { - log.WithError(err).Error("Failed to close AWS app.") + logger.ErrorContext(cf.Context, "Failed to close AWS app", "error", err) } }() @@ -82,55 +77,6 @@ func onAWS(cf *CLIConf) error { return awsApp.RunCommand(cmd) } -func shouldUseAWSEndpointURLMode(cf *CLIConf) bool { - inputAWSCommand := strings.Join(removeAWSCommandFlags(cf.AWSCommandArgs), " ") - switch inputAWSCommand { - // `aws ssm start-session` first calls ssm..amazonaws.com to get an - // stream URL and an token. Then it makes a wss connection with the - // provided token to the provided stream URL. The wss request currently - // respects HTTPS_PROXY but does not respect local CA bundle we provided - // thus causing a failure. Even if this is resolved one day, the wss send - // the token through websocket data channel for authentication, instead of - // sigv4, which likely we won't support. - // - // When using the endpoint URL mode, only the first request goes through - // Teleport Proxy. The wss connection does not respect the endpoint URL and - // goes to AWS directly (thus working fine). - // - // Reference: - // https://github.com/aws/session-manager-plugin/ - // - // "aws ecs execute-command" also start SSM sessions. - case "ssm start-session", "ecs execute-command": - return true - default: - return false - } -} - -func removeAWSCommandFlags(args []string) (ret []string) { - for i := 0; i < len(args); i++ { - switch { - case isAWSFlag(args, i): - // Skip next arg, if next arg is not a flag but a flag value. - if !isAWSFlag(args, i+1) { - i++ - } - continue - default: - ret = append(ret, args[i]) - } - } - return -} - -func isAWSFlag(args []string, i int) bool { - if i >= len(args) { - return false - } - return strings.HasPrefix(args[i], "--") -} - // awsApp is an AWS app that can start local proxies to serve AWS APIs. type awsApp struct { *localProxyApp @@ -143,15 +89,20 @@ type awsApp struct { // newAWSApp creates a new AWS app. func newAWSApp(tc *client.TeleportClient, cf *CLIConf, appInfo *appInfo) (*awsApp, error) { + localProxyApp, err := newLocalProxyApp(tc, appInfo.profile, appInfo.RouteToApp, cf.LocalProxyPort, cf.InsecureSkipVerify) + if err != nil { + return nil, trace.Wrap(err) + } + return &awsApp{ - localProxyApp: newLocalProxyApp(tc, appInfo, cf.LocalProxyPort, cf.InsecureSkipVerify), + localProxyApp: localProxyApp, cf: cf, }, nil } // GetAppName returns the app name. func (a *awsApp) GetAppName() string { - return a.appInfo.RouteToApp.Name + return a.routeToApp.Name } // StartLocalProxies sets up local proxies for serving AWS clients. @@ -223,7 +174,7 @@ func (a *awsApp) GetEnvVars() (map[string]string, error) { // https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html "AWS_ACCESS_KEY_ID": cred.AccessKeyID, "AWS_SECRET_ACCESS_KEY": cred.SecretAccessKey, - "AWS_CA_BUNDLE": a.appInfo.appLocalCAPath(a.cf.SiteName), + "AWS_CA_BUNDLE": a.profile.AppLocalCAPath(a.cf.SiteName, a.routeToApp.Name), } // Set proxy settings. @@ -257,7 +208,7 @@ func (a *awsApp) RunCommand(cmd *exec.Cmd) error { return trace.Wrap(err) } - log.Debugf("Running command: %q", cmd) + logger.DebugContext(a.cf.Context, "Running AWS command", "command", logutils.StringerAttr(cmd)) cmd.Stdout = a.cf.Stdout() cmd.Stderr = a.cf.Stderr() @@ -296,7 +247,7 @@ func getARNFromFlags(cf *CLIConf, app types.Application, logins []string) (strin if cf.AWSRole == "" { if len(roles) == 1 { - log.Infof("AWS Role %v is selected by default as it is the only role configured for this AWS app.", roles[0].Display) + logger.InfoContext(cf.Context, "AWS Role is selected by default as it is the only role configured for this AWS app", "role", roles[0].Display) return roles[0].ARN, nil } @@ -339,13 +290,13 @@ func getARNFromFlags(cf *CLIConf, app types.Application, logins []string) (strin func getARNFromRoles(cf *CLIConf, roleGetter services.CurrentUserRoleGetter, profile *client.ProfileStatus, siteName string, app types.Application) []string { accessChecker, err := services.NewAccessCheckerForRemoteCluster(cf.Context, profile.AccessInfo(), siteName, roleGetter) if err != nil { - log.WithError(err).Debugf("Failed to fetch user roles.") + logger.DebugContext(cf.Context, "Failed to fetch user roles", "error", err) return profile.AWSRolesARNs } logins, err := accessChecker.GetAllowedLoginsForResource(app) if err != nil { - log.WithError(err).Debugf("Failed to fetch app logins.") + logger.DebugContext(cf.Context, "Failed to fetch app logins", "error", err) return profile.AWSRolesARNs } diff --git a/tool/tsh/common/app_aws_test.go b/tool/tsh/common/app_aws_test.go index 15297426e7d39..98782f73dee15 100644 --- a/tool/tsh/common/app_aws_test.go +++ b/tool/tsh/common/app_aws_test.go @@ -149,39 +149,6 @@ func TestAWS(t *testing.T) { setCmdRunner(validateCmd), ) require.NoError(t, err) - - t.Run("aws ssm start-session", func(t *testing.T) { - // Validate --endpoint-url 127.0.0.1: is added to the command. - validateCmd := func(cmd *exec.Cmd) error { - require.Len(t, cmd.Args, 9) - require.Equal(t, []string{"aws", "ssm", "--region", "us-west-1", "start-session", "--target", "target-id", "--endpoint-url"}, cmd.Args[:8]) - require.Contains(t, cmd.Args[8], "127.0.0.1:") - return nil - } - err = Run( - context.Background(), - []string{"aws", "ssm", "--region", "us-west-1", "start-session", "--target", "target-id"}, - setHomePath(tmpHomePath), - setCmdRunner(validateCmd), - ) - require.NoError(t, err) - }) - t.Run("aws ecs execute-command", func(t *testing.T) { - // Validate --endpoint-url 127.0.0.1: is added to the command. - validateCmd := func(cmd *exec.Cmd) error { - require.Len(t, cmd.Args, 13) - require.Equal(t, []string{"aws", "ecs", "execute-command", "--debug", "--cluster", "cluster-name", "--task", "task-name", "--command", "/bin/bash", "--interactive", "--endpoint-url"}, cmd.Args[:12]) - require.Contains(t, cmd.Args[12], "127.0.0.1:") - return nil - } - err = Run( - context.Background(), - []string{"aws", "ecs", "execute-command", "--debug", "--cluster", "cluster-name", "--task", "task-name", "--command", "/bin/bash", "--interactive"}, - setHomePath(tmpHomePath), - setCmdRunner(validateCmd), - ) - require.NoError(t, err) - }) } // TestAWSConsoleLogins given a AWS console application, execute a app login diff --git a/tool/tsh/common/app_azure.go b/tool/tsh/common/app_azure.go index 6a31c014f802d..74a70e770115d 100644 --- a/tool/tsh/common/app_azure.go +++ b/tool/tsh/common/app_azure.go @@ -39,6 +39,7 @@ import ( "github.com/gravitational/teleport/lib/srv/alpnproxy" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const ( @@ -58,7 +59,7 @@ func onAzure(cf *CLIConf) error { defer func() { if err := app.Close(); err != nil { - log.WithError(err).Error("Failed to close Azure app.") + logger.ErrorContext(cf.Context, "Failed to close Azure app", "error", err) } }() @@ -82,9 +83,13 @@ func newAzureApp(tc *client.TeleportClient, cf *CLIConf, appInfo *appInfo) (*azu if err != nil { return nil, err } + localProxyApp, err := newLocalProxyApp(tc, appInfo.profile, appInfo.RouteToApp, cf.LocalProxyPort, cf.InsecureSkipVerify) + if err != nil { + return nil, trace.Wrap(err) + } return &azureApp{ - localProxyApp: newLocalProxyApp(tc, appInfo, cf.LocalProxyPort, cf.InsecureSkipVerify), + localProxyApp: localProxyApp, cf: cf, msiSecret: msiSecret, }, nil @@ -132,7 +137,7 @@ func (a *azureApp) StartLocalProxies(ctx context.Context) error { // but at this moment there is no clear advantage over simply issuing a new random identifier. TenantID: uuid.New().String(), ClientID: uuid.New().String(), - Identity: a.appInfo.RouteToApp.AzureIdentity, + Identity: a.routeToApp.AzureIdentity, } // HTTPS proxy mode @@ -145,7 +150,7 @@ func (a *azureApp) StartLocalProxies(ctx context.Context) error { if ok { azureMiddleware.SetPrivateKey(signer) } else { - log.Warn("Provided tls.Certificate has no valid private key.") + logger.WarnContext(ctx, "Provided tls.Certificate has no valid private key") } }), ) @@ -160,7 +165,7 @@ func (a *azureApp) GetEnvVars() (map[string]string, error) { // 1. `tsh az login` in one console // 2. `az ...` in another console // without custom config dir the second invocation will hang, attempting to connect to (inaccessible without configuration) MSI. - "AZURE_CONFIG_DIR": filepath.Join(profile.FullProfilePath(a.cf.HomePath), "azure", a.appInfo.RouteToApp.ClusterName, a.appInfo.RouteToApp.Name), + "AZURE_CONFIG_DIR": filepath.Join(profile.FullProfilePath(a.cf.HomePath), "azure", a.routeToApp.ClusterName, a.routeToApp.Name), // setting MSI_ENDPOINT instructs Azure CLI to make managed identity calls on this address. // the requests will be handled by tsh proxy. "MSI_ENDPOINT": "https://" + types.TeleportAzureMSIEndpoint + "/" + a.msiSecret, @@ -169,7 +174,7 @@ func (a *azureApp) GetEnvVars() (map[string]string, error) { // This isn't portable and applications other than az CLI may have to set different env variables, // add the application cert to system root store (not recommended, ultimate fallback) // or use equivalent of --insecure flag. - "REQUESTS_CA_BUNDLE": a.appInfo.appLocalCAPath(a.cf.SiteName), + "REQUESTS_CA_BUNDLE": a.profile.AppLocalCAPath(a.cf.SiteName, a.routeToApp.Name), } // Set proxy settings. @@ -186,7 +191,7 @@ func (a *azureApp) RunCommand(cmd *exec.Cmd) error { return trace.Wrap(err) } - log.Debugf("Running command: %q", cmd) + logger.DebugContext(a.cf.Context, "Running azure command", "command", logutils.StringerAttr(cmd)) cmd.Stdout = a.cf.Stdout() cmd.Stderr = a.cf.Stderr() @@ -232,7 +237,7 @@ func getAzureIdentityFromFlags(cf *CLIConf, profile *client.ProfileStatus) (stri // if flag is missing, try to find singleton identity; failing that, print available options. if reqIdentity == "" { if len(identities) == 1 { - log.Infof("Azure identity %v is selected by default as it is the only identity available for this Azure app.", identities[0]) + logger.InfoContext(cf.Context, "Azure identity is selected by default as it is the only identity available for this Azure app", "identity", identities[0]) return identities[0], nil } diff --git a/tool/tsh/common/app_gcp.go b/tool/tsh/common/app_gcp.go index 237eb207446c5..3e3cea00575b3 100644 --- a/tool/tsh/common/app_gcp.go +++ b/tool/tsh/common/app_gcp.go @@ -39,6 +39,7 @@ import ( "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/gcp" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const ( @@ -59,7 +60,7 @@ func onGcloud(cf *CLIConf) error { defer func() { if err := app.Close(); err != nil { - log.WithError(err).Error("Failed to close GCP app.") + logger.ErrorContext(cf.Context, "Failed to close GCP app", "error", err) } }() @@ -82,7 +83,7 @@ func onGsutil(cf *CLIConf) error { defer func() { if err := app.Close(); err != nil { - log.WithError(err).Error("Failed to close GCP app.") + logger.ErrorContext(cf.Context, "Failed to close GCP app", "error", err) } }() @@ -114,9 +115,13 @@ func newGCPApp(tc *client.TeleportClient, cf *CLIConf, appInfo *appInfo) (*gcpAp h := fnv.New32a() _, _ = h.Write([]byte(secret)) prefix := fmt.Sprintf("%x", h.Sum32()) + localProxyApp, err := newLocalProxyApp(tc, appInfo.profile, appInfo.RouteToApp, cf.LocalProxyPort, cf.InsecureSkipVerify) + if err != nil { + return nil, trace.Wrap(err) + } return &gcpApp{ - localProxyApp: newLocalProxyApp(tc, appInfo, cf.LocalProxyPort, cf.InsecureSkipVerify), + localProxyApp: localProxyApp, cf: cf, secret: secret, prefix: prefix, @@ -162,7 +167,7 @@ func (a *gcpApp) Close() error { } func (a *gcpApp) getGcloudConfigPath() string { - return filepath.Join(profile.FullProfilePath(a.cf.HomePath), "gcp", a.appInfo.RouteToApp.ClusterName, a.appInfo.RouteToApp.Name, "gcloud") + return filepath.Join(profile.FullProfilePath(a.cf.HomePath), "gcp", a.routeToApp.ClusterName, a.routeToApp.Name, "gcloud") } // removeBotoConfig removes config files written by WriteBotoConfig. @@ -175,7 +180,7 @@ func (a *gcpApp) removeBotoConfig() []error { } func (a *gcpApp) getBotoConfigDir() string { - return filepath.Join(profile.FullProfilePath(a.cf.HomePath), "gcp", a.appInfo.RouteToApp.ClusterName, a.appInfo.RouteToApp.Name) + return filepath.Join(profile.FullProfilePath(a.cf.HomePath), "gcp", a.routeToApp.ClusterName, a.routeToApp.Name) } func (a *gcpApp) getBotoConfigPath() string { @@ -224,7 +229,7 @@ func (a *gcpApp) writeBotoConfig() error { // GetEnvVars returns required environment variables to configure the // clients. func (a *gcpApp) GetEnvVars() (map[string]string, error) { - projectID, err := gcp.ProjectIDFromServiceAccountName(a.appInfo.RouteToApp.GCPServiceAccount) + projectID, err := gcp.ProjectIDFromServiceAccountName(a.routeToApp.GCPServiceAccount) if err != nil { return nil, trace.Wrap(err) } @@ -236,7 +241,7 @@ func (a *gcpApp) GetEnvVars() (map[string]string, error) { // Set core.custom_ca_certs_file via env variable, customizing the path to CA certs file. // https://cloud.google.com/sdk/gcloud/reference/config/set#:~:text=custom_ca_certs_file - "CLOUDSDK_CORE_CUSTOM_CA_CERTS_FILE": a.appInfo.appLocalCAPath(a.cf.SiteName), + "CLOUDSDK_CORE_CUSTOM_CA_CERTS_FILE": a.profile.AppLocalCAPath(a.cf.SiteName, a.routeToApp.Name), // We need to set project ID. This is sourced from the account name. // https://cloud.google.com/sdk/gcloud/reference/config#GROUP:~:text=authentication%20to%20gsutil.-,project,-Project%20ID%20of @@ -265,7 +270,7 @@ func (a *gcpApp) RunCommand(cmd *exec.Cmd) error { return trace.Wrap(err) } - log.Debugf("Running command: %q", cmd) + logger.DebugContext(a.cf.Context, "Running gcp command", "command", logutils.StringerAttr(cmd)) cmd.Stdout = a.cf.Stdout() cmd.Stderr = a.cf.Stderr() @@ -322,7 +327,7 @@ func getGCPServiceAccountFromFlags(cf *CLIConf, profile *client.ProfileStatus) ( // if flag is missing, try to find singleton service account; failing that, print available options. if reqAccount == "" { if len(accounts) == 1 { - log.Infof("GCP service account %v is selected by default as it is the only one available for this GCP app.", accounts[0]) + logger.InfoContext(cf.Context, "GCP service account is selected by default as it is the only one available for this GCP app", "service_account", accounts[0]) return validate(accounts[0]) } diff --git a/tool/tsh/common/app_local_proxy.go b/tool/tsh/common/app_local_proxy.go index f856cf3d66b51..34dd16ec2ea97 100644 --- a/tool/tsh/common/app_local_proxy.go +++ b/tool/tsh/common/app_local_proxy.go @@ -19,25 +19,32 @@ package common import ( - "cmp" "context" "crypto/tls" "fmt" "net" "net/http" + "strconv" "github.com/gravitational/trace" + "github.com/gravitational/teleport/api/client/proto" + "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/srv/alpnproxy" ) // localProxyApp is a generic app that can start local proxies. type localProxyApp struct { - tc *client.TeleportClient - appInfo *appInfo - insecure bool - port string + tc *client.TeleportClient + // profile is a cached profile status for the current login session. + profile *client.ProfileStatus + // routeToApp is a route to the app without TargetPort being set. + routeToApp proto.RouteToApp + // app is available only when starting a localProxyApp through newLocalProxyAppWithPortMapping. + app types.Application + insecure bool + portMapping client.PortMapping localALPNProxy *alpnproxy.LocalProxy localForwardProxy *alpnproxy.ForwardProxy @@ -45,19 +52,65 @@ type localProxyApp struct { type requestMatcher func(req *http.Request) bool -// newLocalProxyApp creates a new generic app. -func newLocalProxyApp(tc *client.TeleportClient, appInfo *appInfo, port string, insecure bool) *localProxyApp { +// newLocalProxyApp creates a new generic app proxy. +func newLocalProxyApp(tc *client.TeleportClient, profile *client.ProfileStatus, routeToApp proto.RouteToApp, rawLocalPort string, insecure bool) (*localProxyApp, error) { + var portMapping client.PortMapping + if rawLocalPort != "" { + localPort, err := strconv.Atoi(rawLocalPort) + if err != nil { + return nil, trace.Wrap(err, "parsing port") + } + portMapping.LocalPort = localPort + } + return &localProxyApp{ - tc: tc, - appInfo: appInfo, - port: port, - insecure: insecure, + tc: tc, + profile: profile, + routeToApp: routeToApp, + portMapping: portMapping, + insecure: insecure, + }, nil +} + +// newLocalProxyAppWithPortMapping creates a new generic app proxy. Unlike newLocalProxyApp, it +// accepts a specific port mapping as an argument. +func newLocalProxyAppWithPortMapping(ctx context.Context, tc *client.TeleportClient, profile *client.ProfileStatus, routeToApp proto.RouteToApp, app types.Application, portMapping client.PortMapping, insecure bool) (*localProxyApp, error) { + if err := validateTargetPort(app, portMapping.TargetPort); err != nil { + return nil, trace.Wrap(err) + } + return &localProxyApp{ + tc: tc, + profile: profile, + routeToApp: routeToApp, + app: app, + portMapping: portMapping, + insecure: insecure, + }, nil +} + +// validateTargetPort is used in both tsh proxy app and tsh app login. +func validateTargetPort(app types.Application, targetPort int) error { + if targetPort == 0 { + return nil + } + + tcpPorts := app.GetTCPPorts() + if len(tcpPorts) == 0 { + return trace.BadParameter("cannot specify target port %d because app %q does not provide access to multiple ports", + targetPort, app.GetName()) + } + + if !tcpPorts.Contains(targetPort) { + return trace.BadParameter("port %d is not included in target ports of app %q; valid ports: %s", + targetPort, app.GetName(), tcpPorts) } + + return nil } // StartLocalProxy sets up local proxies for serving app clients. func (a *localProxyApp) StartLocalProxy(ctx context.Context, opts ...alpnproxy.LocalProxyConfigOpt) error { - if err := a.startLocalALPNProxy(ctx, a.port, false /*withTLS*/, opts...); err != nil { + if err := a.startLocalALPNProxy(ctx, a.portMapping, false /*withTLS*/, opts...); err != nil { return trace.Wrap(err) } return nil @@ -65,7 +118,7 @@ func (a *localProxyApp) StartLocalProxy(ctx context.Context, opts ...alpnproxy.L // StartLocalProxy sets up local proxies for serving app clients. func (a *localProxyApp) StartLocalProxyWithTLS(ctx context.Context, opts ...alpnproxy.LocalProxyConfigOpt) error { - if err := a.startLocalALPNProxy(ctx, a.port, true /*withTLS*/, opts...); err != nil { + if err := a.startLocalALPNProxy(ctx, a.portMapping, true /*withTLS*/, opts...); err != nil { return trace.Wrap(err) } return nil @@ -73,11 +126,11 @@ func (a *localProxyApp) StartLocalProxyWithTLS(ctx context.Context, opts ...alpn // StartLocalProxy sets up local proxies for serving app clients. func (a *localProxyApp) StartLocalProxyWithForwarder(ctx context.Context, forwardMatcher requestMatcher, opts ...alpnproxy.LocalProxyConfigOpt) error { - if err := a.startLocalALPNProxy(ctx, "", true /*withTLS*/, opts...); err != nil { + if err := a.startLocalALPNProxy(ctx, client.PortMapping{}, true /*withTLS*/, opts...); err != nil { return trace.Wrap(err) } - if err := a.startLocalForwardProxy(ctx, a.port, forwardMatcher); err != nil { + if err := a.startLocalForwardProxy(ctx, a.portMapping.LocalPort, forwardMatcher); err != nil { return trace.Wrap(err) } return nil @@ -96,22 +149,34 @@ func (a *localProxyApp) Close() error { } // startLocalALPNProxy starts the local ALPN proxy. -func (a *localProxyApp) startLocalALPNProxy(ctx context.Context, port string, withTLS bool, opts ...alpnproxy.LocalProxyConfigOpt) error { +func (a *localProxyApp) startLocalALPNProxy(ctx context.Context, portMapping client.PortMapping, withTLS bool, opts ...alpnproxy.LocalProxyConfigOpt) error { + routeToAppWithTargetPort := a.routeToApp + if portMapping.TargetPort != 0 { + routeToAppWithTargetPort.TargetPort = uint32(portMapping.TargetPort) + } // Create an app cert checker to check and reissue app certs for the local app proxy. - appCertChecker := client.NewAppCertChecker(a.tc, a.appInfo.RouteToApp, nil, client.WithTTL(a.tc.KeyTTL)) + appCertChecker := client.NewAppCertChecker(a.tc, routeToAppWithTargetPort, nil, client.WithTTL(a.tc.KeyTTL)) // If a stored cert is found for the app, try using it. // Otherwise, let the checker reissue one as needed. - cert, err := loadAppCertificate(a.tc, a.appInfo.RouteToApp.Name) + cert, err := loadAppCertificate(a.tc, routeToAppWithTargetPort.Name) if err == nil { - appCertChecker.SetCert(cert) + if a.app != nil && len(a.app.GetTCPPorts()) > 0 { + // There are too many cases to cover when dealing with a multi-port app and an existing cert. + // We'd need to consider portMapping passed from argv and TargetPort in the cert. + // As tsh proxy app is not the recommended way to access multi-port apps, let's bail out of + // using the existing cert and instead always generate a new one. + fmt.Println("Warning: Ignoring existing app cert and generating a new one. Connections made through this proxy will be routed according to command arguments.") + } else { + appCertChecker.SetCert(cert) + } } - listenAddr := fmt.Sprintf("localhost:%s", cmp.Or(port, "0")) + listenAddr := fmt.Sprintf("localhost:%d", portMapping.LocalPort) var listener net.Listener if withTLS { - appLocalCAPath := a.appInfo.appLocalCAPath(a.tc.SiteName) + appLocalCAPath := a.profile.AppLocalCAPath(a.tc.SiteName, routeToAppWithTargetPort.Name) localCertGenerator, err := client.NewLocalCertGenerator(ctx, appCertChecker, appLocalCAPath) if err != nil { return trace.Wrap(err) @@ -144,7 +209,7 @@ func (a *localProxyApp) startLocalALPNProxy(ctx context.Context, port string, wi go func() { if err = a.localALPNProxy.Start(ctx); err != nil { - log.WithError(err).Errorf("Failed to start local ALPN proxy.") + logger.ErrorContext(ctx, "Failed to start local ALPN proxy", "error", err) } }() return nil @@ -152,8 +217,8 @@ func (a *localProxyApp) startLocalALPNProxy(ctx context.Context, port string, wi // startLocalForwardProxy starts a local forward proxy that forwards matching requests // to the local ALPN proxy and unmatched requests to their original hosts. -func (a *localProxyApp) startLocalForwardProxy(ctx context.Context, port string, forwardMatcher requestMatcher) error { - listenAddr := fmt.Sprintf("localhost:%s", cmp.Or(port, "0")) +func (a *localProxyApp) startLocalForwardProxy(ctx context.Context, port int, forwardMatcher requestMatcher) error { + listenAddr := fmt.Sprintf("localhost:%d", port) listener, err := net.Listen("tcp", listenAddr) if err != nil { return trace.Wrap(err) @@ -187,7 +252,7 @@ func (a *localProxyApp) startLocalForwardProxy(ctx context.Context, port string, go func() { if err := a.localForwardProxy.Start(); err != nil { - log.WithError(err).Errorf("Failed to start local forward proxy.") + logger.ErrorContext(ctx, "Failed to start local forward proxy", "error", err) } }() return nil diff --git a/tool/tsh/common/db.go b/tool/tsh/common/db.go index baf222ab69f90..bc888ba9c98c0 100644 --- a/tool/tsh/common/db.go +++ b/tool/tsh/common/db.go @@ -55,6 +55,7 @@ import ( "github.com/gravitational/teleport/lib/srv/db/common/role" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" + logutils "github.com/gravitational/teleport/lib/utils/log" ) // onListDatabases implements "tsh db ls" command. @@ -90,7 +91,7 @@ func onListDatabases(cf *CLIConf) error { accessChecker, err := services.NewAccessCheckerForRemoteCluster(cf.Context, profile.AccessInfo(), tc.SiteName, clusterClient.AuthClient) if err != nil { - log.Debugf("Failed to fetch user roles: %v.", err) + logger.DebugContext(cf.Context, "Failed to fetch user roles", "error", err) } activeDatabases, err := profile.DatabasesForCluster(tc.SiteName) @@ -170,10 +171,10 @@ func listDatabasesAllClusters(cf *CLIConf) error { oteltrace.WithAttributes(attribute.String("cluster", cluster.name))) defer span.End() - logger := log.WithField("cluster", cluster.name) + logger := logger.With("cluster", cluster.name) databases, err := apiclient.GetAllResources[types.DatabaseServer](ctx, cluster.auth, &cluster.req) if err != nil { - logger.Errorf("Failed to get databases: %v.", err) + logger.ErrorContext(ctx, "Failed to get databases", "error", err) mu.Lock() errors = append(errors, trace.ConnectionProblem(err, "failed to list databases for cluster %s: %v", cluster.name, err)) @@ -183,7 +184,7 @@ func listDatabasesAllClusters(cf *CLIConf) error { accessChecker, err := services.NewAccessCheckerForRemoteCluster(ctx, cluster.profile.AccessInfo(), cluster.name, cluster.auth) if err != nil { - log.Debugf("Failed to fetch user roles: %v.", err) + logger.DebugContext(ctx, "Failed to fetch user roles", "error", err) } localDBListings := make(databaseListings, 0, len(databases)) @@ -296,7 +297,10 @@ func protocolSupportsInteractiveMode(dbProtocol string) bool { } func databaseLogin(cf *CLIConf, tc *client.TeleportClient, dbInfo *databaseInfo) error { - log.Debugf("Fetching database access certificate for %s on cluster %v.", dbInfo.RouteToDatabase, tc.SiteName) + logger.DebugContext(cf.Context, "Fetching database access certificate", + "database", dbInfo.RouteToDatabase, + "cluster", tc.SiteName, + ) profile, err := tc.ProfileStatus() if err != nil { @@ -307,7 +311,7 @@ func databaseLogin(cf *CLIConf, tc *client.TeleportClient, dbInfo *databaseInfo) // Identity files themselves act as the database credentials (if any), so // don't bother fetching new certs. if profile.IsVirtual { - log.Info("Note: already logged in due to an identity file (`-i ...`); will only update database config files.") + logger.InfoContext(cf.Context, "Note: already logged in due to an identity file (`-i ...`); will only update database config files") } else { if err = client.RetryWithRelogin(cf.Context, tc, func() error { keyRing, err = tc.IssueUserCertsWithMFA(cf.Context, client.ReissueParams{ @@ -378,7 +382,7 @@ func onDatabaseLogout(cf *CLIConf) error { } if profile.IsVirtual { - log.Info("Note: an identity file is in use (`-i ...`); will only update database config files.") + logger.InfoContext(cf.Context, "Note: an identity file is in use (`-i ...`); will only update database config files.") } for _, db := range databases { @@ -611,9 +615,9 @@ func maybeStartLocalProxy(ctx context.Context, cf *CLIConf, return nil, nil } if requires.tunnel { - log.Debugf("Starting local proxy tunnel because: %v", strings.Join(requires.tunnelReasons, ", ")) + logger.DebugContext(ctx, "Starting local proxy tunnel", "reasons", requires.tunnelReasons) } else { - log.Debugf("Starting local proxy because: %v", strings.Join(requires.localProxyReasons, ", ")) + logger.DebugContext(ctx, "Starting local proxy", "reasons", requires.localProxyReasons) } listener, err := createLocalProxyListener("localhost:0", dbInfo.RouteToDatabase, profile) @@ -640,7 +644,7 @@ func maybeStartLocalProxy(ctx context.Context, cf *CLIConf, go func() { defer listener.Close() if err := lp.Start(ctx); err != nil { - log.WithError(err).Errorf("Failed to start local proxy") + logger.ErrorContext(cf.Context, "Failed to start local proxy", "error", err) } }() @@ -677,6 +681,11 @@ type localProxyConfig struct { } func createLocalProxyListener(addr string, route tlsca.RouteToDatabase, profile *client.ProfileStatus) (net.Listener, error) { + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, trace.Wrap(err) + } + if route.Protocol == defaults.ProtocolOracle { localCert, err := tls.LoadX509KeyPair( profile.DatabaseLocalCAPath(), @@ -685,14 +694,15 @@ func createLocalProxyListener(addr string, route tlsca.RouteToDatabase, profile if err != nil { return nil, trace.Wrap(err) } - l, err := tls.Listen("tcp", addr, &tls.Config{ + config := &tls.Config{ Certificates: []tls.Certificate{localCert}, ServerName: "localhost", - }) - return l, trace.Wrap(err) + } + + l = NewTLSMuxListener(l, config) } - l, err := net.Listen("tcp", addr) - return l, trace.Wrap(err) + + return l, nil } // prepareLocalProxyOptions created localProxyOpts needed to create local proxy from localProxyConfig. @@ -789,13 +799,14 @@ func onDatabaseConnect(cf *CLIConf) error { if opts, err = maybeAddGCPMetadata(cf.Context, tc, dbInfo, opts); err != nil { return trace.Wrap(err) } + opts = maybeAddOracleOptions(cf.Context, tc, dbInfo, opts) bb := dbcmd.NewCmdBuilder(tc, profile, dbInfo.RouteToDatabase, rootClusterName, opts...) cmd, err := bb.GetConnectCommand(cf.Context) if err != nil { return trace.Wrap(err) } - log.Debug(cmd.String()) + logger.DebugContext(ctx, "executing command", "command", logutils.StringerAttr(cmd)) cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin @@ -994,7 +1005,7 @@ func (d *databaseInfo) checkAndSetDefaults(cf *CLIConf, tc *client.TeleportClien if err != nil { return trace.Wrap(err) } - log.Debugf("Defaulting to the allowed database user %q\n", dbUser) + logger.DebugContext(cf.Context, "Defaulting to the allowed database user", "database_user", dbUser) d.Username = dbUser } if needDBName { @@ -1002,7 +1013,7 @@ func (d *databaseInfo) checkAndSetDefaults(cf *CLIConf, tc *client.TeleportClien if err != nil { return trace.Wrap(err) } - log.Debugf("Defaulting to the allowed database name %q\n", dbName) + logger.DebugContext(cf.Context, "Defaulting to the allowed database name", "database_name", dbName) d.Database = dbName } return nil @@ -1055,7 +1066,7 @@ func chooseOneDatabase(cf *CLIConf, databases types.Databases) (types.Database, // that database over any others. for _, db := range databases { if db.GetName() == selectors.name { - log.Debugf("Selected database %q by exact name match", db.GetName()) + logger.DebugContext(cf.Context, "Selected database by exact name match", "database", db.GetName()) return db, nil } } @@ -1065,11 +1076,11 @@ func chooseOneDatabase(cf *CLIConf, databases types.Databases) (types.Database, for _, db := range dbs { names = append(names, db.GetName()) } - log.Debugf("Choosing amongst databases (%v) by discovered name", names) + logger.DebugContext(cf.Context, "Choosing amongst databases by discovered name", "databases", names) databases = dbs } if len(databases) == 1 { - log.Debugf("Selected database %q", databases[0].GetName()) + logger.DebugContext(cf.Context, "Selected database", "database", databases[0].GetName()) return databases[0], nil } @@ -1109,6 +1120,29 @@ func getDatabase(ctx context.Context, tc *client.TeleportClient, name string) (t return databases[0], nil } +func getDatabaseServers(ctx context.Context, tc *client.TeleportClient, name string) ([]types.DatabaseServer, error) { + var databases []types.DatabaseServer + + err := client.RetryWithRelogin(ctx, tc, func() error { + matchName := makeNamePredicate(name) + + var err error + predicate := makePredicateConjunction(matchName, tc.PredicateExpression) + logger.DebugContext(ctx, "Listing databases with predicate and labels", "predicate", predicate, "labels", tc.Labels) + + databases, err = tc.ListDatabaseServersWithFilters(ctx, &proto.ListResourcesRequest{ + Namespace: tc.Namespace, + ResourceType: types.KindDatabaseServer, + PredicateExpression: predicate, + Labels: tc.Labels, + UseSearchAsRoles: tc.UseSearchAsRoles, + }) + return trace.Wrap(err) + }) + + return databases, trace.Wrap(err) +} + // getDatabaseByNameOrDiscoveredName fetches a database that unambiguously // matches a given name or a discovered name label. func getDatabaseByNameOrDiscoveredName(cf *CLIConf, tc *client.TeleportClient, activeRoutes []tlsca.RouteToDatabase) (types.Database, error) { @@ -1122,7 +1156,7 @@ func getDatabaseByNameOrDiscoveredName(cf *CLIConf, tc *client.TeleportClient, a for _, db := range activeDBs { names = append(names, db.GetName()) } - log.Debugf("Choosing a database amongst active databases (%v)", names) + logger.DebugContext(cf.Context, "Choosing a database amongst active databases", "databases", names) // preferentially choose from active databases if any of them match. return chooseOneDatabase(cf, activeDBs) } @@ -1157,7 +1191,7 @@ func listDatabasesWithPredicate(ctx context.Context, tc *client.TeleportClient, err := client.RetryWithRelogin(ctx, tc, func() error { var err error predicate := makePredicateConjunction(predicate, tc.PredicateExpression) - log.Debugf("Listing databases with predicate (%v) and labels %v", predicate, tc.Labels) + logger.DebugContext(ctx, "Listing databases with predicate and labels", "predicate", predicate, "labels", tc.Labels) databases, err = tc.ListDatabases(ctx, &proto.ListResourcesRequest{ Namespace: tc.Namespace, ResourceType: types.KindDatabaseServer, @@ -1391,16 +1425,25 @@ func dbInfoHasChanged(cf *CLIConf, certPath string) (bool, error) { } if cf.DatabaseUser != "" && cf.DatabaseUser != identity.RouteToDatabase.Username { - log.Debugf("Will reissue database certificate for user %s (was %s)", cf.DatabaseUser, identity.RouteToDatabase.Username) + logger.DebugContext(cf.Context, "Will reissue database certificate for user", + "current_user", cf.DatabaseUser, + "previous_user", identity.RouteToDatabase.Username, + ) return true, nil } if cf.DatabaseName != "" && cf.DatabaseName != identity.RouteToDatabase.Database { - log.Debugf("Will reissue database certificate for database name %s (was %s)", cf.DatabaseName, identity.RouteToDatabase.Database) + logger.DebugContext(cf.Context, "Will reissue database certificate for database name", + "current_database", cf.DatabaseName, + "previous_database", identity.RouteToDatabase.Database, + ) return true, nil } if !apiutils.ContainSameUniqueElements(dbRoles, identity.RouteToDatabase.Roles) { - log.Debugf("Will reissue database certificate for database roles %v (was %v)", dbRoles, identity.RouteToDatabase.Roles) + logger.DebugContext(cf.Context, "Will reissue database certificate for database roles", + "current_roles", dbRoles, + "previous_roles", identity.RouteToDatabase.Roles, + ) return true, nil } return false, nil @@ -1472,14 +1515,14 @@ func maybePickActiveDatabase(cf *CLIConf, activeRoutes []tlsca.RouteToDatabase) case 0: return nil, trace.NotFound(formatDBNotLoggedIn(cf.SiteName, selectors)) case 1: - log.Debugf("Auto-selecting the only active database %q", activeRoutes[0].ServiceName) + logger.DebugContext(cf.Context, "Auto-selecting the only active database", "database", activeRoutes[0].ServiceName) return &activeRoutes[0], nil default: return nil, trace.BadParameter(formatChooseActiveDB(activeRoutes)) } } if route, ok := findActiveDatabase(selectors.name, activeRoutes); ok { - log.Debugf("Selected active database %q by name", route.ServiceName) + logger.DebugContext(cf.Context, "Selected active database by name", "database", route.ServiceName) return &route, nil } } @@ -1703,8 +1746,10 @@ func getDBConnectLocalProxyRequirement(ctx context.Context, tc *client.TeleportC // Call API and check if a user needs to use MFA to connect to the database. mfaRequired, err := isMFADatabaseAccessRequired(ctx, tc, route) if err != nil { - log.WithError(err).Debugf("error getting MFA requirement for database %v", - route.ServiceName) + logger.DebugContext(ctx, "error getting MFA requirement for database", + "database", route.ServiceName, + "error", err, + ) } else if mfaRequired { // When MFA is required, we should require a local proxy tunnel, // because the local proxy tunnel can hold database MFA certs in-memory diff --git a/tool/tsh/common/db_print.go b/tool/tsh/common/db_print.go index 68bd9012688a1..a840f92bdcdba 100644 --- a/tool/tsh/common/db_print.go +++ b/tool/tsh/common/db_print.go @@ -19,6 +19,7 @@ package common import ( + "context" "fmt" "io" "reflect" @@ -126,7 +127,10 @@ func formatDatabaseRolesForDB(database types.Database, accessChecker services.Ac autoUser, err := accessChecker.DatabaseAutoUserMode(database) if err != nil { - log.Warnf("Failed to get DatabaseAutoUserMode for database %v: %v.", database.GetName(), err) + logger.WarnContext(context.Background(), "Failed to get DatabaseAutoUserMode for database", + "database", database.GetName(), + "error", err, + ) return "" } else if !autoUser.IsEnabled() { return "" @@ -134,7 +138,10 @@ func formatDatabaseRolesForDB(database types.Database, accessChecker services.Ac roles, err := accessChecker.CheckDatabaseRoles(database, nil) if err != nil { - log.Warnf("Failed to CheckDatabaseRoles for database %v: %v.", database.GetName(), err) + logger.WarnContext(context.Background(), "Failed to CheckDatabaseRoles for database", + "database", database.GetName(), + "error", err, + ) return "" } return fmt.Sprintf("%v", roles) diff --git a/tool/tsh/common/db_test.go b/tool/tsh/common/db_test.go index f17db39ed6143..06787491de421 100644 --- a/tool/tsh/common/db_test.go +++ b/tool/tsh/common/db_test.go @@ -231,7 +231,7 @@ func testDatabaseLogin(t *testing.T) { s.user = alice // Log into Teleport cluster. - tmpHomePath, _ := mustLogin(t, s) + tmpHomePath, _ := mustLoginLegacy(t, s) testCases := []struct { // the test name @@ -717,7 +717,7 @@ func testListDatabase(t *testing.T) { }), ) - tshHome, _ := mustLogin(t, s) + tshHome, _ := mustLoginLegacy(t, s) captureStdout := new(bytes.Buffer) err := Run(context.Background(), []string{ @@ -1589,7 +1589,7 @@ func testDatabaseSelection(t *testing.T) { s.user = alice // Log into Teleport cluster. - tmpHomePath, _ := mustLogin(t, s) + tmpHomePath, _ := mustLoginLegacy(t, s) t.Run("GetDatabasesForLogout", func(t *testing.T) { t.Parallel() diff --git a/tool/tsh/common/device.go b/tool/tsh/common/device.go index ac9ec7eea4f12..28ee9907bb1e6 100644 --- a/tool/tsh/common/device.go +++ b/tool/tsh/common/device.go @@ -27,6 +27,7 @@ import ( "github.com/gravitational/trace" "google.golang.org/protobuf/encoding/protojson" + "github.com/gravitational/teleport" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/devicetrust" @@ -247,7 +248,7 @@ func (c *deviceActivateCredentialCommand) run(cf *CLIConf) error { // On error, wait for user input before executing. This is because this // opens in a second window. If we return the error immediately, then // this window closes before the user can inspect it. - log.WithError(err).Error("An error occurred during credential activation. Press enter to close this window.") + logger.ErrorContext(cf.Context, "An error occurred during credential activation, press enter to close this window", "error", err) _, _ = fmt.Scanln() } return trace.Wrap(err) @@ -260,7 +261,10 @@ type deviceDMIReadCommand struct { func (c *deviceDMIReadCommand) run(cf *CLIConf) error { dmiInfo, err := linux.DMIInfoFromSysfs() if err != nil { - log.WithError(err).Warn("Device Trust: Failed to read DMI information") + logger.WarnContext(cf.Context, "Failed to read DMI information", + teleport.ComponentKey, "DeviceTrust", + "error", err, + ) // err swallowed on purpose. } if dmiInfo != nil { diff --git a/tool/tsh/common/kube.go b/tool/tsh/common/kube.go index 9ba2606260cfe..f14ef5b420b23 100644 --- a/tool/tsh/common/kube.go +++ b/tool/tsh/common/kube.go @@ -37,7 +37,6 @@ import ( "github.com/ghodss/yaml" "github.com/gravitational/trace" dockerterm "github.com/moby/term" - "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -163,7 +162,7 @@ func (c *kubeJoinCommand) run(cf *CLIConf) error { return trace.Wrap(err) } if crt != nil && time.Until(crt.NotAfter) > time.Minute { - log.Debugf("Re-using existing TLS cert for kubernetes cluster %q", kubeCluster) + logger.DebugContext(cf.Context, "Re-using existing TLS cert for kubernetes cluster", "cluster", kubeCluster) } else { err = client.RetryWithRelogin(cf.Context, tc, func() error { var err error @@ -585,7 +584,7 @@ func takeKubeCredLock(ctx context.Context, homePath, proxy string, lockTimeout t // If kube credentials lockfile already exists, it means last time kube credentials was called // we had an error while trying to issue certificate, return an error asking user to login manually. if _, err := os.Stat(kubeCredLockfilePath); err == nil { - log.Debugf("Kube credentials lockfile was found at %q, aborting.", kubeCredLockfilePath) + logger.DebugContext(ctx, "Kube credentials lock file was found, aborting", "lock_file", kubeCredLockfilePath) return nil, trace.Wrap(errKubeCredLockfileFound) } @@ -595,7 +594,7 @@ func takeKubeCredLock(ctx context.Context, homePath, proxy string, lockTimeout t // Take a lock while we're trying to issue certificate and possibly relogin unlock, err := utils.FSTryWriteLockTimeout(ctx, kubeCredLockfilePath, lockTimeout) if err != nil { - log.Debugf("could not take kube credentials lock: %v", err.Error()) + logger.DebugContext(ctx, "could not take kube credentials lock", "error", err) return nil, trace.Wrap(errKubeCredLockfileFound) } @@ -603,14 +602,17 @@ func takeKubeCredLock(ctx context.Context, homePath, proxy string, lockTimeout t // We must unlock the lockfile before removing it, otherwise unlock operation will fail // on Windows. if err := unlock(); err != nil { - log.WithError(err).Warnf("could not unlock kube credentials lock") + logger.WarnContext(ctx, "could not unlock kube credentials lock", "error", err) } if !removeFile { return } // Remove kube credentials lockfile. if err = os.Remove(kubeCredLockfilePath); err != nil && !os.IsNotExist(err) { - log.WithError(err).Warnf("could not remove kube credentials lockfile %q", kubeCredLockfilePath) + logger.WarnContext(ctx, "could not remove kube credentials lock file", + "lock_file", kubeCredLockfilePath, + "error", err, + ) } }, nil } @@ -646,7 +648,7 @@ func (c *kubeCredentialsCommand) run(cf *CLIConf) error { ); err == nil { crt, _ := tlsca.ParseCertificatePEM(certPEM) if crt != nil && time.Until(crt.NotAfter) > time.Minute { - log.Debugf("Re-using existing TLS cert for Kubernetes cluster %q", c.kubeCluster) + logger.DebugContext(cf.Context, "Re-using existing TLS cert for Kubernetes cluster", "cluster", c.kubeCluster) return c.writeByteResponse(cf.Stdout(), certPEM, keyPEM, crt.NotAfter) } } @@ -681,7 +683,7 @@ func (c *kubeCredentialsCommand) issueCert(cf *CLIConf) error { return trace.Wrap(err) } if crt != nil && time.Until(crt.NotAfter) > time.Minute { - log.Debugf("Re-using existing TLS cert for Kubernetes cluster %q", c.kubeCluster) + logger.DebugContext(cf.Context, "Re-using existing TLS cert for Kubernetes cluster", "cluster", c.kubeCluster) return c.writeKeyResponse(cf.Stdout(), k, c.kubeCluster) } @@ -689,7 +691,7 @@ func (c *kubeCredentialsCommand) issueCert(cf *CLIConf) error { // a new one. } - log.Debugf("Requesting TLS cert for Kubernetes cluster %q", c.kubeCluster) + logger.DebugContext(cf.Context, "Requesting TLS cert for Kubernetes cluster", "cluster", c.kubeCluster) var unlockKubeCred func(bool) deleteKubeCredsLock := false defer func() { @@ -1050,7 +1052,7 @@ func (c *kubeLSCommand) runAllClusters(cf *CLIConf) error { group.Go(func() error { kc, err := kubeutils.ListKubeClustersWithFilters(groupCtx, cluster.auth, cluster.req) if err != nil { - logrus.Errorf("Failed to get kube clusters: %v.", err) + logger.ErrorContext(groupCtx, "Failed to get kube clusters", "error", err) mu.Lock() errors = append(errors, trace.ConnectionProblem(err, "failed to list kube clusters for cluster %s: %v", cluster.name, err)) mu.Unlock() @@ -1498,7 +1500,7 @@ func buildKubeConfigUpdate(cf *CLIConf, kubeStatus *kubernetesStatus, overrideCo if len(kubeStatus.kubeClusters) == 0 { // If there are no registered k8s clusters, we may have an older teleport cluster. // Fall back to the old kubeconfig, with static credentials from v.Credentials. - log.Debug("Disabling exec plugin mode for kubeconfig because this Teleport cluster has no Kubernetes clusters.") + logger.DebugContext(cf.Context, "Disabling exec plugin mode for kubeconfig because this Teleport cluster has no Kubernetes clusters") return v, nil } diff --git a/tool/tsh/common/kube_proxy.go b/tool/tsh/common/kube_proxy.go index 75c8c120b223e..5e627a6303dbf 100644 --- a/tool/tsh/common/kube_proxy.go +++ b/tool/tsh/common/kube_proxy.go @@ -24,7 +24,6 @@ import ( "encoding/pem" "fmt" "io" - "log/slog" "net" "os" "os/exec" @@ -265,7 +264,7 @@ func (c *proxyKubeCommand) printPrepare(cf *CLIConf, title string, clusters kube for _, cluster := range clusters { contextName, err := kubeconfig.ContextNameFromTemplate(c.overrideContextName, cluster.TeleportCluster, cluster.KubeCluster) if err != nil { - slog.WarnContext(cf.Context, "Failed to generate context name.", "error", err) + logger.WarnContext(cf.Context, "Failed to generate context name", "error", err) contextName = kubeconfig.ContextName(cluster.TeleportCluster, cluster.KubeCluster) } table.AddRow([]string{cluster.TeleportCluster, cluster.KubeCluster, contextName}) @@ -495,7 +494,7 @@ func loadKubeUserCerts(ctx context.Context, tc *client.TeleportClient, clusters if key := kubeKeys[cluster.TeleportCluster]; key != nil { cert, err := kubeCertFromKeyRing(key, cluster.KubeCluster) if err == nil { - log.Debugf("Client cert loaded from keystore for %v.", cluster) + logger.DebugContext(ctx, "Client cert loaded from keystore for cluster", "cluster", cluster) certs.Add(cluster.TeleportCluster, cluster.KubeCluster, cert) continue } @@ -510,7 +509,7 @@ func loadKubeUserCerts(ctx context.Context, tc *client.TeleportClient, clusters return nil, trace.Wrap(err) } - log.Debugf("Client cert issued for %v.", cluster) + logger.DebugContext(ctx, "Client cert issued for cluster", "cluster", cluster) certs.Add(cluster.TeleportCluster, cluster.KubeCluster, cert) } return certs, nil diff --git a/tool/tsh/common/kube_test.go b/tool/tsh/common/kube_test.go index 6fb399aa5cc72..ae0890e8b8bbe 100644 --- a/tool/tsh/common/kube_test.go +++ b/tool/tsh/common/kube_test.go @@ -174,7 +174,7 @@ func setupKubeTestPack(t *testing.T, withMultiplexMode bool) *kubeTestPack { }), ) - mustLoginSetEnv(t, s) + mustLoginSetEnvLegacy(t, s) return &kubeTestPack{ suite: s, rootClusterName: s.root.Config.Auth.ClusterName.GetClusterName(), @@ -551,7 +551,7 @@ func TestKubeSelection(t *testing.T) { t.Parallel() // login for each parallel test to avoid races when multiple tsh // clients work in the same profile dir. - tshHome, _ := mustLogin(t, s) + tshHome, _ := mustLoginLegacy(t, s) // Set kubeconfig to a non-exist file to avoid loading other things. kubeConfigPath := filepath.Join(tshHome, "kube-config") var cmdRunner func(*exec.Cmd) error @@ -591,7 +591,7 @@ func TestKubeSelection(t *testing.T) { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() - tshHome, kubeConfigPath := mustLogin(t, s) + tshHome, kubeConfigPath := mustLoginLegacy(t, s) err := Run( context.Background(), append([]string{"kube", "login", "--insecure"}, @@ -676,7 +676,7 @@ func TestKubeSelection(t *testing.T) { t.Run("access request", func(t *testing.T) { t.Parallel() // login as the user. - tshHome, kubeConfig := mustLogin(t, s) + tshHome, kubeConfig := mustLoginLegacy(t, s) // Run the login command in a goroutine so we can check if the access // request was created and approved. diff --git a/tool/tsh/common/kubectl.go b/tool/tsh/common/kubectl.go index 69604a19700a5..55efc7a0d2b57 100644 --- a/tool/tsh/common/kubectl.go +++ b/tool/tsh/common/kubectl.go @@ -240,7 +240,7 @@ func runKubectlAndCollectRun(cf *CLIConf, fullArgs, args []string) error { writer.CloseWithError(io.EOF) if scanErr := group.Wait(); scanErr != nil { - log.WithError(scanErr).Warn("unable to scan stderr payload") + logger.WarnContext(cf.Context, "unable to scan stderr payload", "error", scanErr) } if err == nil { diff --git a/tool/tsh/common/mfa.go b/tool/tsh/common/mfa.go index 404be014e1c0f..31c6cb48a5195 100644 --- a/tool/tsh/common/mfa.go +++ b/tool/tsh/common/mfa.go @@ -233,7 +233,7 @@ func (c *mfaAddCommand) run(cf *CLIConf) error { if !slices.Contains(defaultDeviceTypes, touchIDDeviceType) { diag, err := touchid.Diag() if err == nil && diag.IsClamshellFailure() { - log.Warn("Touch ID support disabled, is your MacBook lid closed?") + logger.WarnContext(ctx, "Touch ID support disabled, is your MacBook lid closed?") } } @@ -280,7 +280,7 @@ func (c *mfaAddCommand) run(cf *CLIConf) error { // Touch ID is always a resident key/passwordless c.allowPasswordless = true } - log.Debugf("tsh using passwordless registration? %v", c.allowPasswordless) + logger.DebugContext(ctx, "tsh using passwordless registration?", "allow_passwordless", c.allowPasswordless) dev, err := c.addDeviceRPC(ctx, tc) if err != nil { @@ -468,7 +468,7 @@ func promptTOTPRegisterChallenge(ctx context.Context, c *proto.TOTPRegisterChall var showingQRCode bool closeQR, err := showOTPQRCode(otpKey) if err != nil { - log.WithError(err).Debug("Failed to show QR code") + logger.DebugContext(ctx, "Failed to show QR code", "error", err) } else { showingQRCode = true defer closeQR() @@ -515,7 +515,10 @@ func promptTOTPRegisterChallenge(ctx context.Context, c *proto.TOTPRegisterChall } func promptWebauthnRegisterChallenge(ctx context.Context, origin string, cc *wantypes.CredentialCreation) (*proto.MFARegisterResponse, error) { - log.Debugf("WebAuthn: prompting MFA devices with origin %q", origin) + logger.DebugContext(ctx, "prompting MFA devices with origin", + teleport.ComponentKey, "WebAuthn", + "origin", origin, + ) prompt := wancli.NewDefaultPrompt(ctx, os.Stdout) prompt.PINMessage = "Enter your *new* security key PIN" @@ -527,7 +530,10 @@ func promptWebauthnRegisterChallenge(ctx context.Context, origin string, cc *wan } func promptTouchIDRegisterChallenge(origin string, cc *wantypes.CredentialCreation) (*proto.MFARegisterResponse, registerCallback, error) { - log.Debugf("Touch ID: prompting registration with origin %q", origin) + logger.DebugContext(context.TODO(), "prompting registration with origin", + teleport.ComponentKey, "TouchID", + "origin", origin, + ) reg, err := touchid.Register(origin, cc) if err != nil { @@ -662,19 +668,23 @@ func showOTPQRCode(k *otp.Key) (cleanup func(), retErr error) { if err := imageFile.Close(); err != nil { return nil, trace.ConvertSystemError(err) } - log.Debugf("Wrote OTP QR code to %s", imageFile.Name()) + ctx := context.TODO() + logger.DebugContext(ctx, "Wrote OTP QR code to file", "file", imageFile.Name()) cmd := exec.Command(imageViewer, append(imageViewerArgs, imageFile.Name())...) if err := cmd.Start(); err != nil { return nil, trace.ConvertSystemError(err) } - log.Debugf("Opened QR code via %q", imageViewer) + logger.DebugContext(ctx, "Opened QR code via image viewer", "image_viewer", imageViewer) return func() { if err := utils.RemoveSecure(imageFile.Name()); err != nil { - log.WithError(err).Debugf("Failed to clean up temporary QR code file %q", imageFile.Name()) + logger.DebugContext(ctx, "Failed to clean up temporary QR code file", + "file", imageFile.Name(), + "error", err, + ) } if err := cmd.Process.Kill(); err != nil { - log.WithError(err).Debug("Failed to stop the QR code image viewer") + logger.DebugContext(ctx, "Failed to stop the QR code image viewer", "error", err) } }, nil } @@ -684,6 +694,9 @@ func deleteTouchIDCredentialIfApplicable(credentialID string) { case errors.Is(err, &touchid.ErrAttemptFailed{}): // Nothing to do here, just proceed. case err != nil: - log.WithError(err).Errorf("Failed to delete credential: %s\n", credentialID) + logger.ErrorContext(context.Background(), "Failed to delete credential", + "error", err, + "credential", credentialID, + ) } } diff --git a/tool/tsh/common/play.go b/tool/tsh/common/play.go index 64d6ea97ceb47..6ff68eb495413 100644 --- a/tool/tsh/common/play.go +++ b/tool/tsh/common/play.go @@ -58,7 +58,7 @@ func onPlay(cf *CLIConf) error { return playSession(cf) } if cf.PlaySpeed != "1x" { - log.Warn("--speed is not applicable for formats other than pty") + logger.WarnContext(cf.Context, "--speed is not applicable for formats other than pty") } return exportSession(cf) } @@ -94,7 +94,7 @@ func playSession(cf *CLIConf) error { if err := tc.Play(cf.Context, cf.SessionID, speed, cf.NoWait); err != nil { if trace.IsNotFound(err) { - log.WithError(err).Debug("error playing session") + logger.DebugContext(cf.Context, "error playing session", "error", err) return trace.NotFound("Recording for session %s not found.", cf.SessionID) } return trace.Wrap(err) diff --git a/tool/tsh/common/proxy.go b/tool/tsh/common/proxy.go index 2feae62df3081..dc357467e65e2 100644 --- a/tool/tsh/common/proxy.go +++ b/tool/tsh/common/proxy.go @@ -33,6 +33,7 @@ import ( "text/template" "unicode" + "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" "github.com/gravitational/teleport" @@ -186,7 +187,7 @@ func onProxyCommandDB(cf *CLIConf) error { defer func() { if err := listener.Close(); err != nil { - log.WithError(err).Warnf("Failed to close listener.") + logger.WarnContext(cf.Context, "Failed to close listener", "error", err) } }() @@ -229,6 +230,7 @@ func onProxyCommandDB(cf *CLIConf) error { if opts, err = maybeAddGCPMetadata(cf.Context, tc, dbInfo, opts); err != nil { return trace.Wrap(err) } + opts = maybeAddOracleOptions(cf.Context, tc, dbInfo, opts) commands, err := dbcmd.NewCmdBuilder(tc, profile, dbInfo.RouteToDatabase, rootCluster, opts..., @@ -326,36 +328,119 @@ func maybeAddGCPMetadataTplArgs(ctx context.Context, tc *libclient.TeleportClien } } +func maybeAddOracleOptions(ctx context.Context, tc *libclient.TeleportClient, dbInfo *databaseInfo, opts []dbcmd.ConnectCommandFunc) []dbcmd.ConnectCommandFunc { + // Skip for non-Oracle protocols. + if dbInfo.Protocol != defaults.ProtocolOracle { + return opts + } + + // TODO(Tener): DELETE IN 20.0.0 - all agents should now contain improved Oracle engine. + // minimum version to support TCPS-less connection. + cutoffVersion := semver.Version{ + Major: 17, + Minor: 2, + Patch: 0, + PreRelease: "", + } + + devV17Version := semver.Version{ + Major: 17, + Minor: 0, + Patch: 0, + PreRelease: "dev", + } + + dbServers, err := getDatabaseServers(ctx, tc, dbInfo.ServiceName) + if err != nil { + // log, but treat this error as non-fatal. + logger.WarnContext(ctx, "Error getting database servers", "error", err) + return opts + } + + var oldServers, newServers int + + for _, server := range dbServers { + ver, err := semver.NewVersion(server.GetTeleportVersion()) + if err != nil { + logger.DebugContext(ctx, "Failed to parse teleport version", "version", server.GetTeleportVersion(), "error", err) + continue + } + + if ver.Equal(devV17Version) { + newServers++ + } else { + if ver.LessThan(cutoffVersion) { + oldServers++ + } else { + newServers++ + } + } + } + + logger.DebugContext(ctx, "Retrieved agents for database with Oracle support", + "database", dbInfo.ServiceName, + "total", len(dbServers), + "old_count", oldServers, + "new_count", newServers, + ) + + if oldServers > 0 { + logger.WarnContext(ctx, "Detected outdated database agent, for improved client support upgrade all database agents in your cluster to a newer version", + "lowest_supported_version", cutoffVersion, + ) + } + + opts = append(opts, dbcmd.WithOracleOpts(oldServers == 0, newServers > 0)) + return opts +} + type templateCommandItem struct { Description string Command string } func chooseProxyCommandTemplate(templateArgs map[string]any, commands []dbcmd.CommandAlternative, dbInfo *databaseInfo) *template.Template { - // there is only one command, use plain template. - if len(commands) == 1 { - templateArgs["command"] = formatCommand(commands[0].Command) - switch dbInfo.Protocol { - case defaults.ProtocolOracle: - templateArgs["args"] = commands[0].Command.Args - return dbProxyOracleAuthTpl - case defaults.ProtocolSpanner: - templateArgs["databaseName"] = "" - if dbInfo.Database != "" { - templateArgs["databaseName"] = dbInfo.Database + templateArgs["command"] = formatCommand(commands[0].Command) + + // protocol-specific templates + if dbInfo.Protocol == defaults.ProtocolOracle { + // the JDBC connection string should always be found, + // but the order of commands is important as only the first command will actually be shown. + jdbcConnectionString := "" + ixFound := -1 + for ix, cmd := range commands { + for _, arg := range cmd.Command.Args { + if strings.Contains(arg, "jdbc:oracle:") { + jdbcConnectionString = arg + ixFound = ix + } } - return dbProxySpannerAuthTpl } + templateArgs["jdbcConnectionString"] = jdbcConnectionString + templateArgs["canUseTCP"] = ixFound > 0 + return dbProxyOracleAuthTpl + } + + if dbInfo.Protocol == defaults.ProtocolSpanner { + templateArgs["databaseName"] = "" + if dbInfo.Database != "" { + templateArgs["databaseName"] = dbInfo.Database + } + return dbProxySpannerAuthTpl + } + + // there is only one command, use plain template. + if len(commands) == 1 { return dbProxyAuthTpl } // multiple command options, use a different template. - var commandsArg []templateCommandItem for _, cmd := range commands { commandsArg = append(commandsArg, templateCommandItem{cmd.Description, formatCommand(cmd.Command)}) } + delete(templateArgs, "command") templateArgs["commands"] = commandsArg return dbProxyAuthMultiTpl } @@ -373,17 +458,22 @@ func onProxyCommandApp(cf *CLIConf) error { return trace.Wrap(err) } + portMapping, err := libclient.ParsePortMapping(cf.LocalProxyPortMapping) + if err != nil { + return trace.Wrap(err) + } + + profile, err := tc.ProfileStatus() + if err != nil { + return trace.Wrap(err) + } + var ( appInfo *appInfo app types.Application ) if err := libclient.RetryWithRelogin(cf.Context, tc, func() error { var err error - profile, err := tc.ProfileStatus() - if err != nil { - return trace.Wrap(err) - } - clusterClient, err := tc.ConnectToCluster(cf.Context) if err != nil { return trace.Wrap(err) @@ -401,19 +491,27 @@ func onProxyCommandApp(cf *CLIConf) error { return trace.Wrap(err) } - proxyApp := newLocalProxyApp(tc, appInfo, cf.LocalProxyPort, cf.InsecureSkipVerify) + proxyApp, err := newLocalProxyAppWithPortMapping(cf.Context, tc, profile, appInfo.RouteToApp, app, portMapping, cf.InsecureSkipVerify) + if err != nil { + return trace.Wrap(err) + } if err := proxyApp.StartLocalProxy(cf.Context, alpnproxy.WithALPNProtocol(alpnProtocolForApp(app))); err != nil { return trace.Wrap(err) } - fmt.Printf("Proxying connections to %s on %v\n", cf.AppName, proxyApp.GetAddr()) - if cf.LocalProxyPort == "" { + appName := cf.AppName + if portMapping.TargetPort != 0 { + appName = fmt.Sprintf("%s:%d", appName, portMapping.TargetPort) + } + fmt.Printf("Proxying connections to %s on %v\n", appName, proxyApp.GetAddr()) + // If target port is not equal to zero, the user must know about the port flag. + if portMapping.LocalPort == 0 && portMapping.TargetPort == 0 { fmt.Println("To avoid port randomization, you can choose the listening port using the --port flag.") } defer func() { if err := proxyApp.Close(); err != nil { - log.WithError(err).Error("Failed to close app proxy.") + logger.ErrorContext(cf.Context, "Failed to close app proxy", "error", err) } }() @@ -440,7 +538,7 @@ func onProxyCommandAWS(cf *CLIConf) error { defer func() { if err := awsApp.Close(); err != nil { - log.WithError(err).Error("Failed to close AWS app.") + logger.ErrorContext(cf.Context, "Failed to close AWS app", "error", err) } }() @@ -533,7 +631,7 @@ func onProxyCommandAzure(cf *CLIConf) error { defer func() { if err := azApp.Close(); err != nil { - log.WithError(err).Error("Failed to close Azure app.") + logger.ErrorContext(cf.Context, "Failed to close Azure app", "error", err) } }() @@ -564,7 +662,7 @@ func onProxyCommandGCloud(cf *CLIConf) error { defer func() { if err := gcpApp.Close(); err != nil { - log.WithError(err).Error("Failed to close GCP app.") + logger.ErrorContext(cf.Context, "Failed to close GCP app", "error", err) } }() @@ -673,10 +771,6 @@ Your database user is "{{.databaseUser}}".{{if .databaseName}} The target databa `)) -var templateFunctions = map[string]any{ - "contains": strings.Contains, -} - // dbProxyAuthTpl is the message that's printed for an authenticated db proxy. var dbProxyAuthTpl = template.Must(template.New("").Parse( `Started authenticated tunnel for the {{.type}} database "{{.database}}" in cluster "{{.cluster}}" on {{.address}}. @@ -700,21 +794,22 @@ Or use the following JDBC connection string to connect with other GUI/CLI client jdbc:cloudspanner://{{.address}}/projects/{{.gcpProject}}/instances/{{.gcpInstance}}/databases/{{.databaseName}};usePlainText=true `)) -// dbProxyOracleAuthTpl is the message that's printed for an authenticated db proxy. -var dbProxyOracleAuthTpl = template.Must(template.New("").Funcs(templateFunctions).Parse( +var dbProxyOracleAuthTpl = template.Must(template.New("").Parse( `Started authenticated tunnel for the {{.type}} database "{{.database}}" in cluster "{{.cluster}}" on {{.address}}. {{if .randomPort}}To avoid port randomization, you can choose the listening port using the --port flag. {{end}} -` + dbProxyConnectAd + ` Use the following command to connect to the Oracle database server using CLI: $ {{.command}} -or using following Oracle JDBC connection string in order to connect with other GUI/CLI clients: -{{- range $val := .args}} - {{- if contains $val "jdbc:oracle:"}} - {{$val}} - {{- end}} -{{- end}} +{{if .canUseTCP }}Other clients can use: + - a direct connection to {{.address}} without a username and password + - a custom JDBC connection string: {{.jdbcConnectionString}} + +{{else }}You can also connect using Oracle JDBC connection string: + {{.jdbcConnectionString}} + +Note: for improved client compatibility, upgrade your Teleport cluster. For details rerun this command with --debug. +{{- end }} `)) // dbProxyAuthMultiTpl is the message that's printed for an authenticated db proxy if there are multiple command options. diff --git a/tool/tsh/common/proxy_test.go b/tool/tsh/common/proxy_test.go index 639edf57c90f1..c05216143045a 100644 --- a/tool/tsh/common/proxy_test.go +++ b/tool/tsh/common/proxy_test.go @@ -33,6 +33,7 @@ import ( "fmt" "net" "net/http" + "net/url" "os" "os/exec" "os/user" @@ -65,6 +66,7 @@ import ( "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/service" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/teleagent" @@ -125,7 +127,7 @@ func TestSSH(t *testing.T) { } func testRootClusterSSHAccess(t *testing.T, s *suite) { - tshHome, _ := mustLogin(t, s) + tshHome, _ := mustLoginLegacy(t, s) err := Run(context.Background(), []string{ "ssh", s.root.Config.Hostname, @@ -145,7 +147,7 @@ func testRootClusterSSHAccess(t *testing.T, s *suite) { } func testLeafClusterSSHAccess(t *testing.T, s *suite) { - tshHome, _ := mustLogin(t, s, s.leaf.Config.Auth.ClusterName.GetClusterName()) + tshHome, _ := mustLoginLegacy(t, s, s.leaf.Config.Auth.ClusterName.GetClusterName()) require.Eventually(t, func() bool { err := Run(context.Background(), []string{ "ssh", @@ -171,7 +173,7 @@ func testLeafClusterSSHAccess(t *testing.T, s *suite) { func testJumpHostSSHAccess(t *testing.T, s *suite) { // login to root - tshHome, _ := mustLogin(t, s, s.root.Config.Auth.ClusterName.GetClusterName()) + tshHome, _ := mustLoginLegacy(t, s, s.root.Config.Auth.ClusterName.GetClusterName()) // Switch to leaf cluster err := Run(context.Background(), []string{ @@ -251,7 +253,7 @@ func TestWithRsync(t *testing.T) { s := newTestSuite(t) // login and get host info - tshHome, _ := mustLogin(t, s) + tshHome, _ := mustLoginLegacy(t, s) testBin, err := os.Executable() require.NoError(t, err) @@ -534,7 +536,7 @@ func TestProxySSH(t *testing.T) { } // login to Teleport - homePath, kubeConfigPath := mustLogin(t, s) + homePath, kubeConfigPath := mustLoginLegacy(t, s) require.Eventually(t, func() bool { rnodes, _ := s.root.GetAuthServer().GetNodes(context.Background(), "default") @@ -694,7 +696,7 @@ func TestTSHProxyTemplate(t *testing.T) { require.NoError(t, err) s := newTestSuite(t) - tshHome, _ := mustLoginSetEnv(t, s) + tshHome, _ := mustLoginSetEnvLegacy(t, s) // Create proxy template configuration. tshConfigFile := filepath.Join(tshHome, client.TSHConfigPath) @@ -792,7 +794,7 @@ func TestTSHConfigConnectWithOpenSSHClient(t *testing.T) { s := newTestSuite(t, tc.opts...) // Login to the Teleport proxy. - mustLoginSetEnv(t, s) + mustLoginSetEnvLegacy(t, s) // Get SSH config file generated by the 'tsh config' command. sshConfigFile := mustGetOpenSSHConfigFile(t) @@ -952,7 +954,7 @@ func TestList(t *testing.T) { }, } - tshHome, _ := mustLogin(t, s) + tshHome, _ := mustLoginLegacy(t, s) for _, test := range testCases { t.Run(test.description, func(t *testing.T) { stdout := &bytes.Buffer{} @@ -1000,7 +1002,9 @@ func (s *suite) setMockSSOLogin(t *testing.T) CliOption { return setMockSSOLogin(s.root.GetAuthServer(), s.user, s.connector.GetName()) } -func mustLogin(t *testing.T, s *suite, args ...string) (tshHome, kubeConfig string) { +// deprecated: Use mustLogin instead which requires migrating from newTestSuite to +// tools/teleport/testenv.MakeTestServer. +func mustLoginLegacy(t *testing.T, s *suite, args ...string) (tshHome, kubeConfig string) { tshHome = t.TempDir() kubeConfig = filepath.Join(t.TempDir(), teleport.KubeConfigFile) args = append([]string{ @@ -1020,15 +1024,17 @@ func mustLogin(t *testing.T, s *suite, args ...string) (tshHome, kubeConfig stri // login with new temp tshHome and set it in Env. This is useful // when running "ssh" commands with a tsh "ProxyCommand". -func mustLoginSetEnv(t *testing.T, s *suite, args ...string) (tshHome, kubeConfig string) { - tshHome, kubeConfig = mustLogin(t, s, args...) +// deprecated: Create a new helper that depends on mustLogin instead which requires migrating from +// newTestSuite to tools/teleport/testenv.MakeTestServer. +func mustLoginSetEnvLegacy(t *testing.T, s *suite, args ...string) (tshHome, kubeConfig string) { + tshHome, kubeConfig = mustLoginLegacy(t, s, args...) t.Setenv(types.HomeEnvVar, tshHome) return } func mustLoginIdentity(t *testing.T, s *suite) string { identityFile := filepath.Join(t.TempDir(), "identity.pem") - mustLogin(t, s, "--out", identityFile) + mustLoginLegacy(t, s, "--out", identityFile) return identityFile } @@ -1605,3 +1611,148 @@ func TestProxyAppWithIdentity(t *testing.T) { }) require.NoError(t, err, "no proxied app request succeeded") } + +func TestProxyAppMultiPort(t *testing.T) { + disableAgent(t) + // Necessary for self-signed certs to be considered valid. + lib.SetInsecureDevMode(true) + t.Cleanup(func() { lib.SetInsecureDevMode(false) }) + ctx := context.Background() + + const ( + clusterName = "root" + appName = "multi-port-app" + userName = "admin" + accessRoleName = "access" + ) + + fooServerURL := startDummyHTTPServer(t, "foo") + barServerURL := startDummyHTTPServer(t, "bar") + fooServerPort := mustGetPort(t, fooServerURL) + barServerPort := mustGetPort(t, barServerURL) + + user, err := types.NewUser(userName) + user.SetRoles([]string{accessRoleName}) + require.NoError(t, err) + + connector := mockConnector(t) + rootServerOpts := []testserver.TestServerOptFunc{ + testserver.WithBootstrap(connector, user), + testserver.WithClusterName(t, clusterName), + testserver.WithConfig(func(cfg *servicecfg.Config) { + cfg.Auth.NetworkingConfig.SetProxyListenerMode(types.ProxyListenerMode_Multiplex) + cfg.Apps = servicecfg.AppsConfig{ + Enabled: true, + Apps: []servicecfg.App{{ + Name: appName, + URI: "tcp://localhost", + TCPPorts: []servicecfg.PortRange{ + servicecfg.PortRange{Port: fooServerPort}, + servicecfg.PortRange{Port: barServerPort}, + }, + }}, + } + }), + } + process := testserver.MakeTestServer(t, rootServerOpts...) + + tshHome, _ := mustLogin(t, process, user, connector.GetName()) + + // tsh proxy app seems to not handle multiple concurrent invocations well. Because of that, the + // test needs to first set up a local proxy, verify it and only then set up another one. + + fooProxyPort := ports.Pop() + fooTshArgs := []string{ + "--debug", + "--insecure", + "--proxy", process.Config.Proxy.WebAddr.Addr, + "proxy", "app", appName, + "--port", fmt.Sprintf("%s:%d", fooProxyPort, fooServerPort), + } + utils.RunTestBackgroundTask(ctx, t, &utils.TestBackgroundTask{ + Name: "tsh proxy app (foo)", + Task: func(ctx context.Context) error { + return Run(ctx, fooTshArgs, setHomePath(tshHome)) + }, + }) + mustDialLocalAppProxy(t, fooProxyPort, "foo") + + fooNoTargetPortProxyPort := ports.Pop() + fooNoTargetPortTshArgs := []string{ + "--debug", + "--insecure", + "--proxy", process.Config.Proxy.WebAddr.Addr, + "proxy", "app", appName, + "--port", fooNoTargetPortProxyPort, // No target port. + } + utils.RunTestBackgroundTask(ctx, t, &utils.TestBackgroundTask{ + Name: "tsh proxy app (foo no target port)", + Task: func(ctx context.Context) error { + return Run(ctx, fooNoTargetPortTshArgs, setHomePath(tshHome)) + }, + }) + // If there's no target port, the connections should still be routed to the first TCP port. + mustDialLocalAppProxy(t, fooNoTargetPortProxyPort, "foo") + + barProxyPort := ports.Pop() + barTshArgs := []string{ + "--debug", + "--insecure", + "--proxy", process.Config.Proxy.WebAddr.Addr, + "proxy", "app", appName, + "--port", fmt.Sprintf("%s:%d", barProxyPort, barServerPort), + } + utils.RunTestBackgroundTask(ctx, t, &utils.TestBackgroundTask{ + Name: "tsh proxy app (bar)", + Task: func(ctx context.Context) error { + return Run(ctx, barTshArgs, setHomePath(tshHome)) + }, + }) + mustDialLocalAppProxy(t, barProxyPort, "bar") +} + +// mustGetPort extracts the port out of the URL returned by functions such as startDummyHTTPServer. +func mustGetPort(t *testing.T, rawURL string) int { + t.Helper() + + url, err := url.Parse(rawURL) + require.NoError(t, err) + _, portString, err := net.SplitHostPort(url.Host) + require.NoError(t, err) + port, err := strconv.Atoi(portString) + require.NoError(t, err) + return port +} + +func mustLogin(t *testing.T, s *service.TeleportProcess, user types.User, connectorName string, args ...string) (tshHome, kubeConfig string) { + t.Helper() + tshHome = t.TempDir() + kubeConfig = filepath.Join(t.TempDir(), teleport.KubeConfigFile) + args = append([]string{ + "login", + "--insecure", + "--debug", + "--proxy", s.Config.Proxy.WebAddr.String(), + }, args...) + err := Run(context.Background(), args, + setMockSSOLogin(s.GetAuthServer(), user, connectorName), + setHomePath(tshHome), + setKubeConfigPath(kubeConfig), + ) + require.NoError(t, err, trace.DebugReport(err)) + return +} + +// mustDialLocalAppProxy verifies that a local app proxy for an app backed by startDummyHTTPServer +// returns the expected HTTP response. +func mustDialLocalAppProxy(t *testing.T, port string, expectedName string) { + t.Helper() + require.EventuallyWithT(t, func(t *assert.CollectT) { + r, err := http.Get(fmt.Sprintf("http://localhost:%s", port)) + require.NoError(t, err) + defer r.Body.Close() + + require.Equal(t, 200, r.StatusCode) + require.Equal(t, expectedName, r.Header.Get("Server"), "the response header \"Server\" does not have the expected value") + }, 5*time.Second, 50*time.Millisecond) +} diff --git a/tool/tsh/common/putty_config_windows.go b/tool/tsh/common/putty_config_windows.go index 7d202895d3b47..d1ea9eea0f419 100644 --- a/tool/tsh/common/putty_config_windows.go +++ b/tool/tsh/common/putty_config_windows.go @@ -19,6 +19,7 @@ package common import ( + "context" "fmt" "net" "strconv" @@ -175,6 +176,7 @@ func addPuTTYSession(proxyHostname string, hostname string, port int, login stri // addHostCAPublicKey adds a host CA to the registry with a set of hostnames delimited by " || " // as per PuTTY's "Validity" syntax. func addHostCAPublicKey(registryHostCAStruct puttyhosts.HostCAPublicKeyForRegistry) error { + ctx := context.TODO() registryKeyName := fmt.Sprintf(`%v\%v`, puttyRegistrySSHHostCAsKey, registryHostCAStruct.KeyName) // get the subkey with the host CA key name @@ -190,7 +192,10 @@ func addHostCAPublicKey(registryHostCAStruct puttyhosts.HostCAPublicKeyForRegist // ERROR_FILE_NOT_FOUND is an acceptable error, meaning that the value does not already // exist and it must be created if err != syscall.ERROR_FILE_NOT_FOUND { - log.Debugf("Can't get registry value %v: %T", registryKeyName, err) + logger.DebugContext(ctx, "Can't get registry value", + "registry_key", registryKeyName, + "error", err, + ) return trace.Wrap(err) } } @@ -208,14 +213,20 @@ func addHostCAPublicKey(registryHostCAStruct puttyhosts.HostCAPublicKeyForRegist // ERROR_FILE_NOT_FOUND is an acceptable error, meaning that the value does not already // exist and it must be created if err != syscall.ERROR_FILE_NOT_FOUND { - log.Debugf("Can't get registry value %v: %T", registryKeyName, err) + logger.DebugContext(ctx, "Can't get registry value", + "registry_key", registryKeyName, + "error", err, + ) return trace.Wrap(err) } } // if matchHosts has any entries, we do a one-time migration of all the values from the "old" MatchHosts // multistring to the new Validity string, if len(matchHosts) > 0 { - log.Debugf("Found %v legacy MatchHosts value(s) in registry key %v, migrating to new Validity format", len(matchHosts), registryKeyName) + logger.DebugContext(ctx, "Found legacy MatchHosts value(s) in registry key, migrating to new Validity format", + "match_host_count", len(matchHosts), + "registry_key", registryKeyName, + ) hostList = append(hostList, matchHosts...) } @@ -249,11 +260,18 @@ func addHostCAPublicKey(registryHostCAStruct puttyhosts.HostCAPublicKeyForRegist // if matchHosts has any entries, delete the "MatchHosts" key from the registry as its entries were migrated above. if len(matchHosts) > 0 { - log.Debugf("Deleting %v legacy MatchHosts value(s) from registry key %v", len(matchHosts), registryKeyName) + logger.DebugContext(ctx, "Deleting legacy MatchHosts value(s) from registry key", + "match_host_count", len(matchHosts), + "registry_key", registryKeyName, + ) + err := registryKey.DeleteValue("MatchHosts") // failure to delete this value isn't a fatal error, so we should continue regardless if err != nil { - log.Debugf("Failed to delete old MatchHosts value for %v: %v", registryHostCAStruct.KeyName, err) + logger.DebugContext(ctx, "Failed to delete old MatchHosts value for key", + "registry_key", registryHostCAStruct.KeyName, + "error", err, + ) } } @@ -283,9 +301,9 @@ func onPuttyConfig(cf *CLIConf) error { case 0: return trace.NotFound("no matching hosts found") case 1: - log.Debugf("Using host %v", matches[0]) + logger.DebugContext(cf.Context, "Using matched host", "host", matches[0]) default: - log.Debugf("found multiple matching hosts %v %v", matches[0], matches[1]) + logger.DebugContext(cf.Context, "found multiple matching hosts", matches[0], matches[1]) return trace.BadParameter("multiple matching hosts found") } @@ -349,12 +367,15 @@ func onPuttyConfig(cf *CLIConf) error { addToRegistry := puttyhosts.FormatHostCAPublicKeysForRegistry(hostCAPublicKeys, hostname) for cluster, values := range addToRegistry { - for i, registryPublicKeyStruct := range values { + for _, registryPublicKeyStruct := range values { if err := addHostCAPublicKey(registryPublicKeyStruct); err != nil { - log.Errorf("Failed to add host CA key for %v: %T", cluster, err) + logger.ErrorContext(cf.Context, "Failed to add host CA key for cluster", + "cluster", cluster, + "error", err, + ) return trace.Wrap(err) } - log.Debugf("Added/updated host CA key %d for %v", i, cluster) + logger.DebugContext(cf.Context, "Added/updated host CA key for cluster", cluster, cluster) } } @@ -366,7 +387,10 @@ func onPuttyConfig(cf *CLIConf) error { // add session to registry if err := addPuTTYSession(proxyHost, hostname, port, login, ppkFilePath, certificateFilePath, localCommandString, cf.LeafClusterName); err != nil { - log.Errorf("Failed to add PuTTY session for %v: %T\n", userHostString, err) + logger.ErrorContext(cf.Context, "Failed to add PuTTY session", + "user_host", userHostString, + "error",err, + ) return trace.Wrap(err) } diff --git a/tool/tsh/common/recording_export.go b/tool/tsh/common/recording_export.go index 6d868557fca30..5a55447352a1d 100644 --- a/tool/tsh/common/recording_export.go +++ b/tool/tsh/common/recording_export.go @@ -37,6 +37,7 @@ import ( "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/srv/desktop/tdp" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const ( @@ -112,7 +113,7 @@ loop: return frameCount, ctx.Err() case evt, more := <-evts: if !more { - log.Warnln("reached end of stream before seeing session end event") + logger.WarnContext(ctx, "reached end of stream before seeing session end event") break loop } @@ -137,7 +138,7 @@ loop: case *apievents.DesktopRecording: msg, err := tdp.Decode(evt.Message) if err != nil { - log.Warnf("failed to decode desktop recording message: %v", err) + logger.WarnContext(ctx, "failed to decode desktop recording message", "error", err) break loop } @@ -153,7 +154,7 @@ loop: // Note: this works because we don't currently support resizing // the window during a session. If this changes, we'd have to // find the maximum window size first. - log.Debugf("allocating %dx%d screen", msg.Width, msg.Height) + logger.DebugContext(ctx, "allocating screen size", "width", msg.Width, "height", msg.Height) width, height = int32(msg.Width), int32(msg.Height) screen = image.NewNRGBA(image.Rectangle{ Min: image.Pt(0, 0), @@ -196,7 +197,10 @@ loop: delta := evt.DelayMilliseconds - lastEmitted framesToEmit := int64(float64(delta) / frameDelayMillis) if framesToEmit > 0 { - log.Debugf("%dms since last frame, emitting %d frames", delta, framesToEmit) + logger.DebugContext(ctx, "emitting frames", + "last_event_ms", delta, + "frames_to_emit", framesToEmit, + ) buf.Reset() if err := jpeg.Encode(buf, screen, nil); err != nil { return frameCount, trace.Wrap(err) @@ -231,7 +235,7 @@ loop: } default: - log.Debugf("got unexpected audit event %T", evt) + logger.DebugContext(ctx, "got unexpected audit event", "event", logutils.TypeAttr(evt)) } } } diff --git a/tool/tsh/common/resolve_default_addr.go b/tool/tsh/common/resolve_default_addr.go index 5767015e0dc72..fb2a87f384cb7 100644 --- a/tool/tsh/common/resolve_default_addr.go +++ b/tool/tsh/common/resolve_default_addr.go @@ -62,7 +62,7 @@ func raceRequest(ctx context.Context, cli *http.Client, addr string, waitgroup * rsp, err := cli.Do(request) if err != nil { - log.WithError(err).Debug("Proxy address test failed") + logger.DebugContext(ctx, "Proxy address test failed", "error", err) results <- raceResult{addr: addr, err: err} return } @@ -75,16 +75,18 @@ func raceRequest(ctx context.Context, cli *http.Client, addr string, waitgroup * resBody, err := io.ReadAll(io.LimitReader(rsp.Body, maxPingBodySize)) if err != nil { // Log but do not return. We could receive HTTP OK, and we should not fail on error here. - log.Debugf("Failed to read whole response body: %v", err) + logger.DebugContext(ctx, "Failed to read whole response body", "error", err) } // If the request returned a non-OK response then we're still going // to treat this as a failure and return an error to the race // aggregator. if rsp.StatusCode != http.StatusOK { + logger.DebugContext(ctx, "Proxy address test received non-OK response", + "status_code", rsp.StatusCode, + "response_body", string(resBody), + ) err = trace.BadParameter("Proxy address test received non-OK response: %03d", rsp.StatusCode) - log.Debugf("%v, response body: %s ", err, string(resBody)) - results <- raceResult{addr: addr, err: err} return } @@ -98,7 +100,7 @@ func raceRequest(ctx context.Context, cli *http.Client, addr string, waitgroup * func startRacer(ctx context.Context, cli *http.Client, host string, candidates []int, waitGroup *sync.WaitGroup, results chan<- raceResult) []int { port, tail := candidates[0], candidates[1:] addr := net.JoinHostPort(host, strconv.Itoa(port)) - log.Debugf("Trying %s...", addr) + logger.DebugContext(ctx, "Trying request", "addr", addr) waitGroup.Add(1) go raceRequest(ctx, cli, addr, waitGroup, results) return tail @@ -110,7 +112,7 @@ func startRacer(ctx context.Context, cli *http.Client, host string, candidates [ // 2. races the requests against one another, and finally // 3. selects the first to respond as the canonical proxy func pickDefaultAddr(ctx context.Context, insecure bool, host string, ports []int) (string, error) { - log.Debugf("Resolving default proxy port (insecure: %v)", insecure) + logger.DebugContext(ctx, "Resolving default proxy port", "insecure_mode", insecure) if len(ports) == 0 { return "", trace.BadParameter("port list may not be empty") @@ -138,7 +140,7 @@ func pickDefaultAddr(ctx context.Context, insecure bool, host string, ports []in // properly in error conditions. var racersInFlight sync.WaitGroup defer func() { - log.Debug("Waiting for all in-flight proxy address tests to finish") + logger.DebugContext(ctx, "Waiting for all in-flight proxy address tests to finish") racersInFlight.Wait() }() @@ -190,7 +192,7 @@ func pickDefaultAddr(ctx context.Context, insecure bool, host string, ports []in // Note that returning will implicitly cancel the inner context, telling // any outstanding racers that there is no point trying anymore, and they // should exit. - log.Debugf("Address %s succeeded. Selected as canonical proxy address", r.addr) + logger.DebugContext(ctx, "Request to address succeeded, selected as canonical proxy address", "address", r.addr) return r.addr, nil } errors = append(errors, r.err) diff --git a/tool/tsh/common/resolve_default_addr_test.go b/tool/tsh/common/resolve_default_addr_test.go index 7e45e727a2c98..27f728044f86e 100644 --- a/tool/tsh/common/resolve_default_addr_test.go +++ b/tool/tsh/common/resolve_default_addr_test.go @@ -31,17 +31,13 @@ import ( "github.com/stretchr/testify/require" - "github.com/gravitational/teleport" apihelpers "github.com/gravitational/teleport/api/testhelpers" "github.com/gravitational/teleport/integration/helpers" ) -var testLog = log.WithField(teleport.ComponentKey, "test") - func newWaitForeverHandler() (http.Handler, chan struct{}) { doneChannel := make(chan struct{}) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - testLog.Debug("Waiting forever...") <-doneChannel }) @@ -50,7 +46,6 @@ func newWaitForeverHandler() (http.Handler, chan struct{}) { func newRespondingHandlerWithStatus(status int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - testLog.Debug("Responding") w.Header().Add("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(status) io.WriteString(w, "Hello, world") @@ -201,11 +196,9 @@ func TestResolveUndeliveredBodyDoesNotBlockForever(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { f, ok := w.(http.Flusher) if !ok { - testLog.Error("ResponseWriter must also be a Flusher, or the test is invalid") t.Fatal() } - testLog.Debugf("Writing response header to %T", w) w.Header().Set("Content-Length", "1048576") w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusTeapot) @@ -213,10 +206,7 @@ func TestResolveUndeliveredBodyDoesNotBlockForever(t *testing.T) { w.Write([]byte("I'm a little teapot, short and stout.")) f.Flush() - testLog.Debug("Waiting forever instead of sending response body") <-doneChannel - - testLog.Debug("Exiting handler") }) servers := []*httptest.Server{apihelpers.MakeTestServer(t, handler)} diff --git a/tool/tsh/common/tlsmuxlistener.go b/tool/tsh/common/tlsmuxlistener.go new file mode 100644 index 0000000000000..ec1c1c2e7ac27 --- /dev/null +++ b/tool/tsh/common/tlsmuxlistener.go @@ -0,0 +1,96 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package common + +import ( + "bufio" + "context" + "crypto/tls" + "net" + + "github.com/gravitational/trace" +) + +// connReader is a net.Conn wrapper with additional Peek() method. +type connReader struct { + reader *bufio.Reader + net.Conn +} + +// newBufferedConn is a connReader constructor. +func newBufferedConn(conn net.Conn) connReader { + return connReader{ + reader: bufio.NewReader(conn), + Conn: conn, + } +} + +// Peek reads n bytes without advancing the reader. +// It's basically a wrapper around (bufio.Reader).Peek() +func (b connReader) Peek(n int) ([]byte, error) { + return b.reader.Peek(n) +} + +// Read returns data from underlying buffer. +func (b connReader) Read(p []byte) (int, error) { + return b.reader.Read(p) +} + +type tlsMuxListener struct { + listener net.Listener + tlsConfig *tls.Config +} + +func (m *tlsMuxListener) Accept() (net.Conn, error) { + conn, err := m.listener.Accept() + if err != nil { + return nil, trace.Wrap(err) + } + + bufConn := newBufferedConn(conn) + buf, err := bufConn.Peek(1) + if err != nil { + return nil, trace.Wrap(err) + } + + const tlsFirstByte = 0x16 + + switch buf[0] { + case tlsFirstByte: + logger.DebugContext(context.Background(), "Read first byte, assuming TLS connection") + return tls.Server(bufConn, m.tlsConfig), nil + default: + return bufConn, nil + } +} + +func (m *tlsMuxListener) Close() error { + return m.listener.Close() +} + +func (m *tlsMuxListener) Addr() net.Addr { + return m.listener.Addr() +} + +// NewTLSMuxListener returns new multiplexing listener. If the incoming connection appears to use TLS, a TLS listener will serve it. +// Otherwise, it will be served raw. +func NewTLSMuxListener(listener net.Listener, tlsConfig *tls.Config) net.Listener { + return &tlsMuxListener{ + listener: listener, + tlsConfig: tlsConfig, + } +} diff --git a/tool/tsh/common/tlsmuxlistener_test.go b/tool/tsh/common/tlsmuxlistener_test.go new file mode 100644 index 0000000000000..dc9d0e13dac1a --- /dev/null +++ b/tool/tsh/common/tlsmuxlistener_test.go @@ -0,0 +1,114 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package common + +import ( + "crypto/tls" + "crypto/x509/pkix" + "errors" + "io" + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/tlsca" +) + +func TestTLSMuxListenerDataTransfer(t *testing.T) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + defer ln.Close() + + keyPEM, certPEM, err := tlsca.GenerateSelfSignedCA(pkix.Name{CommonName: "tsh"}, []string{"localhost"}, defaults.CATTL) + require.NoError(t, err) + + cert, err := tls.X509KeyPair(certPEM, keyPEM) + require.NoError(t, err) + + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: true, + } + + muxListener := NewTLSMuxListener(ln, tlsConfig) + + // Start a goroutine to accept connections and echo data back. + go func() { + for { + conn, errAccept := muxListener.Accept() + if errAccept != nil { + return // Exit on listener closure. + } + + // Echo the received data back to the client. + go func() { + defer conn.Close() + buf := make([]byte, 1024) + for { + n, err := conn.Read(buf) + if errors.Is(err, io.EOF) { + return + } + assert.NoError(t, err) + + _, err = conn.Write(buf[:n]) + assert.NoError(t, err) + if err != nil { + return + } + } + }() + } + }() + + // Test plain connection. + t.Run("Plain Connection", func(t *testing.T) { + conn, err := net.Dial("tcp", ln.Addr().String()) + require.NoError(t, err) + defer conn.Close() + + message := "Hello, non-TLS!" + _, err = conn.Write([]byte(message)) + require.NoError(t, err) + + buf := make([]byte, len(message)) + _, err = conn.Read(buf) + require.NoError(t, err) + require.Equal(t, message, string(buf), "Plain connection message mismatch") + }) + + // Test TLS connection. + t.Run("TLS Connection", func(t *testing.T) { + tlsConn, err := tls.Dial("tcp", ln.Addr().String(), &tls.Config{ + InsecureSkipVerify: true, + }) + require.NoError(t, err) + defer tlsConn.Close() + + message := "Hello, TLS!" + _, err = tlsConn.Write([]byte(message)) + require.NoError(t, err) + + buf := make([]byte, len(message)) + _, err = tlsConn.Read(buf) + require.NoError(t, err) + require.Equal(t, message, string(buf), "TLS connection message mismatch") + }) +} diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index 454f37fe52168..d65af0b7247db 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -49,7 +49,6 @@ import ( "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/attribute" oteltrace "go.opentelemetry.io/otel/trace" "golang.org/x/crypto/ssh" @@ -103,7 +102,6 @@ import ( ) var ( - log = logrus.WithField(teleport.ComponentKey, teleport.ComponentTSH) logger = logutils.NewPackageLogger(teleport.ComponentKey, teleport.ComponentTSH) ) @@ -428,8 +426,13 @@ type CLIConf struct { // LocalProxyPort is a port used by local proxy listener. LocalProxyPort string + // LocalProxyPortMapping is a listening port and an optional target port used by local proxy + // listener, in the form of "1234" or "1234:5678". + LocalProxyPortMapping string // LocalProxyTunnel specifies whether local proxy will open auth'd tunnel. LocalProxyTunnel bool + // TargetPort is a port used for routing connections to multi-port TCP apps. + TargetPort uint16 // Exec is the command to run via tsh aws. Exec string @@ -890,6 +893,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { appLogin.Flag("aws-role", "(For AWS CLI access only) Amazon IAM role ARN or role name.").StringVar(&cf.AWSRole) appLogin.Flag("azure-identity", "(For Azure CLI access only) Azure managed identity name.").StringVar(&cf.AzureIdentity) appLogin.Flag("gcp-service-account", "(For GCP CLI access only) GCP service account name.").StringVar(&cf.GCPServiceAccount) + appLogin.Flag("target-port", "Port to which connections made using this cert should be routed to. Valid only for multi-port TCP apps.").Uint16Var(&cf.TargetPort) appLogin.Flag("quiet", "Quiet mode").Short('q').BoolVar(&cf.Quiet) appLogout := apps.Command("logout", "Remove app certificate.") appLogout.Arg("app", "App to remove credentials for.").StringVar(&cf.AppName) @@ -934,7 +938,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { proxyApp := proxy.Command("app", "Start local TLS proxy for app connection when using Teleport in single-port mode.") proxyApp.Arg("app", "The name of the application to start local proxy for").Required().StringVar(&cf.AppName) - proxyApp.Flag("port", "Specifies the source port used by by the proxy app listener").Short('p').StringVar(&cf.LocalProxyPort) + proxyApp.Flag("port", "Specifies the listening port used by by the proxy app listener. Accepts an optional target port of a multi-port TCP app after a colon, e.g. \"1234:5678\"").Short('p').StringVar(&cf.LocalProxyPortMapping) proxyApp.Flag("cluster", clusterHelp).Short('c').StringVar(&cf.SiteName) proxyAWS := proxy.Command("aws", "Start local proxy for AWS access.") @@ -1280,7 +1284,10 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { command, err := app.Parse(args) if errors.Is(err, kingpin.ErrExpectedCommand) { if _, ok := cf.TSHConfig.Aliases[aliasCommand]; ok { - log.Debugf("Failing due to recursive alias %q. Aliases seen: %v", aliasCommand, ar.getSeenAliases()) + logger.DebugContext(ctx, "Failing due to recursive alias", + "alias", aliasCommand, + "aliases_seen", ar.getSeenAliases(), + ) return trace.BadParameter("recursive alias %q; correct alias definition and try again", aliasCommand) } } @@ -1351,7 +1358,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { } if cpuProfile != "" { - log.Debugf("writing CPU profile to %v", cpuProfile) + logger.DebugContext(ctx, "writing CPU profile", "file", cpuProfile) f, err := os.Create(cpuProfile) if err != nil { return trace.Wrap(err) @@ -1364,24 +1371,24 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { } if memProfile != "" { - log.Debugf("writing memory profile to %v", memProfile) + logger.DebugContext(ctx, "writing memory profile", "file", memProfile) defer func() { f, err := os.Create(memProfile) if err != nil { - log.Errorf("could not open memory profile: %v", err) + logger.ErrorContext(ctx, "could not open memory profile", "error", err) return } defer f.Close() runtime.GC() if err := pprof.WriteHeapProfile(f); err != nil { - log.Errorf("could not write memory profile: %v", err) + logger.ErrorContext(ctx, "could not write memory profile", "error", err) return } }() } if traceProfile != "" { - log.Debugf("writing trace profile to %v", traceProfile) + logger.DebugContext(ctx, "writing trace profile", "file", traceProfile) f, err := os.Create(traceProfile) if err != nil { return trace.Wrap(err) @@ -1661,7 +1668,7 @@ func initializeTracing(cf *CLIConf) func() { defer cancel() err := provider.Shutdown(shutdownCtx) if err != nil && !errors.Is(err, context.DeadlineExceeded) { - log.WithError(err).Debug("failed to shutdown trace provider") + logger.DebugContext(shutdownCtx, "failed to shutdown trace provider", "error", err) } } } @@ -1692,7 +1699,10 @@ func initializeTracing(cf *CLIConf) func() { SamplingRate: samplingRate, }) if err != nil { - log.WithError(err).Debugf("failed to connect to trace exporter %s", cf.TraceExporter) + logger.DebugContext(cf.Context, "failed to connect to trace exporter", + "error", err, + "exporter", cf.TraceExporter, + ) return func() {} } @@ -1717,7 +1727,7 @@ func initializeTracing(cf *CLIConf) func() { // to get a handle to an Auth client. tc, err := makeClient(cf) if err != nil { - log.WithError(err).Debug("failed to set up span forwarding.") + logger.DebugContext(cf.Context, "failed to set up span forwarding", "error", err) return func() {} } @@ -1741,7 +1751,7 @@ func initializeTracing(cf *CLIConf) func() { provider = p return nil }); err != nil { - log.WithError(err).Debug("failed to set up span forwarding.") + logger.DebugContext(cf.Context, "failed to set up span forwarding", "error", err) return func() {} } @@ -2064,7 +2074,7 @@ func onLogin(cf *CLIConf) error { // If we're in multiplexed mode get SNI name for kube from single multiplexed proxy addr kubeTLSServerName := "" if tc.TLSRoutingEnabled { - log.Debug("Using Proxy SNI for kube TLS server name") + logger.DebugContext(cf.Context, "Using Proxy SNI for kube TLS server name") kubeHost, _ := tc.KubeProxyHostPort() kubeTLSServerName = client.GetKubeTLSServerName(kubeHost) } @@ -2156,7 +2166,7 @@ func onLogin(cf *CLIConf) error { if err := common.ShowClusterAlerts(cf.Context, clusterClient.CurrentCluster(), os.Stderr, map[string]string{ types.AlertOnLogin: "yes", }, types.AlertSeverity_LOW); err != nil { - log.WithError(err).Warn("Failed to display cluster alerts.") + logger.WarnContext(cf.Context, "Failed to display cluster alerts", "error", err) } return nil @@ -2203,7 +2213,10 @@ func onLogout(cf *CLIConf) error { // Log out user from the databases. if profile != nil { for _, db := range profile.Databases { - log.Debugf("Logging %v out of database %v.", profile.Name, db) + logger.DebugContext(cf.Context, "Logging user out of database", + "user", profile.Name, + "database", db, + ) err = dbprofile.Delete(tc, db) if err != nil { return trace.Wrap(err) @@ -2222,7 +2235,7 @@ func onLogout(cf *CLIConf) error { } // Remove Teleport related entries from kubeconfig. - log.Debugf("Removing Teleport related entries with server '%v' from kubeconfig.", tc.KubeClusterAddr()) + logger.DebugContext(cf.Context, "Removing Teleport related entries from kubeconfig", "cluster_addr", tc.KubeClusterAddr()) err = kubeconfig.RemoveByServerAddr("", tc.KubeClusterAddr()) if err != nil { return trace.Wrap(err) @@ -2235,14 +2248,14 @@ func onLogout(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - log.Debugf("Removing Teleport related entries with server '%v' from kubeconfig.", tc.KubeClusterAddr()) + logger.DebugContext(cf.Context, "Removing Teleport related entries from kubeconfig", "cluster_addr", tc.KubeClusterAddr()) if err = kubeconfig.RemoveByServerAddr("", tc.KubeClusterAddr()); err != nil { return trace.Wrap(err) } // Remove Teleport related entries from kubeconfig for all clusters. for _, profile := range profiles { - log.Debugf("Removing Teleport related entries for cluster '%v' from kubeconfig.", profile.Cluster) + logger.DebugContext(cf.Context, "Removing Teleport related entries from kubeconfig", "cluster", profile.Cluster) err = kubeconfig.RemoveByClusterName("", profile.Cluster) if err != nil { return trace.Wrap(err) @@ -2253,7 +2266,10 @@ func onLogout(cf *CLIConf) error { // connection service file. for _, profile := range profiles { for _, db := range profile.Databases { - log.Debugf("Logging %v out of database %v.", profile.Name, db) + logger.DebugContext(cf.Context, "Logging user out of database", + "user", profile.Name, + "database", db, + ) err = dbprofile.Delete(tc, db) if err != nil { return trace.Wrap(err) @@ -2368,14 +2384,14 @@ func getClusterClients(cf *CLIConf, resource string) ([]*clusterClient, error) { ) defer span.End() - logger := log.WithField("cluster", profile.Cluster) + logger := logger.With("cluster", profile.Cluster) - logger.Debug("Creating client...") + logger.DebugContext(ctx, "Creating client") clt, err := tc.ConnectToCluster(ctx) if err != nil { // log error and return nil so that results may still be retrieved // for other clusters. - logger.Errorf("Failed connecting to proxy: %v", err) + logger.ErrorContext(ctx, "Failed connecting to proxy", "error", err) mu.Lock() clusters = append(clusters, &clusterClient{ @@ -2402,7 +2418,7 @@ func getClusterClients(cf *CLIConf, resource string) ([]*clusterClient, error) { if err != nil { // Log that an error happened but do not return an error to // prevent results from other clusters from being retrieved. - logger.Errorf("Failed to lookup leaf clusters: %v", err) + logger.ErrorContext(ctx, "Failed to lookup leaf clusters", "error", err) return nil } @@ -2503,10 +2519,10 @@ func listNodesAllClusters(cf *CLIConf) error { oteltrace.WithAttributes(attribute.String("cluster", cluster.name))) defer span.End() - logger := log.WithField("cluster", cluster.name) + logger := logger.With("cluster", cluster.name) nodes, err := apiclient.GetAllResources[types.Server](ctx, cluster.auth, &cluster.req) if err != nil { - logger.Errorf("Failed to get nodes: %v.", err) + logger.ErrorContext(ctx, "Failed to get nodes", "error", err) mu.Lock() errors = append(errors, trace.ConnectionProblem(err, "failed to list nodes for cluster %s: %v", cluster.name, err)) @@ -3064,7 +3080,10 @@ type databaseWithUsers struct { func getDBUsers(db types.Database, accessChecker services.AccessChecker) *dbUsers { users, err := accessChecker.EnumerateDatabaseUsers(db) if err != nil { - log.Warnf("Failed to EnumerateDatabaseUsers for database %v: %v.", db.GetName(), err) + logger.WarnContext(context.Background(), "Failed to EnumerateDatabaseUsers for database", + "database", db.GetName(), + "error", err, + ) return &dbUsers{} } var denied []string @@ -3095,7 +3114,10 @@ func newDatabaseWithUsers(db types.Database, accessChecker services.AccessChecke if db.SupportsAutoUsers() && db.GetAdminUser().Name != "" { roles, err := accessChecker.CheckDatabaseRoles(db, nil) if err != nil { - log.Warnf("Failed to CheckDatabaseRoles for database %v: %v.", db.GetName(), err) + logger.WarnContext(context.Background(), "Failed to CheckDatabaseRoles for database", + "database", db.GetName(), + "error", err, + ) } else { dbWithUsers.DatabaseRoles = roles } @@ -3144,7 +3166,10 @@ func formatUsersForDB(database types.Database, accessChecker services.AccessChec if database.SupportsAutoUsers() && database.GetAdminUser().Name != "" { autoUser, err := accessChecker.DatabaseAutoUserMode(database) if err != nil { - log.Warnf("Failed to get DatabaseAutoUserMode for database %v: %v.", database.GetName(), err) + logger.WarnContext(context.Background(), "Failed to get DatabaseAutoUserMode for database", + "database", database.GetName(), + "error", err, + ) } else if autoUser.IsEnabled() { defer func() { users = users + " (Auto-provisioned)" @@ -3614,7 +3639,7 @@ func retryWithAccessRequest( // a short debug message in case this is unexpected, but return the // original AccessDenied error from the ssh attempt which is likely to // be far more relevant to the user. - log.Debugf("Not attempting to automatically request access, reason: %v", err) + logger.DebugContext(cf.Context, "Not attempting to automatically request access, reason", "error", err) return trace.Wrap(origErr) } @@ -4112,14 +4137,17 @@ func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, erro if !trace.IsNotFound(err) && !trace.IsConnectionProblem(err) && !trace.IsCompareFailed(err) { return nil, trace.Wrap(err) } - log.WithError(err).Infof("Could not load key for %s into the local agent.", cf.SiteName) + logger.InfoContext(ctx, "Could not load key for cluser into the local agent", + "cluster", cf.SiteName, + "error", err, + ) } } // If we are missing client profile information, ping the webproxy // for proxy info and load it into the client config. if profileError != nil || profile.MissingClusterDetails { - log.Debug("Pinging the proxy to fetch listening addresses for non-web ports.") + logger.DebugContext(cf.Context, "Pinging the proxy to fetch listening addresses for non-web ports") _, err := tc.Ping(cf.Context) if err != nil { return nil, trace.Wrap(err) @@ -4243,7 +4271,7 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err } else if tMatched { if expanded.Host != "" { c.Host = expanded.Host - log.Debugf("Will connect to host %q according to proxy template.", expanded.Host) + logger.DebugContext(ctx, "Will connect to host as dictated by proxy template", "host", expanded.Host) if host, port, err := net.SplitHostPort(c.Host); err == nil { c.Host = host @@ -4253,12 +4281,12 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err } } } else if expanded.Query != "" { - log.Debugf("Will query for hosts via %q according to proxy template.", expanded.Query) + logger.DebugContext(cf.Context, "Will query for hosts as dictated by proxy template.", "query", expanded.Query) cf.PredicateExpression = expanded.Query // The PredicateExpression is ignored if the Host is populated. c.Host = "" } else if expanded.Search != "" { - log.Debugf("Will search for hosts via %q according to proxy template.", expanded.Search) + logger.DebugContext(cf.Context, "Will search for hosts as dictated by proxy template", "search", expanded.Search) cf.SearchKeywords = expanded.Search // The SearchKeywords are ignored if the Host is populated. c.Host = "" @@ -4267,12 +4295,12 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err // Don't overwrite proxy jump if explicitly provided if cf.ProxyJump == "" && expanded.Proxy != "" { cf.ProxyJump = expanded.Proxy - log.Debugf("Will connect to proxy %q according to proxy template.", expanded.Proxy) + logger.DebugContext(cf.Context, "Will connect to proxy as dictated by proxy template", "proxy", expanded.Proxy) } if expanded.Cluster != "" { cf.SiteName = expanded.Cluster - log.Debugf("Will connect to cluster %q according to proxy template.", expanded.Cluster) + logger.DebugContext(cf.Context, "Will connect to cluster as dictated by proxy template", "cluster", expanded.Cluster) } } @@ -4430,7 +4458,7 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err } if err := setX11Config(c, cf, options); err != nil { - log.WithError(err).Info("X11 forwarding is not properly configured, continuing without it.") + logger.InfoContext(ctx, "X11 forwarding is not properly configured, continuing without it", "error", err) } // If the caller does not want to check host keys, pass in a insecure host @@ -4469,7 +4497,7 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err // headless login produces short-lived MFA-verifed certs, which should never be added to the agent. if cf.AuthConnector == constants.HeadlessConnector { if cf.AddKeysToAgent == client.AddKeysToAgentYes || cf.AddKeysToAgent == client.AddKeysToAgentOnly { - log.Info("Skipping adding keys to agent for headless login") + logger.InfoContext(ctx, "Skipping adding keys to agent for headless login") } c.AddKeysToAgent = client.AddKeysToAgentNo } @@ -4693,7 +4721,7 @@ func setClientWebProxyAddr(ctx context.Context, cf *CLIConf, c *client.Config) e proxyAddress := parsedAddrs.WebProxyAddr if parsedAddrs.UsingDefaultWebProxyPort { - log.Debug("Web proxy port was not set. Attempting to detect port number to use.") + logger.DebugContext(ctx, "Web proxy port was not set, attempting to detect port number to use") timeout, cancel := context.WithTimeout(ctx, proxyDefaultResolutionTimeout) defer cancel() @@ -4844,7 +4872,7 @@ func printStatus(debug bool, p *profileInfo, env map[string]string, isActive boo if len(p.AllowedResourceIDs) > 0 { allowedResourcesStr, err := types.ResourceIDsToString(p.AllowedResourceIDs) if err != nil { - log.Warnf("failed to marshal allowed resource IDs to string: %v", err) + logger.WarnContext(context.Background(), "failed to marshal allowed resource IDs to string", "error", err) } else { fmt.Printf(" Allowed Resources: %s\n", allowedResourcesStr) } @@ -4974,7 +5002,7 @@ func onStatus(cf *CLIConf) error { // make the teleport client and retrieve the certificate from the proxy: tc, err := makeClient(cf) if err != nil { - log.WithError(err).Warn("Failed to make client for retrieving cluster alerts.") + logger.WarnContext(cf.Context, "Failed to make client for retrieving cluster alerts", "error", err) return trace.Wrap(err) } @@ -4985,7 +5013,7 @@ func onStatus(cf *CLIConf) error { var accessListsToReview []*accesslist.AccessList if hardwareKeyInteractionRequired { - log.Debug("Skipping fetching access lists to review due to Hardware Key PIN/Touch requirement.") + logger.DebugContext(cf.Context, "Skipping fetching access lists to review due to Hardware Key PIN/Touch requirement") } else { accessListsToReview = cf.getAccessListsToReview(tc) } @@ -5003,11 +5031,11 @@ func onStatus(cf *CLIConf) error { } if hardwareKeyInteractionRequired { - log.Debug("Skipping cluster alerts due to Hardware Key PIN/Touch requirement.") + logger.DebugContext(cf.Context, "Skipping cluster alerts due to Hardware Key PIN/Touch requirement") } else { if err := common.ShowClusterAlerts(cf.Context, tc, os.Stderr, nil, types.AlertSeverity_HIGH); err != nil { - log.WithError(err).Warn("Failed to display cluster alerts.") + logger.WarnContext(cf.Context, "Failed to display cluster alerts", "error", err) } } @@ -5231,7 +5259,7 @@ func awaitRequestResolution(ctx context.Context, clt authclient.ClientI, req typ case types.OpDelete: return nil, trace.Errorf("request %s has expired or been deleted...", event.Resource.GetName()) default: - log.Warnf("Skipping unknown event type %s", event.Type) + logger.WarnContext(ctx, "Skipping unknown event type", "event_type", event.Type) } case <-watcher.Done(): return nil, trace.Wrap(watcher.Error()) @@ -5393,11 +5421,11 @@ func listAppsAllClusters(cf *CLIConf) error { continue } - logger := log.WithField("cluster", cluster.name) + logger := logger.With("cluster", cluster.name) group.Go(func() error { servers, err := apiclient.GetAllResources[types.AppServer](groupCtx, cluster.auth, &cluster.req) if err != nil { - logger.Errorf("Failed to get app servers: %v.", err) + logger.ErrorContext(groupCtx, "Failed to get app servers", "error", err) mu.Lock() errors = append(errors, trace.ConnectionProblem(err, "failed to list app serves for cluster %s: %v", cluster.name, err)) @@ -5604,12 +5632,12 @@ func handleUnimplementedError(ctx context.Context, perr error, cf CLIConf) error ) tc, err := makeClient(&cf) if err != nil { - log.WithError(err).Warning("Failed to create client.") + logger.WarnContext(ctx, "Failed to create client", "error", err) return trace.WrapWithMessage(perr, errMsgFormat, unknownServerVersion, teleport.Version) } pr, err := tc.Ping(ctx) if err != nil { - log.WithError(err).Warning("Failed to call ping.") + logger.WarnContext(ctx, "Failed to call ping", "error", err) return trace.WrapWithMessage(perr, errMsgFormat, unknownServerVersion, teleport.Version) } return trace.WrapWithMessage(perr, errMsgFormat, pr.ServerVersion, teleport.Version) @@ -5697,7 +5725,7 @@ func onHeadlessApprove(cf *CLIConf) error { func (cf *CLIConf) getAccessListsToReview(tc *client.TeleportClient) []*accesslist.AccessList { clusterClient, err := tc.ConnectToCluster(cf.Context) if err != nil { - log.WithError(err).Debug("Error connecting to the cluster") + logger.DebugContext(cf.Context, "Error connecting to the cluster", "error", err) return nil } defer func() { @@ -5708,7 +5736,7 @@ func (cf *CLIConf) getAccessListsToReview(tc *client.TeleportClient) []*accessli // server, which does not support access lists. accessListsToReview, err := clusterClient.AuthClient.AccessListClient().GetAccessListsToReview(cf.Context) if err != nil && !trace.IsNotImplemented(err) { - log.WithError(err).Debug("Error getting access lists to review") + logger.DebugContext(cf.Context, "Error getting access lists to review", "error", err) } return accessListsToReview @@ -5755,7 +5783,7 @@ func tryLockMemory(cf *CLIConf) error { return trace.Wrap(err, mlockFailureMessage) case mlockModeBestEffort: err := mlock.LockMemory() - log.WithError(err).Warning(mlockFailureMessage) + logger.WarnContext(cf.Context, mlockFailureMessage, "error", err) return nil default: return trace.BadParameter("unexpected value for --mlock, expected one of (%v)", strings.Join(mlockModes, ", ")) diff --git a/tool/tsh/common/tsh_helper_test.go b/tool/tsh/common/tsh_helper_test.go index 380b012805d3c..85ec86097b3bd 100644 --- a/tool/tsh/common/tsh_helper_test.go +++ b/tool/tsh/common/tsh_helper_test.go @@ -230,7 +230,7 @@ func (s *suite) setupLeafCluster(t *testing.T, options testSuiteOptions) { tunnelAddr = s.root.Config.Proxy.ReverseTunnelListenAddr.String() } - tc, err := types.NewTrustedCluster("root-cluster", types.TrustedClusterSpecV2{ + tc, err := types.NewTrustedCluster(s.root.Config.Auth.ClusterName.GetClusterName(), types.TrustedClusterSpecV2{ Enabled: true, Token: staticToken, ProxyAddress: s.root.Config.Proxy.WebAddr.String(), @@ -249,7 +249,7 @@ func (s *suite) setupLeafCluster(t *testing.T, options testSuiteOptions) { } s.leaf = runTeleport(t, cfg) - _, err = s.leaf.GetAuthServer().UpsertTrustedCluster(s.leaf.ExitContext(), tc) + _, err = s.leaf.GetAuthServer().UpsertTrustedClusterV2(s.leaf.ExitContext(), tc) require.NoError(t, err) } diff --git a/tool/tsh/common/tsh_test.go b/tool/tsh/common/tsh_test.go index 792c507b5c55d..2ffa313d42cb1 100644 --- a/tool/tsh/common/tsh_test.go +++ b/tool/tsh/common/tsh_test.go @@ -2542,18 +2542,15 @@ func TestSSHCommands(t *testing.T) { func tryCreateTrustedCluster(t *testing.T, authServer *auth.Server, trustedCluster types.TrustedCluster) { ctx := context.TODO() for i := 0; i < 10; i++ { - log.Debugf("Will create trusted cluster %v, attempt %v.", trustedCluster, i) - _, err := authServer.UpsertTrustedCluster(ctx, trustedCluster) + _, err := authServer.UpsertTrustedClusterV2(ctx, trustedCluster) if err == nil { return } if trace.IsConnectionProblem(err) { - log.Debugf("Retrying on connection problem: %v.", err) time.Sleep(500 * time.Millisecond) continue } if trace.IsAccessDenied(err) { - log.Debugf("Retrying on access denied: %v.", err) time.Sleep(500 * time.Millisecond) continue } diff --git a/tool/tsh/common/workload_identity_test.go b/tool/tsh/common/workload_identity_test.go index e9eedeac8ab9f..04e73ce18f7e1 100644 --- a/tool/tsh/common/workload_identity_test.go +++ b/tool/tsh/common/workload_identity_test.go @@ -61,7 +61,7 @@ func TestWorkloadIdentityIssue(t *testing.T) { }), ) - homeDir, _ := mustLogin(t, s) + homeDir, _ := mustLoginLegacy(t, s) temp := t.TempDir() err = Run( ctx, diff --git a/tsconfig.json b/tsconfig.json index 233b31f68ee13..0bee31f133753 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,8 @@ "noEmit": true, "types": ["node", "@types/wicg-file-system-access"], "paths": { + "build/*": ["web/packages/build/src/*"], + "build": ["web/packages/build/src/"], "shared/*": ["web/packages/shared/*"], "design/*": ["web/packages/design/src/*"], "design": ["web/packages/design/src/"], @@ -28,7 +30,7 @@ "teleport": ["web/packages/teleport/src/"], "teleterm/*": ["web/packages/teleterm/src/*"], "e-teleport/*": ["e/web/teleport/src/*"], - "gen-proto-ts/*": ["gen/proto/ts/*"], + "gen-proto-ts/*": ["gen/proto/ts/*"] } }, "exclude": [ diff --git a/web/.storybook/preview.tsx b/web/.storybook/preview.tsx index 75c8e49b40522..052e1d986005e 100644 --- a/web/.storybook/preview.tsx +++ b/web/.storybook/preview.tsx @@ -16,23 +16,21 @@ * along with this program. If not, see . */ -import { ComponentType, PropsWithChildren } from 'react'; - import { Preview } from '@storybook/react'; - import { initialize, mswLoader } from 'msw-storybook-addon'; +import { ComponentType, PropsWithChildren } from 'react'; +import Box from '../packages/design/src/Box'; import { bblpTheme, darkTheme, lightTheme } from '../packages/design/src/theme'; +import { Theme } from '../packages/design/src/theme/themes/types'; import { ConfiguredThemeProvider } from '../packages/design/src/ThemeProvider'; -import Box from '../packages/design/src/Box'; +import history from '../packages/teleport/src/services/history/history'; +import { UserContextProvider } from '../packages/teleport/src/User'; import { StaticThemeProvider as TeletermThemeProvider } from '../packages/teleterm/src/ui/ThemeProvider'; import { darkTheme as teletermDarkTheme, lightTheme as teletermLightTheme, } from '../packages/teleterm/src/ui/ThemeProvider/theme'; -import history from '../packages/teleport/src/services/history/history'; -import { UserContextProvider } from '../packages/teleport/src/User'; -import { Theme } from '../packages/design/src/theme/themes/types'; initialize(); diff --git a/web/.storybook/vite.config.mts b/web/.storybook/vite.config.mts index 7c20b2fb85432..3bd030caba261 100644 --- a/web/.storybook/vite.config.mts +++ b/web/.storybook/vite.config.mts @@ -1,6 +1,7 @@ import { defineConfig } from 'vite'; -import { tsconfigPathsPlugin } from '@gravitational/build/vite/tsconfigPaths.mjs'; + import { reactPlugin } from '@gravitational/build/vite/react.mjs'; +import { tsconfigPathsPlugin } from '@gravitational/build/vite/tsconfigPaths.mjs'; export default defineConfig(({ mode }) => ({ plugins: [tsconfigPathsPlugin(), reactPlugin(mode)], diff --git a/web/@types/styled-components.d.ts b/web/@types/styled-components.d.ts index 7d98d7fc8d0d3..62714bfd3dceb 100644 --- a/web/@types/styled-components.d.ts +++ b/web/@types/styled-components.d.ts @@ -17,6 +17,7 @@ */ import { CSSProp } from 'styled-components'; + import 'react'; import { Theme } from 'design/theme/themes/types'; diff --git a/web/README.md b/web/README.md index 2c8d892a2898c..7799ee04730bf 100644 --- a/web/README.md +++ b/web/README.md @@ -190,7 +190,7 @@ pnpm dlx browserslist 'last 2 chrome version, last 2 edge version, last 2 firefo // Set the default "editor.formatOnSave": false, // absolute config path - "prettier.configPath": ".prettierrc", + "prettier.configPath": ".prettierrc.js", // enable per-language "[html]": { "editor.formatOnSave": true, diff --git a/web/packages/build/eslint.config.mjs b/web/packages/build/eslint.config.mjs index 35716d5fb7f6c..8b0ea9b2216c1 100644 --- a/web/packages/build/eslint.config.mjs +++ b/web/packages/build/eslint.config.mjs @@ -20,7 +20,6 @@ import eslint from '@eslint/js'; import tseslint from 'typescript-eslint'; import reactPlugin from 'eslint-plugin-react'; import reactHooksPlugin from 'eslint-plugin-react-hooks'; -import importPlugin from 'eslint-plugin-import'; import jestPlugin from 'eslint-plugin-jest'; import testingLibraryPlugin from 'eslint-plugin-testing-library'; import jestDomPlugin from 'eslint-plugin-jest-dom'; @@ -32,6 +31,7 @@ export default tseslint.config( '**/dist/**', '**/*_pb.*', '.eslintrc.js', + '.prettierrc.js', '**/tshd/**/*_pb.js', // WASM generated files 'web/packages/teleport/src/ironrdp/pkg', @@ -42,9 +42,6 @@ export default tseslint.config( ...tseslint.configs.recommended, reactPlugin.configs.flat.recommended, reactPlugin.configs.flat['jsx-runtime'], - importPlugin.flatConfigs.errors, - importPlugin.flatConfigs.warnings, - importPlugin.flatConfigs.typescript, { settings: { react: { @@ -68,25 +65,6 @@ export default tseslint.config( }, rules: { ...reactHooksPlugin.configs.recommended.rules, - 'import/order': [ - 'error', - { - groups: [ - 'builtin', - 'external', - 'internal', - 'parent', - 'sibling', - 'index', - 'object', - 'type', - ], - 'newlines-between': 'always-and-inside-groups', - }, - ], - // typescript-eslint recommends to turn import/no-unresolved off. - // https://typescript-eslint.io/troubleshooting/typed-linting/performance/#eslint-plugin-import - 'import/no-unresolved': 'off', '@typescript-eslint/no-unused-expressions': [ 'error', { allowShortCircuit: true, allowTernary: true, enforceForJSX: true }, diff --git a/web/packages/build/jest/jest-environment-patched-jsdom.js b/web/packages/build/jest/jest-environment-patched-jsdom.js index 7240299f7138a..c5d45a7bdc92b 100644 --- a/web/packages/build/jest/jest-environment-patched-jsdom.js +++ b/web/packages/build/jest/jest-environment-patched-jsdom.js @@ -1,6 +1,6 @@ -import { TextEncoder, TextDecoder } from 'node:util'; -import { BroadcastChannel } from 'node:worker_threads'; import { TransformStream } from 'node:stream/web'; +import { TextDecoder, TextEncoder } from 'node:util'; +import { BroadcastChannel } from 'node:worker_threads'; import { TestEnvironment as JSDOMEnvironment } from 'jest-environment-jsdom'; @@ -59,6 +59,13 @@ export default class PatchedJSDOMEnvironment extends JSDOMEnvironment { if (!global.TransformStream) { global.TransformStream = TransformStream; } + // TODO(gzdunek): JSDOM doesn't support AbortSignal.any(). + // Overwriting only this function doesn't help much, something between + // AbortSignal and AbortController is missing. + if (!global.AbortSignal.any) { + global.AbortSignal = AbortSignal; + global.AbortController = AbortController; + } } } export const TestEnvironment = PatchedJSDOMEnvironment; diff --git a/web/packages/build/package.json b/web/packages/build/package.json index 65599166c1fed..acbe180d9f880 100644 --- a/web/packages/build/package.json +++ b/web/packages/build/package.json @@ -12,28 +12,28 @@ "dependencies": { "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", - "@babel/preset-react": "^7.25.9", + "@babel/preset-react": "^7.26.3", "@babel/preset-typescript": "^7.26.0", - "@eslint/js": "^9.16.0", - "@swc/core": "^1.9.3", - "@swc/plugin-styled-components": "^5.0.0", + "@eslint/js": "^9.17.0", + "@ianvs/prettier-plugin-sort-imports": "^4.4.0", + "@swc/core": "^1.10.4", + "@swc/plugin-styled-components": "^6.0.2", "@types/jsdom": "^21.1.7", "@vitejs/plugin-react-swc": "^3.7.2", "babel-plugin-styled-components": "^2.1.4", - "eslint": "^9.16.0", - "eslint-plugin-import": "2.31.0", - "eslint-plugin-jest": "^28.9.0", + "eslint": "^9.17.0", + "eslint-plugin-jest": "^28.10.0", "eslint-plugin-jest-dom": "^5.5.0", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react": "^7.37.3", + "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-testing-library": "^7.1.1", - "globals": "^15.13.0", + "globals": "^15.14.0", "jest-environment-jsdom": "^29.7.0", "jest-fail-on-console": "^3.3.1", "jsdom": "^25.0.1", - "rollup-plugin-visualizer": "^5.12.0", - "typescript-eslint": "^8.18.0", - "vite-plugin-wasm": "^3.3.0", - "vite-tsconfig-paths": "^5.1.3" + "rollup-plugin-visualizer": "^5.13.1", + "typescript-eslint": "^8.19.0", + "vite-plugin-wasm": "^3.4.1", + "vite-tsconfig-paths": "^5.1.4" } } diff --git a/web/packages/build/vite/config.ts b/web/packages/build/vite/config.ts index 58b23d10453e4..0d15db5fe3dbc 100644 --- a/web/packages/build/vite/config.ts +++ b/web/packages/build/vite/config.ts @@ -19,17 +19,15 @@ import { existsSync, readFileSync } from 'fs'; import { resolve } from 'path'; -import { defineConfig } from 'vite'; import { visualizer } from 'rollup-plugin-visualizer'; +import { defineConfig, type UserConfig } from 'vite'; import wasm from 'vite-plugin-wasm'; -import { htmlPlugin, transformPlugin } from './html'; import { generateAppHashFile } from './apphash'; +import { htmlPlugin, transformPlugin } from './html'; import { reactPlugin } from './react.mjs'; import { tsconfigPathsPlugin } from './tsconfigPaths.mjs'; -import type { UserConfig } from 'vite'; - const DEFAULT_PROXY_TARGET = '127.0.0.1:3080'; const ENTRY_FILE_NAME = 'app/app.js'; diff --git a/web/packages/build/vite/html.ts b/web/packages/build/vite/html.ts index eaf802aa34f56..34bfdfe124d68 100644 --- a/web/packages/build/vite/html.ts +++ b/web/packages/build/vite/html.ts @@ -17,13 +17,12 @@ */ import { readFileSync } from 'fs'; +import type { IncomingHttpHeaders } from 'http'; import { get } from 'https'; import { resolve } from 'path'; import { JSDOM } from 'jsdom'; - import type { Plugin } from 'vite'; -import type { IncomingHttpHeaders } from 'http'; function getHTML(target: string, headers: IncomingHttpHeaders) { return new Promise<{ data: string; headers: IncomingHttpHeaders }>( diff --git a/web/packages/design/src/Alert/Alert.story.tsx b/web/packages/design/src/Alert/Alert.story.tsx index 0fe38d2d9b2e4..aaa83b3bafd00 100644 --- a/web/packages/design/src/Alert/Alert.story.tsx +++ b/web/packages/design/src/Alert/Alert.story.tsx @@ -21,7 +21,6 @@ import { action } from '@storybook/addon-actions'; import { Restore } from 'design/Icon'; import { Box } from '..'; - import { Alert, AlertProps, Banner } from './Alert'; export default { diff --git a/web/packages/design/src/Alert/Alert.tsx b/web/packages/design/src/Alert/Alert.tsx index f8ce4b7269310..bfc58e4610850 100644 --- a/web/packages/design/src/Alert/Alert.tsx +++ b/web/packages/design/src/Alert/Alert.tsx @@ -18,20 +18,19 @@ import React, { useState } from 'react'; import styled, { useTheme } from 'styled-components'; -import { style, color, ColorProps } from 'styled-system'; +import { color, ColorProps, style } from 'styled-system'; import { IconProps } from 'design/Icon/Icon'; - import { StatusIcon, StatusKind } from 'design/StatusIcon'; -import { space, SpaceProps, width, WidthProps } from '../system'; -import { Theme } from '../theme'; -import * as Icon from '../Icon'; -import Flex from '../Flex'; -import Text from '../Text'; import Box from '../Box'; -import { ButtonFill, ButtonIntent, Button } from '../Button'; +import { Button, ButtonFill, ButtonIntent } from '../Button'; import ButtonIcon from '../ButtonIcon'; +import Flex from '../Flex'; +import * as Icon from '../Icon'; +import { space, SpaceProps, width, WidthProps } from '../system'; +import Text from '../Text'; +import { Theme } from '../theme'; const linkColor = style({ prop: 'linkColor', diff --git a/web/packages/design/src/Button/Button.test.tsx b/web/packages/design/src/Button/Button.test.tsx index 8f4528e102baf..034b1f1929b07 100644 --- a/web/packages/design/src/Button/Button.test.tsx +++ b/web/packages/design/src/Button/Button.test.tsx @@ -22,14 +22,14 @@ import React, { PropsWithChildren, } from 'react'; -import { render, theme, screen } from 'design/utils/testing'; +import { render, screen, theme } from 'design/utils/testing'; import { Button, ButtonPrimary, ButtonSecondary, - ButtonWarning, ButtonSize, + ButtonWarning, } from './index'; describe('design/Button', () => { diff --git a/web/packages/design/src/Button/Button.tsx b/web/packages/design/src/Button/Button.tsx index 7aba6caa5e2aa..45d3d11b6cd3a 100644 --- a/web/packages/design/src/Button/Button.tsx +++ b/web/packages/design/src/Button/Button.tsx @@ -19,21 +19,20 @@ import React from 'react'; import styled, { CSSObject } from 'styled-components'; -import { shouldForwardProp as defaultValidatorFn } from 'design/ThemeProvider'; - import { - space, - width, - height, alignSelf, + AlignSelfProps, gap, + GapProps, + height, + HeightProps, + space, SpaceProps, + width, WidthProps, - HeightProps, - AlignSelfProps, - GapProps, } from 'design/system'; import { Theme } from 'design/theme/themes/types'; +import { shouldForwardProp as defaultValidatorFn } from 'design/ThemeProvider'; export type ButtonProps = React.ComponentPropsWithoutRef & diff --git a/web/packages/design/src/Button/buttons.story.tsx b/web/packages/design/src/Button/buttons.story.tsx index 1ad26d1a361ad..6a88df3397d51 100644 --- a/web/packages/design/src/Button/buttons.story.tsx +++ b/web/packages/design/src/Button/buttons.story.tsx @@ -17,28 +17,25 @@ */ import { Fragment } from 'react'; - import styled from 'styled-components'; import { Input, MenuItem } from 'design'; -import ButtonLink from '../ButtonLink'; -import ButtonIcon from '../ButtonIcon'; -import * as icons from '../Icon'; -import Flex from '../Flex'; - -import { ButtonWithMenu } from '../ButtonWithMenu'; - import { Button, + ButtonBorder, + ButtonFill, ButtonPrimary, + ButtonProps, ButtonSecondary, - ButtonWarning, - ButtonBorder, ButtonText, - ButtonProps, - ButtonFill, + ButtonWarning, } from '.'; +import ButtonIcon from '../ButtonIcon'; +import ButtonLink from '../ButtonLink'; +import { ButtonWithMenu } from '../ButtonWithMenu'; +import Flex from '../Flex'; +import * as icons from '../Icon'; export default { title: 'Design/Button', diff --git a/web/packages/design/src/ButtonIcon/ButtonIcon.jsx b/web/packages/design/src/ButtonIcon/ButtonIcon.jsx index 7a73900fe8e6d..1659b991719e0 100644 --- a/web/packages/design/src/ButtonIcon/ButtonIcon.jsx +++ b/web/packages/design/src/ButtonIcon/ButtonIcon.jsx @@ -18,7 +18,7 @@ import styled from 'styled-components'; -import { space, color, alignSelf } from 'design/system'; +import { alignSelf, color, space } from 'design/system'; const sizeMap = { 0: { diff --git a/web/packages/design/src/ButtonIcon/index.js b/web/packages/design/src/ButtonIcon/index.js index 45a5cceadbd6b..d15f77cbf5be3 100644 --- a/web/packages/design/src/ButtonIcon/index.js +++ b/web/packages/design/src/ButtonIcon/index.js @@ -17,4 +17,5 @@ */ import ButtonIcon from './ButtonIcon'; + export default ButtonIcon; diff --git a/web/packages/design/src/ButtonLink/index.ts b/web/packages/design/src/ButtonLink/index.ts index f439d750b20d5..df0ab3c0ab209 100644 --- a/web/packages/design/src/ButtonLink/index.ts +++ b/web/packages/design/src/ButtonLink/index.ts @@ -17,4 +17,5 @@ */ import ButtonLink from './ButtonLink'; + export default ButtonLink; diff --git a/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.test.tsx b/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.test.tsx index 899a69ca1f9f9..76f50094345a3 100644 --- a/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.test.tsx +++ b/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.test.tsx @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -import { render, screen, userEvent } from 'design/utils/testing'; import { MenuItem } from 'design'; +import { render, screen, userEvent } from 'design/utils/testing'; import { ButtonWithMenu } from './ButtonWithMenu'; diff --git a/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.tsx b/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.tsx index c23de5a1961bc..6d53ae18947f7 100644 --- a/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.tsx +++ b/web/packages/design/src/ButtonWithMenu/ButtonWithMenu.tsx @@ -26,9 +26,9 @@ import { } from 'react'; import { ButtonBorder, Flex, Menu, MenuItem } from 'design'; +import { ButtonSize } from 'design/Button'; import * as icons from 'design/Icon'; import { IconProps } from 'design/Icon/Icon'; -import { ButtonSize } from 'design/Button'; /** * Displays a button with a menu to the right of it. Unlike with a regular