go-micro-安全tls

go-micro-安全tls


前篇

a


内置的 tls 证书

是用 micro 默认的 tls 非常简单, 只需要 transport.Secure(true) 即可

1
2
3
4
5
6
7
8
9
10
func main() {
service := micro.NewService(
...
micro.Transport(
transport.NewTransport(
transport.Secure(true),
),
),
)
}

自定义 tls 证书

参考: https://studygolang.com/articles/15331

生成 CA 根证书

为了保证证书的可靠性和有效性,在这里可引入 CA 颁发的根证书的概念。其遵守 X.509 标准

根证书(root certificate)是属于根证书颁发机构(CA)的公钥证书。我们可以通过验证 CA 的签名从而信任 CA ,任何人都可以得到 CA 的证书(含公钥),用以验证它所签发的证书(客户端、服务端)

  1. 生成 Key: ca.key

    1
    $ openssl genrsa -out ca.key 2048
  2. 生成密钥: ca.pem

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ openssl req -new -x509 -days 7200 -key ca.key -out ca.pem

    Country Name (2 letter code) [AU]:aa // // 填一堆信息
    State or Province Name (full name) [Some-State]:bb
    Locality Name (eg, city) []:cc
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:dd
    Organizational Unit Name (eg, section) []:ee
    Common Name (e.g. server FQDN or YOUR name) []:ff
    Email Address []:gg

生成 Server 证书

  1. 生成 Key: server.key

    1
    $ openssl ecparam -genkey -name secp384r1 -out server.key
  2. 生成 CSR: server.csr

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ openssl req -new -key server.key -out server.csr

    Country Name (2 letter code) [AU]:aa // 填一堆信息
    State or Province Name (full name) [Some-State]:bb
    Locality Name (eg, city) []:cc
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:dd
    Organizational Unit Name (eg, section) []:ee
    Common Name (e.g. server FQDN or YOUR name) []:ff
    Email Address []:gg

    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:helloworld
    An optional company name []:aa
  3. 生成 ip 白名单文件: extfile.cnf

    1
    $ echo subjectAltName = IP:192.168.1.190 > extfile.cnf
    • extfile.cnf 文件内容

      1
      subjectAltName = IP:192.168.1.190

      如果有多个 ip,用 , 分割

      1
      subjectAltName = IP:192.168.1.191,IP:192.168.1.190,IP:192.168.1.192
  4. 基于 CA 签发: server.pem

    1
    2
    3
    4
    5
    $ openssl x509 -req -sha256 -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -in server.csr -out server.pem -extfile extfile.cnf

    Signature ok
    subject=C = aa, ST = bb, L = cc, O = dd, OU = ee, CN = ff, emailAddress = gg
    Getting CA Private Key
    • 如果少了访问的 ip 白名单文件 ( -extfile extfile.cnf ) , 客户端请求报错: doesn't contain any IP SANs

最后生成一堆东西

1
2
3
4
5
6
7
8
├─conf
│ ca.key
│ ca.pem
│ ca.srl
│ extfile.cnf
│ server.csr
│ server.key
│ server.pem

代码

参考: test_secure_tls

客户端 和 服务器 都是用相同的证书即可 server.pem, server.key

server
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
26
27
28
29
30
31
32
33
34
35
36
func loadTlsConfig() *tls.Config {
cert, err := tls.LoadX509KeyPair("../conf/server.pem", "../conf/server.key")
if err != nil {
log.Fatalf("tls.LoadX509KeyPair err: %v", err)
}

certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile("../conf/ca.pem")
if err != nil {
log.Fatalf("ioutil.ReadFile err: %v", err)
}

if ok := certPool.AppendCertsFromPEM(ca); !ok {
log.Fatalf("certPool.AppendCertsFromPEM err")
}

tlsCfg := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: certPool,
InsecureSkipVerify: false,
}
return tlsCfg
}

func main() {
service := micro.NewService(
...
micro.Transport(
transport.NewTransport(
transport.TLSConfig(loadTlsConfig()),
),
),
)

}

client
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
func loadTlsConfig() *tls.Config {
cert, err := tls.LoadX509KeyPair("../conf/server.pem", "../conf/server.key")
if err != nil {
log.Fatalf("tls.LoadX509KeyPair err: %v", err)
}

certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile("../conf/ca.pem")
if err != nil {
log.Fatalf("ioutil.ReadFile err: %v", err)
}

if ok := certPool.AppendCertsFromPEM(ca); !ok {
log.Fatalf("certPool.AppendCertsFromPEM err")
}

tlsCfg := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: certPool,
InsecureSkipVerify: false,
}
return tlsCfg
}

func init() {
client.DefaultClient.Init(
client.Transport(
transport.NewTransport(
transport.TLSConfig(loadTlsConfig()),
),
),
)
}

func main() {
cl := hello.NewSayService("go.micro.srv.greeter", client.DefaultClient)

rsp, err := cl.Hello(context.TODO(), &hello.Request{
Name: "John",
})
if err != nil {
fmt.Println(err)
return
}

fmt.Println("--- recv srv, msg:", rsp.Msg)
}

踩坑

  • 客户端请求报错: doesn't contain any IP SANs

    参考: https://codeday.me/bug/20190105/485233.html

    是因为 [生成 Server 证书](#生成 Server 证书) 中的 第4步 中没有指定可访问的 ip 白名单文件. ( -extfile extfile.cnf )