Go 定时监控 Https 证书

起因 最近有由于一个域名的 https 证书过期,导致某个网站出现大面积无法正常使用的故障。于是我打算使用 go 语言 来监控域名的 HTTPS 证书过期情况,来及时续期证书。 HTTPS 证书 了解证书加密体系的应该知道,TLS 证书是链式信任的,所以中间任何一个证书过期、失效都会导致整个信任链断裂,不过单纯的 Let’s Encrypt ACME 证书检测可能只关注末端证书即可,除非哪天 Let’s Encrypt 倒下… 解决 在 go 语言中,Go 在发送 HTTP 请求后,在响应体中会包含一个 TLS *tls.ConnectionState 结构体,该结构体中目前存放了服务端返回的整个证书链: // ConnectionState records basic TLS details about the connection. type ConnectionState struct { // Version is the TLS version used by the connection (e.g. VersionTLS12). Version uint16 // HandshakeComplete is true if the handshake has concluded. HandshakeComplete bool // DidResume is true if this connection was successfully resumed from a // previous session with a session ticket or similar mechanism. DidResume bool // CipherSuite is the cipher suite negotiated for the connection (e.g. // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256). CipherSuite uint16 // NegotiatedProtocol is the application protocol negotiated with ALPN. NegotiatedProtocol string // NegotiatedProtocolIsMutual used to indicate a mutual NPN negotiation. // // Deprecated: this value is always true. NegotiatedProtocolIsMutual bool // ServerName is the value of the Server Name Indication extension sent by // the client. It's available both on the server and on the client side. ServerName string // PeerCertificates are the parsed certificates sent by the peer, in the // order in which they were sent. The first element is the leaf certificate // that the connection is verified against. // // On the client side, it can't be empty. On the server side, it can be // empty if Config.ClientAuth is not RequireAnyClientCert or // RequireAndVerifyClientCert. PeerCertificates []*x509.Certificate // VerifiedChains is a list of one or more chains where the first element is // PeerCertificates[0] and the last element is from Config.RootCAs (on the // client side) or Config.ClientCAs (on the server side). // // On the client side, it's set if Config.InsecureSkipVerify is false. On // the server side, it's set if Config.ClientAuth is VerifyClientCertIfGiven // (and the peer provided a certificate) or RequireAndVerifyClientCert. VerifiedChains [][]*x509.Certificate // SignedCertificateTimestamps is a list of SCTs provided by the peer // through the TLS handshake for the leaf certificate, if any. SignedCertificateTimestamps [][]byte // OCSPResponse is a stapled Online Certificate Status Protocol (OCSP) // response provided by the peer for the leaf certificate, if any. OCSPResponse []byte // TLSUnique contains the "tls-unique" channel binding value (see RFC 5929, // Section 3). This value will be nil for TLS 1.3 connections and for all // resumed connections. // // Deprecated: there are conditions in which this value might not be unique // to a connection. See the Security Considerations sections of RFC 5705 and // RFC 7627, and https://mitls.org/pages/attacks/3SHAKE#channelbindings. TLSUnique []byte // ekm is a closure exposed via ExportKeyingMaterial. ekm func(label string, context []byte, length int) ([]byte, error) } 可以看到 PeerCertificates 包含了所有的证书,我们只只要遍历 PeerCertificates,根据 NotBefore, NotAfter 字段就能进行是否过期的判断 ...

六月 11, 2022 · overstarry

Go 并发 Sync.Once 解析

在 go 语言中我们可以使用 sync.Once 对象来实现函数方法只执行一次的功能。 简单代码示例 package main import ( "fmt" "sync" ) func main() { var ( o sync.Once wg sync.WaitGroup ) for i := 0; i < 10; i++ { wg.Add(1) go func(i int) { defer wg.Done() o.Do(func() { fmt.Printf("hello %d\n", i) }) }(i) } wg.Wait() } 输出: hello 9 不使用 Sync.Once 的结果如下: hello 9 hello 4 hello 0 hello 1 hello 2 hello 3 hello 6 hello 5 hello 7 hello 8 可以看到,在使用 sync.Once 的情况下,只执行一次函数。 ...

六月 3, 2022 · overstarry

Go 截取视频某一帧图片

