pax_global_header00006660000000000000000000000064145361140520014513gustar00rootroot0000000000000052 comment=c993f3f390a6ed055ff4660de13f19bf9caba643 golang-github-prometheus-exporter-toolkit-0.11.0/000077500000000000000000000000001453611405200220435ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/.circleci/000077500000000000000000000000001453611405200236765ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/.circleci/config.yml000066400000000000000000000010671453611405200256720ustar00rootroot00000000000000--- # Prometheus has switched to GitHub action. # Circle CI is not disabled repository-wise so that previous pull requests # continue working. # This file does not generate any CircleCI workflow. version: 2.1 executors: golang: docker: - image: busybox jobs: noopjob: executor: golang steps: - run: command: "true" workflows: version: 2 prometheus: jobs: - noopjob triggers: - schedule: cron: "0 0 30 2 *" filters: branches: only: - main golang-github-prometheus-exporter-toolkit-0.11.0/.github/000077500000000000000000000000001453611405200234035ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/.github/ISSUE_TEMPLATE.md000066400000000000000000000024151453611405200261120ustar00rootroot00000000000000 golang-github-prometheus-exporter-toolkit-0.11.0/.github/dependabot.yml000066400000000000000000000001561453611405200262350ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "monthly" golang-github-prometheus-exporter-toolkit-0.11.0/.github/workflows/000077500000000000000000000000001453611405200254405ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/.github/workflows/ci.yml000066400000000000000000000013261453611405200265600ustar00rootroot00000000000000--- name: CI on: # yamllint disable-line rule:truthy pull_request: push: jobs: test: name: Test runs-on: ubuntu-latest container: image: quay.io/prometheus/golang-builder:1.20-base steps: - uses: actions/checkout@v3 - uses: prometheus/promci@v0.0.2 - uses: ./.github/promci/actions/setup_environment - run: make test golangci: name: golangci-lint runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install Go uses: actions/setup-go@v3 with: go-version: '1.20' - name: Lint uses: golangci/golangci-lint-action@v3.4.0 with: version: v1.51.2 golang-github-prometheus-exporter-toolkit-0.11.0/.github/workflows/codeql-analysis.yml000066400000000000000000000045621453611405200312620ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master, release-* ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '26 14 * * 1' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 golang-github-prometheus-exporter-toolkit-0.11.0/.golangci.yml000066400000000000000000000005751453611405200244360ustar00rootroot00000000000000linters: enable: - goimports - misspell - revive issues: exclude-rules: - path: _test.go linters: - errcheck linters-settings: errcheck: exclude-functions: # Used in HTTP handlers, any error is handled by the server itself. - (net/http.ResponseWriter).Write # Never check for logger errors. - (github.com/go-kit/log.Logger).Log golang-github-prometheus-exporter-toolkit-0.11.0/.yamllint000066400000000000000000000006631453611405200237020ustar00rootroot00000000000000--- extends: default rules: braces: max-spaces-inside: 1 level: error brackets: max-spaces-inside: 1 level: error commas: disable comments: disable comments-indentation: disable document-start: disable indentation: spaces: consistent indent-sequences: consistent key-duplicates: ignore: | config/testdata/section_key_dup.bad.yml line-length: disable truthy: check-keys: false golang-github-prometheus-exporter-toolkit-0.11.0/CHANGELOG.md000066400000000000000000000052401453611405200236550ustar00rootroot00000000000000## 0.11.0 / 2023-12-09 * [FEATURE] Allow passing of TLS certificates inline (#158) ## 0.10.0 / 2023-05-01 * [FEATURE] Client TLS: Add option to require a specific Subject Alternate Names #126 * [FEATURE] Add a POST form to the landing page #144 * [FEATURE] Add generic customization to landing page #146 * [ENHANCEMENT] Add a Content-Type header to the landing page #142 * [BUGFIX] Fix Nil pointer references for WebSystemdSocket #127 ## 0.9.1 / 2023-03-08 * [BUGFIX] Landing page: Use HTML5 and Semantic HTML tags #138 * [BUGFIX] test: use port polling to wait for server to start #139 * [BUGFIX] Update kingpin library to fix 32-bit builds #140 ## 0.9.0 / 2023-03-06 * [CHANGE] Update gopkg.in/alecthomas/kingpin.v2 to github.com/alecthomas/kingpin/v2 #133 * [FEATURE] Exporter Landing page #51 ## 0.8.1 / 2022-10-21 * [BUGFIX] Fix systemd activation flag when using a custom kingpin app. #118 ## 0.8.0 / 2022-10-10 * [CHANGE] Change some structs suffix from `Struct` to `Config`. #114 * [FEATURE] Add multiple listeners and systemd socket support. #95 * [FEATURE] Allow TLS parameters to be set in code. #110 ## 0.7.1 / 2021-12-02 * [BUGFIX] Effectively enable HTTP/2 support. #72 ## 0.7.0 / 2021-10-19 * [FEATURE] Add support for security-related HTTP headers. #41 ## 0.6.1 / 2021-06-30 * [BUGFIX] Allow RequireAnyClientCert as client_auth_type. #58 ## 0.6.0 / 2021-06-30 * [CHANGE] Move from github.com/go-kit/kit/log to github.com/go-kit/log #55 ## 0.5.1 / 2021-01-15 This release includes a bugfix for a side-channel security issue that would allow an attacker to verify if a user is defined in the configuration by timing request. #39 * [ENHANCEMENT] Cache basic authentication results to significantly improve performance. #32 * [BUGFIX] Prevent user enumeration by timing requests. #39 ## 0.5.0 / 2021-01-13 * [CHANGE] rename `https` package to `web`. #29 * [CHANGE] `web`: Rename Listen() to ListenAndServe(). #28 ## 0.4.0 / 2020-12-26 This release now correctly resolves relative paths with regards to the configuration file, instead of the current working directory. * [FEATURE] `https`: Add a Validate() function. #22 * [ENHANCEMENT] `https`: Mark kingpin flag as experimental. #20 * [BUGFIX] `https`: Make certificate paths relative to configuration file. #21 ## 0.3.0 / 2020-12-25 * [FEATURE] `https`: Add Serve to use an existing listener. #16 * [BUGFIX] Return 401 Unauthorized when a bad password is used. Previously we returned 403 Forbidden in that case. #17 ## 0.2.0 / 2020-12-16 * [FEATURE] `https/kingpinflags` package for adding kingpin support for TLS. #12 ## 0.1.0 / 2020-12-10 Initial release. * [FEATURE] `https` package for adding TLS to exporters. #8 golang-github-prometheus-exporter-toolkit-0.11.0/CODE_OF_CONDUCT.md000066400000000000000000000002301453611405200246350ustar00rootroot00000000000000# Prometheus Community Code of Conduct Prometheus follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). golang-github-prometheus-exporter-toolkit-0.11.0/CONTRIBUTING.md000066400000000000000000000016661453611405200243050ustar00rootroot00000000000000# Contributing Prometheus uses GitHub to manage reviews of pull requests. * If you have a trivial fix or improvement, go ahead and create a pull request, addressing (with `@...`) the maintainer of this repository (see [MAINTAINERS.md](MAINTAINERS.md)) in the description of the pull request. * If you plan to do something more involved, first discuss your ideas on our [mailing list](https://groups.google.com/forum/?fromgroups#!forum/prometheus-developers). This will avoid unnecessary work and surely give you and us a good deal of inspiration. * Relevant coding style guidelines are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). * Be sure to sign off on the [DCO](https://github.com/probot/dco#how-it-works) golang-github-prometheus-exporter-toolkit-0.11.0/LICENSE000066400000000000000000000261351453611405200230570ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-prometheus-exporter-toolkit-0.11.0/MAINTAINERS.md000066400000000000000000000001441453611405200241360ustar00rootroot00000000000000* Ben Kochie @SuperQ * Julien Pivotto @roidelapluie golang-github-prometheus-exporter-toolkit-0.11.0/Makefile000066400000000000000000000012301453611405200234770ustar00rootroot00000000000000# Copyright 2020 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. include Makefile.common .PHONY: test test:: deps check_license unused common-test golang-github-prometheus-exporter-toolkit-0.11.0/Makefile.common000066400000000000000000000216551453611405200250030ustar00rootroot00000000000000# Copyright 2018 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # A common Makefile that includes rules to be reused in different prometheus projects. # !!! Open PRs only against the prometheus/prometheus/Makefile.common repository! # Example usage : # Create the main Makefile in the root project directory. # include Makefile.common # customTarget: # @echo ">> Running customTarget" # # Ensure GOBIN is not set during build so that promu is installed to the correct path unexport GOBIN GO ?= go GOFMT ?= $(GO)fmt FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH))) GOOPTS ?= GOHOSTOS ?= $(shell $(GO) env GOHOSTOS) GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH) GO_VERSION ?= $(shell $(GO) version) GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION)) PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.') PROMU := $(FIRST_GOPATH)/bin/promu pkgs = ./... ifeq (arm, $(GOHOSTARCH)) GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM) GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM) else GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH) endif GOTEST := $(GO) test GOTEST_DIR := ifneq ($(CIRCLE_JOB),) ifneq ($(shell command -v gotestsum > /dev/null),) GOTEST_DIR := test-results GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml -- endif endif PROMU_VERSION ?= 0.15.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= GOLANGCI_LINT_VERSION ?= v1.55.2 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) # If we're in CI and there is an Actions file, that means the linter # is being run in Actions, so we don't need to run it here. ifneq (,$(SKIP_GOLANGCI_LINT)) GOLANGCI_LINT := else ifeq (,$(CIRCLE_JOB)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint endif endif endif PREFIX ?= $(shell pwd) BIN_DIR ?= $(shell pwd) DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) DOCKERFILE_PATH ?= ./Dockerfile DOCKERBUILD_CONTEXT ?= ./ DOCKER_REPO ?= prom DOCKER_ARCHS ?= amd64 BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS)) PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS)) TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS)) SANITIZED_DOCKER_IMAGE_TAG := $(subst +,-,$(DOCKER_IMAGE_TAG)) ifeq ($(GOHOSTARCH),amd64) ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows)) # Only supported on amd64 test-flags := -race endif endif # This rule is used to forward a target like "build" to "common-build". This # allows a new "build" target to be defined in a Makefile which includes this # one and override "common-build" without override warnings. %: common-% ; .PHONY: common-all common-all: precheck style check_license lint yamllint unused build test .PHONY: common-style common-style: @echo ">> checking code style" @fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \ if [ -n "$${fmtRes}" ]; then \ echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \ echo "Please ensure you are using $$($(GO) version) for formatting code."; \ exit 1; \ fi .PHONY: common-check_license common-check_license: @echo ">> checking license header" @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ exit 1; \ fi .PHONY: common-deps common-deps: @echo ">> getting dependencies" $(GO) mod download .PHONY: update-go-deps update-go-deps: @echo ">> updating Go dependencies" @for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \ $(GO) get -d $$m; \ done $(GO) mod tidy .PHONY: common-test-short common-test-short: $(GOTEST_DIR) @echo ">> running short tests" $(GOTEST) -short $(GOOPTS) $(pkgs) .PHONY: common-test common-test: $(GOTEST_DIR) @echo ">> running all tests" $(GOTEST) $(test-flags) $(GOOPTS) $(pkgs) $(GOTEST_DIR): @mkdir -p $@ .PHONY: common-format common-format: @echo ">> formatting code" $(GO) fmt $(pkgs) .PHONY: common-vet common-vet: @echo ">> vetting code" $(GO) vet $(GOOPTS) $(pkgs) .PHONY: common-lint common-lint: $(GOLANGCI_LINT) ifdef GOLANGCI_LINT @echo ">> running golangci-lint" # 'go list' needs to be executed before staticcheck to prepopulate the modules cache. # Otherwise staticcheck might fail randomly for some reason not yet explained. $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null $(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs) endif .PHONY: common-yamllint common-yamllint: @echo ">> running yamllint on all YAML files in the repository" ifeq (, $(shell command -v yamllint > /dev/null)) @echo "yamllint not installed so skipping" else yamllint . endif # For backward-compatibility. .PHONY: common-staticcheck common-staticcheck: lint .PHONY: common-unused common-unused: @echo ">> running check for unused/missing packages in go.mod" $(GO) mod tidy @git diff --exit-code -- go.sum go.mod .PHONY: common-build common-build: promu @echo ">> building binaries" $(PROMU) build --prefix $(PREFIX) $(PROMU_BINARIES) .PHONY: common-tarball common-tarball: promu @echo ">> building release tarball" $(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) .PHONY: common-docker $(BUILD_DOCKER_ARCHS) common-docker: $(BUILD_DOCKER_ARCHS) $(BUILD_DOCKER_ARCHS): common-docker-%: docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \ -f $(DOCKERFILE_PATH) \ --build-arg ARCH="$*" \ --build-arg OS="linux" \ $(DOCKERBUILD_CONTEXT) .PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS) common-docker-publish: $(PUBLISH_DOCKER_ARCHS) $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION))) .PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS) common-docker-tag-latest: $(TAG_DOCKER_ARCHS) $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)" .PHONY: common-docker-manifest common-docker-manifest: DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG)) DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" .PHONY: promu promu: $(PROMU) $(PROMU): $(eval PROMU_TMP := $(shell mktemp -d)) curl -s -L $(PROMU_URL) | tar -xvzf - -C $(PROMU_TMP) mkdir -p $(FIRST_GOPATH)/bin cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu rm -r $(PROMU_TMP) .PHONY: proto proto: @echo ">> generating code from proto files" @./scripts/genproto.sh ifdef GOLANGCI_LINT $(GOLANGCI_LINT): mkdir -p $(FIRST_GOPATH)/bin curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/$(GOLANGCI_LINT_VERSION)/install.sh \ | sed -e '/install -d/d' \ | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION) endif .PHONY: precheck precheck:: define PRECHECK_COMMAND_template = precheck:: $(1)_precheck PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1))) .PHONY: $(1)_precheck $(1)_precheck: @if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \ echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \ exit 1; \ fi endef golang-github-prometheus-exporter-toolkit-0.11.0/README.md000066400000000000000000000022251453611405200233230ustar00rootroot00000000000000# Prometheus Exporter Toolkit [![CircleCI](https://circleci.com/gh/prometheus/exporter-toolkit/tree/master.svg?style=shield)][circleci] [![Go Report Card](https://goreportcard.com/badge/github.com/prometheus/exporter-toolkit)][goreportcard] [![go-doc](https://godoc.org/github.com/prometheus/exporter-toolkit?status.svg)][godoc] This is a [Go](http://golang.org) library for [Prometheus](http://prometheus.io) [exporters][exporter]. This repository is meant to be used in combination with the [client_golang][client_golang] repository. If you are [instrumenting][instrumentation] an existing Go application, [client_golang][client_golang] is the repository you are looking for. **This repository is currently WIP and experimental.** [circleci]:https://circleci.com/gh/prometheus/exporter-toolkit [client_golang]:https://github.com/prometheus/client_golang [exporter]:https://prometheus.io/docs/introduction/glossary/#exporter [godoc]:https://godoc.org/github.com/prometheus/exporter-toolkit [goreportcard]:https://goreportcard.com/report/github.com/prometheus/exporter-toolkit [instrumentation]:https://prometheus.io/docs/introduction/glossary/#direct-instrumentation golang-github-prometheus-exporter-toolkit-0.11.0/SECURITY.md000066400000000000000000000002541453611405200236350ustar00rootroot00000000000000# Reporting a security issue The Prometheus security policy, including how to report vulnerabilities, can be found here: golang-github-prometheus-exporter-toolkit-0.11.0/VERSION000066400000000000000000000000071453611405200231100ustar00rootroot000000000000000.11.0 golang-github-prometheus-exporter-toolkit-0.11.0/docs/000077500000000000000000000000001453611405200227735ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/docs/introduction.md000066400000000000000000000006051453611405200260370ustar00rootroot00000000000000# Introduction The Prometheus Exporter Toolkit is a toolkit available to promote a common set of practices when developing in the Prometheus ecosystem. It is written in Golang. This documentation is the end user configuration. Developers documentation can be found on [pkg.go.dev](https://pkg.go.dev/github.com/prometheus/exporter-toolkit/). * [Web configuration](web-configuration.md) golang-github-prometheus-exporter-toolkit-0.11.0/docs/web-config.yml000066400000000000000000000010061453611405200255330ustar00rootroot00000000000000# TLS and basic authentication configuration example. # # Additionally, a certificate and a key file are needed. tls_server_config: cert_file: server.crt key_file: server.key # Usernames and passwords required to connect. # Passwords are hashed with bcrypt: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md#about-bcrypt. basic_auth_users: alice: $2y$10$mDwo.lAisC94iLAyP81MCesa29IzH37oigHC/42V2pdJlUprsJPze bob: $2y$10$hLqFl9jSjoAAy95Z/zw8Ye8wkdMBM8c5Bn1ptYqP/AXyV0.oy0S8m golang-github-prometheus-exporter-toolkit-0.11.0/docs/web-configuration.md000066400000000000000000000145141453611405200267440ustar00rootroot00000000000000# Web configuration Exporters and services instrumented with the Exporter Toolkit share the same web configuration file format. This is *experimental* and might change in the future. To specify which web configuration file to load, use the `--web.config.file` flag. The file is written in [YAML format](https://en.wikipedia.org/wiki/YAML), defined by the scheme described below. Brackets indicate that a parameter is optional. For non-list parameters the value is set to the specified default. The file is read upon every http request, such as any change in the configuration, so the certificates are picked up immediately. Generic placeholders are defined as follows: * ``: a boolean that can take the values `true` or `false` * ``: a valid path in the current working directory * ``: a regular string that is a secret, such as a password * ``: a regular string ``` tls_server_config: # Certificate for server to use to authenticate to client. # Expected to be passed as a PEM encoded sequence of bytes as a string. # # NOTE: If passing the cert inline, cert_file should not be specified below. [ cert: ] # Key for server to use to authenticate to client. # Expected to be passed as a PEM encoded sequence of bytes as a string. # # NOTE: If passing the key inline, key_file should not be specified below. [ key: ] # CA certificate for client certificate authentication to the server. # Expected to be passed as a PEM encoded sequence of bytes as a string. # # NOTE: If passing the client_ca inline, client_ca_file should not be specified below. [ client_ca: ] # Certificate and key files for server to use to authenticate to client. cert_file: key_file: # Server policy for client authentication. Maps to ClientAuth Policies. # For more detail on clientAuth options: # https://golang.org/pkg/crypto/tls/#ClientAuthType # # NOTE: If you want to enable client authentication, you need to use # RequireAndVerifyClientCert. Other values are insecure. [ client_auth_type: | default = "NoClientCert" ] # CA certificate for client certificate authentication to the server. [ client_ca_file: ] # Verify that the client certificate has a Subject Alternate Name (SAN) # which is an exact match to an entry in this list, else terminate the # connection. SAN match can be one or multiple of the following: DNS, # IP, e-mail, or URI address from https://pkg.go.dev/crypto/x509#Certificate. [ client_allowed_sans: [ - ] ] # Minimum TLS version that is acceptable. [ min_version: | default = "TLS12" ] # Maximum TLS version that is acceptable. [ max_version: | default = "TLS13" ] # List of supported cipher suites for TLS versions up to TLS 1.2. If empty, # Go default cipher suites are used. Available cipher suites are documented # in the go documentation: # https://golang.org/pkg/crypto/tls/#pkg-constants # # Note that only the cipher returned by the following function are supported: # https://pkg.go.dev/crypto/tls#CipherSuites [ cipher_suites: [ - ] ] # prefer_server_cipher_suites controls whether the server selects the # client's most preferred ciphersuite, or the server's most preferred # ciphersuite. If true then the server's preference, as expressed in # the order of elements in cipher_suites, is used. [ prefer_server_cipher_suites: | default = true ] # Elliptic curves that will be used in an ECDHE handshake, in preference # order. Available curves are documented in the go documentation: # https://golang.org/pkg/crypto/tls/#CurveID [ curve_preferences: [ - ] ] http_server_config: # Enable HTTP/2 support. Note that HTTP/2 is only supported with TLS. # This can not be changed on the fly. [ http2: | default = true ] # List of headers that can be added to HTTP responses. [ headers: # Set the Content-Security-Policy header to HTTP responses. # Unset if blank. [ Content-Security-Policy: ] # Set the X-Frame-Options header to HTTP responses. # Unset if blank. Accepted values are deny and sameorigin. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options [ X-Frame-Options: ] # Set the X-Content-Type-Options header to HTTP responses. # Unset if blank. Accepted value is nosniff. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options [ X-Content-Type-Options: ] # Set the X-XSS-Protection header to all responses. # Unset if blank. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection [ X-XSS-Protection: ] # Set the Strict-Transport-Security header to HTTP responses. # Unset if blank. # Please make sure that you use this with care as this header might force # browsers to load Prometheus and the other applications hosted on the same # domain and subdomains over HTTPS. # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security [ Strict-Transport-Security: ] ] # Usernames and hashed passwords that have full access to the web # server via basic authentication. If empty, no basic authentication is # required. Passwords are hashed with bcrypt. basic_auth_users: [ : ... ] ``` [A sample configuration file](web-config.yml) is provided. ## About bcrypt There are several tools out there to generate bcrypt passwords, e.g. [htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html): `htpasswd -nBC 10 "" | tr -d ':\n'` That command will prompt you for a password and output the hashed password, which will look something like: `$2y$10$X0h1gDsPszWURQaxFh.zoubFi6DXncSjhoQNJgRrnGs7EsimhC7zG` The cost (10 in the example) influences the time it takes for computing the hash. A higher cost will end up slowing down the authentication process. Depending on the machine, a cost of 10 will take about ~70ms, whereas a cost of 18 can take up to a few seconds. That hash will be computed on the first authenticated HTTP request and then cached. ## Performance Basic authentication is meant for simple use cases, with a few users. If you need to authenticate a lot of users, it is recommended to use TLS client certificates, or to use a proper reverse proxy to handle the authentication. golang-github-prometheus-exporter-toolkit-0.11.0/go.mod000066400000000000000000000024421453611405200231530ustar00rootroot00000000000000module github.com/prometheus/exporter-toolkit go 1.18 require ( github.com/alecthomas/kingpin/v2 v2.4.0 github.com/coreos/go-systemd/v22 v22.5.0 github.com/go-kit/log v0.2.1 github.com/prometheus/common v0.45.0 golang.org/x/crypto v0.16.0 golang.org/x/sync v0.5.0 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/procfs v0.11.1 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.12.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect ) golang-github-prometheus-exporter-toolkit-0.11.0/go.sum000066400000000000000000000160061453611405200232010ustar00rootroot00000000000000github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= golang-github-prometheus-exporter-toolkit-0.11.0/web/000077500000000000000000000000001453611405200226205ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/README.md000066400000000000000000000005141453611405200240770ustar00rootroot00000000000000# web package This package can be used by Prometheus exporters to enable TLS and authentication. We actively encourage the community to use this repository, to provide a consistent experience across the ecosystem. Developers documentation can be found on [pkg.go.dev](https://pkg.go.dev/github.com/prometheus/exporter-toolkit/). golang-github-prometheus-exporter-toolkit-0.11.0/web/cache.go000066400000000000000000000051261453611405200242160ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // This code is partly borrowed from Caddy: // Copyright 2015 Matthew Holt and The Caddy Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package web import ( weakrand "math/rand" "sync" "time" ) var cacheSize = 100 func init() { weakrand.Seed(time.Now().UnixNano()) } type cache struct { cache map[string]bool mtx sync.Mutex } // newCache returns a cache that contains a mapping of plaintext passwords // to their hashes (with random eviction). This can greatly improve the // performance of traffic-heavy servers that use secure password hashing // algorithms, with the downside that plaintext passwords will be stored in // memory for a longer time (this should not be a problem as long as your // machine is not compromised, at which point all bets are off, since basicauth // necessitates plaintext passwords being received over the wire anyway). func newCache() *cache { return &cache{ cache: make(map[string]bool), } } func (c *cache) get(key string) (bool, bool) { c.mtx.Lock() defer c.mtx.Unlock() v, ok := c.cache[key] return v, ok } func (c *cache) set(key string, value bool) { c.mtx.Lock() defer c.mtx.Unlock() c.makeRoom() c.cache[key] = value } func (c *cache) makeRoom() { if len(c.cache) < cacheSize { return } // We delete more than just 1 entry so that we don't have // to do this on every request; assuming the capacity of // the cache is on a long tail, we can save a lot of CPU // time by doing a whole bunch of deletions now and then // we won't have to do them again for a while. numToDelete := len(c.cache) / 10 if numToDelete < 1 { numToDelete = 1 } for deleted := 0; deleted <= numToDelete; deleted++ { // Go maps are "nondeterministic" not actually random, // so although we could just chop off the "front" of the // map with less code, this is a heavily skewed eviction // strategy; generating random numbers is cheap and // ensures a much better distribution. rnd := weakrand.Intn(len(c.cache)) i := 0 for key := range c.cache { if i == rnd { delete(c.cache, key) break } i++ } } } golang-github-prometheus-exporter-toolkit-0.11.0/web/cache_test.go000066400000000000000000000021211453611405200252450ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package web import ( "fmt" "testing" ) // TestCacheSize validates that makeRoom function caps the size of the cache // appropriately. func TestCacheSize(t *testing.T) { cache := newCache() expectedSize := 0 for i := 0; i < 200; i++ { cache.set(fmt.Sprintf("foo%d", i), true) expectedSize++ if expectedSize > 100 { expectedSize = 90 } if gotSize := len(cache.cache); gotSize != expectedSize { t.Fatalf("iter %d: cache size invalid: expected %d, got %d", i, expectedSize, gotSize) } } } golang-github-prometheus-exporter-toolkit-0.11.0/web/handler.go000066400000000000000000000075051453611405200245730ustar00rootroot00000000000000// Copyright 2020 The Prometheus Authors // This code is partly borrowed from Caddy: // Copyright 2015 Matthew Holt and The Caddy Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package web import ( "encoding/hex" "fmt" "net/http" "strings" "sync" "github.com/go-kit/log" "golang.org/x/crypto/bcrypt" ) // extraHTTPHeaders is a map of HTTP headers that can be added to HTTP // responses. // This is private on purpose to ensure consistency in the Prometheus ecosystem. var extraHTTPHeaders = map[string][]string{ "Strict-Transport-Security": nil, "X-Content-Type-Options": {"nosniff"}, "X-Frame-Options": {"deny", "sameorigin"}, "X-XSS-Protection": nil, "Content-Security-Policy": nil, } func validateUsers(configPath string) error { c, err := getConfig(configPath) if err != nil { return err } for _, p := range c.Users { _, err = bcrypt.Cost([]byte(p)) if err != nil { return err } } return nil } // validateHeaderConfig checks that the provided header configuration is correct. // It does not check the validity of all the values, only the ones which are // well-defined enumerations. func validateHeaderConfig(headers map[string]string) error { HeadersLoop: for k, v := range headers { values, ok := extraHTTPHeaders[k] if !ok { return fmt.Errorf("HTTP header %q can not be configured", k) } for _, allowedValue := range values { if v == allowedValue { continue HeadersLoop } } if len(values) > 0 { return fmt.Errorf("invalid value for %s. Expected one of: %q, but got: %q", k, values, v) } } return nil } type webHandler struct { tlsConfigPath string handler http.Handler logger log.Logger cache *cache // bcryptMtx is there to ensure that bcrypt.CompareHashAndPassword is run // only once in parallel as this is CPU intensive. bcryptMtx sync.Mutex } func (u *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c, err := getConfig(u.tlsConfigPath) if err != nil { u.logger.Log("msg", "Unable to parse configuration", "err", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } // Configure http headers. for k, v := range c.HTTPConfig.Header { w.Header().Set(k, v) } if len(c.Users) == 0 { u.handler.ServeHTTP(w, r) return } user, pass, auth := r.BasicAuth() if auth { hashedPassword, validUser := c.Users[user] if !validUser { // The user is not found. Use a fixed password hash to // prevent user enumeration by timing requests. // This is a bcrypt-hashed version of "fakepassword". hashedPassword = "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSi" } cacheKey := strings.Join( []string{ hex.EncodeToString([]byte(user)), hex.EncodeToString([]byte(hashedPassword)), hex.EncodeToString([]byte(pass)), }, ":") authOk, ok := u.cache.get(cacheKey) if !ok { // This user, hashedPassword, password is not cached. u.bcryptMtx.Lock() err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass)) u.bcryptMtx.Unlock() authOk = validUser && err == nil u.cache.set(cacheKey, authOk) } if authOk && validUser { u.handler.ServeHTTP(w, r) return } } w.Header().Set("WWW-Authenticate", "Basic") http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) } golang-github-prometheus-exporter-toolkit-0.11.0/web/handler_test.go000066400000000000000000000142131453611405200256240ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package web import ( "context" "net" "net/http" "sync" "testing" "time" ) // TestBasicAuthCache validates that the cache is working by calling a password // protected endpoint multiple times. func TestBasicAuthCache(t *testing.T) { server := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }), } done := make(chan struct{}) t.Cleanup(func() { if err := server.Shutdown(context.Background()); err != nil { t.Fatal(err) } <-done }) go func() { flags := FlagConfig{ WebListenAddresses: &([]string{port}), WebSystemdSocket: OfBool(false), WebConfigFile: OfString("testdata/web_config_users_noTLS.good.yml"), } ListenAndServe(server, &flags, testlogger) close(done) }() waitForPort(t, port) login := func(username, password string, code int) { client := &http.Client{} req, err := http.NewRequest("GET", "http://localhost"+port, nil) if err != nil { t.Fatal(err) } req.SetBasicAuth(username, password) r, err := client.Do(req) if err != nil { t.Fatal(err) } if r.StatusCode != code { t.Fatalf("bad return code, expected %d, got %d", code, r.StatusCode) } } // Initial logins, checking that it just works. login("alice", "alice123", 200) login("alice", "alice1234", 401) var ( start = make(chan struct{}) wg sync.WaitGroup ) wg.Add(300) for i := 0; i < 150; i++ { go func() { <-start login("alice", "alice123", 200) wg.Done() }() go func() { <-start login("alice", "alice1234", 401) wg.Done() }() } close(start) wg.Wait() } // TestBasicAuthWithFakePassword validates that we can't login the "fakepassword" used in // to prevent user enumeration. func TestBasicAuthWithFakepassword(t *testing.T) { server := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }), } done := make(chan struct{}) t.Cleanup(func() { if err := server.Shutdown(context.Background()); err != nil { t.Fatal(err) } <-done }) go func() { flags := FlagConfig{ WebListenAddresses: &([]string{port}), WebSystemdSocket: OfBool(false), WebConfigFile: OfString("testdata/web_config_users_noTLS.good.yml"), } ListenAndServe(server, &flags, testlogger) close(done) }() waitForPort(t, port) login := func() { client := &http.Client{} req, err := http.NewRequest("GET", "http://localhost"+port, nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("fakeuser", "fakepassword") r, err := client.Do(req) if err != nil { t.Fatal(err) } if r.StatusCode != 401 { t.Fatalf("bad return code, expected %d, got %d", 401, r.StatusCode) } } // Login with a cold cache. login() // Login with the response cached. login() } // TestByPassBasicAuthVuln tests for CVE-2022-46146. func TestByPassBasicAuthVuln(t *testing.T) { server := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }), } done := make(chan struct{}) t.Cleanup(func() { if err := server.Shutdown(context.Background()); err != nil { t.Fatal(err) } <-done }) go func() { flags := FlagConfig{ WebListenAddresses: &([]string{port}), WebSystemdSocket: OfBool(false), WebConfigFile: OfString("testdata/web_config_users_noTLS.good.yml"), } ListenAndServe(server, &flags, testlogger) close(done) }() waitForPort(t, port) login := func(username, password string) { client := &http.Client{} req, err := http.NewRequest("GET", "http://localhost"+port, nil) if err != nil { t.Fatal(err) } req.SetBasicAuth(username, password) r, err := client.Do(req) if err != nil { t.Fatal(err) } if r.StatusCode != 401 { t.Fatalf("bad return code, expected %d, got %d", 401, r.StatusCode) } } // Poison the cache. login("alice$2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby", "fakepassword") // Login with a wrong password. login("alice", "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSifakepassword") } // TestHTTPHeaders validates that HTTP headers are added correctly. func TestHTTPHeaders(t *testing.T) { server := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }), } done := make(chan struct{}) t.Cleanup(func() { if err := server.Shutdown(context.Background()); err != nil { t.Fatal(err) } <-done }) go func() { flags := FlagConfig{ WebListenAddresses: &([]string{port}), WebSystemdSocket: OfBool(false), WebConfigFile: OfString("testdata/web_config_headers.good.yml"), } ListenAndServe(server, &flags, testlogger) close(done) }() waitForPort(t, port) client := &http.Client{} req, err := http.NewRequest("GET", "http://localhost"+port, nil) if err != nil { t.Fatal(err) } r, err := client.Do(req) if err != nil { t.Fatal(err) } for k, v := range map[string]string{ "Strict-Transport-Security": "max-age=31536000; includeSubDomains", "X-Frame-Options": "deny", "X-Content-Type-Options": "nosniff", "X-XSS-Protection": "1", } { if got := r.Header.Get(k); got != v { t.Fatalf("unexpected %s header value, expected %q, got %q", k, v, got) } } } func waitForPort(t *testing.T, addr string) { start := time.Now() for { conn, err := net.DialTimeout("tcp", addr, time.Second) if err == nil { conn.Close() return } if time.Since(start) >= 5*time.Second { t.Fatalf("timeout waiting for port %s: %s", addr, err) return } time.Sleep(time.Millisecond * 100) } } golang-github-prometheus-exporter-toolkit-0.11.0/web/kingpinflag/000077500000000000000000000000001453611405200251115ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/kingpinflag/flag.go000066400000000000000000000034021453611405200263500ustar00rootroot00000000000000// Copyright 2020 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package kingpinflag import ( "runtime" "github.com/alecthomas/kingpin/v2" "github.com/prometheus/exporter-toolkit/web" ) // AddFlags adds the flags used by this package to the Kingpin application. // To use the default Kingpin application, call // AddFlags(kingpin.CommandLine, ":portNum") where portNum is the default port. func AddFlags(a *kingpin.Application, defaultAddress string) *web.FlagConfig { systemdSocket := func() *bool { b := false; return &b }() // Socket activation only available on Linux if runtime.GOOS == "linux" { systemdSocket = a.Flag( "web.systemd-socket", "Use systemd socket activation listeners instead of port listeners (Linux only).", ).Bool() } flags := web.FlagConfig{ WebListenAddresses: a.Flag( "web.listen-address", "Addresses on which to expose metrics and web interface. Repeatable for multiple addresses.", ).Default(defaultAddress).Strings(), WebSystemdSocket: systemdSocket, WebConfigFile: a.Flag( "web.config.file", "Path to configuration file that can enable TLS or authentication. See: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md", ).Default("").String(), } return &flags } golang-github-prometheus-exporter-toolkit-0.11.0/web/landing_page.css000066400000000000000000000006361453611405200257470ustar00rootroot00000000000000body { font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji; margin: 0; } header { background-color: {{.HeaderColor}}; color: #fff; font-size: 1rem; padding: 1rem; } main { padding: 1rem; } label { display: inline-block; width: {{.Form.Width}}em; } {{.ExtraCSS}} golang-github-prometheus-exporter-toolkit-0.11.0/web/landing_page.go000066400000000000000000000061241453611405200255620ustar00rootroot00000000000000// Copyright 2023 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build !genassets // +build !genassets //go:generate go run -tags genassets gen_assets.go package web import ( "bytes" _ "embed" "net/http" "text/template" ) // Config represents the configuration of the web listener. type LandingConfig struct { HeaderColor string // Used for the landing page header. CSS string // CSS style tag for the landing page. Name string // The name of the exporter, generally suffixed by _exporter. Description string // A short description about the exporter. Form LandingForm // A POST form. Links []LandingLinks // Links displayed on the landing page. ExtraHTML string // Additional HTML to be embedded. ExtraCSS string // Additional CSS to be embedded. Version string // The version displayed. } // LandingForm provides a configuration struct for creating a POST form on the landing page. type LandingForm struct { Action string Inputs []LandingFormInput Width float64 } // LandingFormInput represents a single form input field. type LandingFormInput struct { Label string Type string Name string Placeholder string Value string } type LandingLinks struct { Address string // The URL the link points to. Text string // The text of the link. Description string // A descriptive textfor the link. } type LandingPageHandler struct { landingPage []byte } var ( //go:embed landing_page.html landingPagehtmlContent string //go:embed landing_page.css landingPagecssContent string ) func NewLandingPage(c LandingConfig) (*LandingPageHandler, error) { var buf bytes.Buffer length := 0 for _, input := range c.Form.Inputs { inputLength := len(input.Label) if inputLength > length { length = inputLength } } c.Form.Width = (float64(length) + 1) / 2 if c.CSS == "" { if c.HeaderColor == "" { // Default to Prometheus orange. c.HeaderColor = "#e6522c" } cssTemplate := template.Must(template.New("landing css").Parse(landingPagecssContent)) if err := cssTemplate.Execute(&buf, c); err != nil { return nil, err } c.CSS = buf.String() } t := template.Must(template.New("landing page").Parse(landingPagehtmlContent)) buf.Reset() if err := t.Execute(&buf, c); err != nil { return nil, err } return &LandingPageHandler{ landingPage: buf.Bytes(), }, nil } func (h *LandingPageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/html; charset=UTF-8") w.Write(h.landingPage) } golang-github-prometheus-exporter-toolkit-0.11.0/web/landing_page.html000066400000000000000000000017471453611405200261270ustar00rootroot00000000000000 {{.Name}}

