diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 37fcfb6..c4df6e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,9 +7,9 @@ on: workflow_dispatch: inputs: tag_name: - description: "Release tag (e.g. v1.0.0)" + description: "Release tag (e.g. v1.0.1)" required: true - default: "v1.0.0" + default: "v1.0.1" jobs: build: @@ -19,18 +19,23 @@ jobs: include: - goos: linux goarch: amd64 + os_name: linux suffix: "" - goos: linux goarch: arm64 + os_name: linux suffix: "" - goos: darwin goarch: amd64 + os_name: macos suffix: "" - goos: darwin goarch: arm64 + os_name: macos suffix: "" - goos: windows goarch: amd64 + os_name: windows suffix: ".exe" steps: @@ -42,6 +47,10 @@ jobs: go-version-file: go.mod cache: true + - name: Set version + id: ver + run: echo "tag=${{ github.event_name == 'workflow_dispatch' && inputs.tag_name || github.ref_name }}" >> "$GITHUB_OUTPUT" + - name: Build env: GOOS: ${{ matrix.goos }} @@ -49,12 +58,12 @@ jobs: CGO_ENABLED: "0" run: | go build -trimpath -ldflags="-s -w" \ - -o httpmon-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.suffix }} . + -o httpmon-${{ steps.ver.outputs.tag }}-${{ matrix.os_name }}-${{ matrix.goarch }}${{ matrix.suffix }} . - uses: actions/upload-artifact@v4 with: - name: httpmon-${{ matrix.goos }}-${{ matrix.goarch }} - path: httpmon-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.suffix }} + name: httpmon-${{ matrix.os_name }}-${{ matrix.goarch }} + path: httpmon-${{ steps.ver.outputs.tag }}-${{ matrix.os_name }}-${{ matrix.goarch }}${{ matrix.suffix }} release: needs: build diff --git a/README.md b/README.md index b0c8007..98f1a78 100644 --- a/README.md +++ b/README.md @@ -60,15 +60,16 @@ Download the latest binary for your platform from the [Releases](https://github. | Platform | File | |----------|------| -| macOS arm64 | `httpmon-darwin-arm64` | -| macOS amd64 | `httpmon-darwin-amd64` | -| Linux amd64 | `httpmon-linux-amd64` | -| Linux arm64 | `httpmon-linux-arm64` | -| Windows amd64 | `httpmon-windows-amd64.exe` | +| macOS Apple Silicon (M1/M2/M3) | `httpmon--macos-arm64` | +| macOS Intel | `httpmon--macos-amd64` | +| Linux x86-64 | `httpmon--linux-amd64` | +| Linux arm64 (Graviton, Pi) | `httpmon--linux-arm64` | +| Windows x86-64 | `httpmon--windows-amd64.exe` | ```bash -chmod +x httpmon-darwin-arm64 -sudo mv httpmon-darwin-arm64 /usr/local/bin/httpmon +# macOS / Linux — replace with the release tag, e.g. v1.0.1 +chmod +x httpmon-v1.0.1-macos-arm64 +sudo mv httpmon-v1.0.1-macos-arm64 /usr/local/bin/httpmon ``` ### From source (requires Go 1.24+) diff --git a/main.go b/main.go index d11ae63..1b0a930 100644 --- a/main.go +++ b/main.go @@ -3,8 +3,9 @@ package main import ( "bufio" "bytes" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" @@ -40,7 +41,7 @@ var ( counterMu sync.Mutex proxyPort string caCert *x509.Certificate - caKey *rsa.PrivateKey + caKey *ecdsa.PrivateKey certCache = make(map[string]*certEntry) certMu sync.Mutex certTTL = time.Hour // overridden by --cert-ttl flag @@ -87,8 +88,8 @@ func randSerial() *big.Int { return n } -func generateCA() (*x509.Certificate, *rsa.PrivateKey, error) { - key, err := rsa.GenerateKey(rand.Reader, 2048) +func generateCA() (*x509.Certificate, *ecdsa.PrivateKey, error) { + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } @@ -100,8 +101,7 @@ func generateCA() (*x509.Certificate, *rsa.PrivateKey, error) { }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(10, 0, 0), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, IsCA: true, } @@ -120,10 +120,9 @@ func generateCA() (*x509.Certificate, *rsa.PrivateKey, error) { } // generateCert returns a cached or newly-created leaf cert for host. -// The expensive RSA key generation runs outside the mutex so concurrent -// requests for different hosts proceed in parallel. A double-checked store -// ensures only one cert per host ends up in the cache even if two goroutines -// race on the same host. +// Key generation runs outside the mutex so concurrent requests for different +// hosts proceed in parallel. A double-checked store ensures only one cert per +// host ends up in the cache even if two goroutines race on the same host. func generateCert(host string) (*tls.Certificate, error) { // Fast path: return cached cert without generating. certMu.Lock() @@ -135,7 +134,7 @@ func generateCert(host string) (*tls.Certificate, error) { certMu.Unlock() // Slow path: generate outside the lock so other hosts are not blocked. - key, err := rsa.GenerateKey(rand.Reader, 2048) + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } @@ -147,7 +146,7 @@ func generateCert(host string) (*tls.Certificate, error) { }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(1, 0, 0), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, DNSNames: []string{host, "*." + host}, IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},