This is the main dovel repository, it has the Go code to run dovel SMTP server.
Author: blmayer (bleemayer@gmail.com)
Date: Fri Sep 15 22:01:05 2023 -0300
Parent: eca63cc
Fixed dkim sign * Added slogs * Added -O flag to scp
commit 8b90660c528793baf7209ac3e95d9bf401d493f3
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 15 22:01:05 2023 -0300
Fixed dkim sign
* Added slogs
* Added -O flag to scp
diff --git a/Makefile b/Makefile
index 44854dd..63b4825 100644
--- a/Makefile
+++ b/Makefile
@@ -1,20 +1,9 @@
-.PHONY: clean deploy deploy-site deploy-assets deploy-pages
+.PHONY: deploy
dovel: $(wildcard cmd/dovel/*.go *.go util/*/*.go)
GOARCH=arm GOARM=6 go build -o $@ cmd/dovel/*.go
deploy: dovel
- scp $^ zero:
+ scp -O $^ zero:
ssh zero "mv $^ .local/bin/"
-deploy-site: index.html www/assets/favicon.ico www/assets/logo.png
- scp $^ zero:dovel.email/
-
-deploy-assets: $(wildcard www/assets/*)
- scp $^ zero:blmayer.dev/www/mail/assets/
-
-deploy-pages: $(wildcard www/*.*)
- scp $^ zero:blmayer.dev/www/mail
-
-deploy-certs: $(wildcard *.pem)
- scp $^ zero:certs/dovel.email/
commit 8b90660c528793baf7209ac3e95d9bf401d493f3
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 15 22:01:05 2023 -0300
Fixed dkim sign
* Added slogs
* Added -O flag to scp
diff --git a/cmd/dovel/backend.go b/cmd/dovel/backend.go
index 061c6da..754a898 100644
--- a/cmd/dovel/backend.go
+++ b/cmd/dovel/backend.go
@@ -3,8 +3,12 @@ package main
import (
"bufio"
"bytes"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
"fmt"
"io"
+ "log/slog"
"net"
"net/mail"
"net/textproto"
@@ -29,21 +33,24 @@ type Session struct {
}
func (s *Session) AuthPlain(username, password string) error {
+ slog.Debug("authenticating", "user", username, "pass", password)
if !v.Validate(username, password) {
+ slog.Warn(username, "not authorized")
return fmt.Errorf("user not authorized")
}
+ slog.Debug("authorized", "user", username)
s.user = username
return nil
}
func (s *Session) Mail(from string, opts *smtp.MailOptions) error {
- println("Mail from:", from)
+ slog.Debug("received mail", "from", from)
s.from = from
return nil
}
func (s *Session) Rcpt(to string) error {
- println("Rcpt to:", to)
+ slog.Debug("received rcpt to", "to", to)
s.tos = append(s.tos, to)
return nil
}
@@ -53,6 +60,7 @@ func (s *Session) Data(raw io.Reader) error {
if err != nil {
return err
}
+ slog.Debug("received data", "data", cont)
w := bytes.Buffer{}
box := mbox.NewWriter(&w)
@@ -70,20 +78,24 @@ func (s *Session) Data(raw io.Reader) error {
// sending email
dom := strings.Split(s.from, "@")[1]
- if dom == domain {
+ if dom == cfg.Domain {
+ // check auth
+ if s.user == "" {
+ return smtp.ErrAuthRequired
+ }
err = s.Send(s.from, s.tos, strings.NewReader(string(cont)))
if err != nil {
- println("send", err.Error())
+ slog.Error("send error", "msg", err.Error())
return err
}
// running handlers is optional
- h := path.Join(configPath, "hooks", "send-"+domain)
+ h := path.Join(configPath, "hooks", "send-"+cfg.Domain)
if f, err := os.Lstat(h); err == nil {
if !f.Mode().IsRegular() {
h, err = os.Readlink(h)
if err != nil {
- println(domain, "read link", err.Error())
+ slog.Error(cfg.Domain, "read link", err.Error())
}
}
@@ -91,7 +103,7 @@ func (s *Session) Data(raw io.Reader) error {
c.Stdin = strings.NewReader(string(mess))
c.Stdout = os.Stdout
if err := c.Run(); err != nil {
- println("run script", err.Error())
+ slog.Error("run script", err.Error())
return err
}
}
@@ -101,19 +113,19 @@ func (s *Session) Data(raw io.Reader) error {
for _, to := range s.tos {
toAddr, err := mail.ParseAddress(to)
if err != nil {
- println("parse to", err.Error())
+ slog.Error("parse to", err.Error())
return err
}
domain := strings.Split(toAddr.Address, "@")[1]
h := path.Join(configPath, "hooks", "receive-"+domain)
if f, err := os.Lstat(h); err != nil {
- println(domain, "receive error:", err.Error())
+ slog.Error(domain, "receive error:", err.Error())
continue
} else if !f.Mode().IsRegular() {
h, err = os.Readlink(h)
if err != nil {
- println(domain, "read link", err.Error())
+ slog.Error(domain, "read link", err.Error())
}
}
@@ -121,7 +133,7 @@ func (s *Session) Data(raw io.Reader) error {
c.Stdin = strings.NewReader(string(mess))
c.Stdout = os.Stdout
if err := c.Run(); err != nil {
- println("run script", err.Error())
+ slog.Error("run script", err.Error())
continue
}
}
@@ -132,7 +144,7 @@ func (s *Session) Data(raw io.Reader) error {
func (s *Session) Reset() {}
func (s *Session) Logout() error {
- println("logged out")
+ slog.Debug("logged out", "user", s.user)
return nil
}
@@ -154,6 +166,7 @@ func (s *Session) Send(from string, tos []string, raw io.Reader) error {
return err
}
for _, to := range tos {
+ slog.Debug("sending email", "to", to)
msg := string(body)
// dns mx for email
@@ -166,13 +179,16 @@ func (s *Session) Send(from string, tos []string, raw io.Reader) error {
return err
}
+ // TODO: use package from emersion
+ slog.Debug("checking wkd key")
key, _ := wkd.FetchPGPKey(addr[0], addr[1])
if key != "" {
- email.Header["Content-Type"] = []string{"application/pgp-encrypted"}
+ slog.Debug("found wkd key")
msg, err = helper.EncryptMessageArmored(key, msg)
if err != nil {
return err
}
+ email.Header["Content-Type"] = []string{"application/pgp-encrypted"}
}
payload := bytes.Buffer{}
@@ -184,22 +200,37 @@ func (s *Session) Send(from string, tos []string, raw io.Reader) error {
payload.Write([]byte(msg))
// dkim
+ slog.Debug("dkim check")
res := bytes.Buffer{}
- if sig := v.GetUser(s.user).PrivateKey; sig != nil {
+ if keyPath := v.GetUser(s.user).PrivateKey; keyPath != "" {
+ slog.Debug("user has dkim key")
+
+ keyData, err := os.ReadFile(keyPath)
+ if err != nil {
+ return err
+ }
+ block, _ := pem.Decode(keyData)
+ privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+ if err != nil {
+ return(err)
+ }
options := &dkim.SignOptions{
Domain: fromdom[1],
Selector: "dkim",
- Signer: v.GetUser(s.user).PrivateKey,
+ Signer: privateKey.(*rsa.PrivateKey),
}
+
err = dkim.Sign(&res, &payload, options)
if err != nil {
- println("failed to sign body:", err.Error())
+ slog.Error("failed to sign body", "err", err)
}
} else {
+ slog.Debug("no dkim key")
io.Copy(&res, &payload)
}
server := mxs[0].Host + ":smtp"
+ slog.Debug("sending", "host", server, "from", from, "to", tos)
err = smtp.SendMail(
server,
nil,
@@ -211,6 +242,8 @@ func (s *Session) Send(from string, tos []string, raw io.Reader) error {
return err
}
}
+
+ slog.Debug("email sent")
return nil
}
commit 8b90660c528793baf7209ac3e95d9bf401d493f3
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 15 22:01:05 2023 -0300
Fixed dkim sign
* Added slogs
* Added -O flag to scp
diff --git a/cmd/dovel/main.go b/cmd/dovel/main.go
index b99bb50..26ac9c5 100644
--- a/cmd/dovel/main.go
+++ b/cmd/dovel/main.go
@@ -3,6 +3,7 @@ package main
import (
"crypto/tls"
"encoding/json"
+ "log/slog"
"os"
"path"
"time"
@@ -13,16 +14,24 @@ import (
)
var (
- domain string
+ cfg = email.Config{}
configPath string
v vault.Vault[email.User]
)
func main() {
+ if os.Getenv("DEBUG") != "" {
+ hand := slog.NewTextHandler(
+ os.Stdout,
+ &slog.HandlerOptions{Level: slog.LevelDebug},
+ )
+ slog.SetDefault(slog.New(hand))
+ }
+
var err error
configPath, err = os.UserConfigDir()
if err != nil {
- println(err, "using ~/.config/dovel/config.json")
+ slog.Error(err.Error(), "using ~/.config/dovel/config.json")
configPath = "~/.config"
}
configPath = path.Join(configPath, "dovel")
@@ -31,19 +40,21 @@ func main() {
panic(err)
}
- cfg := email.Config{}
json.NewDecoder(configFile).Decode(&cfg)
- domain = cfg.Domain
+ slog.Debug("config loaded", "config", cfg)
if cfg.VaultFile != "" {
+ slog.Debug("creating vault", "path", cfg.VaultFile)
v, err = vault.NewJSONPlainTextVault[email.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)
c, err := tls.LoadX509KeyPair(cfg.Certificate, cfg.PrivateKey)
if err != nil {
panic(err)
@@ -58,11 +69,14 @@ func main() {
s.WriteTimeout = 10 * time.Second
s.MaxMessageBytes = 1024 * 1024
s.MaxRecipients = 5
+ s.AllowInsecureAuth = true
if len(tlsCfg.Certificates) > 0 {
s.TLSConfig = tlsCfg
+ slog.Debug("starting server", "tls", tlsCfg)
err = s.ListenAndServeTLS()
} else {
+ slog.Debug("starting server")
err = s.ListenAndServe()
}
if err != nil {
commit 8b90660c528793baf7209ac3e95d9bf401d493f3
Author: blmayer <bleemayer@gmail.com>
Date: Fri Sep 15 22:01:05 2023 -0300
Fixed dkim sign
* Added slogs
* Added -O flag to scp
diff --git a/model.go b/model.go
index 5b0b3c5..f872591 100644
--- a/model.go
+++ b/model.go
@@ -1,9 +1,5 @@
package email
-import (
- "crypto"
-)
-
// Config is used to configure your email server.
// Port is used to specify which port the server should listen
// to connections, a typicall value is 2525.
@@ -11,7 +7,7 @@ import (
// VaultFile is the path to a json file containing the list of users
// that can send email.
// In order to use TLS connections Certficate and PrivateKey fields
- // must have valid path to pem encoded keys.
+// must have valid path to pem encoded keys.
type Config struct {
Port string
Domain string
@@ -27,7 +23,7 @@ type User struct {
Name string
Email string
Password string
- PrivateKey crypto.Signer
+ PrivateKey string
}
func (w User) Login() string {