golang编写采集器监控密码过期

背景:arm64架构环境、项目需要、部分服务进程在用户密码过期后会出现进程异常;使用golang编写采集器(golang方便交叉编译)

功能:暴露采集端口、可直接通过prometheus接入采集、之后通过规则进行密码告警过期提示

代码:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/exec"
    "strings"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
    namespace        = "password_expiry"
    neverExpiryValue = 99999
)

// UserExpiryCollector collects user password expiry metrics
type UserExpiryCollector struct {
    expiryDays *prometheus.Desc
    users      []string
}

// NewUserExpiryCollector creates a new UserExpiryCollector
func NewUserExpiryCollector(users []string) *UserExpiryCollector {
    return &UserExpiryCollector{
        expiryDays: prometheus.NewDesc(
            prometheus.BuildFQName(namespace, "", "days_to_expiry"),
            "Number of days until the user's password expires",
            []string{"user"}, nil,
        ),
        users: users,
    }
}

// Describe sends the super-set of all possible descriptors of metrics collected by this Collector.
func (c *UserExpiryCollector) Describe(ch chan<- *prometheus.Desc) {
    ch <- c.expiryDays
}

// Collect is called by the Prometheus registry when collecting metrics.
func (c *UserExpiryCollector) Collect(ch chan<- prometheus.Metric) {
    for _, user := range c.users {
        daysToExpiry, err := getDaysToExpiry(user)
        if err != nil {
            log.Printf("Error getting expiry for user %s: %v", user, err)
            continue
        }

        ch <- prometheus.MustNewConstMetric(
            c.expiryDays,
            prometheus.GaugeValue,
            float64(daysToExpiry),
            user,
        )
    }
}

// getDaysToExpiry returns the number of days until the user's password expires.
func getDaysToExpiry(user string) (float64, error) {
    cmd := exec.Command("chage", "-l", user)
    cmd.Env = []string{
        "LANG=en-US",
    }
    var out bytes.Buffer
    cmd.Stdout = &out
    err := cmd.Run()
    if err != nil {
        fmt.Println("Error executing command:", err)
        return -2, nil
    }
    output := out.String()
    filteredOutput := strings.Builder{}
    for _, line := range strings.Split(output, "\n") {
        if strings.Contains(line, "Password expires") {
            filteredOutput.WriteString(strings.TrimSpace(strings.Split(line, ":")[1]))
        }
    }
    if filteredOutput.String() == "never" {
        return neverExpiryValue, nil
    }
    //loc, _ := time.LoadLocation("Asia/Shanghai")
    //datenow, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" 00:00:00", loc)
    //dateexpire, _ := time.ParseInLocation("2006-01-02 15:04:05", filteredOutput.String()+" 00:00:00", loc)
    datenow, _ := time.Parse("Jan 2, 2006 15:04:05 MST", time.Now().Format("Jan 2, 2006")+" 00:00:00 CST")
    dateexpire, _ := time.Parse("Jan 2, 2006 15:04:05 MST", filteredOutput.String()+" 00:00:00 CST")
    fmt.Println(dateexpire)
    fmt.Println(datenow)

    daysToExpiry := dateexpire.Sub(datenow).Hours() / 24
    return daysToExpiry, nil
}

// getUsersWithBashShell returns a list of users with /bin/bash as their shell
func getUsersWithBashShell() ([]string, error) {
    file, err := os.Open("/etc/passwd")
    if err != nil {
        return nil, fmt.Errorf("failed to open /etc/passwd: %w", err)
    }
    defer file.Close()

    var users []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        line := scanner.Text()
        parts := strings.Split(line, ":")
        if len(parts) > 6 && parts[6] == "/bin/bash" {
            users = append(users, parts[0])
        }
    }
    if err := scanner.Err(); err != nil {
        return nil, fmt.Errorf("error reading /etc/passwd: %w", err)
    }
    return users, nil
}

func main() {
    users, err := getUsersWithBashShell()
    if err != nil {
        log.Fatalf("Error getting users with /bin/bash shell: %v", err)
    }

    log.Println("Starting password expiry exporter")

    collector := NewUserExpiryCollector(users)
    prometheus.MustRegister(collector)

    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(":9135", nil))
}

###交叉编译的环境参考前面文章prometheusalert交叉编译

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值