package main
import (
"bufio"
"os"
"os/exec"
"runtime"
"strings"
"github.com/pterm/pterm"
)
const (
downloadpath = "/root/unbound"
)
func main() {
pterm.DefaultHeader.WithFullWidth().WithBackgroundStyle(pterm.NewStyle(pterm.BgCyan)).WithTextStyle(pterm.NewStyle(pterm.FgBlack)).Println("Unbound 一键安装脚本[V1.0.0]")
pterm.DefaultHeader.WithFullWidth().WithBackgroundStyle(pterm.NewStyle(pterm.BgBlue)).WithTextStyle(pterm.NewStyle(pterm.FgBlack)).Println("--- XLX笔记 | www.xlxbk.cn ---")
var options []string
options = []string{
"0 退出脚本",
"1 安装Unbound",
"2 升级Unbound",
}
selectedOption, _ := pterm.DefaultInteractiveSelect.WithOptions(options).Show()
switch selectedOption {
case "0 退出脚本":
return
case "1 安装Unbound":
var mdstr string
var packageManager string
var baolist []string
pterm.DefaultBasicText.Println("操作系统类型:" + pterm.LightMagenta(runtime.GOOS))
pterm.DefaultBasicText.Println("CPU 架构:" + pterm.LightMagenta(runtime.GOARCH))
if runtime.GOOS != "linux" {
pterm.Error.Println("脚本只支持Linux系统")
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始检测系统使用的包管理器")
packageManagers := []string{"apt", "yum", "dnf", "zypper", "pacman"}
for _, manager := range packageManagers {
cmd := exec.Command(manager, "--version")
err := cmd.Run()
if err == nil {
pterm.DefaultBasicText.Printfln("系统使用的包管理器可能是: %s\n", manager)
packageManager = manager
break
} else {
pterm.Error.Printfln("命令 %s 未找到或执行出错: %v", manager, err)
}
}
if packageManager == "" {
pterm.Error.Println("系统未找到任何包管理器")
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始检测系统是否安装 wget")
cmdwget := exec.Command("sh", "-c", "command -v wget")
errwget := cmdwget.Run()
if errwget == nil {
pterm.DefaultBasicText.Println("系统已安装 wget")
} else {
pterm.Error.Println("系统未安装 wget")
pterm.DefaultBasicText.Println("开始安装 wget")
cmd := exec.Command(packageManager, "install", "-y", "wget")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("安装 wget 失败:%v", err)
return
}
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始检测系统是否安装 tar")
cmdtar := exec.Command("sh", "-c", "command -v tar")
errtar := cmdtar.Run()
if errtar == nil {
pterm.DefaultBasicText.Println("系统已安装 tar")
} else {
pterm.Error.Println("系统未安装 tar")
pterm.DefaultBasicText.Println("开始安装 tar")
cmd := exec.Command(packageManager, "install", "-y", "tar")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("安装 tar 失败:%v", err)
return
}
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始配置 Unbound 编译环境")
result, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("选择y会删除 /root 目录下 unbound 相关文件以及文件夹和 libevent 相关的文件和文件夹").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if result {
mdstr = "cd /root && rm -rf unbound* && rm -rf libevent* && rm -rf hiredis*"
cmd := exec.Command("sh", "-c", mdstr)
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("删除 unbound 相关文件以及文件夹失败:%v", err)
return
}
}
mdstr = "mkdir -p " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("创建 /root/unbound 目录失败:%v", err)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("请根据上述输出的包管理器选择对应的执行更新系统的命令")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("由于部分系统会弹出交互式的更新所以需要手动执行这步操作")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("apt update && apt upgrade -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("yum update -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("dnf update -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("zypper update -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("pacman -Syu")
resultss, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("是否已经手动更新了系统").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if resultss {
} else {
pterm.Error.Println("请手动更新系统后再执行脚本")
return
}
results, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("是否已经手动配置了 Unbound 运行环境").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if results {
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始下载 Unbound 源代码")
mdstr = "cd /root && wget https://nlnetlabs.nl/downloads/unbound/unbound-latest.tar.gz && tar -zxvf unbound-latest.tar.gz -C " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("下载/解压 Unbound 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("Unbound 源代码下载完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始编译 Unbound")
filename := checkfilename()
mdstr = "cd " + downloadpath + "/" + filename + " && ./configure --enable-subnet --with-libevent --with-libhiredis --enable-cachedb && make && make install"
cmdinstall := exec.Command("sh", "-c", mdstr)
cmdinstall.Stdout = os.Stdout
cmdinstall.Stderr = os.Stderr
errinstall := cmdinstall.Run()
if errinstall != nil {
pterm.Error.Printfln("编译安装 Unbound 失败:%v", errinstall)
return
}
pterm.DefaultBasicText.Println("Unbound 编译安装完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始配置 Unbound")
mdstr = "cd /usr/local/etc/unbound && wget -O root.hints https://www.internic.net/domain/named.root && touch unbound.log && chmod 666 unbound.log && rm -rf unbound.conf && wget https://oss.xlxbk.cn/unbound.conf && chmod 644 unbound.conf"
cmdconf := exec.Command("sh", "-c", mdstr)
cmdconf.Stdout = os.Stdout
cmdconf.Stderr = os.Stderr
errconf := cmdconf.Run()
if errconf != nil {
pterm.Error.Printfln("下载 Unbound 相关配置文件失败:%v", errconf)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件下载完成")
mdstr = "chmod 777 /usr/local/etc/unbound"
cmdchmod := exec.Command("sh", "-c", mdstr)
cmdchmod.Stdout = os.Stdout
cmdchmod.Stderr = os.Stderr
errchmod := cmdchmod.Run()
if errchmod != nil {
pterm.Error.Printfln("赋予目录权限失败:%v", errchmod)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件赋予权限完成")
mdstr = "unbound-anchor"
cmdunbchor := exec.Command("sh", "-c", mdstr)
cmdunbchor.Stdout = os.Stdout
cmdunbchor.Stderr = os.Stderr
errunbchor := cmdunbchor.Run()
if errunbchor != nil {
mdstr = "ls /usr/local/etc/unbound/root.key"
cmdlsrootkey := exec.Command("sh", "-c", mdstr)
cmdlsrootkey.Stdout = os.Stdout
cmdlsrootkey.Stderr = os.Stderr
errlsrootkey := cmdlsrootkey.Run()
if errlsrootkey != nil {
pterm.Error.Printfln("配置Unbound DNSSEC 失败:%v", errunbchor)
return
}
}
pterm.DefaultBasicText.Println("Unbound DNSSEC 配置完成")
mdstr = "unbound-control-setup"
cmdunbcoup := exec.Command("sh", "-c", mdstr)
cmdunbcoup.Stdout = os.Stdout
cmdunbcoup.Stderr = os.Stderr
errunbcoup := cmdunbcoup.Run()
if errunbcoup != nil {
pterm.Error.Printfln("配置Unbound 远程控制失败:%v", errunbcoup)
return
}
pterm.DefaultBasicText.Println("Unbound 远程控制配置完成")
if !checkUserExists("unbound") {
mdstr = "groupadd unbound && useradd -m -g unbound -s /bin/false unbound"
cmduser := exec.Command("sh", "-c", mdstr)
cmduser.Stdout = os.Stdout
cmduser.Stderr = os.Stderr
erruser := cmduser.Run()
if erruser != nil {
pterm.Error.Printfln("创建 unbound 用户失败:%v", erruser)
return
}
pterm.DefaultBasicText.Println("创建 unbound 用户完成")
}
mdstr = "cd /lib/systemd/system && wget https://oss.xlxbk.cn/unbound.service && chmod 644 unbound.service && systemctl daemon-reload && systemctl enable unbound"
cmdserv := exec.Command("sh", "-c", mdstr)
cmdserv.Stdout = os.Stdout
cmdserv.Stderr = os.Stderr
errserv := cmdserv.Run()
if errserv != nil {
pterm.Error.Printfln("配置 Unbound 服务失败:%v", errserv)
return
}
pterm.DefaultBasicText.Println("Unbound 服务配置完成")
mdstr = "systemctl start unbound"
cmdstart := exec.Command("sh", "-c", mdstr)
cmdstart.Stdout = os.Stdout
cmdstart.Stderr = os.Stderr
errstart := cmdstart.Run()
if errstart != nil {
pterm.Error.Printfln("启动 Unbound 服务失败:%v", errstart)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("Unbound 默认目录 /usr/local/etc/unbound 配置文件、日志什么的都在这个目录有特殊需求可以自行更改配置文件")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("Unbound 监听端口 5353")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("Unbound 监听IP 所有IP")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("使用 lsof -i:5353 可以检测Unbound是否启动成功")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("如果启动失败,请运行 systemctl status unbound 截图发送给开发者")
} else {
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("安装 Unbound 编译所需依赖")
if packageManager == "apt" {
baolist = []string{
"build-essential",
"libssl-dev",
"libexpat1-dev",
"bison",
"flex",
"libhiredis-dev",
}
for _, bao := range baolist {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "apt install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 apt 安装 Unbound 编译依赖 %v 失败:%v", bao, err)
return
}
}
}
} else if packageManager == "yum" {
baolist = []string{
"gcc",
"openssl-devel",
"expat-devel",
"libevent-devel",
"make",
"hiredis",
"hiredis-devel",
}
for _, bao := range baolist {
if bao == "hiredis" {
mdstr = "yum search hiredis"
cmdsearch := exec.Command("sh", "-c", mdstr)
cmdsearch.Stdout = os.Stdout
cmdsearch.Stderr = os.Stderr
output, errsearch := cmdsearch.CombinedOutput()
if errsearch != nil {
pterm.Error.Printfln("运行 yum 命令时发生错误: %v", errsearch)
}
if strings.Contains(string(output), "hiredis") {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "yum install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 yum 安装 Unbound 编译依赖%v失败:%v", bao, err)
return
}
}
} else {
cmdishiredis := exec.Command("find", "/", "-name", "libhiredis.so.*")
output, errishiredis := cmdishiredis.CombinedOutput()
if errishiredis != nil {
pterm.Error.Printfln("执行寻找 libhiredis.so.* 失败:%v", errishiredis)
return
}
if strings.Contains(string(output), "libhiredis") {
pterm.DefaultBasicText.Println("hiredis 已经安装过了")
if !checkldso() {
mdstr = "echo '/usr/local/lib' >> /etc/ld.so.conf && ldconfig"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err == nil {
pterm.DefaultBasicText.Println("hiredis 配置成功")
} else {
pterm.Error.Printfln("hiredis 配置失败:%v", err)
return
}
}
break
} else {
mdstr = "cd /root && wget https://oss.xlxbk.cn/hiredis-1.2.0.tar.gz && tar zxvf hiredis-1.2.0.tar.gz -C" + downloadpath + " && cd " + downloadpath + "/hiredis-1.2.0 && make && make install"
cmdhiredis := exec.Command("sh", "-c", mdstr)
cmdhiredis.Stdout = os.Stdout
cmdhiredis.Stderr = os.Stderr
if err := cmdhiredis.Run(); err == nil {
pterm.DefaultBasicText.Println("hiredis 编译安装成功")
} else {
pterm.Error.Printfln("hiredis 编译安装失败:%v", err)
return
}
if !checkldso() {
mdstr = "echo '/usr/local/lib' >> /etc/ld.so.conf && ldconfig"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err == nil {
pterm.DefaultBasicText.Println("hiredis 配置成功")
} else {
pterm.Error.Printfln("hiredis 配置失败:%v", err)
return
}
}
break
}
}
} else {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "yum install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 yum 安装 Unbound 编译依赖 %v 失败:%v", bao, err)
return
}
}
}
}
} else if packageManager == "dnf" {
baolist = []string{
"gcc",
"openssl-devel",
"expat-devel",
"libevent-devel",
"make",
"hiredis",
"hiredis-devel",
}
for _, bao := range baolist {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
cmdisinstall.Stdout = os.Stdout
cmdisinstall.Stderr = os.Stderr
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "dnf install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 dnf 安装 Unbound 编译依赖 %v 失败:%v", bao, err)
return
}
}
}
} else if packageManager == "zypper" {
mdstr = "zypper install -t pattern -y devel_C_C++"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 zypper 安装 Unbound 开发工具和编译器失败:%v", err)
return
}
mdstr = "zypper install -y libevent-devel openssl-devel libexpat-devel libuv-devel libhiredis-devel"
cmd = exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 zypper 安装 Unbound 必要的依赖包失败:%v", err)
return
}
} else if packageManager == "pacman" {
mdstr = "pacman -S -y base-devel libevent openssl expat libuv hiredis"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 pacman 安装 Unbound 编译依赖失败:%v", err)
return
}
}
pterm.DefaultBasicText.Println("Unbound 编译所需依赖安装完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始安装 libevent")
cmdislibevent := exec.Command("find", "/", "-name", "libevent-2.1.so.*")
output, errislibevent := cmdislibevent.CombinedOutput()
if errislibevent != nil {
pterm.Error.Printfln("执行寻找 libevent-2.1.so.* 失败:%v", errislibevent)
return
}
if strings.Contains(string(output), "libevent-2.1.so") {
pterm.DefaultBasicText.Println("libevent 已经安装过了")
} else {
mdstr = "cd /root && wget https://oss.xlxbk.cn/libevent-2.1.12-stable.tar.gz && tar -zxvf libevent-2.1.12-stable.tar.gz && cd libevent-2.1.12-stable && ./configure && make && make install"
cmdlibevent := exec.Command("sh", "-c", mdstr)
cmdlibevent.Stdout = os.Stdout
cmdlibevent.Stderr = os.Stderr
errlibevent := cmdlibevent.Run()
if errlibevent != nil {
pterm.Error.Printfln("编译安装 libevent 失败:%v", errlibevent)
return
}
pterm.DefaultBasicText.Println("libevent 安装完成")
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始下载 Unbound 源代码")
mdstr = "cd /root && wget https://nlnetlabs.nl/downloads/unbound/unbound-latest.tar.gz && tar -zxvf unbound-latest.tar.gz -C " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("下载/解压 Unbound 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("Unbound 源代码下载完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始编译 Unbound")
filename := checkfilename()
mdstr = "cd " + downloadpath + "/" + filename + " && ./configure --enable-subnet --with-libevent --with-libhiredis --enable-cachedb && make && make install"
cmdinstall := exec.Command("sh", "-c", mdstr)
cmdinstall.Stdout = os.Stdout
cmdinstall.Stderr = os.Stderr
errinstall := cmdinstall.Run()
if errinstall != nil {
pterm.Error.Printfln("编译安装 Unbound 失败:%v", errinstall)
return
}
pterm.DefaultBasicText.Println("Unbound 编译安装完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始配置 Unbound")
mdstr = "cd /usr/local/etc/unbound && wget -O root.hints https://www.internic.net/domain/named.root && touch unbound.log && chmod 666 unbound.log && rm -rf unbound.conf && wget https://oss.xlxbk.cn/unbound.conf && chmod 644 unbound.conf"
cmdconf := exec.Command("sh", "-c", mdstr)
cmdconf.Stdout = os.Stdout
cmdconf.Stderr = os.Stderr
errconf := cmdconf.Run()
if errconf != nil {
pterm.Error.Printfln("下载 Unbound 相关配置文件失败:%v", errconf)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件下载完成")
mdstr = "chmod 777 /usr/local/etc/unbound"
cmdchmod := exec.Command("sh", "-c", mdstr)
cmdchmod.Stdout = os.Stdout
cmdchmod.Stderr = os.Stderr
errchmod := cmdchmod.Run()
if errchmod != nil {
pterm.Error.Printfln("赋予目录权限失败:%v", errchmod)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件赋予权限完成")
mdstr = "unbound-anchor"
cmdunbchor := exec.Command("sh", "-c", mdstr)
cmdunbchor.Stdout = os.Stdout
cmdunbchor.Stderr = os.Stderr
errunbchor := cmdunbchor.Run()
if errunbchor != nil {
mdstr = "ls /usr/local/etc/unbound/root.key"
cmdlsrootkey := exec.Command("sh", "-c", mdstr)
cmdlsrootkey.Stdout = os.Stdout
cmdlsrootkey.Stderr = os.Stderr
errlsrootkey := cmdlsrootkey.Run()
if errlsrootkey != nil {
pterm.Error.Printfln("配置Unbound DNSSEC 失败:%v", errunbchor)
return
}
}
pterm.DefaultBasicText.Println("Unbound DNSSEC 配置完成")
mdstr = "unbound-control-setup"
cmdunbcoup := exec.Command("sh", "-c", mdstr)
cmdunbcoup.Stdout = os.Stdout
cmdunbcoup.Stderr = os.Stderr
errunbcoup := cmdunbcoup.Run()
if errunbcoup != nil {
pterm.Error.Printfln("配置Unbound 远程控制失败:%v", errunbcoup)
return
}
pterm.DefaultBasicText.Println("Unbound 远程控制配置完成")
if !checkUserExists("unbound") {
mdstr = "groupadd unbound && useradd -m -g unbound -s /bin/false unbound"
cmduser := exec.Command("sh", "-c", mdstr)
cmduser.Stdout = os.Stdout
cmduser.Stderr = os.Stderr
erruser := cmduser.Run()
if erruser != nil {
pterm.Error.Printfln("创建 unbound 用户失败:%v", erruser)
return
}
pterm.DefaultBasicText.Println("创建 unbound 用户完成")
}
mdstr = "cd /lib/systemd/system && wget https://oss.xlxbk.cn/unbound.service && chmod 644 unbound.service && systemctl daemon-reload && systemctl enable unbound"
cmdserv := exec.Command("sh", "-c", mdstr)
cmdserv.Stdout = os.Stdout
cmdserv.Stderr = os.Stderr
errserv := cmdserv.Run()
if errserv != nil {
pterm.Error.Printfln("配置 Unbound 服务失败:%v", errserv)
return
}
pterm.DefaultBasicText.Println("Unbound 服务配置完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始安装 redis")
cmdisredis := exec.Command("sh", "-c", "command -v redis")
cmdisredis.Stdout = os.Stdout
cmdisredis.Stderr = os.Stderr
errisredis := cmdisredis.Run()
if errisredis == nil {
pterm.DefaultBasicText.Println("redis 已经安装过了")
} else {
cmdredis := exec.Command(packageManager, "install", "-y", "redis")
cmdredis.Stdout = os.Stdout
cmdredis.Stderr = os.Stderr
errredis := cmdredis.Run()
if errredis != nil {
pterm.Error.Printfln("安装 redis 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("redis 安装完成")
}
mdstr = "systemctl start unbound"
cmdstart := exec.Command("sh", "-c", mdstr)
cmdstart.Stdout = os.Stdout
cmdstart.Stderr = os.Stderr
errstart := cmdstart.Run()
if errstart != nil {
pterm.Error.Printfln("启动 Unbound 服务失败:%v", errstart)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).WithStyle(pterm.NewStyle()).Println("Unbound 默认目录 /usr/local/etc/unbound 配置文件、日志什么的都在这个目录有特殊需求可以自行更改配置文件")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("Unbound 监听端口 5363")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("Unbound 监听IP 所有IP")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("使用 lsof -i:5353 可以检测Unbound是否启动成功")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("如果启动失败,请运行 systemctl status unbound 截图发送给开发者")
}
case "2 升级Unbound":
var mdstr string
result, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("选择y会删除 /root 目录下 unbound 相关文件和文件夹").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if result {
mdstr = "cd /root && rm -rf unbound*"
cmd := exec.Command("sh", "-c", mdstr)
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("删除 unbound 相关文件以及文件夹失败:%v", err)
return
}
}
mdstr = "mkdir -p " + downloadpath
cmdmkdir := exec.Command("sh", "-c", mdstr)
errmkdir := cmdmkdir.Run()
if errmkdir != nil {
pterm.Error.Printfln("创建 /root/unbound 目录失败:%v", errmkdir)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始下载 Unbound 源代码")
mdstr = "cd /root && wget https://nlnetlabs.nl/downloads/unbound/unbound-latest.tar.gz && tar -zxvf unbound-latest.tar.gz -C " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("下载/解压 Unbound 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("Unbound 源代码下载完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始编译 Unbound")
filename := checkfilename()
mdstr = "cd " + downloadpath + "/" + filename + " && ./configure --enable-subnet --with-libevent --with-libhiredis --enable-cachedb && make && make install"
cmdinstall := exec.Command("sh", "-c", mdstr)
cmdinstall.Stdout = os.Stdout
cmdinstall.Stderr = os.Stderr
errinstall := cmdinstall.Run()
if errinstall != nil {
pterm.Error.Printfln("编译安装 Unbound 失败:%v", errinstall)
return
}
pterm.DefaultBasicText.Println("Unbound 编译安装完成")
mdstr = "systemctl restart unbound"
cmdstart := exec.Command("sh", "-c", mdstr)
cmdstart.Stdout = os.Stdout
cmdstart.Stderr = os.Stderr
errstart := cmdstart.Run()
if errstart != nil {
pterm.Error.Printfln("重新启动 Unbound 服务失败:%v", errstart)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("Unbound 升级完成")
}
}
func checkUserExists(username string) bool {
file, err := os.Open("/etc/passwd")
if err != nil {
pterm.Error.Printfln("打开/etc/passwd文件失败:%v", err)
return false
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Split(line, ":")
if len(fields) > 0 && fields[0] == username {
return true
}
}
if err := scanner.Err(); err != nil {
pterm.Error.Printfln("读取/etc/passwd文件失败:%v", err)
}
return false
}
func checkfilename() string {
entries, _ := os.ReadDir(downloadpath)
for _, entry := range entries {
if strings.Contains(entry.Name(), "unbound") {
return entry.Name()
}
}
return ""
}
func checkldso() bool {
file, err := os.Open("/etc/ld.so.conf")
if err != nil {
pterm.Error.Printfln("打开 /etc/ld.so.conf 文件失败:%v", err)
return false
}
defer func(file *os.File) {
err = file.Close()
if err != nil {
pterm.Error.Printfln("关闭 /etc/ld.so.conf 文件失败:%v", err)
}
}(file)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "/usr/local/lib") {
return true
}
}
return false
}
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"runtime"
"strings"
"github.com/valyala/fastjson"
"github.com/pterm/pterm"
)
const (
downloadpath = "/root/unbound"
)
func main() {
pterm.DefaultHeader.WithFullWidth().WithBackgroundStyle(pterm.NewStyle(pterm.BgCyan)).WithTextStyle(pterm.NewStyle(pterm.FgBlack)).Println("Unbound 一键安装脚本[V1.0.1]")
pterm.DefaultHeader.WithFullWidth().WithBackgroundStyle(pterm.NewStyle(pterm.BgBlue)).WithTextStyle(pterm.NewStyle(pterm.FgBlack)).Println("--- XLX笔记 | www.xlxbk.cn ---")
var options []string
options = []string{
"0 退出脚本",
"1 安装Unbound+AdGuardHome",
"2 升级Unbound",
}
selectedOption, _ := pterm.DefaultInteractiveSelect.WithOptions(options).Show()
switch selectedOption {
case "0 退出脚本":
return
case "1 安装Unbound+AdGuardHome":
var mdstr string
var packageManager string
var baolist []string
pterm.DefaultBasicText.Println("操作系统类型:" + pterm.LightMagenta(runtime.GOOS))
pterm.DefaultBasicText.Println("CPU 架构:" + pterm.LightMagenta(runtime.GOARCH))
if runtime.GOOS != "linux" {
pterm.Error.Println("脚本只支持Linux系统")
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始检测系统使用的包管理器")
packageManagers := []string{"apt", "yum", "dnf", "zypper", "pacman"}
for _, manager := range packageManagers {
cmd := exec.Command(manager, "--version")
err := cmd.Run()
if err == nil {
pterm.DefaultBasicText.Printfln("系统使用的包管理器可能是: %s\n", manager)
packageManager = manager
break
} else {
pterm.Error.Printfln("命令 %s 未找到或执行出错: %v", manager, err)
}
}
if packageManager == "" {
pterm.Error.Println("系统未找到任何包管理器")
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始检测系统是否安装 wget")
cmdwget := exec.Command("sh", "-c", "command -v wget")
errwget := cmdwget.Run()
if errwget == nil {
pterm.DefaultBasicText.Println("系统已安装 wget")
} else {
pterm.Error.Println("系统未安装 wget")
pterm.DefaultBasicText.Println("开始安装 wget")
cmd := exec.Command(packageManager, "install", "-y", "wget")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("安装 wget 失败:%v", err)
return
}
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始检测系统是否安装 tar")
cmdtar := exec.Command("sh", "-c", "command -v tar")
errtar := cmdtar.Run()
if errtar == nil {
pterm.DefaultBasicText.Println("系统已安装 tar")
} else {
pterm.Error.Println("系统未安装 tar")
pterm.DefaultBasicText.Println("开始安装 tar")
cmd := exec.Command(packageManager, "install", "-y", "tar")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("安装 tar 失败:%v", err)
return
}
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始配置 Unbound 编译环境")
result, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("选择y会删除 /root 目录下 unbound 相关文件以及文件夹和 libevent 相关的文件和文件夹").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if result {
mdstr = "cd /root && rm -rf unbound* && rm -rf libevent* && rm -rf hiredis*"
cmd := exec.Command("sh", "-c", mdstr)
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("删除 unbound 相关文件以及文件夹失败:%v", err)
return
}
}
mdstr = "mkdir -p " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("创建 /root/unbound 目录失败:%v", err)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("请根据上述输出的包管理器选择对应的执行更新系统的命令")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("由于部分系统会弹出交互式的更新所以需要手动执行这步操作")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("apt update && apt upgrade -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("yum update -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("dnf update -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("zypper update -y")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("pacman -Syu")
resultss, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("是否已经手动更新了系统").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if resultss {
} else {
pterm.Error.Println("请手动更新系统后再执行脚本")
return
}
results, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("是否已经手动配置了 Unbound 运行环境").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if results {
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始下载 Unbound 源代码")
mdstr = "cd /root && wget https://nlnetlabs.nl/downloads/unbound/unbound-latest.tar.gz && tar -zxvf unbound-latest.tar.gz -C " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("下载/解压 Unbound 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("Unbound 源代码下载完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始编译 Unbound")
filename := checkfilename()
mdstr = "cd " + downloadpath + "/" + filename + " && ./configure --enable-subnet --with-libevent --with-libhiredis --enable-cachedb && make && make install"
cmdinstall := exec.Command("sh", "-c", mdstr)
cmdinstall.Stdout = os.Stdout
cmdinstall.Stderr = os.Stderr
errinstall := cmdinstall.Run()
if errinstall != nil {
pterm.Error.Printfln("编译安装 Unbound 失败:%v", errinstall)
return
}
pterm.DefaultBasicText.Println("Unbound 编译安装完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始配置 Unbound")
mdstr = "cd /usr/local/etc/unbound && wget -O root.hints https://www.internic.net/domain/named.root && touch unbound.log && chmod 666 unbound.log && rm -rf unbound.conf && wget https://oss.xlxbk.cn/unbound.conf && chmod 644 unbound.conf"
cmdconf := exec.Command("sh", "-c", mdstr)
cmdconf.Stdout = os.Stdout
cmdconf.Stderr = os.Stderr
errconf := cmdconf.Run()
if errconf != nil {
pterm.Error.Printfln("下载 Unbound 相关配置文件失败:%v", errconf)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件下载完成")
mdstr = "chmod 777 /usr/local/etc/unbound"
cmdchmod := exec.Command("sh", "-c", mdstr)
cmdchmod.Stdout = os.Stdout
cmdchmod.Stderr = os.Stderr
errchmod := cmdchmod.Run()
if errchmod != nil {
pterm.Error.Printfln("赋予目录权限失败:%v", errchmod)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件赋予权限完成")
mdstr = "unbound-anchor"
cmdunbchor := exec.Command("sh", "-c", mdstr)
cmdunbchor.Stdout = os.Stdout
cmdunbchor.Stderr = os.Stderr
errunbchor := cmdunbchor.Run()
if errunbchor != nil {
mdstr = "ls /usr/local/etc/unbound/root.key"
cmdlsrootkey := exec.Command("sh", "-c", mdstr)
cmdlsrootkey.Stdout = os.Stdout
cmdlsrootkey.Stderr = os.Stderr
errlsrootkey := cmdlsrootkey.Run()
if errlsrootkey != nil {
pterm.Error.Printfln("配置Unbound DNSSEC 失败:%v", errunbchor)
return
}
}
pterm.DefaultBasicText.Println("Unbound DNSSEC 配置完成")
mdstr = "unbound-control-setup"
cmdunbcoup := exec.Command("sh", "-c", mdstr)
cmdunbcoup.Stdout = os.Stdout
cmdunbcoup.Stderr = os.Stderr
errunbcoup := cmdunbcoup.Run()
if errunbcoup != nil {
pterm.Error.Printfln("配置Unbound 远程控制失败:%v", errunbcoup)
return
}
pterm.DefaultBasicText.Println("Unbound 远程控制配置完成")
if !checkUserExists("unbound") {
mdstr = "groupadd unbound && useradd -m -g unbound -s /bin/false unbound"
cmduser := exec.Command("sh", "-c", mdstr)
cmduser.Stdout = os.Stdout
cmduser.Stderr = os.Stderr
erruser := cmduser.Run()
if erruser != nil {
pterm.Error.Printfln("创建 unbound 用户失败:%v", erruser)
return
}
pterm.DefaultBasicText.Println("创建 unbound 用户完成")
}
mdstr = "cd /lib/systemd/system && wget https://oss.xlxbk.cn/unbound.service && chmod 644 unbound.service && systemctl daemon-reload && systemctl enable unbound"
cmdserv := exec.Command("sh", "-c", mdstr)
cmdserv.Stdout = os.Stdout
cmdserv.Stderr = os.Stderr
errserv := cmdserv.Run()
if errserv != nil {
pterm.Error.Printfln("配置 Unbound 服务失败:%v", errserv)
return
}
pterm.DefaultBasicText.Println("Unbound 服务配置完成")
mdstr = "systemctl start unbound"
cmdstart := exec.Command("sh", "-c", mdstr)
cmdstart.Stdout = os.Stdout
cmdstart.Stderr = os.Stderr
errstart := cmdstart.Run()
if errstart != nil {
pterm.Error.Printfln("启动 Unbound 服务失败:%v", errstart)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("Unbound 默认目录 /usr/local/etc/unbound 配置文件、日志什么的都在这个目录有特殊需求可以自行更改配置文件")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("Unbound 监听端口 5353")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("Unbound 监听IP 所有IP")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("使用 lsof -i:5353 可以检测Unbound是否启动成功")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.BgLightRed)).Println("如果启动失败,请运行 systemctl status unbound 截图发送给开发者")
} else {
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("安装 Unbound 编译所需依赖")
if packageManager == "apt" {
baolist = []string{
"build-essential",
"libssl-dev",
"libexpat1-dev",
"bison",
"flex",
"libhiredis-dev",
}
for _, bao := range baolist {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "apt install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 apt 安装 Unbound 编译依赖 %v 失败:%v", bao, err)
return
}
}
}
} else if packageManager == "yum" {
baolist = []string{
"gcc",
"openssl-devel",
"expat-devel",
"libevent-devel",
"make",
"hiredis",
"hiredis-devel",
}
for _, bao := range baolist {
if bao == "hiredis" {
mdstr = "yum search hiredis"
cmdsearch := exec.Command("sh", "-c", mdstr)
cmdsearch.Stdout = os.Stdout
cmdsearch.Stderr = os.Stderr
output, errsearch := cmdsearch.CombinedOutput()
if errsearch != nil {
pterm.Error.Printfln("运行 yum 命令时发生错误: %v", errsearch)
}
if strings.Contains(string(output), "hiredis") {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "yum install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 yum 安装 Unbound 编译依赖%v失败:%v", bao, err)
return
}
}
} else {
cmdishiredis := exec.Command("find", "/", "-name", "libhiredis.so.*")
output, errishiredis := cmdishiredis.CombinedOutput()
if errishiredis != nil {
pterm.Error.Printfln("执行寻找 libhiredis.so.* 失败:%v", errishiredis)
return
}
if strings.Contains(string(output), "libhiredis") {
pterm.DefaultBasicText.Println("hiredis 已经安装过了")
if !checkldso() {
mdstr = "echo '/usr/local/lib' >> /etc/ld.so.conf && ldconfig"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err == nil {
pterm.DefaultBasicText.Println("hiredis 配置成功")
} else {
pterm.Error.Printfln("hiredis 配置失败:%v", err)
return
}
}
break
} else {
mdstr = "cd /root && wget https://oss.xlxbk.cn/hiredis-1.2.0.tar.gz && tar zxvf hiredis-1.2.0.tar.gz -C" + downloadpath + " && cd " + downloadpath + "/hiredis-1.2.0 && make && make install"
cmdhiredis := exec.Command("sh", "-c", mdstr)
cmdhiredis.Stdout = os.Stdout
cmdhiredis.Stderr = os.Stderr
if err := cmdhiredis.Run(); err == nil {
pterm.DefaultBasicText.Println("hiredis 编译安装成功")
} else {
pterm.Error.Printfln("hiredis 编译安装失败:%v", err)
return
}
if !checkldso() {
mdstr = "echo '/usr/local/lib' >> /etc/ld.so.conf && ldconfig"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err == nil {
pterm.DefaultBasicText.Println("hiredis 配置成功")
} else {
pterm.Error.Printfln("hiredis 配置失败:%v", err)
return
}
}
break
}
}
} else {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "yum install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 yum 安装 Unbound 编译依赖 %v 失败:%v", bao, err)
return
}
}
}
}
} else if packageManager == "dnf" {
baolist = []string{
"gcc",
"openssl-devel",
"expat-devel",
"libevent-devel",
"make",
"hiredis",
"hiredis-devel",
}
for _, bao := range baolist {
mdstr = "command -v " + bao
cmdisinstall := exec.Command("sh", "-c", mdstr)
cmdisinstall.Stdout = os.Stdout
cmdisinstall.Stderr = os.Stderr
if err := cmdisinstall.Run(); err == nil {
continue
} else {
mdstr = "dnf install -y " + bao
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 dnf 安装 Unbound 编译依赖 %v 失败:%v", bao, err)
return
}
}
}
} else if packageManager == "zypper" {
mdstr = "zypper install -t pattern -y devel_C_C++"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 zypper 安装 Unbound 开发工具和编译器失败:%v", err)
return
}
mdstr = "zypper install -y libevent-devel openssl-devel libexpat-devel libuv-devel libhiredis-devel"
cmd = exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 zypper 安装 Unbound 必要的依赖包失败:%v", err)
return
}
} else if packageManager == "pacman" {
mdstr = "pacman -S -y base-devel libevent openssl expat libuv hiredis"
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("使用 pacman 安装 Unbound 编译依赖失败:%v", err)
return
}
}
pterm.DefaultBasicText.Println("Unbound 编译所需依赖安装完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始安装 libevent")
cmdislibevent := exec.Command("find", "/", "-name", "libevent-2.1.so.*")
output, errislibevent := cmdislibevent.CombinedOutput()
if errislibevent != nil {
pterm.Error.Printfln("执行寻找 libevent-2.1.so.* 失败:%v", errislibevent)
return
}
if strings.Contains(string(output), "libevent-2.1.so") {
pterm.DefaultBasicText.Println("libevent 已经安装过了")
} else {
mdstr = "cd /root && wget https://oss.xlxbk.cn/libevent-2.1.12-stable.tar.gz && tar -zxvf libevent-2.1.12-stable.tar.gz && cd libevent-2.1.12-stable && ./configure && make && make install"
cmdlibevent := exec.Command("sh", "-c", mdstr)
cmdlibevent.Stdout = os.Stdout
cmdlibevent.Stderr = os.Stderr
errlibevent := cmdlibevent.Run()
if errlibevent != nil {
pterm.Error.Printfln("编译安装 libevent 失败:%v", errlibevent)
return
}
pterm.DefaultBasicText.Println("libevent 安装完成")
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始下载 Unbound 源代码")
mdstr = "cd /root && wget https://nlnetlabs.nl/downloads/unbound/unbound-latest.tar.gz && tar -zxvf unbound-latest.tar.gz -C " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("下载/解压 Unbound 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("Unbound 源代码下载完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始编译 Unbound")
filename := checkfilename()
mdstr = "cd " + downloadpath + "/" + filename + " && ./configure --enable-subnet --with-libevent --with-libhiredis --enable-cachedb && make && make install"
cmdinstall := exec.Command("sh", "-c", mdstr)
cmdinstall.Stdout = os.Stdout
cmdinstall.Stderr = os.Stderr
errinstall := cmdinstall.Run()
if errinstall != nil {
pterm.Error.Printfln("编译安装 Unbound 失败:%v", errinstall)
return
}
pterm.DefaultBasicText.Println("Unbound 编译安装完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始配置 Unbound")
mdstr = "cd /usr/local/etc/unbound && wget -O root.hints https://www.internic.net/domain/named.root && touch unbound.log && chmod 666 unbound.log && rm -rf unbound.conf && wget https://oss.xlxbk.cn/unbound.conf && chmod 644 unbound.conf"
cmdconf := exec.Command("sh", "-c", mdstr)
cmdconf.Stdout = os.Stdout
cmdconf.Stderr = os.Stderr
errconf := cmdconf.Run()
if errconf != nil {
pterm.Error.Printfln("下载 Unbound 相关配置文件失败:%v", errconf)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件下载完成")
mdstr = "chmod 777 /usr/local/etc/unbound"
cmdchmod := exec.Command("sh", "-c", mdstr)
cmdchmod.Stdout = os.Stdout
cmdchmod.Stderr = os.Stderr
errchmod := cmdchmod.Run()
if errchmod != nil {
pterm.Error.Printfln("赋予目录权限失败:%v", errchmod)
return
}
pterm.DefaultBasicText.Println("Unbound 配置文件赋予权限完成")
mdstr = "unbound-anchor"
cmdunbchor := exec.Command("sh", "-c", mdstr)
cmdunbchor.Stdout = os.Stdout
cmdunbchor.Stderr = os.Stderr
errunbchor := cmdunbchor.Run()
if errunbchor != nil {
mdstr = "ls /usr/local/etc/unbound/root.key"
cmdlsrootkey := exec.Command("sh", "-c", mdstr)
cmdlsrootkey.Stdout = os.Stdout
cmdlsrootkey.Stderr = os.Stderr
errlsrootkey := cmdlsrootkey.Run()
if errlsrootkey != nil {
pterm.Error.Printfln("配置Unbound DNSSEC 失败:%v", errunbchor)
return
}
}
pterm.DefaultBasicText.Println("Unbound DNSSEC 配置完成")
mdstr = "unbound-control-setup"
cmdunbcoup := exec.Command("sh", "-c", mdstr)
cmdunbcoup.Stdout = os.Stdout
cmdunbcoup.Stderr = os.Stderr
errunbcoup := cmdunbcoup.Run()
if errunbcoup != nil {
pterm.Error.Printfln("配置Unbound 远程控制失败:%v", errunbcoup)
return
}
pterm.DefaultBasicText.Println("Unbound 远程控制配置完成")
if !checkUserExists("unbound") {
mdstr = "groupadd unbound && useradd -m -g unbound -s /bin/false unbound"
cmduser := exec.Command("sh", "-c", mdstr)
cmduser.Stdout = os.Stdout
cmduser.Stderr = os.Stderr
erruser := cmduser.Run()
if erruser != nil {
pterm.Error.Printfln("创建 unbound 用户失败:%v", erruser)
return
}
pterm.DefaultBasicText.Println("创建 unbound 用户完成")
}
mdstr = "cd /lib/systemd/system && wget https://oss.xlxbk.cn/unbound.service && chmod 644 unbound.service && systemctl daemon-reload && systemctl enable unbound"
cmdserv := exec.Command("sh", "-c", mdstr)
cmdserv.Stdout = os.Stdout
cmdserv.Stderr = os.Stderr
errserv := cmdserv.Run()
if errserv != nil {
pterm.Error.Printfln("配置 Unbound 服务失败:%v", errserv)
return
}
pterm.DefaultBasicText.Println("Unbound 服务配置完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始安装 redis")
cmdisredis := exec.Command("sh", "-c", "command -v redis")
cmdisredis.Stdout = os.Stdout
cmdisredis.Stderr = os.Stderr
errisredis := cmdisredis.Run()
if errisredis == nil {
pterm.DefaultBasicText.Println("redis 已经安装过了")
} else {
cmdredis := exec.Command(packageManager, "install", "-y", "redis")
cmdredis.Stdout = os.Stdout
cmdredis.Stderr = os.Stderr
errredis := cmdredis.Run()
if errredis != nil {
pterm.Error.Printfln("安装 redis 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("redis 安装完成")
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始安装 AdGuardHome")
mdstr = "cd ~ && wget https://oss.xlxbk.cn/AdGuardHomeinstall.sh && chmod +x AdGuardHomeinstall.sh && ./AdGuardHomeinstall.sh"
cmdadg := exec.Command("sh", "-c", mdstr)
cmdadg.Stdout = os.Stdout
cmdadg.Stderr = os.Stderr
erradg := cmdadg.Run()
if erradg != nil {
pterm.Error.Printfln("安装 AdGuardHome 失败:%v", erradg)
return
}
pterm.DefaultBasicText.Println("AdGuardHome 安装完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始配置 AdGuardHome")
resultname, _ := pterm.DefaultInteractiveTextInput.WithDefaultText("请输入用户名").Show()
resultpwd, _ := pterm.DefaultInteractiveTextInput.WithDefaultText("请输入密码").Show()
if resultname == "" || resultpwd == "" {
pterm.Error.Printfln("用户名或密码不能为空")
return
}
initLoginConfig(resultname, resultpwd)
cook := getAdGuardHomeCookie(resultname, resultpwd)
setconfone(cook)
setconftwo(cook)
list := getfiles(cook)
if len(list) != 0 {
for _, v := range list {
delfiles(cook, v)
}
}
mdstr = "systemctl start unbound"
cmdstart := exec.Command("sh", "-c", mdstr)
cmdstart.Stdout = os.Stdout
cmdstart.Stderr = os.Stderr
errstart := cmdstart.Run()
if errstart != nil {
pterm.Error.Printfln("启动 Unbound 服务失败:%v", errstart)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).WithStyle(pterm.NewStyle()).Println("Unbound 默认目录 /usr/local/etc/unbound 配置文件、日志什么的都在这个目录有特殊需求可以自行更改配置文件")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("Unbound 监听端口 5363")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("Unbound 监听IP 所有IP")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("使用 lsof -i:5363 可以检测Unbound是否启动成功")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("如果启动失败,请运行 systemctl status unbound 截图发送给开发者")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println("AdGuardHome 管理界面地址:http://你的公网ip:9000")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgRed)).Println(fmt.Sprintf("AdGuardHome 账号:%s 密码:%s", resultname, resultpwd))
}
case "2 升级Unbound":
var mdstr string
result, _ := pterm.DefaultInteractiveConfirm.WithDefaultText("选择y会删除 /root 目录下 unbound 相关文件和文件夹").WithTextStyle(pterm.NewStyle(pterm.FgYellow)).Show()
if result {
mdstr = "cd /root && rm -rf unbound*"
cmd := exec.Command("sh", "-c", mdstr)
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("删除 unbound 相关文件以及文件夹失败:%v", err)
return
}
}
mdstr = "mkdir -p " + downloadpath
cmdmkdir := exec.Command("sh", "-c", mdstr)
errmkdir := cmdmkdir.Run()
if errmkdir != nil {
pterm.Error.Printfln("创建 /root/unbound 目录失败:%v", errmkdir)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始下载 Unbound 源代码")
mdstr = "cd /root && wget https://pure.xlxbk.cn/nlnetlabs/downloads/unbound/unbound-latest.tar.gz && tar -zxvf unbound-latest.tar.gz -C " + downloadpath
cmd := exec.Command("sh", "-c", mdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
pterm.Error.Printfln("下载/解压 Unbound 失败:%v", err)
return
}
pterm.DefaultBasicText.Println("Unbound 源代码下载完成")
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("开始编译 Unbound")
filename := checkfilename()
mdstr = "cd " + downloadpath + "/" + filename + " && ./configure --enable-subnet --with-libevent --with-libhiredis --enable-cachedb && make && make install"
cmdinstall := exec.Command("sh", "-c", mdstr)
cmdinstall.Stdout = os.Stdout
cmdinstall.Stderr = os.Stderr
errinstall := cmdinstall.Run()
if errinstall != nil {
pterm.Error.Printfln("编译安装 Unbound 失败:%v", errinstall)
return
}
pterm.DefaultBasicText.Println("Unbound 编译安装完成")
mdstr = "systemctl restart unbound"
cmdstart := exec.Command("sh", "-c", mdstr)
cmdstart.Stdout = os.Stdout
cmdstart.Stderr = os.Stderr
errstart := cmdstart.Run()
if errstart != nil {
pterm.Error.Printfln("重新启动 Unbound 服务失败:%v", errstart)
return
}
pterm.DefaultBasicText.WithStyle(pterm.NewStyle(pterm.FgGreen)).Println("Unbound 升级完成")
}
}
func checkUserExists(username string) bool {
file, err := os.Open("/etc/passwd")
if err != nil {
pterm.Error.Printfln("打开/etc/passwd文件失败:%v", err)
return false
}
defer func(file *os.File) {
err = file.Close()
if err != nil {
pterm.Error.Printfln("关闭/etc/passwd文件失败:%v", err)
}
}(file)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Split(line, ":")
if len(fields) > 0 && fields[0] == username {
return true
}
}
if err := scanner.Err(); err != nil {
pterm.Error.Printfln("读取/etc/passwd文件失败:%v", err)
}
return false
}
func checkfilename() string {
entries, _ := os.ReadDir(downloadpath)
for _, entry := range entries {
if strings.Contains(entry.Name(), "unbound") {
return entry.Name()
}
}
return ""
}
func checkldso() bool {
file, err := os.Open("/etc/ld.so.conf")
if err != nil {
pterm.Error.Printfln("打开 /etc/ld.so.conf 文件失败:%v", err)
return false
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "/usr/local/lib") {
return true
}
}
return false
}
func initLoginConfig(name string, pwd string) {
url := "http://127.0.0.1:3000/control/install/configure"
method := "POST"
payload := strings.NewReader(fmt.Sprintf(`{"web":{"ip":"0.0.0.0","port":9000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":5353,"status":"","can_autofix":false},"username":"%s","password":"%s"}`, name, pwd))
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
pterm.Error.Printfln("请求失败:%v", err)
return
}
req.Header.Add("Accept", "application/json, text/plain, */*")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("Pragma", "no-cache")
req.Header.Add("Proxy-Connection", "keep-alive")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Connection", "keep-alive")
res, err := client.Do(req)
if err != nil {
pterm.Error.Printfln("初始化设置 AdGuardHome 失败:%v", err)
return
}
defer func(Body io.ReadCloser) {
err = Body.Close()
if err != nil {
pterm.Error.Printfln("关闭 Body 失败:%v", err)
}
}(res.Body)
}
func getAdGuardHomeCookie(name string, pwd string) string {
url := "http://127.0.0.1:9000/control/login"
method := "POST"
payload := strings.NewReader(fmt.Sprintf(`{"name":"%s","password":"%s"}`, name, pwd))
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
pterm.Error.Printfln("请求失败:%v", err)
return ""
}
req.Header.Add("Accept", "application/json, text/plain, */*")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("Pragma", "no-cache")
req.Header.Add("Proxy-Connection", "keep-alive")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Connection", "keep-alive")
res, err := client.Do(req)
if err != nil {
pterm.Error.Printfln("获取 AdGuardHome Cookie 失败:%v", err)
return ""
}
defer func(Body io.ReadCloser) {
err = Body.Close()
if err != nil {
pterm.Error.Printfln("关闭 Body 失败:%v", err)
}
}(res.Body)
return res.Cookies()[0].Name + "=" + res.Cookies()[0].Value
}
func setconfone(cook string) {
url := "http://127.0.0.1:9000/control/dns_config"
method := "POST"
payload := strings.NewReader(`{"fallback_dns":[],"bootstrap_dns":["127.0.0.1:5363","tcp://127.0.0.1:5363"],"upstream_mode":"parallel","resolve_clients":true,"local_ptr_upstreams":[],"use_private_ptr_resolvers":true,"upstream_dns":["127.0.0.1:5363","tcp://127.0.0.1:5363"]}`)
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
pterm.Error.Printfln("请求失败:%v", err)
return
}
req.Header.Add("Accept", "application/json, text/plain, */*")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("Pragma", "no-cache")
req.Header.Add("Proxy-Connection", "keep-alive")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
req.Header.Add("Cookie", cook)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Connection", "keep-alive")
res, err := client.Do(req)
if err != nil {
pterm.Error.Printfln("配置 AdGuardHome 失败:%v", err)
return
}
defer func(Body io.ReadCloser) {
err = Body.Close()
if err != nil {
pterm.Error.Printfln("关闭 Body 失败:%v", err)
}
}(res.Body)
}
func setconftwo(cook string) {
url := "http://127.0.0.1:9000/control/dns_config"
method := "POST"
payload := strings.NewReader(`{"ratelimit":0,"ratelimit_subnet_len_ipv4":24,"ratelimit_subnet_len_ipv6":56,"ratelimit_whitelist":[],"blocking_mode":"nxdomain","blocking_ipv4":"0.0.0.0","blocking_ipv6":"::","blocked_response_ttl":10,"edns_cs_enabled":true,"disable_ipv6":false,"dnssec_enabled":true,"edns_cs_use_custom":false,"edns_cs_custom_ip":""}`)
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
pterm.Error.Printfln("请求失败:%v", err)
return
}
req.Header.Add("Accept", "application/json, text/plain, */*")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("Pragma", "no-cache")
req.Header.Add("Proxy-Connection", "keep-alive")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
req.Header.Add("Cookie", cook)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Connection", "keep-alive")
res, err := client.Do(req)
if err != nil {
pterm.Error.Printfln("配置 AdGuardHome 失败:%v", err)
return
}
defer func(Body io.ReadCloser) {
err = Body.Close()
if err != nil {
pterm.Error.Printfln("关闭 Body 失败:%v", err)
}
}(res.Body)
}
func getfiles(cook string) []string {
var list []string
url := "http://127.0.0.1:9000/control/filtering/status"
method := "GET"
client := &http.Client{}
req, err := http.NewRequest(method, url, nil)
if err != nil {
pterm.Error.Printfln("请求失败:%v", err)
return list
}
req.Header.Add("Accept", "application/json, text/plain, */*")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("Pragma", "no-cache")
req.Header.Add("Proxy-Connection", "keep-alive")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
req.Header.Add("Cookie", cook)
req.Header.Add("Connection", "keep-alive")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return list
}
defer func(Body io.ReadCloser) {
err = Body.Close()
if err != nil {
pterm.Error.Printfln("关闭 Body 失败:%v", err)
}
}(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
pterm.Error.Printfln("获取 AdGuardHome 过滤列表失败:%v", err)
return list
}
var p fastjson.Parser
jsonda, _ := p.ParseBytes(body)
listtump := jsonda.GetArray("filters")
if len(listtump) == 0 {
return list
} else {
for _, v := range listtump {
list = append(list, string(v.GetStringBytes("url")))
}
}
return list
}
func delfiles(cook string, liststring string) {
url := "http://127.0.0.1:9000/control/filtering/remove_url"
method := "POST"
payload := strings.NewReader(fmt.Sprintf(`{"url":"%s","whitelist":false}`, liststring))
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
pterm.Error.Printfln("请求失败:%v", err)
return
}
req.Header.Add("Accept", "application/json, text/plain, */*")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("Pragma", "no-cache")
req.Header.Add("Proxy-Connection", "keep-alive")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
req.Header.Add("Cookie", cook)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Connection", "keep-alive")
res, err := client.Do(req)
if err != nil {
pterm.Error.Printfln("删除 AdGuardHome 过滤列表失败:%v", err)
return
}
defer func(Body io.ReadCloser) {
err = Body.Close()
if err != nil {
pterm.Error.Printfln("关闭 Body 失败:%v", err)
}
}(res.Body)
}
这个脚本会有几个地方要确认,按照提示确认即可,理论上兼容所有linux系统,目前只测试了Debian、Ubuntu、RockyLinux这三个
wget https://oss.xlxbk.cn/unbound/linux_amd64/V1.0.0/installunbound && chmod +x installunbound && ./installunbound
wget https://oss.xlxbk.cn/unbound/linux_amd64/V1.0.1/installunbound && chmod +x installunbound && ./installunbound
有问题截图发评论区,看到了就会回复
更新历史
- V1.0.0_2024-06-15 初代版本发布
- V1.0.1_2024-07-10 增加AdGuardHome安装
© 版权声明
原创文章未经允许请勿转载。
THE END
请登录后查看评论内容