{{.Name}}

{{if .Description}}

{{.Description}}

{{end}} {{if .Version}}
Version: {{.Version}}
{{end}}
    {{ range .Links }}
  • {{.Text}}{{if .Description}}: {{.Description}}{{end}}
  • {{ end }}
{{ if .Form.Action }}
{{ range .Form.Inputs }}  
{{ end }}
{{ end }} {{ .ExtraHTML }}
golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/000077500000000000000000000000001453611405200244315ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/client2_selfsigned.key000066400000000000000000000004621453611405200307100ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCgZCrQEAUVqznwKRvu dwwi8wutaRaHHHWDd/IpjJopLhcdvONT7Fv57X0foCvmYFOhZANiAAR/zAKpT17i U9lmokwDicnziss91+vKhQjy2q4EAe1p7jJ9c/fPofP3Zd09pLhkAUONMu0myXjk piLE1vvL121tWg3E3F0MLjLBqiSWqSkEZjQj0YSk3NoGWX/gMgm8ZyA= -----END PRIVATE KEY----- golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/client2_selfsigned.pem000066400000000000000000000013001453611405200306710ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIB3DCCAWGgAwIBAgIUJVN8KehL1MmccvLb/mHthSMfnnswCgYIKoZIzj0EAwIw EDEOMAwGA1UEAwwFdGVzdDMwIBcNMjMwMTEwMTgxMTAwWhgPMjEyMjEyMTcxODEx MDBaMBAxDjAMBgNVBAMMBXRlc3QzMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEf8wC qU9e4lPZZqJMA4nJ84rLPdfryoUI8tquBAHtae4yfXP3z6Hz92XdPaS4ZAFDjTLt Jsl45KYixNb7y9dtbVoNxNxdDC4ywaoklqkpBGY0I9GEpNzaBll/4DIJvGcgo3ow eDAdBgNVHQ4EFgQUvyvu/TnJyRS7OGdujTbWM/W07yMwHwYDVR0jBBgwFoAUvyvu /TnJyRS7OGdujTbWM/W07yMwDwYDVR0TAQH/BAUwAwEB/zAQBgNVHREECTAHggV0 ZXN0MzATBgNVHSUEDDAKBggrBgEFBQcDAjAKBggqhkjOPQQDAgNpADBmAjEAt7HK knE2MzwZ2B2dgn1/q3ikWDiO20Hbd97jo3tmv87FcF2vMqqJpHjcldJqplfsAjEA sfAz49y6Sf6LNlNS+Fc/lbOOwcrlzC+J5GJ8OmNoQPsvvDvhzGbwFiVw1M2uMqtG -----END CERTIFICATE----- golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/client_selfsigned.key000066400000000000000000000004621453611405200306260ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDmoTxYcBfRrqYb/TJy oHlBKo4/fNk2LBUZxpC3HeKasAQzS9AB1evw3k4M3Pe8c4+hZANiAASxUS40AV1Y h1ABCLCoJcG9B8Twv/gg2tU0zqdW9FhK2Fu13MeZkTRJLFVgFzlmCj3o9dIX8iUi RP9jYkQG6wHD44kb9NQ4A7fjs8DOANGWKgY/96liSh/ynPKCoWONW8w= -----END PRIVATE KEY----- golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/client_selfsigned.pem000066400000000000000000000012441453611405200306160ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIBxzCCAU2gAwIBAgIUGCNnsX0qd0HD7UaQsx67ze0UaNowCgYIKoZIzj0EAwIw DzENMAsGA1UEAwwEdGVzdDAgFw0yMTA4MjAxNDQ5MTRaGA8yMTIxMDcyNzE0NDkx NFowDzENMAsGA1UEAwwEdGVzdDB2MBAGByqGSM49AgEGBSuBBAAiA2IABLFRLjQB XViHUAEIsKglwb0HxPC/+CDa1TTOp1b0WErYW7Xcx5mRNEksVWAXOWYKPej10hfy JSJE/2NiRAbrAcPjiRv01DgDt+OzwM4A0ZYqBj/3qWJKH/Kc8oKhY41bzKNoMGYw HQYDVR0OBBYEFPRbKtRBgw+AZ0b6T8oWw/+QoyjaMB8GA1UdIwQYMBaAFPRbKtRB gw+AZ0b6T8oWw/+QoyjaMA8GA1UdEwEB/wQFMAMBAf8wEwYDVR0lBAwwCgYIKwYB BQUHAwIwCgYIKoZIzj0EAwIDaAAwZQIwZqwXMJiTycZdmLN+Pwk/8Sb7wQazbocb 16Zw5mZXqFJ4K+74OQMZ33i82hYohtE/AjEAn0a8q8QupgiXpr0I/PvGTRKqLQRM 0mptBvpn/DcB2p3Hi80GJhtchz9Z0OqbMX4S -----END CERTIFICATE----- golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/server.crt000066400000000000000000000037611453611405200264600ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFsDCCA5igAwIBAgIRAMMSh5NoexSCjSvDRf1fpgMwDQYJKoZIhvcNAQELBQAw aTELMAkGA1UEBhMCVVMxEzARBgNVBAoTClByb21ldGhldXMxKTAnBgNVBAsTIFBy b21ldGhldXMgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRowGAYDVQQDExFQcm9tZXRo ZXVzIFRMUyBDQTAgFw0yMjA3MDgwOTE1MDdaGA8yMDcyMDYyNTA5MTUwN1owNjEL MAkGA1UEBhMCVVMxEzARBgNVBAoTClByb21ldGhldXMxEjAQBgNVBAMTCWxvY2Fs aG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANPl1Iv/z+M8jHHU SggOhvCS/0IfNi82+OprwalmhSL1FyRrGeHDpKArIrHhal7oukizJq96wKTddUVu hjPR7srSYX7J2oPznjb2FmLHnD8y+zxO83XNA5WCDB0yA/KhWHhDmd2pihTTZOo9 jvGi3+LyIqXUeiwIpxuNnH2ghoUy+DTzNCknLkIKAVnDPoM1AI0Wu24rs14A8ZVW ivzY/P8xGwlMmDndrrHwJzMSEMeH7IJi9hx4zJalpoYTVq6Z0Rv0+7SpS+iswi/e MILDhmSvLw0R4x31xkzsPOtUsocVjgBCGGGHo70ISsAxsL6E9QFe2uwZSvbBKfou JaM0txRIZahMeHy5egh2+J08vuZKo9PDBWwKwqQZ4Kb7WtgekiycLmFa/OYHLUX+ Ow8QXu5HU9v9XlP9GV2FQDka2IuMTtS5JCEt5e9ddSb4KVbkRAhfL2snA+w0nmrf CBlrlThFz5Evy5QNAo1ORwiE+8gNUc12EAu9K3TK9WSUYNrLCbkN3oBL+DVp8Y6q quUpKEbElhsJ9V49Err3LPaXpz5aW7Th6oFq7UOB7chqKQ2SNl3/hTlNUw8wFb9Q i8AXs+4SzHo41IEe9QZBvpeucVmdewbJKvNS8Uxs2wmtTq2G2Ae3qGzWl682J7aU w1X6Y46OanQDNtDVQvGN1CW5kvCXAgMBAAGjgYMwgYAwDgYDVR0PAQH/BAQDAgUg MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB8G A1UdIwQYMBaAFMaaHh5g0+YopeLd1IkizXyK9K/zMCAGA1UdEQQZMBeCCWxvY2Fs aG9zdIcEfwAAAYcEfwAAADANBgkqhkiG9w0BAQsFAAOCAgEAUXL/lzbgbs6whVrE 3wkp0oDGVZ0Jti1hpeQk7Slt3PHsgu9OQOSGcv9QHs0ybhkDWZQjoCH6Nurx5QaY GnpNQjylfy3zAziO0c7C1uXf7Z9AEMQwbOHFLefnvq86MtnwJ7sadQo+ViwtMgOW He4YhkTyu2CqK8GFXRQUNm/SunffXp5zErPCNQURh4hrDUGlXPzyxgx1DyqFvF4S X8IpsoED3d7cbEL7E9dgXNl7wuy3qoPi9P9KydFTIELBGt1oco980S1attSM9159 t9iUIUMT4EdzmZxpIyJMCD+Lz9Y3zWVyz7DTqFWOtAtmhM4lu44K4S4d/JfAGEal 3h3SMCbBPKwpsloO4r9TeGi2f+T7hfiFMdCezEyG8sXrObCDyVudyUnXnxDkZ5TQ NOzqJaUJHeKzb+Z9WSovce3Pb8ok3GoDugmwqyjuN/rz/0jsDTJm18I6HHtONbUp AIV/H/4+Kewc+Ztv97J7MeQB/2VKcY3vpZpMSEkg2ummRhXUfi0haxfoSCKvRwiD BElUVtwHTsn3OBnKMGcBt32iLVsvbb/0AtNpohznPdQT7dqDVguejmwHn/fc4u4Q vfAay/ACARti9XKGplQi7xn+OoYcAVPLYitYBRNEc6t+4f3EKehrDIMRCnxOFBVX 9Dnm1DebturSQQEOuX5rP15lG1I= -----END CERTIFICATE----- golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/server.key000066400000000000000000000063101453611405200264510ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- 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 Rri7hsjysanKPyaPM1oofbRyWApMyRo= -----END PRIVATE KEY----- golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/tls-ca-chain.pem000066400000000000000000000100271453611405200273770ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- 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 0ig2QvMFWMw= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFtDCCA5ygAwIBAgIRAMMSh5NoexSCjSvDRf1fpgEwDQYJKoZIhvcNAQELBQAw ajELMAkGA1UEBhMCVVMxEzARBgNVBAoTClByb21ldGhldXMxKTAnBgNVBAsTIFBy b21ldGhldXMgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRswGQYDVQQDExJQcm9tZXRo ZXVzIFJvb3QgQ0EwIBcNMjIwNzA4MDkxNTA0WhgPMjA3MjA2MjUwOTE1MDRaMGox CzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpQcm9tZXRoZXVzMSkwJwYDVQQLEyBQcm9t ZXRoZXVzIENlcnRpZmljYXRlIEF1dGhvcml0eTEbMBkGA1UEAxMSUHJvbWV0aGV1 cyBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArkzRPi21 E299vXw4FBbMfCXI258SxvvjRVRuKdAHLOBpEEqkYH6r6ScbZaisBFtIePv4ddKl rmv+nDwN84/KS54OOtw1cWD4AnDB0kL3B0pWXjTS1F/u57hRLxM6Ta0UubKbta/h WqSOR/fAA5sgcl+JbbR61QWVeYYXg9bM8YGTwQMeJod26tIUeX/Reo9BHuiW4jPb pvVf7rsOs8E2cGwfYjZu6Zj2qcCxQ/ivCpopKFLNlaKko/KlGDGz9KxK5X3ik+sE fPK9LzLC0k2RLGc3EmcMkdyqE3VNih9nV9SalAXN5yBdYaWWjJXykty7ilU32MBF yO4myL48vif2K68pD/CFhG8YmIOud3woMm1IYS9xlsYKf7+f5CNlxqz+eSoOGhcG dSDNft3h5nuq9J/qb2rIgWMSc2puFNRsx+fis0kS5GvjVadR0lxtArbrNm4S+F22 EjGxeBF5VIWiu31uppbdASIw6DTKcrSVVoWxq+Fk3OOB+7q+rornosop9a/omXGH 0cTmgarjJtMqa0TEQiUPQPPnmpC1joeC7/kh7aks93wfHtY73uAVnTjLGTOwlr50 CgRShcRoLLN049V93l46AFHU/4HWns8dqgdcdGnvIdUCFik916pKDSvEc/DfMLGh H6w9Xlg4+2LgCyG2/FBEMTj+bLoraydzyaECAwEAAaNTMFEwDgYDVR0PAQH/BAQD AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU ST6xDptmazIK/QBU8/S2ctHwzTYwDQYJKoZIhvcNAQELBQADggIBAHM79R/uQwQX vsBDfKyBXWFlrhHAgX8XAwMKHjstpQYCcJoiGLRJaMMjxj31T1tylqPdcxz88THN uj9kVFYMo1GU5K9E9lq0LoWQBmX2R7/RgxWqB7FNS+S0xfGyeUb3YPVPI1yhtsKa 6mCtTuCVgsgs/hTa+umjtffxj7l+IQxD8Fq0RFBae+S0v5mjVC2sUVd6usqVt7F6 LUVuYShyAI705guIV9nkz8ZyLzUBJnQAJ8g6DU+nLmdizigUG+JoD/hBbK2hvcjX SL7JLAhYRI4kzWcYR0GUfDf2knFEWNhU8gCPnw70FHMD9QC3NKkQsPvyQRyJh99+ ipwUFbGJJRYWjFBbUxlqZNqBg6+ylZNFGEnG42u2KvPXjgPdivlQWkrX6nG0ayyl rYrvi0FawP3OBpCrhYhqsqkA2m+5L2Pl+J2SsDv4qmPB6fh7K0YDVB37AZSG+nfL oXXpUtwfc9tR71S7GmgkcqYOkHfSzl7ecxXtE2xyl3zhkUPR9YcG+rQhXRRp0lxF kR0EtGOGuvXMCQ/vBVPNEDS3jdceqIrIRI1yPUdhFkF7lrLsfFULllOt6qQWnhn2 A2ObxHToohwuyri/v8QhqNI2Bg0jJHcAJi8I8taToAstCWrtn+WXyfj/QknAik47 aOK9l5wSyyqPfkHybKvT6z9pqWUchJsz -----END CERTIFICATE----- tls_config_noAuth.requireandverifyclientcert.good.yml000066400000000000000000000002411453611405200371260ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "RequireAndVerifyClientCert" client_ca_file: "client_selfsigned.pem" tls_config_noAuth.requireanyclientcert.good.yml000066400000000000000000000001611453611405200357270ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "RequireAnyClientCert" tls_config_noAuth.requireanyclientcertkeyinline.good.yml000066400000000000000000000067711453611405200376540ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key: | -----BEGIN PRIVATE KEY----- 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 Rri7hsjysanKPyaPM1oofbRyWApMyRo= -----END PRIVATE KEY----- client_auth_type: "RequireAnyClientCert" web_config_auth_clientCAs_invalid.bad.yml000066400000000000000000000001431453611405200343760ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_ca_file: "somefile" web_config_auth_clientCAs_missing.bad.yml000066400000000000000000000001671453611405200344270ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "RequireAndVerifyClientCert" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_auth_client_san.bad.yaml000066400000000000000000000003051453611405200333620ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "RequireAndVerifyClientCert" client_ca_file: "client2_selfsigned.pem" client_allowed_sans: - "bad" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_auth_client_san.good.yaml000066400000000000000000000003421453611405200335650ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "RequireAndVerifyClientCert" client_ca_file: "client2_selfsigned.pem" client_allowed_sans: - "one" - "test3" - "two" web_config_auth_user_list_invalid.bad.yml000066400000000000000000000001441453611405200345430ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" basic_auth_users: john: doe golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_empty.yml000066400000000000000000000000001453611405200304620ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_headers.good.yml000066400000000000000000000003751453611405200317050ustar00rootroot00000000000000http_server_config: headers: X-Frame-Options: deny Strict-Transport-Security: max-age=31536000; includeSubDomains X-Content-Type-Options: nosniff X-XSS-Protection: 1 Content-Security-Policy: "default-src 'self' *.test.example.net" web_config_headers_content_type_options.bad.yml000066400000000000000000000001011453611405200357550ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatahttp_server_config: headers: X-Content-Type-Options: sniff web_config_headers_extra_header.bad.yml000066400000000000000000000000651453611405200341330ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatahttp_server_config: headers: Content-Type: foo web_config_headers_frame_options.bad.yml000066400000000000000000000000701453611405200343410ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatahttp_server_config: headers: X-Frame-Options: foo golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_junk.yml000066400000000000000000000006441453611405200303110ustar00rootroot00000000000000hWkNKCp3fvIx3jKnsaBI TuEjdwNS8A2vYdFbiKqr ay3RiOtykgt4m6m3KOol ZreGpJRGmpDSVV9cioiF r7kDOHhHU2frvv0nLcY2 uQMQM4XgqFkCG6gFAIJZ g99tTkrZhN9b6pkJ6J2y rzdt729HrA2RblDGYfjs MW7GxrBdlCnliYJGPhfr g9kaXxMXcDwsw0C0rv0u 637ZmfRGElb6VBVOtgqn RG0MRezjLYCJQBMUdRDE RzO4VicAzj7asVZAT3oo nPw267UONk7h7KBYRgch Alj38foWqjV3heXXdahm TrMzMgl6JIQ1x4OZB5i4 qlrXFJoeV6Pr77nuiEh9 3yE5vMnnKHm2nImEfzMG bI01UDObHRSaoJLC0vTD G9tlcKU883NkQ6nsxJ8Y golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_junk_key.yml000066400000000000000000000000561453611405200311560ustar00rootroot00000000000000tls_server_config: cert_filse: "server.crt" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth.bad.yml000066400000000000000000000001441453611405200313200ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_ca_file: "/dev/null" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth.good.blocking.yml000066400000000000000000000001671453611405200333160ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "RequireAndVerifyClientCert" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth.good.yml000066400000000000000000000001641453611405200315240ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_allCiphers.good.yml000066400000000000000000000014241453611405200336720ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" cipher_suites: - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - TLS_AES_128_GCM_SHA256 - TLS_AES_256_GCM_SHA384 - TLS_CHACHA20_POLY1305_SHA256 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - TLS_RSA_WITH_AES_128_CBC_SHA - TLS_RSA_WITH_AES_256_CBC_SHA - TLS_RSA_WITH_AES_128_GCM_SHA256 - TLS_RSA_WITH_AES_256_GCM_SHA384 golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_allCurves.good.yml000066400000000000000000000003061453611405200335420ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" curve_preferences: - CurveP256 - CurveP384 - CurveP521 - X25519 web_config_noAuth_certPath_empty.bad.yml000066400000000000000000000000741453611405200343130ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "" key_file: "server.key" web_config_noAuth_certPath_invalid.bad.yml000066400000000000000000000001041453611405200345750ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "somefile" key_file: "server.key" web_config_noAuth_certPath_keyPath_empty.bad.yml000066400000000000000000000001121453611405200357710ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "" key_file: "" client_auth_type: "x" web_config_noAuth_certPath_keyPath_invalid.bad.yml000066400000000000000000000001021453611405200362600ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "somefile" key_file: "somefile" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_cert_empty.bad.yml000066400000000000000000000000671453611405200335570ustar00rootroot00000000000000tls_server_config: cert: "" key_file: "server.key" web_config_noAuth_inventedCiphers.bad.yml000066400000000000000000000002621453611405200344540ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" cipher_suites: - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA2048 web_config_noAuth_inventedCurves.bad.yml000066400000000000000000000002311453611405200343220ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" curve_preferences: - CurveP257 web_config_noAuth_keyPath_empty.bad.yml000066400000000000000000000000741453611405200341460ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "" web_config_noAuth_keyPath_invalid.bad.yml000066400000000000000000000001051453611405200344310ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.cert" key_file: "somefile" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_key_empty.bad.yml000066400000000000000000000000671453611405200334120ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key: "" golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_noHTTP2.good.yml000066400000000000000000000003401453611405200327760ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" cipher_suites: - TLS_RSA_WITH_AES_128_CBC_SHA max_version: TLS12 http_server_config: http2: false web_config_noAuth_noHTTP2Cipher.bad.yml000066400000000000000000000002751453611405200336570ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" cipher_suites: - TLS_RSA_WITH_AES_128_CBC_SHA max_version: TLS12 golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_someCiphers.good.yml000066400000000000000000000003721453611405200340660ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" cipher_suites: - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - TLS_RSA_WITH_AES_128_CBC_SHA min_version: TLS12 max_version: TLS12 web_config_noAuth_someCiphers_noOrder.good.yml000066400000000000000000000004501453611405200354740ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" cipher_suites: - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 prefer_server_cipher_suites: false min_version: TLS12 max_version: TLS12 golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_someCurves.good.yml000066400000000000000000000002541453611405200337370ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" min_version: TLS13 curve_preferences: - CurveP521 golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_noAuth_tlsInline.good.yml000066400000000000000000000131411453611405200335440ustar00rootroot00000000000000tls_server_config: cert: | -----BEGIN CERTIFICATE----- MIIFsDCCA5igAwIBAgIRAMMSh5NoexSCjSvDRf1fpgMwDQYJKoZIhvcNAQELBQAw aTELMAkGA1UEBhMCVVMxEzARBgNVBAoTClByb21ldGhldXMxKTAnBgNVBAsTIFBy b21ldGhldXMgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRowGAYDVQQDExFQcm9tZXRo ZXVzIFRMUyBDQTAgFw0yMjA3MDgwOTE1MDdaGA8yMDcyMDYyNTA5MTUwN1owNjEL MAkGA1UEBhMCVVMxEzARBgNVBAoTClByb21ldGhldXMxEjAQBgNVBAMTCWxvY2Fs aG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANPl1Iv/z+M8jHHU SggOhvCS/0IfNi82+OprwalmhSL1FyRrGeHDpKArIrHhal7oukizJq96wKTddUVu hjPR7srSYX7J2oPznjb2FmLHnD8y+zxO83XNA5WCDB0yA/KhWHhDmd2pihTTZOo9 jvGi3+LyIqXUeiwIpxuNnH2ghoUy+DTzNCknLkIKAVnDPoM1AI0Wu24rs14A8ZVW ivzY/P8xGwlMmDndrrHwJzMSEMeH7IJi9hx4zJalpoYTVq6Z0Rv0+7SpS+iswi/e MILDhmSvLw0R4x31xkzsPOtUsocVjgBCGGGHo70ISsAxsL6E9QFe2uwZSvbBKfou JaM0txRIZahMeHy5egh2+J08vuZKo9PDBWwKwqQZ4Kb7WtgekiycLmFa/OYHLUX+ Ow8QXu5HU9v9XlP9GV2FQDka2IuMTtS5JCEt5e9ddSb4KVbkRAhfL2snA+w0nmrf CBlrlThFz5Evy5QNAo1ORwiE+8gNUc12EAu9K3TK9WSUYNrLCbkN3oBL+DVp8Y6q quUpKEbElhsJ9V49Err3LPaXpz5aW7Th6oFq7UOB7chqKQ2SNl3/hTlNUw8wFb9Q i8AXs+4SzHo41IEe9QZBvpeucVmdewbJKvNS8Uxs2wmtTq2G2Ae3qGzWl682J7aU w1X6Y46OanQDNtDVQvGN1CW5kvCXAgMBAAGjgYMwgYAwDgYDVR0PAQH/BAQDAgUg MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB8G A1UdIwQYMBaAFMaaHh5g0+YopeLd1IkizXyK9K/zMCAGA1UdEQQZMBeCCWxvY2Fs aG9zdIcEfwAAAYcEfwAAADANBgkqhkiG9w0BAQsFAAOCAgEAUXL/lzbgbs6whVrE 3wkp0oDGVZ0Jti1hpeQk7Slt3PHsgu9OQOSGcv9QHs0ybhkDWZQjoCH6Nurx5QaY GnpNQjylfy3zAziO0c7C1uXf7Z9AEMQwbOHFLefnvq86MtnwJ7sadQo+ViwtMgOW He4YhkTyu2CqK8GFXRQUNm/SunffXp5zErPCNQURh4hrDUGlXPzyxgx1DyqFvF4S X8IpsoED3d7cbEL7E9dgXNl7wuy3qoPi9P9KydFTIELBGt1oco980S1attSM9159 t9iUIUMT4EdzmZxpIyJMCD+Lz9Y3zWVyz7DTqFWOtAtmhM4lu44K4S4d/JfAGEal 3h3SMCbBPKwpsloO4r9TeGi2f+T7hfiFMdCezEyG8sXrObCDyVudyUnXnxDkZ5TQ NOzqJaUJHeKzb+Z9WSovce3Pb8ok3GoDugmwqyjuN/rz/0jsDTJm18I6HHtONbUp AIV/H/4+Kewc+Ztv97J7MeQB/2VKcY3vpZpMSEkg2ummRhXUfi0haxfoSCKvRwiD BElUVtwHTsn3OBnKMGcBt32iLVsvbb/0AtNpohznPdQT7dqDVguejmwHn/fc4u4Q vfAay/ACARti9XKGplQi7xn+OoYcAVPLYitYBRNEc6t+4f3EKehrDIMRCnxOFBVX 9Dnm1DebturSQQEOuX5rP15lG1I= -----END CERTIFICATE----- key: | -----BEGIN PRIVATE KEY----- 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 Rri7hsjysanKPyaPM1oofbRyWApMyRo= -----END PRIVATE KEY----- client_auth_type: "VerifyClientCertIfGiven" web_config_noAuth_wrongTLSVersion.bad.yml000066400000000000000000000002121453611405200344020ustar00rootroot00000000000000golang-github-prometheus-exporter-toolkit-0.11.0/web/testdatatls_server_config: cert_file: "server.crt" key_file: "server.key" client_auth_type: "VerifyClientCertIfGiven" min_version: TLS111 golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_users.good.yml000066400000000000000000000005551453611405200314330ustar00rootroot00000000000000tls_server_config: cert_file: "server.crt" key_file: "server.key" basic_auth_users: alice: $2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby bob: $2y$18$4VeFDzXIoPHKnKTU3O3GH.N.vZu06CVqczYZ8WvfzrddFU6tGqjR. carol: $2y$10$qRTBuFoULoYNA7AQ/F3ck.trZBPyjV64.oA4ZsSBCIWvXuvQlQTuu dave: $2y$10$2UXri9cIDdgeKjBo4Rlpx.U3ZLDV8X1IxKmsfOvhcM5oXQt/mLmXq golang-github-prometheus-exporter-toolkit-0.11.0/web/testdata/web_config_users_noTLS.good.yml000066400000000000000000000004471453611405200325120ustar00rootroot00000000000000basic_auth_users: alice: $2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby bob: $2y$18$4VeFDzXIoPHKnKTU3O3GH.N.vZu06CVqczYZ8WvfzrddFU6tGqjR. carol: $2y$10$qRTBuFoULoYNA7AQ/F3ck.trZBPyjV64.oA4ZsSBCIWvXuvQlQTuu dave: $2y$10$2UXri9cIDdgeKjBo4Rlpx.U3ZLDV8X1IxKmsfOvhcM5oXQt/mLmXq golang-github-prometheus-exporter-toolkit-0.11.0/web/tls_config.go000066400000000000000000000325741453611405200253110ustar00rootroot00000000000000// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package web import ( "crypto/tls" "crypto/x509" "errors" "fmt" "net" "net/http" "os" "path/filepath" "github.com/coreos/go-systemd/v22/activation" "github.com/go-kit/log" "github.com/go-kit/log/level" config_util "github.com/prometheus/common/config" "golang.org/x/sync/errgroup" "gopkg.in/yaml.v2" ) var ( errNoTLSConfig = errors.New("TLS config is not present") ErrNoListeners = errors.New("no web listen address or systemd socket flag specified") ) type Config struct { TLSConfig TLSConfig `yaml:"tls_server_config"` HTTPConfig HTTPConfig `yaml:"http_server_config"` Users map[string]config_util.Secret `yaml:"basic_auth_users"` } type TLSConfig struct { TLSCert string `yaml:"cert"` TLSKey config_util.Secret `yaml:"key"` ClientCAsText string `yaml:"client_ca"` TLSCertPath string `yaml:"cert_file"` TLSKeyPath string `yaml:"key_file"` ClientAuth string `yaml:"client_auth_type"` ClientCAs string `yaml:"client_ca_file"` CipherSuites []Cipher `yaml:"cipher_suites"` CurvePreferences []Curve `yaml:"curve_preferences"` MinVersion TLSVersion `yaml:"min_version"` MaxVersion TLSVersion `yaml:"max_version"` PreferServerCipherSuites bool `yaml:"prefer_server_cipher_suites"` ClientAllowedSans []string `yaml:"client_allowed_sans"` } type FlagConfig struct { WebListenAddresses *[]string WebSystemdSocket *bool WebConfigFile *string } // SetDirectory joins any relative file paths with dir. func (t *TLSConfig) SetDirectory(dir string) { t.TLSCertPath = config_util.JoinDir(dir, t.TLSCertPath) t.TLSKeyPath = config_util.JoinDir(dir, t.TLSKeyPath) t.ClientCAs = config_util.JoinDir(dir, t.ClientCAs) } // VerifyPeerCertificate will check the SAN entries of the client cert if there is configuration for it func (t *TLSConfig) VerifyPeerCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error { // sender cert comes first, see https://www.rfc-editor.org/rfc/rfc5246#section-7.4.2 cert, err := x509.ParseCertificate(rawCerts[0]) if err != nil { return fmt.Errorf("error parsing client certificate: %s", err) } // Build up a slice of strings with all Subject Alternate Name values sanValues := append(cert.DNSNames, cert.EmailAddresses...) for _, ip := range cert.IPAddresses { sanValues = append(sanValues, ip.String()) } for _, uri := range cert.URIs { sanValues = append(sanValues, uri.String()) } for _, sanValue := range sanValues { for _, allowedSan := range t.ClientAllowedSans { if sanValue == allowedSan { return nil } } } return fmt.Errorf("could not find allowed SANs in client cert, found: %v", t.ClientAllowedSans) } type HTTPConfig struct { HTTP2 bool `yaml:"http2"` Header map[string]string `yaml:"headers,omitempty"` } func getConfig(configPath string) (*Config, error) { content, err := os.ReadFile(configPath) if err != nil { return nil, err } c := &Config{ TLSConfig: TLSConfig{ MinVersion: tls.VersionTLS12, MaxVersion: tls.VersionTLS13, PreferServerCipherSuites: true, }, HTTPConfig: HTTPConfig{HTTP2: true}, } err = yaml.UnmarshalStrict(content, c) if err == nil { err = validateHeaderConfig(c.HTTPConfig.Header) } c.TLSConfig.SetDirectory(filepath.Dir(configPath)) return c, err } func getTLSConfig(configPath string) (*tls.Config, error) { c, err := getConfig(configPath) if err != nil { return nil, err } return ConfigToTLSConfig(&c.TLSConfig) } func validateTLSPaths(c *TLSConfig) error { if c.TLSCertPath == "" && c.TLSCert == "" && c.TLSKeyPath == "" && c.TLSKey == "" && c.ClientCAs == "" && c.ClientCAsText == "" && c.ClientAuth == "" { return errNoTLSConfig } if c.TLSCertPath == "" && c.TLSCert == "" { return errors.New("missing one of cert or cert_file") } if c.TLSKeyPath == "" && c.TLSKey == "" { return errors.New("missing one of key or key_file") } return nil } // ConfigToTLSConfig generates the golang tls.Config from the TLSConfig struct. func ConfigToTLSConfig(c *TLSConfig) (*tls.Config, error) { if err := validateTLSPaths(c); err != nil { return nil, err } loadCert := func() (*tls.Certificate, error) { var certData, keyData []byte var err error if c.TLSCertPath != "" { certData, err = os.ReadFile(c.TLSCertPath) if err != nil { return nil, fmt.Errorf("failed to read cert_file (%s): %s", c.TLSCertPath, err) } } else { certData = []byte(c.TLSCert) } if c.TLSKeyPath != "" { keyData, err = os.ReadFile(c.TLSKeyPath) if err != nil { return nil, fmt.Errorf("failed to read key_file (%s): %s", c.TLSKeyPath, err) } } else { keyData = []byte(c.TLSKey) } cert, err := tls.X509KeyPair(certData, keyData) if err != nil { return nil, fmt.Errorf("failed to load X509KeyPair: %w", err) } return &cert, nil } // Confirm that certificate and key paths are valid. if _, err := loadCert(); err != nil { return nil, err } cfg := &tls.Config{ MinVersion: (uint16)(c.MinVersion), MaxVersion: (uint16)(c.MaxVersion), PreferServerCipherSuites: c.PreferServerCipherSuites, } cfg.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) { return loadCert() } var cf []uint16 for _, c := range c.CipherSuites { cf = append(cf, (uint16)(c)) } if len(cf) > 0 { cfg.CipherSuites = cf } var cp []tls.CurveID for _, c := range c.CurvePreferences { cp = append(cp, (tls.CurveID)(c)) } if len(cp) > 0 { cfg.CurvePreferences = cp } if c.ClientCAs != "" { clientCAPool := x509.NewCertPool() clientCAFile, err := os.ReadFile(c.ClientCAs) if err != nil { return nil, err } clientCAPool.AppendCertsFromPEM(clientCAFile) cfg.ClientCAs = clientCAPool } else if c.ClientCAsText != "" { clientCAPool := x509.NewCertPool() clientCAPool.AppendCertsFromPEM([]byte(c.ClientCAsText)) cfg.ClientCAs = clientCAPool } if c.ClientAllowedSans != nil { // verify that the client cert contains an allowed SAN cfg.VerifyPeerCertificate = c.VerifyPeerCertificate } switch c.ClientAuth { case "RequestClientCert": cfg.ClientAuth = tls.RequestClientCert case "RequireAnyClientCert", "RequireClientCert": // Preserved for backwards compatibility. cfg.ClientAuth = tls.RequireAnyClientCert case "VerifyClientCertIfGiven": cfg.ClientAuth = tls.VerifyClientCertIfGiven case "RequireAndVerifyClientCert": cfg.ClientAuth = tls.RequireAndVerifyClientCert case "", "NoClientCert": cfg.ClientAuth = tls.NoClientCert default: return nil, errors.New("Invalid ClientAuth: " + c.ClientAuth) } if (c.ClientCAs != "" || c.ClientCAsText != "") && cfg.ClientAuth == tls.NoClientCert { return nil, errors.New("Client CA's have been configured without a Client Auth Policy") } return cfg, nil } // ServeMultiple starts the server on the given listeners. The FlagConfig is // also passed on to Serve. func ServeMultiple(listeners []net.Listener, server *http.Server, flags *FlagConfig, logger log.Logger) error { errs := new(errgroup.Group) for _, l := range listeners { l := l errs.Go(func() error { return Serve(l, server, flags, logger) }) } return errs.Wait() } // ListenAndServe starts the server on addresses given in WebListenAddresses in // the FlagConfig or instead uses systemd socket activated listeners if // WebSystemdSocket in the FlagConfig is true. The FlagConfig is also passed on // to ServeMultiple. func ListenAndServe(server *http.Server, flags *FlagConfig, logger log.Logger) error { if flags.WebSystemdSocket == nil && (flags.WebListenAddresses == nil || len(*flags.WebListenAddresses) == 0) { return ErrNoListeners } if flags.WebSystemdSocket != nil && *flags.WebSystemdSocket { level.Info(logger).Log("msg", "Listening on systemd activated listeners instead of port listeners.") listeners, err := activation.Listeners() if err != nil { return err } if len(listeners) < 1 { return errors.New("no socket activation file descriptors found") } return ServeMultiple(listeners, server, flags, logger) } listeners := make([]net.Listener, 0, len(*flags.WebListenAddresses)) for _, address := range *flags.WebListenAddresses { listener, err := net.Listen("tcp", address) if err != nil { return err } defer listener.Close() listeners = append(listeners, listener) } return ServeMultiple(listeners, server, flags, logger) } // Server starts the server on the given listener. Based on the file path // WebConfigFile in the FlagConfig, TLS or basic auth could be enabled. func Serve(l net.Listener, server *http.Server, flags *FlagConfig, logger log.Logger) error { level.Info(logger).Log("msg", "Listening on", "address", l.Addr().String()) tlsConfigPath := *flags.WebConfigFile if tlsConfigPath == "" { level.Info(logger).Log("msg", "TLS is disabled.", "http2", false, "address", l.Addr().String()) return server.Serve(l) } if err := validateUsers(tlsConfigPath); err != nil { return err } // Setup basic authentication. var handler http.Handler = http.DefaultServeMux if server.Handler != nil { handler = server.Handler } c, err := getConfig(tlsConfigPath) if err != nil { return err } server.Handler = &webHandler{ tlsConfigPath: tlsConfigPath, logger: logger, handler: handler, cache: newCache(), } config, err := ConfigToTLSConfig(&c.TLSConfig) switch err { case nil: if !c.HTTPConfig.HTTP2 { server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) } // Valid TLS config. level.Info(logger).Log("msg", "TLS is enabled.", "http2", c.HTTPConfig.HTTP2, "address", l.Addr().String()) case errNoTLSConfig: // No TLS config, back to plain HTTP. level.Info(logger).Log("msg", "TLS is disabled.", "http2", false, "address", l.Addr().String()) return server.Serve(l) default: // Invalid TLS config. return err } server.TLSConfig = config // Set the GetConfigForClient method of the HTTPS server so that the config // and certs are reloaded on new connections. server.TLSConfig.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) { config, err := getTLSConfig(tlsConfigPath) if err != nil { return nil, err } config.NextProtos = server.TLSConfig.NextProtos return config, nil } return server.ServeTLS(l, "", "") } // Validate configuration file by reading the configuration and the certificates. func Validate(tlsConfigPath string) error { if tlsConfigPath == "" { return nil } if err := validateUsers(tlsConfigPath); err != nil { return err } c, err := getConfig(tlsConfigPath) if err != nil { return err } _, err = ConfigToTLSConfig(&c.TLSConfig) if err == errNoTLSConfig { return nil } return err } type Cipher uint16 func (c *Cipher) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string err := unmarshal((*string)(&s)) if err != nil { return err } for _, cs := range tls.CipherSuites() { if cs.Name == s { *c = (Cipher)(cs.ID) return nil } } return errors.New("unknown cipher: " + s) } func (c Cipher) MarshalYAML() (interface{}, error) { return tls.CipherSuiteName((uint16)(c)), nil } type Curve tls.CurveID var curves = map[string]Curve{ "CurveP256": (Curve)(tls.CurveP256), "CurveP384": (Curve)(tls.CurveP384), "CurveP521": (Curve)(tls.CurveP521), "X25519": (Curve)(tls.X25519), } func (c *Curve) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string err := unmarshal((*string)(&s)) if err != nil { return err } if curveid, ok := curves[s]; ok { *c = curveid return nil } return errors.New("unknown curve: " + s) } func (c *Curve) MarshalYAML() (interface{}, error) { for s, curveid := range curves { if *c == curveid { return s, nil } } return fmt.Sprintf("%v", c), nil } type TLSVersion uint16 var tlsVersions = map[string]TLSVersion{ "TLS13": (TLSVersion)(tls.VersionTLS13), "TLS12": (TLSVersion)(tls.VersionTLS12), "TLS11": (TLSVersion)(tls.VersionTLS11), "TLS10": (TLSVersion)(tls.VersionTLS10), } func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string err := unmarshal((*string)(&s)) if err != nil { return err } if v, ok := tlsVersions[s]; ok { *tv = v return nil } return errors.New("unknown TLS version: " + s) } func (tv *TLSVersion) MarshalYAML() (interface{}, error) { for s, v := range tlsVersions { if *tv == v { return s, nil } } return fmt.Sprintf("%v", tv), nil } // Listen starts the server on the given address. Based on the file // tlsConfigPath, TLS or basic auth could be enabled. // // Deprecated: Use ListenAndServe instead. func Listen(server *http.Server, flags *FlagConfig, logger log.Logger) error { return ListenAndServe(server, flags, logger) } golang-github-prometheus-exporter-toolkit-0.11.0/web/tls_config_test.go000066400000000000000000000536011453611405200263420ustar00rootroot00000000000000// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build go1.14 // +build go1.14 package web import ( "crypto/tls" "crypto/x509" "errors" "fmt" "io" "net" "net/http" "os" "regexp" "sync" "testing" "time" ) // Helpers for literal FlagConfig func OfBool(i bool) *bool { return &i } func OfString(i string) *string { return &i } var ( port = getPort() testlogger = &testLogger{} ErrorMap = map[string]*regexp.Regexp{ "HTTP Response to HTTPS": regexp.MustCompile(`server gave HTTP response to HTTPS client`), "No such file": regexp.MustCompile(`no such file`), "Invalid argument": regexp.MustCompile(`invalid argument`), "YAML error": regexp.MustCompile(`yaml`), "Invalid ClientAuth": regexp.MustCompile(`invalid ClientAuth`), "TLS handshake": regexp.MustCompile(`tls`), "HTTP Request to HTTPS server": regexp.MustCompile(`HTTP`), "Invalid Cert or CertPath": regexp.MustCompile(`missing one of cert or cert_file`), "Invalid Key or KeyPath": regexp.MustCompile(`missing one of key or key_file`), "ClientCA set without policy": regexp.MustCompile(`Client CA's have been configured without a Client Auth Policy`), "Bad password": regexp.MustCompile(`hashedSecret too short to be a bcrypted password`), "Unauthorized": regexp.MustCompile(`Unauthorized`), "Forbidden": regexp.MustCompile(`Forbidden`), "Handshake failure": regexp.MustCompile(`handshake failure`), "Unknown cipher": regexp.MustCompile(`unknown cipher`), "Unknown curve": regexp.MustCompile(`unknown curve`), "Unknown TLS version": regexp.MustCompile(`unknown TLS version`), "No HTTP2 cipher": regexp.MustCompile(`TLSConfig.CipherSuites is missing an HTTP/2-required`), // The first token is returned by Go <= 1.17 and the second token is returned by Go >= 1.18. "Incompatible TLS version": regexp.MustCompile(`protocol version not supported|no supported versions satisfy MinVersion and MaxVersion`), "Bad certificate": regexp.MustCompile(`bad certificate`), "Invalid value": regexp.MustCompile(`invalid value for`), "Invalid header": regexp.MustCompile(`HTTP header ".*" can not be configured`), "Invalid client cert": regexp.MustCompile(`bad certificate`), } ) type testLogger struct{} func (t *testLogger) Log(keyvals ...interface{}) error { return nil } func getPort() string { listener, err := net.Listen("tcp", ":0") if err != nil { panic(err) } defer listener.Close() p := listener.Addr().(*net.TCPAddr).Port return fmt.Sprintf(":%v", p) } type TestInputs struct { Name string Server func() *http.Server YAMLConfigPath string ExpectedError *regexp.Regexp UseTLSClient bool ClientMaxTLSVersion uint16 CipherSuites []uint16 ActualCipher uint16 CurvePreferences []tls.CurveID Username string Password string ClientCertificate string } func TestYAMLFiles(t *testing.T) { testTables := []*TestInputs{ { Name: `path to config yml invalid`, YAMLConfigPath: "somefile", ExpectedError: ErrorMap["No such file"], }, { Name: `empty config yml`, YAMLConfigPath: "testdata/web_config_empty.yml", ExpectedError: nil, }, { Name: `invalid config yml (invalid structure)`, YAMLConfigPath: "testdata/web_config_junk.yml", ExpectedError: ErrorMap["YAML error"], }, { Name: `invalid config yml (invalid key)`, YAMLConfigPath: "testdata/web_config_junk_key.yml", ExpectedError: ErrorMap["YAML error"], }, { Name: `invalid config yml (cert path empty)`, YAMLConfigPath: "testdata/web_config_noAuth_certPath_empty.bad.yml", ExpectedError: ErrorMap["Invalid Cert or CertPath"], }, { Name: `invalid config yml (cert empty)`, YAMLConfigPath: "testdata/web_config_noAuth_cert_empty.bad.yml", ExpectedError: ErrorMap["Invalid Cert or CertPath"], }, { Name: `invalid config yml (key path empty)`, YAMLConfigPath: "testdata/web_config_noAuth_keyPath_empty.bad.yml", ExpectedError: ErrorMap["Invalid Key or KeyPath"], }, { Name: `invalid config yml (key empty)`, YAMLConfigPath: "testdata/web_config_noAuth_key_empty.bad.yml", ExpectedError: ErrorMap["Invalid Key or KeyPath"], }, { Name: `invalid config yml (cert path and key path empty)`, YAMLConfigPath: "testdata/web_config_noAuth_certPath_keyPath_empty.bad.yml", ExpectedError: ErrorMap["Invalid Cert or CertPath"], }, { Name: `invalid config yml (cert path invalid)`, YAMLConfigPath: "testdata/web_config_noAuth_certPath_invalid.bad.yml", ExpectedError: ErrorMap["No such file"], }, { Name: `invalid config yml (key path invalid)`, YAMLConfigPath: "testdata/web_config_noAuth_keyPath_invalid.bad.yml", ExpectedError: ErrorMap["No such file"], }, { Name: `invalid config yml (cert path and key path invalid)`, YAMLConfigPath: "testdata/web_config_noAuth_certPath_keyPath_invalid.bad.yml", ExpectedError: ErrorMap["No such file"], }, { Name: `invalid config yml (invalid ClientAuth)`, YAMLConfigPath: "testdata/web_config_noAuth.bad.yml", ExpectedError: ErrorMap["ClientCA set without policy"], }, { Name: `invalid config yml (invalid ClientCAs filepath)`, YAMLConfigPath: "testdata/web_config_auth_clientCAs_invalid.bad.yml", ExpectedError: ErrorMap["No such file"], }, { Name: `invalid config yml (invalid user list)`, YAMLConfigPath: "testdata/web_config_auth_user_list_invalid.bad.yml", ExpectedError: ErrorMap["Bad password"], }, { Name: `invalid config yml (bad cipher)`, YAMLConfigPath: "testdata/web_config_noAuth_inventedCiphers.bad.yml", ExpectedError: ErrorMap["Unknown cipher"], }, { Name: `invalid config yml (bad curves)`, YAMLConfigPath: "testdata/web_config_noAuth_inventedCurves.bad.yml", ExpectedError: ErrorMap["Unknown curve"], }, { Name: `invalid config yml (bad TLS version)`, YAMLConfigPath: "testdata/web_config_noAuth_wrongTLSVersion.bad.yml", ExpectedError: ErrorMap["Unknown TLS version"], }, } for _, testInputs := range testTables { t.Run("run/"+testInputs.Name, testInputs.Test) t.Run("validate/"+testInputs.Name, testInputs.TestValidate) } } func TestServerBehaviour(t *testing.T) { testTables := []*TestInputs{ { Name: `empty string YAMLConfigPath and default client`, YAMLConfigPath: "", ExpectedError: nil, }, { Name: `empty string YAMLConfigPath and TLS client`, YAMLConfigPath: "", UseTLSClient: true, ExpectedError: ErrorMap["HTTP Response to HTTPS"], }, { Name: `valid tls config yml and default client`, YAMLConfigPath: "testdata/web_config_noAuth.good.yml", ExpectedError: ErrorMap["HTTP Request to HTTPS server"], }, { Name: `valid tls config yml and tls client`, YAMLConfigPath: "testdata/web_config_noAuth.good.yml", UseTLSClient: true, ExpectedError: nil, }, { Name: `valid tls config yml (cert and key inline) and tls client`, YAMLConfigPath: "testdata/web_config_noAuth_tlsInline.good.yml", UseTLSClient: true, ExpectedError: nil, }, { Name: `valid tls config yml with TLS 1.1 client`, YAMLConfigPath: "testdata/web_config_noAuth.good.yml", UseTLSClient: true, ClientMaxTLSVersion: tls.VersionTLS11, ExpectedError: ErrorMap["Incompatible TLS version"], }, { Name: `valid tls config yml with all ciphers`, YAMLConfigPath: "testdata/web_config_noAuth_allCiphers.good.yml", UseTLSClient: true, ExpectedError: nil, }, { Name: `valid tls config yml with some ciphers`, YAMLConfigPath: "testdata/web_config_noAuth_someCiphers.good.yml", UseTLSClient: true, CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, ExpectedError: nil, }, { Name: `valid tls config yml with no common cipher`, YAMLConfigPath: "testdata/web_config_noAuth_someCiphers.good.yml", UseTLSClient: true, CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, ExpectedError: ErrorMap["Handshake failure"], }, { Name: `valid tls config yml with multiple client ciphers`, YAMLConfigPath: "testdata/web_config_noAuth_someCiphers.good.yml", UseTLSClient: true, CipherSuites: []uint16{ tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, }, ActualCipher: tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ExpectedError: nil, }, { Name: `valid tls config yml with multiple client ciphers, client chooses cipher`, YAMLConfigPath: "testdata/web_config_noAuth_someCiphers_noOrder.good.yml", UseTLSClient: true, CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, }, ActualCipher: tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ExpectedError: nil, }, { Name: `valid tls config yml with all curves`, YAMLConfigPath: "testdata/web_config_noAuth_allCurves.good.yml", UseTLSClient: true, ExpectedError: nil, }, { Name: `valid tls config yml with some curves`, YAMLConfigPath: "testdata/web_config_noAuth_someCurves.good.yml", UseTLSClient: true, CurvePreferences: []tls.CurveID{tls.CurveP521}, ExpectedError: nil, }, { Name: `valid tls config yml with no common curves`, YAMLConfigPath: "testdata/web_config_noAuth_someCurves.good.yml", UseTLSClient: true, CurvePreferences: []tls.CurveID{tls.CurveP384}, ExpectedError: ErrorMap["Handshake failure"], }, { Name: `valid tls config yml with non-http2 ciphers`, YAMLConfigPath: "testdata/web_config_noAuth_noHTTP2.good.yml", UseTLSClient: true, ExpectedError: nil, }, { Name: `valid tls config yml with non-http2 ciphers but http2 enabled`, YAMLConfigPath: "testdata/web_config_noAuth_noHTTP2Cipher.bad.yml", UseTLSClient: true, ExpectedError: ErrorMap["No HTTP2 cipher"], }, { Name: `valid tls config yml and tls client with RequireAnyClientCert`, YAMLConfigPath: "testdata/tls_config_noAuth.requireanyclientcert.good.yml", UseTLSClient: true, ExpectedError: ErrorMap["Bad certificate"], }, { Name: `valid headers config`, YAMLConfigPath: "testdata/web_config_headers.good.yml", }, { Name: `invalid X-Content-Type-Options headers config`, YAMLConfigPath: "testdata/web_config_headers_content_type_options.bad.yml", ExpectedError: ErrorMap["Invalid value"], }, { Name: `invalid X-Frame-Options headers config`, YAMLConfigPath: "testdata/web_config_headers_frame_options.bad.yml", ExpectedError: ErrorMap["Invalid value"], }, { Name: `HTTP header that can not be overridden`, YAMLConfigPath: "testdata/web_config_headers_extra_header.bad.yml", ExpectedError: ErrorMap["Invalid header"], }, { Name: `valid tls config yml and tls client with RequireAnyClientCert (present certificate)`, YAMLConfigPath: "testdata/tls_config_noAuth.requireanyclientcert.good.yml", UseTLSClient: true, ClientCertificate: "client_selfsigned", ExpectedError: nil, }, { Name: `valid tls config yml (cert from file, key inline) and tls client with RequireAnyClientCert (present certificate)`, YAMLConfigPath: "testdata/tls_config_noAuth.requireanyclientcert.good.yml", UseTLSClient: true, ClientCertificate: "client_selfsigned", ExpectedError: nil, }, { Name: `valid tls config yml and tls client with RequireAndVerifyClientCert`, YAMLConfigPath: "testdata/tls_config_noAuth.requireandverifyclientcert.good.yml", UseTLSClient: true, ExpectedError: ErrorMap["Bad certificate"], }, { Name: `valid tls config yml and tls client with RequireAndVerifyClientCert (present certificate)`, YAMLConfigPath: "testdata/tls_config_noAuth.requireandverifyclientcert.good.yml", UseTLSClient: true, ClientCertificate: "client_selfsigned", ExpectedError: nil, }, { Name: `valid tls config yml and tls client with RequireAndVerifyClientCert (present wrong certificate)`, YAMLConfigPath: "testdata/tls_config_noAuth.requireandverifyclientcert.good.yml", UseTLSClient: true, ClientCertificate: "client2_selfsigned", ExpectedError: ErrorMap["Bad certificate"], }, { Name: `valid tls config yml and tls client with VerifyPeerCertificate (present good SAN DNS entry)`, YAMLConfigPath: "testdata/web_config_auth_client_san.good.yaml", UseTLSClient: true, ClientCertificate: "client2_selfsigned", ExpectedError: nil, }, { Name: `valid tls config yml and tls client with VerifyPeerCertificate (present invalid SAN DNS entries)`, YAMLConfigPath: "testdata/web_config_auth_client_san.bad.yaml", UseTLSClient: true, ClientCertificate: "client2_selfsigned", ExpectedError: ErrorMap["Invalid client cert"], }, } for _, testInputs := range testTables { t.Run(testInputs.Name, testInputs.Test) } } func TestConfigReloading(t *testing.T) { errorChannel := make(chan error, 1) var once sync.Once recordConnectionError := func(err error) { once.Do(func() { errorChannel <- err }) } defer func() { if recover() != nil { recordConnectionError(errors.New("Panic in test function")) } }() goodYAMLPath := "testdata/web_config_noAuth.good.yml" badYAMLPath := "testdata/web_config_noAuth.good.blocking.yml" server := &http.Server{ Addr: port, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }), } defer func() { server.Close() }() go func() { defer func() { if recover() != nil { recordConnectionError(errors.New("Panic starting server")) } }() flagsBadYAMLPath := FlagConfig{ WebListenAddresses: &([]string{port}), WebSystemdSocket: OfBool(false), WebConfigFile: OfString(badYAMLPath), } err := Listen(server, &flagsBadYAMLPath, testlogger) recordConnectionError(err) }() client := getTLSClient("") TestClientConnection := func() error { time.Sleep(250 * time.Millisecond) r, err := client.Get("https://localhost" + port) if err != nil { return err } body, err := io.ReadAll(r.Body) if err != nil { return err } if string(body) != "Hello World!" { return errors.New(string(body)) } return nil } err := TestClientConnection() if err == nil { recordConnectionError(errors.New("connection accepted but should have failed")) } else { swapFileContents(goodYAMLPath, badYAMLPath) defer swapFileContents(goodYAMLPath, badYAMLPath) err = TestClientConnection() if err != nil { recordConnectionError(errors.New("connection failed but should have been accepted")) } else { recordConnectionError(nil) } } err = <-errorChannel if err != nil { t.Errorf(" *** Failed test: %s *** Returned error: %v", "TestConfigReloading", err) } } func (test *TestInputs) Test(t *testing.T) { errorChannel := make(chan error, 1) var once sync.Once recordConnectionError := func(err error) { once.Do(func() { errorChannel <- err }) } defer func() { if recover() != nil { recordConnectionError(errors.New("Panic in test function")) } }() server := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }), } t.Cleanup(func() { server.Close() }) go func() { defer func() { if recover() != nil { recordConnectionError(errors.New("Panic starting server")) } }() flags := FlagConfig{ WebListenAddresses: &([]string{port}), WebSystemdSocket: OfBool(false), WebConfigFile: &test.YAMLConfigPath, } err := ListenAndServe(server, &flags, testlogger) recordConnectionError(err) }() ClientConnection := func() (*http.Response, error) { var client *http.Client var proto string if test.UseTLSClient { client = getTLSClient(test.ClientCertificate) t := client.Transport.(*http.Transport) t.TLSClientConfig.MaxVersion = test.ClientMaxTLSVersion if len(test.CipherSuites) > 0 { t.TLSClientConfig.CipherSuites = test.CipherSuites } if len(test.CurvePreferences) > 0 { t.TLSClientConfig.CurvePreferences = test.CurvePreferences } proto = "https" } else { client = http.DefaultClient proto = "http" } req, err := http.NewRequest("GET", proto+"://localhost"+port, nil) if err != nil { t.Error(err) } if test.Username != "" { req.SetBasicAuth(test.Username, test.Password) } return client.Do(req) } go func() { time.Sleep(250 * time.Millisecond) r, err := ClientConnection() if err != nil { recordConnectionError(err) return } if test.ActualCipher != 0 { if r.TLS.CipherSuite != test.ActualCipher { recordConnectionError( fmt.Errorf("bad cipher suite selected. Expected: %s, got: %s", tls.CipherSuiteName(test.ActualCipher), tls.CipherSuiteName(r.TLS.CipherSuite), ), ) } } body, err := io.ReadAll(r.Body) if err != nil { recordConnectionError(err) return } if string(body) != "Hello World!" { recordConnectionError(errors.New(string(body))) return } recordConnectionError(nil) }() err := <-errorChannel if test.isCorrectError(err) == false { if test.ExpectedError == nil { t.Logf("Expected no error, got error: %v", err) } else { t.Logf("Expected error matching regular expression: %v", test.ExpectedError) t.Logf("Got: %v", err) } t.Fail() } } func (test *TestInputs) TestValidate(t *testing.T) { validationErr := Validate(test.YAMLConfigPath) if test.ExpectedError == nil { if validationErr != nil { t.Errorf("Expected no error, got error: %v", validationErr) } return } if validationErr == nil { t.Errorf("Got no error, expected: %v", test.ExpectedError) return } if !test.ExpectedError.MatchString(validationErr.Error()) { t.Errorf("Expected error %v, got error: %v", test.ExpectedError, validationErr) } } func (test *TestInputs) isCorrectError(returnedError error) bool { switch { case returnedError == nil && test.ExpectedError == nil: case returnedError != nil && test.ExpectedError != nil && test.ExpectedError.MatchString(returnedError.Error()): default: return false } return true } func getTLSClient(clientCertName string) *http.Client { cert, err := os.ReadFile("testdata/tls-ca-chain.pem") if err != nil { panic("Unable to start TLS client. Check cert path") } var clientCertficate tls.Certificate if clientCertName != "" { clientCertficate, err = tls.LoadX509KeyPair( "testdata/"+clientCertName+".pem", "testdata/"+clientCertName+".key", ) if err != nil { panic(fmt.Sprintf("failed to load client certificate: %v", err)) } } client := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: func() *x509.CertPool { caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(cert) return caCertPool }(), GetClientCertificate: func(req *tls.CertificateRequestInfo) (*tls.Certificate, error) { return &clientCertficate, nil }, }, }, } return client } func swapFileContents(file1, file2 string) error { content1, err := os.ReadFile(file1) if err != nil { return err } content2, err := os.ReadFile(file2) if err != nil { return err } err = os.WriteFile(file1, content2, 0644) if err != nil { return err } err = os.WriteFile(file2, content1, 0644) if err != nil { return err } return nil } func TestUsers(t *testing.T) { testTables := []*TestInputs{ { Name: `without basic auth`, YAMLConfigPath: "testdata/web_config_users_noTLS.good.yml", ExpectedError: ErrorMap["Unauthorized"], }, { Name: `with correct basic auth`, YAMLConfigPath: "testdata/web_config_users_noTLS.good.yml", Username: "dave", Password: "dave123", ExpectedError: nil, }, { Name: `without basic auth and TLS`, YAMLConfigPath: "testdata/web_config_users.good.yml", UseTLSClient: true, ExpectedError: ErrorMap["Unauthorized"], }, { Name: `with correct basic auth and TLS`, YAMLConfigPath: "testdata/web_config_users.good.yml", UseTLSClient: true, Username: "dave", Password: "dave123", ExpectedError: nil, }, { Name: `with another correct basic auth and TLS`, YAMLConfigPath: "testdata/web_config_users.good.yml", UseTLSClient: true, Username: "carol", Password: "carol123", ExpectedError: nil, }, { Name: `with bad password and TLS`, YAMLConfigPath: "testdata/web_config_users.good.yml", UseTLSClient: true, Username: "dave", Password: "bad", ExpectedError: ErrorMap["Unauthorized"], }, { Name: `with bad username and TLS`, YAMLConfigPath: "testdata/web_config_users.good.yml", UseTLSClient: true, Username: "nonexistent", Password: "nonexistent", ExpectedError: ErrorMap["Unauthorized"], }, } for _, testInputs := range testTables { t.Run(testInputs.Name, testInputs.Test) } } golang-github-prometheus-exporter-toolkit-0.11.0/web/web-config.yml000066400000000000000000000002401453611405200253570ustar00rootroot00000000000000# Minimal TLS configuration example. Additionally, a certificate and a key file # are needed. tls_server_config: cert_file: server.crt key_file: server.key