前言 最近遇到一个需求,需要截取视频的某一帧图片作为视频封面。我搜寻了相关资料,在 go 语言端常见的有两种做法,1)使用 opencv 的 go 绑定库,2)使用 ffmpeg 的 go 绑定库。 这里我打算使用第二种方法,使用 ffmpeg 的 go 绑定库。 ffmpeg 介绍 FFmpeg 是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用 LGPL 或 GPL 许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库 libavcodec,为了保证高可移植性和编解码质量,libavcodec 里很多 codec 都是从头开发的。 FFmpeg 在 Linux 平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括 Windows、Mac OS X 等。 FFmpeg 项目由以下几部分组成: FFMpeg 视频文件转换命令行工具,也支持经过实时电视卡抓取和编码成视频文件。 FFServer 基于 HTTP(RTSP 正在开发中) 用于实时广播的多媒体服务器,也支持时间平移. FFplay 用 SDL 和 FFmpeg 库开发的一个简单的媒体播放器. libavcodec 一个包含了所有 FFmpeg 音视频编解码器的库。为了保证最优性能和高可复用性,大多数编解码器从头开发的. libavformat 一个包含了所有的普通音视格式的解析器和产生器的库 解决 1 安装 ffmpeg 浏览器访问 https://ffbinaries.com/downloads 根据你的系统安装 ffmpeg 2 安装 ffmpeg 的 go 绑定库 ...

五月 28, 2022 · overstarry

Go errgroup

