首页 > 其他分享 >[golang]使用mTLS双向加密认证http通信

[golang]使用mTLS双向加密认证http通信

时间:2023-08-07 18:55:06浏览次数:35  
标签:http err mTLS ca req golang client 客户端

前言

假设一个场景,服务端部署在内网,客户端需要通过暴露在公网的nginx与服务端进行通信。为了避免在公网进行 http 明文通信造成的信息泄露,nginx与客户端之间的通信应当使用 https 协议,并且nginx也要验证客户端的身份,也就是mTLS双向加密认证通信。

这条通信链路有三个角色:服务端、Nginx、客户端。

  • 服务端部署在内网,与nginx使用http通信。
  • 客户端在公网,与nginx使用https通信,且双向加密认证。

服务端

服务端只使用http,所以这里用gin框架写个简单的示例,返回客户端一些基本的http信息,比如客户端IP、请求方法、host等。

package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
)

/* 中间件: 获取api处理时长 */
func midElapsed(c *gin.Context) {
	start := time.Now()
	c.Next()
	elapsed := time.Since(start)
	log.Printf("API: %s, elapsed: %s", c.Request.URL.Path, elapsed)
}

/* 处理 GET / 请求 */
func f1(c *gin.Context) {
	// 获取客户端IP
	clientIP := c.ClientIP()

	// 获取请求方法
	method := c.Request.Method

	// 获取协议
	proto := c.Request.Proto

	// 获取host
	host := c.Request.Host

	// 请求Path
	path := c.Request.URL.Path

	log.Printf("客户端IP: %s, 请求方法: %s, 协议: %s, host: %s, path: %s", clientIP, method, proto, host, path)

	// 获取请求头
	headers := c.Request.Header
	for hk, hv := range headers {
		log.Printf("header key: %s, value: %s", hk, hv)
	}

	// 获取名为"mycookie"的cookie
	var cookies []string
	cookie, err := c.Cookie("mycookie")
	if err != nil {
		log.Printf("get cookie [mycookie] error: %s", err)
	} else {
		log.Printf("get cookie [mycookie]: %s", cookie)
		cookies = append(cookies, cookie)
	}

	c.JSON(http.StatusOK, gin.H{
		"clientIP": clientIP,
		"method":   method,
		"proto":    proto,
		"host":     host,
		"headers":  headers,
		"cookies":  cookies,
		"path":     path,
	})
}

func main() {
	r := gin.Default()
	r.Use(midElapsed) // 全局引用计算耗时的中间件
	r.GET("/", f1)
	r.Run("0.0.0.0:8080")
}

生成证书

  1. 生成ca根证书。生成过程会要求填写密码、CN、ON、OU等信息,记住密码,填写的信息也要和下一步openssl.cnf文件内容一致。
openssl req -x509 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 3650
  1. 新建并编辑文件openssl.cnf文件。req_distinguished_name中内容按需填写,DNS.1要替换成实际域名。
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
countryName = CN
stateOrProvinceName = Anhui
localityName = Hefei
organizationName = zhangsan
commonName = qw.er.com

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = qw.er.com
  1. 生成服务端证书
openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/CN=qw.er.com" -config openssl.cnf

# 提示输入ca私钥的密码
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -extensions v3_req -extfile openssl.cnf
  1. 生成客户端证书
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj "/CN=qw.er.com" -config openssl.cnf

# 提示输入ca私钥的密码
openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -extensions v3_req -extfile openssl.cnf

Nginx配置

nginx反向代理服务端的配置示例如下

server {
    listen 80 ssl;
    server_name qw.er.com;
    ssl_certificate /home/atlas/apps/nginx/certs/qwer/server.crt;
    ssl_certificate_key /home/atlas/apps/nginx/certs/qwer/server.key;
    
    # 校验客户端证书
    ssl_verify_client on;
    ssl_client_certificate /home/atlas/apps/nginx/certs/qwer/ca.crt;
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-real-ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.0.10:8080; # 服务端地址
    }
}

客户端

以下示例使用命令行传参的方式,指定tls证书文件和是否使用tls通信。

package main

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

var (
	cafile  = flag.String("cafile", "ca.crt", "ca 证书文件")
	crtfile = flag.String("crtfile", "client.crt", "客户端tls证书")
	keyfile = flag.String("keyfile", "client.key", "客户端tls私钥")
	url     = flag.String("url", "http://127.0.0.1:8080", "url")
	isTls   = flag.Bool("tls", false, "是否使用tls")
)

func tlsClient(cafile, crtfile, keyfile string) *http.Transport {
	// 加载证书和私钥
	clientCert, err := tls.LoadX509KeyPair(crtfile, keyfile)
	if err != nil {
		log.Fatalf("load key pair error: %s", err)
	}

	// 加载ca证书
	clientCA, err := os.ReadFile(cafile)
	if err != nil {
		log.Fatalf("load ca cert error: %s", err)
	}

	// 创建根证书池并添加ca证书
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(clientCA)

	// 创建transport
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			Certificates: []tls.Certificate{clientCert},
			RootCAs:      caCertPool,
		},
	}

	return tr
}

func main() {
	flag.Parse()

	req, err := http.NewRequest("GET", *url, nil)
	if err != nil {
		log.Fatalf("new request error: %s", err)
	}
	// 自定义HTTP请求头
	req.Header.Set("myheader1", "myheader1value123")
	// 自定义一个cookie对象
	cookie := &http.Cookie{
		Name: "mycookie",
		Value: "mycookievalue",
	}
	req.AddCookie(cookie)

	client := &http.Client{
		Timeout: time.Second * 5,
	}
	if *isTls {
		client.Transport = tlsClient(*cafile, *crtfile, *keyfile)
	}

	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("get error: %s", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("read error: %s", err)
	}

	log.Printf("body: %+v", string(body))
}

