diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 7dd6855047..ef509ba489 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -21,10 +21,14 @@ jobs: uses: docker/setup-buildx-action@v3 with: install: true - - name: Build container (alpine) - run: 'docker buildx build -f Dockerfile-alpine .' - - name: Build container (debian) - run: 'docker buildx build -f Dockerfile-debian .' + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: '1.25.x' + - name: Build containers + run: go tool mage -v docker:build + env: + GITHUB_REF: ${{ github.ref }} build-linux: name: Build (Linux) @@ -50,18 +54,18 @@ jobs: fetch-tags: true - name: Build binary run: | - CGO_ENABLED=1 GOARCH=amd64 make ghostunnel + go tool mage -compile ./mage-bin + CGO_ENABLED=1 GOARCH=amd64 ./mage-bin -v go:build mv ghostunnel ghostunnel-linux-amd64 - make clean - CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc make ghostunnel + CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc ./mage-bin -v go:build mv ghostunnel ghostunnel-linux-arm64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-linux-amd64-${{ matrix.os }} path: ghostunnel-linux-amd64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-linux-arm64-${{ matrix.os }} path: ghostunnel-linux-arm64 @@ -85,24 +89,24 @@ jobs: fetch-tags: true - name: Build binary run: | - CGO_ENABLED=1 GOARCH=amd64 make ghostunnel + go tool mage -compile ./mage-bin + CGO_ENABLED=1 GOARCH=amd64 ./mage-bin -v go:build mv ghostunnel ghostunnel-darwin-amd64 - make clean - CGO_ENABLED=1 GOARCH=arm64 make ghostunnel + CGO_ENABLED=1 GOARCH=arm64 ./mage-bin -v go:build mv ghostunnel ghostunnel-darwin-arm64 lipo -create -output ghostunnel-darwin-universal ghostunnel-darwin-amd64 ghostunnel-darwin-arm64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-darwin-amd64 path: ghostunnel-darwin-amd64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-darwin-arm64 path: ghostunnel-darwin-arm64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-darwin-universal path: ghostunnel-darwin-universal @@ -125,9 +129,9 @@ jobs: fetch-depth: 100 fetch-tags: true - name: Build binary - run: make ghostunnel + run: go tool mage -v go:build - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-windows-amd64 path: ghostunnel diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7421c3d57e..2e887477a3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,5 +1,7 @@ --- name: Docker +permissions: + contents: read on: push: @@ -8,7 +10,7 @@ on: jobs: buildx: - name: Build container + name: Build and publish containers runs-on: ubuntu-24.04 steps: - name: Checkout @@ -22,20 +24,17 @@ jobs: uses: docker/setup-buildx-action@v3 with: install: true + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: '1.25.x' - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build container (alpine/latest) - if: ${{ github.ref == 'refs/heads/master' }} - run: 'docker buildx build -f Dockerfile-alpine --push -t ghostunnel/ghostunnel:latest -t ghostunnel/ghostunnel:latest-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 .' - - name: Build container (debian/latest) - if: ${{ github.ref == 'refs/heads/master' }} - run: 'docker buildx build -f Dockerfile-debian --push -t ghostunnel/ghostunnel:latest-debian --platform linux/amd64,linux/arm64,linux/arm/v7 .' - - name: Build container (alpine/tagged) - if: ${{ github.ref != 'refs/heads/master' }} - run: 'docker buildx build -f Dockerfile-alpine --push -t ghostunnel/ghostunnel:$(git describe --tags --abbrev=0) -t ghostunnel/ghostunnel:$(git describe --tags --abbrev=0)-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 .' - - name: Build container (debian/tagged) - if: ${{ github.ref != 'refs/heads/master' }} - run: 'docker buildx build -f Dockerfile-debian --push -t ghostunnel/ghostunnel:$(git describe --tags --abbrev=0)-debian --platform linux/amd64,linux/arm64,linux/arm/v7 .' + - name: Build and publish containers + run: go tool mage -v docker:push + env: + GITHUB_REF: ${{ github.ref }} + DOCKER_PLATFORMS: "linux/amd64,linux/arm64,linux/arm/v7" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e1b4fd0b1..daf95864fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,19 +27,21 @@ jobs: with: fetch-depth: 0 - name: Build binary + env: + VERSION: ${{ github.ref_name }} run: | - CGO_ENABLED=1 GOARCH=amd64 make VERSION=$GITHUB_REF_NAME ghostunnel + go tool mage -compile ./mage-bin + CGO_ENABLED=1 GOARCH=amd64 ./mage-bin -v go:build mv ghostunnel ghostunnel-linux-amd64 - make clean - CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc make VERSION=$GITHUB_REF_NAME ghostunnel + CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc ./mage-bin -v go:build mv ghostunnel ghostunnel-linux-arm64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-linux-amd64 path: ghostunnel-linux-amd64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-linux-arm64 path: ghostunnel-linux-arm64 @@ -61,25 +63,27 @@ jobs: with: fetch-depth: 0 - name: Build binary + env: + VERSION: ${{ github.ref_name }} run: | - CGO_ENABLED=1 GOARCH=amd64 make VERSION=$GITHUB_REF_NAME ghostunnel + go tool mage -compile ./mage-bin + CGO_ENABLED=1 GOARCH=amd64 ./mage-bin -v go:build mv ghostunnel ghostunnel-darwin-amd64 - make clean - CGO_ENABLED=1 GOARCH=arm64 make VERSION=$GITHUB_REF_NAME ghostunnel + CGO_ENABLED=1 GOARCH=arm64 ./mage-bin -v go:build mv ghostunnel ghostunnel-darwin-arm64 lipo -create -output ghostunnel-darwin-universal ghostunnel-darwin-amd64 ghostunnel-darwin-arm64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-darwin-amd64 path: ghostunnel-darwin-amd64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-darwin-arm64 path: ghostunnel-darwin-arm64 - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-darwin-universal path: ghostunnel-darwin-universal @@ -101,11 +105,13 @@ jobs: with: fetch-depth: 0 - name: Build binary + env: + VERSION: ${{ github.ref_name }} run: | - make VERSION=$GITHUB_REF_NAME ghostunnel + go tool mage -v go:build mv ghostunnel ghostunnel-windows-amd64.exe - name: Upload artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ghostunnel-windows-amd64.exe path: ghostunnel-windows-amd64.exe @@ -151,7 +157,7 @@ jobs: with: fetch-depth: 0 - name: Download artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: ghostunnel-${{ matrix.target.os }}-${{ matrix.target.arch }} path: dist @@ -179,7 +185,7 @@ jobs: with: fetch-depth: 0 - name: Download artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: ghostunnel-${{ matrix.target.os }}-${{ matrix.target.arch }}.exe path: dist diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 84327348d9..466df8830f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,7 @@ --- name: Test +permissions: + contents: read on: push: @@ -8,8 +10,8 @@ on: branches: [ master ] jobs: - build: - name: Unit tests + test: + name: Tests strategy: matrix: version: [1.25.x] @@ -23,64 +25,47 @@ jobs: id: go - name: Checkout uses: actions/checkout@v6 - - name: Build binary - run: make ghostunnel + - name: Set up Python + if: matrix.os != 'windows-latest' + uses: actions/setup-python@v6 + with: + python-version: '3.11.x' - name: Run tests - run: make unit - - integration-linux: - name: Integration tests (Linux) - strategy: - matrix: - version: [1.25] - os: [ubuntu-22.04, ubuntu-24.04] - runs-on: ${{ matrix.os }} - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Run tests - run: GO_VERSION=${{ matrix.version }} make docker-test + run: go tool mage -v test:all - name: Codecov upload + if: ${{ github.actor != 'dependabot[bot]' && matrix.os != 'windows-latest' }} uses: codecov/codecov-action@v5 - if: ${{ github.actor != 'dependabot[bot]' }} with: files: ./coverage/all.profile - flags: linux + flags: ${{ contains(matrix.os, 'ubuntu') && 'linux' || (contains(matrix.os, 'macos') && 'darwin' || 'windows') }} fail_ci_if_error: true verbose: true token: ${{ secrets.CODECOV_TOKEN }} env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - integration-darwin: - name: Integration tests (Darwin) + + test-docker: + name: Tests (PKCS#11/SoftHSM) strategy: matrix: - version: [1.25.x] - os: [macos-latest] + version: [1.25] + os: [ubuntu-24.04] runs-on: ${{ matrix.os }} steps: + - name: Checkout + uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: ${{ matrix.version }} - id: go - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: '3.11.x' - - name: Install gocovmerge - run: go install github.com/wadey/gocovmerge@latest - - name: Checkout - uses: actions/checkout@v6 - - name: Run tests - run: make test + - name: Run tests in Docker + run: GO_VERSION=${{ matrix.version }} go tool mage -v test:docker - name: Codecov upload uses: codecov/codecov-action@v5 if: ${{ github.actor != 'dependabot[bot]' }} with: files: ./coverage/all.profile - flags: darwin + flags: linux fail_ci_if_error: true verbose: true token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index d03d319944..fb67fdf8eb 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ *.crt *.out ghostunnel +ghostunnel.man +mage-bin ghostunnel.exe ghostunnel.test ghostunnel.certstore @@ -20,3 +22,4 @@ dist/ .idea bazel-* coverage/ +test-keys/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000..82d257a2bb --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,22 @@ +version: "2" + +linters: + # Use default linters (errcheck, govet, ineffassign, staticcheck, unused) + default: standard + # Enable human-readable exclusion presets including std-error-handling + # which excludes common error handling patterns like Close() + exclusions: + presets: + - std-error-handling + - common-false-positives + rules: + # Additional exclusions for os.Remove() in tests - cleanup that's best-effort + - linters: + - errcheck + text: "os.Remove" + path: "_test\\.go" + settings: + errcheck: + # Additional functions to exclude from checking + exclude-functions: + - (*github.com/ghostunnel/ghostunnel/certloader.spiffeTLSConfigSource).Close diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b174cd5de2..9f463986d7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ by forking the repository and sending a pull request. When submitting code, please make efforts to follow existing conventions and style in order to keep the code as readable as possible. Please also make sure -all tests pass by running `make test`, and format your code with `go fmt`. +all tests pass by running `mage test`, and format your code with `go fmt`. Note that ghostunnel relies heavily on integration tests written in Python that run checks on a live instance. If you are adding new features or changing diff --git a/Dockerfile-alpine b/Dockerfile-alpine index 551c93cbdb..d16d98086a 100644 --- a/Dockerfile-alpine +++ b/Dockerfile-alpine @@ -9,14 +9,16 @@ FROM golang:1.25-alpine AS build # Dependencies -RUN apk add --no-cache --update gcc musl-dev libtool make git +RUN apk add --no-cache --update gcc musl-dev libtool git # Copy source COPY . /go/src/github.com/ghostunnel/ghostunnel -# Build +# Install mage and build RUN cd /go/src/github.com/ghostunnel/ghostunnel && \ - GO111MODULE=on make clean ghostunnel && \ + go install github.com/magefile/mage@latest && \ + export PATH=$PATH:$(go env GOPATH)/bin && \ + mage -v go:build && \ cp ghostunnel /usr/bin/ghostunnel # Create a multi-stage build with the binary diff --git a/Dockerfile-debian b/Dockerfile-debian index 5bf728a2ed..0c6546457e 100644 --- a/Dockerfile-debian +++ b/Dockerfile-debian @@ -9,14 +9,16 @@ FROM golang:1.25-bookworm AS build # Dependencies -RUN apt update && apt install -y gcc libtool make git +RUN apt update && apt install -y gcc libtool git # Copy source COPY . /go/src/github.com/ghostunnel/ghostunnel -# Build +# Install mage and build RUN cd /go/src/github.com/ghostunnel/ghostunnel && \ - GO111MODULE=on make clean ghostunnel && \ + go install github.com/magefile/mage@latest && \ + export PATH=$PATH:$(go env GOPATH)/bin && \ + mage -v go:build && \ cp ghostunnel /usr/bin/ghostunnel # Create a multi-stage build with the binary diff --git a/Dockerfile-distroless b/Dockerfile-distroless new file mode 100644 index 0000000000..54c7c73991 --- /dev/null +++ b/Dockerfile-distroless @@ -0,0 +1,29 @@ +# Dockerfile for ghostunnel/ghostunnel built on Debian. +# +# To build this image: +# docker build -t ghostunnel/ghostunnel . +# +# To run ghostunnel from the image (for example): +# docker run --rm ghostunnel/ghostunnel --version + +FROM golang:1.25-bookworm AS build + +# Dependencies +RUN apt update && apt install -y gcc git + +# Copy source +COPY . /go/src/github.com/ghostunnel/ghostunnel + +# Install mage and build +RUN cd /go/src/github.com/ghostunnel/ghostunnel && \ + go install github.com/magefile/mage@latest && \ + export PATH=$PATH:$(go env GOPATH)/bin && \ + GOFLAGS="-tags=nopkcs11" mage -v go:build && \ + cp ghostunnel /usr/bin/ghostunnel + +# Create a multi-stage build with the binary +FROM gcr.io/distroless/base-nossl:nonroot + +COPY --from=build /usr/bin/ghostunnel /usr/bin/ghostunnel + +ENTRYPOINT ["/usr/bin/ghostunnel"] diff --git a/Dockerfile-test b/Dockerfile-test index 2fd890eb70..1a8713e7f1 100644 --- a/Dockerfile-test +++ b/Dockerfile-test @@ -12,15 +12,19 @@ FROM golang:${GO_VERSION} # Install build dependencies RUN apt-get update && \ - apt-get install -y build-essential python3-minimal netcat-traditional softhsm2 rsyslog git $@ - -# Man page for docs -docs/MANPAGE.md: ghostunnel - ./ghostunnel --help-custom-man | pandoc --wrap=preserve --from man --to markdown > $@ - -# Test binary with coverage instrumentation -ghostunnel.test: $(SOURCE_FILES) - go test -c -covermode=count -coverpkg .,./auth,./certloader,./proxy,./wildcard,./socket - -# Clean build output -clean: - rm -rf ghostunnel coverage ghostunnel.test tests/__pycache__ -.PHONY: clean - -# Run all tests (unit + integration tests) -test: unit integration - gocovmerge coverage/*.profile | grep -v "internal/test" > coverage/all.profile - @echo "PASS" -.PHONY: test - -# Run unit tests -unit: - @mkdir -p coverage - go test -v -covermode=count -coverprofile=coverage/unit-test.profile ./... -.PHONY: unit - -integration: $(INTEGRATION_TESTS) -.PHONY: integration - -# Run integration tests -$(INTEGRATION_TESTS): ghostunnel.test - @mkdir -p coverage - @cd tests && ./runner.py $@ -.PHONY: $(INTEGRATION_TESTS) - -# Import test keys into SoftHSM (v2) -softhsm-import: - softhsm2-util --init-token --slot 0 \ - --label ${GHOSTUNNEL_TEST_PKCS11_LABEL} \ - --so-pin ${GHOSTUNNEL_TEST_PKCS11_PIN} \ - --pin ${GHOSTUNNEL_TEST_PKCS11_PIN} - softhsm2-util --id 01 \ - --token ${GHOSTUNNEL_TEST_PKCS11_LABEL} \ - --label ${GHOSTUNNEL_TEST_PKCS11_LABEL} \ - --so-pin ${GHOSTUNNEL_TEST_PKCS11_PIN} \ - --pin ${GHOSTUNNEL_TEST_PKCS11_PIN} \ - --import test-keys/server-pkcs8.pem -.PHONY: softhsm-import - -# Build Docker image -docker-build: - docker build -t ghostunnel/ghostunnel . -.PHONY: docker-build - -# Run unit and integration tests in Docker container -docker-test: - docker build -t ghostunnel/ghostunnel-test -f Dockerfile-test . - docker run -v ${PWD}:/go/src/github.com/ghostunnel/ghostunnel ghostunnel/ghostunnel-test -.PHONY: docker-test diff --git a/README.md b/README.md index 755f468f93..e5250abe08 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,14 @@ Getting Started To get started and play around with the Ghostunnel you will need X.509 client and server certificates. If you don't already maintain a PKI, a good way to get started is to use a package like [cloudflare/cfssl](https://github.com/cloudflare/cfssl). -If you only need some test certificates for playing around with the tunnel you -can find some pre-generated ones in the `test-keys` directory (alongside instructions -on how to generate new ones with OpenSSL). + +For testing and development purposes, you can generate test certificates using: + + # Generate test certificates and keys + mage test:keys + +This will create a `test-keys` directory with all the necessary certificates and keys +for testing. **Note: These are test certificates only and should NOT be used in production.** ### Install @@ -70,20 +75,23 @@ Ghostunnel is available through [GitHub releases][rel] and through [Docker Hub][ Please note that the official release binaries are best effort, and are usually built directly via Github Actions on the latest available images. If you need -compatibility for specific OS versions, we recommend building yourself. +compatibility for specific OS versions we recommend building yourself. -To build Ghostunnel, simply run: +Ghostunnel uses the [mage][mage] build system, a make/rake-like build tool using +Go. You can build Ghostunnel with the [mage][mage] commands shown below. # Compile binary - make ghostunnel + mage go:build - # Generate man page - make ghostunnel.man + # Build containers + mage docker:build -Note that Ghostunnel requires Go 1.22 or later to build, and CGO is required. +You can also run `mage -l` to view all build targets and add `-v` to mage commands +to get more verbose output. [rel]: https://github.com/ghostunnel/ghostunnel/releases [hub]: https://hub.docker.com/r/ghostunnel/ghostunnel +[mage]: https://magefile.org ### Develop @@ -94,11 +102,11 @@ modules][gomod] for managing vendored dependencies. To run tests: # Option 1: run unit & integration tests locally - make test + mage test:all # Option 2: run unit & integration tests in a Docker container # This also runs PKCS#11 integration tests using SoftHSM in the container - make docker-test + mage test:docker # Open coverage information in browser go tool cover -html coverage/all.profile @@ -153,6 +161,10 @@ any of the flags matches. See [ACCESS-FLAGS](docs/ACCESS-FLAGS.md) for more information. In this example, we assume that the CN of the client cert we want to accept connections from is `client`. +**Note:** Before running the examples below, make sure you have generated the test +certificates by running `mage testKeys` (see the [Getting Started](#getting-started) +section above). + Start a backend server: nc -l localhost 8080 diff --git a/certloader/certtlsconfig_test.go b/certloader/certtlsconfig_test.go new file mode 100644 index 0000000000..79c8be08cf --- /dev/null +++ b/certloader/certtlsconfig_test.go @@ -0,0 +1,160 @@ +/*- + * Copyright 2025 Ghostunnel + * + * 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 certloader + +import ( + "crypto/tls" + "crypto/x509" + "io" + "log" + "testing" + + "github.com/stretchr/testify/assert" +) + +// mockCertificateNoPrivateKey simulates a certificate without a private key +type mockCertificateNoPrivateKey struct{} + +func (m *mockCertificateNoPrivateKey) Reload() error { + return nil +} + +func (m *mockCertificateNoPrivateKey) GetIdentifier() string { + return "mock-cert-no-key" +} + +func (m *mockCertificateNoPrivateKey) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) { + // Return a certificate without a private key + return &tls.Certificate{ + Certificate: [][]byte{}, + PrivateKey: nil, // No private key + }, nil +} + +func (m *mockCertificateNoPrivateKey) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + return &tls.Certificate{ + Certificate: [][]byte{}, + PrivateKey: nil, + }, nil +} + +func (m *mockCertificateNoPrivateKey) GetTrustStore() *x509.CertPool { + return nil +} + +// mockCertificateWithPrivateKey simulates a certificate with a private key +type mockCertificateWithPrivateKey struct{} + +func (m *mockCertificateWithPrivateKey) Reload() error { + return nil +} + +func (m *mockCertificateWithPrivateKey) GetIdentifier() string { + return "mock-cert-with-key" +} + +func (m *mockCertificateWithPrivateKey) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) { + // Return a minimal certificate with a non-nil private key placeholder + // In practice, we just need PrivateKey to be non-nil for CanServe to return true + return &tls.Certificate{ + Certificate: [][]byte{}, + PrivateKey: struct{}{}, // Non-nil placeholder + }, nil +} + +func (m *mockCertificateWithPrivateKey) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + return &tls.Certificate{ + Certificate: [][]byte{}, + PrivateKey: struct{}{}, + }, nil +} + +func (m *mockCertificateWithPrivateKey) GetTrustStore() *x509.CertPool { + return x509.NewCertPool() +} + +func TestCertTLSConfigSourceGetServerConfigCannotServe(t *testing.T) { + cert := &mockCertificateNoPrivateKey{} + source := TLSConfigSourceFromCertificate(cert, log.New(io.Discard, "", 0)) + + _, err := source.GetServerConfig(nil) + assert.NotNil(t, err, "should fail when certificate cannot serve (no private key)") + assert.Contains(t, err.Error(), "cannot be used as a server") +} + +func TestCertTLSConfigSourceCanServeWithPrivateKey(t *testing.T) { + cert := &mockCertificateWithPrivateKey{} + source := TLSConfigSourceFromCertificate(cert, log.New(io.Discard, "", 0)) + + assert.True(t, source.CanServe(), "should be able to serve with private key") +} + +func TestCertTLSConfigSourceCanServeWithoutPrivateKey(t *testing.T) { + cert := &mockCertificateNoPrivateKey{} + source := TLSConfigSourceFromCertificate(cert, log.New(io.Discard, "", 0)) + + assert.False(t, source.CanServe(), "should not be able to serve without private key") +} + +func TestCertTLSConfigSourceGetClientConfig(t *testing.T) { + cert := &mockCertificateWithPrivateKey{} + source := TLSConfigSourceFromCertificate(cert, log.New(io.Discard, "", 0)) + + config, err := source.GetClientConfig(nil) + assert.Nil(t, err, "should succeed getting client config") + assert.NotNil(t, config, "client config should not be nil") + + tlsConfig := config.GetClientConfig() + assert.NotNil(t, tlsConfig, "TLS config should not be nil") +} + +func TestCertTLSConfigSourceGetServerConfig(t *testing.T) { + cert := &mockCertificateWithPrivateKey{} + source := TLSConfigSourceFromCertificate(cert, log.New(io.Discard, "", 0)) + + config, err := source.GetServerConfig(nil) + assert.Nil(t, err, "should succeed getting server config with private key") + assert.NotNil(t, config, "server config should not be nil") + + tlsConfig := config.GetServerConfig() + assert.NotNil(t, tlsConfig, "TLS config should not be nil") +} + +func TestNewCertTLSConfigNilBase(t *testing.T) { + cert := &mockCertificateWithPrivateKey{} + config := newCertTLSConfig(cert, nil) + + assert.NotNil(t, config.base, "base should be initialized to non-nil config when nil passed") +} + +func TestNewCertTLSConfigWithBase(t *testing.T) { + cert := &mockCertificateWithPrivateKey{} + base := &tls.Config{ + MinVersion: tls.VersionTLS13, + } + config := newCertTLSConfig(cert, base) + + assert.Equal(t, base, config.base, "base should be set to provided config") +} + +func TestCertTLSConfigSourceReload(t *testing.T) { + cert := &mockCertificateWithPrivateKey{} + source := TLSConfigSourceFromCertificate(cert, log.New(io.Discard, "", 0)) + + err := source.Reload() + assert.Nil(t, err, "reload should succeed") +} diff --git a/certloader/spiffe_tls_config_test.go b/certloader/spiffe_tls_config_test.go index c59620e172..9379fd7ac6 100644 --- a/certloader/spiffe_tls_config_test.go +++ b/certloader/spiffe_tls_config_test.go @@ -139,3 +139,48 @@ func countVerifyPeerCertificate(callCount *int32) func(rawCerts [][]byte, verifi return nil } } + +func TestSpiffeTLSConfigSourceReload(t *testing.T) { + td := spiffeid.RequireTrustDomainFromString("example.org") + ca := spiffetest.NewCA(t, td) + + svid := ca.CreateX509SVID(spiffeid.RequireFromPath(td, "/foo")) + + workloadAPI := spiffetest.New(t) + workloadAPI.SetX509SVIDResponse( + &spiffetest.X509SVIDResponse{ + Bundle: ca.X509Bundle(), + SVIDs: []*x509svid.SVID{svid}, + }) + defer workloadAPI.Stop() + + source, err := TLSConfigSourceFromWorkloadAPI(workloadAPI.Addr(), false, log.Default()) + require.NoError(t, err) + defer func() { _ = source.(*spiffeTLSConfigSource).Close() }() + + // Reload should be a no-op for SPIFFE (workload API maintains itself) + err = source.Reload() + require.NoError(t, err, "Reload should not return an error") +} + +func TestSpiffeTLSConfigSourceCanServe(t *testing.T) { + td := spiffeid.RequireTrustDomainFromString("example.org") + ca := spiffetest.NewCA(t, td) + + svid := ca.CreateX509SVID(spiffeid.RequireFromPath(td, "/foo")) + + workloadAPI := spiffetest.New(t) + workloadAPI.SetX509SVIDResponse( + &spiffetest.X509SVIDResponse{ + Bundle: ca.X509Bundle(), + SVIDs: []*x509svid.SVID{svid}, + }) + defer workloadAPI.Stop() + + source, err := TLSConfigSourceFromWorkloadAPI(workloadAPI.Addr(), false, log.Default()) + require.NoError(t, err) + defer func() { _ = source.(*spiffeTLSConfigSource).Close() }() + + // CanServe should always return true for SPIFFE source + require.True(t, source.CanServe(), "CanServe should return true") +} diff --git a/certstore/certstore_darwin.go b/certstore/certstore_darwin.go index c57d232292..0149d77fcb 100644 --- a/certstore/certstore_darwin.go +++ b/certstore/certstore_darwin.go @@ -445,8 +445,8 @@ func mapToCFDictionary(gomap map[C.CFTypeRef]C.CFTypeRef) C.CFDictionaryRef { ) for k, v := range gomap { - keys = append(keys, unsafe.Pointer(k)) - values = append(values, unsafe.Pointer(v)) + keys = append(keys, unsafe.Pointer(k)) //nolint:govet // CGo pointer conversion is safe here + values = append(values, unsafe.Pointer(v)) //nolint:govet // CGo pointer conversion is safe here } return C.CFDictionaryCreate(nilCFAllocatorRef, &keys[0], &values[0], C.CFIndex(n), nil, nil) diff --git a/certstore/certstore_test.go b/certstore/certstore_test.go index 38de7a88b7..c6a97705c7 100644 --- a/certstore/certstore_test.go +++ b/certstore/certstore_test.go @@ -12,10 +12,7 @@ import ( "crypto/sha256" "crypto/sha512" "crypto/x509" - "runtime" "testing" - - "github.com/github/smimesign/fakeca" ) func TestImportDeleteRSA(t *testing.T) { @@ -27,12 +24,7 @@ func TestImportDeleteECDSA(t *testing.T) { } // ImportDeleteHelper is an abstraction for testing identity Import()/Delete(). -func ImportDeleteHelper(t *testing.T, i *fakeca.Identity) { - if runtime.GOOS == "windows" { - t.Skip("FIXME: Windows runners in Github fail this test due to issues with their OpenSSL installation") - return - } - +func ImportDeleteHelper(t *testing.T, i *identity) { withStore(t, func(store Store) { // Import an identity if err := store.Import(i.PFX("asdf"), "asdf"); err != nil { @@ -99,11 +91,6 @@ func ImportDeleteHelper(t *testing.T, i *fakeca.Identity) { } func TestSignerRSA(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("FIXME: Windows runners in Github fail this test due to issues with their OpenSSL installation") - return - } - rsaPriv, ok := leafRSA.PrivateKey.(*rsa.PrivateKey) if !ok { t.Fatal("expected priv to be an RSA private key") @@ -234,11 +221,6 @@ func TestSignerRSA(t *testing.T) { } func TestSignerECDSA(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("FIXME: Windows runners in Github fail this test due to issues with their OpenSSL installation") - return - } - ecPriv, ok := leafEC.PrivateKey.(*ecdsa.PrivateKey) if !ok { t.Fatal("expected priv to be an ECDSA private key") @@ -320,12 +302,7 @@ func TestCertificateEC(t *testing.T) { CertificateHelper(t, leafEC) } -func CertificateHelper(t *testing.T, leaf *fakeca.Identity) { - if runtime.GOOS == "windows" { - t.Skip("FIXME: Windows runners in Github fail this test due to issues with their OpenSSL installation") - return - } - +func CertificateHelper(t *testing.T, leaf *identity) { withIdentity(t, root, func(caIdent Identity) { withIdentity(t, intermediate, func(interIdent Identity) { withIdentity(t, leaf, func(leafIdent Identity) { diff --git a/certstore/main_test.go b/certstore/main_test.go index 0838d8ab75..5eba7c0769 100644 --- a/certstore/main_test.go +++ b/certstore/main_test.go @@ -13,29 +13,27 @@ import ( "log" "os" "testing" - - "github.com/github/smimesign/fakeca" ) var ( - root = fakeca.New(fakeca.IsCA, fakeca.Subject(pkix.Name{ + root = newIdentity(withIsCA, withSubject(pkix.Name{ Organization: []string{"certstore"}, CommonName: "root", })) - intermediate = root.Issue(fakeca.IsCA, fakeca.Subject(pkix.Name{ + intermediate = root.Issue(withIsCA, withSubject(pkix.Name{ Organization: []string{"certstore"}, CommonName: "intermediate", })) leafKeyRSA, _ = rsa.GenerateKey(rand.Reader, 2048) - leafRSA = intermediate.Issue(fakeca.PrivateKey(leafKeyRSA), fakeca.Subject(pkix.Name{ + leafRSA = intermediate.Issue(withPrivateKey(leafKeyRSA), withSubject(pkix.Name{ Organization: []string{"certstore"}, CommonName: "leaf-rsa", })) leafKeyEC, _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - leafEC = intermediate.Issue(fakeca.PrivateKey(leafKeyEC), fakeca.Subject(pkix.Name{ + leafEC = intermediate.Issue(withPrivateKey(leafKeyEC), withSubject(pkix.Name{ Organization: []string{"certstore"}, CommonName: "leaf-ec", })) @@ -57,7 +55,7 @@ func withStore(t *testing.T, cb func(Store)) { cb(store) } -func withIdentity(t *testing.T, i *fakeca.Identity, cb func(Identity)) { +func withIdentity(t *testing.T, i *identity, cb func(Identity)) { withStore(t, func(store Store) { // Import an identity if err := store.Import(i.PFX("asdf"), "asdf"); err != nil { diff --git a/certstore/testca_test.go b/certstore/testca_test.go new file mode 100644 index 0000000000..e0fc3c9ebe --- /dev/null +++ b/certstore/testca_test.go @@ -0,0 +1,155 @@ +//go:build !linux + +package certstore + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "math/big" + "time" + + "software.sslmate.com/src/go-pkcs12" +) + +// identity is a certificate and private key for testing. +type identity struct { + Certificate *x509.Certificate + PrivateKey crypto.Signer + Issuer *identity + nextSN int64 +} + +// identityOption configures identity creation. +type identityOption func(*identityConfig) + +type identityConfig struct { + subject *pkix.Name + issuer *identity + priv crypto.Signer + isCA bool +} + +// withIsCA marks the identity as a certificate authority. +var withIsCA identityOption = func(c *identityConfig) { + c.isCA = true +} + +// withSubject sets the certificate subject. +func withSubject(name pkix.Name) identityOption { + return func(c *identityConfig) { + c.subject = &name + } +} + +// withPrivateKey sets a custom private key. +func withPrivateKey(key crypto.Signer) identityOption { + return func(c *identityConfig) { + c.priv = key + } +} + +// withIssuer sets the issuing identity. +func withIssuer(issuer *identity) identityOption { + return func(c *identityConfig) { + c.issuer = issuer + } +} + +// newIdentity creates a new identity (root CA or issued certificate). +func newIdentity(opts ...identityOption) *identity { + cfg := &identityConfig{} + for _, opt := range opts { + opt(cfg) + } + + // Generate private key if not provided + priv := cfg.priv + if priv == nil { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err) + } + priv = key + } + + // Build certificate template + subject := pkix.Name{CommonName: "test"} + if cfg.subject != nil { + subject = *cfg.subject + } + + sn, err := rand.Int(rand.Reader, big.NewInt(1<<62)) + if err != nil { + panic(err) + } + + tmpl := &x509.Certificate{ + SerialNumber: sn, + Subject: subject, + NotBefore: time.Unix(0, 0), + NotAfter: time.Now().Add(10 * 365 * 24 * time.Hour), + IsCA: cfg.isCA, + BasicConstraintsValid: true, + } + + if cfg.isCA { + tmpl.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign + } + + // Determine parent cert and signing key + var parent *x509.Certificate + var signingKey crypto.Signer + var issuer *identity + + if cfg.issuer != nil { + parent = cfg.issuer.Certificate + signingKey = cfg.issuer.PrivateKey + issuer = cfg.issuer + tmpl.SerialNumber = big.NewInt(cfg.issuer.incrementSN()) + } else { + parent = tmpl + signingKey = priv + } + + // Create certificate + der, err := x509.CreateCertificate(rand.Reader, tmpl, parent, priv.Public(), signingKey) + if err != nil { + panic(err) + } + + cert, err := x509.ParseCertificate(der) + if err != nil { + panic(err) + } + + return &identity{ + Certificate: cert, + PrivateKey: priv, + Issuer: issuer, + nextSN: 1, + } +} + +// Issue creates a new identity signed by this one. +func (id *identity) Issue(opts ...identityOption) *identity { + opts = append(opts, withIssuer(id)) + return newIdentity(opts...) +} + +// PFX returns the identity as PKCS#12 data encrypted with password. +func (id *identity) PFX(password string) []byte { + pfxData, err := pkcs12.Legacy.Encode(id.PrivateKey, id.Certificate, nil, password) + if err != nil { + panic(err) + } + return pfxData +} + +func (id *identity) incrementSN() int64 { + sn := id.nextSN + id.nextSN++ + return sn +} diff --git a/go.mod b/go.mod index 875323a08c..646ba13a6a 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,12 @@ require ( github.com/coreos/go-systemd/v22 v22.6.0 github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432 github.com/deathowl/go-metrics-prometheus v0.0.0-20221009205350-f2a1482ba35b - github.com/github/smimesign v0.2.0 github.com/go-jose/go-jose/v4 v4.1.3 github.com/hashicorp/go-syslog v1.0.0 github.com/kavu/go_reuseport v1.5.0 github.com/landlock-lsm/go-landlock v0.0.0-20251103212306-430f8e5cd97c github.com/letsencrypt/pkcs11key/v4 v4.0.0 + github.com/magefile/mage v1.15.0 github.com/mholt/acmez v1.2.0 github.com/open-policy-agent/opa v1.11.0 github.com/pires/go-proxyproto v0.8.1 @@ -26,8 +26,10 @@ require ( github.com/wrouesnel/go.connect-proxy-scheme v0.0.0-20240822095422-f6d0c8f327b9 golang.org/x/net v0.47.0 golang.org/x/sync v0.18.0 + golang.org/x/sys v0.38.0 google.golang.org/grpc v1.77.0 google.golang.org/protobuf v1.36.10 + software.sslmate.com/src/go-pkcs12 v0.6.0 ) require ( @@ -64,7 +66,6 @@ require ( github.com/lestrrat-go/option v1.0.1 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/libdns/libdns v1.1.1 // indirect - github.com/magefile/mage v1.15.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mholt/acmez/v3 v3.1.4 // indirect @@ -89,6 +90,7 @@ require ( github.com/ulikunitz/xz v0.5.15 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/vektah/gqlparser/v2 v2.5.31 // indirect + github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad // indirect github.com/weppos/publicsuffix-go v0.50.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -110,14 +112,17 @@ require ( golang.org/x/crypto v0.45.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/mod v0.30.0 // indirect - golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/tools v0.39.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 // indirect sigs.k8s.io/yaml v1.6.0 // indirect - software.sslmate.com/src/go-pkcs12 v0.6.0 // indirect ) go 1.24.6 + +tool ( + github.com/magefile/mage + github.com/wadey/gocovmerge +) diff --git a/go.sum b/go.sum index 2bc0988fe1..f2989fad59 100644 --- a/go.sum +++ b/go.sum @@ -32,7 +32,6 @@ github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+Y github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= @@ -70,8 +69,6 @@ github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7Dlme github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/github/smimesign v0.2.0 h1:Hho4YcX5N1I9XNqhq0fNx0Sts8MhLonHd+HRXVGNjvk= -github.com/github/smimesign v0.2.0/go.mod h1:iZiiwNT4HbtGRVqCQu7uJPEZCuEE5sfSSttcnePkDl4= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= @@ -196,7 +193,6 @@ github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-policy-agent/opa v1.11.0 h1:eOd/jJrbavakiX477yT4LrXZfUWViAot/AsKsjsfe7o= github.com/open-policy-agent/opa v1.11.0/go.mod h1:QimuJO4T3KYxWzrmAymqlFvsIanCjKrGjmmC8GgAdgE= -github.com/pborman/getopt v0.0.0-20180811024354-2b5b3bfb099b/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= @@ -204,7 +200,6 @@ github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -281,6 +276,8 @@ github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXV github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k= github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= +github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad h1:W0LEBv82YCGEtcmPA3uNZBI33/qF//HAAs3MawDjRa0= +github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad/go.mod h1:Hy8o65+MXnS6EwGElrSRjUzQDLXreJlzYLlWiHtt8hM= github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/weppos/publicsuffix-go v0.40.3-0.20250127173806-e489a31678ca/go.mod h1:43Dfyxu2dpmLg56at26Q4k9gwf3yWSUiwk8kGnwzULk= github.com/weppos/publicsuffix-go v0.50.1 h1:elrBHeSkS/eIb169+DnLrknqmdP4AjT0Q0tEdytz1Og= @@ -312,8 +309,6 @@ github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= github.com/zmap/zcrypto v0.0.0-20250129210703-03c45d0bae98 h1:Qp98bmMm9JHPPOaLi2Nb6oWoZ+1OyOMWI7PPeJrirI0= github.com/zmap/zcrypto v0.0.0-20250129210703-03c45d0bae98/go.mod h1:YTUyN/U1oJ7RzCEY5hUweYxbVUu7X+11wB7OXZT15oE= -github.com/zmap/zcrypto v0.0.0-20251114214934-bb32b590b717 h1:4x89hS1LZY4YctWc+XOj4BEz08bUNRTraXtHQRThsmI= -github.com/zmap/zcrypto v0.0.0-20251114214934-bb32b590b717/go.mod h1:NBtLpB/eitQk7/yXV7mHbZ/Gtmigw0Un9H9DEnrl+Zg= github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= github.com/zmap/zlint/v3 v3.6.8 h1:ZvdfwSgqPxeD2Sb0yhH/7jzrpFHBKAjPhzKr1vVev9c= github.com/zmap/zlint/v3 v3.6.8/go.mod h1:Tm0qwwaO629pgJ/En7M9U9Edx4+rQRuoeXVpXvgVHhA= @@ -353,7 +348,6 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -457,7 +451,6 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= diff --git a/magefile.go b/magefile.go new file mode 100644 index 0000000000..761e24d2c7 --- /dev/null +++ b/magefile.go @@ -0,0 +1,461 @@ +//go:build mage + +package main + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +type Go mg.Namespace +type Git mg.Namespace +type Test mg.Namespace +type Docker mg.Namespace + +var Default = Go.Build + +// printf prints the given format and args if verbose mode is enabled. +func printf(format string, args ...interface{}) { + if mg.Verbose() { + fmt.Printf(format, args...) + } +} + +// runCommands executes a list of commands, stopping on the first error. +// Checks the context for cancellation before running each command. +func runCommands(ctx context.Context, cmds [][]string) error { + for _, cmd := range cmds { + if err := ctx.Err(); err != nil { + return fmt.Errorf("context cancelled: %w", err) + } + if err := sh.Run(cmd[0], cmd[1:]...); err != nil { + return err + } + } + return nil +} + +// Builds builds the Ghostunnel binary. +func (Go) Build(ctx context.Context) error { + version := os.Getenv("VERSION") + if version == "" { + version = getVersion() + } + + return sh.Run("go", "build", "-ldflags", fmt.Sprintf("-X main.version=%s", version), "-o", "ghostunnel", ".") +} + +// Man generates the Ghostunnel man page from the built binary. +// Also generates docs/MANPAGE.md from the man page using pandoc. +func (Go) Man(ctx context.Context) error { + mg.CtxDeps(ctx, Go.Build) + + output, err := sh.Output("./ghostunnel", "--help-custom-man") + if err != nil { + return fmt.Errorf("failed to generate man page: %w", err) + } + + if err := os.WriteFile("ghostunnel.man", []byte(output), 0644); err != nil { + return fmt.Errorf("failed to write ghostunnel.man: %w", err) + } + + // Generate docs/MANPAGE.md from the man page using pandoc + if err := sh.Run("pandoc", "-f", "man", "-t", "markdown", "ghostunnel.man", "-o", "docs/MANPAGE.md"); err != nil { + return fmt.Errorf("failed to generate docs/MANPAGE.md: %w", err) + } + + return nil +} + +// Clean removes build artifacts. +func (Git) Clean(ctx context.Context) error { + return sh.Run("git", "clean", "-Xdf") +} + +// build builds the *test* binary with coverage instrumentation. +func (Test) build() error { + return sh.Run("go", "test", "-c", "-covermode=count", "-coverpkg", ".,./auth,./certloader,./proxy,./wildcard,./socket") +} + +// All runs both unit and integration tests, then merges coverage. +func (Test) All(ctx context.Context) error { + mg.CtxDeps(ctx, Test.Unit, Test.Integration) + mg.CtxDeps(ctx, Test.Coverage) + return nil +} + +// Unit runs the unit tests. +func (Test) Unit(ctx context.Context) error { + printf("Running unit tests...\n") + + if err := os.MkdirAll("coverage", 0755); err != nil { + return fmt.Errorf("failed to create coverage directory: %w", err) + } + + return sh.Run("go", "test", "-v", "-covermode=count", "-coverprofile=coverage/unit-test.profile", "./...") +} + +// Integration runs the integration tests. +func (Test) Integration(ctx context.Context) error { + mg.CtxDeps(ctx, Test.build) + + if err := os.MkdirAll("coverage", 0755); err != nil { + return fmt.Errorf("failed to create coverage directory: %w", err) + } + + // Skip integration tests on Windows + if runtime.GOOS == "windows" { + fmt.Fprintf(os.Stderr, "Integration tests are not supported on Windows\n") + return nil + } + + // Run integration tests + testFiles, err := filepath.Glob("tests/test-*.py") + if err != nil || len(testFiles) == 0 { + return fmt.Errorf("failed to find test files: %w", err) + } + + // Run each integration test directly + printf("Running integration tests...\n") + for _, testFile := range testFiles { + if err := ctx.Err(); err != nil { + return fmt.Errorf("context cancelled: %w", err) + } + + testName := strings.TrimSuffix(filepath.Base(testFile), ".py") + printf("=== RUN %s\n", testName) + + // Run the Python test file directly from tests directory + start := time.Now() + testFileName := filepath.Base(testFile) + cmd := exec.CommandContext(ctx, "python3", testFileName) + cmd.Dir = "tests" + + // Capture stdout and stderr + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + duration := time.Since(start) + elapsed := duration.Seconds() + + if err == nil { + printf("=== PASS: %s (%.2fs)\n", testName, elapsed) + continue + } + + // Test failed - output captured stdout/stderr and failure message + os.Stdout.Write(stdout.Bytes()) + os.Stderr.Write(stderr.Bytes()) + printf("=== FAIL: %s (%.2fs)\n", testName, elapsed) + + // Get exit code if available + if exitError, ok := err.(*exec.ExitError); ok { + return fmt.Errorf("integration test %s failed with exit code %d", testName, exitError.ExitCode()) + } + + return fmt.Errorf("integration test %s failed: %w", testName, err) + } + + return nil +} + +// Coverage merges the coverage files into a single file. +func (Test) Coverage(ctx context.Context) error { + mg.CtxDeps(ctx, Test.Unit, Test.Integration) + + // Get all coverage profile files + coverageFiles, err := filepath.Glob("coverage/*.profile") + if err != nil || len(coverageFiles) == 0 { + return fmt.Errorf("failed to find coverage files: %w", err) + } + + // Merge coverage files, excluding internal/test + args := []string{"tool", "gocovmerge"} + args = append(args, coverageFiles...) + + mergeOutput, err := sh.Output("go", args...) + if err != nil { + return fmt.Errorf("failed to merge coverage: %w", err) + } + + // Filter out internal/test lines (same as Makefile's grep -v) + lines := strings.Split(string(mergeOutput), "\n") + var filtered []string + for _, line := range lines { + if !strings.Contains(line, "internal/test") { + filtered = append(filtered, line) + } + } + + // Write merged coverage + return os.WriteFile("coverage/all.profile", []byte(strings.Join(filtered, "\n")), 0644) +} + +// Keys generates test certificates and keys for development/testing purposes. +// These should NOT be used in production. The keys are generated in the test-keys directory. +func (Test) Keys(ctx context.Context) error { + // Create test-keys directory + if err := os.MkdirAll("test-keys", 0755); err != nil { + return err + } + + // Write openssl.ext configuration file + opensslExt := `[root] +keyUsage=critical, keyCertSign +basicConstraints=critical, CA:TRUE, pathlen:0 +[server] +extendedKeyUsage = serverAuth +subjectAltName = IP:127.0.0.1,IP:::1,DNS:localhost,URI:spiffe://ghostunnel/server +[client] +extendedKeyUsage = clientAuth +subjectAltName = IP:127.0.0.1,IP:::1,DNS:localhost,URI:spiffe://ghostunnel/client +` + if err := os.WriteFile("test-keys/openssl.ext", []byte(opensslExt), 0644); err != nil { + return err + } + + // Root CA generation commands + rootCommands := [][]string{ + {"openssl", "genrsa", "-out", "test-keys/root-key.pem", "2048"}, + {"openssl", "req", "-new", "-key", "test-keys/root-key.pem", "-out", "test-keys/root-csr.pem", "-subj", "/CN=root"}, + {"openssl", "x509", "-req", "-sha256", "-in", "test-keys/root-csr.pem", "-signkey", "test-keys/root-key.pem", "-out", "test-keys/root-cert.pem", "-days", "5000", "-extfile", "test-keys/openssl.ext", "-extensions", "root"}, + } + if err := runCommands(ctx, rootCommands); err != nil { + return err + } + + // Create root combined file and cacert.pem + rootCert, rootCertErr := os.ReadFile("test-keys/root-cert.pem") + rootKey, rootKeyErr := os.ReadFile("test-keys/root-key.pem") + if rootCertErr != nil || rootKeyErr != nil { + return fmt.Errorf("failed to read root certificate or key: %v / %v", rootCertErr, rootKeyErr) + } + rootCombined := append(rootCert, rootKey...) + if err := os.WriteFile("test-keys/root-combined.pem", rootCombined, 0644); err != nil { + return err + } + if err := os.WriteFile("test-keys/cacert.pem", rootCert, 0644); err != nil { + return err + } + + // Generate server and client keys + for _, name := range []string{"server", "client"} { + if err := generateEntityKeys(ctx, name); err != nil { + return err + } + } + + printf("Test keys generated successfully in test-keys/ directory\n") + return nil +} + +// generateEntityKeys generates all keys and certificates for a given entity (server or client). +func generateEntityKeys(ctx context.Context, name string) error { + commands := [][]string{ + {"openssl", "genrsa", "-out", fmt.Sprintf("test-keys/%s-key.pem", name), "2048"}, + {"openssl", "req", "-new", "-key", fmt.Sprintf("test-keys/%s-key.pem", name), "-out", fmt.Sprintf("test-keys/%s-csr.pem", name), "-subj", fmt.Sprintf("/CN=%s", name)}, + {"openssl", "x509", "-req", "-sha256", "-in", fmt.Sprintf("test-keys/%s-csr.pem", name), "-CA", "test-keys/root-combined.pem", "-CAkey", "test-keys/root-combined.pem", "-CAcreateserial", "-out", fmt.Sprintf("test-keys/%s-cert.pem", name), "-days", "5000", "-extfile", "test-keys/openssl.ext", "-extensions", name}, + } + if err := runCommands(ctx, commands); err != nil { + return err + } + + // Create combined file + cert, certErr := os.ReadFile(fmt.Sprintf("test-keys/%s-cert.pem", name)) + key, keyErr := os.ReadFile(fmt.Sprintf("test-keys/%s-key.pem", name)) + if certErr != nil || keyErr != nil { + return fmt.Errorf("failed to read certificate or key: %v / %v", certErr, keyErr) + } + combined := append(cert, key...) + if err := os.WriteFile(fmt.Sprintf("test-keys/%s-combined.pem", name), combined, 0644); err != nil { + return err + } + + // Generate PKCS#12 keystore and PKCS#8 key + keystoreCommands := [][]string{ + {"openssl", "pkcs12", "-export", "-out", fmt.Sprintf("test-keys/%s-keystore.p12", name), "-in", fmt.Sprintf("test-keys/%s-combined.pem", name), "-inkey", fmt.Sprintf("test-keys/%s-combined.pem", name), "-passout", "pass:"}, + {"openssl", "pkcs8", "-topk8", "-inform", "PEM", "-outform", "PEM", "-in", fmt.Sprintf("test-keys/%s-key.pem", name), "-out", fmt.Sprintf("test-keys/%s-pkcs8.pem", name), "-nocrypt"}, + } + if err := runCommands(ctx, keystoreCommands); err != nil { + return err + } + + return nil +} + +// SoftHSMImport initializes a SoftHSM token and imports the test server key. +// Automatically generates test keys if they don't exist (via Test.Keys dependency). +// Environment variables can be used to configure SoftHSM: +// - GHOSTUNNEL_TEST_PKCS11_LABEL: Token label (default: "ghostunnel-pkcs11-test") +// - GHOSTUNNEL_TEST_PKCS11_PIN: Token PIN (default: "1234") +// - SOFTHSM2_CONF: SoftHSM config file path (default: "/etc/softhsm/softhsm2.conf") +func (Test) SoftHSMImport(ctx context.Context) error { + mg.CtxDeps(ctx, Test.Keys) + + // Get configuration from environment variables + label := os.Getenv("GHOSTUNNEL_TEST_PKCS11_LABEL") + pin := os.Getenv("GHOSTUNNEL_TEST_PKCS11_PIN") + if label == "" || pin == "" { + return fmt.Errorf("GHOSTUNNEL_TEST_PKCS11_LABEL and GHOSTUNNEL_TEST_PKCS11_PIN must be set") + } + + printf("Initializing SoftHSM token with label: %s\n", label) + + // Initialize and import into SoftHSM token + softhsmCommands := [][]string{ + {"softhsm2-util", "--init-token", "--slot", "0", "--label", label, "--so-pin", pin, "--pin", pin}, + {"softhsm2-util", "--id", "01", "--token", label, "--label", label, "--so-pin", pin, "--pin", pin, "--import", "test-keys/server-pkcs8.pem"}, + } + if err := runCommands(ctx, softhsmCommands); err != nil { + return fmt.Errorf("failed to configure SoftHSM: %w", err) + } + + printf("SoftHSM token initialized and key imported successfully\n") + return nil +} + +// Docker builds and runs tests in a Docker container. +// Output is streamed in real-time as the container runs. +func (Test) Docker(ctx context.Context) error { + args := []string{"build", "-t", "ghostunnel/ghostunnel-test", "-f", "Dockerfile-test"} + if !mg.Verbose() { + args = append(args, "--quiet") + } + args = append(args, ".") + if err := sh.Run("docker", args...); err != nil { + return fmt.Errorf("failed to build test image: %w", err) + } + + pwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("failed to get current directory: %w", err) + } + + args = []string{"run", "-v", fmt.Sprintf("%s:/go/src/github.com/ghostunnel/ghostunnel", pwd), "ghostunnel/ghostunnel-test", "--"} + if mg.Verbose() { + args = append(args, "-v") + } + args = append(args, "test:softhsmimport", "test:all") + return sh.Run("docker", args...) +} + +// Build builds and tags all Docker containers. +// Uses docker buildx for multi-platform builds. Does not push images. +func (Docker) Build(ctx context.Context) error { + return buildDocker(ctx, false) +} + +// Push builds and publishes all Docker containers to Docker Hub. +// Uses docker buildx for multi-platform builds and pushes images. +func (Docker) Push(ctx context.Context) error { + return buildDocker(ctx, true) +} + +// buildDocker builds and tags all Docker containers, optionally pushing them to Docker Hub. +func buildDocker(ctx context.Context, push bool) error { + // Determine base tag (latest for master, version tag otherwise) + baseTag, err := getDockerTag() + if err != nil { + return err + } + + builds := map[string][]string{ + "Dockerfile-alpine": []string{ + fmt.Sprintf("ghostunnel/ghostunnel:%s", baseTag), + fmt.Sprintf("ghostunnel/ghostunnel:%s-alpine", baseTag), + }, + "Dockerfile-debian": []string{fmt.Sprintf("ghostunnel/ghostunnel:%s-debian", baseTag)}, + "Dockerfile-distroless": []string{fmt.Sprintf("ghostunnel/ghostunnel:%s-distroless", baseTag)}, + } + + for dockerfile, tags := range builds { + if err := ctx.Err(); err != nil { + return fmt.Errorf("context cancelled: %w", err) + } + if err := buildDockerImage(dockerfile, tags, push); err != nil { + return fmt.Errorf("failed to build image: %w", err) + } + } + + return nil +} + +// buildDockerImage builds a Docker image using buildx with the given Dockerfile, tags, and platforms. +// If push is true, it will push the image to the registry. +func buildDockerImage(dockerfile string, tags []string, push bool) error { + args := []string{"buildx", "build", "-f", dockerfile} + + platforms := os.Getenv("DOCKER_PLATFORMS") + if platforms != "" { + args = append(args, "--platform", platforms) + } + for _, tag := range tags { + args = append(args, "-t", tag) + } + if !mg.Verbose() { + args = append(args, "--quiet") + } + if push { + args = append(args, "--push") + } + + // Add build context & run + args = append(args, ".") + return sh.Run("docker", args...) +} + +// getVersion gets the version from git describe. +func getVersion() string { + output, err := sh.Output("git", "describe", "--always", "--dirty") + if err != nil { + return "unknown" + } + return strings.TrimSpace(output) +} + +// getDockerTag determines the Docker tag to use based on git state. +// Returns "latest" if on master branch, otherwise returns the most recent tag. +func getDockerTag() (string, error) { + // Check if we're on a tag (for GitHub Actions when triggered by tag push) + // In GitHub Actions, GITHUB_REF will be set, but locally we check git + githubRef := os.Getenv("GITHUB_REF") + if githubRef != "" { + // GitHub Actions: refs/heads/master or refs/tags/v1.2.3 + if strings.HasPrefix(githubRef, "refs/heads/master") { + return "latest", nil + } + if strings.HasPrefix(githubRef, "refs/tags/") { + tag := strings.TrimPrefix(githubRef, "refs/tags/") + return tag, nil + } + } + + // Check current branch + branch, err := sh.Output("git", "rev-parse", "--abbrev-ref", "HEAD") + if err != nil { + return "", fmt.Errorf("failed to determine git ref: %w", err) + } + if strings.TrimSpace(branch) == "master" { + return "latest", nil + } + + // Not on master, get the most recent tag + tag, err := sh.Output("git", "describe", "--tags", "--abbrev=0") + if err != nil { + return "", fmt.Errorf("failed to get git tag: %w", err) + } + + return strings.TrimSpace(tag), nil +} diff --git a/main.go b/main.go index f07ac6eac6..99821ecefb 100644 --- a/main.go +++ b/main.go @@ -416,7 +416,7 @@ func main() { func run(args []string) error { runtime.GOMAXPROCS(runtime.NumCPU()) - app.Version(fmt.Sprintf("rev %s built with %s", version, runtime.Version())) + app.Version(fmt.Sprintf("rev %s built with %s (pkcs11: %v, keychain: %v)", version, runtime.Version(), certloader.SupportsPKCS11(), certloader.SupportsKeychain())) app.Validate(validateFlags) app.UsageTemplate(kingpin.LongHelpTemplate) command := kingpin.MustParse(app.Parse(args)) diff --git a/main_test.go b/main_test.go index ae297e59a5..f86b0422b4 100644 --- a/main_test.go +++ b/main_test.go @@ -17,6 +17,7 @@ package main import ( + "crypto/tls" "encoding/json" "errors" "net" @@ -28,6 +29,7 @@ import ( "testing" "time" + "github.com/ghostunnel/ghostunnel/certloader" "github.com/ghostunnel/ghostunnel/proxy" "github.com/stretchr/testify/assert" ) @@ -391,3 +393,44 @@ func TestProxyLoggingFlags(t *testing.T) { assert.Equal(t, proxyLoggerFlags([]string{"conn-errs", "handshake-errs"}), proxy.LogConnections) assert.Equal(t, proxyLoggerFlags([]string{"conns", "conn-errs"}), proxy.LogHandshakeErrors) } + +// failingTLSConfigSource is a mock TLSConfigSource that always returns errors +type failingTLSConfigSource struct{} + +func (f *failingTLSConfigSource) Reload() error { + return nil +} + +func (f *failingTLSConfigSource) CanServe() bool { + return false +} + +func (f *failingTLSConfigSource) GetClientConfig(base *tls.Config) (certloader.TLSClientConfig, error) { + return nil, errors.New("test error: GetClientConfig failed") +} + +func (f *failingTLSConfigSource) GetServerConfig(base *tls.Config) (certloader.TLSServerConfig, error) { + return nil, errors.New("test error: GetServerConfig failed") +} + +func TestMustGetServerConfigPanicsOnError(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("expected panic when GetServerConfig fails") + } + }() + + source := &failingTLSConfigSource{} + mustGetServerConfig(source, nil) +} + +func TestMustGetClientConfigPanicsOnError(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("expected panic when GetClientConfig fails") + } + }() + + source := &failingTLSConfigSource{} + mustGetClientConfig(source, nil) +} diff --git a/proxy/proxy.go b/proxy/proxy.go index aa285a8346..c15df94b78 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -191,8 +191,8 @@ func (p *Proxy) Accept() { continue } + p.handlers.Add(1) go connTimer.Time(func() { - p.handlers.Add(1) openCounter.Inc(1) totalCounter.Inc(1) diff --git a/proxy/semaphore_test.go b/proxy/semaphore_test.go new file mode 100644 index 0000000000..2c65e26d84 --- /dev/null +++ b/proxy/semaphore_test.go @@ -0,0 +1,47 @@ +/*- + * Copyright 2025 Ghostunnel + * + * 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 proxy + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUnlimitedSemaphoreAcquireWithBackground(t *testing.T) { + sem := &unlimitedSemaphore{} + err := sem.Acquire(context.Background(), 1) + assert.Nil(t, err, "Acquire should return nil for background context") +} + +func TestUnlimitedSemaphoreAcquireWithCanceledContext(t *testing.T) { + sem := &unlimitedSemaphore{} + ctx, cancel := context.WithCancel(context.Background()) + cancel() + err := sem.Acquire(ctx, 1) + assert.NotNil(t, err, "Acquire should return context error when canceled") + assert.Equal(t, context.Canceled, err) +} + +func TestUnlimitedSemaphoreRelease(t *testing.T) { + sem := &unlimitedSemaphore{} + // Should not panic - Release is a no-op for unlimited semaphore + sem.Release(1) + sem.Release(100) + sem.Release(0) +} diff --git a/socket/net_test.go b/socket/net_test.go index 0eda44b309..b0304a78e6 100644 --- a/socket/net_test.go +++ b/socket/net_test.go @@ -17,6 +17,7 @@ package socket import ( + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -105,3 +106,52 @@ func TestParseHTTPAddress(t *testing.T) { t.Errorf("unexpected address: %s", address) } } + +func TestOpenTCPSocket(t *testing.T) { + ln, err := Open("tcp", "127.0.0.1:0") + assert.Nil(t, err, "should be able to open TCP socket on random port") + defer func() { _ = ln.Close() }() + assert.NotNil(t, ln.Addr(), "listener should have an address") +} + +func TestOpenUnixSocket(t *testing.T) { + tmpDir := t.TempDir() + sockPath := filepath.Join(tmpDir, "test.sock") + ln, err := Open("unix", sockPath) + assert.Nil(t, err, "should be able to open Unix socket") + defer func() { _ = ln.Close() }() + assert.NotNil(t, ln.Addr(), "listener should have an address") +} + +func TestParseAndOpenTCPSuccess(t *testing.T) { + ln, err := ParseAndOpen("127.0.0.1:0") + assert.Nil(t, err, "should be able to parse and open TCP address") + defer func() { _ = ln.Close() }() +} + +func TestParseAndOpenUnixSuccess(t *testing.T) { + tmpDir := t.TempDir() + sockPath := filepath.Join(tmpDir, "test.sock") + ln, err := ParseAndOpen("unix:" + sockPath) + assert.Nil(t, err, "should be able to parse and open Unix socket") + defer func() { _ = ln.Close() }() +} + +func TestParseAndOpenInvalidAddress(t *testing.T) { + _, err := ParseAndOpen("invalid-no-port") + assert.NotNil(t, err, "should fail to parse invalid address") +} + +func TestParseAndOpenUnresolvable(t *testing.T) { + _, err := ParseAndOpen("nonexistent.invalid.domain.test:8080") + assert.NotNil(t, err, "should fail to resolve nonexistent domain") +} + +func TestParseAddressWithSkipResolve(t *testing.T) { + // With skipResolve=true, should not fail on unresolvable address + network, address, host, err := ParseAddress("nonexistent.invalid.domain.test:8080", true) + assert.Nil(t, err, "should succeed with skipResolve=true") + assert.Equal(t, "tcp", network) + assert.Equal(t, "nonexistent.invalid.domain.test:8080", address) + assert.Equal(t, "nonexistent.invalid.domain.test", host) +} diff --git a/status_test.go b/status_test.go index 600c984386..0cd00aa44e 100644 --- a/status_test.go +++ b/status_test.go @@ -20,7 +20,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "io" "net" "net/http" @@ -253,7 +252,7 @@ func statusTargetWithResponseStatusCode(code int) (statusResponse, int) { return nil, errors.New("simulating error when talking to backend") } u, _ := url.Parse(statusTarget.URL) // NOTE: I tried using statusTarget.Config.Addr instead, but it wasn't set. - return net.Dial("tcp", fmt.Sprintf("%s:%s", u.Hostname(), u.Port())) + return net.Dial("tcp", net.JoinHostPort(u.Hostname(), u.Port())) }, "", "", "", statusTarget.URL) req := httptest.NewRequest(http.MethodGet, "/not-empty", nil) diff --git a/test-keys/Makefile b/test-keys/Makefile deleted file mode 100644 index b17b9d3ea9..0000000000 --- a/test-keys/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -.PHONY: all -all: root-combined.pem root-key.pem server-combined.pem server-keystore.p12 client-combined.pem client-keystore.p12 server-pkcs8.pem server-key.pem client-pkcs8.pem client-key.pem cacert.pem - rm -f *.srl *-csr.pem - -%-key.pem: - openssl genrsa -out $@ 2048 - -%-csr.pem: %-key.pem - openssl req -new -key $< -out $@ -subj /CN=$(@:-csr.pem=) - -## -## NOTE: At the following rules, CSR should be the 1st, and key/certificate/CA -## the 2nd prerequisite -## - -root-cert.pem: root-csr.pem root-key.pem openssl.ext - openssl x509 -req -sha256 -in $< -signkey $(word 2,$^) -out $@ -days 5000 -extfile openssl.ext -extensions root - -cacert.pem: root-cert.pem - cp $< $@ - -server-cert.pem: server-csr.pem root-combined.pem openssl.ext - openssl x509 -req -sha256 -in $< -CA $(word 2,$^) -CAkey $(word 2,$^) -CAcreateserial -out $@ -days 5000 -extfile openssl.ext -extensions server - -client-cert.pem: client-csr.pem root-combined.pem openssl.ext - openssl x509 -req -sha256 -in $< -CA $(word 2,$^) -CAkey $(word 2,$^) -CAcreateserial -out $@ -days 5000 -extfile openssl.ext -extensions client - -# Combined certificate/key file -%-combined.pem: %-cert.pem %-key.pem - cat $^ > $@ - -# Keystore in PKCS#12 format -%-keystore.p12: %-combined.pem - openssl pkcs12 -export -out $@ -in $< -inkey $< -password pass: - -# Private key in PKCS#8 format (for SoftHSM testing) -%-pkcs8.pem: %-key.pem - openssl pkcs8 -topk8 -inform PEM -outform PEM -in $< -out $@ -nocrypt diff --git a/test-keys/README.md b/test-keys/README.md deleted file mode 100644 index 3363a4a562..0000000000 --- a/test-keys/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Test keys -========= - -The certificates and keys contained in this directory have been generated for -test/development purposes only. Do not use these files in production -deployments! You can regenerate them anytime by running `make all`. diff --git a/test-keys/cacert.pem b/test-keys/cacert.pem deleted file mode 100644 index 5519e8be0a..0000000000 --- a/test-keys/cacert.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICxzCCAa+gAwIBAgIJAOVOC+xQP1L0MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV -BAMMBHJvb3QwHhcNMTkwNjE4MTkwNjA0WhcNMzMwMjI0MTkwNjA0WjAPMQ0wCwYD -VQQDDARyb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ts+OiJP -hFYyks33N3VVMFYAHXmBThx9tu2nIBspwXGDj4kUtVbTtsw4+FP4zdOqVJAjcd17 -sFvQmIkCtNQriE1zbFZZ1wDUMGCrw60JM0sjbWwlgOlio5iY/Zf/Xuh7zN/CNgyM -s6hqF7Rwy+Vw9BvOG3/A2Xu0LzaRmNmzfd5KzMfqkMqunC84CKSGy+jcwu9w2gcm -j0Nbf7AcH9pNb4cdmuP2fnq//IFdDxesfMhZq+CI1WiO3JtUzg3oxPr/RYcofI9P -UkzRzbjn8u9j2gDJ0umCFDKOawhTmj51/rqMXU8IcdsV3T0Iftq1DdweF4k85P6A -oHKOAkRIaTsTlwIDAQABoyYwJDAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgw -BgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAHAx0mgmvlqp4KfCQ/WP8YCblQ8v1 -qWIC4sZk6oGpLMAVa3jMWY2gelrfA8WByOFVzyOnNF4vxidSCPVhBrXNMrWJSq3J -9udQEuREA7v7sElf0sseCtSp8Jp+ase0V+i+fZDPI5ezvGdZgMoOim9K1QcoU911 -4Yj0UBaB2avmktNDAo6D0o4Ph/sTAWoaqllDtql3ovvpIhlQ/3ZgsOrGxJQJrhMO -whK5pvnU0yDH37pRWrcguv/dTTbT5kc1BPmz7oIynhaczVrYOXii9XqqSl0wGYUm -dpj6fVObgA5UrTytEnGLKvPmvYfJgN4CzX0SOm+lC89LWAp4u2xvgRE3HQ== ------END CERTIFICATE----- diff --git a/test-keys/client-cert.pem b/test-keys/client-cert.pem deleted file mode 100644 index 974293b549..0000000000 --- a/test-keys/client-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIJAJG2NWsnKE4bMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV -BAMMBHJvb3QwHhcNMTkwNjE4MTkwNjA0WhcNMzMwMjI0MTkwNjA0WjARMQ8wDQYD -VQQDDAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiqQT6 -+lMjyPHaTIILqndhhuQlrNbGwdCeUC/ATnV0svAbojfAysOspJGoTmTP6v+SHnyp -kODJJoYYPVn7jY4EbSk8NqkSBr1/Ood7DLwCgflh1lKmuzJ1J5PYLew8bDlwD3Wb -bgto5y+NPC+LsM85+GlN5UE1rJkzRpKYa8TEoQ1Kt/nfOjkKLiynJfSL8gzJHKjA -qyFI8fTWaZ0naH5fG8ZMQezkhw/imhvSHW5oZIgQieZqe5vM0B9/BEUjSgblLaak -/snByo86RtH2Ga3QA4g9TiZzs9ou3x4hqqoY7dv8hzBxUHW686ZKiRi5C5F3ieKc -iyFf7GHBTIWUXrl5AgMBAAGjYTBfMBMGA1UdJQQMMAoGCCsGAQUFBwMCMEgGA1Ud -EQRBMD+HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdIYac3BpZmZl -Oi8vZ2hvc3R1bm5lbC9jbGllbnQwDQYJKoZIhvcNAQELBQADggEBAMYM1iOjzjKh -f1cudHrG462KqDlLO/C3kTkboBBVPNEZFGQ+nXIAdrE5Di88NiLLnhlXhpvq5AGt -8WSm9qB411pHZHUPu4x7e0pAhVh46XBe3dh7ICCpBRBOJJXvBpRUP9dLIL8hMOoA -lFUIO4ivHLZ/5bbn25xcMmy9m7BiyehBDmx/VnH4nvlS22D9MGc1wGnKoY89w8Tm -wwnwANgEYV26SUhSylwi7TaB/K43toOmfHOM+l+dreTmKQCtHdRt/4ZX5r7UNxvR -MbhhirlVramubA4VeCzwqT7CWv8CQpi4Sh1/cztNNHo0Rf3sU8VMs7TRNGRqqyvg -MZh1Oyc57pw= ------END CERTIFICATE----- diff --git a/test-keys/client-combined.pem b/test-keys/client-combined.pem deleted file mode 100644 index 0caedabe46..0000000000 --- a/test-keys/client-combined.pem +++ /dev/null @@ -1,46 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIJAJG2NWsnKE4bMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV -BAMMBHJvb3QwHhcNMTkwNjE4MTkwNjA0WhcNMzMwMjI0MTkwNjA0WjARMQ8wDQYD -VQQDDAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiqQT6 -+lMjyPHaTIILqndhhuQlrNbGwdCeUC/ATnV0svAbojfAysOspJGoTmTP6v+SHnyp -kODJJoYYPVn7jY4EbSk8NqkSBr1/Ood7DLwCgflh1lKmuzJ1J5PYLew8bDlwD3Wb -bgto5y+NPC+LsM85+GlN5UE1rJkzRpKYa8TEoQ1Kt/nfOjkKLiynJfSL8gzJHKjA -qyFI8fTWaZ0naH5fG8ZMQezkhw/imhvSHW5oZIgQieZqe5vM0B9/BEUjSgblLaak -/snByo86RtH2Ga3QA4g9TiZzs9ou3x4hqqoY7dv8hzBxUHW686ZKiRi5C5F3ieKc -iyFf7GHBTIWUXrl5AgMBAAGjYTBfMBMGA1UdJQQMMAoGCCsGAQUFBwMCMEgGA1Ud -EQRBMD+HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdIYac3BpZmZl -Oi8vZ2hvc3R1bm5lbC9jbGllbnQwDQYJKoZIhvcNAQELBQADggEBAMYM1iOjzjKh -f1cudHrG462KqDlLO/C3kTkboBBVPNEZFGQ+nXIAdrE5Di88NiLLnhlXhpvq5AGt -8WSm9qB411pHZHUPu4x7e0pAhVh46XBe3dh7ICCpBRBOJJXvBpRUP9dLIL8hMOoA -lFUIO4ivHLZ/5bbn25xcMmy9m7BiyehBDmx/VnH4nvlS22D9MGc1wGnKoY89w8Tm -wwnwANgEYV26SUhSylwi7TaB/K43toOmfHOM+l+dreTmKQCtHdRt/4ZX5r7UNxvR -MbhhirlVramubA4VeCzwqT7CWv8CQpi4Sh1/cztNNHo0Rf3sU8VMs7TRNGRqqyvg -MZh1Oyc57pw= ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA4qkE+vpTI8jx2kyCC6p3YYbkJazWxsHQnlAvwE51dLLwG6I3 -wMrDrKSRqE5kz+r/kh58qZDgySaGGD1Z+42OBG0pPDapEga9fzqHewy8AoH5YdZS -prsydSeT2C3sPGw5cA91m24LaOcvjTwvi7DPOfhpTeVBNayZM0aSmGvExKENSrf5 -3zo5Ci4spyX0i/IMyRyowKshSPH01mmdJ2h+XxvGTEHs5IcP4pob0h1uaGSIEInm -anubzNAffwRFI0oG5S2mpP7JwcqPOkbR9hmt0AOIPU4mc7PaLt8eIaqqGO3b/Icw -cVB1uvOmSokYuQuRd4ninIshX+xhwUyFlF65eQIDAQABAoIBAHDANXsH9T2y4yR9 -tJ9LADHioTFgpkKe/UETkH1wShtwO+LzMhrUgrwp7U81GA8ZzmKIiejr6fYGFOSP -+GgbRY9MIhwS8M3HzpIwsl5yuj/hGgYiUGEic/o1YuVCCucPyw7EkfNsrX5UqqHu -U5SAssUann+iUGr73gXU0G3EBlDtAPHyLks/ejnyEmiCdvzdMUwyOdhPIliy/Wxx -i7BrHqwOQ2Ze8r7RYAFsFDQZu17JvWz89Amyq+gsArDdG1O0pgT1PaJvGmimsADg -rTbsx6CSReqsdMMHVP59W/neYjT4tnj7Tqa9SoLw98J3get7iD1/qEWNZMfweYFG -Mu5N0MUCgYEA9jmuAca4yXDQaXpMyl3mi/93kG9XQzQpJfSJi4h/In3v5maml5wZ -nl+RrRBLbknCaJosybmezArCwNqRtMbv3ROEtlt148UVyLE/nQ1t5SZoVGmqQcuQ -33w8CHmiJXxpFJF8QGlsMwPBkeu0GKWadtHgDhUquPC6ZO8wrVdySKsCgYEA66iB -VVlQcrydJywmghgWfs5IGOcFNiYEnqwb+4YncLH6bnpct1rZPIBGqrOrqyrBuEJC -X8EOhWdbNHesjaMsnPCk18Iu1MrDbFY3jc1yRoceaAfbRgo4wxJrWAAivq2RLrZr -uQoNMZ5szZWb7tj77lPHM06dBNiY/lKSCgjjDmsCgYBVUZVHDkn3mzzMX9F+Cd2a -QzJ1/O663bcobLzAedK76/GV35n4TOHfq/P5iPzqQWq4/jtoxwYMY+oqE1KL1qdE -7r98xOJrw14SBcEqNX4P+igKn/acB+KyXgUbVrJl1N9Afk0UsLMKVlHbs1XbeH62 -j3DY0GLWZKvhz7QJTJTQMwKBgQCmR+DyezHzs7jj4hhN5XdCBIYE5EXeHldWKrUX -9mIv97VwbPCtA/KS6SRz4JE6FtJYFJDHBe8cHIDTdboQutmI55g5EbB/v47DC9bp -PNTshPzkhN6o/h9tLbsGfhoGF2yA3IEIMNp8b3/wVCeSEhM56G1/e3H58GeL1HFt -mTW1fwKBgFSvhnIgwPrrshHKzrHrLwJowtQwOaEVLmYhloV0B4pkUV9Cn55QPqTt -EtCbP00HzeTJN472lknOvZ744cecc3pjqMsCPKc4ThJ8IxonFiiyC9ZDlUdTbUEQ -BSbORtj0VBIpiZFylhSYsw+i+GeI8EFgNMJvjfGVduv0MxrQKxan ------END RSA PRIVATE KEY----- diff --git a/test-keys/client-key.pem b/test-keys/client-key.pem deleted file mode 100644 index 4339e3aca6..0000000000 --- a/test-keys/client-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA4qkE+vpTI8jx2kyCC6p3YYbkJazWxsHQnlAvwE51dLLwG6I3 -wMrDrKSRqE5kz+r/kh58qZDgySaGGD1Z+42OBG0pPDapEga9fzqHewy8AoH5YdZS -prsydSeT2C3sPGw5cA91m24LaOcvjTwvi7DPOfhpTeVBNayZM0aSmGvExKENSrf5 -3zo5Ci4spyX0i/IMyRyowKshSPH01mmdJ2h+XxvGTEHs5IcP4pob0h1uaGSIEInm -anubzNAffwRFI0oG5S2mpP7JwcqPOkbR9hmt0AOIPU4mc7PaLt8eIaqqGO3b/Icw -cVB1uvOmSokYuQuRd4ninIshX+xhwUyFlF65eQIDAQABAoIBAHDANXsH9T2y4yR9 -tJ9LADHioTFgpkKe/UETkH1wShtwO+LzMhrUgrwp7U81GA8ZzmKIiejr6fYGFOSP -+GgbRY9MIhwS8M3HzpIwsl5yuj/hGgYiUGEic/o1YuVCCucPyw7EkfNsrX5UqqHu -U5SAssUann+iUGr73gXU0G3EBlDtAPHyLks/ejnyEmiCdvzdMUwyOdhPIliy/Wxx -i7BrHqwOQ2Ze8r7RYAFsFDQZu17JvWz89Amyq+gsArDdG1O0pgT1PaJvGmimsADg -rTbsx6CSReqsdMMHVP59W/neYjT4tnj7Tqa9SoLw98J3get7iD1/qEWNZMfweYFG -Mu5N0MUCgYEA9jmuAca4yXDQaXpMyl3mi/93kG9XQzQpJfSJi4h/In3v5maml5wZ -nl+RrRBLbknCaJosybmezArCwNqRtMbv3ROEtlt148UVyLE/nQ1t5SZoVGmqQcuQ -33w8CHmiJXxpFJF8QGlsMwPBkeu0GKWadtHgDhUquPC6ZO8wrVdySKsCgYEA66iB -VVlQcrydJywmghgWfs5IGOcFNiYEnqwb+4YncLH6bnpct1rZPIBGqrOrqyrBuEJC -X8EOhWdbNHesjaMsnPCk18Iu1MrDbFY3jc1yRoceaAfbRgo4wxJrWAAivq2RLrZr -uQoNMZ5szZWb7tj77lPHM06dBNiY/lKSCgjjDmsCgYBVUZVHDkn3mzzMX9F+Cd2a -QzJ1/O663bcobLzAedK76/GV35n4TOHfq/P5iPzqQWq4/jtoxwYMY+oqE1KL1qdE -7r98xOJrw14SBcEqNX4P+igKn/acB+KyXgUbVrJl1N9Afk0UsLMKVlHbs1XbeH62 -j3DY0GLWZKvhz7QJTJTQMwKBgQCmR+DyezHzs7jj4hhN5XdCBIYE5EXeHldWKrUX -9mIv97VwbPCtA/KS6SRz4JE6FtJYFJDHBe8cHIDTdboQutmI55g5EbB/v47DC9bp -PNTshPzkhN6o/h9tLbsGfhoGF2yA3IEIMNp8b3/wVCeSEhM56G1/e3H58GeL1HFt -mTW1fwKBgFSvhnIgwPrrshHKzrHrLwJowtQwOaEVLmYhloV0B4pkUV9Cn55QPqTt -EtCbP00HzeTJN472lknOvZ744cecc3pjqMsCPKc4ThJ8IxonFiiyC9ZDlUdTbUEQ -BSbORtj0VBIpiZFylhSYsw+i+GeI8EFgNMJvjfGVduv0MxrQKxan ------END RSA PRIVATE KEY----- diff --git a/test-keys/client-keystore.p12 b/test-keys/client-keystore.p12 deleted file mode 100644 index 67d59dac49..0000000000 Binary files a/test-keys/client-keystore.p12 and /dev/null differ diff --git a/test-keys/client-pkcs8.pem b/test-keys/client-pkcs8.pem deleted file mode 100644 index 579065875e..0000000000 --- a/test-keys/client-pkcs8.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDiqQT6+lMjyPHa -TIILqndhhuQlrNbGwdCeUC/ATnV0svAbojfAysOspJGoTmTP6v+SHnypkODJJoYY -PVn7jY4EbSk8NqkSBr1/Ood7DLwCgflh1lKmuzJ1J5PYLew8bDlwD3Wbbgto5y+N -PC+LsM85+GlN5UE1rJkzRpKYa8TEoQ1Kt/nfOjkKLiynJfSL8gzJHKjAqyFI8fTW -aZ0naH5fG8ZMQezkhw/imhvSHW5oZIgQieZqe5vM0B9/BEUjSgblLaak/snByo86 -RtH2Ga3QA4g9TiZzs9ou3x4hqqoY7dv8hzBxUHW686ZKiRi5C5F3ieKciyFf7GHB -TIWUXrl5AgMBAAECggEAcMA1ewf1PbLjJH20n0sAMeKhMWCmQp79QROQfXBKG3A7 -4vMyGtSCvCntTzUYDxnOYoiJ6Ovp9gYU5I/4aBtFj0wiHBLwzcfOkjCyXnK6P+Ea -BiJQYSJz+jVi5UIK5w/LDsSR82ytflSqoe5TlICyxRqef6JQavveBdTQbcQGUO0A -8fIuSz96OfISaIJ2/N0xTDI52E8iWLL9bHGLsGserA5DZl7yvtFgAWwUNBm7Xsm9 -bPz0CbKr6CwCsN0bU7SmBPU9om8aaKawAOCtNuzHoJJF6qx0wwdU/n1b+d5iNPi2 -ePtOpr1KgvD3wneB63uIPX+oRY1kx/B5gUYy7k3QxQKBgQD2Oa4BxrjJcNBpekzK -XeaL/3eQb1dDNCkl9ImLiH8ife/mZqaXnBmeX5GtEEtuScJomizJuZ7MCsLA2pG0 -xu/dE4S2W3XjxRXIsT+dDW3lJmhUaapBy5DffDwIeaIlfGkUkXxAaWwzA8GR67QY -pZp20eAOFSq48Lpk7zCtV3JIqwKBgQDrqIFVWVByvJ0nLCaCGBZ+zkgY5wU2JgSe -rBv7hidwsfpuely3Wtk8gEaqs6urKsG4QkJfwQ6FZ1s0d6yNoyyc8KTXwi7UysNs -VjeNzXJGhx5oB9tGCjjDEmtYACK+rZEutmu5Cg0xnmzNlZvu2PvuU8czTp0E2Jj+ -UpIKCOMOawKBgFVRlUcOSfebPMxf0X4J3ZpDMnX87rrdtyhsvMB50rvr8ZXfmfhM -4d+r8/mI/OpBarj+O2jHBgxj6ioTUovWp0Tuv3zE4mvDXhIFwSo1fg/6KAqf9pwH -4rJeBRtWsmXU30B+TRSwswpWUduzVdt4fraPcNjQYtZkq+HPtAlMlNAzAoGBAKZH -4PJ7MfOzuOPiGE3ld0IEhgTkRd4eV1YqtRf2Yi/3tXBs8K0D8pLpJHPgkToW0lgU -kMcF7xwcgNN1uhC62YjnmDkRsH+/jsML1uk81OyE/OSE3qj+H20tuwZ+GgYXbIDc -gQgw2nxvf/BUJ5ISEznobX97cfnwZ4vUcW2ZNbV/AoGAVK+GciDA+uuyEcrOsesv -AmjC1DA5oRUuZiGWhXQHimRRX0KfnlA+pO0S0Js/TQfN5Mk3jvaWSc69nvjhx5xz -emOoywI8pzhOEnwjGicWKLIL1kOVR1NtQRAFJs5G2PRUEimJkXKWFJizD6L4Z4jw -QWA0wm+N8ZV26/QzGtArFqc= ------END PRIVATE KEY----- diff --git a/test-keys/openssl.ext b/test-keys/openssl.ext deleted file mode 100644 index d4627db0ce..0000000000 --- a/test-keys/openssl.ext +++ /dev/null @@ -1,9 +0,0 @@ -[root] -keyUsage=critical, keyCertSign -basicConstraints=critical, CA:TRUE, pathlen:0 -[server] -extendedKeyUsage = serverAuth -subjectAltName = IP:127.0.0.1,IP:::1,DNS:localhost,URI:spiffe://ghostunnel/server -[client] -extendedKeyUsage = clientAuth -subjectAltName = IP:127.0.0.1,IP:::1,DNS:localhost,URI:spiffe://ghostunnel/client diff --git a/test-keys/root-cert.pem b/test-keys/root-cert.pem deleted file mode 100644 index 5519e8be0a..0000000000 --- a/test-keys/root-cert.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICxzCCAa+gAwIBAgIJAOVOC+xQP1L0MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV -BAMMBHJvb3QwHhcNMTkwNjE4MTkwNjA0WhcNMzMwMjI0MTkwNjA0WjAPMQ0wCwYD -VQQDDARyb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ts+OiJP -hFYyks33N3VVMFYAHXmBThx9tu2nIBspwXGDj4kUtVbTtsw4+FP4zdOqVJAjcd17 -sFvQmIkCtNQriE1zbFZZ1wDUMGCrw60JM0sjbWwlgOlio5iY/Zf/Xuh7zN/CNgyM -s6hqF7Rwy+Vw9BvOG3/A2Xu0LzaRmNmzfd5KzMfqkMqunC84CKSGy+jcwu9w2gcm -j0Nbf7AcH9pNb4cdmuP2fnq//IFdDxesfMhZq+CI1WiO3JtUzg3oxPr/RYcofI9P -UkzRzbjn8u9j2gDJ0umCFDKOawhTmj51/rqMXU8IcdsV3T0Iftq1DdweF4k85P6A -oHKOAkRIaTsTlwIDAQABoyYwJDAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgw -BgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAHAx0mgmvlqp4KfCQ/WP8YCblQ8v1 -qWIC4sZk6oGpLMAVa3jMWY2gelrfA8WByOFVzyOnNF4vxidSCPVhBrXNMrWJSq3J -9udQEuREA7v7sElf0sseCtSp8Jp+ase0V+i+fZDPI5ezvGdZgMoOim9K1QcoU911 -4Yj0UBaB2avmktNDAo6D0o4Ph/sTAWoaqllDtql3ovvpIhlQ/3ZgsOrGxJQJrhMO -whK5pvnU0yDH37pRWrcguv/dTTbT5kc1BPmz7oIynhaczVrYOXii9XqqSl0wGYUm -dpj6fVObgA5UrTytEnGLKvPmvYfJgN4CzX0SOm+lC89LWAp4u2xvgRE3HQ== ------END CERTIFICATE----- diff --git a/test-keys/root-combined.pem b/test-keys/root-combined.pem deleted file mode 100644 index 0e45b1332f..0000000000 --- a/test-keys/root-combined.pem +++ /dev/null @@ -1,44 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICxzCCAa+gAwIBAgIJAOVOC+xQP1L0MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV -BAMMBHJvb3QwHhcNMTkwNjE4MTkwNjA0WhcNMzMwMjI0MTkwNjA0WjAPMQ0wCwYD -VQQDDARyb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ts+OiJP -hFYyks33N3VVMFYAHXmBThx9tu2nIBspwXGDj4kUtVbTtsw4+FP4zdOqVJAjcd17 -sFvQmIkCtNQriE1zbFZZ1wDUMGCrw60JM0sjbWwlgOlio5iY/Zf/Xuh7zN/CNgyM -s6hqF7Rwy+Vw9BvOG3/A2Xu0LzaRmNmzfd5KzMfqkMqunC84CKSGy+jcwu9w2gcm -j0Nbf7AcH9pNb4cdmuP2fnq//IFdDxesfMhZq+CI1WiO3JtUzg3oxPr/RYcofI9P -UkzRzbjn8u9j2gDJ0umCFDKOawhTmj51/rqMXU8IcdsV3T0Iftq1DdweF4k85P6A -oHKOAkRIaTsTlwIDAQABoyYwJDAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgw -BgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAHAx0mgmvlqp4KfCQ/WP8YCblQ8v1 -qWIC4sZk6oGpLMAVa3jMWY2gelrfA8WByOFVzyOnNF4vxidSCPVhBrXNMrWJSq3J -9udQEuREA7v7sElf0sseCtSp8Jp+ase0V+i+fZDPI5ezvGdZgMoOim9K1QcoU911 -4Yj0UBaB2avmktNDAo6D0o4Ph/sTAWoaqllDtql3ovvpIhlQ/3ZgsOrGxJQJrhMO -whK5pvnU0yDH37pRWrcguv/dTTbT5kc1BPmz7oIynhaczVrYOXii9XqqSl0wGYUm -dpj6fVObgA5UrTytEnGLKvPmvYfJgN4CzX0SOm+lC89LWAp4u2xvgRE3HQ== ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA0ts+OiJPhFYyks33N3VVMFYAHXmBThx9tu2nIBspwXGDj4kU -tVbTtsw4+FP4zdOqVJAjcd17sFvQmIkCtNQriE1zbFZZ1wDUMGCrw60JM0sjbWwl -gOlio5iY/Zf/Xuh7zN/CNgyMs6hqF7Rwy+Vw9BvOG3/A2Xu0LzaRmNmzfd5KzMfq -kMqunC84CKSGy+jcwu9w2gcmj0Nbf7AcH9pNb4cdmuP2fnq//IFdDxesfMhZq+CI -1WiO3JtUzg3oxPr/RYcofI9PUkzRzbjn8u9j2gDJ0umCFDKOawhTmj51/rqMXU8I -cdsV3T0Iftq1DdweF4k85P6AoHKOAkRIaTsTlwIDAQABAoIBAH24w62d75OUYasu -q4yhpR2g6YipffO5ASwlH1UBXTA+IpdewL4u+yUvN3i5eMwgvJqXJsspqCpLVGDe -sIJpT1uB8qRSCFct41bFDSUq8yVmU8VWijYG8g1hWzc5fcZ1D/vkHsRjTzF/5WIk -8GxibarfOVEkJzSFkbXk74MXqvIMloeohYhHBEkuelv4cE79UhArYm//tYNQn6qJ -8x7yu6tgZa3L5YlAXT162LXr4jz5eqnvYuEypjQUcFNsNGWAg50y05+lM855KQXZ -t1qVYXeKqoH6QAgXJCS/ETD8uVf6llryOjEtxLfhKn6zQ9Pkid1ui3iSWrtitSfF -l4HZdoECgYEA/sjGEjlT8MgGtZHFyjiGs5QBuPWDmMnF8yOTEs53zedxflOjgc0T -qp195QT1NP8v+N3KKPxViqWJ3zQIeKSIBSXKV+CVrY3mqF0aOTKVSiUHXJtswPcj -dva5fPToxbg6DslabfQ2U5gabwpnv33oC9F5FIGO6UwVK+y6Uvt9z3ECgYEA09zP -aw9lQkTtjPTmQXd9gymm3kLkNySZ7P2UhZqd1gw80YabIXUQxeDVkanTe5lmpIy3 -xI0fBb5L8nHfBatiRuT4HLg4FTQvSoszSQUwRr90oXdK2KFtdJ10nSalQkl2rKiu -Goiz1zk5v9i5snS5QUyjUjEVXuXmWtyGN1AEH4cCgYAoGNzjPVZyjGhR5vEj1CWO -2Xoz65+cShT3IOAik4/TPdtksDEJWob/0O1hP5h/kLGyDuWj8aJcwZSjJgM3SV2G -wd3IWrXzrGNil9RqsAVCt/Uio+tHqx1PuaCTg8+mXkn0zceHimmKWmO+mfqd7mi4 -g2z0xzcAjFLAqO7h5GZ2YQKBgQCIGBj5qfse9c4vNQZQWaS7w97YednF/nIOhdqJ -dIgdfuD2q2QtutiadFJc7t3WUPVZkLdfwFOESfjZdgKQzsYjymQDduc337zdQswg -BQA9AjG2oz3mKNR6C8dkR/XyveRJB1ZH3za/c5hAP8UR+N8kLknfu34B5ubxySVC -lNkBMwKBgDzeYdoblhseC3FNU0f0rgs3gemoqXOswv31gEULKxjKdRVhYc0CNZrJ -3Rf+cp5yjWitc0eLFNM3W0JblDacR4p27+hBheXv4VfdXswju6V/IwYOPsNb2Xe6 -6P3h7grmtV2H4wSXUv+hi5VWQfGj3DJWB6PiPyYQ4wzwy6Xw/4op ------END RSA PRIVATE KEY----- diff --git a/test-keys/root-key.pem b/test-keys/root-key.pem deleted file mode 100644 index a146568d87..0000000000 --- a/test-keys/root-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA0ts+OiJPhFYyks33N3VVMFYAHXmBThx9tu2nIBspwXGDj4kU -tVbTtsw4+FP4zdOqVJAjcd17sFvQmIkCtNQriE1zbFZZ1wDUMGCrw60JM0sjbWwl -gOlio5iY/Zf/Xuh7zN/CNgyMs6hqF7Rwy+Vw9BvOG3/A2Xu0LzaRmNmzfd5KzMfq -kMqunC84CKSGy+jcwu9w2gcmj0Nbf7AcH9pNb4cdmuP2fnq//IFdDxesfMhZq+CI -1WiO3JtUzg3oxPr/RYcofI9PUkzRzbjn8u9j2gDJ0umCFDKOawhTmj51/rqMXU8I -cdsV3T0Iftq1DdweF4k85P6AoHKOAkRIaTsTlwIDAQABAoIBAH24w62d75OUYasu -q4yhpR2g6YipffO5ASwlH1UBXTA+IpdewL4u+yUvN3i5eMwgvJqXJsspqCpLVGDe -sIJpT1uB8qRSCFct41bFDSUq8yVmU8VWijYG8g1hWzc5fcZ1D/vkHsRjTzF/5WIk -8GxibarfOVEkJzSFkbXk74MXqvIMloeohYhHBEkuelv4cE79UhArYm//tYNQn6qJ -8x7yu6tgZa3L5YlAXT162LXr4jz5eqnvYuEypjQUcFNsNGWAg50y05+lM855KQXZ -t1qVYXeKqoH6QAgXJCS/ETD8uVf6llryOjEtxLfhKn6zQ9Pkid1ui3iSWrtitSfF -l4HZdoECgYEA/sjGEjlT8MgGtZHFyjiGs5QBuPWDmMnF8yOTEs53zedxflOjgc0T -qp195QT1NP8v+N3KKPxViqWJ3zQIeKSIBSXKV+CVrY3mqF0aOTKVSiUHXJtswPcj -dva5fPToxbg6DslabfQ2U5gabwpnv33oC9F5FIGO6UwVK+y6Uvt9z3ECgYEA09zP -aw9lQkTtjPTmQXd9gymm3kLkNySZ7P2UhZqd1gw80YabIXUQxeDVkanTe5lmpIy3 -xI0fBb5L8nHfBatiRuT4HLg4FTQvSoszSQUwRr90oXdK2KFtdJ10nSalQkl2rKiu -Goiz1zk5v9i5snS5QUyjUjEVXuXmWtyGN1AEH4cCgYAoGNzjPVZyjGhR5vEj1CWO -2Xoz65+cShT3IOAik4/TPdtksDEJWob/0O1hP5h/kLGyDuWj8aJcwZSjJgM3SV2G -wd3IWrXzrGNil9RqsAVCt/Uio+tHqx1PuaCTg8+mXkn0zceHimmKWmO+mfqd7mi4 -g2z0xzcAjFLAqO7h5GZ2YQKBgQCIGBj5qfse9c4vNQZQWaS7w97YednF/nIOhdqJ -dIgdfuD2q2QtutiadFJc7t3WUPVZkLdfwFOESfjZdgKQzsYjymQDduc337zdQswg -BQA9AjG2oz3mKNR6C8dkR/XyveRJB1ZH3za/c5hAP8UR+N8kLknfu34B5ubxySVC -lNkBMwKBgDzeYdoblhseC3FNU0f0rgs3gemoqXOswv31gEULKxjKdRVhYc0CNZrJ -3Rf+cp5yjWitc0eLFNM3W0JblDacR4p27+hBheXv4VfdXswju6V/IwYOPsNb2Xe6 -6P3h7grmtV2H4wSXUv+hi5VWQfGj3DJWB6PiPyYQ4wzwy6Xw/4op ------END RSA PRIVATE KEY----- diff --git a/test-keys/server-cert.pem b/test-keys/server-cert.pem deleted file mode 100644 index 1a0995c7af..0000000000 --- a/test-keys/server-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIJAJG2NWsnKE4aMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV -BAMMBHJvb3QwHhcNMTkwNjE4MTkwNjA0WhcNMzMwMjI0MTkwNjA0WjARMQ8wDQYD -VQQDDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0btZV -oXR6FVQuu6ou01zGZanM/Rf42a6dtPm5oUohbbHDTsfOrrZ6p6HdBn1CE4rrnUVF -Gd9N9gGMRGjHeF2MuF0E3PFyLmjHFZ8LvhoJqVrbvzO+SZsa5KqN+jiM/F6vjuFD -YnA9IC4bwWg88SoXYvdzBlpBjrZ+jXmTTu7jHkmGjwlTj4BFyFmXBcBY5drD8yAx -KY8h7c0vday+r54oUuNast4b+da6qbyTOvSkaGpefSVpvifZ5Ujxyj7+U5EpjxSq -9bi8z7eqZ/NXMrLqfBwXavLqitdZbIz+KsdRJs4GERa8FX35sGkt9fvHlc1IOwi/ -ujbRteiLQE9Lky33AgMBAAGjYTBfMBMGA1UdJQQMMAoGCCsGAQUFBwMBMEgGA1Ud -EQRBMD+HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdIYac3BpZmZl -Oi8vZ2hvc3R1bm5lbC9zZXJ2ZXIwDQYJKoZIhvcNAQELBQADggEBAKgmfGFidjjt -47+ut7gOKQG4//tclqk5gGQeC0a1dZ8oh+62OlFo+lZdm8seZYE/6sRI0RlQpBGU -CxFcyIGUIE2+wcNdW+rSG6PkZlx9/2RyqwzsFLkSiMBCJYMRMvBJ+0y9C9UrW3Y2 -NPTcGNbTdz3aIFFKo6+cXcji6e+vna9J2a6D6HlkdXCMhWn8zLJu1Dk1EHYPTrgC -52z70BOkGpwWEpAv4Ad4etQkTzO+FfcXi3FFWyURbHpYMG1tGAAI23JE+AZ8Oosp -5MudVntBQGcSKtpbWmKq/jU23C6HqOAhnP/890sv+ZN1AnemSHiyxDaLM6kAy/yf -zWpdG5th+QA= ------END CERTIFICATE----- diff --git a/test-keys/server-combined.pem b/test-keys/server-combined.pem deleted file mode 100644 index 3b1689b93f..0000000000 --- a/test-keys/server-combined.pem +++ /dev/null @@ -1,46 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBDCCAeygAwIBAgIJAJG2NWsnKE4aMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV -BAMMBHJvb3QwHhcNMTkwNjE4MTkwNjA0WhcNMzMwMjI0MTkwNjA0WjARMQ8wDQYD -VQQDDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0btZV -oXR6FVQuu6ou01zGZanM/Rf42a6dtPm5oUohbbHDTsfOrrZ6p6HdBn1CE4rrnUVF -Gd9N9gGMRGjHeF2MuF0E3PFyLmjHFZ8LvhoJqVrbvzO+SZsa5KqN+jiM/F6vjuFD -YnA9IC4bwWg88SoXYvdzBlpBjrZ+jXmTTu7jHkmGjwlTj4BFyFmXBcBY5drD8yAx -KY8h7c0vday+r54oUuNast4b+da6qbyTOvSkaGpefSVpvifZ5Ujxyj7+U5EpjxSq -9bi8z7eqZ/NXMrLqfBwXavLqitdZbIz+KsdRJs4GERa8FX35sGkt9fvHlc1IOwi/ -ujbRteiLQE9Lky33AgMBAAGjYTBfMBMGA1UdJQQMMAoGCCsGAQUFBwMBMEgGA1Ud -EQRBMD+HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdIYac3BpZmZl -Oi8vZ2hvc3R1bm5lbC9zZXJ2ZXIwDQYJKoZIhvcNAQELBQADggEBAKgmfGFidjjt -47+ut7gOKQG4//tclqk5gGQeC0a1dZ8oh+62OlFo+lZdm8seZYE/6sRI0RlQpBGU -CxFcyIGUIE2+wcNdW+rSG6PkZlx9/2RyqwzsFLkSiMBCJYMRMvBJ+0y9C9UrW3Y2 -NPTcGNbTdz3aIFFKo6+cXcji6e+vna9J2a6D6HlkdXCMhWn8zLJu1Dk1EHYPTrgC -52z70BOkGpwWEpAv4Ad4etQkTzO+FfcXi3FFWyURbHpYMG1tGAAI23JE+AZ8Oosp -5MudVntBQGcSKtpbWmKq/jU23C6HqOAhnP/890sv+ZN1AnemSHiyxDaLM6kAy/yf -zWpdG5th+QA= ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAtG7WVaF0ehVULruqLtNcxmWpzP0X+NmunbT5uaFKIW2xw07H -zq62eqeh3QZ9QhOK651FRRnfTfYBjERox3hdjLhdBNzxci5oxxWfC74aCala278z -vkmbGuSqjfo4jPxer47hQ2JwPSAuG8FoPPEqF2L3cwZaQY62fo15k07u4x5Jho8J -U4+ARchZlwXAWOXaw/MgMSmPIe3NL3Wsvq+eKFLjWrLeG/nWuqm8kzr0pGhqXn0l -ab4n2eVI8co+/lORKY8UqvW4vM+3qmfzVzKy6nwcF2ry6orXWWyM/irHUSbOBhEW -vBV9+bBpLfX7x5XNSDsIv7o20bXoi0BPS5Mt9wIDAQABAoIBAQCXW2Lw8j+DKeE0 -UriwDx4ET8Pg8W7qkbCpGudhkKte32X/MFbsSiNJQNXHU2o/w6UFgShajxmDjJo/ -0CMVMSNIqF9fdPgVkFjUqI6CizXMZQSS0hHVzmkOZkOsVfqaShojqvfMTwdYP2Zu -Yg/PD37/n0V0eB++Xrk37/XMXKqIWbcmScR+YZHmuVcPVy+HST0irSYS5bCPp0CY -6fn2tYlGnv9zbEGxX2vYwJkLYZZ+w7zJP92l6Wsb+UesF3oVeHrqZwqR3H0UUbBl -XyXtiVKSrswsMGgAbPwygCYKWvrsYpNUqjGAfKc0aegLAK/zugBKD+pMYf5A4NT7 -IcobZzIBAoGBANwBwoIqy4fq0D1u+qfRFZAfVz11/ysVmW1MXWJbzRZkRJ7uCwgz -VlyCR/1RzEAKgDaudvS2jWPbADrxhMXZCZH6JvnjVoDvp/tH45VUlcXacbJzTX6T -485toMHZtTe3pCQc6Da/OxFRbjZrpM1BUaIMi5olMC17qzbgz4P9rPQNAoGBANHz -qJD0u4y7LXdPdvwTnoBhW3ju92nCm4ahCLSgpu0cYWMI6b+3m+QN2xuWGGtvd0XO -cBrEvaZ3bHz9JWg/R4lN8dd1yEZ5o/0Y1WBV1r3YTetz2gH4FQUyvfhkh2ropYiS -vsAy4MvLpLXwnmSFLUbuH9cTxsiAz+HQoVjgmBUTAoGAQKb3vP7Mfu2fMl55pHVK -C+dZ4MLbMJMRWlA1pSDrHOhsTfQQI+HKQDc85pFyH2O2l9sfM/ytgimqYKf255lH -ObG0YHzRP+Stjadrlsebl+AAx3sgy9C0AkavFihfG7eIseAY0XXS6tzuMWnirTrD -vYZIRA0nokYUY3UfJl+WwsUCgYA4xtbQFI7fmQLLoB7CPp//gdSV2LHp1OnwN34+ -Mq/RsXoYqSFlakbrHmAhjq6d5y/vHcutQYU0Dlm1V1QVY/95fy2mocB+ZojVejDB -85S2FFfGE7diDu/ITxeWo8EHRVD2pRCWs2udUT4CwcZj0qwS4XSU2lFIiaUuIRhx -C5O1JwKBgQDE3hwprPAunMNlfJD4nWXDn9aLF+4kP+vxKxJ95GeG0lpFYYnMEb5R -xp3wPS8u6mC0f90REe28/Y6s9HRXc9VOwa+/jA3Ye6MexiQimDYW1ClnOp2gRoq8 -KM4xtWWjzEVkEKIEtZKn+X78iP03+U/TAzUdff2ZNrUcOr6VhYs3pg== ------END RSA PRIVATE KEY----- diff --git a/test-keys/server-key.pem b/test-keys/server-key.pem deleted file mode 100644 index 3b190ddcb0..0000000000 --- a/test-keys/server-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAtG7WVaF0ehVULruqLtNcxmWpzP0X+NmunbT5uaFKIW2xw07H -zq62eqeh3QZ9QhOK651FRRnfTfYBjERox3hdjLhdBNzxci5oxxWfC74aCala278z -vkmbGuSqjfo4jPxer47hQ2JwPSAuG8FoPPEqF2L3cwZaQY62fo15k07u4x5Jho8J -U4+ARchZlwXAWOXaw/MgMSmPIe3NL3Wsvq+eKFLjWrLeG/nWuqm8kzr0pGhqXn0l -ab4n2eVI8co+/lORKY8UqvW4vM+3qmfzVzKy6nwcF2ry6orXWWyM/irHUSbOBhEW -vBV9+bBpLfX7x5XNSDsIv7o20bXoi0BPS5Mt9wIDAQABAoIBAQCXW2Lw8j+DKeE0 -UriwDx4ET8Pg8W7qkbCpGudhkKte32X/MFbsSiNJQNXHU2o/w6UFgShajxmDjJo/ -0CMVMSNIqF9fdPgVkFjUqI6CizXMZQSS0hHVzmkOZkOsVfqaShojqvfMTwdYP2Zu -Yg/PD37/n0V0eB++Xrk37/XMXKqIWbcmScR+YZHmuVcPVy+HST0irSYS5bCPp0CY -6fn2tYlGnv9zbEGxX2vYwJkLYZZ+w7zJP92l6Wsb+UesF3oVeHrqZwqR3H0UUbBl -XyXtiVKSrswsMGgAbPwygCYKWvrsYpNUqjGAfKc0aegLAK/zugBKD+pMYf5A4NT7 -IcobZzIBAoGBANwBwoIqy4fq0D1u+qfRFZAfVz11/ysVmW1MXWJbzRZkRJ7uCwgz -VlyCR/1RzEAKgDaudvS2jWPbADrxhMXZCZH6JvnjVoDvp/tH45VUlcXacbJzTX6T -485toMHZtTe3pCQc6Da/OxFRbjZrpM1BUaIMi5olMC17qzbgz4P9rPQNAoGBANHz -qJD0u4y7LXdPdvwTnoBhW3ju92nCm4ahCLSgpu0cYWMI6b+3m+QN2xuWGGtvd0XO -cBrEvaZ3bHz9JWg/R4lN8dd1yEZ5o/0Y1WBV1r3YTetz2gH4FQUyvfhkh2ropYiS -vsAy4MvLpLXwnmSFLUbuH9cTxsiAz+HQoVjgmBUTAoGAQKb3vP7Mfu2fMl55pHVK -C+dZ4MLbMJMRWlA1pSDrHOhsTfQQI+HKQDc85pFyH2O2l9sfM/ytgimqYKf255lH -ObG0YHzRP+Stjadrlsebl+AAx3sgy9C0AkavFihfG7eIseAY0XXS6tzuMWnirTrD -vYZIRA0nokYUY3UfJl+WwsUCgYA4xtbQFI7fmQLLoB7CPp//gdSV2LHp1OnwN34+ -Mq/RsXoYqSFlakbrHmAhjq6d5y/vHcutQYU0Dlm1V1QVY/95fy2mocB+ZojVejDB -85S2FFfGE7diDu/ITxeWo8EHRVD2pRCWs2udUT4CwcZj0qwS4XSU2lFIiaUuIRhx -C5O1JwKBgQDE3hwprPAunMNlfJD4nWXDn9aLF+4kP+vxKxJ95GeG0lpFYYnMEb5R -xp3wPS8u6mC0f90REe28/Y6s9HRXc9VOwa+/jA3Ye6MexiQimDYW1ClnOp2gRoq8 -KM4xtWWjzEVkEKIEtZKn+X78iP03+U/TAzUdff2ZNrUcOr6VhYs3pg== ------END RSA PRIVATE KEY----- diff --git a/test-keys/server-keystore.p12 b/test-keys/server-keystore.p12 deleted file mode 100644 index a24b53cd42..0000000000 Binary files a/test-keys/server-keystore.p12 and /dev/null differ diff --git a/test-keys/server-pkcs8.pem b/test-keys/server-pkcs8.pem deleted file mode 100644 index e587f4d1b6..0000000000 --- a/test-keys/server-pkcs8.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0btZVoXR6FVQu -u6ou01zGZanM/Rf42a6dtPm5oUohbbHDTsfOrrZ6p6HdBn1CE4rrnUVFGd9N9gGM -RGjHeF2MuF0E3PFyLmjHFZ8LvhoJqVrbvzO+SZsa5KqN+jiM/F6vjuFDYnA9IC4b -wWg88SoXYvdzBlpBjrZ+jXmTTu7jHkmGjwlTj4BFyFmXBcBY5drD8yAxKY8h7c0v -day+r54oUuNast4b+da6qbyTOvSkaGpefSVpvifZ5Ujxyj7+U5EpjxSq9bi8z7eq -Z/NXMrLqfBwXavLqitdZbIz+KsdRJs4GERa8FX35sGkt9fvHlc1IOwi/ujbRteiL -QE9Lky33AgMBAAECggEBAJdbYvDyP4Mp4TRSuLAPHgRPw+DxbuqRsKka52GQq17f -Zf8wVuxKI0lA1cdTaj/DpQWBKFqPGYOMmj/QIxUxI0ioX190+BWQWNSojoKLNcxl -BJLSEdXOaQ5mQ6xV+ppKGiOq98xPB1g/Zm5iD88Pfv+fRXR4H75euTfv9cxcqohZ -tyZJxH5hkea5Vw9XL4dJPSKtJhLlsI+nQJjp+fa1iUae/3NsQbFfa9jAmQthln7D -vMk/3aXpaxv5R6wXehV4eupnCpHcfRRRsGVfJe2JUpKuzCwwaABs/DKAJgpa+uxi -k1SqMYB8pzRp6AsAr/O6AEoP6kxh/kDg1PshyhtnMgECgYEA3AHCgirLh+rQPW76 -p9EVkB9XPXX/KxWZbUxdYlvNFmREnu4LCDNWXIJH/VHMQAqANq529LaNY9sAOvGE -xdkJkfom+eNWgO+n+0fjlVSVxdpxsnNNfpPjzm2gwdm1N7ekJBzoNr87EVFuNmuk -zUFRogyLmiUwLXurNuDPg/2s9A0CgYEA0fOokPS7jLstd092/BOegGFbeO73acKb -hqEItKCm7RxhYwjpv7eb5A3bG5YYa293Rc5wGsS9pndsfP0laD9HiU3x13XIRnmj -/RjVYFXWvdhN63PaAfgVBTK9+GSHauiliJK+wDLgy8uktfCeZIUtRu4f1xPGyIDP -4dChWOCYFRMCgYBApve8/sx+7Z8yXnmkdUoL51ngwtswkxFaUDWlIOsc6GxN9BAj -4cpANzzmkXIfY7aX2x8z/K2CKapgp/bnmUc5sbRgfNE/5K2Np2uWx5uX4ADHeyDL -0LQCRq8WKF8bt4ix4BjRddLq3O4xaeKtOsO9hkhEDSeiRhRjdR8mX5bCxQKBgDjG -1tAUjt+ZAsugHsI+n/+B1JXYsenU6fA3fj4yr9GxehipIWVqRuseYCGOrp3nL+8d -y61BhTQOWbVXVBVj/3l/LaahwH5miNV6MMHzlLYUV8YTt2IO78hPF5ajwQdFUPal -EJaza51RPgLBxmPSrBLhdJTaUUiJpS4hGHELk7UnAoGBAMTeHCms8C6cw2V8kPid -ZcOf1osX7iQ/6/ErEn3kZ4bSWkVhicwRvlHGnfA9Ly7qYLR/3RER7bz9jqz0dFdz -1U7Br7+MDdh7ox7GJCKYNhbUKWc6naBGirwozjG1ZaPMRWQQogS1kqf5fvyI/Tf5 -T9MDNR19/Zk2tRw6vpWFizem ------END PRIVATE KEY----- diff --git a/vendor/github.com/github/smimesign/LICENSE.md b/vendor/github.com/github/smimesign/LICENSE.md deleted file mode 100644 index fe6ededa81..0000000000 --- a/vendor/github.com/github/smimesign/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 GitHub, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/github/smimesign/fakeca/LICENSE.md b/vendor/github.com/github/smimesign/fakeca/LICENSE.md deleted file mode 100644 index 7800c58b40..0000000000 --- a/vendor/github.com/github/smimesign/fakeca/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Ben Toews. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/github/smimesign/fakeca/README.md b/vendor/github.com/github/smimesign/fakeca/README.md deleted file mode 100644 index 6a7db21341..0000000000 --- a/vendor/github.com/github/smimesign/fakeca/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# fakeca [](https://pkg.go.dev/github.com/github/smimesign/fakeca?tab=doc) - -This is a package for creating fake certificate authorities for test fixtures. - -## Example - -```go -package main - -import ( - "crypto/x509/pkix" - - "github.com/github/smimesign/fakeca" -) - -func main() { - // Change defaults for cert subjects. - fakeca.DefaultProvince = []string{"CO"} - fakeca.DefaultLocality = []string{"Denver"} - - // Create a root CA. - root := fakeca.New(fakeca.IsCA, fakeca.Subject(pkix.Name{ - CommonName: "root.myorg.com", - })) - - // Create an intermediate CA under the root. - intermediate := root.Issue(fakeca.IsCA, fakeca.Subject(pkix.Name{ - CommonName: "intermediate.myorg.com", - })) - - // Create a leaf certificate under the intermediate. - leaf := intermediate.Issue(fakeca.Subject(pkix.Name{ - CommonName: "leaf.myorg.com", - })) - - // Get PFX (PKCS12) blob containing certificate and encrypted private key. - leafPFX := leaf.PFX("pa55w0rd") - - // Get an *x509.CertPool containing certificate chain from CA to leaf for use - // with Go's TLS libraries. - leafPool := leaf.ChainPool() -} - -``` diff --git a/vendor/github.com/github/smimesign/fakeca/configuration.go b/vendor/github.com/github/smimesign/fakeca/configuration.go deleted file mode 100644 index 7e1be3a75c..0000000000 --- a/vendor/github.com/github/smimesign/fakeca/configuration.go +++ /dev/null @@ -1,240 +0,0 @@ -package fakeca - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "fmt" - "math" - "math/big" - "time" -) - -type configuration struct { - subject *pkix.Name - issuer *Identity - nextSN *int64 - priv *crypto.Signer - isCA bool - notBefore *time.Time - notAfter *time.Time - issuingCertificateURL []string - ocspServer []string - keyUsage x509.KeyUsage -} - -func (c *configuration) generate() *Identity { - templ := &x509.Certificate{ - Subject: c.getSubject(), - IsCA: c.isCA, - BasicConstraintsValid: true, - NotAfter: c.getNotAfter(), - NotBefore: c.getNotBefore(), - IssuingCertificateURL: c.issuingCertificateURL, - OCSPServer: c.ocspServer, - KeyUsage: c.keyUsage, - } - - var ( - parent *x509.Certificate - thisPriv = c.getPrivateKey() - priv crypto.Signer - ) - - if c.issuer != nil { - parent = c.issuer.Certificate - templ.SerialNumber = big.NewInt(c.issuer.IncrementSN()) - priv = c.issuer.PrivateKey - } else { - parent = templ - templ.SerialNumber = randSN() - priv = thisPriv - } - - der, err := x509.CreateCertificate(rand.Reader, templ, parent, thisPriv.Public(), priv) - if err != nil { - panic(err) - } - - cert, err := x509.ParseCertificate(der) - if err != nil { - panic(err) - } - - return &Identity{ - Certificate: cert, - PrivateKey: thisPriv, - Issuer: c.issuer, - NextSN: c.getNextSN(), - } -} - -var ( - // DefaultCountry is the default subject Country. - DefaultCountry = []string{"US"} - - // DefaultProvince is the default subject Province. - DefaultProvince = []string{"CA"} - - // DefaultLocality is the default subject Locality. - DefaultLocality = []string{"San Francisco"} - - // DefaultStreetAddress is the default subject StreetAddress. - DefaultStreetAddress = []string(nil) - - // DefaultPostalCode is the default subject PostalCode. - DefaultPostalCode = []string(nil) - - // DefaultCommonName is the default subject CommonName. - DefaultCommonName = "fakeca" - - cnCounter int64 -) - -func (c *configuration) getSubject() pkix.Name { - if c.subject != nil { - return *c.subject - } - - var cn string - if cnCounter == 0 { - cn = DefaultCommonName - } else { - cn = fmt.Sprintf("%s #%d", DefaultCommonName, cnCounter) - } - cnCounter++ - - return pkix.Name{ - Country: DefaultCountry, - Province: DefaultProvince, - Locality: DefaultLocality, - StreetAddress: DefaultStreetAddress, - PostalCode: DefaultPostalCode, - CommonName: cn, - } -} - -func (c *configuration) getNextSN() int64 { - if c.nextSN == nil { - sn := randSN().Int64() - c.nextSN = &sn - } - - return *c.nextSN -} - -func randSN() *big.Int { - i, err := rand.Int(rand.Reader, big.NewInt(int64(math.MaxInt64))) - if err != nil { - panic(err) - } - - return i -} - -func (c *configuration) getPrivateKey() crypto.Signer { - if c.priv == nil { - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - - signer := crypto.Signer(priv) - - c.priv = &signer - } - - return *c.priv -} - -func (c *configuration) getNotBefore() time.Time { - if c.notBefore == nil { - return time.Unix(0, 0) - } - - return *c.notBefore -} - -func (c *configuration) getNotAfter() time.Time { - if c.notAfter == nil { - return time.Now().Add(time.Hour * 24 * 365 * 10) - } - - return *c.notAfter -} - -// Option is an option that can be passed to New(). -type Option option -type option func(c *configuration) - -// Subject is an Option that sets a identity's subject field. -func Subject(value pkix.Name) Option { - return func(c *configuration) { - c.subject = &value - } -} - -// NextSerialNumber is an Option that determines the SN of the next issued -// certificate. -func NextSerialNumber(value int64) Option { - return func(c *configuration) { - c.nextSN = &value - } -} - -// PrivateKey is an Option for setting the identity's private key. -func PrivateKey(value crypto.Signer) Option { - return func(c *configuration) { - c.priv = &value - } -} - -// Issuer is an Option for setting the identity's issuer. -func Issuer(value *Identity) Option { - return func(c *configuration) { - c.issuer = value - } -} - -// NotBefore is an Option for setting the identity's certificate's NotBefore. -func NotBefore(value time.Time) Option { - return func(c *configuration) { - c.notBefore = &value - } -} - -// NotAfter is an Option for setting the identity's certificate's NotAfter. -func NotAfter(value time.Time) Option { - return func(c *configuration) { - c.notAfter = &value - } -} - -// IssuingCertificateURL is an Option for setting the identity's certificate's -// IssuingCertificateURL. -func IssuingCertificateURL(value ...string) Option { - return func(c *configuration) { - c.issuingCertificateURL = append(c.issuingCertificateURL, value...) - } -} - -// OCSPServer is an Option for setting the identity's certificate's OCSPServer. -func OCSPServer(value ...string) Option { - return func(c *configuration) { - c.ocspServer = append(c.ocspServer, value...) - } -} - -// KeyUsage is an Option for setting the identity's certificate's KeyUsage. -func KeyUsage(ku x509.KeyUsage) Option { - return func(c *configuration) { - c.keyUsage = ku - } -} - -// IsCA is an Option for making an identity a certificate authority. -var IsCA Option = func(c *configuration) { - c.isCA = true -} diff --git a/vendor/github.com/github/smimesign/fakeca/identity.go b/vendor/github.com/github/smimesign/fakeca/identity.go deleted file mode 100644 index 19ec9a4d6f..0000000000 --- a/vendor/github.com/github/smimesign/fakeca/identity.go +++ /dev/null @@ -1,144 +0,0 @@ -package fakeca - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "os/exec" -) - -// Identity is a certificate and private key. -type Identity struct { - Issuer *Identity - PrivateKey crypto.Signer - Certificate *x509.Certificate - NextSN int64 -} - -// New creates a new CA. -func New(opts ...Option) *Identity { - c := &configuration{} - - for _, opt := range opts { - option(opt)(c) - } - - return c.generate() -} - -// Issue issues a new Identity with this one as its parent. -func (id *Identity) Issue(opts ...Option) *Identity { - opts = append(opts, Issuer(id)) - return New(opts...) -} - -// PFX wraps the certificate and private key in an encrypted PKCS#12 packet. The -// provided password must be alphanumeric. -func (id *Identity) PFX(password string) []byte { - return toPFX(id.Certificate, id.PrivateKey, password) -} - -// Chain builds a slice of *x509.Certificate from this CA and its issuers. -func (id *Identity) Chain() []*x509.Certificate { - chain := []*x509.Certificate{} - for this := id; this != nil; this = this.Issuer { - chain = append(chain, this.Certificate) - } - - return chain -} - -// ChainPool builds an *x509.CertPool from this CA and its issuers. -func (id *Identity) ChainPool() *x509.CertPool { - chain := x509.NewCertPool() - for this := id; this != nil; this = this.Issuer { - chain.AddCert(this.Certificate) - } - - return chain -} - -// IncrementSN returns the next serial number. -func (id *Identity) IncrementSN() int64 { - defer func() { - id.NextSN++ - }() - - return id.NextSN -} - -func toPFX(cert *x509.Certificate, priv interface{}, password string) []byte { - // only allow alphanumeric passwords - for _, c := range password { - switch { - case c >= 'a' && c <= 'z': - case c >= 'A' && c <= 'Z': - case c >= '0' && c <= '9': - default: - panic("password must be alphanumeric") - } - } - - passout := fmt.Sprintf("pass:%s", password) - cmd := exec.Command("openssl", "pkcs12", "-export", "-passout", passout) - - cmd.Stdin = bytes.NewReader(append(append(toPKCS8(priv), '\n'), toPEM(cert)...)) - - out := new(bytes.Buffer) - cmd.Stdout = out - - if err := cmd.Run(); err != nil { - panic(err) - } - - return out.Bytes() -} - -func toPEM(cert *x509.Certificate) []byte { - buf := new(bytes.Buffer) - if err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil { - panic(err) - } - - return buf.Bytes() -} - -func toDER(priv interface{}) []byte { - var ( - der []byte - err error - ) - switch p := priv.(type) { - case *rsa.PrivateKey: - der = x509.MarshalPKCS1PrivateKey(p) - case *ecdsa.PrivateKey: - der, err = x509.MarshalECPrivateKey(p) - default: - err = errors.New("unknown key type") - } - if err != nil { - panic(err) - } - - return der -} - -func toPKCS8(priv interface{}) []byte { - cmd := exec.Command("openssl", "pkcs8", "-topk8", "-nocrypt", "-inform", "DER") - - cmd.Stdin = bytes.NewReader(toDER(priv)) - - out := new(bytes.Buffer) - cmd.Stdout = out - - if err := cmd.Run(); err != nil { - panic(err) - } - - return out.Bytes() -} diff --git a/vendor/github.com/magefile/mage/.gitattributes b/vendor/github.com/magefile/mage/.gitattributes new file mode 100644 index 0000000000..571c1ad444 --- /dev/null +++ b/vendor/github.com/magefile/mage/.gitattributes @@ -0,0 +1,2 @@ +site/* linguist-documentation +vendor/* linguist-vendored \ No newline at end of file diff --git a/vendor/github.com/magefile/mage/.gitignore b/vendor/github.com/magefile/mage/.gitignore new file mode 100644 index 0000000000..8d101a18d1 --- /dev/null +++ b/vendor/github.com/magefile/mage/.gitignore @@ -0,0 +1,38 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ + +# Magefile output +mage_output_file.go + +# VScode +.vscode + +# stupid osx +.DS_Store + +# Goland +.idea +*.iml + +# Vim +*.sw[op] +Session.vim +*~ + +# GNU Screen +.screenrc + +# Hugo build lock +.hugo_build.lock diff --git a/vendor/github.com/magefile/mage/.goreleaser.yml b/vendor/github.com/magefile/mage/.goreleaser.yml new file mode 100644 index 0000000000..4123b2dc2e --- /dev/null +++ b/vendor/github.com/magefile/mage/.goreleaser.yml @@ -0,0 +1,53 @@ +project_name: mage +release: + github: + owner: magefile + name: mage + draft: true +build: + binary: mage + main: . + ldflags: -s -w -X github.com/magefile/mage/mage.timestamp={{.Date}} -X github.com/magefile/mage/mage.commitHash={{.Commit}} -X github.com/magefile/mage/mage.gitTag={{.Version}} + goos: + - darwin + - linux + - windows + - freebsd + - netbsd + - openbsd + - dragonfly + goarch: + - amd64 + - arm + - arm64 + ignore: + - goos: openbsd + goarch: arm + goarm: 6 + env: + - CGO_ENABLED=0 +archives: +- + name_template: "{{.Binary}}_{{.Version}}_{{.Os}}-{{.Arch}}" + replacements: + amd64: 64bit + 386: 32bit + arm: ARM + arm64: ARM64 + darwin: macOS + linux: Linux + windows: Windows + openbsd: OpenBSD + netbsd: NetBSD + freebsd: FreeBSD + dragonfly: DragonFlyBSD + format: tar.gz + format_overrides: + - goos: windows + format: zip + files: + - LICENSE +snapshot: + name_template: SNAPSHOT-{{ .Commit }} +checksum: + name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt' diff --git a/vendor/github.com/magefile/mage/CODE_OF_CONDUCT.md b/vendor/github.com/magefile/mage/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..10b331a51d --- /dev/null +++ b/vendor/github.com/magefile/mage/CODE_OF_CONDUCT.md @@ -0,0 +1,85 @@ +# Mage Community Code of Conduct + +This is a copy of the [go code of conduct](https://golang.org/conduct). It applies to all areas of the magefile github organization, the #mage and #mage-dev slack channels on gopher slack, and the mage google group (https://groups.google.com/forum/#!forum/magefile). + +Reports may be directed to Nate Finch, the Mage Project Steward at nate.finch@gmail.com, or Carmen Andoh and Van Riper, the Go Project Stewards at conduct@golang.org. + +## About +Online communities include people from many different backgrounds. The Go contributors are committed to providing a friendly, safe and welcoming environment for all, regardless of gender identity and expression, sexual orientation, disabilities, neurodiversity, physical appearance, body size, ethnicity, nationality, race, age, religion, or similar personal characteristics. + +The first goal of the Code of Conduct is to specify a baseline standard of behavior so that people with different social values and communication styles can talk about Go effectively, productively, and respectfully. + +The second goal is to provide a mechanism for resolving conflicts in the community when they arise. + +The third goal of the Code of Conduct is to make our community welcoming to people from different backgrounds. Diversity is critical to the project; for Go to be successful, it needs contributors and users from all backgrounds. (See Go, Open Source, Community.) + +We believe that healthy debate and disagreement are essential to a healthy project and community. However, it is never ok to be disrespectful. We value diverse opinions, but we value respectful behavior more. + +## Gopher values +These are the values to which people in the Go community (“Gophers”) should aspire. + +## Be friendly and welcoming +* Be patient + * Remember that people have varying communication styles and that not everyone is using their native language. (Meaning and tone can be lost in translation.) +* Be thoughtful + * Productive communication requires effort. Think about how your words will be interpreted. + * Remember that sometimes it is best to refrain entirely from commenting. +* Be respectful + * In particular, respect differences of opinion. +* Be charitable + * Interpret the arguments of others in good faith, do not seek to disagree. + * When we do disagree, try to understand why. +* Avoid destructive behavior: + * Derailing: stay on topic; if you want to talk about something else, start a new conversation. + * Unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved. + * Snarking (pithy, unproductive, sniping comments) + * Discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict. + * Microaggressions: brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group. +People are complicated. You should expect to be misunderstood and to misunderstand others; when this inevitably occurs, resist the urge to be defensive or assign blame. Try not to take offense where no offense was intended. Give people the benefit of the doubt. Even if the intent was to provoke, do not rise to it. It is the responsibility of all parties to de-escalate conflict when it arises. + +## Code of Conduct +### Our Pledge +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +### Our Standards +Examples of behavior that contributes to creating a positive environment include: + +Using welcoming and inclusive language +Being respectful of differing viewpoints and experiences +Gracefully accepting constructive criticism +Focusing on what is best for the community +Showing empathy towards other community members +Examples of unacceptable behavior by participants include: + +The use of sexualized language or imagery and unwelcome sexual attention or advances +Trolling, insulting/derogatory comments, and personal or political attacks +Public or private harassment +Publishing others’ private information, such as a physical or electronic address, without explicit permission +Other conduct which could reasonably be considered inappropriate in a professional setting +Our Responsibilities +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +### Scope +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when the Project Stewards have a reasonable belief that an individual’s behavior may have a negative impact on the project or its community. + +### Conflict Resolution +We do not believe that all conflict is bad; healthy debate and disagreement often yield positive results. However, it is never okay to be disrespectful or to engage in behavior that violates the project’s code of conduct. + +If you see someone violating the code of conduct, you are encouraged to address the behavior directly with those involved. Many issues can be resolved quickly and easily, and this gives people more control over the outcome of their dispute. If you are unable to resolve the matter for any reason, or if the behavior is threatening or harassing, report it. We are dedicated to providing an environment where participants feel welcome and safe. + +Reports should be directed to Carmen Andoh and Van Riper, the Go Project Stewards, at conduct@golang.org \[or Nate Finch at nate.finch@gmail.com\]. It is the Project Stewards’ duty to receive and address reported violations of the code of conduct. They will then work with a committee consisting of representatives from the Open Source Programs Office and the Google Open Source Strategy team. If for any reason you are uncomfortable reaching out the Project Stewards, please email the Google Open Source Programs Office at opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. We will use our discretion in determining when and how to follow up on reported incidents, which may range from not taking action to permanent expulsion from the project and project-sponsored spaces. We will notify the accused of the report and provide them an opportunity to discuss it before any action is taken. The identity of the reporter will be omitted from the details of the report supplied to the accused. In potentially harmful situations, such as ongoing harassment or threats to anyone’s safety, we may take action without notice. + +### Attribution +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +## Summary +* Treat everyone with respect and kindness. +* Be thoughtful in how you communicate. +* Don’t be destructive or inflammatory. +* If you encounter an issue, please mail conduct@golang.org. diff --git a/vendor/github.com/magefile/mage/CONTRIBUTING.md b/vendor/github.com/magefile/mage/CONTRIBUTING.md new file mode 100644 index 0000000000..e1394d2045 --- /dev/null +++ b/vendor/github.com/magefile/mage/CONTRIBUTING.md @@ -0,0 +1,42 @@ +# Contributing + +Of course, contributions are more than welcome. Please read these guidelines for +making the process as painless as possible. + +## Discussion + +Development discussion should take place on the #mage channel of [gopher +slack](https://gophers.slack.com/). + +There is a separate #mage-dev channel that has the github app to post github +activity to the channel, to make it easy to follow. + +## Issues + +If there's an issue you'd like to work on, please comment on it, so we can +discuss approach, etc. and make sure no one else is currently working on that +issue. + +Please always create an issue before sending a PR unless it's an obvious typo +or other trivial change. + +## Dependency Management + +Currently mage has no dependencies(!) outside the standard libary. Let's keep +it that way. Since it's likely that mage will be vendored into a project, +adding dependencies to mage adds dependencies to every project that uses mage. + +## Versions + +Please avoid using features of go and the stdlib that prevent mage from being +buildable with older versions of Go. The CI tests currently check that mage is +buildable with go 1.7 and later. You may build with whatever version you like, +but CI has the final say. + +## Testing + +Please write tests for any new features. Tests must use the normal go testing +package. + +Tests must pass the race detector (run `go test -race ./...`). + diff --git a/vendor/github.com/magefile/mage/README.md b/vendor/github.com/magefile/mage/README.md new file mode 100644 index 0000000000..d96fd025a7 --- /dev/null +++ b/vendor/github.com/magefile/mage/README.md @@ -0,0 +1,81 @@ +[](https://magefile.org) +[](https://travis-ci.org/magefile/mage) [](https://ci.appveyor.com/project/natefinch/mage/branch/master) + +

](https://www.patreon.com/join/natefinch?)
+
diff --git a/vendor/github.com/magefile/mage/internal/run.go b/vendor/github.com/magefile/mage/internal/run.go
new file mode 100644
index 0000000000..79b4f049ac
--- /dev/null
+++ b/vendor/github.com/magefile/mage/internal/run.go
@@ -0,0 +1,115 @@
+package internal
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "runtime"
+ "strings"
+)
+
+var debug *log.Logger = log.New(ioutil.Discard, "", 0)
+
+func SetDebug(l *log.Logger) {
+ debug = l
+}
+
+func RunDebug(cmd string, args ...string) error {
+ env, err := EnvWithCurrentGOOS()
+ if err != nil {
+ return err
+ }
+ buf := &bytes.Buffer{}
+ errbuf := &bytes.Buffer{}
+ debug.Println("running", cmd, strings.Join(args, " "))
+ c := exec.Command(cmd, args...)
+ c.Env = env
+ c.Stderr = errbuf
+ c.Stdout = buf
+ if err := c.Run(); err != nil {
+ debug.Print("error running '", cmd, strings.Join(args, " "), "': ", err, ": ", errbuf)
+ return err
+ }
+ debug.Println(buf)
+ return nil
+}
+
+func OutputDebug(cmd string, args ...string) (string, error) {
+ env, err := EnvWithCurrentGOOS()
+ if err != nil {
+ return "", err
+ }
+ buf := &bytes.Buffer{}
+ errbuf := &bytes.Buffer{}
+ debug.Println("running", cmd, strings.Join(args, " "))
+ c := exec.Command(cmd, args...)
+ c.Env = env
+ c.Stderr = errbuf
+ c.Stdout = buf
+ if err := c.Run(); err != nil {
+ errMsg := strings.TrimSpace(errbuf.String())
+ debug.Print("error running '", cmd, strings.Join(args, " "), "': ", err, ": ", errMsg)
+ return "", fmt.Errorf("error running \"%s %s\": %s\n%s", cmd, strings.Join(args, " "), err, errMsg)
+ }
+ return strings.TrimSpace(buf.String()), nil
+}
+
+// SplitEnv takes the results from os.Environ() (a []string of foo=bar values)
+// and makes a map[string]string out of it.
+func SplitEnv(env []string) (map[string]string, error) {
+ out := map[string]string{}
+
+ for _, s := range env {
+ parts := strings.SplitN(s, "=", 2)
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("badly formatted environment variable: %v", s)
+ }
+ out[parts[0]] = parts[1]
+ }
+ return out, nil
+}
+
+// joinEnv converts the given map into a list of foo=bar environment variables,
+// such as that outputted by os.Environ().
+func joinEnv(env map[string]string) []string {
+ vals := make([]string, 0, len(env))
+ for k, v := range env {
+ vals = append(vals, k+"="+v)
+ }
+ return vals
+}
+
+// EnvWithCurrentGOOS returns a copy of os.Environ with the GOOS and GOARCH set
+// to runtime.GOOS and runtime.GOARCH.
+func EnvWithCurrentGOOS() ([]string, error) {
+ vals, err := SplitEnv(os.Environ())
+ if err != nil {
+ return nil, err
+ }
+ vals["GOOS"] = runtime.GOOS
+ vals["GOARCH"] = runtime.GOARCH
+ return joinEnv(vals), nil
+}
+
+// EnvWithGOOS retuns the os.Environ() values with GOOS and/or GOARCH either set
+// to their runtime value, or the given value if non-empty.
+func EnvWithGOOS(goos, goarch string) ([]string, error) {
+ env, err := SplitEnv(os.Environ())
+ if err != nil {
+ return nil, err
+ }
+ if goos == "" {
+ env["GOOS"] = runtime.GOOS
+ } else {
+ env["GOOS"] = goos
+ }
+ if goarch == "" {
+ env["GOARCH"] = runtime.GOARCH
+ } else {
+ env["GOARCH"] = goarch
+ }
+ return joinEnv(env), nil
+}
diff --git a/vendor/github.com/magefile/mage/mage/command_string.go b/vendor/github.com/magefile/mage/mage/command_string.go
new file mode 100644
index 0000000000..fcdaf9f949
--- /dev/null
+++ b/vendor/github.com/magefile/mage/mage/command_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=Command"; DO NOT EDIT.
+
+package mage
+
+import "strconv"
+
+const _Command_name = "NoneVersionInitCleanCompileStatic"
+
+var _Command_index = [...]uint8{0, 4, 11, 15, 20, 33}
+
+func (i Command) String() string {
+ if i < 0 || i >= Command(len(_Command_index)-1) {
+ return "Command(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Command_name[_Command_index[i]:_Command_index[i+1]]
+}
diff --git a/vendor/github.com/magefile/mage/mage/magefile_tmpl.go b/vendor/github.com/magefile/mage/mage/magefile_tmpl.go
new file mode 100644
index 0000000000..1df9703792
--- /dev/null
+++ b/vendor/github.com/magefile/mage/mage/magefile_tmpl.go
@@ -0,0 +1,47 @@
+package mage
+
+var mageTpl = `//go:build mage
+// +build mage
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+
+ "github.com/magefile/mage/mg" // mg contains helpful utility functions, like Deps
+)
+
+// Default target to run when none is specified
+// If not set, running mage will list available targets
+// var Default = Build
+
+// A build step that requires additional params, or platform specific steps for example
+func Build() error {
+ mg.Deps(InstallDeps)
+ fmt.Println("Building...")
+ cmd := exec.Command("go", "build", "-o", "MyApp", ".")
+ return cmd.Run()
+}
+
+// A custom install step if you need your bin someplace other than go/bin
+func Install() error {
+ mg.Deps(Build)
+ fmt.Println("Installing...")
+ return os.Rename("./MyApp", "/usr/bin/MyApp")
+}
+
+// Manage your deps, or running package managers.
+func InstallDeps() error {
+ fmt.Println("Installing Deps...")
+ cmd := exec.Command("go", "get", "github.com/stretchr/piglatin")
+ return cmd.Run()
+}
+
+// Clean up after yourself
+func Clean() {
+ fmt.Println("Cleaning...")
+ os.RemoveAll("MyApp")
+}
+`
diff --git a/vendor/github.com/magefile/mage/mage/main.go b/vendor/github.com/magefile/mage/mage/main.go
new file mode 100644
index 0000000000..0062bd35aa
--- /dev/null
+++ b/vendor/github.com/magefile/mage/mage/main.go
@@ -0,0 +1,779 @@
+package mage
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "errors"
+ "flag"
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "sort"
+ "strings"
+ "syscall"
+ "text/template"
+ "time"
+
+ "github.com/magefile/mage/internal"
+ "github.com/magefile/mage/mg"
+ "github.com/magefile/mage/parse"
+ "github.com/magefile/mage/sh"
+)
+
+// magicRebuildKey is used when hashing the output binary to ensure that we get
+// a new binary even if nothing in the input files or generated mainfile has
+// changed. This can be used when we change how we parse files, or otherwise
+// change the inputs to the compiling process.
+const magicRebuildKey = "v0.3"
+
+// (Aaaa)(Bbbb) -> aaaaBbbb
+var firstWordRx = regexp.MustCompile(`^([[:upper:]][^[:upper:]]+)([[:upper:]].*)$`)
+
+// (AAAA)(Bbbb) -> aaaaBbbb
+var firstAbbrevRx = regexp.MustCompile(`^([[:upper:]]+)([[:upper:]][^[:upper:]].*)$`)
+
+func lowerFirstWord(s string) string {
+ if match := firstWordRx.FindStringSubmatch(s); match != nil {
+ return strings.ToLower(match[1]) + match[2]
+ }
+ if match := firstAbbrevRx.FindStringSubmatch(s); match != nil {
+ return strings.ToLower(match[1]) + match[2]
+ }
+ return strings.ToLower(s)
+}
+
+var mainfileTemplate = template.Must(template.New("").Funcs(map[string]interface{}{
+ "lower": strings.ToLower,
+ "lowerFirst": func(s string) string {
+ parts := strings.Split(s, ":")
+ for i, t := range parts {
+ parts[i] = lowerFirstWord(t)
+ }
+ return strings.Join(parts, ":")
+ },
+}).Parse(mageMainfileTplString))
+var initOutput = template.Must(template.New("").Parse(mageTpl))
+
+const (
+ mainfile = "mage_output_file.go"
+ initFile = "magefile.go"
+)
+
+var debug = log.New(ioutil.Discard, "DEBUG: ", log.Ltime|log.Lmicroseconds)
+
+// set by ldflags when you "mage build"
+var (
+ commitHash = "