Obtain https certificate expiration time using Zabbix-agent2 custom plug-in

Posted by MikeA on Mon, 17 Jan 2022 20:00:13 +0100

WeChat Public Number: Operations and Maintenance Development Story, Author: wanger

demand

For people who often maintain the website, they need to deal with https certificates frequently. Generally, the validity period of https certificates is one year. Once the certificates expire, the loss to the company is enormous. Last year NetEase mailbox because https certificates forgot to renew, resulting in a large number of users unable to use the mailbox normally is a good example. It's not realistic to check manually when we think about it. The best way to do this is to monitor expiration times and send notifications automatically at a certain time. You can use ZABBIX or ssl_from Prometheus Exporter to monitor, at zabbix4. Before version 4, zabbix-agent2 appeared after version 4.4 using custom scripts. In addition to the official plug-ins, they can also be customized to meet our monitoring needs. Then, we will show you how to use the zabbix-agent2 custom plug-ins to achieve the requirements of obtaining https certificate expiration time.

zabbix-agent2 custom https_expire plug-in

Previous questions have described how to use custom plugins to monitor mqtt, the original address https://mp.weixin.qq.com/s/4e6WnFyjXZYnXphx2erkGw However, the Watcher interface was used to actively push the new data to the server side. This time, the data will be collected by implementing the Exporter interface. Here is the official document again https://www.zabbix.com/documentation/current/manual/config/items/plugins Translate official posts with Zabbix Certified Expert Mihong https://mp.weixin.qq.com/s/7JLuVdZYX719pgGSP3xc9w
Here I'll recap some of the standard specifications for custom plug-ins
1. Plugins must import zabbix.com/pkg/plugin package.

import "zabbix.com/pkg/plugin"

1. A plug-in must define a structure and embed the plugin.Base structure.

type Plugin struct {
    plugin.Base
}
var impl Plugin

1. A plug-in must implement one or more plug-in interfaces.

func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) {
    if len(params) > 0 {
        p.Debugf("received %d parameters while expected none", len(params))
        return nil, errors.New("Too many parameters")
    }
    return time.Now().Format(time.RFC3339)
}

1. The plug-in must register itself during initialization.

func init() {
    plugin.RegisterMetrics(&impl, "Time", "system.time", "Returns time string in RFC 3999 format.")
}

Ssl_ Not many expire codes. Plugin written by Nicola Tulakesky Wang Er. Complete code can go github See

func (p *Plugin) Configure(global *plugin.GlobalOptions, options interface{}) {
	if err = conf.Unmarshal(options, &p.options); err != nil {
		p.Errf("cannot unmarshal configuration options: %s", err)
	}

	if p.options.Timeout == 0 {
		p.options.Timeout = global.Timeout
	}
	p.client = newClient(p.options.Timeout)

}

func (p *Plugin) Validate(options interface{}) error {

	return conf.Unmarshal(options, &opts)
}

func checkParamnums(params []string) error {
	if len(params) > paramnums {
		err:=errors.New("Too many parameters.")
		return zbxerr.ErrorTooFewParameters.Wrap(err)
	} else if len(params) ==0 {
		err:=errors.New("Missing URL parameters.")
		return zbxerr.ErrorTooFewParameters.Wrap(err)
	}
	return nil
}

func checkParams(params []string) (string, error) {
	if strings.HasPrefix(params[0], "http://") {
		errorsting:=fmt.Sprintf("Target is using http scheme: %s", params[0])
		err:=errors.New(errorsting)
		return "",zbxerr.ErrorInvalidParams.Wrap(err)
	}

	if !strings.HasPrefix(params[0], "https://") {
		params[0] = "https://" + params[0]
	}
	return string(params[0]),nil
}
func (cli *client) Query(url string) (int64, error) {
	resp, err := cli.client.Get(url)
	if err != nil {
		impl.Debugf("cannot fetch data: %s", err)
		err:=errors.New("cannot fetch data")
		return 0, zbxerr.ErrorCannotFetchData.Wrap(err)
	}
	defer resp.Body.Close()
	certInfo:=resp.TLS.PeerCertificates[0]
	expiredays:=(certInfo.NotAfter.Unix()-time.Now().Unix())/60/60/24
	return expiredays,nil
}

