在Go中将连接升级为TLS

huangapple go评论102阅读模式
英文:

Upgrade a connection to TLS in Go

问题

我有一个打开的TCP连接,并使用for循环从中读取,代码如下:

for {
  // tx.Text的类型是textproto.Conn
  // 底层连接存储在tx.Conn中
  l, err := tx.Text.Reader.ReadLine()

  // 处理文本行...
}

现在我想通过以下方式升级连接到TLS(TlsConf包含使用tls.LoadX509KeyPair加载的证书)

tx.Conn = tls.Server(tx.Conn, tx.Server.Conf.TlsConf)
tx.Text = textproto.NewConn(tx.Conn)

当我这样做时,当服务器尝试握手时,客户端会出现段错误。我正在实现一个SMTP服务器,并使用swaks-tls标志进行测试。swaks的终端输出如下:

-> STARTTLS
<-  220 Start TLS
Segmentation fault: 11

由于swaks是一个经过测试的工具,并且在我之前使用的nodeJS SMTP实现中工作正常,我不认为错误在客户端。

我做错了什么或者缺少了什么?

附注:当从现有的不安全连接启动TLS连接时,会发生什么?客户端是在不同的端口上建立新连接还是重用连接?

英文:

I have an open TCP connection and read from it with a for loop like so

for {
  // tx.Text is of type textproto.Conn
  // the underlying connection is stored in tx.Conn
  l, err := tx.Text.Reader.ReadLine()

  // do stuff with the text line ...
}

Now I want to upgrade the connection to TLS like this (TlsConf contains a certificate loaded with tls.LoadX509KeyPair)

tx.Conn = tls.Server(tx.Conn, tx.Server.Conf.TlsConf)
tx.Text = textproto.NewConn(tx.Conn)

When I do this I get a segmentation fault on the client when the server attempts a handshake. I'm implementing a SMTP server and am testing it with swaks using the -tls flag. The terminal output of swaks is the following

-&gt; STARTTLS
&lt;-  220 Start TLS
Segmentation fault: 11

Since swaks is a tested tool, and worked with the nodeJS SMTP implementation I had before, I don't suspect the error is on the client side.

What did I do wrong or what is missing?

PS: When a TLS connection is started from an existing insecure connection, what does exactly happen? Does the client establish a new connection on a different port or is the connection reused?

答案1

得分: 14

这是如何将net.conn升级为tls.conn的方法:

1)在代码的某个地方,你定义了这些变量

var TLSconfig *tls.Config
...
// conn是一个普通的net.Conn类型的连接
conn, err := listener.Accept()
...

2)在上面的某个地方初始化TLSConfig,可以像这样做

cert, err := tls.LoadX509KeyPair("/path/to/cert", "/path/to/key")
if err != nil {
    // ...
}
TLSconfig = &tls.Config{
Certificates: []tls.Certificate{cert}, 
ClientAuth: tls.VerifyClientCertIfGiven, 
ServerName: "example.com"}

3)此时你正在读写一个标准连接。

当客户端发出STARTTLS命令时,在你的服务器中执行以下操作:

// 初始化一个新的TLS连接。我需要一个*tls.Conn类型的连接
// 这样我才能执行Handshake()
var tlsConn *tls.Conn
tlsConn = tls.Server(client.socket, TLSconfig)
// 运行握手
tlsConn.Handshake()
// 这是关键。由于我不再需要访问任何TLS函数,
// 我可以将tlsConn转换回net.Conn类型
conn = net.Conn(tlsConn)

接下来,你可能需要使用新的连接更新缓冲区等。

像这样测试你的服务器:

openssl s_client -starttls smtp -crlf -connect  example.com:25

这样可以通过tls连接与服务器进行交互,并可以发出一些命令等。

关于Go中的类型转换的更多信息

我猜类型转换是Go如此强大的另一个原因!

http://golang.org/ref/spec#Conversions

http://golang.org/doc/effective_go.html#conversions

英文:

Here's how to upgrade a net.conn to tls.con:

  1. Somewhere in your code, you have these variables defined

    var TLSconfig *tls.Config
    ...
    // conn is a normal connection of type net.Conn
    conn, err := listener.Accept()
    ...

  2. Initialize TLSConfig somewhere above, do something like this

    cert, err := tls.LoadX509KeyPair("/path/to/cert", "/path/to/key")
    if err != nil {
    // ...
    }
    TLSconfig = &tls.Config{
    Certificates: []tls.Certificate{cert},
    ClientAuth: tls.VerifyClientCertIfGiven,
    ServerName: "example.com"}

  3. At this point you are reading/writing to a standard connection.

When the client issues STARTTLS command, do this in your server:

// Init a new TLS connection. I need a *tls.Conn type 
// so that I can do the Handshake()
var tlsConn *tls.Conn
tlsConn = tls.Server(client.socket, TLSconfig)
// run a handshake
tlsConn.Handshake()
// Here is the trick. Since I do not need to access 
// any of the TLS functions anymore,
// I can convert tlsConn back in to a net.Conn type
conn = net.Conn(tlsConn)

Next, you may probably update your buffers with the new connection, etc.

Test your server like this:

openssl s_client -starttls smtp -crlf -connect  example.com:25

This allows you to interact with the server through the tls connection and you can issue some commands, etc.

More about conversions in Go

I guess conversions are another reason for what makes Go so powerful!

http://golang.org/ref/spec#Conversions

http://golang.org/doc/effective_go.html#conversions

答案2

得分: 1

放弃了swaks,使用Go自带的smtp.SendMail构建了一个小工具来测试TLS:

package main

import (
  "fmt"
  "net/smtp"
)

func main() {
  err := smtp.SendMail(
    "127.0.0.1:2525",
    nil,
    "src@test.local",
    []string{"dst@test.local"},
    []byte("Hello! Just testing."),
  )
  if err != nil {
    panic(err)
  }
}
英文:

Ditched swaks, built a small tool to test TLS using Go's own smtp.SendMail:

package main

import (
  &quot;fmt&quot;
  &quot;net/smtp&quot;
)

func main() {
  err := smtp.SendMail(
    &quot;127.0.0.1:2525&quot;,
    nil,
    &quot;src@test.local&quot;,
    []string{&quot;dst@test.local&quot;},
    []byte(&quot;Hello! Just testing.&quot;),
  )
  if err != nil {
    panic(err)
  }
}

huangapple
  • 本文由 发表于 2012年10月29日 00:31:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/13110713.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定