Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -42,19 +47,23 @@ 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 }}
GOARCH: ${{ matrix.goarch }}
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
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-<version>-macos-arm64` |
| macOS Intel | `httpmon-<version>-macos-amd64` |
| Linux x86-64 | `httpmon-<version>-linux-amd64` |
| Linux arm64 (Graviton, Pi) | `httpmon-<version>-linux-arm64` |
| Windows x86-64 | `httpmon-<version>-windows-amd64.exe` |

```bash
chmod +x httpmon-darwin-arm64
sudo mv httpmon-darwin-arm64 /usr/local/bin/httpmon
# macOS / Linux — replace <version> 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+)
Expand Down
23 changes: 11 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package main
import (
"bufio"
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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,
}
Expand All @@ -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()
Expand All @@ -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
}
Expand All @@ -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},
Expand Down
Loading