今天在做concourse发邮件这部分,一看官网上有开箱即用的resource, 心想今天能早点下班了。官方地址如下:
https://github.com/pivotal-cf/email-resource
问题
根据README.md配置了关于邮件发送这块的resource,发现一直卡Dial状态,接着就超时了。
我想是官方pivotal-cf group下的,再怎么也不会出问题吧,应该是我哪里配置配的有问题,然后我又捣鼓了一个小时,还是没搞定。
pipeline配置 1 2 3 4 5 6 7 8 9 10 11 12 resources: - name: send-an-email type: email source: smtp: host: smtp.example.com port: "465" username: a-user password: my-password from: build-system@example.com to: [ "dev-team@example.com" , "product@example.net" ]
我的pipeline resource定义完全就是照搬的github上的样例,就是把他的587端口修改成了465,因为我司是用的阿里云邮箱,使用的是465端口,为什么就无法发送了呢?
原因分析
接着我选了个587端口的邮箱,gmail,发现是可以正常发送的。
难道是被阿里云禁止? 这个应该不可能,因为换成用python的脚本是可以正常发送,而且阿里云只禁止25端口
465端口和587端口的区别 好好的为什么会有两个端口,我就去谷歌上查了下。
465端口可以理解为全程走TLS,在你的本地mail sender和mail server建联的时候就是采用了TLS。
而587不是,587端口你可以通过明文,如果可能,则可以升级使用TLS方式发送邮件。采用的是STARTTLS协议
两者在行为上还是存在区别。
源码分析
https://github.com/pivotal-cf/email-resource/blob/master/out/sender.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 c, err = smtp.Dial(fmt.Sprintf("%s:%s" , s.host, s.port)) ... .. if ok, _ := c.Extension("STARTTLS" ); ok { config := s.tlsConfig() if err = c.StartTLS(config); err != nil { return errors.Wrap(err, "unable to start TLS" ) } } ..
可以看到他用smtp.Dial 方法去连接,然后调用StartTLS 方法传入配置
如果查看smtp.Dial方法的源码,可以看到其方法是调用的net.Dial方法,这个方法并不是用来建立SSL连接的
1 2 3 4 5 6 7 8 9 10 func Dial (addr string ) (*Client, error) { conn, err := net.Dial("tcp" , addr) if err != nil { return nil , err } host, _, _ := net.SplitHostPort(addr) return NewClient(conn, host) }
解决方法 既然知道了465的行为,也就是在一开始的时候就需要去做ssl的建联,而非通过smtp.Dial,所以将代码修改如下:
https://github.com/huangyisan/email-resource/blob/master/out/sender.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 if s.port == "465" { fmt.Fprintln(os.Stderr, "Dialing with TLS for 465 Port..." ) conn, err := tls.Dial("tcp" , fmt.Sprintf("%s:%s" ,s.host,s.port), s.tlsConfig()) if err != nil { return errors.Wrap(err, "unable to start TLS" ) } c, err = smtp.NewClient(conn, s.host) defer c.Close() } else { c, err = smtp.Dial(fmt.Sprintf("%s:%s" , s.host, s.port)) if err != nil { return errors.Wrap(err, "Error Dialing smtp server" ) } defer c.Close() if ok, _ := c.Extension("STARTTLS" ); ok { config := s.tlsConfig() if err = c.StartTLS(config); err != nil { return errors.Wrap(err, "unable to start TLS" ) } } }
在建联初期,使用tls.Dial 方法,传入tls的配置进行建联,得到一个conn对象,然后使用smtp.NewClient 方法传入conn对象,得到*smtp.Client 对象, 让该对象进行邮件发送处理。
refer
https://stackoverflow.com/questions/15796530/what-is-the-difference-between-ports-465-and-587
https://sendgrid.com/blog/what-is-starttls/
https://sendgrid.com/blog/whats-the-difference-between-ports-465-and-587/
https://gist.github.com/chrisgillis/10888032