This is the main dovel repository, it has the Go code to run dovel SMTP server.
Author: brian (git@myr.sh)
Date: Sun Dec 31 15:58:26 2023 -0300
Parent: 1a87fa1
Updated vault package * Updated smtp server package
diff --git a/TODO.txt b/TODO.txt index 11e2cd8..9bf7f49 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,6 +5,6 @@ ✓ Support PGP ✓ Use XDG desktop for config path ✓ Support SMTP for sending email +✓ Change PGP to external package ☐ Add denylist filtering ☐ Improve documentation -☐ Change PGP to external package
diff --git a/backend.go b/backend.go index 73741d2..b9a80c1 100644 --- a/backend.go +++ b/backend.go @@ -18,14 +18,14 @@ import ( "github.com/emersion/go-mbox" "github.com/emersion/go-msgauth/dkim" - "github.com/emersion/go-openpgp-wkd" + wkd "github.com/emersion/go-openpgp-wkd" "github.com/emersion/go-smtp" "golang.org/x/crypto/openpgp" ) // A Session is returned after EHLO. type Session struct { - user string + user User from string tos []string } @@ -37,7 +37,7 @@ func (s *Session) AuthPlain(username, password string) error { return fmt.Errorf("user not authorized") } slog.Debug("authorized", "user", username) - s.user = username + s.user = v.GetUser(username) return nil } @@ -47,7 +47,7 @@ func (s *Session) Mail(from string, opts *smtp.MailOptions) error { return nil } -func (s *Session) Rcpt(to string) error { +func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error { slog.Debug("received rcpt to", "to", to) s.tos = append(s.tos, to) return nil @@ -83,7 +83,7 @@ func (s *Session) Data(raw io.Reader) error { dom := strings.Split(s.from, "@")[1] if dom == cfg.Domain { // check auth - if s.user == "" { + if s.user.Name == "" { return smtp.ErrAuthRequired } err = s.Send(s.from, tos, strings.NewReader(string(cont))) @@ -180,7 +180,6 @@ func (s *Session) Send(from string, tos []*mail.Address, raw io.Reader) error { return err } - // TODO: use external package slog.Debug("checking wkd key") key, _ := wkd.Discover(to.Address) if key != nil { @@ -210,7 +209,7 @@ func (s *Session) Send(from string, tos []*mail.Address, raw io.Reader) error { // dkim slog.Debug("dkim check") res := bytes.Buffer{} - if keyPath := v.GetUser(s.user).PrivateKey; keyPath != "" { + if keyPath := s.user.PrivateKey; keyPath != "" { slog.Debug("user has dkim key") keyData, err := os.ReadFile(keyPath)
diff --git a/go.mod b/go.mod index c31be00..41811e6 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,10 @@ module dovel.email/server go 1.19 require ( - git.derelict.garden/bryon/vault v0.3.0 github.com/emersion/go-mbox v1.0.3 github.com/emersion/go-msgauth v0.6.6 github.com/emersion/go-openpgp-wkd v0.0.0-20200304100729-bbe7fac61be2 - github.com/emersion/go-smtp v0.16.0 + github.com/emersion/go-smtp v0.20.0 golang.org/x/crypto v0.7.0 )
diff --git a/go.sum b/go.sum index da7ec0f..b32889c 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8b github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-smtp v0.16.0 h1:eB9CY9527WdEZSs5sWisTmilDX7gG+Q/2IdRcmubpa8= github.com/emersion/go-smtp v0.16.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.20.0 h1:eTD2iPzYKRrFF+NDHPwKgsBt5ZmB2jO6P2rP2LlVYWI= +github.com/emersion/go-smtp v0.20.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
diff --git a/main.go b/main.go index 7490266..e57d5a4 100644 --- a/main.go +++ b/main.go @@ -8,14 +8,13 @@ import ( "path" "time" - "git.derelict.garden/bryon/vault" "github.com/emersion/go-smtp" ) var ( cfg = Config{} configPath string - v vault.Vault[User] + v store ) func main() { @@ -42,15 +41,6 @@ func main() { json.NewDecoder(configFile).Decode(&cfg) slog.Debug("config loaded", "config", cfg) - if cfg.VaultFile != "" { - slog.Debug("creating vault", "path", cfg.VaultFile) - v, err = vault.NewJSONPlainTextVault[User](cfg.VaultFile) - if err != nil { - panic(err) - } - slog.Debug("vault created", "vault", v) - } - tlsCfg := &tls.Config{} if cfg.Certificate != "" { slog.Debug("loading certs", "cert", cfg.Certificate, "key", cfg.PrivateKey) @@ -72,10 +62,10 @@ func main() { if len(tlsCfg.Certificates) > 0 { s.TLSConfig = tlsCfg - slog.Debug("starting server", "tls", tlsCfg) + slog.Info("server started", "tls", tlsCfg, "port", cfg.Port) err = s.ListenAndServeTLS() } else { - slog.Debug("starting server") + slog.Info("server started", "port", cfg.Port) err = s.ListenAndServe() } if err != nil {
diff --git a/model.go b/model.go index 56c314b..6bb2bc3 100644 --- a/model.go +++ b/model.go @@ -11,11 +11,16 @@ package main type Config struct { Port string Domain string - VaultFile string Certificate string PrivateKey string } +// Vault interface gives validation and user fetching. +type Vault interface { + Validate(n, p string) bool + GetUser(n string) User +} + // User represents a user that should be able to send emails. This struct is // found in the users json file, that is on the path pointed by VaultFile field // in [Config]. PrivateKey is the path to a private key for the DKIM signature. @@ -26,10 +31,20 @@ type User struct { PrivateKey string } -func (w User) Login() string { - return w.Name +type store struct { + users []User +} + +func (s store) Validate(n, p string) bool { + u := s.GetUser(n) + return u.Password == p } -func (w User) Pass() string { - return w.Password +func (s store) GetUser(n string) User { + for _, u := range s.users { + if u.Name == n { + return u + } + } + return User{} }