33 Commits

Author SHA1 Message Date
Bernhard Froehlich
ed1c3a9888 Merge branch 'master' of github.com:decke/smtprelay 2020-06-07 17:19:52 +00:00
Bernhard Froehlich
6f3bd16988 The check if authentication was properly done is redundant now as of smtpd v0.2.0
See:	32be721d71
2020-06-07 17:17:28 +00:00
Bernhard Froehlich
4e0bf0908d Update dependencies 2020-06-07 17:15:57 +00:00
Bernhard Fröhlich
6662fb7155 Merge pull request #3 from nwillems/add-hasher-tool
Add helper to do hashing
2020-06-06 07:58:51 +02:00
Nicolai Willems
076fd65dea Use default cost for bcrypt 2020-06-05 22:48:33 +02:00
Bernhard Froehlich
880c3c365c Update dependencies and chrj/smtpd to latest master which contains a fix for us 2020-06-04 08:41:19 +00:00
Nicolai Willems
36673ae3f0 Add helper to do hashing 2020-05-24 21:07:29 +02:00
Bernhard Froehlich
b42ad6ddc9 Add release script as requested in #2 2020-05-20 18:33:40 +00:00
Bernhard Froehlich
999ccab778 Fix spelling 2020-05-16 10:44:04 +00:00
Bernhard Froehlich
53c2c27647 Support LOGIN authentication on outgoing SMTP server
PR:		#1
Obtained from:	https://gist.github.com/andelf/5118732
2020-05-15 21:08:17 +00:00
Bernhard Froehlich
2afbe67407 Add Go Report Card badge 2020-05-11 13:56:24 +00:00
Bernhard Froehlich
00b96161b3 Remove duplication of TLS cipher suites for tls:// and startssl:// 2020-05-11 13:52:25 +00:00
Bernhard Froehlich
e10cbcdbb0 Update crypto dependency 2020-05-11 13:33:33 +00:00
Bernhard Froehlich
0e643f7230 Update CI to go 1.14 2020-03-02 10:46:59 +00:00
Bernhard Froehlich
324585c63c Update list of cipher suites and add ciphers for TLS 1.3 2020-03-02 10:45:02 +00:00
Bernhard Froehlich
92cb02e46b Update crypto dependency 2020-03-02 10:44:35 +00:00
Bernhard Froehlich
62fcc11f61 Update crypto dependency 2019-12-16 19:20:48 +00:00
Bernhard Froehlich
bfe8d39bb3 Merge branch 'master' of github.com:decke/smtprelay 2019-10-30 20:58:57 +00:00
Bernhard Froehlich
cbb6f523c5 Update crypto dependency 2019-10-30 20:57:38 +00:00
Bernhard Fröhlich
eb4a6b9eb6 Remove Travis CI 2019-10-20 13:24:42 +02:00
Bernhard Fröhlich
45db4ef786 Play with GitHub Actions 2019-10-20 13:21:39 +02:00
Bernhard Froehlich
946effcbcf Use Go 1.13 for CI 2019-10-14 09:55:05 +00:00
Bernhard Froehlich
118d1b88c2 Add Travis CI support and remove drone.yml 2019-10-14 09:51:09 +00:00
Bernhard Froehlich
5fd6aad9b1 Update project URL from code.bluelife.at to GitHub 2019-10-14 09:29:49 +00:00
Bernhard Froehlich
21b597f351 Bump version to 1.3.0 2019-09-07 11:31:28 +00:00
Bernhard Froehlich
5f82b4736c Update dependencies 2019-09-07 11:31:05 +00:00
Bernhard Fröhlich
de430286b3 Merge branch 'remote-sender' of beppler/smtprelay into master 2019-09-07 06:20:28 +00:00
Carlos Alberto Costa Beppler
769193ea4d Adjust the description of remote_sender parameter.
It represents the e-mail address used while sending message to the outgoing SMTP server.
2019-09-06 21:00:35 -03:00
Carlos Alberto Costa Beppler
0b65e904d8 Allows specify the sender used on SMTP conversation with outgoing server. 2019-09-06 17:07:37 -03:00
Bernhard Froehlich
2c9645ac68 Add drone config 2019-05-09 08:17:45 +00:00
Bernhard Froehlich
770e819e2b Improve error checking 2019-02-21 08:29:49 +00:00
Bernhard Froehlich
d11f8d81ea Fix formatting 2019-02-21 08:27:12 +00:00
Bernhard Froehlich
6270d75571 Improve TLS Config to prefer server ciphers, remove 3DES ciphers and require TLS 1.1 or higher 2019-01-08 15:09:29 +00:00
12 changed files with 211 additions and 38 deletions

