mirror of
https://github.com/fosrl/newt.git
synced 2026-08-06 13:44:44 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3b2c0c527 | ||
|
|
15224904a0 | ||
|
|
527edc8d80 |
+19
-19
@@ -68,7 +68,7 @@ jobs:
|
||||
echo "image_created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Configure AWS credentials (OIDC)
|
||||
uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1
|
||||
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -243,14 +243,14 @@ jobs:
|
||||
# uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -309,7 +309,7 @@ jobs:
|
||||
IMAGE_CREATED: ${{ needs.pre-run.outputs.image_created }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -368,14 +368,14 @@ jobs:
|
||||
echo "Checked out $(git rev-parse --short HEAD) for tag ${TAG}"
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -434,7 +434,7 @@ jobs:
|
||||
IMAGE_CREATED: ${{ needs.pre-run.outputs.image_created }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -483,14 +483,14 @@ jobs:
|
||||
echo "Checked out $(git rev-parse --short HEAD) for tag ${TAG}"
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -556,14 +556,14 @@ jobs:
|
||||
#PUBLISH_MINOR: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_minor || vars.PUBLISH_MINOR }}
|
||||
steps:
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -642,7 +642,7 @@ jobs:
|
||||
IMAGE_CREATED: ${{ needs.pre-run.outputs.image_created }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -656,19 +656,19 @@ jobs:
|
||||
echo "Checked out $(git rev-parse --short HEAD) for tag ${TAG}"
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -912,7 +912,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Create GitHub Release (draft)
|
||||
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
||||
with:
|
||||
tag_name: ${{ env.TAG }}
|
||||
generate_release_notes: true
|
||||
@@ -940,7 +940,7 @@ jobs:
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: Configure AWS credentials (OIDC)
|
||||
uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1
|
||||
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
|
||||
role-duration-seconds: 3600
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v7.0.1
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v7.0.1
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||
- uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 14
|
||||
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
@@ -42,10 +42,10 @@ jobs:
|
||||
- go-build-release-windows-amd64
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
|
||||
@@ -683,23 +683,6 @@ func (b *SharedBind) receiveIPv4Simple(conn *net.UDPConn, bufs [][]byte, sizes [
|
||||
}
|
||||
}
|
||||
|
||||
// IsMagicPacket reports whether payload is one of our connectivity-test magic
|
||||
// packets (a MagicTestRequest or MagicTestResponse). These packets are meant to
|
||||
// travel directly between physical UDP sockets and must never be encapsulated by
|
||||
// WireGuard - e.g. if OS routing mistakenly sends one into a WireGuard TUN
|
||||
// interface (because the destination falls inside a routed tunnel subnet), it
|
||||
// should be dropped there rather than tunneled, which would otherwise make a
|
||||
// LAN-local endpoint test falsely appear to succeed over the tunnel.
|
||||
func IsMagicPacket(payload []byte) bool {
|
||||
if len(payload) >= MagicTestRequestLen && bytes.HasPrefix(payload, MagicTestRequest) {
|
||||
return true
|
||||
}
|
||||
if len(payload) >= MagicTestResponseLen && bytes.HasPrefix(payload, MagicTestResponse) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// handleMagicPacket checks if the packet is a magic test packet and responds if so.
|
||||
// Returns true if the packet was a magic packet and was handled (should not be passed to WireGuard).
|
||||
func (b *SharedBind) handleMagicPacket(data []byte, addr *net.UDPAddr) bool {
|
||||
|
||||
+12
-143
@@ -35,11 +35,10 @@ import (
|
||||
)
|
||||
|
||||
type WgConfig struct {
|
||||
IpAddress string `json:"ipAddress"`
|
||||
Peers []Peer `json:"peers"`
|
||||
Targets []Target `json:"targets"`
|
||||
Certs []CertData `json:"certs"`
|
||||
ChainId string `json:"chainId"`
|
||||
IpAddress string `json:"ipAddress"`
|
||||
Peers []Peer `json:"peers"`
|
||||
Targets []Target `json:"targets"`
|
||||
ChainId string `json:"chainId"`
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
@@ -54,23 +53,6 @@ type Target struct {
|
||||
HTTPTargets []netstack2.HTTPTarget `json:"httpTargets,omitempty"` // for http protocol, list of downstream services to load balance across
|
||||
TLSCert string `json:"tlsCert,omitempty"` // PEM-encoded certificate for incoming HTTPS termination
|
||||
TLSKey string `json:"tlsKey,omitempty"` // PEM-encoded private key for incoming HTTPS termination
|
||||
TLSCertID string `json:"tlsCertId,omitempty"` // references an entry in the sync message's Certs list instead of inlining TLSCert/TLSKey
|
||||
}
|
||||
|
||||
// CertData is a single shared TLS certificate/key pair, referenced by ID from
|
||||
// one or more Targets via TLSCertID. Sent once per sync message so that many
|
||||
// targets backed by the same certificate (e.g. a wildcard cert) don't each
|
||||
// carry a full copy of the PEM data.
|
||||
type CertData struct {
|
||||
ID string `json:"id"`
|
||||
Cert string `json:"cert"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// CertPair holds the resolved PEM certificate/key material for a CertData entry.
|
||||
type CertPair struct {
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
|
||||
type PortRange struct {
|
||||
@@ -140,11 +122,6 @@ type WireGuardService struct {
|
||||
|
||||
// connection blocking: when true, all new incoming connections are dropped
|
||||
blocked atomic.Bool
|
||||
|
||||
// certs resolves TLSCertID references on incoming Targets to their PEM
|
||||
// cert/key material. Replaced wholesale on every full sync.
|
||||
certs map[string]CertPair
|
||||
certsMu sync.RWMutex
|
||||
}
|
||||
|
||||
// generateChainId generates a random chain ID for deduplicating round-trip messages.
|
||||
@@ -219,8 +196,6 @@ func NewWireGuardService(interfaceName string, port uint16, mtu int, host string
|
||||
wsClient.RegisterHandler("newt/wg/targets/add", service.handleAddTarget)
|
||||
wsClient.RegisterHandler("newt/wg/targets/remove", service.handleRemoveTarget)
|
||||
wsClient.RegisterHandler("newt/wg/targets/update", service.handleUpdateTarget)
|
||||
wsClient.RegisterHandler("newt/certs/add", service.handleAddCerts)
|
||||
wsClient.RegisterHandler("newt/certs/remove", service.handleRemoveCerts)
|
||||
|
||||
return service, nil
|
||||
}
|
||||
@@ -569,7 +544,6 @@ func (s *WireGuardService) handleConfig(msg websocket.WSMessage) {
|
||||
}
|
||||
|
||||
s.config = config
|
||||
s.SetCerts(config.Certs)
|
||||
|
||||
if s.stopGetConfig != nil {
|
||||
s.stopGetConfig()
|
||||
@@ -594,107 +568,6 @@ func (s *WireGuardService) handleConfig(msg websocket.WSMessage) {
|
||||
logger.Info("Client connectivity setup. Ready to accept connections from clients!")
|
||||
}
|
||||
|
||||
// SetCerts replaces the TLSCertID lookup table used by resolveTLS. The server
|
||||
// sends the complete set of referenced certs on every full sync, so this is a
|
||||
// wholesale replacement rather than an incremental merge.
|
||||
func (s *WireGuardService) SetCerts(certs []CertData) {
|
||||
m := make(map[string]CertPair, len(certs))
|
||||
for _, c := range certs {
|
||||
m[c.ID] = CertPair{Cert: c.Cert, Key: c.Key}
|
||||
}
|
||||
s.certsMu.Lock()
|
||||
s.certs = m
|
||||
s.certsMu.Unlock()
|
||||
}
|
||||
|
||||
// resolveTLS returns the PEM cert/key to use for target's incoming HTTPS
|
||||
// termination: target.TLSCertID looked up in the certs table if set, falling
|
||||
// back to the target's own inline TLSCert/TLSKey otherwise.
|
||||
func (s *WireGuardService) resolveTLS(target Target) (cert, key string) {
|
||||
if target.TLSCertID == "" {
|
||||
return target.TLSCert, target.TLSKey
|
||||
}
|
||||
s.certsMu.RLock()
|
||||
pair, ok := s.certs[target.TLSCertID]
|
||||
s.certsMu.RUnlock()
|
||||
if !ok {
|
||||
logger.Warn("No cert found for tlsCertId %s, falling back to inline cert on target", target.TLSCertID)
|
||||
return target.TLSCert, target.TLSKey
|
||||
}
|
||||
return pair.Cert, pair.Key
|
||||
}
|
||||
|
||||
// AddCerts upserts the given certs into the lookup table used by resolveTLS,
|
||||
// without discarding any certs already present. Used for incremental cert
|
||||
// pushes (e.g. after a renewal) outside of a full newt/sync or
|
||||
// newt/wg/receive-config, which replace the table wholesale via SetCerts.
|
||||
func (s *WireGuardService) AddCerts(certs []CertData) {
|
||||
if len(certs) == 0 {
|
||||
return
|
||||
}
|
||||
s.certsMu.Lock()
|
||||
if s.certs == nil {
|
||||
s.certs = make(map[string]CertPair, len(certs))
|
||||
}
|
||||
for _, c := range certs {
|
||||
s.certs[c.ID] = CertPair{Cert: c.Cert, Key: c.Key}
|
||||
}
|
||||
s.certsMu.Unlock()
|
||||
}
|
||||
|
||||
// RemoveCerts deletes the given cert IDs from the lookup table, e.g. once the
|
||||
// server knows no target references them anymore.
|
||||
func (s *WireGuardService) RemoveCerts(ids []string) {
|
||||
if len(ids) == 0 {
|
||||
return
|
||||
}
|
||||
s.certsMu.Lock()
|
||||
for _, id := range ids {
|
||||
delete(s.certs, id)
|
||||
}
|
||||
s.certsMu.Unlock()
|
||||
}
|
||||
|
||||
// handleAddCerts processes a "newt/certs/add" message: an array of CertData
|
||||
// to upsert into the cert lookup table.
|
||||
func (s *WireGuardService) handleAddCerts(msg websocket.WSMessage) {
|
||||
jsonData, err := json.Marshal(msg.Data)
|
||||
if err != nil {
|
||||
logger.Info("Error marshaling cert add data: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var certs []CertData
|
||||
if err := json.Unmarshal(jsonData, &certs); err != nil {
|
||||
logger.Warn("Error unmarshaling cert add data: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
s.AddCerts(certs)
|
||||
logger.Info("Added %d certs", len(certs))
|
||||
}
|
||||
|
||||
// handleRemoveCerts processes a "newt/certs/remove" message: {ids: [...]}
|
||||
// naming the cert IDs to drop from the lookup table.
|
||||
func (s *WireGuardService) handleRemoveCerts(msg websocket.WSMessage) {
|
||||
jsonData, err := json.Marshal(msg.Data)
|
||||
if err != nil {
|
||||
logger.Info("Error marshaling cert remove data: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var data struct {
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &data); err != nil {
|
||||
logger.Warn("Error unmarshaling cert remove data: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
s.RemoveCerts(data.IDs)
|
||||
logger.Info("Removed %d certs", len(data.IDs))
|
||||
}
|
||||
|
||||
// Sync synchronizes the clients WireGuard peers and targets with the desired state
|
||||
// received as part of the main newt/sync message.
|
||||
func (s *WireGuardService) Sync(peers []Peer, targets []Target) {
|
||||
@@ -807,7 +680,6 @@ func (s *WireGuardService) syncTargets(desiredTargets []Target) error {
|
||||
continue
|
||||
}
|
||||
|
||||
tlsCert, tlsKey := s.resolveTLS(target)
|
||||
rules = append(rules, netstack2.SubnetRule{
|
||||
SourcePrefix: sourcePrefix,
|
||||
DestPrefix: destPrefix,
|
||||
@@ -817,8 +689,8 @@ func (s *WireGuardService) syncTargets(desiredTargets []Target) error {
|
||||
ResourceId: target.ResourceId,
|
||||
Protocol: target.Protocol,
|
||||
HTTPTargets: target.HTTPTargets,
|
||||
TLSCert: tlsCert,
|
||||
TLSKey: tlsKey,
|
||||
TLSCert: target.TLSCert,
|
||||
TLSKey: target.TLSKey,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1104,7 +976,6 @@ func (s *WireGuardService) ensureTargets(targets []Target) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid CIDR %s: %v", sp, err)
|
||||
}
|
||||
tlsCert, tlsKey := s.resolveTLS(target)
|
||||
s.tnet.AddProxySubnetRule(netstack2.SubnetRule{
|
||||
SourcePrefix: sourcePrefix,
|
||||
DestPrefix: destPrefix,
|
||||
@@ -1114,8 +985,8 @@ func (s *WireGuardService) ensureTargets(targets []Target) error {
|
||||
ResourceId: target.ResourceId,
|
||||
Protocol: target.Protocol,
|
||||
HTTPTargets: target.HTTPTargets,
|
||||
TLSCert: tlsCert,
|
||||
TLSKey: tlsKey,
|
||||
TLSCert: target.TLSCert,
|
||||
TLSKey: target.TLSKey,
|
||||
})
|
||||
logger.Info("Added target subnet from %s to %s rewrite to %s with port ranges: %v", sp, target.DestPrefix, target.RewriteTo, target.PortRange)
|
||||
}
|
||||
@@ -1509,7 +1380,6 @@ func (s *WireGuardService) handleAddTarget(msg websocket.WSMessage) {
|
||||
logger.Info("Invalid CIDR %s: %v", sp, err)
|
||||
continue
|
||||
}
|
||||
tlsCert, tlsKey := s.resolveTLS(target)
|
||||
s.tnet.AddProxySubnetRule(netstack2.SubnetRule{
|
||||
SourcePrefix: sourcePrefix,
|
||||
DestPrefix: destPrefix,
|
||||
@@ -1519,8 +1389,8 @@ func (s *WireGuardService) handleAddTarget(msg websocket.WSMessage) {
|
||||
ResourceId: target.ResourceId,
|
||||
Protocol: target.Protocol,
|
||||
HTTPTargets: target.HTTPTargets,
|
||||
TLSCert: tlsCert,
|
||||
TLSKey: tlsKey,
|
||||
TLSCert: target.TLSCert,
|
||||
TLSKey: target.TLSKey,
|
||||
})
|
||||
logger.Info("Added target subnet from %s to %s rewrite to %s with port ranges: %v", sp, target.DestPrefix, target.RewriteTo, target.PortRange)
|
||||
}
|
||||
@@ -1639,7 +1509,6 @@ func (s *WireGuardService) handleUpdateTarget(msg websocket.WSMessage) {
|
||||
logger.Info("Invalid CIDR %s: %v", sp, err)
|
||||
continue
|
||||
}
|
||||
tlsCert, tlsKey := s.resolveTLS(target)
|
||||
s.tnet.AddProxySubnetRule(netstack2.SubnetRule{
|
||||
SourcePrefix: sourcePrefix,
|
||||
DestPrefix: destPrefix,
|
||||
@@ -1649,8 +1518,8 @@ func (s *WireGuardService) handleUpdateTarget(msg websocket.WSMessage) {
|
||||
ResourceId: target.ResourceId,
|
||||
Protocol: target.Protocol,
|
||||
HTTPTargets: target.HTTPTargets,
|
||||
TLSCert: tlsCert,
|
||||
TLSKey: tlsKey,
|
||||
TLSCert: target.TLSCert,
|
||||
TLSKey: target.TLSKey,
|
||||
})
|
||||
logger.Info("Added target subnet from %s to %s rewrite to %s with port ranges: %v", sp, target.DestPrefix, target.RewriteTo, target.PortRange)
|
||||
}
|
||||
|
||||
@@ -3,15 +3,9 @@ package netstack2
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@@ -71,13 +65,6 @@ type HTTPHandler struct {
|
||||
// of the PEM certificate and key. Parsing a keypair is relatively expensive
|
||||
// and the same cert is likely reused across many connections.
|
||||
tlsCache sync.Map // map[string]*tls.Config
|
||||
|
||||
// fallbackTLSOnce/fallbackTLSCfg hold a lazily-generated self-signed
|
||||
// certificate used when a rule's configured cert/key fails to parse, so
|
||||
// that a misconfigured rule degrades to a browser cert warning instead of
|
||||
// silently dropping every connection.
|
||||
fallbackTLSOnce sync.Once
|
||||
fallbackTLSCfg *tls.Config
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -275,13 +262,7 @@ func (h *HTTPHandler) getTLSConfig(rule *SubnetRule) (*tls.Config, error) {
|
||||
|
||||
cert, err := tls.X509KeyPair([]byte(rule.TLSCert), []byte(rule.TLSKey))
|
||||
if err != nil {
|
||||
// A misconfigured rule (bad/missing PEM data) must not take the whole
|
||||
// connection down: fall back to a self-signed cert so the handshake
|
||||
// still completes and the request reaches handleRequest, which routes
|
||||
// independently of the cert. Clients will see a cert warning instead
|
||||
// of a silent connection reset.
|
||||
logger.Warn("HTTP handler: falling back to self-signed cert for rule (invalid configured keypair): %v", err)
|
||||
return h.getFallbackTLSConfig(), nil
|
||||
return nil, fmt.Errorf("failed to parse TLS keypair: %w", err)
|
||||
}
|
||||
cfg := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
@@ -292,57 +273,6 @@ func (h *HTTPHandler) getTLSConfig(rule *SubnetRule) (*tls.Config, error) {
|
||||
return actual.(*tls.Config), nil
|
||||
}
|
||||
|
||||
// getFallbackTLSConfig returns a *tls.Config backed by a self-signed
|
||||
// certificate, generated once and reused for the lifetime of the handler.
|
||||
func (h *HTTPHandler) getFallbackTLSConfig() *tls.Config {
|
||||
h.fallbackTLSOnce.Do(func() {
|
||||
cert, err := generateSelfSignedCert()
|
||||
if err != nil {
|
||||
// Generation of an in-memory self-signed cert has no external
|
||||
// dependencies and should never fail; if it somehow does, there
|
||||
// is no sensible fallback left, so surface it loudly.
|
||||
logger.Error("HTTP handler: failed to generate fallback self-signed cert: %v", err)
|
||||
return
|
||||
}
|
||||
h.fallbackTLSCfg = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
})
|
||||
return h.fallbackTLSCfg
|
||||
}
|
||||
|
||||
// generateSelfSignedCert creates a fresh, in-memory self-signed TLS
|
||||
// certificate/key pair valid for one year, used as a fallback when a rule's
|
||||
// configured certificate cannot be parsed.
|
||||
func generateSelfSignedCert() (tls.Certificate, error) {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("failed to generate private key: %w", err)
|
||||
}
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("failed to generate serial number: %w", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{CommonName: "newt-fallback"},
|
||||
NotBefore: time.Now().Add(-time.Hour),
|
||||
NotAfter: time.Now().AddDate(1, 0, 0),
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("failed to create certificate: %w", err)
|
||||
}
|
||||
|
||||
return tls.Certificate{
|
||||
Certificate: [][]byte{derBytes},
|
||||
PrivateKey: priv,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getProxy returns a cached *httputil.ReverseProxy for the given target,
|
||||
// creating one on first use. Reusing the proxy preserves its http.Transport
|
||||
// connection pool, avoiding repeated TCP/TLS handshakes to the downstream.
|
||||
|
||||
@@ -144,7 +144,6 @@ func (n *Newt) handleSync(msg websocket.WSMessage) {
|
||||
|
||||
// Sync clients WireGuard peers and targets, if clients are set up
|
||||
if n.wgService != nil {
|
||||
n.wgService.SetCerts(syncData.Certs)
|
||||
n.wgService.Sync(syncData.Peers, syncData.ClientTargets)
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -1010,9 +1010,7 @@ func (n *Newt) registerHandlers(ctx context.Context) {
|
||||
}
|
||||
|
||||
bcChainId := generateChainId()
|
||||
// Pangolin intentionally does not answer backwards-compatible
|
||||
// registrations with newt/wg/connect. Do not replace the chain ID of
|
||||
// the real registration while its response may already be in flight.
|
||||
n.pendingRegisterChainId = bcChainId
|
||||
if err := n.client.SendMessage(topicWGRegister, map[string]interface{}{
|
||||
"publicKey": n.publicKey.String(),
|
||||
"newtVersion": n.config.Version,
|
||||
|
||||
+1
-2
@@ -280,8 +280,7 @@ func (n *Newt) startPingCheck(fn pingFunc, serverIP, tunnelID string) chan struc
|
||||
"chainId": pingChainId,
|
||||
}, 3*time.Second)
|
||||
bcChainId := generateChainId()
|
||||
// This compatibility message has no wg/connect response and must
|
||||
// not supersede the pending real registration chain.
|
||||
n.pendingRegisterChainId = bcChainId
|
||||
if err := n.client.SendMessage("newt/wg/register", map[string]interface{}{
|
||||
"publicKey": n.publicKey.String(),
|
||||
"backwardsCompatible": true,
|
||||
|
||||
@@ -70,6 +70,5 @@ type SyncData struct {
|
||||
RemoteExitNodeSubnets []string `json:"remoteExitNodeSubnets"`
|
||||
Peers []wgclients.Peer `json:"peers"`
|
||||
ClientTargets []wgclients.Target `json:"clientTargets"`
|
||||
Certs []wgclients.CertData `json:"certs"`
|
||||
BrowserGatewayTargets []BrowserGatewayTarget `json:"browserGatewayTargets"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user