// Export implements the Exporter interface.
func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (interface{}, error) {
	if err = checkParamnums(params); err != nil {
		return nil, err
	}
	urls,err:= checkParams(params)
	if err!= nil {
		return nil,err
	}
	body, err := p.client.Query(urls)
	if err!=nil{
		return nil, err
	}
	return body,nil

}
func init() {
	plugin.RegisterMetrics(&impl, pluginName,
		"https_expire", "Returns the number of days between the HTTPS certificate expiration time and the current date.")
}

Download the zabbix agent2 source and compile the custom plug-in

yum install golang
git clone https://git.zabbix.com/scm/zbx/zabbix.git --depth 1 zabbix-agent2
cd zabbix-agent2
git submodule add https://github.com/cxf210/ssl_expire.git src/go/plugins/https_expire

Import https_expire plug-in

vi src/go/plugins/plugins_linux.go

Add last line

        _ "zabbix.com/plugins/ceph"
        _ "zabbix.com/plugins/docker"
        _ "zabbix.com/plugins/kernel"
        _ "zabbix.com/plugins/log"
        _ "zabbix.com/plugins/memcached"
        _ "zabbix.com/plugins/modbus"
        _ "zabbix.com/plugins/mqtt"
        _ "zabbix.com/plugins/mysql"
        _ "zabbix.com/plugins/net/netif"
        _ "zabbix.com/plugins/net/tcp"
        ...
        _ "zabbix.com/plugins/https_expire"

Compile and install zabbix agent2

yum install automake autoconf pcre* -y
./bootstrap.sh 
pushd . 
cd src/go/ 
go mod vendor 
popd 
./configure --enable-agent2 --enable-static 
make install

Edit Profile

Here I have adjusted the log level to facilitate foreground debugging
Optional parameters
Plugins.Https_expire.Timeout = 5

egrep -v "^$|^#" conf/zabbix_agent2.conf  
LogType=console
LogFile=/tmp/zabbix_agent2.log
DebugLevel=4
Server=172.17.0.5
Plugins.Https_expire.Timeout=5
Hostname=node2
ControlSocket=/tmp/agent.sock

Start Zabbix_agent2

cd /root/zabbix_agent/src/go/bin
zabbix_agent2 -c conf/zabbix_agent2.conf

![image.png](https://img-blog.csdnimg.cn/img_convert/01e581803e6800e564773504acd5a50d.png#align=left&display=inline&height=81&margin=[object Object]&name=image.png&originHeight=161&originWidth=1060&size=30987&status=done&style=none&width=530)

Zabbix Create Monitoring Items

Examples of key values are as follows

https_expire["www.xyzabbix.cn"]

or

https_expire["https://www.xyzabbix.cn"]

![image.png](https://img-blog.csdnimg.cn/img_convert/d1fe529aa9654569c15a7e152c271045.png#align=left&display=inline&height=336&margin=[object Object]&name=image.png&originHeight=672&originWidth=930&size=46093&status=done&style=none&width=465)
Look at the latest data. This certificate has forty days to expire
![image.png](https://img-blog.csdnimg.cn/img_convert/532389a54e779e6c31ee9a228b2d950a.png#align=left&display=inline&height=80&margin=[object Object]&name=image.png&originHeight=159&originWidth=1625&size=15012&status=done&style=none&width=812.5)
I'm using the Ali Cloud ssl certificate and I can see that there are actually forty days before the expiration date. Today is 2021.3.7
![image.png](https://img-blog.csdnimg.cn/img_convert/a9e0059914c661a2288c57a573f193b2.png#align=left&display=inline&height=118&margin=[object Object]&name=image.png&originHeight=236&originWidth=1497&size=21620&status=done&style=none&width=748.5)
You can create a trigger to send an alert when there is still a month to go
![image.png](https://img-blog.csdnimg.cn/img_convert/7921a39a29c3807bfbde3372e13b2f02.png#align=left&display=inline&height=302&margin=[object Object]&name=image.png&originHeight=603&originWidth=949&size=40242&status=done&style=none&width=474.5)

Topics: Go Zabbix https