Nginx配置

server {
    listen 80 ssl;
    server_name qw.er.com;
    ssl_certificate /home/elifen/apps/nginx/certs/qwer/server.crt;
    ssl_certificate_key /home/elifen/apps/nginx/certs/qwer/server.key;
    ssl_verify_client on;
    ssl_client_certificate /home/elifen/apps/nginx/qwer/ca.crt;
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-real-ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.0.10:8080;
    }
}

测试

这里需要先确保qw.er.com能被正常解析到nginx服务器,比如配置hosts文件或dns解析记录。

go run main.go -cafile ./ca.crt -crtfile ./client.crt -keyfile ./client.key -url 'https://qw.er.com:80/' -tls

输出示例

2023/08/07 17:34:51 body: {"clientIP":"192.168.0.11","cookies":["mycookievalue"],"headers":{"Accept-Encoding":["gzip"],"Connection":["close"],"Cookie":["mycookie=mycookievalue"],"Myheader1":["myheader1value123"],"User-Agent":["Go-http-client/1.1"],"X-Forwarded-For":["192.168.0.11"],"X-Real-Ip":["192.168.0.11"]},"host":"qw.er.com","method":"GET","path":"/","proto":"HTTP/1.0"}

标签:http,err,mTLS,ca,req,golang,client,客户端
From: https://www.cnblogs.com/XY-Heruo/p/17612447.html

相关文章

  • Go / Golang JSON 一些心得
    自定义序列化和反序列化可以实现json.Marshaler和json.Unmarshaler自定义json的序列化和反序列化typeTags[]stringfunc(tTags)MarshalJSON()([]byte,error){return[]byte(strconv.Quote(strings.Join(t,","))),nil}func(t*Tags)UnmarshalJSON(b[]b......
  • JavaHTTP心跳:服务器与客户端实时连接的实现方式
    JavaHTTP心跳:服务器与客户端实时连接的实现方式在网络通信中,实时连接是一种至关重要的功能。它允许服务器与客户端之间保持持久的通信信道,实现快速、高效的数据传输。对于Java开发者来说,实现服务器与客户端之间的实时连接可以通过JavaHTTP心跳技术来实现。本文将介绍如何利用Java......
  • Java HTTP请求封装的方法及实现
    JavaHTTP请求封装的方法及实现在Java开发中,我们经常需要与服务器进行数据交互,发送HTTP请求是其中常见的一种方式。为了简化开发过程,我们可以封装HTTP请求的方法,让调用者只需要关注业务逻辑而不用关心底层的细节实现。本文将介绍一种基于Java的HTTP请求封装方法及其实现。JavaHT......
  • Java HTTP多线程下载实现方法
    JavaHTTP多线程下载实现方法在如今互联网高速发展的时代,对于下载来说,速度是一个非常重要的因素。而多线程下载是一种提高下载速度的常见方法。本文将介绍如何使用Java编程实现HTTP多线程下载功能。JavaHTTP多线程下载实现方法一、背景知识在开始实现之前,我们需要了解一些基本......
  • Go中 net/http 使用
    net/http是Go语言标准库中的一个包,提供了实现HTTP客户端和服务器的功能。它使得编写基于HTTP协议的Web应用程序变得简单和方便。net/http包的主要用途包括:实现HTTP客户端:可以发送HTTP请求并接收服务器的响应。实现HTTP服务器:可以创建一个HTTP服务器,接受客户端的请求并返回响应1......
  • 缓慢的http拒绝服务攻击
    详细描述缓慢的HTTP拒绝服务攻击是一种专门针对于Web的应用层拒绝服务攻击,攻击者操纵网络上的肉鸡,对目标Web服务器进行海量HTTP请求攻击,直到服务器带宽被打满,造成了拒绝服务。慢速HTTP拒绝服务攻击经过不断的演变和发展,主要有三种攻击类型,分别是Slowheaders、Slowbody、Slowr......
  • golang之浮点数处理库decimal
    decimal库包是用来解决float类型对象之间运算不准确的问题的。所以,如果你想使用decimal库包,你必须先把float类型对象通过decimal.NewFromFloat()函数转成decimal.Decimal类型,然后再计算,最后还得再转成你所需要的类型。范例:packagemainimport"log"funcmain(){a:=......
  • 拆解爬虫使用隧道HTTP代理的原理
    今天,让我们来一起探索一下爬虫如何利用隧道HTTP代理来实现无限可能!本文将为你详解这一原理,并分享一些实用的操作技巧。快来和我一起探索吧!一、隧道HTTP代理是什么?在爬虫的世界里,隧道HTTP代理就像是一个隐身斗篷,可以帮助我们在互联网上隐藏身份。它实际上是位于我们和目标网站之间的......
  • Ceph对象存储ingress配置https
    每当引用TLSSecrets时,指的是PEM编码的X.509、RSA(2048)Secrets。可以使用以下命令生成自签名证书和私钥:$opensslreq-x509-nodes-days3650-newkeyrsa:2048-keyout${KEY_FILE}-out${CERT_FILE}-subj"/CN=${HOST}/O=${HOST}"例如:$opensslreq-x509-nodes......
  • Apache HTTPD换行解析漏洞(CVE-2017-15715)
    ApacheHTTPD换行解析漏洞(CVE-2017-15715)【项目中遇到】ApacheHTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页,其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将按照PHP后缀进行解析,导致绕过一些服务器安全策略。1.环境搭建cdhttpd/CVE-2017-15715/doc......