mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-28 18:06:34 +02:00
Compare commits
50 commits
Author | SHA1 | Date | |
---|---|---|---|
|
4dd5357fe3 | ||
|
9e4947c62f | ||
|
63ccf6ab93 | ||
|
b566661353 | ||
|
9f4b03f916 | ||
|
db221cb826 | ||
|
f1a9401ddc | ||
|
cb0e8aaf06 | ||
|
2e7d1c7f12 | ||
|
8738066ce4 | ||
|
e1d84a1dde | ||
|
e71fca76f2 | ||
|
a10b505386 | ||
|
395541775c | ||
|
e78cfc0687 | ||
|
62addcf2a5 | ||
|
4af9150b39 | ||
|
3891293fa7 | ||
|
cd731789be | ||
|
c7ffb95483 | ||
|
a1eb75a8fe | ||
|
5f95dd32db | ||
|
c848c225e8 | ||
|
c47055bece | ||
|
8d9f1bb38e | ||
|
c5716a6045 | ||
|
9161cac1eb | ||
|
e984d07a55 | ||
|
ce46562a48 | ||
|
1a199eb9f5 | ||
|
bed6770e16 | ||
|
38ca6d52b9 | ||
|
b188a168af | ||
|
e7675a5b2a | ||
|
a96ab2fe93 | ||
|
ab5f3ac7f3 | ||
|
bc263e3ee5 | ||
|
08623ef346 | ||
|
d6b02441b3 | ||
|
2795cc68aa | ||
|
8c6955dbe2 | ||
|
4c9398e95b | ||
|
5ef16bcd28 | ||
|
562101ae03 | ||
|
bdfc17d1ce | ||
|
c4a5502f49 | ||
|
e1eca4e97c | ||
|
9cd5160468 | ||
|
ad183873f4 | ||
|
b86c9931b1 |
265 changed files with 10374 additions and 4620 deletions
83
.github/CODE_OF_CONDUCT.md
vendored
Normal file
83
.github/CODE_OF_CONDUCT.md
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [info@pomerium.io](mailto:info@pomerium.io). All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
80
.github/CONTRIBUTING.md
vendored
Normal file
80
.github/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
# Contributing
|
||||
|
||||
First of all, thank you for considering contributing to Pomerium! You can have a direct impact on Pomerium by helping with its code or documentation.
|
||||
|
||||
- To contribute to Pomerium, open a [pull request](https://github.com/pomerium/pomerium/pulls) (PR) to the Pomerium repository.
|
||||
- To contribute to the documentation, open a [pull request](https://github.com/pomerium/documentation/pulls) (PR) to the documentation repository.
|
||||
|
||||
If you're new to our community, that's okay: **we gladly welcome pull requests from anyone, regardless of your native language or coding experience.**
|
||||
|
||||
## General
|
||||
|
||||
We try to hold contributions to a high standard for quality, so don't be surprised if we ask for revisions--even if it seems small or insignificant. Please don't take it personally. If your change is on the right track, we can guide you to make it mergeable.
|
||||
|
||||
Here are some of the expectations we have of contributors:
|
||||
|
||||
- If your change is more than just a minor alteration, **open an issue to propose your change first.** This way we can avoid confusion, coordinate what everyone is working on, and ensure that changes are in-line with the project's goals and the best interests of its users. If there's already an issue about it, comment on the existing issue to claim it.
|
||||
|
||||
- **Keep pull requests small.** Smaller PRs are more likely to be merged because they are easier to review! We might ask you to break up large PRs into smaller ones. [An example of what we DON'T do.](https://twitter.com/iamdevloper/status/397664295875805184)
|
||||
|
||||
- **Keep related commits together in a PR.** We do want pull requests to be small, but you should also keep multiple related commits in the same PR if they rely on each other.
|
||||
|
||||
- **Write tests.** Tests are essential! Written properly, they ensure your change works, and that other changes in the future won't break your change. CI checks should pass.
|
||||
|
||||
- **Benchmarks should be included for optimizations.** Optimizations sometimes make code harder to read or have changes that are less than obvious. They should be proven with benchmarks or profiling.
|
||||
|
||||
- **[Squash](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) insignificant commits.** Every commit should be significant. Commits which merely rewrite a comment or fix a typo can be combined into another commit that has more substance. Interactive rebase can do this, or a simpler way is `git reset --soft <diverging-commit>` then `git commit -s`.
|
||||
|
||||
- **Own your contributions.** Pomerium is a growing project, and it's much better when individual contributors help maintain their change after it is merged.
|
||||
|
||||
- **Use comments properly.** We expect good godoc comments for package-level functions, types, and values. Comments are also useful whenever the purpose for a line of code is not obvious.
|
||||
|
||||
- **Recommended reading**
|
||||
|
||||
- [CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments)
|
||||
- [Linus Torvalds describes a good commit message](https://gist.github.com/matthewhudson/1475276)
|
||||
- [Best Practices for Maintainers](https://opensource.guide/best-practices/)
|
||||
- [Shrinking Code Review](https://alexgaynor.net/2015/dec/29/shrinking-code-review/)
|
||||
|
||||
## Docs
|
||||
|
||||
Pomerium's documentation is available at <https://www.pomerium.com/docs>. If you find a typo, feel a section could be better described, or have an idea for a totally new application or section, don't hesitate to make a pull request change. There are few ways you can do this.
|
||||
|
||||
### Simple edits
|
||||
|
||||
The easiest way to fix minor documentation issues in Pomerium is to click on "Edit this page in Github" on any page.
|
||||
|
||||
Doing so will create a [fork](https://help.github.com/en/articles/fork-a-repo) of the project, allow you to [update the page](https://guides.github.com/features/mastering-markdown/), and create a [pull request](https://help.github.com/en/articles/about-pull-requests).
|
||||
|
||||
### Bigger changes
|
||||
|
||||
If you need to add a new page, or would like greater control over the editing process you can edit the docs similar to how you would make changes to the source code.
|
||||
|
||||
#### Pre-reqs
|
||||
|
||||
Before building the docs, you'll need to install the following pre-requisites.
|
||||
|
||||
1. [Node.js](https://nodejs.org/en/download/).
|
||||
2. [Yarn](https://yarnpkg.com/lang/en/docs).
|
||||
|
||||
#### Make changes
|
||||
|
||||
Once you have Nodejs and Yarn installed, simply run `yarn start` in a terminal which will install any required node packages as well as start up a development server. You should see something like the below, with a link to the local doc server.
|
||||
|
||||
```bash
|
||||
[SUCCESS] Docusaurus website is running at: http://localhost:3001/
|
||||
```
|
||||
|
||||
Once you have the development server up and running, any changes you make will automatically be reloaded and accessible in your browser.
|
||||
|
||||
### PR Previews
|
||||
|
||||
We use [Netlify](https://www.netlify.com) to build and host our docs. One of nice features of Netlify, is that a preview of the docs are automatically created for each new pull request that is made, which lets you be sure that the version of your docs that you see locally match what will ultimately be deployed in production.
|
||||
|
||||
[configuration variables]: /docs/reference
|
||||
[download]: https://github.com/pomerium/pomerium/releases
|
||||
[environmental configuration variables]: https://12factor.net/config
|
||||
[verify]: https://verify.pomerium.com/
|
||||
[identity provider]: /docs/identity-providers
|
||||
[make]: https://en.wikipedia.org/wiki/Make_(software)
|
||||
[tls certificates]: /docs/concepts/certificates
|
2
.github/workflows/docker-main.yaml
vendored
2
.github/workflows/docker-main.yaml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
|||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
|
|
@ -5,6 +5,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- 0-[0-9]+-*
|
||||
- experimental/*
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
|
@ -32,7 +33,7 @@ jobs:
|
|||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
|
8
.github/workflows/lint.yaml
vendored
8
.github/workflows/lint.yaml
vendored
|
@ -13,14 +13,14 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
|
||||
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34
|
||||
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
go-version: 1.24.x
|
||||
cache: false
|
||||
|
||||
- run: make deps-build
|
||||
|
||||
- uses: golangci/golangci-lint-action@2226d7cb06a077cd73e56eedd38eecad18e5d837
|
||||
- uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84
|
||||
with:
|
||||
version: v1.60.1
|
||||
version: v1.64.8
|
||||
args: --timeout=10m
|
||||
|
|
12
.github/workflows/release.yaml
vendored
12
.github/workflows/release.yaml
vendored
|
@ -26,14 +26,14 @@ jobs:
|
|||
run: git fetch --prune --unshallow
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 22.x
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
go-version: 1.24.x
|
||||
cache: false
|
||||
|
||||
- name: Set up Docker
|
||||
|
@ -57,7 +57,7 @@ jobs:
|
|||
run: gcloud auth configure-docker
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6.2.1
|
||||
uses: goreleaser/goreleaser-action@v6.3.0
|
||||
with:
|
||||
version: v0.184.0
|
||||
args: release --config .github/goreleaser.yaml
|
||||
|
@ -99,7 +99,7 @@ jobs:
|
|||
echo "tag=${LATEST_TAG}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish latest tag
|
||||
if: "steps.latestTag.outputs.tag == steps.tagName.outputs.tag"
|
||||
if: steps.latestTag.outputs.tag == steps.tagName.outputs.tag
|
||||
run: |
|
||||
docker manifest create -a pomerium/pomerium:latest pomerium/pomerium:amd64-${{ steps.tagName.outputs.tag }} pomerium/pomerium:arm64v8-${{ steps.tagName.outputs.tag }}
|
||||
docker manifest push pomerium/pomerium:latest
|
||||
|
|
21
.github/workflows/test.yaml
vendored
21
.github/workflows/test.yaml
vendored
|
@ -12,7 +12,6 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go-version: [1.23.x]
|
||||
node-version: [22.x]
|
||||
platform: [ubuntu-22.04]
|
||||
deployment: [multi, single]
|
||||
|
@ -21,12 +20,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
|
||||
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34
|
||||
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
go-version: 1.24.x
|
||||
cache: false
|
||||
|
||||
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: yarn
|
||||
|
@ -60,11 +59,11 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
|
||||
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34
|
||||
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
go-version: 1.24.x
|
||||
|
||||
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: yarn
|
||||
|
@ -76,7 +75,7 @@ jobs:
|
|||
make build
|
||||
|
||||
- name: save binary
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
path: bin/pomerium*
|
||||
name: pomerium ${{ github.run_id }} ${{ matrix.platform }}
|
||||
|
@ -124,12 +123,12 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34
|
||||
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
go-version: 1.24.x
|
||||
cache: false
|
||||
|
||||
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
|
|
|
@ -11,11 +11,11 @@ linters:
|
|||
enable:
|
||||
- asasalint
|
||||
- bodyclose
|
||||
- copyloopvar
|
||||
- dogsled
|
||||
- errcheck
|
||||
- errorlint
|
||||
- exportloopref
|
||||
# - gci # https://github.com/daixiang0/gci/issues/209
|
||||
- gci
|
||||
- gocheckcompilerdirectives
|
||||
- gofumpt
|
||||
- goimports
|
||||
|
@ -30,10 +30,10 @@ linters:
|
|||
- revive
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- tenv
|
||||
- unconvert
|
||||
- unused
|
||||
- usestdlibvars
|
||||
- usetesting
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
golang 1.23.0
|
||||
golangci-lint 1.60.1
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:lts-bookworm@sha256:f6b9c31ace05502dd98ef777aaa20464362435dcc5e312b0e213121dcf7d8b95 AS ui
|
||||
FROM node:lts-bookworm@sha256:c7fd844945a76eeaa83cb372e4d289b4a30b478a1c80e16c685b62c54156285b AS ui
|
||||
WORKDIR /build
|
||||
|
||||
COPY .git ./.git
|
||||
|
@ -13,7 +13,7 @@ RUN make yarn
|
|||
COPY ./ui/ ./ui/
|
||||
RUN make build-ui
|
||||
|
||||
FROM golang:1.24-bookworm@sha256:d7d795d0a9f51b00d9c9bfd17388c2c626004a50c6ed7c581e095122507fe1ab AS build
|
||||
FROM golang:1.24-bookworm@sha256:fa1a01d362a7b9df68b021d59a124d28cae6d99ebd1a876e3557c4dd092f1b1d AS build
|
||||
WORKDIR /go/src/github.com/pomerium/pomerium
|
||||
|
||||
RUN apt-get update \
|
||||
|
@ -29,7 +29,7 @@ COPY --from=ui /build/ui/dist ./ui/dist
|
|||
RUN make build-go NAME=pomerium
|
||||
RUN touch /config.yaml
|
||||
|
||||
FROM gcr.io/distroless/base-debian12:debug@sha256:3a59a8d10471fc8487fd2ca93746b0195ed4c3236c14fe8412cf7b2ec4b8c1f3
|
||||
FROM gcr.io/distroless/base-debian12:debug@sha256:02be0066ee51d3d8a77be84e7136df6b9946c46e114aa2ffc5f08027cc5840ff
|
||||
ENV AUTOCERT_DIR=/data/autocert
|
||||
WORKDIR /pomerium
|
||||
COPY --from=build /go/src/github.com/pomerium/pomerium/bin/* /bin/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:lts-bookworm@sha256:f6b9c31ace05502dd98ef777aaa20464362435dcc5e312b0e213121dcf7d8b95 AS ui
|
||||
FROM node:lts-bookworm@sha256:c7fd844945a76eeaa83cb372e4d289b4a30b478a1c80e16c685b62c54156285b AS ui
|
||||
WORKDIR /build
|
||||
|
||||
COPY .git ./.git
|
||||
|
@ -13,7 +13,7 @@ RUN make yarn
|
|||
COPY ./ui/ ./ui/
|
||||
RUN make build-ui
|
||||
|
||||
FROM golang:1.24-bookworm@sha256:d7d795d0a9f51b00d9c9bfd17388c2c626004a50c6ed7c581e095122507fe1ab AS build
|
||||
FROM golang:1.24-bookworm@sha256:fa1a01d362a7b9df68b021d59a124d28cae6d99ebd1a876e3557c4dd092f1b1d AS build
|
||||
WORKDIR /go/src/github.com/pomerium/pomerium
|
||||
|
||||
RUN apt-get update \
|
||||
|
|
7
Makefile
7
Makefile
|
@ -90,9 +90,10 @@ build-ui: yarn
|
|||
@cd ui; yarn build
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Verifies `golint` passes.
|
||||
@echo "==> $@"
|
||||
@go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.1 run ./... --fix
|
||||
lint:
|
||||
@echo "@==> $@"
|
||||
@VERSION=$$(go run github.com/mikefarah/yq/v4@v4.34.1 '.jobs.lint.steps[] | select(.uses == "golangci/golangci-lint-action*") | .with.version' .github/workflows/lint.yaml) && \
|
||||
go run github.com/golangci/golangci-lint/cmd/golangci-lint@$$VERSION run ./... --fix
|
||||
|
||||
.PHONY: test
|
||||
test: get-envoy ## Runs the go tests.
|
||||
|
|
|
@ -7,12 +7,13 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/atomicutil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// ValidateOptions checks that configuration are complete and valid.
|
||||
|
|
|
@ -58,7 +58,6 @@ func TestOptions_Validate(t *testing.T) {
|
|||
{"empty callback path", badCallbackPath, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := ValidateOptions(tt.o); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Options.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
@ -105,7 +104,6 @@ func TestNew(t *testing.T) {
|
|||
{"bad signing key", badSigningKey, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := New(context.Background(), &config.Config{Options: tt.opts})
|
||||
if (err != nil) != tt.wantErr {
|
||||
|
|
|
@ -3,11 +3,12 @@ package authenticate
|
|||
import (
|
||||
"context"
|
||||
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/pomerium/pomerium/authenticate/events"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
identitypb "github.com/pomerium/pomerium/pkg/grpc/identity"
|
||||
"github.com/pomerium/pomerium/pkg/identity"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
type authenticateConfig struct {
|
||||
|
|
|
@ -25,11 +25,11 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/middleware"
|
||||
"github.com/pomerium/pomerium/internal/sessions"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/identity"
|
||||
"github.com/pomerium/pomerium/pkg/identity/oidc"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// Handler returns the authenticate service's handler chain.
|
||||
|
@ -117,9 +117,6 @@ func (a *Authenticate) mountDashboard(r *mux.Router) {
|
|||
handlers.DeviceEnrolled(a.getUserInfoData(r)).ServeHTTP(w, r)
|
||||
return nil
|
||||
}))
|
||||
|
||||
cr := sr.PathPrefix("/callback").Subrouter()
|
||||
cr.Path("/").Handler(a.requireValidSignature(a.Callback)).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
// RetrieveSession is the middleware used retrieve session by the sessionLoader
|
||||
|
@ -141,7 +138,7 @@ func (a *Authenticate) VerifySession(next http.Handler) http.Handler {
|
|||
if err != nil {
|
||||
log.FromRequest(r).Info().
|
||||
Err(err).
|
||||
Str("idp_id", idpID).
|
||||
Str("idp-id", idpID).
|
||||
Msg("authenticate: session load error")
|
||||
span.AddEvent("session load error",
|
||||
oteltrace.WithAttributes(attribute.String("error", err.Error())))
|
||||
|
@ -150,8 +147,8 @@ func (a *Authenticate) VerifySession(next http.Handler) http.Handler {
|
|||
|
||||
if sessionState.IdentityProviderID != idpID {
|
||||
log.FromRequest(r).Info().
|
||||
Str("idp_id", idpID).
|
||||
Str("session_idp_id", sessionState.IdentityProviderID).
|
||||
Str("idp-id", idpID).
|
||||
Str("session-idp-id", sessionState.IdentityProviderID).
|
||||
Str("id", sessionState.ID).
|
||||
Msg("authenticate: session not associated with identity provider")
|
||||
span.AddEvent("session not associated with identity provider")
|
||||
|
@ -161,7 +158,7 @@ func (a *Authenticate) VerifySession(next http.Handler) http.Handler {
|
|||
if err := state.flow.VerifySession(ctx, r, sessionState); err != nil {
|
||||
log.FromRequest(r).Info().
|
||||
Err(err).
|
||||
Str("idp_id", idpID).
|
||||
Str("idp-id", idpID).
|
||||
Msg("authenticate: couldn't verify session")
|
||||
span.AddEvent("couldn't verify session",
|
||||
oteltrace.WithAttributes(attribute.String("error", err.Error())))
|
||||
|
@ -526,54 +523,6 @@ func (a *Authenticate) revokeSession(ctx context.Context, w http.ResponseWriter,
|
|||
return state.flow.RevokeSession(ctx, r, authenticator, sessionState)
|
||||
}
|
||||
|
||||
// Callback handles the result of a successful call to the authenticate service
|
||||
// and is responsible setting per-route sessions.
|
||||
func (a *Authenticate) Callback(w http.ResponseWriter, r *http.Request) error {
|
||||
redirectURLString := r.FormValue(urlutil.QueryRedirectURI)
|
||||
encryptedSession := r.FormValue(urlutil.QuerySessionEncrypted)
|
||||
|
||||
redirectURL, err := urlutil.ParseAndValidateURL(redirectURLString)
|
||||
if err != nil {
|
||||
return httputil.NewError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
rawJWT, err := a.saveCallbackSession(w, r, encryptedSession)
|
||||
if err != nil {
|
||||
return httputil.NewError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
// if programmatic, encode the session jwt as a query param
|
||||
if isProgrammatic := r.FormValue(urlutil.QueryIsProgrammatic); isProgrammatic == "true" {
|
||||
q := redirectURL.Query()
|
||||
q.Set(urlutil.QueryPomeriumJWT, string(rawJWT))
|
||||
redirectURL.RawQuery = q.Encode()
|
||||
}
|
||||
httputil.Redirect(w, r, redirectURL.String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveCallbackSession takes an encrypted per-route session token, decrypts
|
||||
// it using the shared service key, then stores it the local session store.
|
||||
func (a *Authenticate) saveCallbackSession(w http.ResponseWriter, r *http.Request, enctoken string) ([]byte, error) {
|
||||
state := a.state.Load()
|
||||
|
||||
// 1. extract the base64 encoded and encrypted JWT from query params
|
||||
encryptedJWT, err := base64.URLEncoding.DecodeString(enctoken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("proxy: malfromed callback token: %w", err)
|
||||
}
|
||||
// 2. decrypt the JWT using the cipher using the _shared_ secret key
|
||||
rawJWT, err := cryptutil.Decrypt(state.sharedCipher, encryptedJWT, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("proxy: callback token decrypt error: %w", err)
|
||||
}
|
||||
// 3. Save the decrypted JWT to the session store directly as a string, without resigning
|
||||
if err = state.sessionStore.SaveSession(w, r, rawJWT); err != nil {
|
||||
return nil, fmt.Errorf("proxy: callback session save failure: %w", err)
|
||||
}
|
||||
return rawJWT, nil
|
||||
}
|
||||
|
||||
func (a *Authenticate) getIdentityProviderIDForRequest(r *http.Request) string {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return ""
|
||||
|
|
|
@ -219,7 +219,6 @@ func TestAuthenticate_SignOut(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
@ -343,7 +342,6 @@ func TestAuthenticate_OAuthCallback(t *testing.T) {
|
|||
{"bad hmac", http.MethodGet, time.Now().Unix(), base64.URLEncoding.EncodeToString([]byte("malformed_state")), "", "", "", "code", "https://corp.pomerium.io", "https://authenticate.pomerium.io", &mstore.Store{}, identity.MockProvider{AuthenticateResponse: oauth2.Token{}}, "https://corp.pomerium.io", http.StatusBadRequest},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
@ -449,7 +447,6 @@ func TestAuthenticate_SessionValidatorMiddleware(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
@ -694,14 +691,6 @@ func (m mockDataBrokerServiceClient) Put(ctx context.Context, in *databroker.Put
|
|||
return m.put(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func mustParseURL(rawurl string) *url.URL {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// stubFlow is a stub implementation of the flow interface.
|
||||
type stubFlow struct {
|
||||
verifySignatureErr error
|
||||
|
|
|
@ -20,14 +20,3 @@ func (a *Authenticate) requireValidSignatureOnRedirect(next httputil.HandlerFunc
|
|||
return next(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// requireValidSignature validates the pomerium_signature.
|
||||
func (a *Authenticate) requireValidSignature(next httputil.HandlerFunc) http.Handler {
|
||||
return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
err := a.state.Load().flow.VerifyAuthenticateSignature(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return next(w, r)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,33 +6,28 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/pomerium/datasource/pkg/directory"
|
||||
"github.com/pomerium/pomerium/authorize/evaluator"
|
||||
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/atomicutil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/metrics"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// Authorize struct holds
|
||||
type Authorize struct {
|
||||
state *atomicutil.Value[*authorizeState]
|
||||
store *store.Store
|
||||
currentConfig *atomicutil.Value[*config.Config]
|
||||
accessTracker *AccessTracker
|
||||
globalCache storage.Cache
|
||||
groupsCacheWarmer *cacheWarmer
|
||||
state *atomicutil.Value[*authorizeState]
|
||||
store *store.Store
|
||||
currentConfig *atomicutil.Value[*config.Config]
|
||||
accessTracker *AccessTracker
|
||||
|
||||
tracerProvider oteltrace.TracerProvider
|
||||
tracer oteltrace.Tracer
|
||||
|
@ -43,21 +38,19 @@ func New(ctx context.Context, cfg *config.Config) (*Authorize, error) {
|
|||
tracerProvider := trace.NewTracerProvider(ctx, "Authorize")
|
||||
tracer := tracerProvider.Tracer(trace.PomeriumCoreTracer)
|
||||
a := &Authorize{
|
||||
currentConfig: atomicutil.NewValue(&config.Config{Options: new(config.Options)}),
|
||||
currentConfig: atomicutil.NewValue(cfg),
|
||||
store: store.New(),
|
||||
globalCache: storage.NewGlobalCache(time.Minute),
|
||||
tracerProvider: tracerProvider,
|
||||
tracer: tracer,
|
||||
}
|
||||
a.accessTracker = NewAccessTracker(a, accessTrackerMaxSize, accessTrackerDebouncePeriod)
|
||||
|
||||
state, err := newAuthorizeStateFromConfig(ctx, tracerProvider, cfg, a.store, nil)
|
||||
state, err := newAuthorizeStateFromConfig(ctx, nil, tracerProvider, cfg, a.store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.state = atomicutil.NewValue(state)
|
||||
|
||||
a.groupsCacheWarmer = newCacheWarmer(state.dataBrokerClientConnection, a.globalCache, directory.GroupRecordType)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
|
@ -73,10 +66,6 @@ func (a *Authorize) Run(ctx context.Context) error {
|
|||
a.accessTracker.Run(ctx)
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
a.groupsCacheWarmer.Run(ctx)
|
||||
return nil
|
||||
})
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
|
@ -149,6 +138,7 @@ func newPolicyEvaluator(
|
|||
evaluator.WithGoogleCloudServerlessAuthenticationServiceAccount(opts.GetGoogleCloudServerlessAuthenticationServiceAccount()),
|
||||
evaluator.WithJWTClaimsHeaders(opts.JWTClaimsHeaders),
|
||||
evaluator.WithJWTGroupsFilter(opts.JWTGroupsFilter),
|
||||
evaluator.WithDefaultJWTIssuerFormat(opts.JWTIssuerFormat),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -156,13 +146,9 @@ func newPolicyEvaluator(
|
|||
func (a *Authorize) OnConfigChange(ctx context.Context, cfg *config.Config) {
|
||||
currentState := a.state.Load()
|
||||
a.currentConfig.Store(cfg)
|
||||
if newState, err := newAuthorizeStateFromConfig(ctx, a.tracerProvider, cfg, a.store, currentState.evaluator); err != nil {
|
||||
if newState, err := newAuthorizeStateFromConfig(ctx, currentState, a.tracerProvider, cfg, a.store); err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("authorize: error updating state")
|
||||
} else {
|
||||
a.state.Store(newState)
|
||||
|
||||
if currentState.dataBrokerClientConnection != newState.dataBrokerClientConnection {
|
||||
a.groupsCacheWarmer.UpdateConn(newState.dataBrokerClientConnection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,6 @@ func TestNew(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := New(context.Background(), &config.Config{Options: &tt.config})
|
||||
|
@ -104,7 +103,6 @@ func TestAuthorize_OnConfigChange(t *testing.T) {
|
|||
{"bad option", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", policies, false},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
o := &config.Options{
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
package authorize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
)
|
||||
|
||||
type cacheWarmer struct {
|
||||
cc *grpc.ClientConn
|
||||
cache storage.Cache
|
||||
typeURL string
|
||||
|
||||
updatedCC chan *grpc.ClientConn
|
||||
}
|
||||
|
||||
func newCacheWarmer(
|
||||
cc *grpc.ClientConn,
|
||||
cache storage.Cache,
|
||||
typeURL string,
|
||||
) *cacheWarmer {
|
||||
return &cacheWarmer{
|
||||
cc: cc,
|
||||
cache: cache,
|
||||
typeURL: typeURL,
|
||||
|
||||
updatedCC: make(chan *grpc.ClientConn, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (cw *cacheWarmer) UpdateConn(cc *grpc.ClientConn) {
|
||||
for {
|
||||
select {
|
||||
case cw.updatedCC <- cc:
|
||||
return
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case <-cw.updatedCC:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cw *cacheWarmer) Run(ctx context.Context) {
|
||||
// Run a syncer for the cache warmer until the underlying databroker connection is changed.
|
||||
// When that happens cancel the currently running syncer and start a new one.
|
||||
|
||||
runCtx, runCancel := context.WithCancel(ctx)
|
||||
go cw.run(runCtx, cw.cc)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
runCancel()
|
||||
return
|
||||
case cc := <-cw.updatedCC:
|
||||
log.Ctx(ctx).Info().Msg("cache-warmer: received updated databroker client connection, restarting syncer")
|
||||
cw.cc = cc
|
||||
runCancel()
|
||||
runCtx, runCancel = context.WithCancel(ctx)
|
||||
go cw.run(runCtx, cw.cc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cw *cacheWarmer) run(ctx context.Context, cc *grpc.ClientConn) {
|
||||
log.Ctx(ctx).Debug().Str("type-url", cw.typeURL).Msg("cache-warmer: running databroker syncer to warm cache")
|
||||
_ = databroker.NewSyncer(ctx, "cache-warmer", cacheWarmerSyncerHandler{
|
||||
client: databroker.NewDataBrokerServiceClient(cc),
|
||||
cache: cw.cache,
|
||||
}, databroker.WithTypeURL(cw.typeURL)).Run(ctx)
|
||||
}
|
||||
|
||||
type cacheWarmerSyncerHandler struct {
|
||||
client databroker.DataBrokerServiceClient
|
||||
cache storage.Cache
|
||||
}
|
||||
|
||||
func (h cacheWarmerSyncerHandler) GetDataBrokerServiceClient() databroker.DataBrokerServiceClient {
|
||||
return h.client
|
||||
}
|
||||
|
||||
func (h cacheWarmerSyncerHandler) ClearRecords(_ context.Context) {
|
||||
h.cache.InvalidateAll()
|
||||
}
|
||||
|
||||
func (h cacheWarmerSyncerHandler) UpdateRecords(ctx context.Context, serverVersion uint64, records []*databroker.Record) {
|
||||
for _, record := range records {
|
||||
req := &databroker.QueryRequest{
|
||||
Type: record.Type,
|
||||
Limit: 1,
|
||||
}
|
||||
req.SetFilterByIDOrIndex(record.Id)
|
||||
|
||||
res := &databroker.QueryResponse{
|
||||
Records: []*databroker.Record{record},
|
||||
TotalCount: 1,
|
||||
ServerVersion: serverVersion,
|
||||
RecordVersion: record.Version,
|
||||
}
|
||||
|
||||
expiry := time.Now().Add(time.Hour * 24 * 365)
|
||||
key, err := storage.MarshalQueryRequest(req)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("cache-warmer: failed to marshal query request")
|
||||
continue
|
||||
}
|
||||
value, err := storage.MarshalQueryResponse(res)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("cache-warmer: failed to marshal query response")
|
||||
continue
|
||||
}
|
||||
|
||||
h.cache.Set(expiry, key, value)
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package authorize
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/databroker"
|
||||
"github.com/pomerium/pomerium/internal/testutil"
|
||||
databrokerpb "github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/protoutil"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
)
|
||||
|
||||
func TestCacheWarmer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.GetContext(t, 10*time.Minute)
|
||||
cc := testutil.NewGRPCServer(t, func(srv *grpc.Server) {
|
||||
databrokerpb.RegisterDataBrokerServiceServer(srv, databroker.New(ctx, noop.NewTracerProvider()))
|
||||
})
|
||||
t.Cleanup(func() { cc.Close() })
|
||||
|
||||
client := databrokerpb.NewDataBrokerServiceClient(cc)
|
||||
_, err := client.Put(ctx, &databrokerpb.PutRequest{
|
||||
Records: []*databrokerpb.Record{
|
||||
{Type: "example.com/record", Id: "e1", Data: protoutil.NewAnyBool(true)},
|
||||
{Type: "example.com/record", Id: "e2", Data: protoutil.NewAnyBool(true)},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
cache := storage.NewGlobalCache(time.Minute)
|
||||
|
||||
cw := newCacheWarmer(cc, cache, "example.com/record")
|
||||
go cw.Run(ctx)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
req := &databrokerpb.QueryRequest{
|
||||
Type: "example.com/record",
|
||||
Limit: 1,
|
||||
}
|
||||
req.SetFilterByIDOrIndex("e1")
|
||||
res, err := storage.NewCachingQuerier(storage.NewStaticQuerier(), cache).Query(ctx, req)
|
||||
require.NoError(t, err)
|
||||
return len(res.GetRecords()) == 1
|
||||
}, 10*time.Second, time.Millisecond*100)
|
||||
}
|
|
@ -19,6 +19,7 @@ import (
|
|||
"google.golang.org/genproto/googleapis/rpc/status"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/pomerium/pomerium/authorize/checkrequest"
|
||||
"github.com/pomerium/pomerium/authorize/evaluator"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
|
@ -98,6 +99,9 @@ func (a *Authorize) handleResultDenied(
|
|||
case invalidClientCertReason(reasons):
|
||||
denyStatusCode = httputil.StatusInvalidClientCertificate
|
||||
denyStatusText = httputil.DetailsText(httputil.StatusInvalidClientCertificate)
|
||||
case request.Policy.IsMCP():
|
||||
denyStatusCode = http.StatusUnauthorized
|
||||
denyStatusText = httputil.DetailsText(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
return a.deniedResponse(ctx, in, denyStatusCode, denyStatusText, nil)
|
||||
|
@ -158,7 +162,7 @@ func (a *Authorize) deniedResponse(
|
|||
"code": code, // http code
|
||||
})
|
||||
headers.Set("Content-Type", "application/json")
|
||||
case getCheckRequestURL(in).Path == "/robots.txt":
|
||||
case checkrequest.GetURL(in).Path == "/robots.txt":
|
||||
code = 200
|
||||
respBody = []byte("User-agent: *\nDisallow: /")
|
||||
headers.Set("Content-Type", "text/plain")
|
||||
|
@ -216,7 +220,7 @@ func (a *Authorize) requireLoginResponse(
|
|||
options := a.currentConfig.Load().Options
|
||||
state := a.state.Load()
|
||||
|
||||
if !a.shouldRedirect(in) {
|
||||
if !a.shouldRedirect(in, request) {
|
||||
return a.deniedResponse(ctx, in, http.StatusUnauthorized, "Unauthenticated", nil)
|
||||
}
|
||||
|
||||
|
@ -226,7 +230,7 @@ func (a *Authorize) requireLoginResponse(
|
|||
}
|
||||
|
||||
// always assume https scheme
|
||||
checkRequestURL := getCheckRequestURL(in)
|
||||
checkRequestURL := checkrequest.GetURL(in)
|
||||
checkRequestURL.Scheme = "https"
|
||||
var signInURLQuery url.Values
|
||||
|
||||
|
@ -235,8 +239,12 @@ func (a *Authorize) requireLoginResponse(
|
|||
signInURLQuery = url.Values{}
|
||||
signInURLQuery.Add("pomerium_traceparent", id)
|
||||
}
|
||||
var additionalHosts []string
|
||||
if request.Policy != nil {
|
||||
additionalHosts = request.Policy.DependsOn
|
||||
}
|
||||
redirectTo, err := state.authenticateFlow.AuthenticateSignInURL(
|
||||
ctx, signInURLQuery, &checkRequestURL, idp.GetId())
|
||||
ctx, signInURLQuery, &checkRequestURL, idp.GetId(), additionalHosts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -255,7 +263,7 @@ func (a *Authorize) requireWebAuthnResponse(
|
|||
state := a.state.Load()
|
||||
|
||||
// always assume https scheme
|
||||
checkRequestURL := getCheckRequestURL(in)
|
||||
checkRequestURL := checkrequest.GetURL(in)
|
||||
checkRequestURL.Scheme = "https"
|
||||
|
||||
// If we're already on a webauthn route, return OK.
|
||||
|
@ -264,7 +272,7 @@ func (a *Authorize) requireWebAuthnResponse(
|
|||
return a.okResponse(result.Headers), nil
|
||||
}
|
||||
|
||||
if !a.shouldRedirect(in) {
|
||||
if !a.shouldRedirect(in, request) {
|
||||
return a.deniedResponse(ctx, in, http.StatusUnauthorized, "Unauthenticated", nil)
|
||||
}
|
||||
|
||||
|
@ -349,7 +357,11 @@ func (a *Authorize) userInfoEndpointURL(in *envoy_service_auth_v3.CheckRequest)
|
|||
return urlutil.NewSignedURL(a.state.Load().sharedKey, debugEndpoint).Sign(), nil
|
||||
}
|
||||
|
||||
func (a *Authorize) shouldRedirect(in *envoy_service_auth_v3.CheckRequest) bool {
|
||||
func (a *Authorize) shouldRedirect(in *envoy_service_auth_v3.CheckRequest, request *evaluator.Request) bool {
|
||||
if request.Policy.IsMCP() {
|
||||
return false
|
||||
}
|
||||
|
||||
requestHeaders := in.GetAttributes().GetRequest().GetHttp().GetHeaders()
|
||||
if requestHeaders == nil {
|
||||
return true
|
||||
|
|
|
@ -113,6 +113,18 @@ func TestAuthorize_handleResult(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, 495, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
||||
})
|
||||
t.Run("mcp-route-unauthenticated", func(t *testing.T) {
|
||||
res, err := a.handleResult(context.Background(),
|
||||
&envoy_service_auth_v3.CheckRequest{},
|
||||
&evaluator.Request{
|
||||
Policy: &config.Policy{MCP: &config.MCP{}},
|
||||
},
|
||||
&evaluator.Result{
|
||||
Allow: evaluator.NewRuleResult(false, criteria.ReasonUserUnauthenticated),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 401, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthorize_okResponse(t *testing.T) {
|
||||
|
|
44
authorize/checkrequest/checkrequest.go
Normal file
44
authorize/checkrequest/checkrequest.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Package checkrequest contains helper functions for working with Envoy
|
||||
// ext_authz CheckRequest messages.
|
||||
package checkrequest
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
)
|
||||
|
||||
// GetURL converts the request URL from an ext_authz CheckRequest to a [url.URL].
|
||||
func GetURL(req *envoy_service_auth_v3.CheckRequest) url.URL {
|
||||
h := req.GetAttributes().GetRequest().GetHttp()
|
||||
u := url.URL{
|
||||
Scheme: h.GetScheme(),
|
||||
Host: h.GetHost(),
|
||||
}
|
||||
u.Host = urlutil.GetDomainsForURL(&u, false)[0]
|
||||
// envoy sends the query string as part of the path
|
||||
path := h.GetPath()
|
||||
if idx := strings.Index(path, "?"); idx != -1 {
|
||||
u.RawPath, u.RawQuery = path[:idx], path[idx+1:]
|
||||
u.RawQuery = u.Query().Encode()
|
||||
} else {
|
||||
u.RawPath = path
|
||||
}
|
||||
u.Path, _ = url.PathUnescape(u.RawPath)
|
||||
return u
|
||||
}
|
||||
|
||||
// GetHeaders returns the HTTP headers from an ext_authz CheckRequest, canonicalizing
|
||||
// the header keys.
|
||||
func GetHeaders(req *envoy_service_auth_v3.CheckRequest) map[string]string {
|
||||
hdrs := make(map[string]string)
|
||||
ch := req.GetAttributes().GetRequest().GetHttp().GetHeaders()
|
||||
for k, v := range ch {
|
||||
hdrs[httputil.CanonicalHeaderKey(k)] = v
|
||||
}
|
||||
return hdrs
|
||||
}
|
55
authorize/checkrequest/checkrequest_test.go
Normal file
55
authorize/checkrequest/checkrequest_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package checkrequest
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetURL(t *testing.T) {
|
||||
req := &envoy_service_auth_v3.CheckRequest{
|
||||
Attributes: &envoy_service_auth_v3.AttributeContext{
|
||||
Request: &envoy_service_auth_v3.AttributeContext_Request{
|
||||
Http: &envoy_service_auth_v3.AttributeContext_HttpRequest{
|
||||
Host: "example.com:80",
|
||||
Path: "/some/path?a=b",
|
||||
Scheme: "http",
|
||||
Method: "GET",
|
||||
Headers: map[string]string{"X-Request-Id": "CHECK-REQUEST-ID"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
Path: "/some/path",
|
||||
RawPath: "/some/path",
|
||||
RawQuery: "a=b",
|
||||
}, GetURL(req))
|
||||
}
|
||||
|
||||
func TestGetHeaders(t *testing.T) {
|
||||
req := &envoy_service_auth_v3.CheckRequest{
|
||||
Attributes: &envoy_service_auth_v3.AttributeContext{
|
||||
Request: &envoy_service_auth_v3.AttributeContext_Request{
|
||||
Http: &envoy_service_auth_v3.AttributeContext_HttpRequest{
|
||||
Headers: map[string]string{
|
||||
"content-type": "application/www-x-form-urlencoded",
|
||||
"x-request-id": "CHECK-REQUEST-ID",
|
||||
":authority": "example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, map[string]string{
|
||||
"Content-Type": "application/www-x-form-urlencoded",
|
||||
"X-Request-Id": "CHECK-REQUEST-ID",
|
||||
":authority": "example.com",
|
||||
}, GetHeaders(req))
|
||||
}
|
|
@ -3,9 +3,6 @@ package authorize
|
|||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||
|
@ -18,47 +15,6 @@ type sessionOrServiceAccount interface {
|
|||
Validate() error
|
||||
}
|
||||
|
||||
func getDataBrokerRecord(
|
||||
ctx context.Context,
|
||||
recordType string,
|
||||
recordID string,
|
||||
lowestRecordVersion uint64,
|
||||
) (*databroker.Record, error) {
|
||||
q := storage.GetQuerier(ctx)
|
||||
|
||||
req := &databroker.QueryRequest{
|
||||
Type: recordType,
|
||||
Limit: 1,
|
||||
}
|
||||
req.SetFilterByIDOrIndex(recordID)
|
||||
|
||||
res, err := q.Query(ctx, req, grpc.WaitForReady(true))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(res.GetRecords()) == 0 {
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
|
||||
// if the current record version is less than the lowest we'll accept, invalidate the cache
|
||||
if res.GetRecords()[0].GetVersion() < lowestRecordVersion {
|
||||
q.InvalidateCache(ctx, req)
|
||||
} else {
|
||||
return res.GetRecords()[0], nil
|
||||
}
|
||||
|
||||
// retry with an up to date cache
|
||||
res, err = q.Query(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(res.GetRecords()) == 0 {
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
|
||||
return res.GetRecords()[0], nil
|
||||
}
|
||||
|
||||
func (a *Authorize) getDataBrokerSessionOrServiceAccount(
|
||||
ctx context.Context,
|
||||
sessionID string,
|
||||
|
@ -67,9 +23,9 @@ func (a *Authorize) getDataBrokerSessionOrServiceAccount(
|
|||
ctx, span := a.tracer.Start(ctx, "authorize.getDataBrokerSessionOrServiceAccount")
|
||||
defer span.End()
|
||||
|
||||
record, err := getDataBrokerRecord(ctx, grpcutil.GetTypeURL(new(session.Session)), sessionID, dataBrokerRecordVersion)
|
||||
record, err := storage.GetDataBrokerRecord(ctx, grpcutil.GetTypeURL(new(session.Session)), sessionID, dataBrokerRecordVersion)
|
||||
if storage.IsNotFound(err) {
|
||||
record, err = getDataBrokerRecord(ctx, grpcutil.GetTypeURL(new(user.ServiceAccount)), sessionID, dataBrokerRecordVersion)
|
||||
record, err = storage.GetDataBrokerRecord(ctx, grpcutil.GetTypeURL(new(user.ServiceAccount)), sessionID, dataBrokerRecordVersion)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -100,7 +56,7 @@ func (a *Authorize) getDataBrokerUser(
|
|||
ctx, span := a.tracer.Start(ctx, "authorize.getDataBrokerUser")
|
||||
defer span.End()
|
||||
|
||||
record, err := getDataBrokerRecord(ctx, grpcutil.GetTypeURL(new(user.User)), userID, 0)
|
||||
record, err := storage.GetDataBrokerRecord(ctx, grpcutil.GetTypeURL(new(user.User)), userID, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package authorize
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -12,45 +11,9 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
)
|
||||
|
||||
func Test_getDataBrokerRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second*10)
|
||||
t.Cleanup(clearTimeout)
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
recordVersion, queryVersion uint64
|
||||
underlyingQueryCount, cachedQueryCount int
|
||||
}{
|
||||
{"cached", 1, 1, 1, 2},
|
||||
{"invalidated", 1, 2, 3, 4},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s1 := &session.Session{Id: "s1", Version: fmt.Sprint(tc.recordVersion)}
|
||||
|
||||
sq := storage.NewStaticQuerier(s1)
|
||||
cq := storage.NewCachingQuerier(sq, storage.NewGlobalCache(time.Minute))
|
||||
qctx := storage.WithQuerier(ctx, cq)
|
||||
|
||||
s, err := getDataBrokerRecord(qctx, grpcutil.GetTypeURL(s1), s1.GetId(), tc.queryVersion)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
s, err = getDataBrokerRecord(qctx, grpcutil.GetTypeURL(s1), s1.GetId(), tc.queryVersion)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorize_getDataBrokerSessionOrServiceAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ type evaluatorConfig struct {
|
|||
GoogleCloudServerlessAuthenticationServiceAccount string
|
||||
JWTClaimsHeaders config.JWTClaimHeaders
|
||||
JWTGroupsFilter config.JWTGroupsFilter
|
||||
DefaultJWTIssuerFormat config.JWTIssuerFormat
|
||||
}
|
||||
|
||||
// cacheKey() returns a hash over the configuration, except for the policies.
|
||||
|
@ -105,3 +106,10 @@ func WithJWTGroupsFilter(groups config.JWTGroupsFilter) Option {
|
|||
cfg.JWTGroupsFilter = groups
|
||||
}
|
||||
}
|
||||
|
||||
// WithDefaultJWTIssuerFormat sets the default JWT issuer format in the config.
|
||||
func WithDefaultJWTIssuerFormat(format config.JWTIssuerFormat) Option {
|
||||
return func(cfg *evaluatorConfig) {
|
||||
cfg.DefaultJWTIssuerFormat = format
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,25 +4,30 @@ package evaluator
|
|||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/hashicorp/go-set/v3"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/pomerium/pomerium/authorize/checkrequest"
|
||||
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/errgrouputil"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/contextutil"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/policy/criteria"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// Request contains the inputs needed for evaluation.
|
||||
|
@ -36,30 +41,37 @@ type Request struct {
|
|||
// RequestHTTP is the HTTP field in the request.
|
||||
type RequestHTTP struct {
|
||||
Method string `json:"method"`
|
||||
Host string `json:"host"`
|
||||
Hostname string `json:"hostname"`
|
||||
Path string `json:"path"`
|
||||
RawPath string `json:"raw_path"`
|
||||
RawQuery string `json:"raw_query"`
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
ClientCertificate ClientCertificateInfo `json:"client_certificate"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
// NewRequestHTTP creates a new RequestHTTP.
|
||||
func NewRequestHTTP(
|
||||
method string,
|
||||
requestURL url.URL,
|
||||
headers map[string]string,
|
||||
clientCertificate ClientCertificateInfo,
|
||||
ip string,
|
||||
// RequestHTTPFromCheckRequest populates a RequestHTTP from an Envoy CheckRequest proto.
|
||||
func RequestHTTPFromCheckRequest(
|
||||
ctx context.Context,
|
||||
in *envoy_service_auth_v3.CheckRequest,
|
||||
) RequestHTTP {
|
||||
requestURL := checkrequest.GetURL(in)
|
||||
rawPath, rawQuery, _ := strings.Cut(in.GetAttributes().GetRequest().GetHttp().GetPath(), "?")
|
||||
attrs := in.GetAttributes()
|
||||
clientCertMetadata := attrs.GetMetadataContext().GetFilterMetadata()["com.pomerium.client-certificate-info"]
|
||||
return RequestHTTP{
|
||||
Method: method,
|
||||
Method: attrs.GetRequest().GetHttp().GetMethod(),
|
||||
Host: attrs.GetRequest().GetHttp().GetHost(),
|
||||
Hostname: requestURL.Hostname(),
|
||||
Path: requestURL.Path,
|
||||
RawPath: rawPath,
|
||||
RawQuery: rawQuery,
|
||||
URL: requestURL.String(),
|
||||
Headers: headers,
|
||||
ClientCertificate: clientCertificate,
|
||||
IP: ip,
|
||||
Headers: checkrequest.GetHeaders(in),
|
||||
ClientCertificate: getClientCertificateInfo(ctx, clientCertMetadata),
|
||||
IP: attrs.GetSource().GetAddress().GetSocketAddress().GetAddress(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +89,41 @@ type ClientCertificateInfo struct {
|
|||
Intermediates string `json:"intermediates,omitempty"`
|
||||
}
|
||||
|
||||
// getClientCertificateInfo translates from the client certificate Envoy
|
||||
// metadata to the ClientCertificateInfo type.
|
||||
func getClientCertificateInfo(
|
||||
ctx context.Context, metadata *structpb.Struct,
|
||||
) ClientCertificateInfo {
|
||||
var c ClientCertificateInfo
|
||||
if metadata == nil {
|
||||
return c
|
||||
}
|
||||
c.Presented = metadata.Fields["presented"].GetBoolValue()
|
||||
escapedChain := metadata.Fields["chain"].GetStringValue()
|
||||
if escapedChain == "" {
|
||||
// No validated client certificate.
|
||||
return c
|
||||
}
|
||||
|
||||
chain, err := url.QueryUnescape(escapedChain)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Str("chain", escapedChain).Err(err).
|
||||
Msg(`received unexpected client certificate "chain" value`)
|
||||
return c
|
||||
}
|
||||
|
||||
// Split the chain into the leaf and any intermediate certificates.
|
||||
p, rest := pem.Decode([]byte(chain))
|
||||
if p == nil {
|
||||
log.Ctx(ctx).Error().Str("chain", escapedChain).
|
||||
Msg(`received unexpected client certificate "chain" value (no PEM block found)`)
|
||||
return c
|
||||
}
|
||||
c.Leaf = string(pem.EncodeToMemory(p))
|
||||
c.Intermediates = string(rest)
|
||||
return c
|
||||
}
|
||||
|
||||
// RequestSession is the session field in the request.
|
||||
type RequestSession struct {
|
||||
ID string `json:"id"`
|
||||
|
@ -109,7 +156,7 @@ func New(
|
|||
) (*Evaluator, error) {
|
||||
cfg := getConfig(options...)
|
||||
|
||||
err := updateStore(ctx, store, cfg)
|
||||
err := updateStore(store, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -245,6 +292,7 @@ var internalPathsNeedingLogin = set.From([]string{
|
|||
"/.pomerium/webauthn",
|
||||
"/.pomerium/routes",
|
||||
"/.pomerium/api/v1/routes",
|
||||
"/.pomerium/mcp/authorize",
|
||||
})
|
||||
|
||||
func (e *Evaluator) evaluateInternal(_ context.Context, req *Request) (*PolicyResponse, error) {
|
||||
|
@ -321,8 +369,8 @@ func (e *Evaluator) getClientCA(policy *config.Policy) (string, error) {
|
|||
return string(e.clientCA), nil
|
||||
}
|
||||
|
||||
func updateStore(ctx context.Context, store *store.Store, cfg *evaluatorConfig) error {
|
||||
jwk, err := getJWK(ctx, cfg)
|
||||
func updateStore(store *store.Store, cfg *evaluatorConfig) error {
|
||||
jwk, err := getJWK(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("authorize: couldn't create signer: %w", err)
|
||||
}
|
||||
|
@ -332,13 +380,14 @@ func updateStore(ctx context.Context, store *store.Store, cfg *evaluatorConfig)
|
|||
)
|
||||
store.UpdateJWTClaimHeaders(cfg.JWTClaimsHeaders)
|
||||
store.UpdateJWTGroupsFilter(cfg.JWTGroupsFilter)
|
||||
store.UpdateDefaultJWTIssuerFormat(cfg.DefaultJWTIssuerFormat)
|
||||
store.UpdateRoutePolicies(cfg.Policies)
|
||||
store.UpdateSigningKey(jwk)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getJWK(ctx context.Context, cfg *evaluatorConfig) (*jose.JSONWebKey, error) {
|
||||
func getJWK(cfg *evaluatorConfig) (*jose.JSONWebKey, error) {
|
||||
var decodedCert []byte
|
||||
// if we don't have a signing key, generate one
|
||||
if len(cfg.SigningKey) == 0 {
|
||||
|
@ -358,10 +407,6 @@ func getJWK(ctx context.Context, cfg *evaluatorConfig) (*jose.JSONWebKey, error)
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't generate signing key: %w", err)
|
||||
}
|
||||
log.Ctx(ctx).Info().Str("Algorithm", jwk.Algorithm).
|
||||
Str("KeyID", jwk.KeyID).
|
||||
Interface("Public Key", jwk.Public()).
|
||||
Msg("authorize: signing key")
|
||||
|
||||
return jwk, nil
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/testutil"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||
|
@ -22,6 +24,113 @@ import (
|
|||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
)
|
||||
|
||||
func Test_getClientCertificateInfo(t *testing.T) {
|
||||
const leafPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBZTCCAQugAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPSW50ZXJt
|
||||
ZWRpYXRlIENBMCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAwMTAxMDEwMDAwMDBaMB8x
|
||||
HTAbBgNVBAMTFENsaWVudCBjZXJ0aWZpY2F0ZSAxMFkwEwYHKoZIzj0CAQYIKoZI
|
||||
zj0DAQcDQgAESly1cwEbcxaJBl6qAhrX1k7vejTFNE2dEbrTMpUYMl86GEWdsDYN
|
||||
KSa/1wZCowPy82gPGjfAU90odkqJOusCQqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUH
|
||||
AwIwHwYDVR0jBBgwFoAU6Qb7nEl2XHKpf/QLL6PENsHFqbowCgYIKoZIzj0EAwID
|
||||
SAAwRQIgXREMUz81pYwJCMLGcV0ApaXIUap1V5n1N4VhyAGxGLYCIQC8p/LwoSgu
|
||||
71H3/nCi5MxsECsvVtsmHIfwXt0wulQ1TA==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
const intermediatePEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBYzCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAxMHUm9vdCBD
|
||||
QTAiGA8wMDAxMDEwMTAwMDAwMFoYDzAwMDEwMTAxMDAwMDAwWjAaMRgwFgYDVQQD
|
||||
Ew9JbnRlcm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATYaTr9
|
||||
uH4LpEp541/2SlKrdQZwNns+NHY/ftm++NhMDUn+izzNbPZ5aPT6VBs4Q6vbgfkK
|
||||
kDaBpaKzb+uOT+o1o0IwQDAdBgNVHQ4EFgQU6Qb7nEl2XHKpf/QLL6PENsHFqbow
|
||||
HwYDVR0jBBgwFoAUiQ3r61y+vxDn6PMWZrpISr67HiQwCgYIKoZIzj0EAwIDSQAw
|
||||
RgIhAMvdURs28uib2QwSMnqJjKasMb30yrSJvTiSU+lcg97/AiEA+6GpioM0c221
|
||||
n/XNKVYEkPmeXHRoz9ZuVDnSfXKJoHE=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
const rootPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBNzCB36ADAgECAgIQADAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdSb290IENB
|
||||
MCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAwMTAxMDEwMDAwMDBaMBIxEDAOBgNVBAMT
|
||||
B1Jvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS6q0mTvm29xasq7Lwk
|
||||
aRGb2S/LkQFsAwaCXohSNvonCQHRMCRvA1IrQGk/oyBS5qrDoD9/7xkcVYHuTv5D
|
||||
CbtuoyEwHzAdBgNVHQ4EFgQUiQ3r61y+vxDn6PMWZrpISr67HiQwCgYIKoZIzj0E
|
||||
AwIDRwAwRAIgF1ux0ridbN+bo0E3TTcNY8Xfva7yquYRMmEkfbGvSb0CIDqK80B+
|
||||
fYCZHo3CID0gRSemaQ/jYMgyeBFrHIr6icZh
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
cases := []struct {
|
||||
label string
|
||||
presented bool
|
||||
chain string
|
||||
expected ClientCertificateInfo
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
"not presented",
|
||||
false,
|
||||
"",
|
||||
ClientCertificateInfo{},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"presented",
|
||||
true,
|
||||
url.QueryEscape(leafPEM),
|
||||
ClientCertificateInfo{
|
||||
Presented: true,
|
||||
Leaf: leafPEM,
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"presented with intermediates",
|
||||
true,
|
||||
url.QueryEscape(leafPEM + intermediatePEM + rootPEM),
|
||||
ClientCertificateInfo{
|
||||
Presented: true,
|
||||
Leaf: leafPEM,
|
||||
Intermediates: intermediatePEM + rootPEM,
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"invalid chain URL encoding",
|
||||
false,
|
||||
"invalid%URL%encoding",
|
||||
ClientCertificateInfo{},
|
||||
`{"chain":"invalid%URL%encoding","error":"invalid URL escape \"%UR\"","level":"error","message":"received unexpected client certificate \"chain\" value"}`,
|
||||
},
|
||||
{
|
||||
"invalid chain PEM encoding",
|
||||
true,
|
||||
"not valid PEM data",
|
||||
ClientCertificateInfo{
|
||||
Presented: true,
|
||||
},
|
||||
`{"chain":"not valid PEM data","level":"error","message":"received unexpected client certificate \"chain\" value (no PEM block found)"}`,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for i := range cases {
|
||||
c := &cases[i]
|
||||
t.Run(c.label, func(t *testing.T) {
|
||||
metadata := &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"presented": structpb.NewBoolValue(c.presented),
|
||||
"chain": structpb.NewStringValue(c.chain),
|
||||
},
|
||||
}
|
||||
var info ClientCertificateInfo
|
||||
logOutput := testutil.CaptureLogs(t, func() {
|
||||
info = getClientCertificateInfo(ctx, metadata)
|
||||
})
|
||||
assert.Equal(t, c.expected, info)
|
||||
assert.Contains(t, logOutput, c.expectedLog)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluator(t *testing.T) {
|
||||
signingKey, err := cryptutil.NewSigningKey()
|
||||
require.NoError(t, err)
|
||||
|
@ -527,13 +636,9 @@ func TestEvaluator(t *testing.T) {
|
|||
t.Run("http method", func(t *testing.T) {
|
||||
res, err := eval(t, options, []proto.Message{}, &Request{
|
||||
Policy: policies[8],
|
||||
HTTP: NewRequestHTTP(
|
||||
http.MethodGet,
|
||||
*mustParseURL("https://from.example.com/"),
|
||||
nil,
|
||||
ClientCertificateInfo{},
|
||||
"",
|
||||
),
|
||||
HTTP: RequestHTTP{
|
||||
Method: http.MethodGet,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, res.Allow.Value)
|
||||
|
@ -541,13 +646,10 @@ func TestEvaluator(t *testing.T) {
|
|||
t.Run("http path", func(t *testing.T) {
|
||||
res, err := eval(t, options, []proto.Message{}, &Request{
|
||||
Policy: policies[9],
|
||||
HTTP: NewRequestHTTP(
|
||||
"POST",
|
||||
*mustParseURL("https://from.example.com/test"),
|
||||
nil,
|
||||
ClientCertificateInfo{},
|
||||
"",
|
||||
),
|
||||
HTTP: RequestHTTP{
|
||||
Method: "POST",
|
||||
Path: "/test",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, res.Allow.Value)
|
||||
|
|
|
@ -44,6 +44,8 @@ func TestGCPIdentityTokenSource(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_normalizeServiceAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
serviceAccount string
|
||||
|
@ -59,7 +61,6 @@ func Test_normalizeServiceAccount(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
gotServiceAccount, err := normalizeServiceAccount(tc.serviceAccount)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// HeadersResponse is the output from the headers.rego script.
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -20,6 +20,8 @@ import (
|
|||
|
||||
"github.com/pomerium/datasource/pkg/directory"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/headertemplate"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||
|
@ -149,20 +151,20 @@ func (e *headersEvaluatorEvaluation) fillSetRequestHeaders(ctx context.Context)
|
|||
}
|
||||
|
||||
for k, v := range e.request.Policy.SetRequestHeaders {
|
||||
e.response.Headers.Add(k, os.Expand(v, func(name string) string {
|
||||
switch name {
|
||||
case "$":
|
||||
return "$"
|
||||
case "pomerium.access_token":
|
||||
e.response.Headers.Add(k, headertemplate.Render(v, func(ref []string) string {
|
||||
switch {
|
||||
case slices.Equal(ref, []string{"pomerium", "access_token"}):
|
||||
s, _ := e.getSessionOrServiceAccount(ctx)
|
||||
return s.GetOauthToken().GetAccessToken()
|
||||
case "pomerium.client_cert_fingerprint":
|
||||
case slices.Equal(ref, []string{"pomerium", "client_cert_fingerprint"}):
|
||||
return e.getClientCertFingerprint()
|
||||
case "pomerium.id_token":
|
||||
case slices.Equal(ref, []string{"pomerium", "id_token"}):
|
||||
s, _ := e.getSessionOrServiceAccount(ctx)
|
||||
return s.GetIdToken().GetRaw()
|
||||
case "pomerium.jwt":
|
||||
case slices.Equal(ref, []string{"pomerium", "jwt"}):
|
||||
return e.getSignedJWT(ctx)
|
||||
case len(ref) > 3 && ref[0] == "pomerium" && ref[1] == "request" && ref[2] == "headers":
|
||||
return e.request.HTTP.Headers[httputil.CanonicalHeaderKey(ref[3])]
|
||||
}
|
||||
|
||||
return ""
|
||||
|
@ -247,18 +249,16 @@ func (e *headersEvaluatorEvaluation) getGroupIDs(ctx context.Context) []string {
|
|||
return make([]string, 0)
|
||||
}
|
||||
|
||||
func (e *headersEvaluatorEvaluation) getJWTPayloadIss() (string, error) {
|
||||
var issuerFormat string
|
||||
if e.request.Policy != nil {
|
||||
func (e *headersEvaluatorEvaluation) getJWTPayloadIss() string {
|
||||
issuerFormat := e.evaluator.store.GetDefaultJWTIssuerFormat()
|
||||
if e.request.Policy != nil && e.request.Policy.JWTIssuerFormat != "" {
|
||||
issuerFormat = e.request.Policy.JWTIssuerFormat
|
||||
}
|
||||
switch issuerFormat {
|
||||
case "uri":
|
||||
return fmt.Sprintf("https://%s/", e.request.HTTP.Hostname), nil
|
||||
case "", "hostOnly":
|
||||
return e.request.HTTP.Hostname, nil
|
||||
case config.JWTIssuerFormatURI:
|
||||
return fmt.Sprintf("https://%s/", e.request.HTTP.Hostname)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported JWT issuer format: %s", issuerFormat)
|
||||
return e.request.HTTP.Hostname
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,14 +412,9 @@ func (e *headersEvaluatorEvaluation) getJWTPayload(ctx context.Context) (map[str
|
|||
return e.cachedJWTPayload, nil
|
||||
}
|
||||
|
||||
iss, err := e.getJWTPayloadIss()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.gotJWTPayload = true
|
||||
e.cachedJWTPayload = map[string]any{
|
||||
"iss": iss,
|
||||
"iss": e.getJWTPayloadIss(),
|
||||
"aud": e.getJWTPayloadAud(),
|
||||
"jti": e.getJWTPayloadJTI(),
|
||||
"iat": e.getJWTPayloadIAT(),
|
||||
|
|
|
@ -218,15 +218,19 @@ func TestHeadersEvaluator(t *testing.T) {
|
|||
HTTP: RequestHTTP{
|
||||
Hostname: "from.example.com",
|
||||
ClientCertificate: ClientCertificateInfo{Leaf: testValidCert},
|
||||
Headers: map[string]string{
|
||||
"X-Incoming-Header": "INCOMING",
|
||||
},
|
||||
},
|
||||
Policy: &config.Policy{
|
||||
SetRequestHeaders: map[string]string{
|
||||
"X-Custom-Header": "CUSTOM_VALUE",
|
||||
"X-ID-Token": "${pomerium.id_token}",
|
||||
"X-Access-Token": "${pomerium.access_token}",
|
||||
"Client-Cert-Fingerprint": "${pomerium.client_cert_fingerprint}",
|
||||
"Authorization": "Bearer ${pomerium.jwt}",
|
||||
"Foo": "escaped $$dollar sign",
|
||||
"X-Custom-Header": "CUSTOM_VALUE",
|
||||
"X-ID-Token": "${pomerium.id_token}",
|
||||
"X-Access-Token": "${pomerium.access_token}",
|
||||
"Client-Cert-Fingerprint": "${pomerium.client_cert_fingerprint}",
|
||||
"Authorization": "Bearer ${pomerium.jwt}",
|
||||
"Foo": "escaped $$dollar sign",
|
||||
"X-Incoming-Custom-Header": `From-Incoming ${pomerium.request.headers["X-Incoming-Header"]}`,
|
||||
},
|
||||
},
|
||||
Session: RequestSession{ID: "s1"},
|
||||
|
@ -239,6 +243,7 @@ func TestHeadersEvaluator(t *testing.T) {
|
|||
assert.Equal(t, "3febe6467787e93f0a01030e0803072feaa710f724a9dc74de05cfba3d4a6d23",
|
||||
output.Headers.Get("Client-Cert-Fingerprint"))
|
||||
assert.Equal(t, "escaped $dollar sign", output.Headers.Get("Foo"))
|
||||
assert.Equal(t, "From-Incoming INCOMING", output.Headers.Get("X-Incoming-Custom-Header"))
|
||||
authHeader := output.Headers.Get("Authorization")
|
||||
assert.True(t, strings.HasPrefix(authHeader, "Bearer "))
|
||||
authHeader = strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
@ -437,34 +442,59 @@ func TestHeadersEvaluator(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, "u1@example.com", output.Headers.Get("X-Pomerium-Claim-Email"))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("issuer format", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
func TestHeadersEvaluator_JWTIssuerFormat(t *testing.T) {
|
||||
privateJWK, _ := newJWK(t)
|
||||
|
||||
for _, tc := range []struct {
|
||||
format string
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{"", "example.com", "example.com"},
|
||||
{"hostOnly", "host-only.example.com", "host-only.example.com"},
|
||||
{"uri", "uri.example.com", "https://uri.example.com/"},
|
||||
} {
|
||||
store := store.New()
|
||||
store.UpdateSigningKey(privateJWK)
|
||||
|
||||
eval := func(_ *testing.T, input *Request) (*HeadersResponse, error) {
|
||||
ctx := context.Background()
|
||||
e := NewHeadersEvaluator(store)
|
||||
return e.Evaluate(ctx, input)
|
||||
}
|
||||
|
||||
hostname := "route.example.com"
|
||||
|
||||
cases := []struct {
|
||||
globalFormat config.JWTIssuerFormat
|
||||
routeFormat config.JWTIssuerFormat
|
||||
expected string
|
||||
}{
|
||||
{"", "", "route.example.com"},
|
||||
{"hostOnly", "", "route.example.com"},
|
||||
{"uri", "", "https://route.example.com/"},
|
||||
|
||||
{"", "hostOnly", "route.example.com"},
|
||||
{"hostOnly", "hostOnly", "route.example.com"},
|
||||
{"uri", "hostOnly", "route.example.com"},
|
||||
|
||||
{"", "uri", "https://route.example.com/"},
|
||||
{"hostOnly", "uri", "https://route.example.com/"},
|
||||
{"uri", "uri", "https://route.example.com/"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
store.UpdateDefaultJWTIssuerFormat(tc.globalFormat)
|
||||
output, err := eval(t,
|
||||
nil,
|
||||
&Request{
|
||||
HTTP: RequestHTTP{
|
||||
Hostname: tc.input,
|
||||
Hostname: hostname,
|
||||
},
|
||||
Policy: &config.Policy{
|
||||
JWTIssuerFormat: tc.format,
|
||||
JWTIssuerFormat: tc.routeFormat,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
m := decodeJWTAssertion(t, output.Headers)
|
||||
assert.Equal(t, tc.output, m["iss"], "unexpected issuer for format=%s", tc.format)
|
||||
}
|
||||
})
|
||||
assert.Equal(t, tc.expected, m["iss"],
|
||||
"unexpected issuer for global format=%q, route format=%q",
|
||||
tc.globalFormat, tc.routeFormat)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadersEvaluator_JWTGroupsFilter(t *testing.T) {
|
||||
|
|
|
@ -11,11 +11,11 @@ import (
|
|||
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/contextutil"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/policy"
|
||||
"github.com/pomerium/pomerium/pkg/policy/criteria"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// PolicyRequest is the input to policy evaluation.
|
||||
|
|
|
@ -2,25 +2,23 @@ package authorize
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/pomerium/pomerium/authorize/checkrequest"
|
||||
"github.com/pomerium/pomerium/authorize/evaluator"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/config/envoyconfig"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/sessions"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
"github.com/pomerium/pomerium/pkg/contextutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||
|
@ -33,11 +31,7 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
|
|||
ctx, span := a.tracer.Start(ctx, "authorize.grpc.Check")
|
||||
defer span.End()
|
||||
|
||||
querier := storage.NewCachingQuerier(
|
||||
storage.NewQuerier(a.state.Load().dataBrokerClient),
|
||||
a.globalCache,
|
||||
)
|
||||
ctx = storage.WithQuerier(ctx, querier)
|
||||
ctx = a.withQuerierForCheckRequest(ctx)
|
||||
|
||||
state := a.state.Load()
|
||||
|
||||
|
@ -54,8 +48,11 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
|
|||
|
||||
// load the session
|
||||
s, err := a.loadSession(ctx, hreq, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if errors.Is(err, sessions.ErrInvalidSession) {
|
||||
// ENG-2172: if this is an invalid session, don't evaluate policy, return forbidden
|
||||
return a.deniedResponse(ctx, in, int32(http.StatusForbidden), http.StatusText(http.StatusForbidden), nil)
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("error loading session: %w", err)
|
||||
}
|
||||
|
||||
// if there's a session or service account, load the user
|
||||
|
@ -80,7 +77,7 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
|
|||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Str("request-id", requestID).Msg("grpc check ext_authz_error")
|
||||
}
|
||||
a.logAuthorizeCheck(ctx, in, res, s, u)
|
||||
a.logAuthorizeCheck(ctx, req, res, s, u)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
|
@ -94,7 +91,7 @@ func (a *Authorize) loadSession(
|
|||
// attempt to create a session from an incoming idp token
|
||||
s, err = config.NewIncomingIDPTokenSessionCreator(
|
||||
func(ctx context.Context, recordType, recordID string) (*databroker.Record, error) {
|
||||
return getDataBrokerRecord(ctx, recordType, recordID, 0)
|
||||
return storage.GetDataBrokerRecord(ctx, recordType, recordID, 0)
|
||||
},
|
||||
func(ctx context.Context, records []*databroker.Record) error {
|
||||
_, err := a.state.Load().dataBrokerClient.Put(ctx, &databroker.PutRequest{
|
||||
|
@ -103,15 +100,7 @@ func (a *Authorize) loadSession(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// invalidate cache
|
||||
for _, record := range records {
|
||||
q := &databroker.QueryRequest{
|
||||
Type: record.GetType(),
|
||||
Limit: 1,
|
||||
}
|
||||
q.SetFilterByIDOrIndex(record.GetId())
|
||||
storage.GetQuerier(ctx).InvalidateCache(ctx, q)
|
||||
}
|
||||
storage.InvalidateCacheForDataBrokerRecords(ctx, records...)
|
||||
return nil
|
||||
},
|
||||
).CreateSession(ctx, a.currentConfig.Load(), req.Policy, hreq)
|
||||
|
@ -122,6 +111,7 @@ func (a *Authorize) loadSession(
|
|||
Str("request-id", requestID).
|
||||
Err(err).
|
||||
Msg("error creating session for incoming idp token")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionState, _ := a.state.Load().sessionStore.LoadSessionStateAndCheckIDP(hreq)
|
||||
|
@ -145,18 +135,10 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
|
|||
ctx context.Context,
|
||||
in *envoy_service_auth_v3.CheckRequest,
|
||||
) (*evaluator.Request, error) {
|
||||
requestURL := getCheckRequestURL(in)
|
||||
attrs := in.GetAttributes()
|
||||
clientCertMetadata := attrs.GetMetadataContext().GetFilterMetadata()["com.pomerium.client-certificate-info"]
|
||||
req := &evaluator.Request{
|
||||
IsInternal: envoyconfig.ExtAuthzContextExtensionsIsInternal(attrs.GetContextExtensions()),
|
||||
HTTP: evaluator.NewRequestHTTP(
|
||||
attrs.GetRequest().GetHttp().GetMethod(),
|
||||
requestURL,
|
||||
getCheckRequestHeaders(in),
|
||||
getClientCertificateInfo(ctx, clientCertMetadata),
|
||||
attrs.GetSource().GetAddress().GetSocketAddress().GetAddress(),
|
||||
),
|
||||
HTTP: evaluator.RequestHTTPFromCheckRequest(ctx, in),
|
||||
}
|
||||
req.Policy = a.getMatchingPolicy(envoyconfig.ExtAuthzContextExtensionsRouteID(attrs.GetContextExtensions()))
|
||||
return req, nil
|
||||
|
@ -175,9 +157,24 @@ func (a *Authorize) getMatchingPolicy(routeID uint64) *config.Policy {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *Authorize) withQuerierForCheckRequest(ctx context.Context) context.Context {
|
||||
state := a.state.Load()
|
||||
q := storage.NewQuerier(state.dataBrokerClient)
|
||||
// if sync queriers are enabled, use those
|
||||
if len(state.syncQueriers) > 0 {
|
||||
m := map[string]storage.Querier{}
|
||||
for recordType, sq := range state.syncQueriers {
|
||||
m[recordType] = storage.NewFallbackQuerier(sq, q)
|
||||
}
|
||||
q = storage.NewTypedQuerier(q, m)
|
||||
}
|
||||
q = storage.NewCachingQuerier(q, storage.GlobalCache)
|
||||
return storage.WithQuerier(ctx, q)
|
||||
}
|
||||
|
||||
func getHTTPRequestFromCheckRequest(req *envoy_service_auth_v3.CheckRequest) *http.Request {
|
||||
hattrs := req.GetAttributes().GetRequest().GetHttp()
|
||||
u := getCheckRequestURL(req)
|
||||
u := checkrequest.GetURL(req)
|
||||
hreq := &http.Request{
|
||||
Method: hattrs.GetMethod(),
|
||||
URL: &u,
|
||||
|
@ -200,57 +197,3 @@ func getCheckRequestHeaders(req *envoy_service_auth_v3.CheckRequest) map[string]
|
|||
}
|
||||
return hdrs
|
||||
}
|
||||
|
||||
func getCheckRequestURL(req *envoy_service_auth_v3.CheckRequest) url.URL {
|
||||
h := req.GetAttributes().GetRequest().GetHttp()
|
||||
u := url.URL{
|
||||
Scheme: h.GetScheme(),
|
||||
Host: h.GetHost(),
|
||||
}
|
||||
u.Host = urlutil.GetDomainsForURL(&u, false)[0]
|
||||
// envoy sends the query string as part of the path
|
||||
path := h.GetPath()
|
||||
if idx := strings.Index(path, "?"); idx != -1 {
|
||||
u.RawPath, u.RawQuery = path[:idx], path[idx+1:]
|
||||
u.RawQuery = u.Query().Encode()
|
||||
} else {
|
||||
u.RawPath = path
|
||||
}
|
||||
u.Path, _ = url.PathUnescape(u.RawPath)
|
||||
return u
|
||||
}
|
||||
|
||||
// getClientCertificateInfo translates from the client certificate Envoy
|
||||
// metadata to the ClientCertificateInfo type.
|
||||
func getClientCertificateInfo(
|
||||
ctx context.Context, metadata *structpb.Struct,
|
||||
) evaluator.ClientCertificateInfo {
|
||||
var c evaluator.ClientCertificateInfo
|
||||
if metadata == nil {
|
||||
return c
|
||||
}
|
||||
c.Presented = metadata.Fields["presented"].GetBoolValue()
|
||||
escapedChain := metadata.Fields["chain"].GetStringValue()
|
||||
if escapedChain == "" {
|
||||
// No validated client certificate.
|
||||
return c
|
||||
}
|
||||
|
||||
chain, err := url.QueryUnescape(escapedChain)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Str("chain", escapedChain).Err(err).
|
||||
Msg(`received unexpected client certificate "chain" value`)
|
||||
return c
|
||||
}
|
||||
|
||||
// Split the chain into the leaf and any intermediate certificates.
|
||||
p, rest := pem.Decode([]byte(chain))
|
||||
if p == nil {
|
||||
log.Ctx(ctx).Error().Str("chain", escapedChain).
|
||||
Msg(`received unexpected client certificate "chain" value (no PEM block found)`)
|
||||
return c
|
||||
}
|
||||
c.Leaf = string(pem.EncodeToMemory(p))
|
||||
c.Intermediates = string(rest)
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/pomerium/pomerium/authorize/evaluator"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/atomicutil"
|
||||
"github.com/pomerium/pomerium/internal/testutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
)
|
||||
|
@ -92,20 +91,25 @@ func Test_getEvaluatorRequest(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
expect := &evaluator.Request{
|
||||
Policy: &a.currentConfig.Load().Options.Policies[0],
|
||||
HTTP: evaluator.NewRequestHTTP(
|
||||
http.MethodGet,
|
||||
mustParseURL("http://example.com/some/path?qs=1"),
|
||||
map[string]string{
|
||||
HTTP: evaluator.RequestHTTP{
|
||||
Method: http.MethodGet,
|
||||
Host: "example.com",
|
||||
Hostname: "example.com",
|
||||
Path: "/some/path",
|
||||
RawPath: "/some/path",
|
||||
RawQuery: "qs=1",
|
||||
URL: "http://example.com/some/path?qs=1",
|
||||
Headers: map[string]string{
|
||||
"Accept": "text/html",
|
||||
"X-Forwarded-Proto": "https",
|
||||
},
|
||||
evaluator.ClientCertificateInfo{
|
||||
ClientCertificate: evaluator.ClientCertificateInfo{
|
||||
Presented: true,
|
||||
Leaf: certPEM[1:] + "\n",
|
||||
Intermediates: "",
|
||||
},
|
||||
"",
|
||||
),
|
||||
IP: "",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expect, actual)
|
||||
}
|
||||
|
@ -145,127 +149,25 @@ func Test_getEvaluatorRequestWithPortInHostHeader(t *testing.T) {
|
|||
expect := &evaluator.Request{
|
||||
Policy: &a.currentConfig.Load().Options.Policies[0],
|
||||
Session: evaluator.RequestSession{},
|
||||
HTTP: evaluator.NewRequestHTTP(
|
||||
http.MethodGet,
|
||||
mustParseURL("http://example.com/some/path?qs=1"),
|
||||
map[string]string{
|
||||
HTTP: evaluator.RequestHTTP{
|
||||
Method: http.MethodGet,
|
||||
Host: "example.com:80",
|
||||
Hostname: "example.com",
|
||||
Path: "/some/path",
|
||||
RawPath: "/some/path",
|
||||
RawQuery: "qs=1",
|
||||
URL: "http://example.com/some/path?qs=1",
|
||||
Headers: map[string]string{
|
||||
"Accept": "text/html",
|
||||
"X-Forwarded-Proto": "https",
|
||||
},
|
||||
evaluator.ClientCertificateInfo{},
|
||||
"",
|
||||
),
|
||||
ClientCertificate: evaluator.ClientCertificateInfo{},
|
||||
IP: "",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expect, actual)
|
||||
}
|
||||
|
||||
func Test_getClientCertificateInfo(t *testing.T) {
|
||||
const leafPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBZTCCAQugAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPSW50ZXJt
|
||||
ZWRpYXRlIENBMCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAwMTAxMDEwMDAwMDBaMB8x
|
||||
HTAbBgNVBAMTFENsaWVudCBjZXJ0aWZpY2F0ZSAxMFkwEwYHKoZIzj0CAQYIKoZI
|
||||
zj0DAQcDQgAESly1cwEbcxaJBl6qAhrX1k7vejTFNE2dEbrTMpUYMl86GEWdsDYN
|
||||
KSa/1wZCowPy82gPGjfAU90odkqJOusCQqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUH
|
||||
AwIwHwYDVR0jBBgwFoAU6Qb7nEl2XHKpf/QLL6PENsHFqbowCgYIKoZIzj0EAwID
|
||||
SAAwRQIgXREMUz81pYwJCMLGcV0ApaXIUap1V5n1N4VhyAGxGLYCIQC8p/LwoSgu
|
||||
71H3/nCi5MxsECsvVtsmHIfwXt0wulQ1TA==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
const intermediatePEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBYzCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAxMHUm9vdCBD
|
||||
QTAiGA8wMDAxMDEwMTAwMDAwMFoYDzAwMDEwMTAxMDAwMDAwWjAaMRgwFgYDVQQD
|
||||
Ew9JbnRlcm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATYaTr9
|
||||
uH4LpEp541/2SlKrdQZwNns+NHY/ftm++NhMDUn+izzNbPZ5aPT6VBs4Q6vbgfkK
|
||||
kDaBpaKzb+uOT+o1o0IwQDAdBgNVHQ4EFgQU6Qb7nEl2XHKpf/QLL6PENsHFqbow
|
||||
HwYDVR0jBBgwFoAUiQ3r61y+vxDn6PMWZrpISr67HiQwCgYIKoZIzj0EAwIDSQAw
|
||||
RgIhAMvdURs28uib2QwSMnqJjKasMb30yrSJvTiSU+lcg97/AiEA+6GpioM0c221
|
||||
n/XNKVYEkPmeXHRoz9ZuVDnSfXKJoHE=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
const rootPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIBNzCB36ADAgECAgIQADAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdSb290IENB
|
||||
MCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAwMTAxMDEwMDAwMDBaMBIxEDAOBgNVBAMT
|
||||
B1Jvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS6q0mTvm29xasq7Lwk
|
||||
aRGb2S/LkQFsAwaCXohSNvonCQHRMCRvA1IrQGk/oyBS5qrDoD9/7xkcVYHuTv5D
|
||||
CbtuoyEwHzAdBgNVHQ4EFgQUiQ3r61y+vxDn6PMWZrpISr67HiQwCgYIKoZIzj0E
|
||||
AwIDRwAwRAIgF1ux0ridbN+bo0E3TTcNY8Xfva7yquYRMmEkfbGvSb0CIDqK80B+
|
||||
fYCZHo3CID0gRSemaQ/jYMgyeBFrHIr6icZh
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
cases := []struct {
|
||||
label string
|
||||
presented bool
|
||||
chain string
|
||||
expected evaluator.ClientCertificateInfo
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
"not presented",
|
||||
false,
|
||||
"",
|
||||
evaluator.ClientCertificateInfo{},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"presented",
|
||||
true,
|
||||
url.QueryEscape(leafPEM),
|
||||
evaluator.ClientCertificateInfo{
|
||||
Presented: true,
|
||||
Leaf: leafPEM,
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"presented with intermediates",
|
||||
true,
|
||||
url.QueryEscape(leafPEM + intermediatePEM + rootPEM),
|
||||
evaluator.ClientCertificateInfo{
|
||||
Presented: true,
|
||||
Leaf: leafPEM,
|
||||
Intermediates: intermediatePEM + rootPEM,
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"invalid chain URL encoding",
|
||||
false,
|
||||
"invalid%URL%encoding",
|
||||
evaluator.ClientCertificateInfo{},
|
||||
`{"chain":"invalid%URL%encoding","error":"invalid URL escape \"%UR\"","level":"error","message":"received unexpected client certificate \"chain\" value"}`,
|
||||
},
|
||||
{
|
||||
"invalid chain PEM encoding",
|
||||
true,
|
||||
"not valid PEM data",
|
||||
evaluator.ClientCertificateInfo{
|
||||
Presented: true,
|
||||
},
|
||||
`{"chain":"not valid PEM data","level":"error","message":"received unexpected client certificate \"chain\" value (no PEM block found)"}`,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for i := range cases {
|
||||
c := &cases[i]
|
||||
t.Run(c.label, func(t *testing.T) {
|
||||
metadata := &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"presented": structpb.NewBoolValue(c.presented),
|
||||
"chain": structpb.NewStringValue(c.chain),
|
||||
},
|
||||
}
|
||||
var info evaluator.ClientCertificateInfo
|
||||
logOutput := testutil.CaptureLogs(t, func() {
|
||||
info = getClientCertificateInfo(ctx, metadata)
|
||||
})
|
||||
assert.Equal(t, c.expected, info)
|
||||
assert.Contains(t, logOutput, c.expectedLog)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockDataBrokerServiceClient struct {
|
||||
databroker.DataBrokerServiceClient
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// A Store stores data for the OPA rego policy evaluation.
|
||||
|
@ -33,6 +33,7 @@ type Store struct {
|
|||
googleCloudServerlessAuthenticationServiceAccount atomic.Pointer[string]
|
||||
jwtClaimHeaders atomic.Pointer[map[string]string]
|
||||
jwtGroupsFilter atomic.Pointer[config.JWTGroupsFilter]
|
||||
defaultJWTIssuerFormat atomic.Pointer[config.JWTIssuerFormat]
|
||||
signingKey atomic.Pointer[jose.JSONWebKey]
|
||||
}
|
||||
|
||||
|
@ -66,6 +67,13 @@ func (s *Store) GetJWTGroupsFilter() config.JWTGroupsFilter {
|
|||
return config.JWTGroupsFilter{}
|
||||
}
|
||||
|
||||
func (s *Store) GetDefaultJWTIssuerFormat() config.JWTIssuerFormat {
|
||||
if f := s.defaultJWTIssuerFormat.Load(); f != nil {
|
||||
return *f
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Store) GetSigningKey() *jose.JSONWebKey {
|
||||
return s.signingKey.Load()
|
||||
}
|
||||
|
@ -89,6 +97,12 @@ func (s *Store) UpdateJWTGroupsFilter(groups config.JWTGroupsFilter) {
|
|||
s.jwtGroupsFilter.Store(&groups)
|
||||
}
|
||||
|
||||
// UpdateDefaultJWTIssuerFormat updates the JWT groups filter in the store.
|
||||
func (s *Store) UpdateDefaultJWTIssuerFormat(format config.JWTIssuerFormat) {
|
||||
// This isn't used by the Rego code, so we don't need to write it to the opastorage.Store instance.
|
||||
s.defaultJWTIssuerFormat.Store(&format)
|
||||
}
|
||||
|
||||
// UpdateRoutePolicies updates the route policies in the store.
|
||||
func (s *Store) UpdateRoutePolicies(routePolicies []*config.Policy) {
|
||||
s.write("/route_policies", routePolicies)
|
||||
|
|
|
@ -2,9 +2,7 @@ package authorize
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/rs/zerolog"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
@ -21,19 +19,19 @@ import (
|
|||
|
||||
func (a *Authorize) logAuthorizeCheck(
|
||||
ctx context.Context,
|
||||
in *envoy_service_auth_v3.CheckRequest,
|
||||
req *evaluator.Request,
|
||||
res *evaluator.Result, s sessionOrServiceAccount, u *user.User,
|
||||
) {
|
||||
ctx, span := a.tracer.Start(ctx, "authorize.grpc.LogAuthorizeCheck")
|
||||
defer span.End()
|
||||
|
||||
hdrs := getCheckRequestHeaders(in)
|
||||
hdrs := req.HTTP.Headers
|
||||
impersonateDetails := a.getImpersonateDetails(ctx, s)
|
||||
|
||||
evt := log.Ctx(ctx).Info().Str("service", "authorize")
|
||||
fields := a.currentConfig.Load().Options.GetAuthorizeLogFields()
|
||||
for _, field := range fields {
|
||||
evt = populateLogEvent(ctx, field, evt, in, s, u, hdrs, impersonateDetails, res)
|
||||
evt = populateLogEvent(ctx, field, evt, req, s, u, impersonateDetails, res)
|
||||
}
|
||||
evt = log.HTTPHeaders(evt, fields, hdrs)
|
||||
|
||||
|
@ -134,22 +132,19 @@ func populateLogEvent(
|
|||
ctx context.Context,
|
||||
field log.AuthorizeLogField,
|
||||
evt *zerolog.Event,
|
||||
in *envoy_service_auth_v3.CheckRequest,
|
||||
req *evaluator.Request,
|
||||
s sessionOrServiceAccount,
|
||||
u *user.User,
|
||||
hdrs map[string]string,
|
||||
impersonateDetails *impersonateDetails,
|
||||
res *evaluator.Result,
|
||||
) *zerolog.Event {
|
||||
path, query, _ := strings.Cut(in.GetAttributes().GetRequest().GetHttp().GetPath(), "?")
|
||||
|
||||
switch field {
|
||||
case log.AuthorizeLogFieldCheckRequestID:
|
||||
return evt.Str(string(field), hdrs["X-Request-Id"])
|
||||
return evt.Str(string(field), req.HTTP.Headers["X-Request-Id"])
|
||||
case log.AuthorizeLogFieldEmail:
|
||||
return evt.Str(string(field), u.GetEmail())
|
||||
case log.AuthorizeLogFieldHost:
|
||||
return evt.Str(string(field), in.GetAttributes().GetRequest().GetHttp().GetHost())
|
||||
return evt.Str(string(field), req.HTTP.Host)
|
||||
case log.AuthorizeLogFieldIDToken:
|
||||
if s, ok := s.(*session.Session); ok {
|
||||
evt = evt.Str(string(field), s.GetIdToken().GetRaw())
|
||||
|
@ -180,13 +175,13 @@ func populateLogEvent(
|
|||
}
|
||||
return evt
|
||||
case log.AuthorizeLogFieldIP:
|
||||
return evt.Str(string(field), in.GetAttributes().GetSource().GetAddress().GetSocketAddress().GetAddress())
|
||||
return evt.Str(string(field), req.HTTP.IP)
|
||||
case log.AuthorizeLogFieldMethod:
|
||||
return evt.Str(string(field), in.GetAttributes().GetRequest().GetHttp().GetMethod())
|
||||
return evt.Str(string(field), req.HTTP.Method)
|
||||
case log.AuthorizeLogFieldPath:
|
||||
return evt.Str(string(field), path)
|
||||
return evt.Str(string(field), req.HTTP.RawPath)
|
||||
case log.AuthorizeLogFieldQuery:
|
||||
return evt.Str(string(field), query)
|
||||
return evt.Str(string(field), req.HTTP.RawQuery)
|
||||
case log.AuthorizeLogFieldRequestID:
|
||||
return evt.Str(string(field), requestid.FromContext(ctx))
|
||||
case log.AuthorizeLogFieldServiceAccountID:
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
|
@ -24,27 +22,16 @@ func Test_populateLogEvent(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
ctx = requestid.WithValue(ctx, "REQUEST-ID")
|
||||
|
||||
checkRequest := &envoy_service_auth_v3.CheckRequest{
|
||||
Attributes: &envoy_service_auth_v3.AttributeContext{
|
||||
Request: &envoy_service_auth_v3.AttributeContext_Request{
|
||||
Http: &envoy_service_auth_v3.AttributeContext_HttpRequest{
|
||||
Host: "HOST",
|
||||
Path: "https://www.example.com/some/path?a=b",
|
||||
Method: "GET",
|
||||
},
|
||||
},
|
||||
Source: &envoy_service_auth_v3.AttributeContext_Peer{
|
||||
Address: &envoy_config_core_v3.Address{
|
||||
Address: &envoy_config_core_v3.Address_SocketAddress{
|
||||
SocketAddress: &envoy_config_core_v3.SocketAddress{
|
||||
Address: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
req := &evaluator.Request{
|
||||
HTTP: evaluator.RequestHTTP{
|
||||
Method: "GET",
|
||||
Host: "HOST",
|
||||
RawPath: "/some/path",
|
||||
RawQuery: "a=b",
|
||||
Headers: map[string]string{"X-Request-Id": "CHECK-REQUEST-ID"},
|
||||
IP: "127.0.0.1",
|
||||
},
|
||||
}
|
||||
headers := map[string]string{"X-Request-Id": "CHECK-REQUEST-ID"}
|
||||
s := &session.Session{
|
||||
Id: "SESSION-ID",
|
||||
IdToken: &session.IDToken{
|
||||
|
@ -86,7 +73,7 @@ func Test_populateLogEvent(t *testing.T) {
|
|||
{log.AuthorizeLogFieldImpersonateUserID, s, `{"impersonate-user-id":"IMPERSONATE-USER-ID"}`},
|
||||
{log.AuthorizeLogFieldIP, s, `{"ip":"127.0.0.1"}`},
|
||||
{log.AuthorizeLogFieldMethod, s, `{"method":"GET"}`},
|
||||
{log.AuthorizeLogFieldPath, s, `{"path":"https://www.example.com/some/path"}`},
|
||||
{log.AuthorizeLogFieldPath, s, `{"path":"/some/path"}`},
|
||||
{log.AuthorizeLogFieldQuery, s, `{"query":"a=b"}`},
|
||||
{log.AuthorizeLogFieldRemovedGroupsCount, s, `{"removed-groups-count":42}`},
|
||||
{log.AuthorizeLogFieldRequestID, s, `{"request-id":"REQUEST-ID"}`},
|
||||
|
@ -96,15 +83,13 @@ func Test_populateLogEvent(t *testing.T) {
|
|||
{log.AuthorizeLogFieldUser, sa, `{"user":"SERVICE-ACCOUNT-USER-ID"}`},
|
||||
{log.AuthorizeLogFieldUser, nil, `{"user":""}`},
|
||||
} {
|
||||
|
||||
tc := tc
|
||||
t.Run(string(tc.field), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var buf bytes.Buffer
|
||||
log := zerolog.New(&buf)
|
||||
evt := log.Log()
|
||||
evt = populateLogEvent(ctx, tc.field, evt, checkRequest, tc.s, u, headers, impersonateDetails, res)
|
||||
evt = populateLogEvent(ctx, tc.field, evt, req, tc.s, u, impersonateDetails, res)
|
||||
evt.Send()
|
||||
|
||||
assert.Equal(t, tc.expect, strings.TrimSpace(buf.String()))
|
||||
|
|
|
@ -9,18 +9,23 @@ import (
|
|||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
googlegrpc "google.golang.org/grpc"
|
||||
|
||||
"github.com/pomerium/datasource/pkg/directory"
|
||||
"github.com/pomerium/pomerium/authorize/evaluator"
|
||||
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/authenticateflow"
|
||||
"github.com/pomerium/pomerium/pkg/grpc"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
)
|
||||
|
||||
var outboundGRPCConnection = new(grpc.CachedOutboundGRPClientConn)
|
||||
|
||||
type authenticateFlow interface {
|
||||
AuthenticateSignInURL(ctx context.Context, queryParams url.Values, redirectURL *url.URL, idpID string) (string, error)
|
||||
AuthenticateSignInURL(ctx context.Context, queryParams url.Values, redirectURL *url.URL, idpID string, additionalLoginHosts []string) (string, error)
|
||||
}
|
||||
|
||||
type authorizeState struct {
|
||||
|
@ -30,14 +35,15 @@ type authorizeState struct {
|
|||
dataBrokerClient databroker.DataBrokerServiceClient
|
||||
sessionStore *config.SessionStore
|
||||
authenticateFlow authenticateFlow
|
||||
syncQueriers map[string]storage.Querier
|
||||
}
|
||||
|
||||
func newAuthorizeStateFromConfig(
|
||||
ctx context.Context,
|
||||
previousState *authorizeState,
|
||||
tracerProvider oteltrace.TracerProvider,
|
||||
cfg *config.Config,
|
||||
store *store.Store,
|
||||
previousPolicyEvaluator *evaluator.Evaluator,
|
||||
) (*authorizeState, error) {
|
||||
if err := validateOptions(cfg.Options); err != nil {
|
||||
return nil, fmt.Errorf("authorize: bad options: %w", err)
|
||||
|
@ -46,8 +52,12 @@ func newAuthorizeStateFromConfig(
|
|||
state := new(authorizeState)
|
||||
|
||||
var err error
|
||||
var previousEvaluator *evaluator.Evaluator
|
||||
if previousState != nil {
|
||||
previousEvaluator = previousState.evaluator
|
||||
}
|
||||
|
||||
state.evaluator, err = newPolicyEvaluator(ctx, cfg.Options, store, previousPolicyEvaluator)
|
||||
state.evaluator, err = newPolicyEvaluator(ctx, cfg.Options, store, previousEvaluator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("authorize: failed to update policy with options: %w", err)
|
||||
}
|
||||
|
@ -88,5 +98,29 @@ func newAuthorizeStateFromConfig(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
state.syncQueriers = make(map[string]storage.Querier)
|
||||
if previousState != nil {
|
||||
if previousState.dataBrokerClientConnection == state.dataBrokerClientConnection {
|
||||
state.syncQueriers = previousState.syncQueriers
|
||||
} else {
|
||||
for _, v := range previousState.syncQueriers {
|
||||
v.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
if cfg.Options.IsRuntimeFlagSet(config.RuntimeFlagAuthorizeUseSyncedData) {
|
||||
for _, recordType := range []string{
|
||||
grpcutil.GetTypeURL(new(session.Session)),
|
||||
grpcutil.GetTypeURL(new(user.User)),
|
||||
grpcutil.GetTypeURL(new(user.ServiceAccount)),
|
||||
directory.GroupRecordType,
|
||||
directory.UserRecordType,
|
||||
} {
|
||||
if _, ok := state.syncQueriers[recordType]; !ok {
|
||||
state.syncQueriers[recordType] = storage.NewSyncQuerier(state.dataBrokerClient, recordType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
|
111
changelogs/v0.29.0.md
Normal file
111
changelogs/v0.29.0.md
Normal file
|
@ -0,0 +1,111 @@
|
|||
## What's Changed
|
||||
|
||||
### Breaking
|
||||
- Enable seamless request tracing across multiple services with the new OTEL-based tracing system. Users can now easily configure and understand traces, with improved visibility into the flow of requests, even at low sample rates. All previously supported tracing methods are removed. ([@kralicky](https://github.com/kralicky) in [#5388](https://github.com/pomerium/pomerium/pull/5388)) and [#5447](https://github.com/pomerium/pomerium/pull/5447))
|
||||
|
||||
## New
|
||||
- New `jwt_issuer_format` global setting. ([@kenjenkins](https://github.com/kenjenkins) in [#5519](https://github.com/pomerium/pomerium/pull/5519))
|
||||
- Enable UDP routes with `CONNECT-UDP` tunneling. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5390](https://github.com/pomerium/pomerium/pull/5390))
|
||||
- HTTP/3 Support ([@calebdoxsey](https://github.com/calebdoxsey) in [#5349](https://github.com/pomerium/pomerium/pull/5349))
|
||||
- Enable authorization errors to return a JSON response instead of HTML, providing a cleaner and more consistent error format for developers using gRPC services. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5400](https://github.com/pomerium/pomerium/pull/5400) (ENG-1750))
|
||||
- Prevent false positive vulnerability reports by only generating a fallback certificate when no other certificate is configured, minimizing unnecessary certificate generation. ([@kenjenkins](https://github.com/kenjenkins) in [#5250](https://github.com/pomerium/pomerium/pull/5250))
|
||||
- Enable precise control over user group claims in JWTs by allowing filtering of groups either globally or per route. This enhancement helps reduce excessive group lists, preventing large headers that can disrupt upstream services while maintaining the integrity of signed JWTs. ([@kenjenkins](https://github.com/kenjenkins) in [#5417](https://github.com/pomerium/pomerium/pull/5417) (ENG-1802))
|
||||
- Enable core Pomerium to access the original PPL policy by adding a `source_ppl` field to the configuration, ensuring better introspection and compatibility with the Routes Portal. This enhancement allows the raw PPL to be passed alongside the generated Rego, providing more comprehensive policy visibility. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5419](https://github.com/pomerium/pomerium/pull/5419) (ENG-1832))
|
||||
|
||||
* importutil: refactor GenerateRouteNames to allow for protobuf or config routes by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5427
|
||||
- Add names, descriptions, and logos to routes, enhancing route cards with clear identifiers and visual appeal. Enjoy a more informative and engaging interface with route-specific icons and descriptions, and easily connect to services with direct links or command instructions. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5424](https://github.com/pomerium/pomerium/pull/5424) (ENG-1833))
|
||||
- Enhance Directory Group query performance by introducing a cache warming feature that preloads records, significantly reducing delays and timeouts. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5439](https://github.com/pomerium/pomerium/pull/5439) (ENG-1915))
|
||||
- Access your available routes through a new JSON endpoint at `/.pomerium/api/v1/routes`, providing a list tailored to your permissions. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5428](https://github.com/pomerium/pomerium/pull/5428) (ENG-1845))
|
||||
- Discover available routes with a new HTML page that displays each accessible route as a card. This intuitive interface makes navigation and route management simpler and more efficient. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5443](https://github.com/pomerium/pomerium/pull/5443) (ENG-1871))
|
||||
- Discover and display site logos automatically by fetching and embedding favicons from destination addresses, enhancing visual recognition and user experience. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5448](https://github.com/pomerium/pomerium/pull/5448))
|
||||
- Enhance user experience with new icons for well-known services, making it easier to identify them at a glance. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5453](https://github.com/pomerium/pomerium/pull/5453))
|
||||
- Enable dynamic configuration reloading by handling `SIGHUP` signals, allowing updates without restarting the application. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5459](https://github.com/pomerium/pomerium/pull/5459))
|
||||
- Enable customization of the HTTP/3 advertise port in the `Alt-Svc` header, enhancing flexibility for configurations using protobuf. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5466](https://github.com/pomerium/pomerium/pull/5466))
|
||||
- Authenticate using IdP access and identity tokens, with initial support for Azure AD access tokens. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5484](https://github.com/pomerium/pomerium/pull/5484) (ENG-2001, ENG-2001))
|
||||
- Improve file management by setting consistent default directories and logging errors when directory environment variables are unset. Ensure file writes are atomic to prevent redundancy and potential file conflicts, enhancing reliability and reducing clutter in temporary storage. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5477](https://github.com/pomerium/pomerium/pull/5477))
|
||||
- Enhance performance by reducing redundant session creation for identical IdP tokens. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5491](https://github.com/pomerium/pomerium/pull/5491) (ENG-2025, ENG-2025))
|
||||
- Enhance PPL logic with new `not` and `exclude` operators, allowing more flexible string and list matching. Define more precise permissions by excluding specific domains or groups in your policy configurations. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5490](https://github.com/pomerium/pomerium/pull/5490) (ENG-2030, ENG-2030))
|
||||
* Support loading idp token sessions in the proxy service by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5488
|
||||
* Handle long names in the cards for route portal by @nhayfield in https://github.com/pomerium/pomerium/pull/5514
|
||||
|
||||
### Fixes
|
||||
- Ensure the "groups" claim in JWTs is serialized as an empty list instead of JSON null, improving compatibility with third-party libraries. ([@kenjenkins](https://github.com/kenjenkins) in [#5394](https://github.com/pomerium/pomerium/pull/5394))
|
||||
- Ensure complete and accurate metrics output by properly flushing the buffered writer. ([@kenjenkins](https://github.com/kenjenkins) in [#5398](https://github.com/pomerium/pomerium/pull/5398))
|
||||
- Ensure custom branding settings are consistently applied across all pages, even when using multiple configuration sources. This resolves an issue where core pages were not displaying the correct branding when using an ingress controller, ensuring a uniform appearance with your chosen colors and logo. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5401](https://github.com/pomerium/pomerium/pull/5401) (ENG-1766))
|
||||
- Ensure the HTTP redirect server properly uses the proxy protocol when configured. ([@calebdoxsey](https://github.com/calebdoxsey) in [#5405](https://github.com/pomerium/pomerium/pull/5405))
|
||||
- Ensure that logo URLs containing `%` signs are correctly processed, preventing configuration errors in Envoy. This fix allows branding options with special characters to be used without causing issues. ([@kenjenkins](https://github.com/kenjenkins) in [#5460](https://github.com/pomerium/pomerium/pull/5460) (ENG-1958))
|
||||
- Fix the `identity_manager_last_session_refresh_errors` metrics view. ([@kenjenkins](https://github.com/kenjenkins) in [#5543](https://github.com/pomerium/pomerium/pull/5543))
|
||||
- Reduce memory usage during metrics output by @wasaga in https://github.com/pomerium/pomerium/pull/5530
|
||||
- Ensure Pomerium in Zero mode can connect to the cloud control plane using `HTTPS_PROXY` egress proxy by @wasaga in https://github.com/pomerium/pomerium/pull/5520
|
||||
|
||||
### Changed
|
||||
* authorize: enable WaitForReady on databroker query requests by @kralicky in https://github.com/pomerium/pomerium/pull/5415
|
||||
* authorize: filter only by group ID by @kenjenkins in https://github.com/pomerium/pomerium/pull/5437
|
||||
* authorize: log JWT groups filtering by @kenjenkins in https://github.com/pomerium/pomerium/pull/5432
|
||||
* authorize: remove audit logging by @wasaga in https://github.com/pomerium/pomerium/pull/5369
|
||||
* authorize: remove unused mutex by @wasaga in https://github.com/pomerium/pomerium/pull/5442
|
||||
* authorize: remove wait for ready by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5376
|
||||
* authorize: return 403 on invalid sessions by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5536
|
||||
* chore(deps): bump golang.org/x/net from 0.31.0 to 0.33.0 by @kenjenkins in https://github.com/pomerium/pomerium/pull/5404
|
||||
* config: add internal_address_config to address deprecation warning by @kralicky in https://github.com/pomerium/pomerium/pull/5425
|
||||
* config: add new OTLP tracing fields by @kenjenkins in https://github.com/pomerium/pomerium/pull/5421
|
||||
* config: add options to adjust databroker lease ttl, and retry initial interval by @kralicky in https://github.com/pomerium/pomerium/pull/5391
|
||||
* config: adjust envoy otel trace batching settings to match go sdk by @kralicky in https://github.com/pomerium/pomerium/pull/5446
|
||||
* config: fix JWT groups filter option by @kenjenkins in https://github.com/pomerium/pomerium/pull/5429
|
||||
* config: preserve existing user when creating sessions from idp token by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5502
|
||||
* config: reimplement file watcher by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5498
|
||||
* config: set default tracing sample rate to 1.0 by @kralicky in https://github.com/pomerium/pomerium/pull/5422
|
||||
* config: support emails from directory user by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5504
|
||||
* envoy: enable extended connect by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5387
|
||||
* get-envoy: allow downloading a specific os/arch by @kralicky in https://github.com/pomerium/pomerium/pull/5499
|
||||
* identity: disable session refresh for idp token sessions, fix query cache invalidation by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5495
|
||||
* internal: cleanup headers by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5408
|
||||
* internal: fix dependencies by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5450
|
||||
* internal: fix reporter client context on config change by @kralicky in https://github.com/pomerium/pomerium/pull/5392
|
||||
* internal: Fix trace client update by @kralicky in https://github.com/pomerium/pomerium/pull/5480
|
||||
* internal: improve port allocation by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5485
|
||||
* internal: pgx client tracing by @kralicky in https://github.com/pomerium/pomerium/pull/5438
|
||||
* internal: remove noisy logs by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5501
|
||||
* internal: replace xxhash with xxh3 by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5457
|
||||
* metrics: restore global registry in unit tests by @kenjenkins in https://github.com/pomerium/pomerium/pull/5399
|
||||
* proxy: add short timeout for logo discovery by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5506
|
||||
* proxy: disable HTTP2 `CONNECT` for websockets by @wasaga in https://github.com/pomerium/pomerium/pull/5516
|
||||
* proxy: fix connect command in routes portal by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5475
|
||||
* proxy: fix routes portal error message by @calebdoxsey in https://github.com/pomerium/pomerium/pull/5476
|
||||
* proxy: minor cleanup in GenerateCatchAllCertificate by @kenjenkins in https://github.com/pomerium/pomerium/pull/5397
|
||||
* proxy: only enable IPv6 addresses in the Envoy if IPv6 is enabled by @wasaga in https://github.com/pomerium/pomerium/pull/5538
|
||||
* testenv: avoid dns lookups for *.localhost.pomerium.io by @kralicky in https://github.com/pomerium/pomerium/pull/5372
|
||||
* testenv: embedded envoy cpu/memory profiling config by @kralicky in https://github.com/pomerium/pomerium/pull/5377
|
||||
* testenv: fix testcontainers docker client using the global tracer provider by @kralicky in https://github.com/pomerium/pomerium/pull/5440
|
||||
* tests: Fix small timeout causing test flake by @kralicky in https://github.com/pomerium/pomerium/pull/5436
|
||||
* tests: remove test code from config/options_test.go by @kralicky in https://github.com/pomerium/pomerium/pull/5423
|
||||
* tracing: add missing check for otel_exporter_otlp_endpoint in envoy trace config by @kralicky in https://github.com/pomerium/pomerium/pull/5472
|
||||
* tracing: handle empty protocol by @kralicky in https://github.com/pomerium/pomerium/pull/5474
|
||||
|
||||
### Dependency Updates
|
||||
* chore(deps): bump the github-actions group with 4 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5383
|
||||
* chore(deps): bump the docker group in /.github with 3 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5382
|
||||
* chore(deps): bump the docker group with 3 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5380
|
||||
* chore(deps): bump github.com/quic-go/quic-go from 0.48.1 to 0.48.2 by @dependabot in https://github.com/pomerium/pomerium/pull/5384
|
||||
* chore(deps): bump the go group across 1 directory with 26 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5385
|
||||
* chore(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /ui by @dependabot in https://github.com/pomerium/pomerium/pull/5373
|
||||
* chore(deps): bump golang.org/x/crypto from 0.29.0 to 0.31.0 by @dependabot in https://github.com/pomerium/pomerium/pull/5396
|
||||
* chore(deps): bump busybox from `db142d4` to `2919d01` in /.github in the docker group by @dependabot in https://github.com/pomerium/pomerium/pull/5414
|
||||
* chore(deps): bump the go group with 27 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5412
|
||||
* chore(deps): bump the docker group with 2 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5413
|
||||
* chore(deps): bump the docker group in /.github with 3 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5464
|
||||
* chore(deps): bump the docker group with 3 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5463
|
||||
* chore(deps): bump the github-actions group with 14 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5462
|
||||
* upgrade x/oauth2 from v0.24.0 to v0.27.0 by @kenjenkins in https://github.com/pomerium/pomerium/pull/5493
|
||||
* chore(deps): bump github.com/go-jose/go-jose/v3 from 3.0.3 to 3.0.4 by @dependabot in https://github.com/pomerium/pomerium/pull/5505
|
||||
* chore(deps): bump github.com/go-jose/go-jose/v4 from 4.0.2 to 4.0.5 by @dependabot in https://github.com/pomerium/pomerium/pull/5496
|
||||
* chore(deps-dev): bump esbuild from 0.21.1 to 0.25.0 in /ui by @dependabot in https://github.com/pomerium/pomerium/pull/5478
|
||||
* chore(deps): bump busybox from `a5d0ce4` to `498a000` in /.github in the docker group by @dependabot in https://github.com/pomerium/pomerium/pull/5512
|
||||
* chore(deps): bump the github-actions group with 7 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5510
|
||||
* chore(deps): bump the go group across 1 directory with 44 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5511
|
||||
* chore(deps): bump the docker group with 2 updates by @dependabot in https://github.com/pomerium/pomerium/pull/5509
|
||||
|
||||
## New Contributors
|
||||
* @gaurdro made their first contribution in https://github.com/pomerium/pomerium/pull/5456
|
||||
|
||||
**Full Changelog**: https://github.com/pomerium/pomerium/compare/v0.28.0...v0.29.0
|
|
@ -12,13 +12,13 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/internal/version"
|
||||
_ "github.com/pomerium/pomerium/internal/zero/bootstrap/writers/filesystem"
|
||||
_ "github.com/pomerium/pomerium/internal/zero/bootstrap/writers/k8s"
|
||||
zero_cmd "github.com/pomerium/pomerium/internal/zero/cmd"
|
||||
"github.com/pomerium/pomerium/pkg/cmd/pomerium"
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -53,10 +53,6 @@ func main() {
|
|||
}
|
||||
|
||||
func run(ctx context.Context, configFile string) error {
|
||||
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("config_file_source", configFile).Bool("bootstrap", true)
|
||||
})
|
||||
|
||||
var src config.Source
|
||||
|
||||
src, err := config.NewFileOrEnvironmentSource(ctx, configFile, files.FullVersion())
|
||||
|
|
|
@ -90,7 +90,7 @@ func TestAutocertOptions_Validate(t *testing.T) {
|
|||
}
|
||||
},
|
||||
"ok/trusted-ca-file": func(t *testing.T) test {
|
||||
f, err := os.CreateTemp("", "pomerium-test-ca")
|
||||
f, err := os.CreateTemp(t.TempDir(), "pomerium-test-ca")
|
||||
require.NoError(t, err)
|
||||
n, err := f.Write(certPEM)
|
||||
require.NoError(t, err)
|
||||
|
@ -128,7 +128,7 @@ func TestAutocertOptions_Validate(t *testing.T) {
|
|||
}
|
||||
},
|
||||
"fail/trusted-ca-combined": func(t *testing.T) test {
|
||||
f, err := os.CreateTemp("", "pomerium-test-ca")
|
||||
f, err := os.CreateTemp(t.TempDir(), "pomerium-test-ca")
|
||||
require.NoError(t, err)
|
||||
n, err := f.Write(certPEM)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -108,10 +108,6 @@ func NewFileOrEnvironmentSource(
|
|||
ctx context.Context,
|
||||
configFile, envoyVersion string,
|
||||
) (*FileOrEnvironmentSource, error) {
|
||||
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("config_file_source", configFile)
|
||||
})
|
||||
|
||||
options, err := newOptionsFromConfig(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -167,7 +163,7 @@ func NewFileOrEnvironmentSource(
|
|||
|
||||
func (src *FileOrEnvironmentSource) check(ctx context.Context) {
|
||||
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("config_change_id", uuid.New().String())
|
||||
return c.Str("config-change-id", uuid.New().String())
|
||||
})
|
||||
src.mu.Lock()
|
||||
cfg := src.config
|
||||
|
@ -254,7 +250,7 @@ func (src *FileWatcherSource) onConfigChange(ctx context.Context, cfg *Config) {
|
|||
// store the config and trigger an update
|
||||
src.cfg = cfg.Clone()
|
||||
src.hash = getAllConfigFilePathsHash(src.cfg)
|
||||
log.Ctx(ctx).Info().Uint64("hash", src.hash).Msg("config/filewatchersource: underlying config change, triggering update")
|
||||
log.Ctx(ctx).Debug().Uint64("hash", src.hash).Msg("config/filewatchersource: underlying config change, triggering update")
|
||||
src.Trigger(ctx, src.cfg)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,11 @@ import (
|
|||
"encoding/pem"
|
||||
"testing"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
)
|
||||
|
||||
func TestGenerateCatchAllCertificate(t *testing.T) {
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/hashutil"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
|
||||
"github.com/pomerium/pomerium/pkg/policy/parser"
|
||||
)
|
||||
|
||||
|
@ -617,3 +618,64 @@ func (f JWTGroupsFilter) Equal(other JWTGroupsFilter) bool {
|
|||
}
|
||||
return f.set.Equal(other.set)
|
||||
}
|
||||
|
||||
type JWTIssuerFormat string
|
||||
|
||||
const (
|
||||
JWTIssuerFormatUnset JWTIssuerFormat = ""
|
||||
JWTIssuerFormatHostOnly JWTIssuerFormat = "hostOnly"
|
||||
JWTIssuerFormatURI JWTIssuerFormat = "uri"
|
||||
)
|
||||
|
||||
var knownJWTIssuerFormats = map[JWTIssuerFormat]struct{}{
|
||||
JWTIssuerFormatUnset: {},
|
||||
JWTIssuerFormatHostOnly: {},
|
||||
JWTIssuerFormatURI: {},
|
||||
}
|
||||
|
||||
func JWTIssuerFormatFromPB(format *configpb.IssuerFormat) JWTIssuerFormat {
|
||||
if format == nil {
|
||||
return JWTIssuerFormatUnset
|
||||
}
|
||||
|
||||
switch *format {
|
||||
case configpb.IssuerFormat_IssuerHostOnly:
|
||||
return JWTIssuerFormatHostOnly
|
||||
case configpb.IssuerFormat_IssuerURI:
|
||||
return JWTIssuerFormatURI
|
||||
default:
|
||||
return JWTIssuerFormatUnset
|
||||
}
|
||||
}
|
||||
|
||||
func (f JWTIssuerFormat) ToPB() *configpb.IssuerFormat {
|
||||
switch f {
|
||||
case JWTIssuerFormatUnset:
|
||||
return nil
|
||||
case JWTIssuerFormatHostOnly:
|
||||
return configpb.IssuerFormat_IssuerHostOnly.Enum()
|
||||
case JWTIssuerFormatURI:
|
||||
return configpb.IssuerFormat_IssuerURI.Enum()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (f JWTIssuerFormat) Valid() bool {
|
||||
_, ok := knownJWTIssuerFormats[f]
|
||||
return ok
|
||||
}
|
||||
|
||||
func MCPFromPB(src *configpb.MCP) *MCP {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
return &MCP{}
|
||||
}
|
||||
|
||||
func MCPToPB(src *MCP) *configpb.MCP {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
return &configpb.MCP{}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
func TestBuilder_buildACMETLSALPNCluster(t *testing.T) {
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil, true)
|
||||
testutil.AssertProtoJSONEqual(t,
|
||||
`{
|
||||
"name": "pomerium-acme-tls-alpn",
|
||||
|
@ -34,7 +34,7 @@ func TestBuilder_buildACMETLSALPNCluster(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBuilder_buildACMETLSALPNFilterChain(t *testing.T) {
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil, true)
|
||||
testutil.AssertProtoJSONEqual(t,
|
||||
`{
|
||||
"filterChainMatch": {
|
||||
|
|
|
@ -16,12 +16,13 @@ import (
|
|||
envoy_config_overload_v3 "github.com/envoyproxy/go-control-plane/envoy/config/overload/v3"
|
||||
envoy_extensions_access_loggers_file_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3"
|
||||
envoy_extensions_resource_monitors_downstream_connections_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/resource_monitors/downstream_connections/v3"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/config/otelconfig"
|
||||
"github.com/pomerium/pomerium/internal/telemetry"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
const maxActiveDownstreamConnections = 50000
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
|
||||
func TestBuilder_BuildBootstrapAdmin(t *testing.T) {
|
||||
t.Setenv("TMPDIR", "/tmp")
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
adminCfg, err := b.BuildBootstrapAdmin(&config.Config{
|
||||
Options: &config.Options{
|
||||
|
@ -35,7 +35,7 @@ func TestBuilder_BuildBootstrapAdmin(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBuilder_BuildBootstrapLayeredRuntime(t *testing.T) {
|
||||
b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil)
|
||||
b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil, true)
|
||||
staticCfg, err := b.BuildBootstrapLayeredRuntime(context.Background(), &config.Config{})
|
||||
assert.NoError(t, err)
|
||||
testutil.AssertProtoJSONEqual(t, `
|
||||
|
@ -61,7 +61,7 @@ func TestBuilder_BuildBootstrapLayeredRuntime(t *testing.T) {
|
|||
|
||||
func TestBuilder_BuildBootstrapStaticResources(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil)
|
||||
b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil, true)
|
||||
staticCfg, err := b.BuildBootstrapStaticResources(context.Background(), &config.Config{}, false)
|
||||
assert.NoError(t, err)
|
||||
testutil.AssertProtoJSONEqual(t, `
|
||||
|
@ -105,14 +105,14 @@ func TestBuilder_BuildBootstrapStaticResources(t *testing.T) {
|
|||
`, staticCfg)
|
||||
})
|
||||
t.Run("bad gRPC address", func(t *testing.T) {
|
||||
b := New("xyz:zyx", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil)
|
||||
b := New("xyz:zyx", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil, true)
|
||||
_, err := b.BuildBootstrapStaticResources(context.Background(), &config.Config{}, false)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuilder_BuildBootstrapStatsConfig(t *testing.T) {
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
statsCfg, err := b.BuildBootstrapStatsConfig(&config.Config{
|
||||
Options: &config.Options{
|
||||
|
@ -132,7 +132,7 @@ func TestBuilder_BuildBootstrapStatsConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBuilder_BuildBootstrap(t *testing.T) {
|
||||
b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil)
|
||||
b := New("localhost:1111", "localhost:2222", "localhost:3333", filemgr.NewManager(), nil, true)
|
||||
t.Run("OverloadManager", func(t *testing.T) {
|
||||
bootstrap, err := b.BuildBootstrap(context.Background(), &config.Config{
|
||||
Options: &config.Options{
|
||||
|
|
|
@ -7,11 +7,12 @@ import (
|
|||
|
||||
// A Builder builds envoy config from pomerium config.
|
||||
type Builder struct {
|
||||
localGRPCAddress string
|
||||
localHTTPAddress string
|
||||
localMetricsAddress string
|
||||
filemgr *filemgr.Manager
|
||||
reproxy *reproxy.Handler
|
||||
localGRPCAddress string
|
||||
localHTTPAddress string
|
||||
localMetricsAddress string
|
||||
filemgr *filemgr.Manager
|
||||
reproxy *reproxy.Handler
|
||||
addIPV6InternalRanges bool
|
||||
}
|
||||
|
||||
// New creates a new Builder.
|
||||
|
@ -21,15 +22,17 @@ func New(
|
|||
localMetricsAddress string,
|
||||
fileManager *filemgr.Manager,
|
||||
reproxyHandler *reproxy.Handler,
|
||||
addIPV6InternalRanges bool,
|
||||
) *Builder {
|
||||
if reproxyHandler == nil {
|
||||
reproxyHandler = reproxy.New()
|
||||
}
|
||||
return &Builder{
|
||||
localGRPCAddress: localGRPCAddress,
|
||||
localHTTPAddress: localHTTPAddress,
|
||||
localMetricsAddress: localMetricsAddress,
|
||||
filemgr: fileManager,
|
||||
reproxy: reproxyHandler,
|
||||
localGRPCAddress: localGRPCAddress,
|
||||
localHTTPAddress: localHTTPAddress,
|
||||
localMetricsAddress: localMetricsAddress,
|
||||
filemgr: fileManager,
|
||||
reproxy: reproxyHandler,
|
||||
addIPV6InternalRanges: addIPV6InternalRanges,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// BuildClusters builds envoy clusters from the given config.
|
||||
|
@ -216,7 +216,6 @@ func (b *Builder) buildPolicyEndpoints(
|
|||
) ([]Endpoint, error) {
|
||||
var endpoints []Endpoint
|
||||
for _, dst := range policy.To {
|
||||
dst := dst
|
||||
ts, err := b.buildPolicyTransportSocket(ctx, cfg, policy, dst.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -27,7 +27,7 @@ func Test_BuildClusters(t *testing.T) {
|
|||
|
||||
opts := config.NewDefaultOptions()
|
||||
ctx := context.Background()
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
clusters, err := b.BuildClusters(ctx, &config.Config{Options: opts})
|
||||
require.NoError(t, err)
|
||||
testutil.AssertProtoJSONFileEqual(t, "testdata/clusters.json", clusters)
|
||||
|
@ -38,7 +38,7 @@ func Test_buildPolicyTransportSocket(t *testing.T) {
|
|||
cacheDir, _ := os.UserCacheDir()
|
||||
customCA := filepath.Join(cacheDir, "pomerium", "envoy", "files", "custom-ca-3133535332543131503345494c.pem")
|
||||
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
rootCABytes, _ := getCombinedCertificateAuthority(ctx, &config.Config{Options: &config.Options{}})
|
||||
rootCA := b.filemgr.BytesDataSource("ca.pem", rootCABytes).GetFilename()
|
||||
|
||||
|
@ -517,7 +517,7 @@ func Test_buildPolicyTransportSocket(t *testing.T) {
|
|||
|
||||
func Test_buildCluster(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
rootCABytes, _ := getCombinedCertificateAuthority(ctx, &config.Config{Options: &config.Options{}})
|
||||
rootCA := b.filemgr.BytesDataSource("ca.pem", rootCABytes).GetFilename()
|
||||
o1 := config.NewDefaultOptions()
|
||||
|
@ -1012,7 +1012,7 @@ func Test_bindConfig(t *testing.T) {
|
|||
ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer clearTimeout()
|
||||
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
t.Run("no bind config", func(t *testing.T) {
|
||||
cluster, err := b.buildPolicyCluster(ctx, &config.Config{Options: &config.Options{}}, &config.Policy{
|
||||
From: "https://from.example.com",
|
||||
|
|
|
@ -44,10 +44,10 @@ func ExtAuthzFilter(grpcClientTimeout *durationpb.Duration) *envoy_extensions_fi
|
|||
}
|
||||
|
||||
// HTTPConnectionManagerFilter creates a new HTTP connection manager filter.
|
||||
func HTTPConnectionManagerFilter(
|
||||
func (b *Builder) HTTPConnectionManagerFilter(
|
||||
httpConnectionManager *envoy_extensions_filters_network_http_connection_manager.HttpConnectionManager,
|
||||
) *envoy_config_listener_v3.Filter {
|
||||
applyGlobalHTTPConnectionManagerOptions(httpConnectionManager)
|
||||
b.applyGlobalHTTPConnectionManagerOptions(httpConnectionManager)
|
||||
return &envoy_config_listener_v3.Filter{
|
||||
Name: "envoy.filters.network.http_connection_manager",
|
||||
ConfigType: &envoy_config_listener_v3.Filter_TypedConfig{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package envoyconfig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
@ -19,6 +20,7 @@ func (b *Builder) buildVirtualHost(
|
|||
options *config.Options,
|
||||
name string,
|
||||
host string,
|
||||
hasMCPPolicy bool,
|
||||
) (*envoy_config_route_v3.VirtualHost, error) {
|
||||
vh := &envoy_config_route_v3.VirtualHost{
|
||||
Name: name,
|
||||
|
@ -35,7 +37,7 @@ func (b *Builder) buildVirtualHost(
|
|||
}
|
||||
|
||||
// these routes match /.pomerium/... and similar paths
|
||||
rs, err := b.buildPomeriumHTTPRoutes(options, host)
|
||||
rs, err := b.buildPomeriumHTTPRoutes(options, host, hasMCPPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -56,6 +58,15 @@ func (b *Builder) buildLocalReplyConfig(
|
|||
headers = toEnvoyHeaders(options.GetSetResponseHeaders())
|
||||
}
|
||||
|
||||
jsonBody, err := json.MarshalIndent(map[string]any{
|
||||
"requestId": "%STREAM_ID%",
|
||||
"status": "%RESPONSE_CODE%",
|
||||
"statusText": "%RESPONSE_CODE_DETAILS%",
|
||||
}, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error rendering error json for local reply: %w", err)
|
||||
}
|
||||
|
||||
data := make(map[string]any)
|
||||
httputil.AddBrandingOptionsToMap(data, options.BrandingOptions)
|
||||
for k, v := range data {
|
||||
|
@ -70,81 +81,146 @@ func (b *Builder) buildLocalReplyConfig(
|
|||
data["requestId"] = "%STREAM_ID%"
|
||||
data["responseFlags"] = "%RESPONSE_FLAGS%"
|
||||
|
||||
bs, err := ui.RenderPage("Error", "Error", data)
|
||||
htmlBody, err := ui.RenderPage("Error", "Error", data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error rendering error page for local reply: %w", err)
|
||||
}
|
||||
|
||||
responseFlagFilter := &envoy_config_accesslog_v3.AccessLogFilter_ResponseFlagFilter{
|
||||
ResponseFlagFilter: &envoy_config_accesslog_v3.ResponseFlagFilter{
|
||||
Flags: []string{
|
||||
"DC",
|
||||
"DF",
|
||||
"DI",
|
||||
"DO",
|
||||
"DPE",
|
||||
"DT",
|
||||
"FI",
|
||||
"IH",
|
||||
"LH",
|
||||
"LR",
|
||||
"NC",
|
||||
"NFCF",
|
||||
"NR",
|
||||
"OM",
|
||||
"RFCF",
|
||||
"RL",
|
||||
"RLSE",
|
||||
"SI",
|
||||
// "UAEX", // excluded because this response is handled in the authorize service
|
||||
"UC",
|
||||
"UF",
|
||||
"UH",
|
||||
"UMSDR",
|
||||
"UO",
|
||||
"UPE",
|
||||
"UR",
|
||||
"URX",
|
||||
"UT",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &envoy_http_connection_manager.LocalReplyConfig{
|
||||
Mappers: []*envoy_http_connection_manager.ResponseMapper{{
|
||||
Filter: &envoy_config_accesslog_v3.AccessLogFilter{
|
||||
FilterSpecifier: &envoy_config_accesslog_v3.AccessLogFilter_ResponseFlagFilter{
|
||||
ResponseFlagFilter: &envoy_config_accesslog_v3.ResponseFlagFilter{
|
||||
Flags: []string{
|
||||
"DC",
|
||||
"DF",
|
||||
"DI",
|
||||
"DO",
|
||||
"DPE",
|
||||
"DT",
|
||||
"FI",
|
||||
"IH",
|
||||
"LH",
|
||||
"LR",
|
||||
"NC",
|
||||
"NFCF",
|
||||
"NR",
|
||||
"OM",
|
||||
"RFCF",
|
||||
"RL",
|
||||
"RLSE",
|
||||
"SI",
|
||||
// "UAEX", // excluded because this response is handled in the authorize service
|
||||
"UC",
|
||||
"UF",
|
||||
"UH",
|
||||
"UMSDR",
|
||||
"UO",
|
||||
"UPE",
|
||||
"UR",
|
||||
"URX",
|
||||
"UT",
|
||||
Mappers: []*envoy_http_connection_manager.ResponseMapper{
|
||||
{
|
||||
Filter: &envoy_config_accesslog_v3.AccessLogFilter{
|
||||
FilterSpecifier: &envoy_config_accesslog_v3.AccessLogFilter_AndFilter{
|
||||
AndFilter: &envoy_config_accesslog_v3.AndFilter{
|
||||
Filters: []*envoy_config_accesslog_v3.AccessLogFilter{
|
||||
{FilterSpecifier: responseFlagFilter},
|
||||
{FilterSpecifier: &envoy_config_accesslog_v3.AccessLogFilter_MetadataFilter{
|
||||
MetadataFilter: &envoy_config_accesslog_v3.MetadataFilter{
|
||||
Matcher: buildLocalReplyTypeMatcher("plain"),
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
BodyFormatOverride: &envoy_config_core_v3.SubstitutionFormatString{
|
||||
ContentType: "text/html; charset=UTF-8",
|
||||
Format: &envoy_config_core_v3.SubstitutionFormatString_TextFormatSource{
|
||||
TextFormatSource: &envoy_config_core_v3.DataSource{
|
||||
Specifier: &envoy_config_core_v3.DataSource_InlineBytes{
|
||||
InlineBytes: bs,
|
||||
BodyFormatOverride: &envoy_config_core_v3.SubstitutionFormatString{
|
||||
ContentType: "text/plain; charset=UTF-8",
|
||||
Format: &envoy_config_core_v3.SubstitutionFormatString_TextFormatSource{
|
||||
TextFormatSource: &envoy_config_core_v3.DataSource{
|
||||
Specifier: &envoy_config_core_v3.DataSource_InlineBytes{
|
||||
// just return the json body for plain text
|
||||
InlineBytes: jsonBody,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
HeadersToAdd: headers,
|
||||
},
|
||||
HeadersToAdd: headers,
|
||||
}},
|
||||
{
|
||||
Filter: &envoy_config_accesslog_v3.AccessLogFilter{
|
||||
FilterSpecifier: &envoy_config_accesslog_v3.AccessLogFilter_AndFilter{
|
||||
AndFilter: &envoy_config_accesslog_v3.AndFilter{
|
||||
Filters: []*envoy_config_accesslog_v3.AccessLogFilter{
|
||||
{FilterSpecifier: responseFlagFilter},
|
||||
{FilterSpecifier: &envoy_config_accesslog_v3.AccessLogFilter_MetadataFilter{
|
||||
MetadataFilter: &envoy_config_accesslog_v3.MetadataFilter{
|
||||
Matcher: buildLocalReplyTypeMatcher("json"),
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
BodyFormatOverride: &envoy_config_core_v3.SubstitutionFormatString{
|
||||
ContentType: "application/json; charset=UTF-8",
|
||||
Format: &envoy_config_core_v3.SubstitutionFormatString_TextFormatSource{
|
||||
TextFormatSource: &envoy_config_core_v3.DataSource{
|
||||
Specifier: &envoy_config_core_v3.DataSource_InlineBytes{
|
||||
InlineBytes: jsonBody,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
HeadersToAdd: headers,
|
||||
},
|
||||
{
|
||||
Filter: &envoy_config_accesslog_v3.AccessLogFilter{
|
||||
FilterSpecifier: responseFlagFilter,
|
||||
},
|
||||
BodyFormatOverride: &envoy_config_core_v3.SubstitutionFormatString{
|
||||
ContentType: "text/html; charset=UTF-8",
|
||||
Format: &envoy_config_core_v3.SubstitutionFormatString_TextFormatSource{
|
||||
TextFormatSource: &envoy_config_core_v3.DataSource{
|
||||
Specifier: &envoy_config_core_v3.DataSource_InlineBytes{
|
||||
InlineBytes: htmlBody,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
HeadersToAdd: headers,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func applyGlobalHTTPConnectionManagerOptions(hcm *envoy_http_connection_manager.HttpConnectionManager) {
|
||||
func (b *Builder) applyGlobalHTTPConnectionManagerOptions(hcm *envoy_http_connection_manager.HttpConnectionManager) {
|
||||
if hcm.InternalAddressConfig == nil {
|
||||
// see doc comment on InternalAddressConfig for details
|
||||
hcm.InternalAddressConfig = &envoy_http_connection_manager.HttpConnectionManager_InternalAddressConfig{
|
||||
CidrRanges: []*envoy_config_core_v3.CidrRange{
|
||||
// localhost
|
||||
{AddressPrefix: "127.0.0.1", PrefixLen: wrapperspb.UInt32(32)},
|
||||
ranges := []*envoy_config_core_v3.CidrRange{
|
||||
// localhost
|
||||
{AddressPrefix: "127.0.0.1", PrefixLen: wrapperspb.UInt32(32)},
|
||||
|
||||
// RFC1918
|
||||
{AddressPrefix: "10.0.0.0", PrefixLen: wrapperspb.UInt32(8)},
|
||||
{AddressPrefix: "192.168.0.0", PrefixLen: wrapperspb.UInt32(16)},
|
||||
{AddressPrefix: "172.16.0.0", PrefixLen: wrapperspb.UInt32(12)},
|
||||
}
|
||||
if b.addIPV6InternalRanges {
|
||||
ranges = append(ranges, []*envoy_config_core_v3.CidrRange{
|
||||
// Localhost IPv6
|
||||
{AddressPrefix: "::1", PrefixLen: wrapperspb.UInt32(128)},
|
||||
|
||||
// RFC1918
|
||||
{AddressPrefix: "10.0.0.0", PrefixLen: wrapperspb.UInt32(8)},
|
||||
{AddressPrefix: "192.168.0.0", PrefixLen: wrapperspb.UInt32(16)},
|
||||
{AddressPrefix: "172.16.0.0", PrefixLen: wrapperspb.UInt32(12)},
|
||||
|
||||
// RFC4193
|
||||
{AddressPrefix: "fd00::", PrefixLen: wrapperspb.UInt32(8)},
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
// see doc comment on InternalAddressConfig for details
|
||||
hcm.InternalAddressConfig = &envoy_http_connection_manager.HttpConnectionManager_InternalAddressConfig{
|
||||
CidrRanges: ranges,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,18 @@ func Test_buildLocalReplyConfig(t *testing.T) {
|
|||
lrc, err := b.buildLocalReplyConfig(opts)
|
||||
require.NoError(t, err)
|
||||
tmpl := string(lrc.Mappers[0].GetBodyFormatOverride().GetTextFormatSource().GetInlineBytes())
|
||||
assert.Equal(t, `{
|
||||
"requestId": "%STREAM_ID%",
|
||||
"status": "%RESPONSE_CODE%",
|
||||
"statusText": "%RESPONSE_CODE_DETAILS%"
|
||||
}`, tmpl)
|
||||
tmpl = string(lrc.Mappers[1].GetBodyFormatOverride().GetTextFormatSource().GetInlineBytes())
|
||||
assert.Equal(t, `{
|
||||
"requestId": "%STREAM_ID%",
|
||||
"status": "%RESPONSE_CODE%",
|
||||
"statusText": "%RESPONSE_CODE_DETAILS%"
|
||||
}`, tmpl)
|
||||
tmpl = string(lrc.Mappers[2].GetBodyFormatOverride().GetTextFormatSource().GetInlineBytes())
|
||||
assert.Equal(t, `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
const listenerBufferLimit uint32 = 32 * 1024
|
||||
|
|
|
@ -51,7 +51,7 @@ func (b *Builder) buildEnvoyAdminHTTPConnectionManagerFilter() *envoy_config_lis
|
|||
},
|
||||
}})
|
||||
|
||||
return HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
return b.HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
CodecType: envoy_http_connection_manager.HttpConnectionManager_AUTO,
|
||||
StatPrefix: "envoy-admin",
|
||||
RouteSpecifier: &envoy_http_connection_manager.HttpConnectionManager_RouteConfig{
|
||||
|
|
|
@ -98,7 +98,7 @@ func (b *Builder) buildGRPCHTTPConnectionManagerFilter() *envoy_config_listener_
|
|||
Routes: routes,
|
||||
}})
|
||||
|
||||
return HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
return b.HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
CodecType: envoy_http_connection_manager.HttpConnectionManager_AUTO,
|
||||
StatPrefix: "grpc_ingress",
|
||||
// limit request first byte to last byte time
|
||||
|
|
|
@ -164,6 +164,7 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter(
|
|||
LuaFilter(luascripts.ExtAuthzSetCookie),
|
||||
LuaFilter(luascripts.CleanUpstream),
|
||||
LuaFilter(luascripts.RewriteHeaders),
|
||||
LuaFilter(luascripts.LocalReplyType),
|
||||
}
|
||||
// if we support http3 and this is the non-quic listener, add an alt-svc header indicating h3 is available
|
||||
if !useQUIC && cfg.Options.CodecType == config.CodecTypeHTTP3 {
|
||||
|
@ -207,9 +208,6 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter(
|
|||
mgr.CodecType = envoy_extensions_filters_network_http_connection_manager.HttpConnectionManager_AUTO
|
||||
} else if cfg.Options.GetCodecType() == config.CodecTypeAuto || cfg.Options.GetCodecType() == config.CodecTypeHTTP2 {
|
||||
mgr.CodecType = cfg.Options.GetCodecType().ToEnvoy()
|
||||
mgr.Http2ProtocolOptions = &envoy_config_core_v3.Http2ProtocolOptions{
|
||||
AllowConnect: true,
|
||||
}
|
||||
} else {
|
||||
mgr.CodecType = cfg.Options.GetCodecType().ToEnvoy()
|
||||
}
|
||||
|
@ -236,7 +234,7 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter(
|
|||
}
|
||||
}
|
||||
|
||||
return HTTPConnectionManagerFilter(mgr), nil
|
||||
return b.HTTPConnectionManagerFilter(mgr), nil
|
||||
}
|
||||
|
||||
func newListenerAccessLog() *envoy_config_accesslog_v3.AccessLog {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func Test_requireProxyProtocol(t *testing.T) {
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil, true)
|
||||
t.Run("required", func(t *testing.T) {
|
||||
li, err := b.buildMainListener(context.Background(), &config.Config{Options: &config.Options{
|
||||
UseProxyProtocol: true,
|
||||
|
|
|
@ -121,7 +121,7 @@ func (b *Builder) buildMetricsHTTPConnectionManagerFilter() *envoy_config_listen
|
|||
},
|
||||
}})
|
||||
|
||||
return HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
return b.HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
CodecType: envoy_http_connection_manager.HttpConnectionManager_AUTO,
|
||||
StatPrefix: "metrics",
|
||||
RouteSpecifier: &envoy_http_connection_manager.HttpConnectionManager_RouteConfig{
|
||||
|
|
|
@ -51,7 +51,7 @@ func TestBuildListeners(t *testing.T) {
|
|||
OutboundPort: "10003",
|
||||
MetricsPort: "10004",
|
||||
}
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
t.Run("enable grpc by default", func(t *testing.T) {
|
||||
cfg := cfg.Clone()
|
||||
lis, err := b.BuildListeners(ctx, cfg, false)
|
||||
|
@ -103,7 +103,7 @@ func TestBuildListeners(t *testing.T) {
|
|||
}]
|
||||
}
|
||||
}
|
||||
}`, httpConfig.Get("httpFilters.6").String(),
|
||||
}`, httpConfig.Get("httpFilters.7").String(),
|
||||
"should add alt-svc header")
|
||||
case "quic-ingress":
|
||||
hasQUIC = true
|
||||
|
@ -125,7 +125,7 @@ func Test_buildMetricsHTTPConnectionManagerFilter(t *testing.T) {
|
|||
certFileName := filepath.Join(cacheDir, "pomerium", "envoy", "files", "tls-crt-5a353247453159375849565a.pem")
|
||||
keyFileName := filepath.Join(cacheDir, "pomerium", "envoy", "files", "tls-key-3159554e32473758435257364b.pem")
|
||||
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
li, err := b.buildMetricsListener(&config.Config{
|
||||
Options: &config.Options{
|
||||
MetricsAddr: "127.0.0.1:9902",
|
||||
|
@ -143,7 +143,7 @@ func Test_buildMetricsHTTPConnectionManagerFilter(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_buildMainHTTPConnectionManagerFilter(t *testing.T) {
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil, true)
|
||||
|
||||
options := config.NewDefaultOptions()
|
||||
options.SkipXffAppend = true
|
||||
|
|
|
@ -14,6 +14,7 @@ var luascripts struct {
|
|||
RemoveImpersonateHeaders string
|
||||
RewriteHeaders string
|
||||
SetClientCertificateMetadata string
|
||||
LocalReplyType string
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -23,6 +24,7 @@ func init() {
|
|||
"luascripts/remove-impersonate-headers.lua": &luascripts.RemoveImpersonateHeaders,
|
||||
"luascripts/rewrite-headers.lua": &luascripts.RewriteHeaders,
|
||||
"luascripts/set-client-certificate-metadata.lua": &luascripts.SetClientCertificateMetadata,
|
||||
"luascripts/local-reply-type.lua": &luascripts.LocalReplyType,
|
||||
}
|
||||
|
||||
err := fs.WalkDir(luaFS, "luascripts", func(p string, d fs.DirEntry, err error) error {
|
||||
|
|
|
@ -11,8 +11,9 @@ import (
|
|||
)
|
||||
|
||||
func TestLuaCleanUpstream(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
t.Parallel()
|
||||
|
||||
L := newLua(t)
|
||||
|
||||
bs, err := luaFS.ReadFile("luascripts/clean-upstream.lua")
|
||||
require.NoError(t, err)
|
||||
|
@ -46,9 +47,52 @@ func TestLuaCleanUpstream(t *testing.T) {
|
|||
}, headers)
|
||||
}
|
||||
|
||||
func TestLuaLocalReplyContentType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
L := newLua(t)
|
||||
|
||||
bs, err := luaFS.ReadFile("luascripts/local-reply-type.lua")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = L.DoString(string(bs))
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, tc := range []struct {
|
||||
accept string
|
||||
expect string
|
||||
}{
|
||||
{"text/html", "html"},
|
||||
{"application/json", "json"},
|
||||
{"text/plain", "plain"},
|
||||
{"text/plain,text/html", "plain"},
|
||||
{"text/plain;q=0.8,text/html;q=0.9", "html"},
|
||||
{"application/json;q=0.8,text/*;q=0.9", "html"},
|
||||
} {
|
||||
headers := map[string]string{
|
||||
"accept": tc.accept,
|
||||
}
|
||||
dynamicMetadata := map[string]map[string]any{}
|
||||
handle := newLuaRequestHandle(L, headers, dynamicMetadata)
|
||||
|
||||
err = L.CallByParam(lua.P{
|
||||
Fn: L.GetGlobal("envoy_on_request"),
|
||||
NRet: 0,
|
||||
Protect: true,
|
||||
}, handle)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, map[string]map[string]any{
|
||||
"envoy.filters.http.lua": {
|
||||
"pomerium_local_reply_type": tc.expect,
|
||||
},
|
||||
}, dynamicMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLuaRewriteHeaders(t *testing.T) {
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
t.Parallel()
|
||||
|
||||
L := newLua(t)
|
||||
|
||||
bs, err := luaFS.ReadFile("luascripts/rewrite-headers.lua")
|
||||
require.NoError(t, err)
|
||||
|
@ -85,6 +129,39 @@ func TestLuaRewriteHeaders(t *testing.T) {
|
|||
assert.Equal(t, "https://frontend/one/some/uri/", headers["Location"])
|
||||
}
|
||||
|
||||
func newLua(t *testing.T) *lua.LState {
|
||||
L := lua.NewState()
|
||||
t.Cleanup(L.Close)
|
||||
|
||||
L.SetGlobal("print", L.NewFunction(func(L *lua.LState) int {
|
||||
var args []any
|
||||
top := L.GetTop()
|
||||
for i := 1; i <= top; i++ {
|
||||
args = append(args, fromLua(L, L.Get(i)))
|
||||
}
|
||||
t.Log(args...)
|
||||
return 0
|
||||
}))
|
||||
|
||||
return L
|
||||
}
|
||||
|
||||
func newLuaRequestHandle(L *lua.LState,
|
||||
headers map[string]string,
|
||||
dynamicMetadata map[string]map[string]any,
|
||||
) lua.LValue {
|
||||
return newLuaType(L, map[string]lua.LGFunction{
|
||||
"headers": func(L *lua.LState) int {
|
||||
L.Push(newLuaHeaders(L, headers))
|
||||
return 1
|
||||
},
|
||||
"streamInfo": func(L *lua.LState) int {
|
||||
L.Push(newLuaStreamInfo(L, dynamicMetadata))
|
||||
return 1
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func newLuaResponseHandle(L *lua.LState,
|
||||
headers map[string]string,
|
||||
metadata map[string]any,
|
||||
|
|
76
config/envoyconfig/luascripts/local-reply-type.lua
Normal file
76
config/envoyconfig/luascripts/local-reply-type.lua
Normal file
|
@ -0,0 +1,76 @@
|
|||
-- This filter interprets the accept header of an incoming request and attempts to map it to
|
||||
-- a metadata value of either "html", "json" or "plain". This metadata value is used to format
|
||||
-- local replies in a format the client expects.
|
||||
|
||||
function parse_accept_header(header_value)
|
||||
-- returns a table with a type field, the table is sorted by position and weight
|
||||
if header_value == nil then
|
||||
return {}
|
||||
end
|
||||
|
||||
local content_types = {}
|
||||
local start_idx = 1
|
||||
local position = 1
|
||||
while true do
|
||||
local end_idx = string.find(header_value, ",", start_idx)
|
||||
local segment
|
||||
if end_idx == nil then
|
||||
segment = string.sub(header_value, start_idx)
|
||||
else
|
||||
segment = string.sub(header_value, start_idx, end_idx-1)
|
||||
end
|
||||
|
||||
local mime_type = segment
|
||||
local q = 1.0
|
||||
local semicolon_idx = string.find(segment, ';')
|
||||
if semicolon_idx ~= nil then
|
||||
mime_type = string.sub(segment, 1, semicolon_idx-1)
|
||||
q_idx = string.find(segment, "q=", semicolon_idx+1)
|
||||
if q_idx ~= nil then
|
||||
q_str = string.sub(segment, q_idx+2)
|
||||
q = tonumber(q_str)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(content_types, { type=mime_type, q=q, position=position })
|
||||
position = position+1
|
||||
|
||||
if end_idx == nil then
|
||||
break
|
||||
else
|
||||
start_idx = end_idx+1
|
||||
end
|
||||
end
|
||||
table.sort(content_types, function(a,b)
|
||||
if a.q == b.q then
|
||||
return a.position < b.position
|
||||
end
|
||||
return a.q > b.q
|
||||
end)
|
||||
return content_types
|
||||
end
|
||||
|
||||
function envoy_on_request(request_handle)
|
||||
local headers = request_handle:headers()
|
||||
local dynamic_meta = request_handle:streamInfo():dynamicMetadata()
|
||||
|
||||
local content_types = parse_accept_header(headers:get("accept"))
|
||||
for _, v in pairs(content_types) do
|
||||
if v.type == "text/html" or v.type == "text/*" then
|
||||
dynamic_meta:set("envoy.filters.http.lua", "pomerium_local_reply_type", "html")
|
||||
return
|
||||
elseif v.type == "text/plain" then
|
||||
dynamic_meta:set("envoy.filters.http.lua", "pomerium_local_reply_type", "plain")
|
||||
return
|
||||
elseif v.type == "application/json" or v.type == "application/*" then
|
||||
dynamic_meta:set("envoy.filters.http.lua", "pomerium_local_reply_type", "json")
|
||||
return
|
||||
end
|
||||
end
|
||||
-- if nothing matched, just return html
|
||||
dynamic_meta:set("envoy.filters.http.lua", "pomerium_local_reply_type", "html")
|
||||
end
|
||||
|
||||
function envoy_on_response(response_handle)
|
||||
-- unused
|
||||
end
|
23
config/envoyconfig/matchers.go
Normal file
23
config/envoyconfig/matchers.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package envoyconfig
|
||||
|
||||
import envoy_type_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
|
||||
|
||||
func buildLocalReplyTypeMatcher(localReplyType string) *envoy_type_matcher_v3.MetadataMatcher {
|
||||
return &envoy_type_matcher_v3.MetadataMatcher{
|
||||
Filter: "envoy.filters.http.lua",
|
||||
Path: []*envoy_type_matcher_v3.MetadataMatcher_PathSegment{{
|
||||
Segment: &envoy_type_matcher_v3.MetadataMatcher_PathSegment_Key{
|
||||
Key: "pomerium_local_reply_type",
|
||||
},
|
||||
}},
|
||||
Value: &envoy_type_matcher_v3.ValueMatcher{
|
||||
MatchPattern: &envoy_type_matcher_v3.ValueMatcher_StringMatch{
|
||||
StringMatch: &envoy_type_matcher_v3.StringMatcher{
|
||||
MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{
|
||||
Exact: localReplyType,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ func (b *Builder) buildOutboundListener(cfg *config.Config) (*envoy_config_liste
|
|||
func (b *Builder) buildOutboundHTTPConnectionManager() *envoy_config_listener_v3.Filter {
|
||||
rc := b.buildOutboundRouteConfiguration()
|
||||
|
||||
return HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
return b.HTTPConnectionManagerFilter(&envoy_http_connection_manager.HttpConnectionManager{
|
||||
CodecType: envoy_http_connection_manager.HttpConnectionManager_AUTO,
|
||||
StatPrefix: "grpc_egress",
|
||||
// limit request first byte to last byte time
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func Test_buildOutboundRoutes(t *testing.T) {
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", nil, nil, true)
|
||||
routes := b.buildOutboundRoutes()
|
||||
testutil.AssertProtoJSONEqual(t, `[
|
||||
{
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package envoyconfig_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
@ -28,9 +34,9 @@ func TestH2C(t *testing.T) {
|
|||
|
||||
http := up.Route().
|
||||
From(env.SubdomainURL("grpc-http")).
|
||||
To(values.Bind(up.Port(), func(port int) string {
|
||||
To(values.Bind(up.Addr(), func(addr string) string {
|
||||
// override the target protocol to use http://
|
||||
return fmt.Sprintf("http://127.0.0.1:%d", port)
|
||||
return fmt.Sprintf("http://%s", addr)
|
||||
})).
|
||||
Policy(func(p *config.Policy) { p.AllowPublicUnauthenticatedAccess = true })
|
||||
|
||||
|
@ -118,6 +124,234 @@ func TestHTTP(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestTCPTunnel(t *testing.T) {
|
||||
env := testenv.New(t, testenv.Debug())
|
||||
|
||||
env.Add(scenarios.NewIDP([]*scenarios.User{{Email: "test@example.com"}}))
|
||||
up := upstreams.TCP()
|
||||
routeH1 := up.Route().
|
||||
From(env.SubdomainURL("h1")).
|
||||
PPL(`{"allow":{"and":["email":{"is":"test@example.com"}]}}`)
|
||||
routeH2 := up.Route().
|
||||
From(env.SubdomainURL("h2")).
|
||||
Policy(func(p *config.Policy) {
|
||||
p.AllowWebsockets = true
|
||||
}).
|
||||
PPL(`{"allow":{"and":["email":{"is":"test@example.com"}]}}`)
|
||||
|
||||
up.Handle(func(_ context.Context, c net.Conn) error {
|
||||
c.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
buf := make([]byte, 8)
|
||||
n, err := c.Read(buf)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, string(buf[:n]), "hello")
|
||||
c.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||
_, err = c.Write([]byte("world"))
|
||||
require.NoError(t, err)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
env.AddUpstream(up)
|
||||
env.Start()
|
||||
snippets.WaitStartupComplete(env)
|
||||
|
||||
t.Run("http1", func(t *testing.T) {
|
||||
assert.NoError(t, up.Dial(routeH1, func(_ context.Context, c net.Conn) error {
|
||||
c.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||
_, err := c.Write([]byte("hello"))
|
||||
require.NoError(t, err)
|
||||
|
||||
buf := make([]byte, 8)
|
||||
c.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
n, err := c.Read(buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, string(buf[:n]), "world")
|
||||
return nil
|
||||
}, upstreams.AuthenticateAs("test@example.com"), upstreams.DialProtocol(upstreams.DialHTTP1)))
|
||||
})
|
||||
|
||||
t.Run("http2", func(t *testing.T) {
|
||||
assert.NoError(t, up.Dial(routeH2, func(_ context.Context, c net.Conn) error {
|
||||
c.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||
_, err := c.Write([]byte("hello"))
|
||||
require.NoError(t, err)
|
||||
|
||||
buf := make([]byte, 8)
|
||||
c.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
n, err := c.Read(buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, string(buf[:n]), "world")
|
||||
return nil
|
||||
}, upstreams.AuthenticateAs("test@example.com"), upstreams.DialProtocol(upstreams.DialHTTP2)))
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkHTTP1TCPTunnel(b *testing.B) {
|
||||
env := testenv.New(b, testenv.Silent())
|
||||
env.Add(scenarios.NewIDP([]*scenarios.User{{Email: "test@example.com"}}))
|
||||
up := upstreams.TCP()
|
||||
h1 := up.Route().
|
||||
From(env.SubdomainURL("bench-h1")).
|
||||
PPL(`{"allow":{"and":["email":{"is":"test@example.com"}]}}`)
|
||||
|
||||
env.AddUpstream(up)
|
||||
env.Start()
|
||||
snippets.WaitStartupComplete(env)
|
||||
|
||||
b.Run("http1", func(b *testing.B) {
|
||||
benchmarkTCP(b, up, h1, tcpBenchmarkParams{
|
||||
msgLen: 512,
|
||||
protocol: upstreams.DialHTTP1,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkHTTP2TCPTunnel(b *testing.B) {
|
||||
env := testenv.New(b, testenv.Silent())
|
||||
env.Add(scenarios.NewIDP([]*scenarios.User{{Email: "test@example.com"}}))
|
||||
up := upstreams.TCP()
|
||||
|
||||
h2 := up.Route().
|
||||
From(env.SubdomainURL("bench-h2")).
|
||||
Policy(func(p *config.Policy) {
|
||||
p.AllowWebsockets = true
|
||||
}).
|
||||
PPL(`{"allow":{"and":["email":{"is":"test@example.com"}]}}`)
|
||||
|
||||
env.AddUpstream(up)
|
||||
env.Start()
|
||||
snippets.WaitStartupComplete(env)
|
||||
|
||||
b.Run("http2", func(b *testing.B) {
|
||||
benchmarkTCP(b, up, h2, tcpBenchmarkParams{
|
||||
msgLen: 512,
|
||||
protocol: upstreams.DialHTTP2,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type tcpBenchmarkParams struct {
|
||||
msgLen int
|
||||
protocol upstreams.Protocol
|
||||
}
|
||||
|
||||
func benchmarkTCP(b *testing.B, up upstreams.TCPUpstream, route testenv.Route, params tcpBenchmarkParams) {
|
||||
sendMsg := func(c net.Conn, buf []byte) error {
|
||||
c.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||
_, err := c.Write(buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
recvMsg := func(c net.Conn, buf []byte) error {
|
||||
c.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
for read := 0; read != len(buf); {
|
||||
n, err := c.Read(buf)
|
||||
read += n
|
||||
if err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
up.Handle(func(_ context.Context, c net.Conn) error {
|
||||
for {
|
||||
buf := make([]byte, params.msgLen)
|
||||
if err := recvMsg(c, buf[:]); err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := sendMsg(c, buf[:]); err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
})
|
||||
var threads atomic.Int32
|
||||
var requests atomic.Int32
|
||||
var bytes atomic.Int64
|
||||
start := time.Now()
|
||||
b.RunParallel(func(p *testing.PB) {
|
||||
threads.Add(1)
|
||||
require.NoError(b, up.Dial(route, func(_ context.Context, c net.Conn) error {
|
||||
buf := make([]byte, params.msgLen)
|
||||
for p.Next() {
|
||||
requests.Add(1)
|
||||
bytes.Add(int64(params.msgLen))
|
||||
require.NoError(b, sendMsg(c, buf[:]))
|
||||
require.NoError(b, recvMsg(c, buf[:]))
|
||||
}
|
||||
return nil
|
||||
}, upstreams.AuthenticateAs("test@example.com"), upstreams.DialProtocol(params.protocol)))
|
||||
})
|
||||
duration := time.Since(start)
|
||||
b.Logf("sent %d requests over %d parallel connections in %s", requests.Load(), threads.Load(), duration)
|
||||
b.Logf("throughput: %f bytes/s", float64(bytes.Load())/duration.Seconds())
|
||||
}
|
||||
|
||||
func TestHttp1Websocket(t *testing.T) {
|
||||
env := testenv.New(t)
|
||||
|
||||
up := upstreams.HTTP(nil)
|
||||
up.HandleWS("/ws", websocket.Upgrader{}, func(conn *websocket.Conn) error {
|
||||
for {
|
||||
mt, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// echo the message back
|
||||
err = conn.WriteMessage(mt, message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
route := up.Route().
|
||||
From(env.SubdomainURL("ws-test")).
|
||||
Policy(func(p *config.Policy) {
|
||||
p.AllowPublicUnauthenticatedAccess = true
|
||||
p.AllowWebsockets = true
|
||||
})
|
||||
|
||||
env.AddUpstream(up)
|
||||
env.Start()
|
||||
snippets.WaitStartupComplete(env)
|
||||
|
||||
assert.NoError(t, up.DialWS(route, func(conn *websocket.Conn) error {
|
||||
if err := conn.SetWriteDeadline(time.Now().Add(1 * time.Second)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := conn.WriteMessage(websocket.TextMessage, []byte("hello world")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := conn.SetReadDeadline(time.Now().Add(1 * time.Second)); err != nil {
|
||||
return err
|
||||
}
|
||||
mt, bytes, err := conn.ReadMessage()
|
||||
if err := err; err != nil {
|
||||
return err
|
||||
}
|
||||
assert.Equal(t, websocket.TextMessage, mt)
|
||||
assert.Equal(t, "hello world", string(bytes))
|
||||
return nil
|
||||
}, upstreams.Path("/ws")))
|
||||
}
|
||||
|
||||
func TestClientCert(t *testing.T) {
|
||||
env := testenv.New(t)
|
||||
env.Add(scenarios.DownstreamMTLS(config.MTLSEnforcementRejectConnection))
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// BuildRouteConfigurations builds the route configurations for the RDS service.
|
||||
|
@ -50,14 +50,14 @@ func (b *Builder) buildMainRouteConfiguration(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
allHosts, err := getAllRouteableHosts(cfg.Options, cfg.Options.Addr)
|
||||
allHosts, mcpHosts, err := getAllRouteableHosts(cfg.Options, cfg.Options.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var virtualHosts []*envoy_config_route_v3.VirtualHost
|
||||
for _, host := range allHosts {
|
||||
vh, err := b.buildVirtualHost(cfg.Options, host, host)
|
||||
vh, err := b.buildVirtualHost(cfg.Options, host, host, mcpHosts[host])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func (b *Builder) buildMainRouteConfiguration(
|
|||
}
|
||||
}
|
||||
|
||||
vh, err := b.buildVirtualHost(cfg.Options, "catch-all", "*")
|
||||
vh, err := b.buildVirtualHost(cfg.Options, "catch-all", "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -106,21 +106,28 @@ func (b *Builder) buildMainRouteConfiguration(
|
|||
return rc, nil
|
||||
}
|
||||
|
||||
func getAllRouteableHosts(options *config.Options, addr string) ([]string, error) {
|
||||
func getAllRouteableHosts(options *config.Options, addr string) ([]string, map[string]bool, error) {
|
||||
allHosts := set.NewTreeSet(cmp.Compare[string])
|
||||
mcpHosts := make(map[string]bool)
|
||||
|
||||
if addr == options.Addr {
|
||||
hosts, err := options.GetAllRouteableHTTPHosts()
|
||||
hosts, hostsMCP, err := options.GetAllRouteableHTTPHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
allHosts.InsertSlice(hosts)
|
||||
// Merge any MCP hosts
|
||||
for host, isMCP := range hostsMCP {
|
||||
if isMCP {
|
||||
mcpHosts[host] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if addr == options.GetGRPCAddr() {
|
||||
hosts, err := options.GetAllRouteableGRPCHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
allHosts.InsertSlice(hosts)
|
||||
}
|
||||
|
@ -131,7 +138,7 @@ func getAllRouteableHosts(options *config.Options, addr string) ([]string, error
|
|||
filtered = append(filtered, host)
|
||||
}
|
||||
}
|
||||
return filtered, nil
|
||||
return filtered, mcpHosts, nil
|
||||
}
|
||||
|
||||
func newRouteConfiguration(name string, virtualHosts []*envoy_config_route_v3.VirtualHost) *envoy_config_route_v3.RouteConfiguration {
|
||||
|
|
|
@ -32,7 +32,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}}
|
||||
b := New("grpc", "http", "metrics", filemgr.NewManager(), nil)
|
||||
b := New("grpc", "http", "metrics", filemgr.NewManager(), nil, true)
|
||||
routeConfiguration, err := b.buildMainRouteConfiguration(ctx, cfg)
|
||||
assert.NoError(t, err)
|
||||
testutil.AssertProtoJSONEqual(t, `{
|
||||
|
@ -195,7 +195,7 @@ func Test_getAllDomains(t *testing.T) {
|
|||
}
|
||||
t.Run("routable", func(t *testing.T) {
|
||||
t.Run("http", func(t *testing.T) {
|
||||
actual, err := getAllRouteableHosts(options, "127.0.0.1:9000")
|
||||
actual, _, err := getAllRouteableHosts(options, "127.0.0.1:9000")
|
||||
require.NoError(t, err)
|
||||
expect := []string{
|
||||
"a.example.com",
|
||||
|
@ -214,7 +214,7 @@ func Test_getAllDomains(t *testing.T) {
|
|||
assert.Equal(t, expect, actual)
|
||||
})
|
||||
t.Run("grpc", func(t *testing.T) {
|
||||
actual, err := getAllRouteableHosts(options, "127.0.0.1:9001")
|
||||
actual, _, err := getAllRouteableHosts(options, "127.0.0.1:9001")
|
||||
require.NoError(t, err)
|
||||
expect := []string{
|
||||
"authorize.example.com:9001",
|
||||
|
@ -225,7 +225,7 @@ func Test_getAllDomains(t *testing.T) {
|
|||
t.Run("both", func(t *testing.T) {
|
||||
newOptions := *options
|
||||
newOptions.GRPCAddr = newOptions.Addr
|
||||
actual, err := getAllRouteableHosts(&newOptions, "127.0.0.1:9000")
|
||||
actual, _, err := getAllRouteableHosts(&newOptions, "127.0.0.1:9000")
|
||||
require.NoError(t, err)
|
||||
expect := []string{
|
||||
"a.example.com",
|
||||
|
@ -252,7 +252,7 @@ func Test_getAllDomains(t *testing.T) {
|
|||
options.Policies = []config.Policy{
|
||||
{From: "https://a.example.com"},
|
||||
}
|
||||
actual, err := getAllRouteableHosts(options, ":443")
|
||||
actual, _, err := getAllRouteableHosts(options, ":443")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"a.example.com"}, actual)
|
||||
})
|
||||
|
@ -275,7 +275,6 @@ func Test_urlMatchesHost(t *testing.T) {
|
|||
{"non standard port", "http://example.com:81", "example.com", false},
|
||||
{"non standard host port", "http://example.com:81", "example.com:80", false},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ func (b *Builder) buildGRPCRoutes() ([]*envoy_config_route_v3.Route, error) {
|
|||
func (b *Builder) buildPomeriumHTTPRoutes(
|
||||
options *config.Options,
|
||||
host string,
|
||||
isMCPHost bool,
|
||||
) ([]*envoy_config_route_v3.Route, error) {
|
||||
var routes []*envoy_config_route_v3.Route
|
||||
|
||||
|
@ -60,6 +61,7 @@ func (b *Builder) buildPomeriumHTTPRoutes(
|
|||
return nil, err
|
||||
}
|
||||
if !isFrontingAuthenticate {
|
||||
// Add common routes
|
||||
routes = append(routes,
|
||||
b.buildControlPlanePathRoute(options, "/ping"),
|
||||
b.buildControlPlanePathRoute(options, "/healthz"),
|
||||
|
@ -68,6 +70,11 @@ func (b *Builder) buildPomeriumHTTPRoutes(
|
|||
b.buildControlPlanePathRoute(options, "/.well-known/pomerium"),
|
||||
b.buildControlPlanePrefixRoute(options, "/.well-known/pomerium/"),
|
||||
)
|
||||
|
||||
// Only add oauth-authorization-server route if there's an MCP policy for this host
|
||||
if isMCPHost {
|
||||
routes = append(routes, b.buildControlPlanePathRoute(options, "/.well-known/oauth-authorization-server"))
|
||||
}
|
||||
}
|
||||
|
||||
authRoutes, err := b.buildPomeriumAuthenticateHTTPRoutes(options, host)
|
||||
|
|
|
@ -104,7 +104,7 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
|
|||
AuthenticateURLString: "https://authenticate.example.com",
|
||||
AuthenticateCallbackPath: "/oauth2/callback",
|
||||
}
|
||||
routes, err := b.buildPomeriumHTTPRoutes(options, "authenticate.example.com")
|
||||
routes, err := b.buildPomeriumHTTPRoutes(options, "authenticate.example.com", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.AssertProtoJSONEqual(t, `[
|
||||
|
@ -125,7 +125,7 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
|
|||
AuthenticateURLString: "https://authenticate.example.com",
|
||||
AuthenticateCallbackPath: "/oauth2/callback",
|
||||
}
|
||||
routes, err := b.buildPomeriumHTTPRoutes(options, "authenticate.example.com")
|
||||
routes, err := b.buildPomeriumHTTPRoutes(options, "authenticate.example.com", false)
|
||||
require.NoError(t, err)
|
||||
testutil.AssertProtoJSONEqual(t, "null", routes)
|
||||
})
|
||||
|
@ -2244,3 +2244,107 @@ func mustParseURL(t *testing.T, str string) *url.URL {
|
|||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
func Test_buildPomeriumHTTPRoutesWithMCP(t *testing.T) {
|
||||
routeString := func(typ, name string) string {
|
||||
str := `{
|
||||
"name": "pomerium-` + typ + `-` + name + `",
|
||||
"decorator": {
|
||||
"operation": "internal: ${method} ${host}${path}"
|
||||
},
|
||||
"match": {
|
||||
"` + typ + `": "` + name + `"
|
||||
},
|
||||
"responseHeadersToAdd": [
|
||||
{
|
||||
"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
|
||||
"header": {
|
||||
"key": "X-Frame-Options",
|
||||
"value": "SAMEORIGIN"
|
||||
}
|
||||
},
|
||||
{
|
||||
"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
|
||||
"header": {
|
||||
"key": "X-XSS-Protection",
|
||||
"value": "1; mode=block"
|
||||
}
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"cluster": "pomerium-control-plane-http"
|
||||
},
|
||||
"typedPerFilterConfig": {
|
||||
"envoy.filters.http.ext_authz": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
|
||||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "true",
|
||||
"route_id": "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
return str
|
||||
}
|
||||
|
||||
t.Run("without MCP policy", func(t *testing.T) {
|
||||
b := &Builder{filemgr: filemgr.NewManager()}
|
||||
options := &config.Options{
|
||||
Services: "all",
|
||||
AuthenticateURLString: "https://authenticate.example.com",
|
||||
Policies: []config.Policy{
|
||||
{
|
||||
From: "https://example.com",
|
||||
To: mustParseWeightedURLs(t, "https://to.example.com"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
routes, err := b.buildPomeriumHTTPRoutes(options, "example.com", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
hasOAuthServer := false
|
||||
for _, route := range routes {
|
||||
if route.GetMatch().GetPath() == "/.well-known/oauth-authorization-server" {
|
||||
hasOAuthServer = true
|
||||
}
|
||||
}
|
||||
|
||||
assert.False(t, hasOAuthServer, "/.well-known/oauth-authorization-server route should NOT be present")
|
||||
})
|
||||
|
||||
t.Run("with MCP policy", func(t *testing.T) {
|
||||
b := &Builder{filemgr: filemgr.NewManager()}
|
||||
options := &config.Options{
|
||||
Services: "all",
|
||||
AuthenticateURLString: "https://authenticate.example.com",
|
||||
Policies: []config.Policy{
|
||||
{
|
||||
From: "https://example.com",
|
||||
To: mustParseWeightedURLs(t, "https://to.example.com"),
|
||||
},
|
||||
{
|
||||
From: "https://mcp.example.com",
|
||||
To: mustParseWeightedURLs(t, "https://mcp-backend.example.com"),
|
||||
MCP: &config.MCP{}, // This marks the policy as an MCP policy
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
routes, err := b.buildPomeriumHTTPRoutes(options, "example.com", true)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the expected route structures
|
||||
testutil.AssertProtoJSONEqual(t, `[
|
||||
`+routeString("path", "/ping")+`,
|
||||
`+routeString("path", "/healthz")+`,
|
||||
`+routeString("path", "/.pomerium")+`,
|
||||
`+routeString("prefix", "/.pomerium/")+`,
|
||||
`+routeString("path", "/.well-known/pomerium")+`,
|
||||
`+routeString("prefix", "/.well-known/pomerium/")+`,
|
||||
`+routeString("path", "/.well-known/oauth-authorization-server")+`
|
||||
]`, routes)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -64,9 +64,7 @@
|
|||
"statusOnError": {
|
||||
"code": "InternalServerError"
|
||||
},
|
||||
"metadataContextNamespaces": [
|
||||
"com.pomerium.client-certificate-info"
|
||||
]
|
||||
"metadataContextNamespaces": ["com.pomerium.client-certificate-info"]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -96,6 +94,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.http.lua",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua",
|
||||
"defaultSourceCode": {
|
||||
"inlineString": "-- This filter interprets the accept header of an incoming request and attempts to map it to\n-- a metadata value of either \"html\", \"json\" or \"plain\". This metadata value is used to format\n-- local replies in a format the client expects.\n\nfunction parse_accept_header(header_value)\n -- returns a table with a type field, the table is sorted by position and weight\n if header_value == nil then\n return {}\n end\n\n local content_types = {}\n local start_idx = 1\n local position = 1\n while true do\n local end_idx = string.find(header_value, \",\", start_idx)\n local segment\n if end_idx == nil then\n segment = string.sub(header_value, start_idx)\n else\n segment = string.sub(header_value, start_idx, end_idx-1)\n end\n\n local mime_type = segment\n local q = 1.0\n local semicolon_idx = string.find(segment, ';')\n if semicolon_idx ~= nil then\n mime_type = string.sub(segment, 1, semicolon_idx-1)\n q_idx = string.find(segment, \"q=\", semicolon_idx+1)\n if q_idx ~= nil then\n q_str = string.sub(segment, q_idx+2)\n q = tonumber(q_str)\n end\n end\n\n table.insert(content_types, { type=mime_type, q=q, position=position })\n position = position+1\n\n if end_idx == nil then\n break\n else\n start_idx = end_idx+1\n end\n end\n table.sort(content_types, function(a,b)\n if a.q == b.q then\n return a.position < b.position\n end\n return a.q > b.q\n end)\n return content_types\nend\n\nfunction envoy_on_request(request_handle)\n local headers = request_handle:headers()\n local dynamic_meta = request_handle:streamInfo():dynamicMetadata()\n\n local content_types = parse_accept_header(headers:get(\"accept\"))\n for _, v in pairs(content_types) do\n if v.type == \"text/html\" or v.type == \"text/*\" then\n dynamic_meta:set(\"envoy.filters.http.lua\", \"pomerium_local_reply_type\", \"html\")\n return\n elseif v.type == \"text/plain\" then\n dynamic_meta:set(\"envoy.filters.http.lua\", \"pomerium_local_reply_type\", \"plain\")\n return\n elseif v.type == \"application/json\" or v.type == \"application/*\" then\n dynamic_meta:set(\"envoy.filters.http.lua\", \"pomerium_local_reply_type\", \"json\")\n return\n end\n end\n -- if nothing matched, just return html\n dynamic_meta:set(\"envoy.filters.http.lua\", \"pomerium_local_reply_type\", \"html\")\nend\n\nfunction envoy_on_response(response_handle)\n -- unused\nend\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.http.router",
|
||||
"typedConfig": {
|
||||
|
@ -113,11 +120,160 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"http2ProtocolOptions": {
|
||||
"allowConnect": true
|
||||
},
|
||||
"localReplyConfig": {
|
||||
"mappers": [
|
||||
{
|
||||
"filter": {
|
||||
"andFilter": {
|
||||
"filters": [
|
||||
{
|
||||
"responseFlagFilter": {
|
||||
"flags": [
|
||||
"DC",
|
||||
"DF",
|
||||
"DI",
|
||||
"DO",
|
||||
"DPE",
|
||||
"DT",
|
||||
"FI",
|
||||
"IH",
|
||||
"LH",
|
||||
"LR",
|
||||
"NC",
|
||||
"NFCF",
|
||||
"NR",
|
||||
"OM",
|
||||
"RFCF",
|
||||
"RL",
|
||||
"RLSE",
|
||||
"SI",
|
||||
"UC",
|
||||
"UF",
|
||||
"UH",
|
||||
"UMSDR",
|
||||
"UO",
|
||||
"UPE",
|
||||
"UR",
|
||||
"URX",
|
||||
"UT"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadataFilter": {
|
||||
"matcher": {
|
||||
"filter": "envoy.filters.http.lua",
|
||||
"path": [{ "key": "pomerium_local_reply_type" }],
|
||||
"value": {
|
||||
"stringMatch": {
|
||||
"exact": "plain"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"bodyFormatOverride": {
|
||||
"contentType": "text/plain; charset=UTF-8",
|
||||
"textFormatSource": {
|
||||
"inlineBytes": "ewogICJyZXF1ZXN0SWQiOiAiJVNUUkVBTV9JRCUiLAogICJzdGF0dXMiOiAiJVJFU1BPTlNFX0NPREUlIiwKICAic3RhdHVzVGV4dCI6ICIlUkVTUE9OU0VfQ09ERV9ERVRBSUxTJSIKfQ=="
|
||||
}
|
||||
},
|
||||
"headersToAdd": [
|
||||
{
|
||||
"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
|
||||
"header": {
|
||||
"key": "X-Frame-Options",
|
||||
"value": "SAMEORIGIN"
|
||||
}
|
||||
},
|
||||
{
|
||||
"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
|
||||
"header": {
|
||||
"key": "X-XSS-Protection",
|
||||
"value": "1; mode=block"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"filter": {
|
||||
"andFilter": {
|
||||
"filters": [
|
||||
{
|
||||
"responseFlagFilter": {
|
||||
"flags": [
|
||||
"DC",
|
||||
"DF",
|
||||
"DI",
|
||||
"DO",
|
||||
"DPE",
|
||||
"DT",
|
||||
"FI",
|
||||
"IH",
|
||||
"LH",
|
||||
"LR",
|
||||
"NC",
|
||||
"NFCF",
|
||||
"NR",
|
||||
"OM",
|
||||
"RFCF",
|
||||
"RL",
|
||||
"RLSE",
|
||||
"SI",
|
||||
"UC",
|
||||
"UF",
|
||||
"UH",
|
||||
"UMSDR",
|
||||
"UO",
|
||||
"UPE",
|
||||
"UR",
|
||||
"URX",
|
||||
"UT"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadataFilter": {
|
||||
"matcher": {
|
||||
"filter": "envoy.filters.http.lua",
|
||||
"path": [{ "key": "pomerium_local_reply_type" }],
|
||||
"value": {
|
||||
"stringMatch": {
|
||||
"exact": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"bodyFormatOverride": {
|
||||
"contentType": "application/json; charset=UTF-8",
|
||||
"textFormatSource": {
|
||||
"inlineBytes": "ewogICJyZXF1ZXN0SWQiOiAiJVNUUkVBTV9JRCUiLAogICJzdGF0dXMiOiAiJVJFU1BPTlNFX0NPREUlIiwKICAic3RhdHVzVGV4dCI6ICIlUkVTUE9OU0VfQ09ERV9ERVRBSUxTJSIKfQ=="
|
||||
}
|
||||
},
|
||||
"headersToAdd": [
|
||||
{
|
||||
"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
|
||||
"header": {
|
||||
"key": "X-Frame-Options",
|
||||
"value": "SAMEORIGIN"
|
||||
}
|
||||
},
|
||||
{
|
||||
"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
|
||||
"header": {
|
||||
"key": "X-XSS-Protection",
|
||||
"value": "1; mode=block"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bodyFormatOverride": {
|
||||
"contentType": "text/html; charset=UTF-8",
|
||||
|
@ -234,10 +390,6 @@
|
|||
"addressPrefix": "127.0.0.1",
|
||||
"prefixLen": 32
|
||||
},
|
||||
{
|
||||
"addressPrefix": "::1",
|
||||
"prefixLen": 128
|
||||
},
|
||||
{
|
||||
"addressPrefix": "10.0.0.0",
|
||||
"prefixLen": 8
|
||||
|
@ -250,6 +402,10 @@
|
|||
"addressPrefix": "172.16.0.0",
|
||||
"prefixLen": 12
|
||||
},
|
||||
{
|
||||
"addressPrefix": "::1",
|
||||
"prefixLen": 128
|
||||
},
|
||||
{
|
||||
"addressPrefix": "fd00::",
|
||||
"prefixLen": 8
|
||||
|
|
|
@ -61,10 +61,6 @@
|
|||
"addressPrefix": "127.0.0.1",
|
||||
"prefixLen": 32
|
||||
},
|
||||
{
|
||||
"addressPrefix": "::1",
|
||||
"prefixLen": 128
|
||||
},
|
||||
{
|
||||
"addressPrefix": "10.0.0.0",
|
||||
"prefixLen": 8
|
||||
|
@ -77,6 +73,10 @@
|
|||
"addressPrefix": "172.16.0.0",
|
||||
"prefixLen": 12
|
||||
},
|
||||
{
|
||||
"addressPrefix": "::1",
|
||||
"prefixLen": 128
|
||||
},
|
||||
{
|
||||
"addressPrefix": "fd00::",
|
||||
"prefixLen": 8
|
||||
|
|
|
@ -82,7 +82,7 @@ func TestValidateCertificate(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_buildDownstreamTLSContext(t *testing.T) {
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil)
|
||||
b := New("local-grpc", "local-http", "local-metrics", filemgr.NewManager(), nil, true)
|
||||
|
||||
cacheDir, _ := os.UserCacheDir()
|
||||
clientCAFileName := filepath.Join(cacheDir, "pomerium", "envoy", "files", "client-ca-4e4c564e5a36544a4a33385a.pem")
|
||||
|
|
|
@ -13,13 +13,14 @@ import (
|
|||
metadatav3 "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v3"
|
||||
envoy_tracing_v3 "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3"
|
||||
envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
extensions_trace_context "github.com/pomerium/envoy-custom/api/extensions/http/early_header_mutation/trace_context"
|
||||
extensions_uuidx "github.com/pomerium/envoy-custom/api/extensions/request_id/uuidx"
|
||||
extensions_pomerium_otel "github.com/pomerium/envoy-custom/api/extensions/tracers/pomerium_otel"
|
||||
"github.com/pomerium/pomerium/config/otelconfig"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
func isTracingEnabled(cfg *otelconfig.Config) bool {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/middleware"
|
||||
"github.com/pomerium/pomerium/internal/telemetry"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/metrics"
|
||||
metrics_const "github.com/pomerium/pomerium/pkg/metrics"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -92,16 +93,20 @@ func (mgr *MetricsManager) updateServer(ctx context.Context, cfg *Config) {
|
|||
mgr.handler = nil
|
||||
|
||||
if mgr.addr == "" {
|
||||
log.Ctx(ctx).Info().Msg("metrics: http server disabled")
|
||||
return
|
||||
}
|
||||
|
||||
var labels map[string]string
|
||||
if cfg.Options.IsRuntimeFlagSet(RuntimeFlagAddExtraMetricsLabels) {
|
||||
labels = getCommonLabels(mgr.installationID)
|
||||
}
|
||||
|
||||
mgr.endpoints = append(cfg.MetricsScrapeEndpoints,
|
||||
MetricsScrapeEndpoint{
|
||||
Name: "envoy",
|
||||
URL: url.URL{Scheme: "http", Host: cfg.Options.MetricsAddr, Path: "/metrics/envoy"},
|
||||
})
|
||||
handler, err := metrics.PrometheusHandler(toInternalEndpoints(mgr.endpoints), mgr.installationID, defaultMetricsTimeout)
|
||||
handler, err := metrics.PrometheusHandler(toInternalEndpoints(mgr.endpoints), defaultMetricsTimeout, labels)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("metrics: failed to create prometheus handler")
|
||||
return
|
||||
|
@ -128,3 +133,17 @@ func toInternalEndpoints(src []MetricsScrapeEndpoint) []metrics.ScrapeEndpoint {
|
|||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func getCommonLabels(installationID string) map[string]string {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
hostname = "__none__"
|
||||
}
|
||||
m := map[string]string{
|
||||
metrics_const.HostnameLabel: hostname,
|
||||
}
|
||||
if installationID != "" {
|
||||
m[metrics_const.InstallationIDLabel] = installationID
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ import (
|
|||
"time"
|
||||
|
||||
envoy_http_connection_manager "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
goset "github.com/hashicorp/go-set/v3"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/volatiletech/null/v9"
|
||||
|
@ -196,6 +196,12 @@ type Options struct {
|
|||
// List of JWT claims to insert as x-pomerium-claim-* headers on proxied requests
|
||||
JWTClaimsHeaders JWTClaimHeaders `mapstructure:"jwt_claims_headers" yaml:"jwt_claims_headers,omitempty"`
|
||||
|
||||
// JWTIssuerFormat controls the default format of the 'iss' claim in JWTs passed to upstream services.
|
||||
// Possible values:
|
||||
// - "hostOnly" (default): Issuer strings will be the hostname of the route, with no scheme or trailing slash.
|
||||
// - "uri": Issuer strings will be a complete URI, including the scheme and ending with a trailing slash.
|
||||
JWTIssuerFormat JWTIssuerFormat `mapstructure:"jwt_issuer_format" yaml:"jwt_issuer_format,omitempty"`
|
||||
|
||||
// BearerTokenFormat indicates how authorization bearer tokens are interepreted. Possible values:
|
||||
// - "default": Only Bearer tokens prefixed with Pomerium- will be interpreted by Pomerium.
|
||||
// - "idp_access_token": The Bearer token will be interpreted as an IdP access token.
|
||||
|
@ -405,7 +411,7 @@ func checkConfigKeysErrors(configFile string, o *Options, unused []string) error
|
|||
default:
|
||||
evt = log.Ctx(ctx).Error()
|
||||
}
|
||||
evt.Str("config_file", configFile).Str("key", check.Key)
|
||||
evt.Str("config-file", configFile).Str("key", check.Key)
|
||||
if check.DocsURL != "" {
|
||||
evt = evt.Str("help", check.DocsURL)
|
||||
}
|
||||
|
@ -415,7 +421,7 @@ func checkConfigKeysErrors(configFile string, o *Options, unused []string) error
|
|||
// check for unknown runtime flags
|
||||
for flag := range o.RuntimeFlags {
|
||||
if _, ok := defaultRuntimeFlags[flag]; !ok {
|
||||
log.Ctx(ctx).Error().Str("config_file", configFile).Str("flag", string(flag)).Msg("unknown runtime flag")
|
||||
log.Ctx(ctx).Error().Str("config-file", configFile).Str("flag", string(flag)).Msg("unknown runtime flag")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -649,14 +655,11 @@ func (o *Options) Validate() error {
|
|||
return fmt.Errorf("config: failed to parse headers: %w", err)
|
||||
}
|
||||
|
||||
hasCert := false
|
||||
|
||||
if o.Cert != "" || o.Key != "" {
|
||||
_, err := cryptutil.CertificateFromBase64(o.Cert, o.Key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config: bad cert base64 %w", err)
|
||||
}
|
||||
hasCert = true
|
||||
}
|
||||
|
||||
for _, c := range o.CertificateData {
|
||||
|
@ -664,7 +667,6 @@ func (o *Options) Validate() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("config: bad cert entry, cert is invalid: %w", err)
|
||||
}
|
||||
hasCert = true
|
||||
}
|
||||
|
||||
for _, c := range o.CertificateFiles {
|
||||
|
@ -672,7 +674,6 @@ func (o *Options) Validate() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("config: bad cert entry, file reference invalid. %w", err)
|
||||
}
|
||||
hasCert = true
|
||||
}
|
||||
|
||||
if o.CertFile != "" || o.KeyFile != "" {
|
||||
|
@ -680,7 +681,6 @@ func (o *Options) Validate() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("config: bad cert file %w", err)
|
||||
}
|
||||
hasCert = true
|
||||
}
|
||||
|
||||
if err := o.DownstreamMTLS.validate(); err != nil {
|
||||
|
@ -690,11 +690,6 @@ func (o *Options) Validate() error {
|
|||
// strip quotes from redirect address (#811)
|
||||
o.HTTPRedirectAddr = strings.Trim(o.HTTPRedirectAddr, `"'`)
|
||||
|
||||
if !o.InsecureServer && !hasCert && !o.AutocertOptions.Enable {
|
||||
log.Ctx(ctx).Info().Msg("neither `autocert`, " +
|
||||
"`insecure_server` or manually provided certificates were provided, server will be using a self-signed certificate")
|
||||
}
|
||||
|
||||
if err := ValidateDNSLookupFamily(o.DNSLookupFamily); err != nil {
|
||||
return fmt.Errorf("config: %w", err)
|
||||
}
|
||||
|
@ -761,6 +756,10 @@ func (o *Options) Validate() error {
|
|||
}
|
||||
}
|
||||
|
||||
if !o.JWTIssuerFormat.Valid() {
|
||||
return fmt.Errorf("config: unsupported jwt_issuer_format value %q", o.JWTIssuerFormat)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1274,23 +1273,27 @@ func (o *Options) GetAllRouteableGRPCHosts() ([]string, error) {
|
|||
}
|
||||
|
||||
// GetAllRouteableHTTPHosts returns all the possible HTTP hosts handled by the Pomerium options.
|
||||
func (o *Options) GetAllRouteableHTTPHosts() ([]string, error) {
|
||||
func (o *Options) GetAllRouteableHTTPHosts() ([]string, map[string]bool, error) {
|
||||
hosts := goset.NewTreeSet(cmp.Compare[string])
|
||||
mcpHosts := make(map[string]bool)
|
||||
|
||||
if IsAuthenticate(o.Services) {
|
||||
if o.AuthenticateInternalURLString != "" {
|
||||
authenticateURL, err := o.GetInternalAuthenticateURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
hosts.InsertSlice(urlutil.GetDomainsForURL(authenticateURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort)))
|
||||
domains := urlutil.GetDomainsForURL(authenticateURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort))
|
||||
hosts.InsertSlice(domains)
|
||||
}
|
||||
|
||||
if o.AuthenticateURLString != "" {
|
||||
authenticateURL, err := o.GetAuthenticateURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
hosts.InsertSlice(urlutil.GetDomainsForURL(authenticateURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort)))
|
||||
domains := urlutil.GetDomainsForURL(authenticateURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort))
|
||||
hosts.InsertSlice(domains)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1299,18 +1302,35 @@ func (o *Options) GetAllRouteableHTTPHosts() ([]string, error) {
|
|||
for policy := range o.GetAllPolicies() {
|
||||
fromURL, err := urlutil.ParseAndValidateURL(policy.From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
domains := urlutil.GetDomainsForURL(fromURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort))
|
||||
hosts.InsertSlice(domains)
|
||||
|
||||
// Track if the domains are associated with an MCP policy
|
||||
if policy.IsMCP() {
|
||||
for _, domain := range domains {
|
||||
mcpHosts[domain] = true
|
||||
}
|
||||
}
|
||||
|
||||
hosts.InsertSlice(urlutil.GetDomainsForURL(fromURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort)))
|
||||
if policy.TLSDownstreamServerName != "" {
|
||||
tlsURL := fromURL.ResolveReference(&url.URL{Host: policy.TLSDownstreamServerName})
|
||||
hosts.InsertSlice(urlutil.GetDomainsForURL(tlsURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort)))
|
||||
tlsDomains := urlutil.GetDomainsForURL(tlsURL, !o.IsRuntimeFlagSet(RuntimeFlagMatchAnyIncomingPort))
|
||||
hosts.InsertSlice(tlsDomains)
|
||||
|
||||
// Track if the TLS domains are associated with an MCP policy
|
||||
if policy.IsMCP() {
|
||||
for _, domain := range tlsDomains {
|
||||
mcpHosts[domain] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hosts.Slice(), nil
|
||||
return hosts.Slice(), mcpHosts, nil
|
||||
}
|
||||
|
||||
// GetClientSecret gets the client secret.
|
||||
|
@ -1499,8 +1519,6 @@ func (o *Options) ApplySettings(ctx context.Context, certsIndex *cryptutil.Certi
|
|||
if settings.IdpAccessTokenAllowedAudiences != nil {
|
||||
values := slices.Clone(settings.IdpAccessTokenAllowedAudiences.Values)
|
||||
o.IDPAccessTokenAllowedAudiences = &values
|
||||
} else {
|
||||
o.IDPAccessTokenAllowedAudiences = nil
|
||||
}
|
||||
setSlice(&o.AuthorizeURLStrings, settings.AuthorizeServiceUrls)
|
||||
set(&o.AuthorizeInternalURLString, settings.AuthorizeInternalServiceUrl)
|
||||
|
@ -1510,10 +1528,13 @@ func (o *Options) ApplySettings(ctx context.Context, certsIndex *cryptutil.Certi
|
|||
set(&o.SigningKey, settings.SigningKey)
|
||||
setMap(&o.SetResponseHeaders, settings.SetResponseHeaders)
|
||||
setMap(&o.JWTClaimsHeaders, settings.JwtClaimsHeaders)
|
||||
o.BearerTokenFormat = BearerTokenFormatFromPB(settings.BearerTokenFormat)
|
||||
setOptional(&o.BearerTokenFormat, BearerTokenFormatFromPB(settings.BearerTokenFormat))
|
||||
if len(settings.JwtGroupsFilter) > 0 {
|
||||
o.JWTGroupsFilter = NewJWTGroupsFilter(settings.JwtGroupsFilter)
|
||||
}
|
||||
if f := JWTIssuerFormatFromPB(settings.JwtIssuerFormat); f != JWTIssuerFormatUnset {
|
||||
o.JWTIssuerFormat = f
|
||||
}
|
||||
setDuration(&o.DefaultUpstreamTimeout, settings.DefaultUpstreamTimeout)
|
||||
set(&o.MetricsAddr, settings.MetricsAddress)
|
||||
set(&o.MetricsBasicAuth, settings.MetricsBasicAuth)
|
||||
|
@ -1624,6 +1645,7 @@ func (o *Options) ToProto() *config.Config {
|
|||
settings.JwtClaimsHeaders = o.JWTClaimsHeaders
|
||||
settings.BearerTokenFormat = o.BearerTokenFormat.ToPB()
|
||||
settings.JwtGroupsFilter = o.JWTGroupsFilter.ToSlice()
|
||||
settings.JwtIssuerFormat = o.JWTIssuerFormat.ToPB()
|
||||
copyOptionalDuration(&settings.DefaultUpstreamTimeout, o.DefaultUpstreamTimeout)
|
||||
copySrcToOptionalDest(&settings.MetricsAddress, &o.MetricsAddr)
|
||||
copySrcToOptionalDest(&settings.MetricsBasicAuth, &o.MetricsBasicAuth)
|
||||
|
@ -1859,13 +1881,6 @@ func compareByteSliceSlice(a, b [][]byte) int {
|
|||
}
|
||||
}
|
||||
|
||||
func min(x, y int) int {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// NewAtomicOptions creates a new AtomicOptions.
|
||||
func NewAtomicOptions() *atomicutil.Value[*Options] {
|
||||
return atomicutil.NewValue(new(Options))
|
||||
|
|
|
@ -331,7 +331,7 @@ func Test_parsePolicyFile(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempFile, _ := os.CreateTemp("", "*.json")
|
||||
tempFile, _ := os.CreateTemp(t.TempDir(), "*.json")
|
||||
defer tempFile.Close()
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Write(tt.policyBytes)
|
||||
|
@ -462,7 +462,7 @@ func TestOptionsFromViper(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempFile, _ := os.CreateTemp("", "*.json")
|
||||
tempFile, _ := os.CreateTemp(t.TempDir(), "*.json")
|
||||
defer tempFile.Close()
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Write(tt.configBytes)
|
||||
|
@ -506,8 +506,7 @@ func Test_NewOptionsFromConfigEnvVar(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for k, v := range tt.envKeyPairs {
|
||||
os.Setenv(k, v)
|
||||
defer os.Unsetenv(k)
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
_, err := newOptionsFromConfig("")
|
||||
if (err != nil) != tt.wantErr {
|
||||
|
@ -578,7 +577,7 @@ func Test_AutoCertOptionsFromEnvVar(t *testing.T) {
|
|||
"ok/custom-ca-file": func(t *testing.T) test {
|
||||
certPEM, err := newCACertPEM()
|
||||
require.NoError(t, err)
|
||||
f, err := os.CreateTemp("", "pomerium-test-ca")
|
||||
f, err := os.CreateTemp(t.TempDir(), "pomerium-test-ca")
|
||||
require.NoError(t, err)
|
||||
n, err := f.Write(certPEM)
|
||||
require.NoError(t, err)
|
||||
|
@ -617,8 +616,7 @@ func Test_AutoCertOptionsFromEnvVar(t *testing.T) {
|
|||
tc := run(t)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
for k, v := range tc.envs {
|
||||
os.Setenv(k, v)
|
||||
defer os.Unsetenv(k)
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
o, err := newOptionsFromConfig("")
|
||||
if err != nil {
|
||||
|
@ -658,7 +656,6 @@ func TestCertificatesArrayParsing(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -827,7 +824,6 @@ func TestOptions_DefaultURL(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
u, err := tc.f()
|
||||
|
@ -892,22 +888,26 @@ func TestOptions_GetAllRouteableGRPCHosts(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOptions_GetAllRouteableHTTPHosts(t *testing.T) {
|
||||
p1 := Policy{From: "https://from1.example.com"}
|
||||
p1.Validate()
|
||||
p2 := Policy{From: "https://from2.example.com"}
|
||||
p2.Validate()
|
||||
p3 := Policy{From: "https://from3.example.com", TLSDownstreamServerName: "from.example.com"}
|
||||
p3.Validate()
|
||||
to := WeightedURLs{{URL: url.URL{Scheme: "https", Host: "to.example.com"}}}
|
||||
p1 := Policy{From: "https://from1.example.com", To: to}
|
||||
assert.NoError(t, p1.Validate())
|
||||
p2 := Policy{From: "https://from2.example.com", To: to}
|
||||
assert.NoError(t, p2.Validate())
|
||||
p3 := Policy{From: "https://from3.example.com", TLSDownstreamServerName: "from.example.com", To: to}
|
||||
assert.NoError(t, p3.Validate())
|
||||
p4 := Policy{From: "https://from4.example.com", MCP: &MCP{}, To: to}
|
||||
assert.NoError(t, p4.Validate())
|
||||
|
||||
opts := &Options{
|
||||
AuthenticateURLString: "https://authenticate.example.com",
|
||||
AuthorizeURLString: "https://authorize.example.com",
|
||||
DataBrokerURLString: "https://databroker.example.com",
|
||||
Policies: []Policy{p1, p2, p3},
|
||||
Policies: []Policy{p1, p2, p3, p4},
|
||||
Services: "all",
|
||||
}
|
||||
hosts, err := opts.GetAllRouteableHTTPHosts()
|
||||
hosts, mcpHosts, err := opts.GetAllRouteableHTTPHosts()
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, cmp.Diff(mcpHosts, map[string]bool{"from4.example.com:443": true, "from4.example.com": true}))
|
||||
|
||||
assert.Equal(t, []string{
|
||||
"authenticate.example.com",
|
||||
|
@ -920,10 +920,14 @@ func TestOptions_GetAllRouteableHTTPHosts(t *testing.T) {
|
|||
"from2.example.com:443",
|
||||
"from3.example.com",
|
||||
"from3.example.com:443",
|
||||
"from4.example.com",
|
||||
"from4.example.com:443",
|
||||
}, hosts)
|
||||
}
|
||||
|
||||
func TestOptions_ApplySettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second)
|
||||
defer clearTimeout()
|
||||
|
||||
|
@ -989,6 +993,48 @@ func TestOptions_ApplySettings(t *testing.T) {
|
|||
})
|
||||
assert.Equal(t, NewJWTGroupsFilter([]string{"quux", "zulu"}), options.JWTGroupsFilter)
|
||||
})
|
||||
|
||||
t.Run("jwt_issuer_format", func(t *testing.T) {
|
||||
options := NewDefaultOptions()
|
||||
assert.Equal(t, JWTIssuerFormatUnset, options.JWTIssuerFormat)
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{
|
||||
JwtIssuerFormat: configpb.IssuerFormat_IssuerURI.Enum(),
|
||||
})
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{})
|
||||
assert.Equal(t, JWTIssuerFormatURI, options.JWTIssuerFormat)
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{
|
||||
JwtIssuerFormat: configpb.IssuerFormat_IssuerHostOnly.Enum(),
|
||||
})
|
||||
assert.Equal(t, JWTIssuerFormatHostOnly, options.JWTIssuerFormat)
|
||||
})
|
||||
|
||||
t.Run("bearer_token_format", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
options := NewDefaultOptions()
|
||||
assert.Nil(t, options.BearerTokenFormat)
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{
|
||||
BearerTokenFormat: configpb.BearerTokenFormat_BEARER_TOKEN_FORMAT_DEFAULT.Enum(),
|
||||
})
|
||||
assert.Equal(t, ptr(BearerTokenFormatDefault), options.BearerTokenFormat)
|
||||
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{})
|
||||
assert.Equal(t, ptr(BearerTokenFormatDefault), options.BearerTokenFormat, "should preserve existing bearer token format")
|
||||
})
|
||||
|
||||
t.Run("idp_access_token_allowed_audiences", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
options := NewDefaultOptions()
|
||||
assert.Nil(t, options.IDPAccessTokenAllowedAudiences)
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{
|
||||
IdpAccessTokenAllowedAudiences: &configpb.Settings_StringList{Values: []string{"x", "y", "z"}},
|
||||
})
|
||||
assert.Equal(t, ptr([]string{"x", "y", "z"}), options.IDPAccessTokenAllowedAudiences)
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{})
|
||||
assert.Equal(t, ptr([]string{"x", "y", "z"}), options.IDPAccessTokenAllowedAudiences,
|
||||
"should preserve idp access token allowed audiences")
|
||||
})
|
||||
}
|
||||
|
||||
func TestOptions_GetSetResponseHeaders(t *testing.T) {
|
||||
|
@ -1191,7 +1237,6 @@ LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IUUNBUUVFSUdHaDZGbEJlOHl5OWRSSmdtKzM1
|
|||
0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||
}, nil},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1241,7 +1286,6 @@ func TestOptions_GetCookieSameSite(t *testing.T) {
|
|||
{"none", http.SameSiteNoneMode},
|
||||
{"UnKnOwN", http.SameSiteDefaultMode},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.input, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1270,7 +1314,6 @@ func TestOptions_GetCSRFSameSite(t *testing.T) {
|
|||
{"UnKnOwN", "", csrf.SameSiteDefaultMode},
|
||||
{"", apple.Name, csrf.SameSiteNoneMode},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.cookieSameSite, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1748,3 +1791,7 @@ func must[T any](t T, err error) T {
|
|||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ type Policy struct {
|
|||
// Possible values:
|
||||
// - "hostOnly" (default): Issuer strings will be the hostname of the route, with no scheme or trailing slash.
|
||||
// - "uri": Issuer strings will be a complete URI, including the scheme and ending with a trailing slash.
|
||||
JWTIssuerFormat string `mapstructure:"jwt_issuer_format" yaml:"jwt_issuer_format,omitempty"`
|
||||
JWTIssuerFormat JWTIssuerFormat `mapstructure:"jwt_issuer_format" yaml:"jwt_issuer_format,omitempty"`
|
||||
// BearerTokenFormat indicates how authorization bearer tokens are interepreted. Possible values:
|
||||
// - "default": Only Bearer tokens prefixed with Pomerium- will be interpreted by Pomerium
|
||||
// - "idp_access_token": The Bearer token will be interpreted as an IdP access token.
|
||||
|
@ -200,8 +200,16 @@ type Policy struct {
|
|||
ShowErrorDetails bool `mapstructure:"show_error_details" yaml:"show_error_details" json:"show_error_details"`
|
||||
|
||||
Policy *PPLPolicy `mapstructure:"policy" yaml:"policy,omitempty" json:"policy,omitempty"`
|
||||
|
||||
DependsOn []string `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"`
|
||||
|
||||
// MCP is an experimental support for Model Context Protocol upstreams
|
||||
MCP *MCP `mapstructure:"mcp" yaml:"mcp,omitempty" json:"mcp,omitempty"`
|
||||
}
|
||||
|
||||
// MCP is an experimental support for Model Context Protocol upstreams configuration
|
||||
type MCP struct{}
|
||||
|
||||
// RewriteHeader is a policy configuration option to rewrite an HTTP header.
|
||||
type RewriteHeader struct {
|
||||
Header string `mapstructure:"header" yaml:"header" json:"header"`
|
||||
|
@ -298,6 +306,7 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
|||
AllowWebsockets: pb.GetAllowWebsockets(),
|
||||
CORSAllowPreflight: pb.GetCorsAllowPreflight(),
|
||||
Description: pb.GetDescription(),
|
||||
DependsOn: pb.GetDependsOn(),
|
||||
EnableGoogleCloudServerlessAuthentication: pb.GetEnableGoogleCloudServerlessAuthentication(),
|
||||
From: pb.GetFrom(),
|
||||
HostPathRegexRewritePattern: pb.GetHostPathRegexRewritePattern(),
|
||||
|
@ -309,9 +318,11 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
|||
IDPClientID: pb.GetIdpClientId(),
|
||||
IDPClientSecret: pb.GetIdpClientSecret(),
|
||||
JWTGroupsFilter: NewJWTGroupsFilter(pb.JwtGroupsFilter),
|
||||
JWTIssuerFormat: JWTIssuerFormatFromPB(pb.JwtIssuerFormat),
|
||||
KubernetesServiceAccountToken: pb.GetKubernetesServiceAccountToken(),
|
||||
KubernetesServiceAccountTokenFile: pb.GetKubernetesServiceAccountTokenFile(),
|
||||
LogoURL: pb.GetLogoUrl(),
|
||||
MCP: MCPFromPB(pb.GetMcp()),
|
||||
Name: pb.GetName(),
|
||||
PassIdentityHeaders: pb.PassIdentityHeaders,
|
||||
Path: pb.GetPath(),
|
||||
|
@ -388,13 +399,6 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
|||
p.EnvoyOpts.Name = pb.Name
|
||||
}
|
||||
|
||||
switch pb.GetJwtIssuerFormat() {
|
||||
case configpb.IssuerFormat_IssuerHostOnly:
|
||||
p.JWTIssuerFormat = "hostOnly"
|
||||
case configpb.IssuerFormat_IssuerURI:
|
||||
p.JWTIssuerFormat = "uri"
|
||||
}
|
||||
|
||||
p.BearerTokenFormat = BearerTokenFormatFromPB(pb.BearerTokenFormat)
|
||||
|
||||
for _, rwh := range pb.RewriteResponseHeaders {
|
||||
|
@ -462,15 +466,18 @@ func (p *Policy) ToProto() (*configpb.Route, error) {
|
|||
AllowWebsockets: p.AllowWebsockets,
|
||||
CorsAllowPreflight: p.CORSAllowPreflight,
|
||||
Description: p.Description,
|
||||
DependsOn: p.DependsOn,
|
||||
EnableGoogleCloudServerlessAuthentication: p.EnableGoogleCloudServerlessAuthentication,
|
||||
EnvoyOpts: p.EnvoyOpts,
|
||||
From: p.From,
|
||||
Id: p.ID,
|
||||
IdleTimeout: idleTimeout,
|
||||
JwtGroupsFilter: p.JWTGroupsFilter.ToSlice(),
|
||||
JwtIssuerFormat: p.JWTIssuerFormat.ToPB(),
|
||||
KubernetesServiceAccountToken: p.KubernetesServiceAccountToken,
|
||||
KubernetesServiceAccountTokenFile: p.KubernetesServiceAccountTokenFile,
|
||||
LogoUrl: p.LogoURL,
|
||||
Mcp: MCPToPB(p.MCP),
|
||||
Name: p.Name,
|
||||
PassIdentityHeaders: p.PassIdentityHeaders,
|
||||
Path: p.Path,
|
||||
|
@ -555,13 +562,6 @@ func (p *Policy) ToProto() (*configpb.Route, error) {
|
|||
pb.LoadBalancingWeights = weights
|
||||
}
|
||||
|
||||
switch p.JWTIssuerFormat {
|
||||
case "", "hostOnly":
|
||||
pb.JwtIssuerFormat = configpb.IssuerFormat_IssuerHostOnly
|
||||
case "uri":
|
||||
pb.JwtIssuerFormat = configpb.IssuerFormat_IssuerURI
|
||||
}
|
||||
|
||||
pb.BearerTokenFormat = p.BearerTokenFormat.ToPB()
|
||||
|
||||
for _, rwh := range p.RewriteResponseHeaders {
|
||||
|
@ -698,6 +698,14 @@ func (p *Policy) Validate() error {
|
|||
p.compiledRegex, _ = regexp.Compile(rawRE)
|
||||
}
|
||||
|
||||
if !p.JWTIssuerFormat.Valid() {
|
||||
return fmt.Errorf("config: unsupported jwt_issuer_format value %q", p.JWTIssuerFormat)
|
||||
}
|
||||
|
||||
if len(p.DependsOn) > 5 {
|
||||
return fmt.Errorf("config: depends_on is limited to 5 additional redirect hosts, got %v", p.DependsOn)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -824,6 +832,11 @@ func (p *Policy) IsForKubernetes() bool {
|
|||
return p.KubernetesServiceAccountTokenFile != "" || p.KubernetesServiceAccountToken != ""
|
||||
}
|
||||
|
||||
// IsMCP returns true if the route is for the Model Context Protocol upstream server.
|
||||
func (p *Policy) IsMCP() bool {
|
||||
return p != nil && p.MCP != nil
|
||||
}
|
||||
|
||||
// IsTCP returns true if the route is for TCP.
|
||||
func (p *Policy) IsTCP() bool {
|
||||
return strings.HasPrefix(p.From, "tcp")
|
||||
|
|
|
@ -56,6 +56,7 @@ func Test_PolicyValidate(t *testing.T) {
|
|||
{"TCP To URLs", Policy{From: "tcp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "tcp://one.example.com:5000", "tcp://two.example.com:5000")}, false},
|
||||
{"mix of TCP and non-TCP To URLs", Policy{From: "tcp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "https://example.com", "tcp://example.com:5000")}, true},
|
||||
{"UDP To URLs", Policy{From: "udp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "udp://one.example.com:5000", "udp://two.example.com:5000")}, false},
|
||||
{"too many depends_on hosts", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), DependsOn: []string{"a", "b", "c", "d", "e", "f"}}, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@ -176,7 +177,6 @@ func Test_PolicyRouteID(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.NoError(t, tt.basePolicy.Validate())
|
||||
|
@ -292,6 +292,22 @@ func TestPolicy_FromToPb(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, p.Redirect.HTTPSRedirect, policyFromProto.Redirect.HTTPSRedirect)
|
||||
})
|
||||
|
||||
t.Run("JWT issuer format", func(t *testing.T) {
|
||||
for f := range knownJWTIssuerFormats {
|
||||
p := &Policy{
|
||||
From: "https://pomerium.io",
|
||||
To: mustParseWeightedURLs(t, "http://localhost"),
|
||||
JWTIssuerFormat: f,
|
||||
}
|
||||
pbPolicy, err := p.ToProto()
|
||||
require.NoError(t, err)
|
||||
|
||||
policyFromPb, err := NewPolicyFromProto(pbPolicy)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, f, policyFromPb.JWTIssuerFormat)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPolicy_Matches(t *testing.T) {
|
||||
|
|
|
@ -14,9 +14,6 @@ var (
|
|||
// RuntimeFlagGRPCDatabrokerKeepalive enables gRPC keepalive to the databroker service
|
||||
RuntimeFlagGRPCDatabrokerKeepalive = runtimeFlag("grpc_databroker_keepalive", false)
|
||||
|
||||
// RuntimeFlagLegacyIdentityManager enables the legacy identity manager
|
||||
RuntimeFlagLegacyIdentityManager = runtimeFlag("legacy_identity_manager", false)
|
||||
|
||||
// RuntimeFlagMatchAnyIncomingPort enables ignoring the incoming port when matching routes
|
||||
RuntimeFlagMatchAnyIncomingPort = runtimeFlag("match_any_incoming_port", true)
|
||||
|
||||
|
@ -25,6 +22,13 @@ var (
|
|||
// is deprecated pending removal in a future release, but this flag allows a temporary
|
||||
// opt-out from the deprecation.
|
||||
RuntimeFlagPomeriumJWTEndpoint = runtimeFlag("pomerium_jwt_endpoint", false)
|
||||
|
||||
// RuntimeFlagAddExtraMetricsLabels enables adding extra labels to metrics (host and installation id)
|
||||
RuntimeFlagAddExtraMetricsLabels = runtimeFlag("add_extra_metrics_labels", true)
|
||||
|
||||
// RuntimeFlagAuthorizeUseSyncedData enables synced data for querying the databroker for
|
||||
// certain types of data.
|
||||
RuntimeFlagAuthorizeUseSyncedData = runtimeFlag("authorize_use_synced_data", true)
|
||||
)
|
||||
|
||||
// RuntimeFlag is a runtime flag that can flip on/off certain features
|
||||
|
|
|
@ -202,7 +202,7 @@ func (c *incomingIDPTokenSessionCreator) createSessionAccessToken(
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("error verifying access token: %w", err)
|
||||
} else if !res.Valid {
|
||||
return nil, fmt.Errorf("invalid access token")
|
||||
return nil, fmt.Errorf("%w: invalid access token", sessions.ErrInvalidSession)
|
||||
}
|
||||
|
||||
s = c.newSessionFromIDPClaims(cfg, sessionID, res.Claims)
|
||||
|
@ -265,7 +265,7 @@ func (c *incomingIDPTokenSessionCreator) createSessionForIdentityToken(
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("error verifying identity token: %w", err)
|
||||
} else if !res.Valid {
|
||||
return nil, fmt.Errorf("invalid identity token")
|
||||
return nil, fmt.Errorf("%w: invalid identity token", sessions.ErrInvalidSession)
|
||||
}
|
||||
|
||||
s = c.newSessionFromIDPClaims(cfg, sessionID, res.Claims)
|
||||
|
@ -405,22 +405,8 @@ func (cfg *Config) GetIncomingIDPAccessTokenForPolicy(policy *Policy, r *http.Re
|
|||
bearerTokenFormat = *policy.BearerTokenFormat
|
||||
}
|
||||
|
||||
if token := r.Header.Get(httputil.HeaderPomeriumIDPAccessToken); token != "" {
|
||||
return token, true
|
||||
}
|
||||
|
||||
if auth := r.Header.Get(httputil.HeaderAuthorization); auth != "" {
|
||||
prefix := httputil.AuthorizationTypePomeriumIDPAccessToken + " "
|
||||
if strings.HasPrefix(strings.ToLower(auth), strings.ToLower(prefix)) {
|
||||
return auth[len(prefix):], true
|
||||
}
|
||||
|
||||
prefix = "Bearer " + httputil.AuthorizationTypePomeriumIDPAccessToken + "-"
|
||||
if strings.HasPrefix(strings.ToLower(auth), strings.ToLower(prefix)) {
|
||||
return auth[len(prefix):], true
|
||||
}
|
||||
|
||||
prefix = "Bearer "
|
||||
prefix := "Bearer "
|
||||
if strings.HasPrefix(strings.ToLower(auth), strings.ToLower(prefix)) &&
|
||||
bearerTokenFormat == BearerTokenFormatIDPAccessToken {
|
||||
return auth[len(prefix):], true
|
||||
|
@ -440,22 +426,8 @@ func (cfg *Config) GetIncomingIDPIdentityTokenForPolicy(policy *Policy, r *http.
|
|||
bearerTokenFormat = *policy.BearerTokenFormat
|
||||
}
|
||||
|
||||
if token := r.Header.Get(httputil.HeaderPomeriumIDPIdentityToken); token != "" {
|
||||
return token, true
|
||||
}
|
||||
|
||||
if auth := r.Header.Get(httputil.HeaderAuthorization); auth != "" {
|
||||
prefix := httputil.AuthorizationTypePomeriumIDPIdentityToken + " "
|
||||
if strings.HasPrefix(strings.ToLower(auth), strings.ToLower(prefix)) {
|
||||
return auth[len(prefix):], true
|
||||
}
|
||||
|
||||
prefix = "Bearer " + httputil.AuthorizationTypePomeriumIDPIdentityToken + "-"
|
||||
if strings.HasPrefix(strings.ToLower(auth), strings.ToLower(prefix)) {
|
||||
return auth[len(prefix):], true
|
||||
}
|
||||
|
||||
prefix = "Bearer "
|
||||
prefix := "Bearer "
|
||||
if strings.HasPrefix(strings.ToLower(auth), strings.ToLower(prefix)) &&
|
||||
bearerTokenFormat == BearerTokenFormatIDPIdentityToken {
|
||||
return auth[len(prefix):], true
|
||||
|
|
|
@ -206,24 +206,6 @@ func TestGetIncomingIDPAccessTokenForPolicy(t *testing.T) {
|
|||
name: "empty headers",
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
name: "custom header",
|
||||
headers: http.Header{"X-Pomerium-Idp-Access-Token": {"access token via custom header"}},
|
||||
expectedOK: true,
|
||||
expectedToken: "access token via custom header",
|
||||
},
|
||||
{
|
||||
name: "custom authorization",
|
||||
headers: http.Header{"Authorization": {"Pomerium-Idp-Access-Token access token via custom authorization"}},
|
||||
expectedOK: true,
|
||||
expectedToken: "access token via custom authorization",
|
||||
},
|
||||
{
|
||||
name: "custom bearer",
|
||||
headers: http.Header{"Authorization": {"Bearer Pomerium-Idp-Access-Token-access token via custom bearer"}},
|
||||
expectedOK: true,
|
||||
expectedToken: "access token via custom bearer",
|
||||
},
|
||||
{
|
||||
name: "bearer disabled",
|
||||
headers: http.Header{"Authorization": {"Bearer access token via bearer"}},
|
||||
|
@ -289,24 +271,6 @@ func TestGetIncomingIDPIdentityTokenForPolicy(t *testing.T) {
|
|||
name: "empty headers",
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
name: "custom header",
|
||||
headers: http.Header{"X-Pomerium-Idp-Identity-Token": {"identity token via custom header"}},
|
||||
expectedOK: true,
|
||||
expectedToken: "identity token via custom header",
|
||||
},
|
||||
{
|
||||
name: "custom authorization",
|
||||
headers: http.Header{"Authorization": {"Pomerium-Idp-Identity-Token identity token via custom authorization"}},
|
||||
expectedOK: true,
|
||||
expectedToken: "identity token via custom authorization",
|
||||
},
|
||||
{
|
||||
name: "custom bearer",
|
||||
headers: http.Header{"Authorization": {"Bearer Pomerium-Idp-Identity-Token-identity token via custom bearer"}},
|
||||
expectedOK: true,
|
||||
expectedToken: "identity token via custom bearer",
|
||||
},
|
||||
{
|
||||
name: "bearer disabled",
|
||||
headers: http.Header{"Authorization": {"Bearer identity token via bearer"}},
|
||||
|
@ -496,12 +460,14 @@ func TestIncomingIDPTokenSessionCreator_CreateSession(t *testing.T) {
|
|||
cfg.Options.AuthenticateURLString = srv.URL
|
||||
cfg.Options.ClientSecret = "CLIENT_SECRET_1"
|
||||
cfg.Options.ClientID = "CLIENT_ID_1"
|
||||
bearerTokenFormatIDPAccessToken := BearerTokenFormatIDPAccessToken
|
||||
cfg.Options.BearerTokenFormat = &bearerTokenFormatIDPAccessToken
|
||||
route := &Policy{}
|
||||
route.IDPClientSecret = "CLIENT_SECRET_2"
|
||||
route.IDPClientID = "CLIENT_ID_2"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.example.com", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set(httputil.HeaderPomeriumIDPAccessToken, "ACCESS_TOKEN")
|
||||
req.Header.Set("Authorization", "Bearer ACCESS_TOKEN")
|
||||
c := NewIncomingIDPTokenSessionCreator(
|
||||
func(_ context.Context, _, _ string) (*databroker.Record, error) {
|
||||
return nil, storage.ErrNotFound
|
||||
|
@ -537,12 +503,14 @@ func TestIncomingIDPTokenSessionCreator_CreateSession(t *testing.T) {
|
|||
cfg.Options.AuthenticateURLString = srv.URL
|
||||
cfg.Options.ClientSecret = "CLIENT_SECRET_1"
|
||||
cfg.Options.ClientID = "CLIENT_ID_1"
|
||||
bearerTokenFormatIDPIdentityToken := BearerTokenFormatIDPIdentityToken
|
||||
cfg.Options.BearerTokenFormat = &bearerTokenFormatIDPIdentityToken
|
||||
route := &Policy{}
|
||||
route.IDPClientSecret = "CLIENT_SECRET_2"
|
||||
route.IDPClientID = "CLIENT_ID_2"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.example.com", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set(httputil.HeaderPomeriumIDPIdentityToken, "IDENTITY_TOKEN")
|
||||
req.Header.Set("Authorization", "Bearer IDENTITY_TOKEN")
|
||||
c := NewIncomingIDPTokenSessionCreator(
|
||||
func(_ context.Context, _, _ string) (*databroker.Record, error) {
|
||||
return nil, storage.ErrNotFound
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/rs/zerolog"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
@ -19,7 +20,6 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/atomicutil"
|
||||
"github.com/pomerium/pomerium/internal/events"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/internal/version"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
|
@ -27,9 +27,8 @@ import (
|
|||
"github.com/pomerium/pomerium/pkg/grpc/registry"
|
||||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||
"github.com/pomerium/pomerium/pkg/identity"
|
||||
"github.com/pomerium/pomerium/pkg/identity/legacymanager"
|
||||
"github.com/pomerium/pomerium/pkg/identity/manager"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// DataBroker represents the databroker service. The databroker service is a simple interface
|
||||
|
@ -38,7 +37,6 @@ type DataBroker struct {
|
|||
Options
|
||||
dataBrokerServer *dataBrokerServer
|
||||
manager *manager.Manager
|
||||
legacyManager *legacymanager.Manager
|
||||
eventsMgr *events.Manager
|
||||
|
||||
localListener net.Listener
|
||||
|
@ -50,8 +48,7 @@ type DataBroker struct {
|
|||
}
|
||||
|
||||
type Options struct {
|
||||
managerOptions []manager.Option
|
||||
legacyManagerOptions []legacymanager.Option
|
||||
managerOptions []manager.Option
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
@ -68,12 +65,6 @@ func WithManagerOptions(managerOptions ...manager.Option) Option {
|
|||
}
|
||||
}
|
||||
|
||||
func WithLegacyManagerOptions(legacyManagerOptions ...legacymanager.Option) Option {
|
||||
return func(o *Options) {
|
||||
o.legacyManagerOptions = append(o.legacyManagerOptions, legacyManagerOptions...)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new databroker service.
|
||||
func New(ctx context.Context, cfg *config.Config, eventsMgr *events.Manager, opts ...Option) (*DataBroker, error) {
|
||||
options := Options{}
|
||||
|
@ -115,7 +106,7 @@ func New(ctx context.Context, cfg *config.Config, eventsMgr *events.Manager, opt
|
|||
}
|
||||
|
||||
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("service", "databroker").Str("config_source", "bootstrap")
|
||||
return c.Str("service", "databroker").Str("config-source", "bootstrap")
|
||||
})
|
||||
localGRPCConnection, err := grpc.DialContext(
|
||||
ctx,
|
||||
|
@ -201,13 +192,7 @@ func (c *DataBroker) update(ctx context.Context, cfg *config.Config) error {
|
|||
options := append([]manager.Option{
|
||||
manager.WithDataBrokerClient(dataBrokerClient),
|
||||
manager.WithEventManager(c.eventsMgr),
|
||||
manager.WithEnabled(!cfg.Options.IsRuntimeFlagSet(config.RuntimeFlagLegacyIdentityManager)),
|
||||
}, c.managerOptions...)
|
||||
legacyOptions := append([]legacymanager.Option{
|
||||
legacymanager.WithDataBrokerClient(dataBrokerClient),
|
||||
legacymanager.WithEventManager(c.eventsMgr),
|
||||
legacymanager.WithEnabled(cfg.Options.IsRuntimeFlagSet(config.RuntimeFlagLegacyIdentityManager)),
|
||||
}, c.legacyManagerOptions...)
|
||||
|
||||
if cfg.Options.SupportsUserRefresh() {
|
||||
authenticator, err := identity.NewAuthenticator(ctx, c.tracerProvider, oauthOptions)
|
||||
|
@ -215,7 +200,6 @@ func (c *DataBroker) update(ctx context.Context, cfg *config.Config) error {
|
|||
log.Ctx(ctx).Error().Err(err).Msg("databroker: failed to create authenticator")
|
||||
} else {
|
||||
options = append(options, manager.WithAuthenticator(authenticator))
|
||||
legacyOptions = append(legacyOptions, legacymanager.WithAuthenticator(authenticator))
|
||||
}
|
||||
} else {
|
||||
log.Ctx(ctx).Info().Msg("databroker: disabling refresh of user sessions")
|
||||
|
@ -227,12 +211,6 @@ func (c *DataBroker) update(ctx context.Context, cfg *config.Config) error {
|
|||
c.manager.UpdateConfig(options...)
|
||||
}
|
||||
|
||||
if c.legacyManager == nil {
|
||||
c.legacyManager = legacymanager.New(legacyOptions...)
|
||||
} else {
|
||||
c.legacyManager.UpdateConfig(legacyOptions...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
168
go.mod
168
go.mod
|
@ -1,29 +1,31 @@
|
|||
module github.com/pomerium/pomerium
|
||||
|
||||
go 1.23.6
|
||||
|
||||
toolchain go1.23.7
|
||||
go 1.23.8
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.50.0
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1
|
||||
cloud.google.com/go/storage v1.51.0
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.2
|
||||
github.com/CAFxX/httpcompression v0.0.9
|
||||
github.com/VictoriaMetrics/fastcache v1.12.2
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.8
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0
|
||||
github.com/bits-and-blooms/bitset v1.21.0
|
||||
github.com/caddyserver/certmagic v0.21.7
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.12
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.0
|
||||
github.com/bits-and-blooms/bitset v1.22.0
|
||||
github.com/bufbuild/protovalidate-go v0.9.3
|
||||
github.com/caddyserver/certmagic v0.22.2
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/cloudflare/circl v1.6.0
|
||||
github.com/coreos/go-oidc/v3 v3.12.0
|
||||
github.com/docker/docker v28.0.1+incompatible
|
||||
github.com/coreos/go-oidc/v3 v3.13.0
|
||||
github.com/docker/docker v28.0.4+incompatible
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1
|
||||
github.com/exaring/otelpgx v0.9.0
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/gaissmai/bart v0.20.3
|
||||
github.com/go-chi/chi/v5 v5.2.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.4
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1
|
||||
github.com/google/btree v1.1.3
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-jsonnet v0.20.0
|
||||
|
@ -31,21 +33,21 @@ require (
|
|||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-set/v3 v3.0.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/jackc/pgx/v5 v5.7.2
|
||||
github.com/jackc/pgx/v5 v5.7.4
|
||||
github.com/jxskiss/base62 v1.1.0
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/martinlindhe/base36 v1.1.1
|
||||
github.com/mholt/acmez/v3 v3.0.1
|
||||
github.com/minio/minio-go/v7 v7.0.87
|
||||
github.com/mholt/acmez/v3 v3.1.1
|
||||
github.com/minio/minio-go/v7 v7.0.89
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
|
||||
github.com/natefinch/atomic v1.0.1
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
github.com/open-policy-agent/opa v1.2.0
|
||||
github.com/open-policy-agent/opa v1.3.0
|
||||
github.com/peterbourgon/ff/v3 v3.4.0
|
||||
github.com/pires/go-proxyproto v0.8.0
|
||||
github.com/pomerium/csrf v1.7.0
|
||||
|
@ -53,91 +55,92 @@ require (
|
|||
github.com/pomerium/envoy-custom v1.33.0
|
||||
github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46
|
||||
github.com/pomerium/webauthn v0.0.0-20240603205124-0428df511172
|
||||
github.com/prometheus/client_golang v1.21.0
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/prometheus/client_model v0.6.1
|
||||
github.com/prometheus/common v0.62.0
|
||||
github.com/prometheus/procfs v0.15.1
|
||||
github.com/quic-go/quic-go v0.50.0
|
||||
github.com/prometheus/common v0.63.0
|
||||
github.com/prometheus/procfs v0.16.0
|
||||
github.com/quic-go/quic-go v0.50.1
|
||||
github.com/rs/cors v1.11.1
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/spf13/viper v1.20.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/testcontainers/testcontainers-go v0.35.0
|
||||
github.com/testcontainers/testcontainers-go v0.36.0
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f
|
||||
github.com/volatiletech/null/v9 v9.0.0
|
||||
github.com/yuin/gopher-lua v1.1.1
|
||||
github.com/zeebo/xxh3 v1.0.2
|
||||
go.opencensus.io v0.24.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.59.0
|
||||
go.opentelemetry.io/otel v1.34.0
|
||||
go.opentelemetry.io/otel/bridge/opencensus v1.34.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0
|
||||
go.opentelemetry.io/otel/metric v1.34.0
|
||||
go.opentelemetry.io/otel/sdk v1.34.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0
|
||||
go.opentelemetry.io/otel/trace v1.34.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.60.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel/bridge/opencensus v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
|
||||
go.opentelemetry.io/otel/metric v1.35.0
|
||||
go.opentelemetry.io/otel/sdk v1.35.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0
|
||||
go.opentelemetry.io/otel/trace v1.35.0
|
||||
go.opentelemetry.io/proto/otlp v1.5.0
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
go.uber.org/mock v0.5.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/oauth2 v0.27.0
|
||||
golang.org/x/sync v0.11.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/time v0.10.0
|
||||
google.golang.org/api v0.223.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2
|
||||
google.golang.org/grpc v1.70.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.12.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/time v0.11.0
|
||||
google.golang.org/api v0.224.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb
|
||||
google.golang.org/grpc v1.71.1
|
||||
google.golang.org/protobuf v1.36.6
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
cloud.google.com/go v0.116.0 // indirect
|
||||
cel.dev/expr v0.19.2 // indirect
|
||||
cloud.google.com/go v0.118.3 // indirect
|
||||
cloud.google.com/go/auth v0.15.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.2.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.21.2 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
cloud.google.com/go/iam v1.4.1 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.0 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // 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/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.61 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.65 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect
|
||||
github.com/aws/smithy-go v1.22.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
|
@ -146,6 +149,7 @@ require (
|
|||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
|
@ -161,27 +165,27 @@ require (
|
|||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/cel-go v0.24.1 // indirect
|
||||
github.com/google/go-tpm v0.9.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.5 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/kralicky/go-adaptive-radix-tree v0.0.0-20240624235931-330eb762e74c // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/libdns/libdns v0.2.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.62 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
|
@ -195,8 +199,8 @@ require (
|
|||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.19.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
|
@ -206,15 +210,16 @@ require (
|
|||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/sryoya/protorand v0.0.0-20240429201223-e7440656b2a4 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.2 // indirect
|
||||
|
@ -230,19 +235,18 @@ require (
|
|||
github.com/zeebo/assert v1.3.1 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.35.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
|
358
go.sum
358
go.sum
|
@ -1,5 +1,7 @@
|
|||
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
|
||||
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1 h1:zgJPqo17m28+Lf5BW4xv3PvU20BnrmTcGYrog22lLIU=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U=
|
||||
cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
|
||||
cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
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=
|
||||
|
@ -15,8 +17,8 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
|
|||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||
cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
|
||||
cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
|
||||
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
|
||||
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||
|
@ -31,14 +33,14 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4
|
|||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
|
||||
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
|
||||
cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk=
|
||||
cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM=
|
||||
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
|
||||
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
|
||||
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
|
||||
cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU=
|
||||
cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
|
||||
cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
|
||||
cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
|
||||
cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM=
|
||||
cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
|
@ -48,14 +50,14 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
|
||||
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
|
||||
cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI=
|
||||
cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io=
|
||||
cloud.google.com/go/storage v1.51.0 h1:ZVZ11zCiD7b3k+cH5lQs/qcNaoSz3U9I0jgwVzqDlCw=
|
||||
cloud.google.com/go/storage v1.51.0/go.mod h1:YEJfu/Ki3i5oHC/7jyTgsGZwdQ8P9hqMqvpi5kRKGgc=
|
||||
cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE=
|
||||
cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8=
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg=
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
|
@ -67,12 +69,12 @@ github.com/CAFxX/httpcompression v0.0.9 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWG
|
|||
github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
||||
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=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1/go.mod h1:0wEl7vrAD8mehJyohS9HZy+WyEOaQO2mJx86Cvh93kM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
|
@ -90,6 +92,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah
|
|||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||
|
@ -98,10 +102,10 @@ github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38y
|
|||
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.8 h1:RpwAfYcV2lr/yRc4lWhUM9JRPQqKgKWmou3LV7UfWP4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.8/go.mod h1:t+G7Fq1OcO8cXTPPXzxQSnj/5Xzdc9jAAD3Xrn9/Mgo=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.61 h1:Hd/uX6Wo2iUW1JWII+rmyCD7MMhOe7ALwQXN6sKDd1o=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.61/go.mod h1:L7vaLkwHY1qgW0gG1zG0z/X0sQ5tpIY5iI13+j3qI80=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.12 h1:Y/2a+jLPrPbHpFkpAAYkVEtJmxORlXoo5k2g1fa2sUo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.12/go.mod h1:xse1YTjmORlb/6fhkWi8qJh3cvZi4JoVNhc+NbJt4kI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.65 h1:q+nV2yYegofO/SUXruT+pn4KxkxmaQ++1B/QedcKBFM=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.65/go.mod h1:4zyjAuGOdikpNYiSGpsGz8hLGmUzlY8pc8r9QQ/RXYQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
|
||||
|
@ -114,33 +118,35 @@ github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcu
|
|||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2 h1:t/gZFyrijKuSU0elA5kRngP/oU3mc0I+Dvp8HwRE4c0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0 h1:EBm8lXevBWe+kK9VOU/IBeOI189WPRwPUc3LvJK9GOs=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.0/go.mod h1:4qzsZSzB/KiX2EzDjs9D7A8rI/WGJxZceVJIHqtJjIU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.0 h1:2U9sF8nKy7UgyEeLiZTRg6ShBS22z8UnYpV6aRFL0is=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.0/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0 h1:wjAdc85cXdQR5uLx5FwWvGIHm4OPJhTyzUHU8craXtE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.16 h1:BHEK2Q/7CMRMCb3nySi/w8UbIcPhKvYP5s1xf8/izn0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.16/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.0 h1:OIw2nryEApESTYI5deCZGcq4Gvz8DBAt4tJlNyg3v5o=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.0/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.2 h1:pdgODsAhGo4dvzC3JAG5Ce0PX8kWXrTZGx+jxADD+5E=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.2/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0 h1:90uX0veLKcdHVfvxhkWUQSCi5VabtwMLFutYiRke4oo=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
|
||||
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bits-and-blooms/bitset v1.21.0 h1:9RlxRbMI5dRNNburKqfDSiz5POfImKgtablyV01WUw0=
|
||||
github.com/bits-and-blooms/bitset v1.21.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
|
||||
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bufbuild/protovalidate-go v0.9.3 h1:XvdtwQuppS3wjzGfpOirsqwN5ExH2+PiIuA/XZd3MTM=
|
||||
github.com/bufbuild/protovalidate-go v0.9.3/go.mod h1:2lUDP6fNd3wxznRNH3Nj64VB07+PySeslamkerwP6tE=
|
||||
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
|
||||
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q=
|
||||
github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg=
|
||||
github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI=
|
||||
github.com/caddyserver/certmagic v0.22.2 h1:qzZURXlrxwR5m25/jpvVeEyJHeJJMvAwe5zlMufOTQk=
|
||||
github.com/caddyserver/certmagic v0.22.2/go.mod h1:hbqE7BnkjhX5IJiFslPmrSeobSeZvI6ux8tyxhsd6qs=
|
||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
|
@ -158,14 +164,14 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
||||
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 h1:boJj011Hh+874zpIySeApCX4GeOjPl9qhRF3QuIZq+Q=
|
||||
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
|
||||
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
||||
github.com/coreos/go-oidc/v3 v3.13.0 h1:M66zd0pcc5VxvBNM4pB331Wrsanby+QomQYjN8HamW8=
|
||||
github.com/coreos/go-oidc/v3 v3.13.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
|
@ -176,22 +182,24 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v4 v4.5.1 h1:7DCIXrQjo1LKmM96YD+hLVJ2EEsyyoWxJfpdd56HLps=
|
||||
github.com/dgraph-io/badger/v4 v4.5.1/go.mod h1:qn3Be0j3TfV4kPbVoK0arXCD1/nr1ftth6sbL5jxdoA=
|
||||
github.com/dgraph-io/badger/v4 v4.6.0 h1:acOwfOOZ4p1dPRnYzvkVm7rUk2Y21TgPVepCy5dJdFQ=
|
||||
github.com/dgraph-io/badger/v4 v4.6.0/go.mod h1:KSJ5VTuZNC3Sd+YhvVjk2nYua9UZnnTr/SkXvdtiPgI=
|
||||
github.com/dgraph-io/ristretto/v2 v2.1.0 h1:59LjpOJLNDULHh8MC4UaegN52lC4JnO2dITsie/Pa8I=
|
||||
github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4TypQYLJZzBSAemuB4=
|
||||
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
|
||||
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||
github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
|
||||
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
@ -220,6 +228,8 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
|
|||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gaissmai/bart v0.20.3 h1:hZxPDasx5f2rmNsKwvNu5JMV0+SUs/uovkUNKN/807U=
|
||||
github.com/gaissmai/bart v0.20.3/go.mod h1:HRCXF6EPBV4dcRPUTZtjVx384e3RYVHJ5H22ApAqltA=
|
||||
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
|
@ -255,6 +265,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
|||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
|
@ -303,8 +315,10 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/flatbuffers v24.12.23+incompatible h1:ubBKR94NR4pXUCY/MUsRVzd9umNW7ht7EG9hHfS9FX8=
|
||||
github.com/google/flatbuffers v24.12.23+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI=
|
||||
github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
@ -345,8 +359,8 @@ github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0
|
|||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.5 h1:VgzTY2jogw3xt39CusEnFJWm7rlsq5yL5q9XdLOuP5g=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.5/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
|
@ -359,10 +373,10 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
|||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 h1:KcFzXwzM/kGhIRHvc8jdixfIJjVzuUJdnv+5xsPutog=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
|
@ -374,8 +388,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
|||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
|
@ -384,8 +396,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
|||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
|
||||
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
|
@ -406,8 +418,8 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs
|
|||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
@ -423,12 +435,12 @@ github.com/kralicky/go-adaptive-radix-tree v0.0.0-20240624235931-330eb762e74c h1
|
|||
github.com/kralicky/go-adaptive-radix-tree v0.0.0-20240624235931-330eb762e74c/go.mod h1:oJwexVSshEat0E3evyKOH6QzN8GFWrhLvEoh8GiJzss=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8=
|
||||
github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/martinlindhe/base36 v1.1.1 h1:1F1MZ5MGghBXDZ2KJ3QfxmiydlWOGB8HCEtkap5NkVg=
|
||||
github.com/martinlindhe/base36 v1.1.1/go.mod h1:vMS8PaZ5e/jV9LwFKlm0YLnXl/hpOihiBxKkIoc3g08=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
@ -438,16 +450,16 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8=
|
||||
github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||
github.com/mholt/acmez/v3 v3.1.1 h1:Jh+9uKHkPxUJdxM16q5mOr+G2V0aqkuFtNA28ihCxhQ=
|
||||
github.com/mholt/acmez/v3 v3.1.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
|
||||
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.87 h1:nkr9x0u53PespfxfUqxP3UYWiE2a41gaofgNnC4Y8WQ=
|
||||
github.com/minio/minio-go/v7 v7.0.87/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg=
|
||||
github.com/minio/minio-go/v7 v7.0.89 h1:hx4xV5wwTUfyv8LarhJAwNecnXpoTsj9v3f3q/ZkiJU=
|
||||
github.com/minio/minio-go/v7 v7.0.89/go.mod h1:2rFnGAp02p7Dddo1Fq4S2wYOfpF0MUTSeLTRC90I204=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=
|
||||
|
@ -492,14 +504,14 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
|||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/open-policy-agent/opa v1.2.0 h1:88NDVCM0of1eO6Z4AFeL3utTEtMuwloFmWWU7dRV1z0=
|
||||
github.com/open-policy-agent/opa v1.2.0/go.mod h1:30euUmOvuBoebRCcJ7DMF42bRBOPznvt0ACUMYDUGVY=
|
||||
github.com/open-policy-agent/opa v1.3.0 h1:zVvQvQg+9+FuSRBt4LgKNzJwsWl/c85kD5jPozJTydY=
|
||||
github.com/open-policy-agent/opa v1.3.0/go.mod h1:t9iPNhaplD2qpiBqeudzJtEX3fKHK8zdA29oFvofAHo=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc=
|
||||
github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
|
@ -537,8 +549,8 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr
|
|||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
|
||||
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
|
@ -551,22 +563,22 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9
|
|||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM=
|
||||
github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg=
|
||||
github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0=
|
||||
github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
||||
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=
|
||||
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
@ -574,20 +586,19 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
|
|||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v1.11.0 h1:NoPa5GIoBwuqzIviCrnUJa+t5Xb4xi5Z+zODJnIDsEQ=
|
||||
|
@ -599,19 +610,21 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
|||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/sryoya/protorand v0.0.0-20240429201223-e7440656b2a4 h1:/jKH9ivHOUkahZs3zPfJfOmkXDFB6OdsHZ4W8gyDb/c=
|
||||
github.com/sryoya/protorand v0.0.0-20240429201223-e7440656b2a4/go.mod h1:9a23nlv6vzBeVlQq6JQCjljZ6sfzsB6aha1m5Ly1W2Y=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
|
@ -628,7 +641,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
|
||||
|
@ -636,8 +648,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
|||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM=
|
||||
github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
|
||||
github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
|
||||
github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00=
|
||||
github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
|
@ -690,44 +702,44 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
|||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.59.0 h1:bgG6F0HBLngIG79m8VYMdgh3adfcjCgLbsO8StsovQk=
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.59.0/go.mod h1:JV4DSHIsqQoFVaeE6xDef6xdI2I/IOOsWnXWCzQ0EXQ=
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.34.0 h1:pv/Yi44N2BM1Kyl6wxO6bTiwcxUA7Deog3Rc7NO9ITE=
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.34.0/go.mod h1:1aF3HFtAyIi+B2xJHOdKQcNz+bcDS+JLAZjsohcW1P4=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.34.0 h1:9pQdCEvV/6RWQmag94D6rhU+A4rzUhYBEJ8bpscx5p8=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.34.0/go.mod h1:FwM71WS8i1/mAK4n48t0KU6qUS/OZRBgDrHZv3RlJ+w=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 h1:D3htJISCUU/wOVlKwisVKancWm+2U4h9xDEaiMkiyRE=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0/go.mod h1:DAX1bsj+uDm2ZuOQH/RgZRx7RQZWyzV5W2WR/0UX8JA=
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.34.0 h1:fcA0FMvHmco/mjXbmRoNx9IRs6+UuRVyXhMHzP+pEc8=
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.34.0/go.mod h1:VvybuUSU0G7m9DLza4YX8KKniYlIphI+mZ6ufkaV1m8=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/bridge/opencensus v1.34.0 h1:2Uxf3WAnOkGFTMlMShbiHNF2qN1iGdnt5m6hUnUp07k=
|
||||
go.opentelemetry.io/otel/bridge/opencensus v1.34.0/go.mod h1:ALZT48QF8vj9XiFlBFuBGBQsj9Wk8Sk1zdyu6/1MCVs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 h1:ajl4QczuJVA2TU9W9AGw++86Xga/RKt//16z/yxPgdk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0/go.mod h1:Vn3/rlOJ3ntf/Q3zAI0V5lDnTbHGaUsNUeF6nZmm7pA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.60.0 h1:sevByeAWTtfBdJQT7nkJfK5wOCjNpmDMZGPEBx3l1RA=
|
||||
go.opentelemetry.io/contrib/propagators/autoprop v0.60.0/go.mod h1:uEhyRPnUTSeUwMjDdrMQnsJ0sQ2mf/fA94hfchemm4A=
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.35.0 h1:xoXA+5dVwsf5uE5GvSJ3lKiapyMFuIzbEmJwQ0JP+QU=
|
||||
go.opentelemetry.io/contrib/propagators/aws v1.35.0/go.mod h1:s11Orts/IzEgw9Srw5iRXtk2kM2j3jt/45noUWyf60E=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.35.0 h1:DpwKW04LkdFRFCIgM3sqwTJA/QREHMeMHYPWP1WeaPQ=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.35.0/go.mod h1:9+SNxwqvCWo1qQwUpACBY5YKNVxFJn5mlbXg/4+uKBg=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.35.0 h1:ZsgYijVvOpju4mq3g4QyqCwLKs2vKenlCpZHbKu50OA=
|
||||
go.opentelemetry.io/contrib/propagators/ot v1.35.0/go.mod h1:t1ZwtgjEtDH9uW6OlCRVLL2wOgsTJmp0pJwNouUq+HE=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/bridge/opencensus v1.35.0 h1:4nJfffRbozhqnuukfRkiahA94mnpryCLJLiduMIDJKI=
|
||||
go.opentelemetry.io/otel/bridge/opencensus v1.35.0/go.mod h1:359S30saRYNsB4A46EDx91SpXsQFNgkma7ftg2/L5/M=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
|
@ -750,8 +762,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -786,8 +798,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -827,8 +839,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -836,8 +848,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
|
|||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -851,8 +863,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -912,15 +924,15 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -931,13 +943,13 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -983,8 +995,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
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=
|
||||
|
@ -1005,8 +1017,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
|||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc=
|
||||
google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M=
|
||||
google.golang.org/api v0.224.0 h1:Ir4UPtDsNiwIOHdExr3fAj4xZ42QjK7uQte3lORLJwU=
|
||||
google.golang.org/api v0.224.0/go.mod h1:3V39my2xAGkodXy0vEqcEtkqgw2GtrFL5WuBZlCTCOQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -1042,12 +1054,12 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
|||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 h1:DMTIbak9GhdaSxEjvVzAeNZvyc03I61duqNbnm3SU0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
|
@ -1061,8 +1073,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
|
|||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
|
||||
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
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=
|
||||
|
@ -1077,8 +1089,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
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=
|
||||
|
@ -1087,8 +1099,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
@ -63,9 +63,7 @@ func TestHealth(t *testing.T) {
|
|||
endpoints := []string{"healthz", "ping"}
|
||||
|
||||
for _, route := range pomeriumRoutes {
|
||||
route := route
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
routeToCheck := fmt.Sprintf("%s/%s", route, endpoint)
|
||||
t.Run(routeToCheck, func(t *testing.T) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, routeToCheck, nil)
|
||||
|
|
|
@ -41,6 +41,6 @@ func (v *Value[T]) Swap(val T) T {
|
|||
}
|
||||
|
||||
// Swap swaps the value atomically.
|
||||
func (v *Value[T]) CompareAndSwap(old, new T) bool {
|
||||
return v.value.CompareAndSwap(old, new)
|
||||
func (v *Value[T]) CompareAndSwap(old, n T) bool {
|
||||
return v.value.CompareAndSwap(old, n)
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@ import (
|
|||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/pkg/grpc"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||
"github.com/pomerium/pomerium/pkg/identity"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// timeNow is time.Now but pulled out as a variable for tests.
|
||||
|
|
98
internal/authenticateflow/authenticateflow_int_test.go
Normal file
98
internal/authenticateflow/authenticateflow_int_test.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package authenticateflow_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/testenv"
|
||||
"github.com/pomerium/pomerium/internal/testenv/scenarios"
|
||||
"github.com/pomerium/pomerium/internal/testenv/snippets"
|
||||
"github.com/pomerium/pomerium/internal/testenv/upstreams"
|
||||
"github.com/pomerium/pomerium/internal/testenv/values"
|
||||
)
|
||||
|
||||
func newHTTPUpstream(
|
||||
env testenv.Environment, subdomain string,
|
||||
) (upstreams.HTTPUpstream, testenv.Route) {
|
||||
up := upstreams.HTTP(nil)
|
||||
up.Handle("/", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintln(w, "hello world") })
|
||||
route := up.Route().
|
||||
From(env.SubdomainURL(subdomain)).
|
||||
To(values.Bind(up.Addr(), func(addr string) string {
|
||||
// override the target protocol to use http://
|
||||
return fmt.Sprintf("http://%s", addr)
|
||||
})).
|
||||
Policy(func(p *config.Policy) { p.AllowAnyAuthenticatedUser = true })
|
||||
env.AddUpstream(up)
|
||||
return up, route
|
||||
}
|
||||
|
||||
func TestMultiDomainLogin(t *testing.T) {
|
||||
env := testenv.New(t)
|
||||
|
||||
env.Add(scenarios.NewIDP([]*scenarios.User{{Email: "test@example.com"}}))
|
||||
|
||||
// Create three routes to be linked via multi-domain login...
|
||||
upstreamA, routeA := newHTTPUpstream(env, "a")
|
||||
upstreamB, routeB := newHTTPUpstream(env, "b")
|
||||
upstreamC, routeC := newHTTPUpstream(env, "c")
|
||||
// ...and one route that will not be involved.
|
||||
upstreamD, routeD := newHTTPUpstream(env, "d")
|
||||
|
||||
// Configure route A to redirect through routes B and C on login.
|
||||
routeA.Policy(func(p *config.Policy) {
|
||||
p.DependsOn = []string{
|
||||
strings.TrimPrefix(routeB.URL().Value(), "https://"),
|
||||
strings.TrimPrefix(routeC.URL().Value(), "https://"),
|
||||
}
|
||||
})
|
||||
|
||||
env.Start()
|
||||
snippets.WaitStartupComplete(env)
|
||||
|
||||
// By default the testenv code will use a separate http.Client for each
|
||||
// separate route. Instead we specifically want to test the cross-route
|
||||
// behavior for a single client.
|
||||
cj, err := cookiejar.New(nil)
|
||||
require.NoError(t, err)
|
||||
sharedClient := http.Client{Jar: cj}
|
||||
withSharedClient := upstreams.ClientHook(
|
||||
func(_ *http.Client) *http.Client { return &sharedClient })
|
||||
|
||||
assertResponseOK := func(resp *http.Response, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
||||
assertRedirect := func(resp *http.Response, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusFound, resp.StatusCode)
|
||||
io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
// Log in to the first route.
|
||||
assertResponseOK(upstreamA.Get(routeA, withSharedClient, upstreams.AuthenticateAs("test@example.com")))
|
||||
|
||||
// The client should also be authenticated for routes B and C without any
|
||||
// additional login redirects.
|
||||
sharedClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
assertResponseOK(upstreamB.Get(routeB, withSharedClient))
|
||||
assertResponseOK(upstreamC.Get(routeC, withSharedClient))
|
||||
|
||||
// The client should not be authenticated for route D.
|
||||
assertRedirect(upstreamD.Get(routeD, withSharedClient))
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
|
@ -23,7 +24,6 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/sessions"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc"
|
||||
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||
"github.com/pomerium/pomerium/pkg/identity"
|
||||
"github.com/pomerium/pomerium/pkg/identity/manager"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// Stateful implements the stateful authentication flow. In this flow, the
|
||||
|
@ -163,7 +164,9 @@ func (s *Stateful) SignIn(
|
|||
// base64 our encrypted payload for URL-friendlyness
|
||||
encodedJWT := base64.URLEncoding.EncodeToString(encryptedJWT)
|
||||
|
||||
callbackURL, err := urlutil.GetCallbackURL(r, encodedJWT)
|
||||
additionalHosts := strings.Split(r.FormValue(urlutil.QueryAdditionalHosts), ",")
|
||||
|
||||
callbackURL, err := urlutil.GetCallbackURL(r, encodedJWT, additionalHosts)
|
||||
if err != nil {
|
||||
return httputil.NewError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
@ -324,7 +327,11 @@ func (s *Stateful) LogAuthenticateEvent(*http.Request) {}
|
|||
// AuthenticateSignInURL returns a URL to redirect the user to the authenticate
|
||||
// domain.
|
||||
func (s *Stateful) AuthenticateSignInURL(
|
||||
ctx context.Context, queryParams url.Values, redirectURL *url.URL, idpID string,
|
||||
ctx context.Context,
|
||||
queryParams url.Values,
|
||||
redirectURL *url.URL,
|
||||
idpID string,
|
||||
additionalHosts []string,
|
||||
) (string, error) {
|
||||
signinURL := s.authenticateURL.ResolveReference(&url.URL{
|
||||
Path: "/.pomerium/sign_in",
|
||||
|
@ -335,6 +342,9 @@ func (s *Stateful) AuthenticateSignInURL(
|
|||
}
|
||||
queryParams.Set(urlutil.QueryRedirectURI, redirectURL.String())
|
||||
queryParams.Set(urlutil.QueryIdentityProviderID, idpID)
|
||||
if len(additionalHosts) > 0 {
|
||||
queryParams.Set(urlutil.QueryAdditionalHosts, strings.Join(additionalHosts, ","))
|
||||
}
|
||||
otel.GetTextMapPropagator().Inject(ctx, trace.PomeriumURLQueryCarrier(queryParams))
|
||||
signinURL.RawQuery = queryParams.Encode()
|
||||
redirectTo := urlutil.NewSignedURL(s.sharedKey, signinURL).String()
|
||||
|
@ -387,6 +397,23 @@ func (s *Stateful) Callback(w http.ResponseWriter, r *http.Request) error {
|
|||
redirectURL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
// Redirect chaining for multi-domain login.
|
||||
additionalHosts := r.URL.Query().Get(urlutil.QueryAdditionalHosts)
|
||||
if additionalHosts != "" {
|
||||
nextHops := strings.Split(additionalHosts, ",")
|
||||
log.Ctx(r.Context()).Debug().Strs("next-hops", nextHops).Msg("multi-domain login callback")
|
||||
|
||||
callbackURL, err := urlutil.GetCallbackURL(r, encryptedSession, nextHops[1:])
|
||||
if err != nil {
|
||||
return httputil.NewError(http.StatusInternalServerError,
|
||||
fmt.Errorf("proxy: couldn't get next hop callback URL: %w", err))
|
||||
}
|
||||
callbackURL.Host = nextHops[0]
|
||||
signedCallbackURL := urlutil.NewSignedURL(s.sharedKey, callbackURL)
|
||||
httputil.Redirect(w, r, signedCallbackURL.String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
||||
// redirect
|
||||
httputil.Redirect(w, r, redirectURL.String(), http.StatusFound)
|
||||
return nil
|
||||
|
|
|
@ -67,7 +67,6 @@ func TestStatefulSignIn(t *testing.T) {
|
|||
{"good programmatic request", "corp.example.example", map[string]string{urlutil.QueryIsProgrammatic: "true", urlutil.QueryRedirectURI: "https://dst.some.example/"}, true, &sessions.State{}, &mock.Encoder{}, nil, "", "https://dst.some.example/.pomerium/callback/"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sessionStore := &mstore.Store{SaveError: tt.saveError}
|
||||
flow, err := NewStateful(context.Background(), trace.NewNoopTracerProvider(), &config.Config{Options: opts}, sessionStore)
|
||||
|
@ -129,7 +128,7 @@ func TestStatefulAuthenticateSignInURL(t *testing.T) {
|
|||
|
||||
t.Run("NilQueryParams", func(t *testing.T) {
|
||||
redirectURL := &url.URL{Scheme: "https", Host: "example.com"}
|
||||
u, err := flow.AuthenticateSignInURL(context.Background(), nil, redirectURL, "fake-idp-id")
|
||||
u, err := flow.AuthenticateSignInURL(context.Background(), nil, redirectURL, "fake-idp-id", nil)
|
||||
assert.NoError(t, err)
|
||||
parsed, _ := url.Parse(u)
|
||||
assert.NoError(t, urlutil.NewSignedURL(key, parsed).Validate())
|
||||
|
@ -144,7 +143,7 @@ func TestStatefulAuthenticateSignInURL(t *testing.T) {
|
|||
redirectURL := &url.URL{Scheme: "https", Host: "example.com"}
|
||||
q := url.Values{}
|
||||
q.Set("foo", "bar")
|
||||
u, err := flow.AuthenticateSignInURL(context.Background(), q, redirectURL, "fake-idp-id")
|
||||
u, err := flow.AuthenticateSignInURL(context.Background(), q, redirectURL, "fake-idp-id", nil)
|
||||
assert.NoError(t, err)
|
||||
parsed, _ := url.Parse(u)
|
||||
assert.NoError(t, urlutil.NewSignedURL(key, parsed).Validate())
|
||||
|
@ -156,6 +155,21 @@ func TestStatefulAuthenticateSignInURL(t *testing.T) {
|
|||
assert.Equal(t, "fake-idp-id", q.Get("pomerium_idp_id"))
|
||||
assert.Equal(t, "bar", q.Get("foo"))
|
||||
})
|
||||
t.Run("AdditionalHosts", func(t *testing.T) {
|
||||
redirectURL := &url.URL{Scheme: "https", Host: "example.com"}
|
||||
additionalHosts := []string{"foo.example.com", "bar.example.com:1234"}
|
||||
u, err := flow.AuthenticateSignInURL(context.Background(), nil, redirectURL, "fake-idp-id", additionalHosts)
|
||||
assert.NoError(t, err)
|
||||
parsed, _ := url.Parse(u)
|
||||
assert.NoError(t, urlutil.NewSignedURL(key, parsed).Validate())
|
||||
assert.Equal(t, "https", parsed.Scheme)
|
||||
assert.Equal(t, "authenticate.example.com", parsed.Host)
|
||||
assert.Equal(t, "/.pomerium/sign_in", parsed.Path)
|
||||
q := parsed.Query()
|
||||
assert.Equal(t, "https://example.com", parsed.Query().Get("pomerium_redirect_uri"))
|
||||
assert.Equal(t, "fake-idp-id", q.Get("pomerium_idp_id"))
|
||||
assert.Equal(t, "foo.example.com,bar.example.com:1234", q.Get("pomerium_additional_hosts"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestStatefulGetIdentityProviderIDForURLValues(t *testing.T) {
|
||||
|
@ -278,6 +292,7 @@ func TestStatefulCallback(t *testing.T) {
|
|||
}
|
||||
location, _ := url.Parse(w.Result().Header.Get("Location"))
|
||||
assert.Equal(t, "example.com", location.Host)
|
||||
assert.Equal(t, "/", location.Path)
|
||||
assert.Equal(t, "ok", location.Query().Get("pomerium_callback_uri"))
|
||||
} else {
|
||||
if err == nil || !strings.Contains(err.Error(), tt.wantErrorMsg) {
|
||||
|
@ -288,6 +303,60 @@ func TestStatefulCallback(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStatefulCallback_AdditionalHosts(t *testing.T) {
|
||||
opts := config.NewDefaultOptions()
|
||||
opts.SharedKey = "80ldlrU2d7w+wVpKNfevk6fmb8otEx6CqOfshj2LwhQ="
|
||||
sharedKey, _ := opts.GetSharedKey()
|
||||
|
||||
flow, err := NewStateful(
|
||||
context.Background(),
|
||||
trace.NewNoopTracerProvider(),
|
||||
&config.Config{Options: opts},
|
||||
&mstore.Store{Session: &sessions.State{}},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
redirectURI := "https://route.example.com/"
|
||||
callbackURI := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "route.example.com",
|
||||
Path: "/.pomerium/callback",
|
||||
RawQuery: url.Values{
|
||||
urlutil.QuerySessionEncrypted: []string{goodEncryptionString},
|
||||
urlutil.QueryRedirectURI: []string{redirectURI},
|
||||
urlutil.QueryAdditionalHosts: []string{"foo.example.com,bar.example.com"},
|
||||
}.Encode(),
|
||||
}
|
||||
signedCallbackURI := urlutil.NewSignedURL(sharedKey, callbackURI)
|
||||
|
||||
doCallback := func(uri string) *http.Response {
|
||||
t.Helper()
|
||||
r := httptest.NewRequest(http.MethodGet, uri, nil)
|
||||
r.Host = r.URL.Host
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
err = flow.Callback(w, r)
|
||||
require.NoError(t, err)
|
||||
return w.Result()
|
||||
}
|
||||
|
||||
// Callback() should serve redirects to the additional hosts before the final redirect URI.
|
||||
res := doCallback(signedCallbackURI.String())
|
||||
location, _ := url.Parse(res.Header.Get("Location"))
|
||||
assert.Equal(t, "foo.example.com", location.Host)
|
||||
assert.Equal(t, "/.pomerium/callback/", location.Path)
|
||||
|
||||
res = doCallback(location.String())
|
||||
location, _ = url.Parse(res.Header.Get("Location"))
|
||||
assert.Equal(t, "bar.example.com", location.Host)
|
||||
assert.Equal(t, "/.pomerium/callback/", location.Path)
|
||||
|
||||
res = doCallback(location.String())
|
||||
location, _ = url.Parse(res.Header.Get("Location"))
|
||||
assert.Equal(t, "route.example.com", location.Host)
|
||||
assert.Equal(t, "/", location.Path)
|
||||
}
|
||||
|
||||
func TestStatefulRevokeSession(t *testing.T) {
|
||||
opts := config.NewDefaultOptions()
|
||||
flow, err := NewStateful(context.Background(), trace.NewNoopTracerProvider(), &config.Config{Options: opts}, nil)
|
||||
|
|
|
@ -9,6 +9,9 @@ import (
|
|||
"net/url"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"go.opentelemetry.io/otel"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/oauth2"
|
||||
googlegrpc "google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
@ -21,7 +24,6 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/sessions"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc"
|
||||
|
@ -31,9 +33,7 @@ import (
|
|||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||
"github.com/pomerium/pomerium/pkg/hpke"
|
||||
"github.com/pomerium/pomerium/pkg/identity"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"go.opentelemetry.io/otel"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
"github.com/pomerium/pomerium/pkg/telemetry/trace"
|
||||
)
|
||||
|
||||
// Stateless implements the stateless authentication flow. In this flow, the
|
||||
|
@ -355,7 +355,11 @@ func getUserClaim(profile *identitypb.Profile, field string) *string {
|
|||
// AuthenticateSignInURL returns a URL to redirect the user to the authenticate
|
||||
// domain.
|
||||
func (s *Stateless) AuthenticateSignInURL(
|
||||
ctx context.Context, queryParams url.Values, redirectURL *url.URL, idpID string,
|
||||
ctx context.Context,
|
||||
queryParams url.Values,
|
||||
redirectURL *url.URL,
|
||||
idpID string,
|
||||
_ []string,
|
||||
) (string, error) {
|
||||
authenticateHPKEPublicKey, err := s.authenticateKeyFetcher.FetchPublicKey(ctx)
|
||||
if err != nil {
|
||||
|
|
|
@ -219,10 +219,10 @@ func (mgr *Manager) renewConfigCerts(ctx context.Context) error {
|
|||
|
||||
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
||||
if len(renew) > 0 {
|
||||
c = c.Strs("renew_domains", renew)
|
||||
c = c.Strs("renew-domains", renew)
|
||||
}
|
||||
if len(ocsp) > 0 {
|
||||
c = c.Strs("ocsp_refresh", ocsp)
|
||||
c = c.Strs("ocsp-refresh", ocsp)
|
||||
}
|
||||
return c
|
||||
})
|
||||
|
|
|
@ -551,7 +551,7 @@ func Test_configureTrustedRoots(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
ok := roots.AppendCertsFromPEM(ca.certPEM)
|
||||
require.Equal(t, true, ok)
|
||||
f, err := os.CreateTemp("", "pomerium-test-ca")
|
||||
f, err := os.CreateTemp(t.TempDir(), "pomerium-test-ca")
|
||||
require.NoError(t, err)
|
||||
n, err := f.Write(ca.certPEM)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -9,13 +9,14 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/testenv"
|
||||
"github.com/pomerium/pomerium/internal/testenv/envutil"
|
||||
"github.com/pomerium/pomerium/internal/testenv/scenarios"
|
||||
"github.com/pomerium/pomerium/internal/testenv/snippets"
|
||||
"github.com/pomerium/pomerium/internal/testenv/upstreams"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue