Skip to content
Open
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
43 changes: 43 additions & 0 deletions .github/workflows/renew-cert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Renew Certificate

on:
schedule:
- cron: '0 6 1 * *' # First day of every month at 6am UTC
workflow_dispatch: # Manual trigger

jobs:
renew:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install lego
run: |
curl -sL https://github.com/go-acme/lego/releases/latest/download/lego_linux_amd64.tar.gz | tar xz
sudo mv lego /usr/local/bin/

- name: Renew certificate
env:
CLOUDFLARE_DNS_API_TOKEN: ${{ secrets.CLOUDFLARE_DNS_API_TOKEN }}
run: |
lego --email="herrera.monterroso.mario@gmail.com" \
--dns cloudflare \
--domains="local.labelctl.dev" \
--accept-tos \
--path="./.lego" \
run

cp .lego/certificates/local.labelctl.dev.crt certs/server.crt
cp .lego/certificates/local.labelctl.dev.key certs/server.key

- name: Commit updated certificate
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add certs/server.crt certs/server.key
if git diff --cached --quiet; then
echo "No certificate changes"
else
git commit -m "chore: renew Let's Encrypt certificate for local.labelctl.dev"
git push
fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ tsc-bridge.exe
*.exe
.DS_Store
.ai-sessions/
.lego/
48 changes: 48 additions & 0 deletions certs/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-----BEGIN CERTIFICATE-----
MIIDhjCCAw2gAwIBAgISBfRSqev+aldQsYsS6WahZreFMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
ODAeFw0yNjAzMDgyMDQxMjFaFw0yNjA2MDYyMDQxMjBaMB0xGzAZBgNVBAMTEmxv
Y2FsLmxhYmVsY3RsLmRldjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABK/KfWZb
zQ1KDKZS82OdTj/Ny9hLTYlM/YYvmFQfsbyUDynmPfk5CsxFDvMpy61Iu6Ywb1s1
3VA2jGteuFMyKQKjggIWMIICEjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI
KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUKPIkRGWqb3PFOEtNAnNv
YsJZjX4wHwYDVR0jBBgwFoAUjw0TovYuftFQbDMYOF1ZjiNykcowMgYIKwYBBQUH
AQEEJjAkMCIGCCsGAQUFBzAChhZodHRwOi8vZTguaS5sZW5jci5vcmcvMB0GA1Ud
EQQWMBSCEmxvY2FsLmxhYmVsY3RsLmRldjATBgNVHSAEDDAKMAgGBmeBDAECATAs
BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vZTguYy5sZW5jci5vcmcvNi5jcmwwggEF
BgorBgEEAdZ5AgQCBIH2BIHzAPEAdgDLOPcViXyEoURfW8Hd+8lu8ppZzUcKaQWF
sMsUwxRY5wAAAZzPZG32AAAEAwBHMEUCIQDObDO1HnbH4udWbsCvUMSUs2NsYIk4
1QwsaxkqMPGfIAIgD7QrqqJMcG42XDD1QNV8dx+n2zHse4sBMihl/fYsnj0AdwCW
l2S/VViXrfdDh2g3CEJ36fA61fak8zZuRqQ/D8qpxgAAAZzPZG4PAAAEAwBIMEYC
IQCRu6reTOB+B2IugxQeRYXJY1BzepdoUq9kuhT8/ZmNiwIhAKzLVMmmSFUg6irJ
a6yvZarNUIgFO+eJqJSYl+A9gmgqMAoGCCqGSM49BAMDA2cAMGQCMEYmOjHwswRn
ee3mm57saH0FxdMpevPWh1mphqBMt417QfKow6YillWN/YP4R7lIPgIwBeZ6UT6l
lYd9Ub2tJn7eLOKDehD+4hKNzwgHGn3OUspApf57lfPkwLjAWWjP12vm
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa
Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF
bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c
S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb
R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB
9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j
cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB
BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE
DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j
ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0
RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d
AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8
otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA
aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm
Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2
HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1
Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR
xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d
tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/
jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS
u1igv3OefnWjSQ==
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions certs/server.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIP94a9FyoPzqzqLt3Cgd4HF/FjsSHPckdpxM+/C3FZxMoAoGCCqGSM49
AwEHoUQDQgAEr8p9ZlvNDUoMplLzY51OP83L2EtNiUz9hi+YVB+xvJQPKeY9+TkK
zEUO8ynLrUi7pjBvWzXdUDaMa164UzIpAg==
-----END EC PRIVATE KEY-----
28 changes: 28 additions & 0 deletions certs_embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"crypto/tls"
_ "embed"
"log"
)