24
.github/workflows/go.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Go
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.14
uses: actions/setup-go@v1
with:
go-version: 1.14
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -t -d ./...
- name: Build
run: go build -v .

View File

@@ -1,5 +1,7 @@
# smtprelay
[![Go Report Card](https://goreportcard.com/badge/github.com/decke/smtprelay)](https://goreportcard.com/report/github.com/decke/smtprelay)
Simple Golang based SMTP relay/proxy server that accepts mail via SMTP
and forwards it directly to another SMTP server.

View File

@@ -29,7 +29,7 @@ func AuthReady() bool {
}
func AuthFetch(username string) (string, string, error) {
if ! AuthReady() {
if !AuthReady() {
return "", "", errors.New("Authentication file not specified. Call LoadFile() first")
}
@@ -65,4 +65,3 @@ func AuthCheckPassword(username string, secret string) error {
}
return errors.New("Password invalid")
}

6
cmd/README.md Normal file
View File

@@ -0,0 +1,6 @@
To run the hasher, do like this
```bash
$ go run hasher.go hunter2
```

18
cmd/hasher.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"fmt"
"os"
"golang.org/x/crypto/bcrypt"
)
func main() {
password := os.Args[1]
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
fmt.Println("Error generating hash: %s", err)
}
fmt.Println(string(hash))
}

View File

@@ -7,7 +7,7 @@ import (
)
const (
VERSION = "1.2.0"
VERSION = "1.3.0"
)
var (
@@ -25,7 +25,9 @@ var (
remoteHost = flag.String("remote_host", "smtp.gmail.com:587", "Outgoing SMTP server")
remoteUser = flag.String("remote_user", "", "Username for authentication on outgoing SMTP server")
remotePass = flag.String("remote_pass", "", "Password for authentication on outgoing SMTP server")
versionInfo= flag.Bool("version", false, "Show version information")
remoteAuth = flag.String("remote_auth", "plain", "Auth method on outgoing SMTP server (plain, login)")
remoteSender = flag.String("remote_sender", "", "Sender e-mail address on outgoing SMTP server")
versionInfo = flag.Bool("version", false, "Show version information")
)
func ConfigLoad() {

8
go.mod
View File

@@ -1,7 +1,9 @@
module code.bluelife.at/decke/smtprelay
module github.com/decke/smtprelay
require (
github.com/chrj/smtpd v0.1.2
github.com/chrj/smtpd v0.2.0
github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
)
go 1.13

13
go.sum
View File

@@ -1,7 +1,12 @@
github.com/chrj/smtpd v0.1.2 h1:yWaMOCmnPlcNgJzkak1TBhhkObAfomd+NmZG5epdO88=
github.com/chrj/smtpd v0.1.2/go.mod h1:jt4ydELuZmqhn9hn3YpEPV1dY00aOB+Q1nWXnBDFKeY=
github.com/chrj/smtpd v0.2.0 h1:QGbE4UQz7sKjvXpRgNLuiBOjcWTzBKu/dj0hyDLpD14=
github.com/chrj/smtpd v0.2.0/go.mod h1:1hmG9KbrE10JG1SmvG79Krh4F6713oUrw2+gRp1oSYk=
github.com/eaigner/dkim v0.0.0-20150301120808-6fe4a7ee9cfb/go.mod h1:FSCIHbrqk7D01Mj8y/jW+NS1uoCerr+ad+IckTHTFf4=
github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de h1:fkw+7JkxF3U1GzQoX9h69Wvtvxajo5Rbzy6+YMMzPIg=
github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de/go.mod h1:irMhzlTz8+fVFj6CH2AN2i+WI5S6wWFtK3MBCIxIpyI=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

73
main.go
View File

@@ -25,7 +25,7 @@ func connectionChecker(peer smtpd.Peer) error {
nets := strings.Split(*allowedNets, " ")
for i := range(nets) {
for i := range nets {
_, allowedNet, _ := net.ParseCIDR(nets[i])
if allowedNet.Contains(peerIP) {
@@ -94,10 +94,6 @@ func authChecker(peer smtpd.Peer, username string, password string) error {
}
func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error {
if *allowedUsers != "" && peer.Username == "" {
return smtpd.Error{Code: 530, Message: "Authentication Required"}
}
peerIP := ""
if addr, ok := peer.Addr.(*net.TCPAddr); ok {
peerIP = addr.IP.String()
@@ -110,22 +106,37 @@ func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error {
host, _, _ := net.SplitHostPort(*remoteHost)
if *remoteUser != "" && *remotePass != "" {
switch *remoteAuth {
case "plain":
auth = smtp.PlainAuth("", *remoteUser, *remotePass, host)
case "login":
auth = LoginAuth(*remoteUser, *remotePass)
default:
return smtpd.Error{Code: 530, Message: "Authentication method not supported"}
}
}
env.AddReceivedLine(peer)
log.Printf("delivering using smarthost %s\n", *remoteHost)
var sender string
if *remoteSender == "" {
sender = env.Sender
} else {
sender = *remoteSender
}
err := SendMail(
*remoteHost,
auth,
env.Sender,
sender,
env.Recipients,
env.Data,
)
if err != nil {
log.Printf("delivery failed: %v\n", err);
log.Printf("delivery failed: %v\n", err)
return smtpd.Error{Code: 554, Message: "Forwarding failed"}
}
@@ -135,6 +146,30 @@ func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error {
}
func main() {
// Ciphersuites as defined in stock Go but without 3DES and RC4
// https://golang.org/src/crypto/tls/cipher_suites.go
var tlsCipherSuites = []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // does not provide PFS
tls.TLS_RSA_WITH_AES_256_GCM_SHA384, // does not provide PFS
tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
}
ConfigLoad()
@@ -144,7 +179,7 @@ func main() {
}
if *logFile != "" {
f, err := os.OpenFile(*logFile, os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0600)
f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
log.Fatalf("Error opening logfile: %v", err)
}
@@ -155,7 +190,7 @@ func main() {
listeners := strings.Split(*listen, " ")
for i := range(listeners) {
for i := range listeners {
listener := listeners[i]
server := &smtpd.Server{
@@ -191,13 +226,19 @@ func main() {
log.Fatal(err)
}
server.TLSConfig = &tls.Config {
Certificates: [] tls.Certificate{cert},
server.TLSConfig = &tls.Config{
PreferServerCipherSuites: true,
MinVersion: tls.VersionTLS11,
CipherSuites: tlsCipherSuites,
Certificates: []tls.Certificate{cert},
}
server.ForceTLS = *localForceTLS
log.Printf("Listen on %s (STARTSSL) ...\n", listener)
lsnr, err := net.Listen("tcp", listener)
if err != nil {
log.Fatal(err)
}
defer lsnr.Close()
go server.Serve(lsnr)
@@ -214,12 +255,18 @@ func main() {
log.Fatal(err)
}
server.TLSConfig = &tls.Config {
Certificates: [] tls.Certificate{cert},
server.TLSConfig = &tls.Config{
PreferServerCipherSuites: true,
MinVersion: tls.VersionTLS11,
CipherSuites: tlsCipherSuites,
Certificates: []tls.Certificate{cert},
}
log.Printf("Listen on %s (TLS) ...\n", listener)
lsnr, err := tls.Listen("tcp", listener, server.TLSConfig)
if err != nil {
log.Fatal(err)
}
defer lsnr.Close()
go server.Serve(lsnr)

34
scripts/release.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/bin/sh
PROJECT=smtprelay
VERSION=1.3.0
for goos in freebsd linux windows
do
for goarch in 386 amd64
do
export GOOS=${goos}
export GOARCH=${goarch}
RELDIR=${PROJECT}-${VERSION}-${GOOS}-${GOARCH}
rm -rf ${RELDIR}
mkdir ${RELDIR} || exit 1
cp -p README.md LICENSE ${PROJECT}.ini ${RELDIR}/ || exit 1
if [ ${GOOS} = "windows" ]; then
BINARY=${PROJECT}.exe
sed -I '' -e 's/;logfile =.*/logfile =/g' ${RELDIR}/${PROJECT}.ini
sed -I '' -e 's/$/^M/' ${RELDIR}/${PROJECT}.ini
else
BINARY=${PROJECT}
fi
go build -ldflags="-s -w" -o ${RELDIR}/${BINARY} || exit 1
chown -R root:wheel ${RELDIR} || exit 1
tar cvfJ ${RELDIR}.tar.xz ${RELDIR} || exit 1
rm -rf ${RELDIR}
done
done

27
smtp.go
View File

@@ -451,3 +451,30 @@ func validateLine(line string) error {
}
return nil
}
// LOGIN authentication
type loginAuth struct {
username, password string
}
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", []byte{}, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.New("Unknown fromServer")
}
}
return nil, nil
}

View File

@@ -54,3 +54,10 @@
; Authentication credentials on outgoing SMTP server
;remote_user =
;remote_pass =
; Authentication method on outgoing SMTP server
; (plain, login)
;remote_auth = plain
; Sender e-mail address on outgoing SMTP server
;remote_sender =