我们知道在 go 语言中很容易开启携程进行并发任务,但是如何处理并发过程中的错误是非常棘手的,接下来我就来介绍 errgroup 的用法。 errgroup errgroup 包里主要是一个结构体和相应的方法,它可以让你在并发任务中处理错误。 type Group struct { // context 的 cancel 方法 cancel func() // 复用 WaitGroup wg sync.WaitGroup sem chan token // 用来保证只会接受一次错误 errOnce sync.Once // 保存第一个返回的错误 err error } func WithContext(ctx context.Context) (*Group, context.Context) func (g *Group) done() func (g *Group) Wait() error func (g *Group) Go(f func() error) func (g *Group) TryGo(f func() error) bool func (g *Group) SetLimit(n int) 通过 WithContext 可以创建一个可以取消的 Group,当然除此之外也可以零值的 Group 也可以直接使用,但是出错之后就不会取消其他的 goroutine 了。Go 方法 传入一个函数参数,会启动一个 goroutine 处理。 Wait 类似 WaitGroup 的 Wait 方法,等待所有的 goroutine 结束后退出,返回的错误是一个出错的 err。 TryGo 是和 SetLimit 配套的,只有当 group 中的 goroutines 数量小于配置的数量时,才会在 goroutine 中调用函数。TryGo 用来判断 goroutine 是否启动。 ...

五月 21, 2022 · overstarry

应用内存升高原因排查

起因 最近一个部署了 go 应用的服务器出现了 OOM 的现象,内存占用过高。 原因 通过 Pyroscope 分析得出是因为 Minio 的 go sdk 中的 PutObject 函数占用了大量的内存。Pyroscope 是什么,前面的文章已经介绍过了,这里就不过多介绍了。 接下来我们通过查看相关的源码来查看是什么原因。 // PutObject creates an object in a bucket. // // You must have WRITE permissions on a bucket to create an object. // // - For size smaller than 16MiB PutObject automatically does a // single atomic PUT operation. // // - For size larger than 16MiB PutObject automatically does a // multipart upload operation. // // - For size input as -1 PutObject does a multipart Put operation // until input stream reaches EOF. Maximum object size that can // be uploaded through this operation will be 5TiB. // // WARNING: Passing down '-1' will use memory and these cannot // be reused for best outcomes for PutObject(), pass the size always. // // NOTE: Upon errors during upload multipart operation is entirely aborted. func (c *Client) PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts PutObjectOptions, ) (info UploadInfo, err error) { if objectSize < 0 && opts.DisableMultipart { return UploadInfo{}, errors.New("object size must be provided with disable multipart upload") } err = opts.validate() if err != nil { return UploadInfo{}, err } return c.putObjectCommon(ctx, bucketName, objectName, reader, objectSize, opts) } 从方法的注释可以看出,当传递的大小为 -1 时,会进行多次 put 操作,直到输入流结束。多次 put 操作的最大大小为 5TiB,并且不能重用内存,导致占用大量内存。 ...

五月 14, 2022 · overstarry

多平台博客发布工具 openwrite 使用

介绍 OpenWrite 是一款便捷的多平台博客发布工具,可以在 OpenWrite 编写 markdown 文档,然后发布到其他博客平台,目前已经支持 CSDN、SegmentFault、掘金、博客园、简书等知名平台。 使用 注册 进入 http://admin.openwrite.cn/, 注册并登陆账号。 安装浏览器插件 第一步需要安装浏览器插件,根据这个链接教程安装,只有安装了插件,才能配置分发的平台。根据对插件的源码的分析,插件是通过 cookie、storage 等浏览器数据进行认证登陆的。此插件还包括模拟写作和 mackdown 编辑器。 认证配置渠道 安装完插件后,点击渠道管理,可以看到可配置的平台,支持多个平台。相应的平台如果没有登陆则显示未登陆,这时需要你登陆相应的平台。登陆平台后,点击认证,即可配置渠道。 这里以掘金为例,认证成功后,可以进行相应的配置,配置文章分类和标题。 发布文章 点击文章管理界面,添加文章,添加文章后,可以选择配置的平台,并发布到相应的平台。 普通用户一个月只有 10 次机会,一个平台占用一次机会。

五月 7, 2022 · overstarry

Buf 初入门 2

上文讲了 Buf lint 命令的基础方法,本文将介绍 Buf lint 命令的一些常用配置。 如果你的项目中没有 buf.yaml 配置文件,Buf lint 会提供一个默认的配置文件,默认内容如下: version: v1 lint: use: - DEFAULT except: - FILE_LOWER_SNAKE_CASE ignore: - bat - ban/ban.proto ignore_only: ENUM_PASCAL_CASE: - foo/foo.proto - bar BASIC: - foo enum_zero_value_suffix: _UNSPECIFIED rpc_allow_same_request_response: false rpc_allow_google_protobuf_empty_requests: false rpc_allow_google_protobuf_empty_responses: false service_suffix: Service allow_comment_ignores: true 配置选项 接下来开始介绍 Buf lint 命令的配置选项。 use use 选项配置 lint 的类别,不同类别有相应的规则,有多种类别:DEFAULT、FILE_LOWER_SNAKE_CASE、BASIC 等。默认是 DEFAULT 类别。 except except 选项配置 lint 不使用的类别。 下面的配置,显示使用 DEFAULT 类别,但是不使用 ENUM_NO_ALLOW_ALIAS ,BASIC 类别中的规则。 version: v1 lint: use: - DEFAULT except: - ENUM_NO_ALLOW_ALIAS - BASIC ignore ignore 选项指定忽略的文件,可以是文件名,也可以是目录,注意路径是要相对于 buf.yaml 文件的。 ...

五月 4, 2022 · overstarry

Buf 初入门 1

Buf 的目标是将 API 开发转向模式驱动的范式,从而为未来铺平道路,使 API 以服务所有者和客户可以依赖的方式定义。 与简单地暴露 REST/JSON 服务相比,使用 IDL 来定义 API 有很多好处,今天,Protobuf 是业界最稳定、最广泛采用的 IDL。但就目前的情况来看,使用 Protobuf 比使用 JSON 作为数据传输格式要困难得多。 Buf 正在建立工具,使 Protobuf 对服务所有者和客户来说是可靠和友好的,同时保持它在技术上的明显优势。您的组织不需要重新发明轮子来高效地创建、维护和使用 Protobuf API。我们将为您处理您的 Protobuf 管理策略,因此您可以专注于重要的事情。 本篇文章是讲述 buf 使用的第一篇文章,主要讲解使用 buf 定义 proto 文件的 lint 规则。统一的 lint 规则,可以让个人或团队定义的 API 保持一致。 lint 1 创建 buf.yaml 文件 使用 buf mod init 命令创建 buf.yaml 文件。 version: v1 breaking: use: - FILE lint: use: - DEFAULT 使用默认 lint 规则。 2 运行 lint 命令 使用 buf lint 命令运行 lint。 ...

四月 23, 2022 · overstarry

Go 统计代码测试覆盖率

今天来讲一讲如何统计 go 代码的测试覆盖率,主要是 cover 命令。 cover 基本用法 1 先简单写个函数和相应的测试,代码如下: func Max(a, b int) int { if a > b { return a } return b } 这个函数就是简单的比较大小,如果 a > b,返回 a,否则返回 b。 测试代码如下 package main import "testing" func TestMax(t *testing.T) { type args struct { a int b int } tests := []struct { name string args args want int }{ { name: "a is larger than b", args: args{ a: 2, b: 1, }, want: 2, }, { name: "b is larger than a", args: args{ a: 1, b: 2, }, want: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Max(tt.args.a, tt.args.b); got != tt.want { t.Errorf("Max() = %v, want %v", got, tt.want) } }) } } 2 go test -cover 能够统计出代码测试的覆盖率,这是一种比统计函数是否被调用更强悍的手法。我们执行这个命令。输出如下: ...

四月 16, 2022 · overstarry

Kswapd0 is consuming a lot of CPU.

Issue Recently, a Linux server has been experiencing high CPU usage, causing the server to become unresponsive and other processes to fail to execute smoothly. By using the top command to check resource usage, it can be seen that the kswapd0 process is consuming a large amount of CPU resources. Cause and solution Based on my research, the kswapd0 process is responsible for managing virtual memory. Typically, a Linux system comprises RAM, swap, and EXT4 components. The EXT4 partition is used to store regular files and can be created on either an HDD or an SSD, though it is relatively slower compared to RAM. RAM, commonly referred to as memory, is used for high-speed program execution. The swap partition, on the other hand, is used as additional virtual memory and is normally allocated on physical disks, especially on machines with limited physical RAM. ...

四月 9, 2022 · overstarry