// Package squid renders /etc/edgeguard/squid/squid.conf from // forward_proxy_acls and reloads squid.service. Cache directory // + in-memory cache are hard-coded sensible defaults; the operator // scope is just "what can pass through the proxy". package squid import ( "bytes" "context" _ "embed" "fmt" "os" "path/filepath" "text/template" "github.com/jackc/pgx/v5/pgxpool" "git.netcell-it.de/projekte/edgeguard-native/internal/configgen" "git.netcell-it.de/projekte/edgeguard-native/internal/models" "git.netcell-it.de/projekte/edgeguard-native/internal/services/forwardproxy" ) const ( confPath = "/etc/edgeguard/squid/squid.conf" listenPort = 3128 ) //go:embed squid.cfg.tpl var cfgTpl string var tpl = template.Must(template.New("squid").Parse(cfgTpl)) type View struct { ListenPort int ACLs []models.ForwardProxyACL } type Generator struct { Pool *pgxpool.Pool Repo *forwardproxy.Repo SkipReload bool } func New(pool *pgxpool.Pool) *Generator { return &Generator{Pool: pool, Repo: forwardproxy.New(pool)} } func (g *Generator) Name() string { return "squid" } func (g *Generator) Render(ctx context.Context) error { acls, err := g.Repo.List(ctx) if err != nil { return fmt.Errorf("list acls: %w", err) } view := View{ListenPort: listenPort, ACLs: acls} var body bytes.Buffer if err := tpl.Execute(&body, view); err != nil { return fmt.Errorf("template: %w", err) } if err := os.MkdirAll(filepath.Dir(confPath), 0o755); err != nil { return fmt.Errorf("mkdir: %w", err) } tmp := confPath + ".tmp" if err := os.WriteFile(tmp, body.Bytes(), 0o644); err != nil { return fmt.Errorf("write %s: %w", tmp, err) } if err := os.Rename(tmp, confPath); err != nil { return fmt.Errorf("rename: %w", err) } if err := ensureDistroSymlink(); err != nil { return fmt.Errorf("symlink: %w", err) } if g.SkipReload { return nil } return configgen.ReloadService("squid") } // ensureDistroSymlink legt /etc/squid/squid.conf als Symlink auf // unsere managed conf an. Squid systemd-Unit liest die Distro-Datei; // ohne Symlink driftet der edgeguard-Renderer und der laufende // Daemon auseinander (gleicher Bug-Pattern wie wg-quick). // Existing real file (Distro-Default) wird nach .distro-bak verschoben, // nicht gelöscht. func ensureDistroSymlink() error { const link = "/etc/squid/squid.conf" if cur, err := os.Readlink(link); err == nil && cur == confPath { return nil } if _, err := os.Stat(link); err == nil { _ = os.Rename(link, link+".distro-bak") } return os.Symlink(confPath, link) }