Update dependencies and enhance deployment scripts
Some checks failed
Go / build (push) Has been cancelled
Go / release (push) Has been cancelled

- Bumped versions of several dependencies in go.mod, including golang.org/x/crypto to v0.43.0 and golang.org/x/net to v0.46.0.
- Added new indirect dependencies for improved functionality.
- Removed outdated files: package.json, POLICY_TESTS_SUCCESS.md, and POLICY_TESTS_SUMMARY.md.
- Introduced a comprehensive deployment script for automated setup and configuration.
- Added testing scripts for deployment validation and policy system tests.
- Bumped version to v0.19.0.
This commit is contained in:
2025-10-24 21:03:44 +01:00
parent eaf8f584ed
commit 44d22a383e
16 changed files with 1257 additions and 430 deletions

View File

@@ -64,6 +64,10 @@ type C struct {
SpiderMode string `env:"ORLY_SPIDER_MODE" default:"none" usage:"spider mode for syncing events: none, follows"`
PolicyEnabled bool `env:"ORLY_POLICY_ENABLED" default:"false" usage:"enable policy-based event processing (configuration found in $HOME/.config/ORLY/policy.json)"`
// TLS configuration
TLSDomains []string `env:"ORLY_TLS_DOMAINS" usage:"comma-separated list of domains to respond to for TLS"`
Certs []string `env:"ORLY_CERTS" usage:"comma-separated list of paths to certificate root names (e.g., /path/to/cert will load /path/to/cert.pem and /path/to/cert.key)"`
}
// New creates and initializes a new configuration object for the relay
@@ -200,9 +204,7 @@ func (kv KVSlice) Swap(i, j int) { kv[i], kv[j] = kv[j], kv[i] }
// resulting slice remains sorted by keys as per the KVSlice implementation.
func (kv KVSlice) Compose(kv2 KVSlice) (out KVSlice) {
// duplicate the initial KVSlice
for _, p := range kv {
out = append(out, p)
}
out = append(out, kv...)
out:
for i, p := range kv2 {
for j, q := range out {

View File

@@ -4,9 +4,12 @@ import (
"context"
"fmt"
"net/http"
"os"
"path/filepath"
"sync"
"time"
"golang.org/x/crypto/acme/autocert"
"lol.mleku.dev/chk"
"lol.mleku.dev/log"
"next.orly.dev/app/config"
@@ -159,25 +162,86 @@ func Run(
log.I.F("payment processor started successfully")
}
}
addr := fmt.Sprintf("%s:%d", cfg.Listen, cfg.Port)
log.I.F("starting listener on http://%s", addr)
// Create HTTP server for graceful shutdown
srv := &http.Server{
Addr: addr,
Handler: l,
// Check if TLS is enabled
var tlsEnabled bool
var tlsServer *http.Server
var httpServer *http.Server
if len(cfg.TLSDomains) > 0 {
// Validate TLS configuration
if err = ValidateTLSConfig(cfg.TLSDomains, cfg.Certs); chk.E(err) {
log.E.F("invalid TLS configuration: %v", err)
} else {
tlsEnabled = true
log.I.F("TLS enabled for domains: %v", cfg.TLSDomains)
// Create cache directory for autocert
cacheDir := filepath.Join(cfg.DataDir, "autocert")
if err = os.MkdirAll(cacheDir, 0700); chk.E(err) {
log.E.F("failed to create autocert cache directory: %v", err)
tlsEnabled = false
} else {
// Set up autocert manager
m := &autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: autocert.DirCache(cacheDir),
HostPolicy: autocert.HostWhitelist(cfg.TLSDomains...),
}
// Create TLS server on port 443
tlsServer = &http.Server{
Addr: ":443",
Handler: l,
TLSConfig: TLSConfig(m, cfg.Certs...),
}
// Create HTTP server for ACME challenges and redirects on port 80
httpServer = &http.Server{
Addr: ":80",
Handler: m.HTTPHandler(nil),
}
// Start TLS server
go func() {
log.I.F("starting TLS listener on https://:443")
if err := tlsServer.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
log.E.F("TLS server error: %v", err)
}
}()
// Start HTTP server for ACME challenges
go func() {
log.I.F("starting HTTP listener on http://:80 for ACME challenges")
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.E.F("HTTP server error: %v", err)
}
}()
}
}
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.E.F("HTTP server error: %v", err)
// Start regular HTTP server if TLS is not enabled or as fallback
if !tlsEnabled {
addr := fmt.Sprintf("%s:%d", cfg.Listen, cfg.Port)
log.I.F("starting listener on http://%s", addr)
httpServer = &http.Server{
Addr: addr,
Handler: l,
}
}()
go func() {
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.E.F("HTTP server error: %v", err)
}
}()
}
// Graceful shutdown handler
go func() {
<-ctx.Done()
log.I.F("shutting down HTTP server gracefully")
log.I.F("shutting down servers gracefully")
// Stop spider manager if running
if l.spiderManager != nil {
@@ -189,11 +253,22 @@ func Run(
shutdownCtx, cancelShutdown := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelShutdown()
// Shutdown the server gracefully
if err := srv.Shutdown(shutdownCtx); err != nil {
log.E.F("HTTP server shutdown error: %v", err)
} else {
log.I.F("HTTP server shutdown completed")
// Shutdown TLS server if running
if tlsServer != nil {
if err := tlsServer.Shutdown(shutdownCtx); err != nil {
log.E.F("TLS server shutdown error: %v", err)
} else {
log.I.F("TLS server shutdown completed")
}
}
// Shutdown HTTP server
if httpServer != nil {
if err := httpServer.Shutdown(shutdownCtx); err != nil {
log.E.F("HTTP server shutdown error: %v", err)
} else {
log.I.F("HTTP server shutdown completed")
}
}
once.Do(func() { close(quit) })

132
app/tls.go Normal file
View File

@@ -0,0 +1,132 @@
package app
import (
"crypto/tls"
"crypto/x509"
"fmt"
"strings"
"sync"
"golang.org/x/crypto/acme/autocert"
"lol.mleku.dev/chk"
"lol.mleku.dev/log"
)
// TLSConfig returns a TLS configuration that works with LetsEncrypt automatic SSL cert issuer
// as well as any provided certificate files from providers.
//
// The certs are provided in the form of paths where .pem and .key files exist
func TLSConfig(m *autocert.Manager, certs ...string) (tc *tls.Config) {
certMap := make(map[string]*tls.Certificate)
var mx sync.Mutex
for _, certPath := range certs {
if certPath == "" {
continue
}
var err error
var c tls.Certificate
// Load certificate and key files
if c, err = tls.LoadX509KeyPair(
certPath+".pem", certPath+".key",
); chk.E(err) {
log.E.F("failed to load certificate from %s: %v", certPath, err)
continue
}
// Extract domain names from certificate
if len(c.Certificate) > 0 {
if x509Cert, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
// Use the common name as the primary domain
if x509Cert.Subject.CommonName != "" {
certMap[x509Cert.Subject.CommonName] = &c
log.I.F("loaded certificate for domain: %s", x509Cert.Subject.CommonName)
}
// Also add any subject alternative names
for _, san := range x509Cert.DNSNames {
if san != "" {
certMap[san] = &c
log.I.F("loaded certificate for SAN domain: %s", san)
}
}
}
}
}
if m == nil {
// Create a basic TLS config without autocert
tc = &tls.Config{
GetCertificate: func(helo *tls.ClientHelloInfo) (*tls.Certificate, error) {
mx.Lock()
defer mx.Unlock()
// Check for exact match first
if cert, exists := certMap[helo.ServerName]; exists {
return cert, nil
}
// Check for wildcard matches
for domain, cert := range certMap {
if strings.HasPrefix(domain, "*.") {
baseDomain := domain[2:] // Remove "*."
if strings.HasSuffix(helo.ServerName, baseDomain) {
return cert, nil
}
}
}
return nil, fmt.Errorf("no certificate found for %s", helo.ServerName)
},
}
} else {
tc = m.TLSConfig()
tc.GetCertificate = func(helo *tls.ClientHelloInfo) (*tls.Certificate, error) {
mx.Lock()
// Check for exact match first
if cert, exists := certMap[helo.ServerName]; exists {
mx.Unlock()
return cert, nil
}
// Check for wildcard matches
for domain, cert := range certMap {
if strings.HasPrefix(domain, "*.") {
baseDomain := domain[2:] // Remove "*."
if strings.HasSuffix(helo.ServerName, baseDomain) {
mx.Unlock()
return cert, nil
}
}
}
mx.Unlock()
// Fall back to autocert for domains not in our certificate map
return m.GetCertificate(helo)
}
}
return tc
}
// ValidateTLSConfig checks if the TLS configuration is valid
func ValidateTLSConfig(domains []string, certs []string) (err error) {
if len(domains) == 0 {
return fmt.Errorf("no TLS domains specified")
}
// Validate domain names
for _, domain := range domains {
if domain == "" {
continue
}
if strings.Contains(domain, " ") || strings.Contains(domain, "\t") {
return fmt.Errorf("invalid domain name: %s", domain)
}
}
return nil
}