fix tunnel embed, acme renew
Kim committed
Mar 6, 2026 at 13:52 UTC
5e2ed3a00b09f21c7b754ac94306838642c9686c
2 files changed
+39
-28
cmd/relay-server/tunnel.go
+8
-20
@@ -5,8 +5,6 @@ import (
5
"encoding/hex"
6
"fmt"
7
"net/http"
8
- "os"
9
- "path/filepath"
8
"strings"
9
)
10
@@ -199,46 +197,36 @@ func serveTunnelBinary(w http.ResponseWriter, r *http.Request) {
197
slug = strings.TrimSuffix(slug, ".sha256")
198
}
199
202
- path, ok := tunnelBinaryPathBySlug(slug)
200
+ data, filename, ok := tunnelBinaryBySlug(slug)
201
if !ok {
202
http.NotFound(w, r)
203
return
204
}
207
-
208
- data, err := os.ReadFile(path)
209
- if err != nil {
210
- http.NotFound(w, r)
211
- return
212
- }
205
sum := sha256.Sum256(data)
206
checksumHex := hex.EncodeToString(sum[:])
207
208
if checksumRequest {
209
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
210
if r.Method == http.MethodGet {
219
- _, _ = fmt.Fprintf(w, "%s portal-tunnel-%s\n", checksumHex, slug)
211
+ _, _ = fmt.Fprintf(w, "%s %s\n", checksumHex, filename)
212
}
213
return
214
}
215
216
w.Header().Set("Content-Type", "application/octet-stream")
217
+ w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename))
218
w.Header().Set("X-Checksum-Sha256", checksumHex)
219
if r.Method == http.MethodGet {
220
_, _ = w.Write(data)
221
}
222
}
223
231
-func tunnelBinaryPathBySlug(slug string) (string, bool) {
232
- candidates := []string{
233
- filepath.Join("dist", "tunnel", tunnelBinaryName(slug)),
234
- filepath.Join("bin", tunnelBinaryName(slug)),
235
- }
236
- for _, candidate := range candidates {
237
- if _, err := os.Stat(candidate); err == nil {
238
- return candidate, true
239
- }
224
+func tunnelBinaryBySlug(slug string) ([]byte, string, bool) {
225
+ filename := tunnelBinaryName(slug)
226
+ if data, err := embeddedDistFS.ReadFile("dist/tunnel/" + filename); err == nil {
227
+ return data, filename, true
228
}
241
- return "", false
229
+ return nil, "", false
230
}
231
232
func tunnelBinaryName(slug string) string {
portal/acme/acme.go
+31
-8
@@ -24,6 +24,7 @@ import (
24
lego "github.com/go-acme/lego/v4/lego"
25
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
26
"github.com/go-acme/lego/v4/registration"
27
+ "github.com/rs/zerolog/log"
28
)
29
30
const (
@@ -32,6 +33,9 @@ const (
33
accountKeyFileName = "acme-account.key"
34
registrationFileName = "acme-registration.json"
35
defaultACMEEmailPrefix = "acme@"
36
+ defaultRenewInterval = 24 * time.Hour
37
+ defaultDNSSyncInterval = 10 * time.Minute
38
+ defaultSyncTimeout = 2 * time.Minute
39
)
40
41
type Config struct {
@@ -99,7 +103,7 @@ func (m *Manager) EnsureCertificate(ctx context.Context) (string, string, error)
103
return "", "", errors.New("cloudflare token is required for non-local relay certificates")
104
}
105
102
- if err := EnsureDNSRecords(ctx, m.cfg.BaseDomain, m.cfg.CloudflareToken); err != nil {
106
+ if err := m.syncDNS(ctx); err != nil {
107
return "", "", fmt.Errorf("ensure dns records: %w", err)
108
}
109
@@ -124,7 +128,7 @@ func (m *Manager) Start(ctx context.Context) {
128
129
m.startOnce.Do(func() {
130
m.wg.Add(1)
127
- go m.renewalLoop(ctx)
131
+ go m.maintenanceLoop(ctx)
132
})
133
}
134
@@ -195,11 +199,13 @@ func (m *Manager) provision(ctx context.Context) error {
199
return nil
200
}
201
198
-func (m *Manager) renewalLoop(ctx context.Context) {
202
+func (m *Manager) maintenanceLoop(ctx context.Context) {
203
defer m.wg.Done()
204
201
- ticker := time.NewTicker(24 * time.Hour)
202
- defer ticker.Stop()
205
+ renewTicker := time.NewTicker(defaultRenewInterval)
206
+ dnsTicker := time.NewTicker(defaultDNSSyncInterval)
207
+ defer renewTicker.Stop()
208
+ defer dnsTicker.Stop()
209
210
for {
211
select {
@@ -207,17 +213,34 @@ func (m *Manager) renewalLoop(ctx context.Context) {
213
return
214
case <-m.stopCh:
215
return
210
- case <-ticker.C:
216
+ case <-dnsTicker.C:
217
+ syncCtx, cancel := context.WithTimeout(ctx, defaultSyncTimeout)
218
+ err := m.syncDNS(syncCtx)
219
+ cancel()
220
+ if err != nil {
221
+ log.Warn().Err(err).Str("base_domain", m.cfg.BaseDomain).Msg("sync dns records")
222
+ }
223
+ case <-renewTicker.C:
224
if !m.shouldRenew() {
225
continue
226
}
214
- renewCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
215
- _ = m.provision(renewCtx)
227
+ renewCtx, cancel := context.WithTimeout(ctx, defaultSyncTimeout)
228
+ err := m.provision(renewCtx)
229
cancel()
230
+ if err != nil {
231
+ log.Warn().Err(err).Str("base_domain", m.cfg.BaseDomain).Msg("renew acme certificate")
232
+ }
233
}
234
}
235
}
236
237
+func (m *Manager) syncDNS(ctx context.Context) error {
238
+ if m == nil || isLocalhost(m.cfg.BaseDomain) || m.cfg.CloudflareToken == "" {
239
+ return nil
240
+ }
241
+ return EnsureDNSRecords(ctx, m.cfg.BaseDomain, m.cfg.CloudflareToken)
242
+}
243
+
244
func (m *Manager) shouldRenew() bool {
245
m.mu.RLock()
246
defer m.mu.RUnlock()