You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中mTLS双向认证配置咨询(含SmallStep证书需求)

问题描述

我希望在Golang中使用mTLS实现一台服务器与多个客户端的连接,需求如下:

  • 服务器作为唯一证书生成源,负责生成客户端证书
  • 客户端仅能与服务器通信,彼此无法互联
  • 客户端在80端口暴露Golang HTTP服务,由服务器主动发起API请求调用客户端(仍称此节点为服务器,因它是证书唯一生成源并向客户端提供内容)

已尝试用SmallStep生成证书,但配置存在错误,当前未使用ca.crt,现咨询:该场景应如何配置?需生成哪些证书?能否让客户端使用服务器生成的证书监听请求?


解决方案

一、证书生成(SmallStep 实现)

1. 准备CA根证书

首先确保已初始化SmallStep CA(未初始化先执行step ca init),然后导出CA根证书——这是所有节点信任链的核心,必须在服务器和客户端都配置:

step ca root ca.crt

2. 生成服务器证书

服务器证书的标识要和实际访问地址匹配,保证客户端能识别:

step ca certificate "pm1" server.crt server.key

这里的pm1是服务器的唯一标识(域名/IP均可)。

3. 生成客户端证书

每个客户端需要唯一证书,且限制用途为客户端认证(避免客户端用此证书搭建服务端),同时添加唯一标识方便服务器验证:

step ca certificate --san "client-01" --profile "client" client-01.crt client-01.key
  • --profile "client":指定证书仅用于客户端认证,默认配置可能包含服务端用途,这样能防止客户端用该证书监听服务
  • --san "client-01":给客户端添加唯一标识,后续可用于身份校验

二、Golang代码配置

1. 服务器端(主动调用客户端HTTP服务)

服务器需要用自身证书作为客户端发起请求,同时验证客户端证书的合法性:

package main

import (
    "crypto/tls"
    "crypto/x509"
    "log"
    "net/http"
    "os"
)

func main() {
    // 加载CA根证书
    caCert, err := os.ReadFile("ca.crt")
    if err != nil {
        log.Fatalf("读取CA证书失败: %v", err)
    }
    caCertPool := x509.NewCertPool()
    if !caCertPool.AppendCertsFromPEM(caCert) {
        log.Fatal("添加CA证书到信任池失败")
    }

    // 加载服务器自身证书(发起请求时作为客户端身份凭证)
    serverCert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        log.Fatalf("加载服务器证书失败: %v", err)
    }

    // 配置HTTP Transport,用于发起mTLS请求
    transport := &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs:      caCertPool,          // 信任CA根证书,验证客户端证书合法性
            Certificates: []tls.Certificate{serverCert}, // 服务器自身证书,供客户端验证身份
            MinVersion:   tls.VersionTLS13,
        },
    }

    // 创建HTTP客户端
    client := &http.Client{Transport: transport}

    // 调用客户端API示例
    resp, err := client.Get("https://client-01:80/api/endpoint")
    if err != nil {
        log.Fatalf("调用客户端服务失败: %v", err)
    }
    defer resp.Body.Close()
    // 处理响应逻辑...
}

2. 客户端(监听HTTP服务,仅接受服务器请求)

客户端用自身证书作为服务端监听,同时验证服务器身份,确保只有指定服务器能访问:

package main

import (
    "crypto/tls"
    "crypto/x509"
    "log"
    "net/http"
    "os"
)

func main() {
    // 加载CA根证书
    caCert, err := os.ReadFile("ca.crt")
    if err != nil {
        log.Fatalf("读取CA证书失败: %v", err)
    }
    caCertPool := x509.NewCertPool()
    if !caCertPool.AppendCertsFromPEM(caCert) {
        log.Fatal("添加CA证书到信任池失败")
    }

    // 加载客户端自身证书(作为服务端监听时的身份凭证)
    clientCert, err := tls.LoadX509KeyPair("client-01.crt", "client-01.key")
    if err != nil {
        log.Fatalf("加载客户端证书失败: %v", err)
    }

    // 配置TLS,验证服务器身份并限制访问
    tlsConfig := &tls.Config{
        ClientCAs:    caCertPool,          // 信任CA根证书,验证服务器证书合法性
        ClientAuth:   tls.RequireAndVerifyClientCert, // 要求发起请求的一方(即服务器)提供证书
        Certificates: []tls.Certificate{clientCert}, // 客户端自身证书,供服务器验证身份
        MinVersion:   tls.VersionTLS13,
        // 验证服务器身份,确保只有指定服务器能访问
        VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
            cert, err := x509.ParseCertificate(rawCerts[0])
            if err != nil {
                return err
            }
            // 检查服务器证书的标识是否为指定的"pm1"
            if cert.Subject.CommonName != "pm1" {
                return log.Fatalf("未授权的服务器: %s", cert.Subject.CommonName)
            }
            return nil
        },
    }

    // 创建HTTP服务器,监听80端口
    server := &http.Server{
        Addr:      ":80",
        TLSConfig: tlsConfig,
        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("来自客户端服务的响应!"))
        }),
    }

    // 启动HTTPS服务器
    log.Println("客户端服务启动于 :80")
    if err := server.ListenAndServeTLS("", ""); err != nil {
        log.Fatalf("服务启动失败: %v", err)
    }
}

三、关键说明

  1. 必须使用CA根证书:之前的核心错误是未加载ca.crt,导致双方无法验证对方证书的合法性,根证书是信任链的基础。
  2. 客户端证书用途限制:通过--profile "client"生成的证书仅能用于客户端认证,无法作为服务端证书使用,从根源避免客户端滥用证书搭建服务。
  3. 客户端监听的安全性:客户端可以使用服务器生成的证书监听请求,但需通过VerifyPeerCertificate校验服务器身份,确保只有指定服务器能调用客户端API,实现客户端之间无法互联的需求。

内容的提问来源于stack exchange,提问作者Maggie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:37:03