//go:embed certs/server.crt
var embeddedCert []byte

//go:embed certs/server.key
var embeddedKey []byte

// loadEmbeddedCert returns the embedded Let's Encrypt certificate for local.labelctl.dev.
// Returns nil if the embedded cert is missing or invalid.
func loadEmbeddedCert() *tls.Certificate {
if len(embeddedCert) == 0 || len(embeddedKey) == 0 {
return nil
}
cert, err := tls.X509KeyPair(embeddedCert, embeddedKey)
if err != nil {
log.Printf("[tls] Embedded cert invalid: %v — falling back to self-signed", err)
return nil
}
log.Printf("[tls] Using embedded Let's Encrypt certificate for local.labelctl.dev")
return &cert
}
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ require (
github.com/boombuler/barcode v1.1.0
github.com/signintech/gopdf v0.36.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6
github.com/xuri/excelize/v2 v2.9.0
golang.org/x/image v0.18.0
)

require (
Expand All @@ -17,12 +19,10 @@ require (
github.com/pkg/errors v0.8.1 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.4 // indirect
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 // indirect
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
)
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQ
github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
28 changes: 17 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -749,19 +749,25 @@ func startServers(port string) error {
}()
}

// HTTPS server with auto-generated certs (also on dedicated thread)
// HTTPS server — try embedded Let's Encrypt cert first, fallback to self-signed
httpsPort := fmt.Sprintf("%d", portInt(port)+1)
httpsAddr := "127.0.0.1:" + httpsPort
certFile, keyFile, caFile, err := ensureCerts(defaultHostname)
if err != nil {
log.Printf("[tls] Could not generate certs: %v — HTTPS disabled", err)
return nil // HTTP is running, HTTPS is optional
}
go installCACert(caFile)
tlsCert, err := loadCertWithCA(certFile, keyFile, caFile)
if err != nil {
log.Printf("[tls] Could not load certs: %v — HTTPS disabled", err)
return nil
var tlsCert tls.Certificate
if embedded := loadEmbeddedCert(); embedded != nil {
tlsCert = *embedded
} else {
certFile, keyFile, caFile, err := ensureCerts(defaultHostname)
if err != nil {
log.Printf("[tls] Could not generate certs: %v — HTTPS disabled", err)
return nil
}
go installCACert(caFile)
loaded, err := loadCertWithCA(certFile, keyFile, caFile)
if err != nil {
log.Printf("[tls] Could not load certs: %v — HTTPS disabled", err)
return nil
}
tlsCert = loaded
}
httpsLn, err := net.Listen("tcp", httpsAddr)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"time"
)

const defaultHostname = "myprinter.com"
const defaultHostname = "local.labelctl.dev"

// certDir returns the directory for TLS certificates.
func certDir() string {
Expand Down Expand Up @@ -101,7 +101,7 @@ func ensureCerts(hostname string) (certFile, keyFile, caFile string, err error)
Organization: []string{"TSC Bridge"},
CommonName: hostname,
},
DNSNames: []string{hostname, "localhost", "tsc-bridge", "myprinter.com"},
DNSNames: []string{hostname, "localhost", "local.labelctl.dev"},
IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
NotBefore: time.Now(),
NotAfter: time.Now().Add(397 * 24 * time.Hour), // Max 398 days for Apple/Chrome compliance
Expand Down
Loading