Compare commits

...

10 Commits

Author SHA1 Message Date
zhaoyupeng
eb87d6fbed feat: 🚛 完成了 client 资源创建 2025-12-31 18:58:52 +08:00
zhaoyupeng
0bcb138fd5 feat: 添加了 imager 工具 package
refactor: 将  images 的获取分散到各个组件里面
2025-12-29 23:01:42 +08:00
zhaoyupeng
c53c15fa8c chore: caddy config file(json)
nginx: proxy version api
2025-12-05 18:39:30 +08:00
zhaoyupeng
f4f3590aec feat(hsnet): add upsert.sh script for hs-net deployment automation
- Implement upsert.sh script to automate hs-net deployment steps
- Copy token file to /etc/yizhisec/token
- Detect local IP and update /etc/hosts accordingly
- Replace placeholder IPs in server.conf files
- Create required directories including /mnt/huge and workspace paths
- Copy configuration files and binaries based on CPU AVX support
- Copy lastVersion.txt to workspace
- Load hs-net container image using k0s ctr
- Install, enable, and start hs-net systemd service
- Write upsert.sh file to workdir with appropriate permissions
2025-12-01 17:47:58 +08:00
zhaoyupeng
3a29e6221d feat: add hs-net make
wip: hs-net upsert.sh
2025-11-28 19:39:26 +08:00
zhaoyupeng
38def02bf4 feat(front): add front app build command and minio support
- Add new command "front" with flags for replica count and vendor
- Implement front app build logic in maker.AppFront method
- Add minio to make command list
- Add minio and minio-init images to image list
- Change EMQX dependency path to "dependency/emqx"
- Update app OEM logic to use model.GetVendor for vendor info
- Fix app OEM download and rename logic with updated vendor fields
- Modify nginx deployment manifest to allow configurable replicas
- Update user app mysql address to mysql-cluster-mysql-master.db-mysql:3306
- Add server_license_init.conf generation script for configmap upsert
- Clean and reformat imports across several files
- Remove unused package files for make.mysql.go, make.redis.go, make.longhorn.go
2025-11-27 17:35:01 +08:00
zhaoyupeng
fdad0eb36c refactor: 整理结构和 maker 构建方式 2025-11-27 11:06:38 +08:00
zhaoyupeng
11523e3e48 feat: 🎉 complete maker nginx(app) 2025-11-26 22:47:00 +08:00
zhaoyupeng
4ec58ce4e5 feat: 🏡 make apps 2025-11-26 16:17:38 +08:00
zhaoyupeng
1d3c159c00 feat: add dep.proxy 2025-11-25 23:08:52 +08:00
119 changed files with 7522 additions and 331 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ dist
.trae
.vscode
.idea
forge

View File

@@ -1,47 +0,0 @@
# AGENTS.md
This file provides guidance to Qoder (qoder.com) when working with code in this repository.
## Project Overview
This is a Go project (`yizhisec.com/hsv2/forge`) using Go 1.25.2.
## Development Commands
### Build
```bash
go build ./...
```
### Run Tests
```bash
go test ./...
```
### Run Single Test
```bash
go test -run TestName ./path/to/package
```
### Lint
```bash
go vet ./...
```
### Format Code
```bash
go fmt ./...
```
### Tidy Dependencies
```bash
go mod tidy
```
## Architecture
The codebase structure has not yet been established. As the project grows, update this section with:
- Main package organization
- Key architectural patterns
- Important design decisions
- Module relationships

BIN
forge

Binary file not shown.

View File

@@ -0,0 +1,3 @@
FROM docker-mirror.yizhisec.com/library/nginx:1.29.4-alpine3.23
RUN apk add curl wget tzdata unzip

View File

@@ -1,9 +1,8 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/cmd/installcmd"
)
func installCmd() *cobra.Command {
@@ -11,15 +10,12 @@ func installCmd() *cobra.Command {
Use: "install",
Short: "Install the project",
Long: `Install the built project to the specified location.`,
RunE: func(cmd *cobra.Command, args []string) error {
return runInstall(args)
},
}
_cmd.AddCommand(
installcmd.Check(),
installcmd.Prepare(),
)
return _cmd
}
func runInstall(args []string) error {
fmt.Println("Running install command...")
return nil
}

View File

@@ -0,0 +1,39 @@
package installcmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/installer"
)
func Check() *cobra.Command {
var (
workdir string
target string
ignoreDisk bool
ignoreMemory bool
ignoreCPU bool
)
_cmd := &cobra.Command{
Use: "check",
Short: "Check system requirements",
Long: `Check system requirements for the project.`,
RunE: func(cmd *cobra.Command, args []string) error {
_installer := installer.NewInstaller(workdir, target)
return _installer.Check(
cmd.Context(),
installer.WithIgnoreDiskCheck(ignoreDisk),
installer.WithIgnoreMemoryCheck(ignoreMemory),
installer.WithIgnoreCPUCheck(ignoreCPU),
)
},
}
_cmd.Flags().StringVar(&workdir, "workdir", "/root/hs-installation", "Working directory")
_cmd.Flags().StringVar(&target, "target", "self", "Target")
_cmd.Flags().BoolVar(&ignoreDisk, "ignore-check-disk", false, "ignore disk requirement check result")
_cmd.Flags().BoolVar(&ignoreMemory, "ignore-check-memory", false, "ignore memory requirement check result")
_cmd.Flags().BoolVar(&ignoreCPU, "ignore-check-cpu", false, "ignore cpu requirement check result")
return _cmd
}

View File

@@ -0,0 +1,29 @@
package installcmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/installer"
)
func K0s() *cobra.Command {
var (
workdir string
target string
)
_cmd := &cobra.Command{
Use: "k0s",
Short: "Install k0s",
Long: "Install k0s",
RunE: func(cmd *cobra.Command, args []string) error {
_installer := installer.NewInstaller(workdir, target)
return _installer.K0s(cmd.Context())
},
}
_cmd.PersistentFlags().StringVar(&workdir, "workdir", "/root/hs-installation", "working directory")
_cmd.PersistentFlags().StringVar(&target, "target", "self", "target directory")
return _cmd
}

View File

@@ -0,0 +1,30 @@
package installcmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/installer"
)
func Prepare() *cobra.Command {
var (
workdir string
target string
)
_cmd := &cobra.Command{
Use: "prepare",
Short: "Prepare for installation",
Long: "Prepare for installation",
RunE: func(cmd *cobra.Command, args []string) error {
_installer := installer.NewInstaller(workdir, target)
_installer.Prepare(cmd.Context())
return nil
},
}
_cmd.Flags().StringVar(&workdir, "workdir", "/root/hs-installation", "Working directory")
_cmd.Flags().StringVar(&target, "target", "self", "Target")
return _cmd
}

View File

@@ -35,7 +35,7 @@ func makeCmd() *cobra.Command {
return nil
}
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
if err = mk.DependencyCheck(cmd.Context()); err != nil {
return err
}
@@ -50,20 +50,25 @@ func makeCmd() *cobra.Command {
_cmd.PersistentFlags().StringVar(&opt.Cfg.Make.Dir, "dir", "/root/hsv2-installation", "make base directory")
_cmd.AddCommand(
makecmd.ALL(),
makecmd.Images(),
makecmd.Binaries(),
makecmd.Debs(),
makecmd.K0s(),
makecmd.Flannel(),
makecmd.Longhorn(),
makecmd.Mysql(),
makecmd.Redis(),
makecmd.ES(),
makecmd.EMQX(),
makecmd.Minio(),
makecmd.Yosguard(),
makecmd.Registry(),
makecmd.LessDNS(),
makecmd.HSNet(),
makecmd.ConfigMap(),
makecmd.Proxy(),
makecmd.Seafile(),
makecmd.App(),
makecmd.Client(),
)
return _cmd

130
internal/cmd/makecmd/all.go Normal file
View File

@@ -0,0 +1,130 @@
package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func ALL() *cobra.Command {
var (
_workdir string
)
_cmd := &cobra.Command{
Use: "all",
Short: "Make all",
RunE: func(cmd *cobra.Command, args []string) error {
var (
err error
mk = maker.NewMaker(_workdir)
)
if err = mk.Images(cmd.Context()); err != nil {
return err
}
if err = mk.K0s(cmd.Context()); err != nil {
return err
}
if err = mk.Registry(cmd.Context(), "50Gi"); err != nil {
return err
}
if err = mk.Flannel(cmd.Context(), "host-gw"); err != nil {
return err
}
if err = mk.Longhorn(cmd.Context(), 2); err != nil {
return err
}
if err = mk.MySQL(cmd.Context()); err != nil {
return err
}
if err = mk.Redis(cmd.Context()); err != nil {
return err
}
if err = mk.Elastic(cmd.Context()); err != nil {
return err
}
if err = mk.EMQX(cmd.Context()); err != nil {
return err
}
if err = mk.Minio(cmd.Context(), "100Gi"); err != nil {
return err
}
if err = mk.Yosguard(cmd.Context()); err != nil {
return err
}
if err = mk.LessDNS(cmd.Context()); err != nil {
return err
}
if err = mk.HSNet(cmd.Context()); err != nil {
return err
}
if err = mk.ConfigMap(cmd.Context()); err != nil {
return err
}
if err = mk.Proxy(cmd.Context()); err != nil {
return err
}
if err = mk.Seafile(cmd.Context()); err != nil {
return err
}
if err = mk.Proxy(cmd.Context()); err != nil {
return err
}
if err = mk.Seafile(cmd.Context()); err != nil {
return err
}
if err = mk.AppOEM(cmd.Context(), "standard", 2); err != nil {
return err
}
if err = mk.AppUser(cmd.Context(), 2); err != nil {
return err
}
if err = mk.AppClient(cmd.Context(), 2); err != nil {
return err
}
if err = mk.AppGateway(cmd.Context(), 2); err != nil {
return err
}
if err = mk.AppFront(cmd.Context(), "standard", 2); err != nil {
return err
}
if err = mk.AppMie(cmd.Context(), 2); err != nil {
return err
}
if err = mk.AppNginx(cmd.Context()); err != nil {
return err
}
return nil
},
}
_cmd.PersistentFlags().StringVar(&_workdir, "workdir", opt.DefaultWorkdir, "Work directory")
return _cmd
}

View File

@@ -2,6 +2,8 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func App() *cobra.Command {
@@ -12,7 +14,158 @@ func App() *cobra.Command {
_cmd.AddCommand(
appUser(),
appClient(),
appGateway(),
appMie(),
appOEM(),
appFront(),
appNginx(),
)
return _cmd
}
func appUser() *cobra.Command {
var (
replica int
)
_cmd := &cobra.Command{
Use: "user",
Short: "Make User App",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.AppUser(cmd.Context(), replica)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
return _cmd
}
func appClient() *cobra.Command {
var (
replica int
)
_cmd := &cobra.Command{
Use: "client",
Short: "Make Client App",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.AppClient(cmd.Context(), replica)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
return _cmd
}
func appGateway() *cobra.Command {
var (
replica int
)
_cmd := &cobra.Command{
Use: "gateway",
Short: "Make Gateway App",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.AppGateway(cmd.Context(), replica)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
return _cmd
}
func appMie() *cobra.Command {
var (
replica int
)
_cmd := &cobra.Command{
Use: "mie",
Short: "Make Mie App",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.AppMie(cmd.Context(), replica)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
return _cmd
}
func appOEM() *cobra.Command {
var (
replica int
vendor string
)
_cmd := &cobra.Command{
Use: "oem",
Short: "Make OEM App",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.AppOEM(cmd.Context(), vendor, replica)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
_cmd.Flags().StringVar(&vendor, "vendor", "standard", "Vendor name")
return _cmd
}
func appFront() *cobra.Command {
var (
replica int
vendor string
)
_cmd := &cobra.Command{
Use: "front",
Short: "Make Front App",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.AppFront(cmd.Context(), vendor, replica)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
_cmd.Flags().StringVar(&vendor, "vendor", "standard", "Vendor name")
return _cmd
}
func appNginx() *cobra.Command {
var (
replica int
disableSeafile bool
)
_cmd := &cobra.Command{
Use: "nginx",
Short: "Make Nginx App",
RunE: func(cmd *cobra.Command, args []string) error {
opts := []maker.NginxOpt{
maker.WithNginxReplica(replica),
}
if disableSeafile {
opts = append(opts, maker.WithoutNginxSeafile())
}
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.AppNginx(cmd.Context(), opts...)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
_cmd.Flags().BoolVar(&disableSeafile, "disable-seafile", false, "Disable seafile")
return _cmd
}

View File

@@ -1,25 +0,0 @@
package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
)
func appUser() *cobra.Command {
var (
replica int
)
_cmd := &cobra.Command{
Use: "user",
Short: "Make User App",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
return mk.AppUser(cmd.Context(), replica)
},
}
_cmd.Flags().IntVar(&replica, "replica-count", 2, "Replica count")
return _cmd
}

View File

@@ -3,17 +3,16 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Binaries() *cobra.Command {
func K0s() *cobra.Command {
cmd := &cobra.Command{
Use: "binaries",
Aliases: []string{"bin", "B"},
Short: "Build binary files",
Long: `Build all required binary files for the project.`,
Use: "k0s",
Short: "make k0s files(binary, images)",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
return mk.Binary(cmd.Context())
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.K0s(cmd.Context())
},
}

View File

@@ -0,0 +1,176 @@
package makecmd
import (
"encoding/json"
"fmt"
"io"
"net/http"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
tc "yizhisec.com/hsv2/forge/pkg/tool/client"
)
func Client() *cobra.Command {
_cmd := &cobra.Command{
Use: "client",
Short: "make client pkg",
}
_cmd.AddCommand(
clientMac(),
clientWin(),
clientLinux(),
)
return _cmd
}
func clientMac() *cobra.Command {
_cmd := &cobra.Command{
Use: "mac",
Aliases: []string{"macos"},
Short: "make client-mac pkg",
RunE: func(cmd *cobra.Command, args []string) error {
const (
DMG_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_appleclient-csgElink/release/2.1.0-std/hybridscope-client-mac.dmg"
PKG_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_appleclient-csgElink/release/2.1.0-std/hybridscope-client-mac.pkg"
VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_appleclient/release/2.1.0-std/mac_version.json"
)
var (
err error
version string
)
if version, err = clientVersion(VERSION_URL); err != nil {
return err
}
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.ClientPKG(
cmd.Context(),
"mac",
version,
"/api/v2_2/_client/mac",
maker.WithClientPKGDownload(DMG_URL, "hybridscope-client-mac.dmg"),
maker.WithClientPKGDownload(PKG_URL, "hybridscope-client-mac.pkg"),
maker.WithClientPKGDownload(VERSION_URL, "version.json"),
)
},
}
return _cmd
}
func clientWin() *cobra.Command {
_cmd := &cobra.Command{
Use: "win",
Short: "make client-win pkg",
RunE: func(cmd *cobra.Command, args []string) error {
const (
ZIP_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_client-yizhianquan/release/2.1.0-std/hs_client.zip"
VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_client-yizhianquan/release/2.1.0-std/windows_version.json"
)
var (
err error
version string
)
if version, err = clientVersion(VERSION_URL); err != nil {
return err
}
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.ClientPKG(
cmd.Context(),
"win",
version,
"/api/v2_2/_client/win",
maker.WithClientPKGDownload(ZIP_URL, "hs_client.zip"),
maker.WithClientPKGDownload(VERSION_URL, "version.json"),
maker.WithClientPKGCMD("unzip /data/hs_client.zip"),
maker.WithClientPKGCMD("mv resources/app.7z /data/app.7z"),
maker.WithClientPKGCMD("mv resources/hybridscope_offline_installer.exe /data/hybridscope_offline_installer.exe"),
maker.WithClientPKGCMD("mv resources/hybridscope-client-setup-zh.exe /data/hybridscope-client-setup-zh.exe"),
maker.WithClientPKGCMD("mv resources/hybridscope-client-setup-en.exe /data/hybridscope-client-setup-en.exe"),
maker.WithClientPKGCMD("rm -rf /data/hs_client.zip"),
maker.WithClientPKGCMD("rm -rf resources"),
)
},
}
return _cmd
}
func clientLinux() *cobra.Command {
_cmd := &cobra.Command{
Use: "linux",
Short: "make client-linux pkg",
RunE: func(cmd *cobra.Command, args []string) error {
const (
DEB_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/universal-hsclient-linux/release/2.1.0/hscore-linux-2.1.0-csgElink-amd64.deb"
VERSION_URL = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/universal-hsclient-linux/release/2.1.0/hscore-linux-2.1.0-std-amd64.json"
)
var (
err error
version string
)
if version, err = clientVersion(VERSION_URL); err != nil {
return err
}
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.ClientPKG(
cmd.Context(),
"linux",
version,
"/api/v2_2/_client/linux",
maker.WithClientPKGDownload(DEB_URL, "hybridscope-client-linux.deb"),
maker.WithClientPKGDownload(VERSION_URL, "version.json"),
)
},
}
return _cmd
}
func clientVersion(_url string) (string, error) {
type Res struct {
Version string `json:"version"`
}
var (
err error
rr *http.Response
res Res
bs []byte
)
logger.Debug("clientVersion: getting client version with url = %s", _url)
if rr, err = tc.HttpClient().Get(_url); err != nil {
return "", err
}
defer rr.Body.Close()
if bs, err = io.ReadAll(rr.Body); err != nil {
return "", err
}
logger.Debug("clientVersion: got client version body raw: %s, url = %s", string(bs), _url)
if err = json.Unmarshal(bs, &res); err != nil {
return "", err
}
if res.Version == "" {
return "", fmt.Errorf("get version empty, url = %s", _url)
}
return res.Version, nil
}

View File

@@ -3,6 +3,7 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func ConfigMap() *cobra.Command {
@@ -11,7 +12,7 @@ func ConfigMap() *cobra.Command {
Aliases: []string{"cm"},
Short: "构建 ConfigMap",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.ConfigMap(cmd.Context())
},
}

View File

@@ -1,21 +0,0 @@
package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
)
func Debs() *cobra.Command {
cmd := &cobra.Command{
Use: "debs",
Aliases: []string{"deb"},
Short: "Build Debian packages",
Long: `Build all required Debian packages for the project.`,
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
return mk.Deb(cmd.Context())
},
}
return cmd
}

View File

@@ -3,6 +3,7 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func EMQX() *cobra.Command {
@@ -10,7 +11,7 @@ func EMQX() *cobra.Command {
Use: "emqx",
Short: "Make EMQX",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.EMQX(cmd.Context())
},
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func ES() *cobra.Command {
@@ -18,7 +19,7 @@ func ES() *cobra.Command {
Use: "es",
Aliases: []string{"elastic", "elasticsearch"},
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Elastic(
cmd.Context(),
maker.WithElasticMakeHelper(makeHelper),

View File

@@ -3,6 +3,7 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Flannel() *cobra.Command {
@@ -15,7 +16,7 @@ func Flannel() *cobra.Command {
Short: "Build Flannel resources",
Long: `Build and prepare Flannel network resources.`,
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Flannel(cmd.Context(), _mode)
},
}

View File

@@ -3,6 +3,7 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func HSNet() *cobra.Command {
@@ -11,7 +12,7 @@ func HSNet() *cobra.Command {
Short: "Build hs-net",
Long: `Build hs-net`,
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.HSNet(cmd.Context())
},
}

View File

@@ -21,7 +21,7 @@ func Images() *cobra.Command {
return err
}
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Images(cmd.Context())
},

View File

@@ -3,6 +3,7 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func LessDNS() *cobra.Command {
@@ -11,7 +12,7 @@ func LessDNS() *cobra.Command {
Short: "Build lessdns",
Long: `Build lessdns`,
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.LessDNS(cmd.Context())
},
}

View File

@@ -3,6 +3,7 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Longhorn() *cobra.Command {
@@ -15,7 +16,7 @@ func Longhorn() *cobra.Command {
Short: "Build Longhorn resources",
Long: `Build and prepare Longhorn storage resources.`,
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Longhorn(cmd.Context(), replicaCount)
},
}

View File

@@ -0,0 +1,28 @@
package makecmd
import (
"fmt"
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Minio() *cobra.Command {
var (
storage int
)
_cmd := &cobra.Command{
Use: "minio",
Short: "Make Minio",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Minio(cmd.Context(), fmt.Sprintf("%dGi", storage))
},
}
_cmd.Flags().IntVar(&storage, "storage-size", 100, "Storage size(GB)")
return _cmd
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Mysql() *cobra.Command {
@@ -23,7 +24,7 @@ func Mysql() *cobra.Command {
maker.WithMySQLStorage(fmt.Sprintf("%dGi", storage)),
}
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.MySQL(cmd.Context(), opts...)
},
}

View File

@@ -0,0 +1,20 @@
package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Proxy() *cobra.Command {
_cmd := &cobra.Command{
Use: "proxy",
Short: "Make Proxy(by caddy)",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Proxy(cmd.Context())
},
}
return _cmd
}

View File

@@ -1,15 +1,18 @@
package makecmd
import (
"fmt"
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Redis() *cobra.Command {
var (
replicas int
password string
storage string
storage int
)
_cmd := &cobra.Command{
@@ -17,19 +20,19 @@ func Redis() *cobra.Command {
Short: "Build Redis resources",
Long: `Build and prepare Redis cache resources.`,
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Redis(
cmd.Context(),
maker.WithRedisReplicaCount(replicas),
maker.WithRedisPassword(password),
maker.WithRedisStorage(storage),
maker.WithRedisStorage(fmt.Sprintf("%dGi")),
)
},
}
_cmd.Flags().IntVar(&replicas, "replica-count", 2, "Redis 副本数")
_cmd.Flags().StringVar(&password, "password", "", "Redis 密码")
_cmd.Flags().StringVar(&storage, "storage-size", "5Gi", "Redis 存储大小(如: 5Gi)")
_cmd.Flags().IntVar(&storage, "storage-size", 5, "Redis 存储大小(单位Gi)如: 5")
return _cmd
}

View File

@@ -0,0 +1,28 @@
package makecmd
import (
"fmt"
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Registry() *cobra.Command {
var (
size int
)
_cmd := &cobra.Command{
Use: "registry",
Short: "Make registry dependency",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Registry(cmd.Context(), fmt.Sprintf("%dGi", size))
},
}
_cmd.Flags().IntVar(&size, "storage-size", 50, "Redis 存储大小(单位Gi)如: 100")
return _cmd
}

View File

@@ -0,0 +1,27 @@
package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Seafile() *cobra.Command {
var (
storage int
)
_cmd := &cobra.Command{
Use: "seafile",
Short: "make seafile dependency",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Seafile(cmd.Context())
},
}
_cmd.Flags().IntVar(&storage, "storage-size", 50, "指定 seafile 空间大小(单位GB)")
return _cmd
}

View File

@@ -3,6 +3,7 @@ package makecmd
import (
"github.com/spf13/cobra"
"yizhisec.com/hsv2/forge/internal/controller/maker"
"yizhisec.com/hsv2/forge/internal/opt"
)
func Yosguard() *cobra.Command {
@@ -11,7 +12,7 @@ func Yosguard() *cobra.Command {
Aliases: []string{"YOS"},
Short: "Make Yosguard",
RunE: func(cmd *cobra.Command, args []string) error {
mk := maker.NewMaker()
mk := maker.NewMaker(opt.Cfg.Make.Dir)
return mk.Yosguard(cmd.Context())
},
}

View File

@@ -0,0 +1,140 @@
package installer
import (
"context"
"fmt"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/syscheck"
)
type CheckOption func(*checkOpt)
type checkOpt struct {
ignoreDisk bool
ignoreMemory bool
ignoreCPU bool
}
func WithIgnoreDiskCheck(ignore bool) CheckOption {
return func(o *checkOpt) {
o.ignoreDisk = ignore
}
}
func WithIgnoreMemoryCheck(ignore bool) CheckOption {
return func(o *checkOpt) {
o.ignoreMemory = ignore
}
}
func WithIgnoreCPUCheck(ignore bool) CheckOption {
return func(o *checkOpt) {
o.ignoreCPU = ignore
}
}
func (i *installer) Check(ctx context.Context, opts ...CheckOption) error {
var (
err error
o = &checkOpt{}
)
for _, fn := range opts {
fn(o)
}
logger.Info("☑️ installer.Check: Starting system checks...")
if err = i.targetOK(ctx); err != nil {
return err
}
// 1. Check disk space: >= 500GB
logger.Info("☑️ installer.Check: Checking disk space...")
diskSpaceResult, err := syscheck.CheckDiskSpace(ctx, i, 500)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check disk space: %v", err)
return fmt.Errorf("failed to check disk space: %w", err)
}
if !diskSpaceResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskSpaceResult.Name, diskSpaceResult.Message, diskSpaceResult.Expected, diskSpaceResult.Actual)
if !o.ignoreDisk {
return fmt.Errorf("disk space check failed: %s", diskSpaceResult.Message)
}
}
logger.Info("✅ %s: %s", diskSpaceResult.Name, diskSpaceResult.Actual)
// 2. Check disk performance: write >= 500MB/s, read >= 500MB/s
logger.Info("☑️ installer.Check: Checking disk performance...")
diskPerfResult, err := syscheck.CheckDiskPerformance(ctx, i, 500, 500)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check disk performance: %v", err)
return fmt.Errorf("failed to check disk performance: %w", err)
}
if !diskPerfResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskPerfResult.Name, diskPerfResult.Message, diskPerfResult.Expected, diskPerfResult.Actual)
if !o.ignoreDisk {
return fmt.Errorf("disk performance check failed: %s", diskPerfResult.Message)
}
}
logger.Info("✅ %s: %s", diskPerfResult.Name, diskPerfResult.Actual)
// 3. Check memory size: >= 15.5GB
logger.Info("☑️ installer.Check: Checking memory size...")
memResult, err := syscheck.CheckMemory(ctx, i, 15.5)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check memory: %v", err)
return fmt.Errorf("failed to check memory: %w", err)
}
if !memResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", memResult.Name, memResult.Message, memResult.Expected, memResult.Actual)
if !o.ignoreMemory {
return fmt.Errorf("memory check failed: %s", memResult.Message)
}
}
logger.Info("✅ %s: %s", memResult.Name, memResult.Actual)
// 4. Check CPU cores: >= 8
logger.Info("☑️ installer.Check: Checking CPU cores...")
cpuCoresResult, err := syscheck.CheckCPUCores(ctx, i, 8)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check CPU cores: %v", err)
return fmt.Errorf("failed to check CPU cores: %w", err)
}
if !cpuCoresResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuCoresResult.Name, cpuCoresResult.Message, cpuCoresResult.Expected, cpuCoresResult.Actual)
if !o.ignoreCPU {
return fmt.Errorf("CPU cores check failed: %s", cpuCoresResult.Message)
}
}
logger.Info("✅ %s: %s", cpuCoresResult.Name, cpuCoresResult.Actual)
// 5. Check CPU frequency: >= 2GHz
logger.Info("☑️ installer.Check: Checking CPU frequency...")
cpuFreqResult, err := syscheck.CheckCPUFrequency(ctx, i, 2.0)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check CPU frequency: %v", err)
return fmt.Errorf("failed to check CPU frequency: %w", err)
}
if !cpuFreqResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuFreqResult.Name, cpuFreqResult.Message, cpuFreqResult.Expected, cpuFreqResult.Actual)
if !o.ignoreCPU {
return fmt.Errorf("CPU frequency check failed: %s", cpuFreqResult.Message)
}
}
logger.Info("✅ %s: %s", cpuFreqResult.Name, cpuFreqResult.Actual)
logger.Info("✅ installer.Check: All system checks passed successfully!")
return nil
}

View File

@@ -1,8 +1,153 @@
package controller
package installer
import (
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
)
type installer struct {
workdir string
target string
}
func NewInstaller() *installer {
return &installer{}
func (i *installer) buildCommand(ctx context.Context, cmds ...string) *exec.Cmd {
if len(cmds) == 0 {
return nil
}
if i.target == "self" {
return exec.CommandContext(ctx, cmds[0], cmds[1:]...)
}
sshArgs := append([]string{i.target}, cmds...)
return exec.CommandContext(ctx, "ssh", sshArgs...)
}
// ExecuteCommand implements syscheck.CommandExecutor interface
func (i *installer) ExecuteCommand(ctx context.Context, cmds ...string) (string, error) {
cmd := i.buildCommand(ctx, cmds...)
if cmd == nil {
return "", fmt.Errorf("failed to build command")
}
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("command failed: %w, output: %s", err, string(output))
}
return string(output), nil
}
type CopyFileOption func(*copyFileOptions)
type copyFileOptions struct {
addExecutable bool
}
func withCopyFileExecutable() func(*copyFileOptions) {
return func(o *copyFileOptions) {
o.addExecutable = true
}
}
func (i *installer) copyFile(ctx context.Context, src, dst string, opts ...CopyFileOption) error {
logger.Debug("☑️ installer.copyFile: Copying file from %s to %s (target: %s)", src, dst, i.target)
var (
err error
o = &copyFileOptions{}
srcFile, dstFile *os.File
srcInfo os.FileInfo
)
for _, fn := range opts {
fn(o)
}
if i.target == "self" {
// Simply copy file locally
logger.Debug("Copying file locally: %s -> %s", src, dst)
// Open source file
if srcFile, err = os.Open(src); err != nil {
logger.Error("❌ Failed to open source file %s: %v", src, err)
return fmt.Errorf("failed to open source file: %w", err)
}
defer srcFile.Close()
// Create destination directory if needed
dstDir := filepath.Dir(dst)
if err = os.MkdirAll(dstDir, 0755); err != nil {
logger.Error("❌ Failed to create destination directory %s: %v", dstDir, err)
return fmt.Errorf("failed to create destination directory: %w", err)
}
// Create destination file
if dstFile, err = os.Create(dst); err != nil {
logger.Error("❌ Failed to create destination file %s: %v", dst, err)
return fmt.Errorf("failed to create destination file: %w", err)
}
defer dstFile.Close()
// Copy file content
if _, err = io.Copy(dstFile, srcFile); err != nil {
logger.Error("❌ Failed to copy file content: %v", err)
return fmt.Errorf("failed to copy file content: %w", err)
}
// Get source file permissions
if srcInfo, err = os.Stat(src); err == nil {
if err = os.Chmod(dst, srcInfo.Mode()); err != nil {
logger.Debug("⚠️ Failed to set file permissions: %v", err)
}
}
logger.Info("✅ File copied locally: %s -> %s", src, dst)
return nil
}
// Copy file via scp to remote target
logger.Debug("Copying file via scp: %s -> %s:%s", src, i.target, dst)
// Format: scp <src> <target>:<dst>
cmd := exec.CommandContext(ctx, "scp", src, fmt.Sprintf("%s:%s", i.target, dst))
output, err := cmd.CombinedOutput()
if err != nil {
logger.Error("❌ Failed to copy file via scp: %v, output: %s", err, string(output))
return fmt.Errorf("failed to copy file via scp: %w, output: %s", err, string(output))
}
logger.Info("✅ File copied via scp: %s -> %s:%s", src, i.target, dst)
return nil
}
func (i *installer) targetOK(ctx context.Context) error {
cmd := i.buildCommand(ctx, "whoami")
output, err := cmd.CombinedOutput()
if err != nil {
logger.Debug("❌ installer.targetOK: check target %s failed, err = %v", i.target, err)
return err
}
if string(output) != "root\n" {
logger.Debug("❌ installer.targetOK: check target %s failed, output = %s", i.target, string(output))
return errors.New("target is not root user")
}
return nil
}
func NewInstaller(workdir, target string) *installer {
if target == "" {
logger.Warn("🎯 NewInstaller: target empty, set to default(self)")
target = "self"
}
return &installer{workdir: workdir, target: target}
}

View File

@@ -0,0 +1,123 @@
package installer
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"github.com/samber/lo"
)
type K0sOpt func(*k0sOpt)
type k0sOpt struct {
Type string // controller, worker
controllerAsWorker bool
WorkerTokenFile string
}
func WithK0sType(t string) K0sOpt {
types := []string{"controller", "worker"}
return func(o *k0sOpt) {
if lo.Contains(types, t) {
o.Type = t
}
}
}
func WithK0sControllerAsWorker() K0sOpt {
return func(o *k0sOpt) {
o.controllerAsWorker = true
}
}
func WithK0sWorkerTokenFile(filename string) K0sOpt {
return func(o *k0sOpt) {
if filename != "" {
o.WorkerTokenFile = filename
}
}
}
func (i *installer) K0s(ctx context.Context, opts ...K0sOpt) error {
var (
err error
o = &k0sOpt{
Type: "controller",
controllerAsWorker: false,
WorkerTokenFile: "/etc/k0s/worker.token",
}
)
if err = i.targetOK(ctx); err != nil {
return err
}
for _, fn := range opts {
fn(o)
}
binaries := []string{
"dependency/bin/k0s",
"dependency/bin/k9s", "dependency/bin/kubectl", "dependency/bin/helm"}
if err = i.checkFiles(binaries...); err != nil {
return err
}
// check image tar files:
images := []string{
"dependency/image/k0s.apiserver-network-proxy-agent.tar",
"dependency/image/k0s.cni-node.tar",
"dependency/image/k0s.coredns.tar",
"dependency/image/k0s.kube-proxy.tar",
"dependency/image/k0s.kube-router.tar",
"dependency/image/k0s.metrics-server.tar",
"dependency/image/k0s.pause.tar",
}
if err = i.checkFiles(images...); err != nil {
return err
}
// copy binaries to /usr/local/bin and add executable permissions
if err = i.copyFile(ctx, "dependency/bin/k0s", "/usr/local/bin/k0s", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/k9s", "/usr/local/bin/k9s", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/kubectl", "/usr/local/bin/kubectl", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/helm", "/usr/local/bin/helm", withCopyFileExecutable()); err != nil {
return err
}
i.ExecuteCommand(ctx, "k0s", "")
return nil
}
// checkBinaryFiles checks if the required binary files exist in the dependency/bin directory
func (i *installer) checkFiles(fileBaseName ...string) error {
logger.Info("☑️ installer.checkFiles: Checking files in %s...", i.workdir)
for _, file := range fileBaseName {
filename := filepath.Join(i.workdir, file)
logger.Debug("Checking file: %s", filename)
if _, err := os.Stat(filename); os.IsNotExist(err) {
logger.Error("❌ File not found: %s", filename)
return fmt.Errorf("file not found: %s", filename)
} else if err != nil {
logger.Error("❌ Failed to check file %s: %v", filename, err)
return fmt.Errorf("failed to check file %s: %w", filename, err)
}
logger.Info("✅ File found: %s", file)
}
logger.Info("✅ installer.checkBinaryFiles: All binary files verified successfully!")
return nil
}

View File

@@ -0,0 +1,184 @@
package installer
import (
"context"
"fmt"
"gitea.loveuer.com/yizhisec/pkg3/logger"
)
func (i *installer) Prepare(ctx context.Context) error {
var (
err error
)
logger.Info("☑️ installer.Prepare: Starting system preparation...")
if err = i.targetOK(ctx); err != nil {
return err
}
// 1. Set timezone to Asia/Shanghai
logger.Info("☑️ installer.Prepare: Setting timezone to Asia/Shanghai...")
if err = i.setTimezone(ctx); err != nil {
logger.Debug("❌ installer.Prepare: Failed to set timezone: %v", err)
return fmt.Errorf("failed to set timezone: %w", err)
}
logger.Info("✅ installer.Prepare: Timezone set successfully")
// 2. Disable swap
logger.Info("☑️ installer.Prepare: Disabling swap...")
if err = i.disableSwap(ctx); err != nil {
logger.Debug("❌ installer.Prepare: Failed to disable swap: %v", err)
return fmt.Errorf("failed to disable swap: %w", err)
}
logger.Info("✅ installer.Prepare: Swap disabled successfully")
// 3. Load module: iscsi_tcp
logger.Info("☑️ installer.Prepare: Loading kernel module iscsi_tcp...")
if err = i.loadKernelModule(ctx, "iscsi_tcp"); err != nil {
logger.Debug("❌ installer.Prepare: Failed to load iscsi_tcp module: %v", err)
return fmt.Errorf("failed to load iscsi_tcp module: %w", err)
}
logger.Info("✅ installer.Prepare: iscsi_tcp module loaded successfully")
// 4. Load module: br_netfilter
logger.Info("☑️ installer.Prepare: Loading kernel module br_netfilter...")
if err = i.loadKernelModule(ctx, "br_netfilter"); err != nil {
logger.Debug("❌ installer.Prepare: Failed to load br_netfilter module: %v", err)
return fmt.Errorf("failed to load br_netfilter module: %w", err)
}
logger.Info("✅ installer.Prepare: br_netfilter module loaded successfully")
// 5. Apply sysctl settings
logger.Info("☑️ installer.Prepare: Applying sysctl settings...")
if err = i.applySysctlSettings(ctx); err != nil {
logger.Debug("❌ installer.Prepare: Failed to apply sysctl settings: %v", err)
return fmt.Errorf("failed to apply sysctl settings: %w", err)
}
logger.Info("✅ installer.Prepare: Sysctl settings applied successfully")
logger.Info("✅ installer.Prepare: System preparation completed successfully!")
return nil
}
// setTimezone sets the system timezone to Asia/Shanghai
func (i *installer) setTimezone(ctx context.Context) error {
// Check if timezone file exists
cmd := i.buildCommand(ctx, "test", "-f", "/usr/share/zoneinfo/Asia/Shanghai")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("timezone file /usr/share/zoneinfo/Asia/Shanghai not found")
}
// Remove old localtime link/file
cmd = i.buildCommand(ctx, "rm", "-f", "/etc/localtime")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to remove /etc/localtime: %v", err)
}
// Create symlink
cmd = i.buildCommand(ctx, "ln", "-s", "/usr/share/zoneinfo/Asia/Shanghai", "/etc/localtime")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to create symlink: %w", err)
}
return nil
}
// disableSwap disables all swap partitions and removes swap entries from /etc/fstab
func (i *installer) disableSwap(ctx context.Context) error {
// Turn off all swap
cmd := i.buildCommand(ctx, "swapoff", "-a")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to swapoff: %v (may be already off)", err)
}
// Comment out swap entries in /etc/fstab to make it persistent
cmd = i.buildCommand(ctx, "sed", "-i", "/swap/s/^/#/", "/etc/fstab")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to comment swap in /etc/fstab: %v", err)
}
return nil
}
// loadKernelModule loads a kernel module and ensures it's loaded on boot
func (i *installer) loadKernelModule(ctx context.Context, moduleName string) error {
// Load the module immediately
cmd := i.buildCommand(ctx, "modprobe", moduleName)
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to load module %s: %w", moduleName, err)
}
// Add to /etc/modules-load.d/ to load on boot
filePath := fmt.Sprintf("/etc/modules-load.d/%s.conf", moduleName)
cmd = i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("echo '%s' > %s", moduleName, filePath))
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to add module to modules-load.d: %v", err)
}
return nil
}
// applySysctlSettings applies required sysctl settings for Kubernetes
func (i *installer) applySysctlSettings(ctx context.Context) error {
const sysctlConfig = `# Kubernetes required settings
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.forwarding = 1
vm.swappiness = 0
vm.overcommit_memory = 1
vm.panic_on_oom = 0
fs.file-max = 1000000
fs.inotify.max_user_watches = 2099999999
fs.inotify.max_user_instances = 2099999999
fs.inotify.max_queued_events = 2099999999
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192
`
// Write sysctl config file
cmd := i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("cat > /etc/sysctl.d/99-kubernetes.conf << 'EOF'\n%sEOF", sysctlConfig))
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to write sysctl config: %w", err)
}
// Apply sysctl settings
cmd = i.buildCommand(ctx, "sysctl", "--system")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to apply sysctl settings: %w", err)
}
return nil
}

View File

@@ -0,0 +1,5 @@
package installer
func (i *installer) Redis() error {
return nil
}

View File

@@ -0,0 +1,7 @@
package installer
import "context"
func (i *installer) YosGuard(ctx context.Context) error {
return nil
}

View File

@@ -0,0 +1,245 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) AppClient(ctx context.Context, replica int) error {
const (
_config = `Version: "3"
APNs: /yizhisec/hs_nginx/data/443/oem/data.json
BackupSeafile:
Host: hs-resource-server
Port: 19980
ClientDir:
CompatibleAppFile: /yizhisec/hs_nginx/resource/compatible_apps.csv
OEMFilePath: /yizhisec/hs_nginx/data/443/oem/data.json
StorageDir: /data/storage/client_pkg
WindowsClient:
App7zDir: app7z_0
Dir: windows
Database:
Elastic:
Address: http://es-service.db-es:9200
IPDBFile: /etc/client_server/ipv4.ipdb
Mysql:
Address: mysql.db-mysql:3306
DBName: mie
Password: L0hMysql.
UserName: root
Redis:
Address: redis-master.db-redis:6379
Password: HybridScope0xRed1s.
DisabledFeatureFilePath: /etc/yizhisec/disabled_features
ExportWithBlindWatermark: 1
ExternalOA:
Host: ""
Port: 0
ExternalOASecret:
HsID: ""
HsSecret: ""
GatewayLinkPort: 23209
Key:
Token: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
LicensePubKey: /etc/yizhisec/license/pub_key
Log:
Dir: ./log
Level: 1
Name: client_server
MQTTServer:
Host: emqx-service.db-emqx
Port: 1883
NginxEnvFilePath: /yizhisec/hs_nginx/.env
Pipelines:
- processor:
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0UmVzb3VyY2VDb25maWcoImNvbnRyb2xsZXIiKSkKY3B1dCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImNwdSJdCm1lbXQgPSBjWyJoYXJkd2FyZV9yZXNvdXJjZV90aHJlaG9sZCJdWyJtZW1vcnkiXQpkaXNrdCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImRpc2siXQpzd2l0Y2ggPSBjWyJzd2l0Y2giXQpkZWJ1Z19pbmZvID0ge30KZGVmIGNoZWNrKGNwdSwgbWVtb3J5LCBkaXNrKToKICBpZiBzd2l0Y2ggPT0gRmFsc2U6CiAgICByZXR1cm4KICBtc2cgPSAiIgogIGV2dCA9IHt9CiAgaWYgY3B1ID4gY3B1dDoKICAgICAgbXNnID0gIkNQVeWNoOeUqOi2hei/hyIgKyBzdHIoY3B1dCkgKyAiJSIKICBpZiBtZW1vcnkgPiBtZW10OgogICAgaWYgbGVuKG1zZykgIT0gMDoKICAgICAgbXNnID0gbXNnICsgIu+8jCAiCiAgICBtc2cgPSBtc2cgKyAi5YaF5a2Y5Y2g55So6LaF6L+HIiArIHN0cihtZW10KSArICIlIgogIGlmIGRpc2sgPiBkaXNrdDoKICAgIGlmIGxlbihtc2cpICE9IDA6CiAgICAgIG1zZyA9IG1zZyArICLvvIwgIgogICAgbXNnID0gbXNnICsgIuejgeebmOWNoOeUqOi2hei/hyIgKyBzdHIoZGlza3QpICsgIiUiCiAgaWYgbGVuKG1zZykgIT0gMDoKICAgIG1zZyA9IG1zZyArICLjgILor7flj4rml7bmn6XnnIvmnI3liqHlmajnmoTkvb/nlKjmg4XlhrXmiJbph43mlrDosIPmlbTpmIjlgLzjgIIiCiAgaWYgbGVuKG1zZykgPT0gMDoKICAgIHJldHVybiBldnQKICBpZ25vcmUgPSBDb21wYXJlQW5kU2V0KCJfcGlwZWxpbmVfbHN0X2N0IiwgR2V0VGltZVNlYygpLCA2MCo2MCkKICBpZiBpZ25vcmUgPT0gMDoKICAgIGRlYnVnX2luZm9bImluZm8iXSA9ICJyYXRlIGxpbWl0IGlnbm9yZSIKICAgIHJldHVybgogIG1zZyA9ICLnrZbnlaXmjqfliLblmajnmoQiICsgbXNnCiAgZXZ0WyJkZXRhaWwiXSA9IG1zZwogIGV2dFsiZG9tYWluX2lkIl0gPSAwCiAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDEwMQogIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogIHJldHVybiBldnQKCm91dHB1dCA9IGpzb24uZW5jb2RlKGNoZWNrKHZbImNwdSJdWyJwZXJjZW50Il0sIHZbIm1lbSJdWyJwZXJjZW50Il0sIHZbImRpc2siXVsicGVyY2VudCJdKSk=
Type: starlark
sink:
Cols:
- create_time
- detail
- domain_id
- message_type
DB: mie
Host: mysql.db-mysql:3306
Pwd: L0hMysql.
Table: message_content
Type: mysql_sink
User: root
source:
Host: redis-master.db-redis:6379
Key: evt_server_state:controller
Pwd: HybridScope0xRed1s.
Tick: 3
Type: redis_source
- processor:
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0UmVzb3VyY2VDb25maWcoImdhdGV3YXkiKSkKY3B1dCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImNwdSJdCm1lbXQgPSBjWyJoYXJkd2FyZV9yZXNvdXJjZV90aHJlaG9sZCJdWyJtZW1vcnkiXQpkaXNrdCA9IGNbImhhcmR3YXJlX3Jlc291cmNlX3RocmVob2xkIl1bImRpc2siXQpzd2l0Y2ggPSBjWyJzd2l0Y2giXQpkZWJ1Z19pbmZvID0ge30KZGVmIGNoZWNrKCk6CiAgaWYgc3dpdGNoID09IEZhbHNlOgogICAgcmV0dXJuCiAga3MgPSB2LmtleXMoKQogIGlmIGxlbihrcykgPiAwOgogICAgayA9IGtzWzBdCiAgY3B1ID0gdltrXVsiY3B1Il0KICBtZW0gPSB2W2tdWyJtZW0iXQogIGRpc2sgPSB2W2tdWyJkaXNrIl0KICBtc2cgPSAiIgogIGV2dCA9IHt9CiAgaWYgY3B1ID4gY3B1dDoKICAgICAgbXNnID0gIkNQVeWNoOeUqOi2hei/hyIgKyBzdHIoY3B1dCkgKyAiJSIKICBpZiBtZW0gPiBtZW10OgogICAgaWYgbGVuKG1zZykgIT0gMDoKICAgICAgbXNnID0gbXNnICsgIu+8jCAiCiAgICBtc2cgPSBtc2cgKyAi5YaF5a2Y5Y2g55So6LaF6L+HIiArIHN0cihtZW10KSArICIlIgogIGlmIGRpc2sgPiBkaXNrdDoKICAgIGlmIGxlbihtc2cpICE9IDA6CiAgICAgIG1zZyA9IG1zZyArICLvvIwgIgogICAgbXNnID0gbXNnICsgIuejgeebmOWNoOeUqOi2hei/hyIgKyBzdHIoZGlza3QpICsgIiUiCiAgaWYgbGVuKG1zZykgIT0gMDoKICAgIG1zZyA9IG1zZyArICLjgILor7flj4rml7bmn6XnnIvmnI3liqHlmajnmoTkvb/nlKjmg4XlhrXmiJbph43mlrDosIPmlbTpmIjlgLzjgIIiCiAgaWYgbGVuKG1zZykgPT0gMDoKICAgIHJldHVybiBldnQKICBpZ25vcmUgPSBDb21wYXJlQW5kU2V0KCJfcGlwZWxpbmVfbHN0X2d0IiwgR2V0VGltZVNlYygpLCA2MCo2MCkKICBpZiBpZ25vcmUgPT0gMDoKICAgIGRlYnVnX2luZm9bImluZm8iXSA9ICJyYXRlIGxpbWl0IGlnbm9yZSIKICAgIHJldHVybgogIG1zZyA9ICLnvZHlhbPnmoQiICsgbXNnCiAgZXZ0WyJkZXRhaWwiXSA9IG1zZwogIGV2dFsiZG9tYWluX2lkIl0gPSAwCiAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDEwMgogIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogIHJldHVybiBldnQKCm91dHB1dCA9IGpzb24uZW5jb2RlKGNoZWNrKCkp
Type: starlark
sink:
Cols:
- create_time
- detail
- domain_id
- message_type
DB: mie
Host: mysql.db-mysql:3306
Pwd: L0hMysql.
Table: message_content
Type: mysql_sink
User: root
source:
Host: redis-master.db-redis:6379
Key: evt_server_state:gateway
Pwd: HybridScope0xRed1s.
Tick: 3
Type: redis_source
- processor:
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0TGljZW5zZUNvbmZpZygibGljZW5zZSIpKQpsZWZ0ID0gY1sibGljZW5zZV90aHJlaG9sZCJdWyJyZW1haW5pbmdfZGF5Il0Kc3dpdGNoID0gY1sic3dpdGNoIl0KZGVidWdfaW5mbyA9IHt9CmRlZiBjaGVjaygpOgogIGlmIHN3aXRjaCA9PSBGYWxzZToKICAgIHJldHVybgogIAogIGV0ID0gdlsiZXhwaXJlX3RpbWUiXQogIGV2dCA9IHt9CiAgY3VyID0gR2V0VGltZVNlYygpCiAgaWYgZXQgPCAoY3VyICsgbGVmdCAqIDg2NDAwKToKICAgIGlnbm9yZSA9IENvbXBhcmVBbmRTZXQoIl9waXBlbGluZV9sc3RfbCIsIEdldFRpbWVTZWMoKSwgNjAqNjAqMjQpCiAgICBpZiBpZ25vcmUgPT0gMDoKICAgICAgZGVidWdfaW5mb1siaW5mbyJdID0gInJhdGUgbGltaXQgaWdub3JlIgogICAgICByZXR1cm4KICAgIGV2dFsiZGV0YWlsIl0gPSAi5Lqn5ZOB5o6I5p2D5Ymp5L2Z5aSp5pWw5bCP5LqOIiArIHN0cihsZWZ0KSArICLlpKnjgILor7flj4rml7bmn6XnnIvkvb/nlKjmg4XlhrXjgIHph43mlrDosIPmlbTpmIjlgLzmiJbogZTns7vnrqHnkIblkZjmt7vliqDmjojmnYPjgIIiCiAgICBldnRbImRvbWFpbl9pZCJdID0gMAogICAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDIwMgogICAgZXZ0WyJjcmVhdGVfdGltZSJdID0gR2V0VGltZVNlYygpCiAgICByZXR1cm4gZXZ0Cm91dHB1dCA9IGpzb24uZW5jb2RlKGNoZWNrKCkp
Type: starlark
sink:
Cols:
- create_time
- detail
- domain_id
- message_type
DB: mie
Host: mysql.db-mysql:3306
Pwd: L0hMysql.
Table: message_content
Type: mysql_sink
User: root
source:
Host: redis-master.db-redis:6379
Key: license_state_cache:expire
Pwd: HybridScope0xRed1s.
Tick: 3
Type: redis_source
- processor:
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpjID0ganNvbi5kZWNvZGUoR2V0TGljZW5zZUNvbmZpZygibGljZW5zZV9kZXZpY2UiKSkKY2wgPSBjWyJsaWNlbnNlX2RldmljZV90aHJlaG9sZCJdWyJjbGllbnRfbGVmdCJdCm1sID0gY1sibGljZW5zZV9kZXZpY2VfdGhyZWhvbGQiXVsibW9iaWxlX2xlZnQiXQptYyA9IHZbIm1heF9jbGllbnRfY291bnQiXQptYiA9IHZbIm1heF9tb2JpbGVfY2xpZW50X2NvdW50Il0KY2MgPSB2WyJjdXJyZW50X2NsaWVudF9jb3VudCJdCmNtID0gdlsiY3VycmVudF9tb2JpbGVfY2xpZW50X2NvdW50Il0Kc3dpdGNoID0gY1sic3dpdGNoIl0KZGVidWdfaW5mbyA9IHt9CmRlZiBjaGVjaygpOgogIGlmIHN3aXRjaCA9PSBGYWxzZToKICAgIHJldHVybgogIGV2dCA9IHt9CiAgbXNnID0gIiIKICBpZiAobWMtY2MpIDwgY2w6CiAgICBtc2cgPSBtc2cgKyAi5a6i5oi356uv5L2/55So5pWw6YeP5bCR5LqOIitzdHIoY2wpKyLkuKoiCiAgaWYgbGVuKG1zZykgPiAwOgogICAgbXNnID0gbXNnICsgIu+8jCIKICBpZiAobWItY20pIDwgbWw6CiAgICBtc2cgPSBtc2cgKyAi56e75Yqo56uv5L2/55So5pWw6YeP5bCR5LqOIitzdHIobWwpKyLkuKrjgIIiCiAgaWYgbGVuKG1zZykgPiAwOgogICAgaWdub3JlID0gQ29tcGFyZUFuZFNldCgiX3BpcGVsaW5lX2xzdF9sZCIsIEdldFRpbWVTZWMoKSwgNjAqNjApCiAgICBpZiBpZ25vcmUgPT0gMDoKICAgICAgZGVidWdfaW5mb1siaW5mbyJdID0gInJhdGUgbGltaXQgaWdub3JlIgogICAgICByZXR1cm4KICAgIGV2dFsiZGV0YWlsIl0gPSBtc2cgKyAi6K+35Y+K5pe25p+l55yL5L2/55So5oOF5Ya144CB6YeN5paw6LCD5pW06ZiI5YC85oiW6IGU57O7566h55CG5ZGY5re75Yqg5o6I5p2D44CCIgogICAgZXZ0WyJkb21haW5faWQiXSA9IDAKICAgIGV2dFsibWVzc2FnZV90eXBlIl0gPSAyMDIKICAgIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogICAgcmV0dXJuIGV2dApvdXRwdXQgPSBqc29uLmVuY29kZShjaGVjaygpKQ==
Type: starlark
sink:
Cols:
- create_time
- detail
- domain_id
- message_type
DB: mie
Host: mysql.db-mysql:3306
Pwd: L0hMysql.
Table: message_content
Type: mysql_sink
User: root
source:
Host: redis-master.db-redis:6379
Key: license_state_cache:online
Pwd: HybridScope0xRed1s.
Tick: 3
Type: redis_source
- processor:
Script: diA9IGpzb24uZGVjb2RlKGV2ZW50KQpkZWJ1Z19pbmZvID0ge30KZGVmIGNoZWNrKCk6CiAgZXZ0ID0ge30KICBtc2cgPSAiIgogIAogIGRpZCA9IHZbImRvbWFpbl9pZCJdCiAgdWlkID0gdlsidXNlcl9pZCJdCiAgdW5hbWUgPSBHZXRVc2VyTmFtZSh1aWQpCiAgbGltaXQgPSB2WyJsaW1pdCJdCiAgcmFuZ2UgPSB2WyJyYW5nZSJdCiAgcnUgPSB2WyJyYW5nZV91bml0Il0KICBzdSA9IHZbInNpemVfdW5pdCJdCiAgZSA9IHZbImV2dCJdCiAgaWYgZSA9PSAiY291bnQiOgogICAgZXZ0WyJtZXNzYWdlX3R5cGUiXSA9IDMwMQogICAgdW5pdCA9IHN0cihyYW5nZSkKICAgIGlmIHJ1ID09ICJIIiBvciBydSA9PSAiaCI6CiAgICAgICAgdW5pdCA9IHVuaXQgKyAi5bCP5pe2IgogICAgaWYgcnUgPT0gIkQiIG9yIHJ1ID09ICJkIjoKICAgICAgICB1bml0ID0gdW5pdCArICLlpKkiCiAgICBtc2cgPSAi5ZyoIiArIHVuaXQgKyAi5YaF77yMIiArIHVuYW1lICsgIueahOWfn+WGheaWh+S7tuWvvOWHuuaVsOmHj+i2hei/hyIgKyBzdHIobGltaXQpICsgIuS4qu+8jCIKICBpZiBlID09ICJzaXplIjoKICAgIGV2dFsibWVzc2FnZV90eXBlIl0gPSAzMDIKICAgIHVuaXQgPSBzdHIocmFuZ2UpCiAgICBpZiBydSA9PSAiSCIgb3IgcnUgPT0gImgiOgogICAgICAgIHVuaXQgPSB1bml0ICsgIuWwj+aXtiIKICAgIGlmIHJ1ID09ICJEIiBvciBydSA9PSAiZCI6CiAgICAgICAgdW5pdCA9IHVuaXQgKyAi5aSpIgogICAgbXNnID0gIuWcqCIgKyB1bml0ICsgIuWGhe+8jCIgKyB1bmFtZSArICLnmoTln5/lhoXmlofku7blr7zlh7rlpKflsI/otoXov4ciICsgc3RyKGxpbWl0KQogICAgaWYgc3UgPT0gIk0iIG9yIHN1ID09ICJtIjoKICAgICAgICBtc2cgPSBtc2cgKyAiTULvvIwiCiAgICBpZiBzdSA9PSAiRyIgb3Igc3UgPT0gImciOgogICAgICAgIG1zZyA9IG1zZyArICJHQu+8jCIKICAgIGlmIHN1ID09ICJUIiBvciBzdSA9PSAidCI6CiAgICAgICAgbXNnID0gbXNnICsgIlRC77yMIgoKICBpZiBsZW4obXNnKSA+IDA6CiAgICBldnRbImRldGFpbCJdID0gbXNnICsgIuivt+WPiuaXtui3n+i4quWvvOWHuuaWh+S7tueahOaVj+aEn+eoi+W6puaIlumHjeaWsOiwg+aVtOmYiOWAvOOAgiIKICAgIGV2dFsiZG9tYWluX2lkIl0gPSBkaWQKICAgIGV2dFsiY3JlYXRlX3RpbWUiXSA9IEdldFRpbWVTZWMoKQogICAgcmV0dXJuIGV2dApvdXRwdXQgPSBqc29uLmVuY29kZShjaGVjaygpKQ==
Type: starlark
sink:
Cols:
- create_time
- detail
- domain_id
- message_type
DB: mie
Host: mysql.db-mysql:3306
Pwd: L0hMysql.
Table: message_content
Type: mysql_sink
User: root
source:
DB: mie
Host: mysql.db-mysql:3306
Pwd: L0hMysql.
Table: evt_export_file_over
Type: mysql_source
User: root
Sentry:
TracesSampleRate: 1
StaticURLPathPrefix:
NetworkAppIcon: /user/avatar
Storage:
PublicFolderFileDir: /data/storage/public_folder_file
TmpDir: /data/storage/tmp
UploadedFilesDir: /data/storage/uploaded_files
TranslationPath: translation.csv
UpgradeCheckFilePath: /yizhisec/hs_nginx/resource/release_version_record.csv
UserManagement:
Host: user-service
Port: 9013
WatermarkServer:
Host: hs-watermark
Port: 9014
Web:
Host: 0.0.0.0
Mode: release
Port: 9129
Web2:
Host: 0.0.0.0
Mode: release
Port: 9024
WebMessages:
Host: 0.0.0.0
Mode: release
Port: 9025
WorkDir: /yizhisec/client_server
YosGuard:
Host: 172.17.0.1
Port: 7788`
_upsert = `#!/bin/bash
kubectl create configmap config-client --namespace hsv2 --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deployment.yaml
kubectl rollout restart deployment client-deployment -n hsv2`
)
var (
err error
workdir = filepath.Join(m.workdir, "app", "client")
)
logger.Info("☑️ maker.AppClient: 开始构建 client 应用..., dir = %s", workdir)
logger.Debug("☑️ maker.AppClient: 开始创建工作目录 = %s", workdir)
if err = os.MkdirAll(workdir, 0755); err != nil {
logger.Debug("❌ maker.AppClient: 创建目录失败: %v", err)
return err
}
logger.Debug("✅ maker.AppClient: 创建工作目录成功 = %s", workdir)
if replica < 1 {
replica = 1
}
logger.Debug("☑️ maker.AppClient: 开始构建 yaml 资源文件")
content := []byte(fmt.Sprintf(resource.YAMLAppClient, replica))
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), content, 0644); err != nil {
logger.Debug("❌ maker.AppClient: 写入 deployment.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppClient: 开始 yaml 资源文件成功")
logger.Debug("☑️ maker.AppClient: 开始构建 config 文件")
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), []byte(_config), 0644); err != nil {
logger.Debug("❌ maker.AppClient: 写入 config.yml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppClient: 构建 config 文件成功")
logger.Debug("☑️ maker.AppClient: 开始构建 upsert.sh 脚本")
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
logger.Debug("❌ maker.AppClient: 写入 upsert.sh 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppClient: 构建 upsert.sh 脚本成功")
logger.Info("✅ maker.AppClient: 构建 client 应用成功!!!")
return nil
}

View File

@@ -0,0 +1,73 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/model"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) AppFront(ctx context.Context, vendor string, replica int) error {
var (
err error
location = filepath.Join(m.workdir, "app", "front")
bs []byte
_vendor = model.GetVendor(vendor)
)
logger.Info("☑️ maker.Front: 开始构建 front app..., workdir = %s", location)
if _vendor == nil {
logger.Debug("❌ maker.Front: vendor not supported, vendor = %s", vendor)
return fmt.Errorf("vendor not supported: %s", vendor)
}
if err = os.MkdirAll(location, 0755); err != nil {
logger.Debug("❌ maker.Front: 创建目录失败: path = %s, err = %v", location, err)
return err
}
path := filepath.Join(location, "front.user.yaml")
logger.Debug("☑️ maker.Front: writing front.user.yaml, path = %s", path)
bs = []byte(fmt.Sprintf(resource.YAMLAppFrontUser, replica))
if err = os.WriteFile(path, bs, 0644); err != nil {
logger.Debug("❌ maker.Front: 写入 front.user.yaml 失败: path = %s, err = %v", path, err)
return err
}
logger.Debug("✅ maker.Front: write front.user.yaml success, path = %s", path)
path = filepath.Join(location, "front.admin.yaml")
logger.Debug("☑️ maker.Front: writing front.admin.yaml, path = %s", path)
bs = []byte(fmt.Sprintf(resource.YAMLAppFrontAdmin, replica))
if err = os.WriteFile(path, bs, 0644); err != nil {
logger.Debug("❌ maker.Front: 写入 front.admin.yaml 失败: path = %s, err = %v", path, err)
return err
}
logger.Debug("✅ maker.Front: write front.admin.yaml success, path = %s", path)
// todo, pull front images
// 1. make image dir
imgDir := filepath.Join(m.workdir, "dependency", "image")
if err = os.MkdirAll(imgDir, 0755); err != nil {
logger.Debug("❌ maker.Front: 创建目录失败: path = %s, err = %v", imgDir, err)
return err
}
logger.Debug("☑️ maker.Front: pulling front images, vendor = %s", vendor)
if err = m.Image(ctx, _vendor.AppFrontUserImageName, WithImageSave(filepath.Join(imgDir, "app.front.user.tar")), WithImageForcePull(true)); err != nil {
logger.Debug("❌ maker.Front: 拉取 front 用户镜像失败: %s, err = %v", _vendor.AppFrontUserImageName, err)
return err
}
if err = m.Image(ctx, _vendor.AppFrontAdminImageName, WithImageSave(filepath.Join(imgDir, "app.front.admin.tar")), WithImageForcePull(true)); err != nil {
logger.Debug("❌ maker.Front: 拉取 front 管理镜像失败: %s, err = %v", _vendor.AppFrontAdminImageName, err)
return err
}
logger.Info("✅ maker.Front: 构建 front app 完成")
return nil
}

View File

@@ -0,0 +1,98 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) AppGateway(ctx context.Context, replica int) error {
const (
_config = `Version: "3"
Database:
Elastic:
Address: http://es-service.db-es:9200
Mysql:
Address: mysql.db-mysql:3306
DBName: mie
Password: L0hMysql.
UserName: root
Redis:
Address: redis-master.db-redis:6379
Password: HybridScope0xRed1s.
Gateway:
Cert:
ClientCrt: /yizhisec/ssl/client.crt
ClientKey: /yizhisec/ssl/client.key
TokenFilePath: /etc/yizhisec/token
Key:
Token: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
Log:
Dir: ./log
Level: 1
Name: gateway_controller
Sentry:
TracesSampleRate: 1
UserManagement:
Host: user-service
Port: 9013
Web:
Host: 0.0.0.0
Mode: release
Port: 9012
WorkDir: /yizhisec/gateway_controller/workspace
YosGuard:
Host: 172.17.0.1
Port: 7788`
_upsert = `#!/bin/bash
kubectl create configmap config-gateway --namespace hsv2 --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deployment.yaml
kubectl rollout restart deployment gateway-deployment -n hsv2`
)
var (
err error
workdir = filepath.Join(m.workdir, "app", "gateway")
)
logger.Info("☑️ maker.AppGateway: 开始构建 gateway 应用..., dir = %s", workdir)
logger.Debug("☑️ maker.AppGateway: 开始创建工作目录 = %s", workdir)
if err = os.MkdirAll(workdir, 0755); err != nil {
logger.Debug("❌ maker.AppGateway: 创建目录失败: %v", err)
return err
}
logger.Debug("✅ maker.AppGateway: 创建工作目录成功 = %s", workdir)
if replica < 1 {
replica = 1
}
logger.Debug("☑️ maker.AppGateway: 开始构建 yaml 资源文件")
content := []byte(fmt.Sprintf(resource.YAMLAppGateway, replica))
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), content, 0644); err != nil {
logger.Debug("❌ maker.AppGateway: 写入 deployment.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppGateway: 开始 yaml 资源文件成功")
logger.Debug("☑️ maker.AppGateway: 开始构建 config 文件")
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), []byte(_config), 0644); err != nil {
logger.Debug("❌ maker.AppGateway: 写入 config.yml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppGateway: 构建 config 文件成功")
logger.Debug("☑️ maker.AppGateway: 开始构建 upsert.sh 脚本")
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
logger.Debug("❌ maker.AppGateway: 写入 upsert.sh 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppGateway: 构建 upsert.sh 脚本成功")
logger.Info("✅ maker.AppGateway: 构建 gateway 应用成功!!!")
return nil
}

View File

@@ -0,0 +1,196 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) AppMie(ctx context.Context, replica int) error {
const (
_config = `Version: "3"
BackupSeafile:
Host: hs-resource-server
Port: 19980
account_manager:
address: http://user-service:9013
client_server:
msg: http://client-service:9025
api: http://client-service:9024
web: http://client-service:9129
backend_queue_names:
request: request_que
web: web_que
backup_database_server:
host: hs-backup-server
port: 9349
backup_seafile_server:
host: hs-resource-server
port: 19980
clientPKG:
android:
client_pkg_dir: /data/storage/client_pkg/android
client_pkg_file_path: /data/storage/client_pkg/android/SecureApplication-Client-Android.apk
client_pkg_name: SecureApplication-Client-Android.apk
client_version_file_path: /data/storage/client_pkg/android/android_version.json
dir: /data/storage/client_pkg
ios:
client_pkg_dir: /data/storage/client_pkg/ios
client_pkg_file_path: ''
client_pkg_name: ''
client_version_file_path: /data/storage/client_pkg/ios/ios_version.json
linux:
client_pkg_dir: /data/storage/client_pkg/linux
client_pkg_file_path: /data/storage/client_pkg/linux/hscore-ubuntu-22.04-amd64.deb
client_pkg_name: hscore-ubuntu-22.04-amd64.deb
client_version_file_path: /data/storage/client_pkg/linux/linux_version.json
mac:
client_pkg_beta_file_path: /yizhisec/hs_nginx/resource/hybridscope-client-mac-beta.pkg
client_pkg_beta_name: hybridscope-client-mac-beta.pkg
client_pkg_dir: /data/storage/client_pkg/mac
client_pkg_file_path: /yizhisec/hs_nginx/resource/hybridscope-client-mac.pkg
client_pkg_name: hybridscope-client-mac.pkg
client_version_file_path: /data/storage/client_pkg/mac/mac_version.json
oem_dir: /yizhisec/hs_nginx/data/443/oem
oem_file_path: /yizhisec/hs_nginx/data/443/oem/data.json
windows:
client_main_zip_name: app.7z
client_pkg_cfg_file_name: login.conf
client_pkg_dir: /data/storage/client_pkg/windows
client_pkg_unzip_dir: package
client_pkg_zip: /data/storage/client_pkg/windows/dsclient.zip
client_zip_version: version
databases:
elasticsearch:
host: es-service.db-es
port: 9200
ipdb:
path: /etc/mie-server/ipdb/ip.ipdb
mysql:
db: mie
host: mysql-cluster-mysql-master.db-mysql
password: L0hMysql.
port: 3306
username: root
redis:
host: redis-master.db-redis
password: HybridScope0xRed1s.
port: 6379
username: ''
exe_root_license:
path: /etc/mie-server/root.pem
gateway_service:
host: gateway-service.hsv2
port: 9012
host: 0.0.0.0
license:
version: 3
license_init_conf: /etc/mie-server/server_license_init.conf
public_key: /etc/mie-server/license/pub_key
log_level: 20
mqtt_server:
host: emqx-service.db-emqx
port: 1883
port: 9002
resource_server:
address: http://hs-resource-server:19980
secret_key: i345piuh48776lkjsdhfsdfljho
sentry_dsn: null
static_urlpath_prefix:
network_app_icon: /user/avatar
storage:
avatar_dir: /data/storage/avatar
mobile_app_dir: /yizhisec/hs_nginx
network_app_icon: network_app
patch_dir: /data/storage/patch
public_folder_file_dir: /data/storage/public_folder_file
share_file_storage: /data/storage/share_file
sync_avatar_dir: sync
tmp_dir: /data/storage/tmp
upload_avatar_dir: local
uploaded_files: /data/storage/uploaded_files
token_key: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
translation_path: /etc/mie-server/translation.csv
yosguard_service:
host: 172.17.0.1
port: 7788
ElinkLogin: true
export_with_blind_watermark: true`
_upsert = `#!/bin/bash
kubectl create configmap config-api --namespace hsv2 --from-file=conf.yml=./conf.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deployment-api.yaml
kubectl apply -f deployment-sweeper.yaml
kubectl apply -f deployment-worker.yaml
kubectl apply -f deployment-cron.yaml
kubectl rollout restart deployment api-deployment -n hsv2`
)
var (
err error
workdir = filepath.Join(m.workdir, "app", "mie")
)
logger.Info("☑️ maker.AppMie: 开始构建 mie ... workdir = %s", workdir)
logger.Debug("☑️ maker.AppMie: 开始创建工作目录 = %s", workdir)
if err = os.MkdirAll(workdir, 0755); err != nil {
logger.Debug("❌ maker.AppMie: 创建目录失败: %v", err)
return err
}
logger.Debug("✅ maker.AppMie: 创建工作目录成功 = %s", workdir)
if replica < 1 {
replica = 1
}
logger.Debug("☑️ maker.AppMie: 写入 conf.yml 文件..., dest = %s", filepath.Join(workdir, "conf.yml"))
if err = os.WriteFile(filepath.Join(workdir, "conf.yml"), []byte(_config), 0644); err != nil {
logger.Debug("❌ maker.AppMie: 写入 conf.yml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppMie: 写入 conf.yml 文件成功, dest = %s", filepath.Join(workdir, "conf.yml"))
logger.Debug("☑️ maker.AppMie: 写入 deployment-api.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-api.yaml"))
apiYaml := []byte(fmt.Sprintf(resource.YAMLAppMieAPI, replica))
if err = os.WriteFile(filepath.Join(workdir, "deployment-api.yaml"), apiYaml, 0644); err != nil {
logger.Debug("❌ maker.AppMie: 写入 deployment-api.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppMie: 写入 deployment-api.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-api.yaml"))
logger.Debug("☑️ maker.AppMie: 写入 deployment-sweeper.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-sweeper.yaml"))
if err = os.WriteFile(filepath.Join(workdir, "deployment-sweeper.yaml"), resource.YAMLAppMieSweeper, 0644); err != nil {
logger.Debug("❌ maker.AppMie: 写入 deployment-sweeper.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppMie: 写入 deployment-sweeper.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-sweeper.yaml"))
logger.Debug("☑️ maker.AppMie: 写入 deployment-worker.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-worker.yaml"))
if err = os.WriteFile(filepath.Join(workdir, "deployment-worker.yaml"), resource.YAMLAppMieWorker, 0644); err != nil {
logger.Debug("❌ maker.AppMie: 写入 deployment-worker.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppMie: 写入 deployment-worker.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-worker.yaml"))
logger.Debug("☑️ maker.AppMie: 写入 deployment-cron.yaml 文件..., dest = %s", filepath.Join(workdir, "deployment-cron.yaml"))
if err = os.WriteFile(filepath.Join(workdir, "deployment-cron.yaml"), resource.YAMLAppMieCron, 0644); err != nil {
logger.Debug("❌ maker.AppMie: 写入 deployment-cron.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppMie: 写入 deployment-cron.yaml 文件成功, dest = %s", filepath.Join(workdir, "deployment-cron.yaml"))
logger.Debug("☑️ maker.AppMie: 写入 upsert.sh 文件..., dest = %s", filepath.Join(workdir, "upsert.sh"))
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
logger.Debug("❌ maker.AppMie: 写入 upsert.sh 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppMie: 写入 upsert.sh 文件成功, dest = %s", filepath.Join(workdir, "upsert.sh"))
logger.Info("✅ maker.AppMie: 构建 mie 成功!!! workdir = %s", workdir)
return nil
}

View File

@@ -0,0 +1,179 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"github.com/samber/lo"
"yizhisec.com/hsv2/forge/pkg/resource"
)
type NginxOpt func(*nginxOpt)
type nginxOpt struct {
WithoutSeafile bool
Replica int
}
func WithNginxReplica(replica int) NginxOpt {
return func(o *nginxOpt) {
if replica >= 0 {
o.Replica = replica
}
}
}
func WithoutNginxSeafile() NginxOpt {
return func(o *nginxOpt) {
o.WithoutSeafile = true
}
}
func (m *maker) AppNginx(ctx context.Context, opts ...NginxOpt) error {
const (
_upsert = `#!/bin/bash
kubectl create configmap nginx-main --namespace hsv2 --from-file=nginx.conf=./conf/nginx.conf --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap nginx-user --namespace hsv2 --from-file=user.conf=./conf/user.conf --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap nginx-gateway --namespace hsv2 --from-file=gateway.conf=./conf/gateway.conf --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap nginx-web --namespace hsv2 --from-file=web.conf=./conf/web.conf --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap nginx-client --namespace hsv2 --from-file=client.conf=./conf/client.conf --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap nginx-common --namespace hsv2 --from-file=common.conf=./conf/common.conf --dry-run=client -o yaml | kubectl apply -f -
%s
kubectl create configmap ssl-ca-crt --namespace hsv2 --from-file=ca.crt=./ssl/ca.crt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-ffdhe2048 --namespace hsv2 --from-file=ffdhe2048.txt=./ssl/ffdhe2048.txt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-server-crt --namespace hsv2 --from-file=server.crt=./ssl/server.crt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-server-key --namespace hsv2 --from-file=server.key=./ssl/server.key --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-mqtt-crt --namespace hsv2 --from-file=mqtt.server.crt=./ssl/mqtt.server.crt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-mqtt-key --namespace hsv2 --from-file=mqtt.server.key=./ssl/mqtt.server.key --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-client-server-crt --namespace hsv2 --from-file=client.server.crt=./ssl/client.server.crt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-client-server-key --namespace hsv2 --from-file=client.server.key=./ssl/client.server.key --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-web-server-crt --namespace hsv2 --from-file=web.server.crt=./ssl/web.server.crt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-web-server-key --namespace hsv2 --from-file=web.server.key=./ssl/web.server.key --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f nginx.yaml
kubectl rollout restart deployment nginx-deployment -n hsv2`
)
var (
err error
workdir = filepath.Join(m.workdir, "app", "nginx")
o = nginxOpt{
WithoutSeafile: false,
Replica: 2,
}
applySeafile = "kubectl create configmap nginx-seafile --namespace hsv2 --from-file=seafile.conf=./conf/seafile.conf --dry-run=client -o yaml | kubectl apply -f -"
)
logger.Info(" ☑️ maker.AppNginx: 开始构建 nginx, workdir = %s", workdir)
for _, fn := range opts {
fn(&o)
}
logger.Debug(" ☑️ maker.AppNginx: 创建工作目录 = %s", workdir)
if err = os.MkdirAll(workdir, 0755); err != nil {
return err
}
logger.Debug("✅ maker.AppNginx: 创建工作目录成功 = %s", workdir)
// 子目录: conf 与 ssl
confDir := filepath.Join(workdir, "conf")
sslDir := filepath.Join(workdir, "ssl")
logger.Debug(" ☑️ maker.AppNginx: 创建 conf 与 ssl 子目录")
if err = os.MkdirAll(confDir, 0755); err != nil {
logger.Debug("❌ maker.AppNginx: 创建 conf 目录失败: %v", err)
return err
}
if err = os.MkdirAll(sslDir, 0755); err != nil {
logger.Debug("❌ maker.AppNginx: 创建 ssl 目录失败: %v", err)
return err
}
logger.Debug("✅ maker.AppNginx: 创建 conf 与 ssl 子目录成功")
// 写入 nginx 配置文件到 conf 子目录(列表 + for 循环)
logger.Debug(" ☑️ maker.AppNginx: 写入 nginx 配置文件到 conf 子目录")
confFiles := []struct {
name string
content []byte
}{
{"nginx.conf", resource.NGINXMain},
{"user.conf", resource.NGINXUser},
{"gateway.conf", resource.NGINXGateway},
{"web.conf", resource.NGINXWeb},
{"client.conf", resource.NGINXClient},
{"common.conf", resource.NGINXCommon},
}
// 过滤 seafile.conf 文件
if !o.WithoutSeafile {
confFiles = append(confFiles, struct {
name string
content []byte
}{
"seafile.conf", resource.NGINXSeafile,
})
}
for _, f := range confFiles {
dest := filepath.Join(confDir, f.name)
if err = os.WriteFile(dest, f.content, 0644); err != nil {
logger.Debug("❌ maker.AppNginx: 写入 %s 失败: %v", f.name, err)
return err
}
logger.Debug("✅ maker.AppNginx: 写入 %s 成功, dest = %s", f.name, dest)
}
logger.Debug("✅ maker.AppNginx: 写入 nginx 配置文件成功")
// 写入 ssl 文件到 ssl 子目录
logger.Debug(" ☑️ maker.AppNginx: 写入 SSL 证书与密钥到 ssl 子目录")
sslFiles := []struct{ name, content string }{
{"ffdhe2048.txt", resource.SSLFFDHE2048},
{"ca.crt", resource.SSLCaCrt},
{"server.crt", resource.SSLServerCrt},
{"server.key", resource.SSLServerKey},
{"mqtt.server.crt", resource.SSLMQTTServerCrt},
{"mqtt.server.key", resource.SSLMQTTServerKey},
{"client.server.crt", resource.SSLClientServerCrt},
{"client.server.key", resource.SSLClientServerKey},
{"web.server.crt", resource.SSLWebServerCrt},
{"web.server.key", resource.SSLWebServerKey},
}
for _, f := range sslFiles {
dest := filepath.Join(sslDir, f.name)
if err = os.WriteFile(dest, []byte(f.content), 0644); err != nil {
logger.Debug("❌ maker.AppNginx: 写入 %s 失败: %v", f.name, err)
return err
}
logger.Debug("✅ maker.AppNginx: 写入 %s 成功, dest = %s", f.name, dest)
}
// write nginx deployment yaml
dest := filepath.Join(workdir, "nginx.yaml")
content := []byte(fmt.Sprintf(resource.YAMLAppNGINX, o.Replica))
if err = os.WriteFile(dest, content, 0644); err != nil {
logger.Debug("❌ maker.AppNginx: 写入 nginx.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppNginx: 写入 nginx.yaml 成功, dest = %s", dest)
// write nginx upsert script
dest = filepath.Join(workdir, "upsert.sh")
content = []byte(fmt.Sprintf(_upsert, lo.If(o.WithoutSeafile, "").Else(applySeafile)))
if err = os.WriteFile(dest, content, 0755); err != nil {
logger.Debug("❌ maker.AppNginx: 写入 upsert.sh 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppNginx: 写入 upsert.sh 成功, dest = %s", dest)
logger.Info("✅ maker.AppNginx: nginx 构建完成")
return nil
}

View File

@@ -0,0 +1,159 @@
package maker
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/archiver"
"yizhisec.com/hsv2/forge/pkg/model"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) AppOEM(ctx context.Context, vendor string, replica int) error {
const (
_nginx = `user root;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 512;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
sendfile on;
keepalive_timeout 65;
client_max_body_size 10M;
server {
listen 80;
root /data;
location / {
try_files \$uri \$uri/ =404;
}
}
}`
_dockerfile = `FROM hub.yizhisec.com/external/nginx:1.29.1-alpine3.22
WORKDIR /data
COPY oem /data/oem
COPY nginx.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]`
_image = "hub.yizhisec.com/hybridscope/v2/oem-%s:latest"
)
var (
err error
_vendor = model.GetVendor(vendor)
workdir = filepath.Join(m.workdir, "app", "oem")
output []byte
)
logger.Info("☑️ maker.AppOEM: 开始构建 oem[%s], workdir = %s", vendor, workdir)
if _vendor == nil {
supported := model.GetVendorNames()
logger.Debug("❌ maker.AppOEM: vendor not supported, 支持的 vendor 有: %v", supported)
return fmt.Errorf("请检查 vendor 是否正确, 支持的 vendor 有: %v", supported)
}
// 1. make workdir
logger.Debug("☑️ maker.AppOEM: 开始创建 workdir = %s", workdir)
if err = os.MkdirAll(workdir, 0o755); err != nil {
return err
}
logger.Debug("✅ maker.AppOEM: workdir 创建成功 = %s", workdir)
// 2. download oem.tar.gz
logger.Debug("☑️ maker.AppOEM: 开始下载 oem[%s] url = %s", vendor, _vendor)
if err = archiver.DownloadAndExtract(ctx, _vendor.OEMUrl, workdir); err != nil {
logger.Debug("❌ maker.AppOEM: oem[%s] tar 下载失败, url = %s, err = %v", vendor, _vendor.OEMUrl, err)
return err
}
if _vendor.OEMDir != "oem" {
if err = os.Rename(
filepath.Join(workdir, _vendor.OEMDir),
filepath.Join(workdir, "oem"),
); err != nil {
logger.Debug("❌ maker.AppOEM: oem[%s] tar 重命名失败, err = %v", vendor, err)
return err
}
}
logger.Debug("✅ maker.AppOEM: oem[%s] tar 下载成功", vendor)
// 3. write nginx.conf
logger.Debug("☑️ maker.AppOEM: 开始写入 nginx.conf")
if err = os.WriteFile(
filepath.Join(workdir, "nginx.conf"),
[]byte(_nginx),
0o644,
); err != nil {
logger.Debug("❌ maker.AppOEM: nginx.conf 写入失败, err = %v", err)
return err
}
logger.Debug("✅ maker.AppOEM: nginx.conf 写入成功")
// 4. write Dockerfile
logger.Debug("☑️ maker.AppOEM: 开始写入 Dockerfile")
if err = os.WriteFile(
filepath.Join(workdir, "Dockerfile"),
[]byte(_dockerfile),
0o644,
); err != nil {
logger.Debug("❌ maker.AppOEM: Dockerfile 写入失败, err = %v", err)
return err
}
logger.Debug("✅ maker.AppOEM: Dockerfile 写入成功")
// 5. build docker image
imageName := fmt.Sprintf(_image, vendor)
logger.Debug("☑️ maker.AppOEM: 开始构建 docker image = %s", imageName)
// docker build -t <image_name> -f <workdir/Dockerfile> <workdir>
_cmd := exec.CommandContext(ctx, "docker", "build", "-t", imageName, "-f", filepath.Join(workdir, "Dockerfile"), workdir)
if output, err = _cmd.CombinedOutput(); err != nil {
logger.Debug("❌ maker.AppOEM: docker image 构建失败, err = %v, output = %s", err, string(output))
return err
}
logger.Debug("✅ maker.AppOEM: docker image 构建成功, image = %s", imageName)
// 6. save docker image to image dir
logger.Debug("☑️ maker.AppOEM: 开始保存 docker image = %s 到 %s", imageName, filepath.Join(m.workdir, "image", imageName))
if err = os.MkdirAll(filepath.Join(m.workdir, "dependency", "image"), 0o755); err != nil {
logger.Debug("❌ maker.AppOEM: image 目录创建失败, err = %v", err)
return err
}
if err = exec.CommandContext(ctx, "docker", "save", "-o", filepath.Join(m.workdir, "dependency", "image", "app.oem.tar"), imageName).Run(); err != nil {
logger.Debug("❌ maker.AppOEM: docker image 保存失败, err = %v", err)
return err
}
logger.Debug("✅ maker.AppOEM: docker image 保存成功, image = %s", imageName)
// 7. render oem.yaml
logger.Debug("☑️ maker.AppOEM: 开始渲染 deployment.yaml")
oemYAML := fmt.Sprintf(resource.YAMLAppOEM, replica, imageName)
if err = os.WriteFile(
filepath.Join(workdir, "deployment.yaml"),
[]byte(oemYAML),
0o644,
); err != nil {
logger.Debug("❌ maker.AppOEM: deployment.yaml 写入失败, err = %v", err)
return err
}
logger.Debug("✅ maker.AppOEM: deployment.yaml 写入成功")
logger.Info("✅ maker.AppOEM: 开始构建 oem[%s] 成功!!!", vendor)
return nil
}

View File

@@ -2,8 +2,127 @@ package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) AppUser(ctx context.Context, replica int) error {
const (
_config = `Version: "3"
Database:
Elastic:
Address: http://es-service.db-es:9200
IPDB:
Path: /etc/hs_user_management/ipdb/ip.ipdb
Mysql:
Address: mysql-cluster-mysql-master.db-mysql:3306
DBName: mie
Password: L0hMysql.
UserName: root
Redis:
Address: redis-master.db-redis:6379
Password: HybridScope0xRed1s.
DisabledFeatureFilePath: /etc/yizhisec/disabled_features
EnableTenant: false
Key:
Token: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
LicensePubKey: /etc/yizhisec/license/pub_key
Log:
Dir: ./log
Level: 1
Name: hs_user_management
Sentry:
TracesSampleRate: 1
Sso:
DingTalk:
ApiHost: oapi.dingtalk.com
LoginUrl: https://oapi.dingtalk.com/connect/qrconnect
Feishu:
ApiHost: open.feishu.cn
LoginUrl: https://open.feishu.cn/open-apis/authen/v1/index
Proxy:
CallbackHost: hssso.yizhisec.com:33443
Cert:
ClientCrt: /etc/hs_user_management/proxy/certs/client.crt
ClientKey: /etc/hs_user_management/proxy/certs/client.key
ServiceHost: hssso.yizhisec.com:33444
RedirectPath:
BoundFailed: /#/accountSettings/thirdAccount
BoundSuccess: /#/accountSettings/thirdAccount
LoginFailed: /#/thirdError
LoginNeedBoundUser: /#/bind
LoginSuccess: /#/
WorkWeiXin:
ApiHost: qyapi.weixin.qq.com
LoginUrl: https://login.work.weixin.qq.com/wwlogin/sso/login
Storage:
Avatar:
ADSyncDir: ad
Base: /data/storage/avatar
LDAPSyncDir: ldap
LocalDir: local
SyncDir: sync
TranslationPath: translation.csv
Web:
Host: 0.0.0.0
Mode: release
Port: 9013
WorkDir: /yizhisec/hs_user_management/workspace
YosGuard:
Host: 172.17.0.1
Port: 7788
ElinkLogin: false`
_upsert = `#!/bin/bash
kubectl create configmap config-user --namespace hsv2 --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deployment.yaml
kubectl rollout restart deployment user-deployment -n hsv2`
)
var (
err error
workdir = filepath.Join(m.workdir, "app", "user")
)
logger.Info("☑️ maker.AppUser: 开始构建 user 应用..., dir = %s", workdir)
logger.Debug("☑️ maker.AppUser: 开始创建工作目录 = %s", workdir)
if err = os.MkdirAll(workdir, 0755); err != nil {
logger.Debug("❌ maker.AppUser: 创建目录失败: %v", err)
return err
}
logger.Debug("✅ maker.AppUser: 创建工作目录成功 = %s", workdir)
if replica < 1 {
replica = 1
}
logger.Debug("☑️ maker.AppUser: 开始构建 yaml 资源文件")
content := []byte(fmt.Sprintf(resource.YAMLAppUser, replica))
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), []byte(content), 0644); err != nil {
logger.Debug("❌ maker.AppUser: 写入 deployment.yaml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppUser: 开始 yaml 资源文件成功")
// 写入 config.yml
logger.Debug("☑️ maker.AppUser: 开始构建 config 文件")
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), []byte(_config), 0644); err != nil {
logger.Debug("❌ maker.AppUser: 写入 config.yml 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppUser: 构建 config 文件成功")
logger.Debug("☑️ maker.AppUser: 开始构建 upsert.sh 脚本")
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
logger.Debug("❌ maker.AppUser: 写入 upsert.sh 失败: %v", err)
return err
}
logger.Debug("✅ maker.AppUser: 构建 upsert.sh 脚本成功")
logger.Info("✅ maker.AppUser: 构建 user 应用成功!!!")
return nil
}

View File

@@ -5,24 +5,24 @@ import (
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/archiver"
"yizhisec.com/hsv2/forge/pkg/model"
)
func (m *maker) Binary(ctx context.Context) error {
func (m *maker) K0s(ctx context.Context) error {
var (
tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/k8s-bin.tar"
binDir = filepath.Join(opt.Cfg.Make.Dir, "dependency")
tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/k8s-bin.tar"
location = filepath.Join(m.workdir, "dependency", "k0s")
)
logger.Info("☑️ 开始准备 k8s 二进制文件...")
logger.Info("☑️ 开始准备 k0s...")
logger.Debug("下载地址: %s", tarURL)
logger.Debug("目标目录: %s", binDir)
logger.Debug("目标目录: %s", location)
if err := archiver.DownloadAndExtract(
ctx,
tarURL,
binDir,
location,
archiver.WithInsecureSkipVerify(),
archiver.WithGzipCompression(true),
); err != nil {
@@ -30,7 +30,32 @@ func (m *maker) Binary(ctx context.Context) error {
return err
}
logger.Info("✅ 准备 k8s 二进制文件成功!!!")
logger.Debug("☑️ maker.K0s: 开始准备相关镜像...")
var images = []*model.Image{
{Name: "quay.io/k0sproject/apiserver-network-proxy-agent:v0.32.0", Fallback: "hub.yizhisec.com/external/apiserver-network-proxy-agent:v0.32.0", Save: "k0s.apiserver-network-proxy-agent.tar"},
{Name: "quay.io/k0sproject/cni-node:1.7.1-k0s.0", Fallback: "", Save: "k0s.cni-node.tar"},
{Name: "quay.io/k0sproject/coredns:1.12.2", Fallback: "", Save: "k0s.coredns.tar"},
{Name: "quay.io/k0sproject/kube-proxy:v1.33.4", Fallback: "", Save: "k0s.kube-proxy.tar"},
{Name: "quay.io/k0sproject/kube-router:v2.5.0-iptables1.8.11-0", Fallback: "", Save: "k0s.kube-router.tar"},
{Name: "quay.io/k0sproject/metrics-server:v0.7.2-0", Fallback: "", Save: "k0s.metrics-server.tar"},
{Name: "quay.io/k0sproject/pause:3.10.1", Fallback: "", Save: "k0s.pause.tar"},
}
for _, image := range images {
opts := []ImageOpt{
WithImageFallback(image.Fallback),
WithImageSave(filepath.Join(location, image.Save)),
WithImageForcePull(image.Force),
}
if err := m.Image(ctx, image.Name, opts...); err != nil {
logger.Error("❌ maker.K0s: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
logger.Debug("❌ maker.K0s: 获取镜像失败: %s, %v", image.Name, err)
return err
}
}
logger.Debug("✅ maker.K0s: 准备相关镜像成功!!!")
logger.Info("✅ 准备 k0s 成功!!!")
return nil
}

View File

@@ -22,6 +22,10 @@ func (m *maker) DependencyCheck(ctx context.Context) error {
return fmt.Errorf("docker 命令未找到: %w", err)
}
if err := checkCommand(ctx, "7z"); err != nil {
return fmt.Errorf("7z 命令未找到: %w", err)
}
return nil
}

View File

@@ -0,0 +1,154 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"github.com/samber/lo"
"yizhisec.com/hsv2/forge/pkg/resource"
)
type clientPKGDownload struct {
URL string
Name string
}
type ClientPKGOption func(*clientPKGOption)
type clientPKGOption struct {
Downloads []*clientPKGDownload
CMDs []string
}
func WithClientPKGDownload(url, location string) ClientPKGOption {
return func(o *clientPKGOption) {
if url == "" || location == "" {
return
}
o.Downloads = append(o.Downloads, &clientPKGDownload{
URL: url,
Name: location,
})
}
}
func WithClientPKGCMD(cmd string) ClientPKGOption {
return func(o *clientPKGOption) {
if cmd == "" {
return
}
o.CMDs = append(o.CMDs, cmd)
}
}
func (m *maker) ClientPKG(ctx context.Context, _os string, _version string, api string, opts ...ClientPKGOption) error {
const (
Dockerfile = `
FROM hub.yizhisec.com/external/nginx:1.29.4-alpine3.23
RUN mkdir -p /data
%s
%s
COPY nginx.conf /etc/nginx/nginx.conf
`
)
if !lo.Contains([]string{"mac", "win", "linux"}, _os) {
return fmt.Errorf("invalid os: %s", _os)
}
var (
o = &clientPKGOption{
Downloads: []*clientPKGDownload{},
CMDs: []string{},
}
err error
location = filepath.Join(m.workdir, "client", _os)
_file string
_content string
_cmds = ""
)
for _, fn := range opts {
fn(o)
}
logger.Info("☑️ maker.ClientPKG: start build client pkg, os = %s, version = %s, location = %s", _os, _version, location)
if err = os.MkdirAll(location, 0755); err != nil {
logger.Debug("❌ maker.ClientPKG: create directory failed, directory = %s, err = %s", location, err.Error())
return err
}
if err = os.WriteFile(filepath.Join(location, "version.txt"), []byte(_version), 0644); err != nil {
logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", filepath.Join(location, "version.txt"), err.Error())
return err
}
_file = filepath.Join(location, "nginx.conf")
_content = fmt.Sprintf(resource.NGINXClientPKG, api)
logger.Debug("☑️ maker.ClientPKG: start write file = %s", _file)
if os.WriteFile(_file, []byte(_content), 0644); err != nil {
logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", _file, err.Error())
return err
}
logger.Debug("✅ maker.ClientPKG: write file success, file = %s", _file)
lines := lo.Map(o.Downloads, func(d *clientPKGDownload, index int) string {
return fmt.Sprintf("RUN wget -O /data/%s %s", d.Name, d.URL)
})
if len(o.CMDs) > 0 {
_cmds = fmt.Sprintf("RUN %s", strings.Join(o.CMDs, " && "))
}
_file = filepath.Join(location, "Dockerfile")
_content = fmt.Sprintf(Dockerfile, strings.Join(lines, "\n"), _cmds)
logger.Debug("☑️ maker.ClientPKG: start write file = %s", _file)
if os.WriteFile(_file, []byte(_content), 0644); err != nil {
logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", _file, err.Error())
return err
}
logger.Debug("✅ maker.ClientPKG: write file success, file = %s", _file)
imgName := fmt.Sprintf("hub.yizhisec.com/hsv2/client/%s:%s", _os, _version)
logger.Debug("☑️ maker.ClientPKG: build docker image, os = %s, version = %s, full_name = %s", _os, _version, imgName)
_cmd := fmt.Sprintf("docker build --network host -t %s -f Dockerfile .", imgName)
if err = m.RunCommand(ctx, location, _cmd); err != nil {
logger.Debug("❌ maker.ClientPKG: run command failed, cmd = %s, err = %s", _cmd, err.Error())
return err
}
imgLocation := filepath.Join(location, _os+".tar")
logger.Debug("☑️ maker.ClientPKG: save img to %s", imgLocation)
_cmd = fmt.Sprintf("docker save hub.yizhisec.com/hsv2/client/%s:%s -o %s", _os, _version, imgLocation)
if err = m.RunCommand(ctx, location, _cmd); err != nil {
logger.Debug("❌ maker.ClientPKG: run command failed, cmd = %s, err = %s", _cmd, err.Error())
return err
}
imgNameInCluster := fmt.Sprintf("10.96.123.45:80/hsv2/client/%s:%s", _os, _version)
deployment := filepath.Join(location, "deployment.yaml")
_content = strings.ReplaceAll(
fmt.Sprintf(
resource.YAMLClientPKG,
1, // replica 先默认 1 就够了
imgNameInCluster,
),
"__os__",
_os,
)
if err = os.WriteFile(deployment, []byte(_content), 0644); err != nil {
logger.Debug("❌ maker.ClientPKG: write file failed, file = %s, err = %s", deployment, err.Error())
return err
}
logger.Info("️✅ maker.ClientPKG: build client pkg success, os = %s, version = %s, location = %s", _os, _version, location)
return nil
}

View File

@@ -0,0 +1,28 @@
package maker
import (
"context"
"fmt"
"os/exec"
)
func (m *maker) RunCommand(ctx context.Context, dir string, _cmds ...string) error {
if len(_cmds) == 0 {
return nil
}
for _, cmdStr := range _cmds {
cmd := exec.CommandContext(ctx, "sh", "-c", cmdStr)
if dir != "" {
cmd.Dir = dir
}
// Execute the command
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to run command '%s' in directory '%s': %w", cmdStr, dir, err)
}
}
return nil
}

View File

@@ -9,7 +9,6 @@ import (
"gitea.loveuer.com/yizhisec/pkg3/logger"
"github.com/samber/lo"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/downloader"
"yizhisec.com/hsv2/forge/pkg/extractor"
"yizhisec.com/hsv2/forge/pkg/tool/random"
@@ -86,6 +85,11 @@ EEuYRYXDouPJ1F//rYraSoJ4mtaipB6z1A==
-----END EC PRIVATE KEY-----`
upsert = `#!/bin/bash
# Generate server_license_init.conf
uuid=$(cat /proc/sys/kernel/random/uuid)
now=$(date +%s)
echo "{\"uuid\": \"$uuid\", \"install_time\": $now}" > ./server_license_init.conf
kubectl create configmap config-token --namespace hsv2 --from-file=token=./token --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap config-license-init --namespace hsv2 --from-file=server_license_init.conf=./server_license_init.conf --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap config-oem-data --namespace hsv2 --from-file=data.json=./oem_data.json --dry-run=client -o yaml | kubectl apply -f -
@@ -96,12 +100,22 @@ kubectl create configmap ssl-client-key --namespace hsv2 --from-file=client.key=
kubectl create configmap ssl-client-ca-crt --namespace hsv2 --from-file=client.ca.crt=./ssl_client_ca.crt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-client-ca-key --namespace hsv2 --from-file=client.ca.key=./ssl_client_ca.key --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-web-crt --namespace hsv2 --from-file=web.server.crt=./ssl_web.crt --dry-run=client -o yaml | kubectl apply -f -
`
_version_yaml = `
apiVersion: v1
kind: ConfigMap
metadata:
name: config-version
namespace: hsv2
data:
version.txt: |
__version__
`
)
var (
err error
dir = filepath.Join(opt.Cfg.Make.Dir, "configmap")
dir = filepath.Join(m.workdir, "configmap")
vendorUrlMap = map[string]string{
"standard": "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem.tar.gz",
"elink": "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_csgElink.tar.gz",
@@ -204,6 +218,12 @@ kubectl create configmap ssl-web-crt --namespace hsv2 --from-file=web.server.crt
}
logger.Debug("✅ maker.ConfigMap: 写入 ssl_client_ca.key 文件: %s 成功", filepath.Join(dir, "ssl_client_ca.key"))
if err = os.WriteFile(filepath.Join(dir, "version.yaml"), []byte(_version_yaml), 0644); err != nil {
logger.Debug("❌ maker.ConfigMap: 写入 version.yaml 文件: %s 失败, 错误: %v", filepath.Join(dir, "version.yaml"), err)
return err
}
logger.Debug("✅ maker.ConfigMap: 写入 version.yaml 文件: %s 成功", filepath.Join(dir, "version.yaml"))
// upsert configmap
logger.Debug("☑️ maker.ConfigMap: 执行 upsert 脚本: %s", filepath.Join(dir, "upsert.sh"))
if err = os.WriteFile(filepath.Join(dir, "upsert.sh"), []byte(upsert), 0755); err != nil {

View File

@@ -1,37 +0,0 @@
package maker
import (
"context"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/archiver"
)
func (m *maker) Deb(ctx context.Context) error {
var (
tarURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/deb/docker.tar.gz"
binDir = filepath.Join(opt.Cfg.Make.Dir, "dependency", "deb")
)
logger.Info("☑️ 开始准备 deb(docker) 文件...")
logger.Debug("下载地址: %s", tarURL)
logger.Debug("目标目录: %s", binDir)
if err := archiver.DownloadAndExtract(
ctx,
tarURL,
binDir,
archiver.WithInsecureSkipVerify(),
archiver.WithGzipCompression(true),
); err != nil {
logger.Info("❌ 下载并解压 deb(docker) 文件失败")
logger.Debug("下载并解压 deb(docker) 文件失败: %v", err)
return err
}
logger.Info("✅ 准备 deb(docker) 文件成功!!!")
return nil
}

View File

@@ -6,11 +6,11 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/archiver"
"yizhisec.com/hsv2/forge/pkg/model"
"yizhisec.com/hsv2/forge/pkg/resource"
)
@@ -38,7 +38,7 @@ func WithElasticMemRate(memRate int) ElasticOpt {
func WithElasticStorageGi(storage string) ElasticOpt {
return func(o *elasticOpt) {
if matched, _ := regexp.MatchString(`^\d+(\.\d+)?[EPTGMK]i?$`, storage); matched {
if opt.StorageSizeReg.MatchString(storage) {
o.Storage = storage
}
}
@@ -66,8 +66,8 @@ RUN chmod +x /data/create_index.sh
var (
err error
output []byte
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "elastic")
helperTarLocation = filepath.Join(opt.Cfg.Make.Dir, "dependency", "image", "es-init-helper.alpine-3.22.2.tar")
location = filepath.Join(m.workdir, "dependency", "elastic")
helperTarLocation = filepath.Join(m.workdir, "dependency", "image", "es-init-helper.alpine-3.22.2.tar")
)
opt := &elasticOpt{
@@ -179,6 +179,28 @@ RUN chmod +x /data/create_index.sh
}
logger.Debug("✅ maker.Elastic: 写入 kibana.yaml 文件成功")
logger.Debug("☑️ maker.Elastic: 开始获取所需镜像...")
var images = []*model.Image{
{Name: "hub.yizhisec.com/external/elasticsearch:7.17.28", Fallback: "", Save: "elasticsearch.7.17.28.tar"},
{Name: "hub.yizhisec.com/external/kibana:7.17.28", Fallback: "", Save: "kibana.7.17.28.tar"},
}
for _, image := range images {
opts := []ImageOpt{
WithImageFallback(image.Fallback),
WithImageSave(filepath.Join(location, image.Save)),
WithImageForcePull(image.Force),
}
if err := m.Image(ctx, image.Name, opts...); err != nil {
logger.Error("❌ maker.Elastic: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
logger.Debug("❌ maker.Elastic: 获取镜像失败: %s, %v", image.Name, err)
return err
}
}
logger.Debug("✅ maker.Elastic: 获取所需镜像成功!!!")
logger.Info("✅ maker.Elastic: 构建 elastic(es) 依赖成功!!!")
return nil
}

View File

@@ -6,14 +6,14 @@ import (
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/model"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) EMQX(ctx context.Context) error {
var (
err error
location = filepath.Join(opt.Cfg.Make.Dir, "emqx")
location = filepath.Join(m.workdir, "dependency", "emqx")
)
logger.Info("☑️ maker.EMQX: 开始构建 emqx(mqtt) 依赖...")
@@ -32,6 +32,14 @@ func (m *maker) EMQX(ctx context.Context) error {
}
logger.Debug("✅ maker.EMQX: 写入 emqx.yaml 文件成功, dest = %s", filepath.Join(location, "emqx.yaml"))
logger.Debug("☑️ maker.EMQX: 开始获取所需镜像...")
var img = &model.Image{Name: "hub.yizhisec.com/external/emqx:5.1", Fallback: "", Save: "emqx.5.1.tar"}
if err = m.Image(ctx, img.Name, WithImageFallback(img.Fallback), WithImageSave(filepath.Join(location, img.Save))); err != nil {
logger.Debug("❌ maker.EMQX: 获取镜像失败: %s, %v", img.Name, err)
return err
}
logger.Debug("✅ maker.EMQX: 获取所需镜像成功!!!")
logger.Info("✅ maker.EMQX: 构建 emqx(mqtt) 依赖成功!!!")
return nil
}

View File

@@ -7,14 +7,14 @@ import (
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/model"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) Flannel(ctx context.Context, mode string) error {
var (
err error
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "flannel")
location = filepath.Join(m.workdir, "dependency", "flannel")
)
logger.Info("☑️ 开始构建 flannel 资源...")
@@ -34,6 +34,27 @@ func (m *maker) Flannel(ctx context.Context, mode string) error {
return err
}
var images = []*model.Image{
{Name: "ghcr.io/flannel-io/flannel:v0.27.4", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel:v0.27.4", Save: "flannel.tar"},
{Name: "ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Save: "flannel-cni-plugin.tar"},
}
logger.Debug("MakeFlannel: 开始获取镜像...")
for _, image := range images {
opts := []ImageOpt{
WithImageFallback(image.Fallback),
WithImageSave(filepath.Join(location, image.Save)),
WithImageForcePull(image.Force),
}
if err := m.Image(ctx, image.Name, opts...); err != nil {
logger.Error("❌ MakeFlannel: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
logger.Debug("❌ MakeFlannel: 获取镜像失败: %s, %v", image.Name, err)
return err
}
}
logger.Debug("MakeFlannel: 获取镜像成功!!!")
logger.Info("✅ 构建 flannel 成功!!!")
return nil

View File

@@ -6,12 +6,13 @@ import (
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/archiver"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) HSNet(ctx context.Context) error {
const (
service = `[Unit]
_service = `[Unit]
Description=hs-net Container Service
Documentation=https://docs.containerd.io
After=network.target containerd.service
@@ -19,6 +20,7 @@ After=network.target containerd.service
[Service]
# 启动前清理旧容器
# ExecStartPre=-/usr/local/bin/k0s ctr -n hs-net task kill hs-net
ExecStartPre=-/usr/local/bin/k0s ctr namespace create hs-net
ExecStartPre=-/usr/local/bin/k0s ctr -n hs-net container rm hs-net
# 拉取最新镜像(按需启用/注释)
@@ -28,9 +30,7 @@ ExecStartPre=-/usr/local/bin/k0s ctr -n hs-net container rm hs-net
ExecStart=/usr/local/bin/k0s ctr -n hs-net run \
--net-host \
--privileged \
--cgroup host \
--env LD_LIBRARY_PATH=/yizhisec/hs_net \
--env RUSTFLAGS="-C target-cpu=nehalem" \
--env RUST_BACKTRACE=1 \
--mount type=bind,src=/etc/localtime,dst=/etc/localtime,options=rbind:ro \
--mount type=bind,src=/etc/hosts,dst=/etc/hosts,options=rbind:ro \
@@ -43,11 +43,11 @@ ExecStart=/usr/local/bin/k0s ctr -n hs-net run \
--mount type=bind,src=/yizhisec/hs_net/conf,dst=/etc/hs_net,options=rbind:rw \
hub.yizhisec.com/hybridscope/hsnet:release_2.1.0-std hs-net
# --cgroup host \
# --env RUSTFLAGS="-C target-cpu=nehalem" \
# 重启策略
Restart=on-failure
Restart=always
RestartSec=5s
StartLimitInterval=60s
StartLimitBurst=5
# 资源限制(按需调整)
MemoryLimit=2G
@@ -64,19 +64,291 @@ ExecStopPost=/usr/local/bin/k0s ctr -n hs-net container rm hs-net
[Install]
WantedBy=multi-user.target`
_conf_out = `log:
level: info
controller:
protocol: https
registerHost: hs-gateway-register-controller
host: hs-gateway-controller
port: 443
tokenFilePath: /etc/yizhisec/token
registerRetry: 30
wg:
private_key: qPfOaNKrV11kzaGQiNQNyiu6wMQGUpIM+/xqboVAnng=
private_network: 246.0.0.1/8
listen_port: 23209
mtu: 1300
obf_key: 0
keep_alive: 61
tun_name: wg_tun
yosGuard:
host: __ip__
port: 7788
mqtt:
protocol: tls
host: mqtt.yizhisec.com
port: '443'
cert: /yizhisec/hs_net/conf/ssl/mqtt.client.crt
key: /yizhisec/hs_net/conf/ssl/mqtt.client.key
keep_alive: 60
paseto_key: TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=
resource_server: hs-gateway-controller
dns_cache:
Address: 127.0.0.1:9028
gatewayVersionFile: /etc/yizhisec/gateway_version.json
lastVersion: null
workDir: /yizhisec/hs_net/workspace
eth_names: []
tag: ''
cluster_mock: ''
openobserve_uri: ''
tcp_mode_disable: false
`
_conf_in = `{
"LogLevel": "info",
"LogFile": "/yizhisec/hs_net/workspace/log/wireguard",
"DnsVirtNetwork": null,
"DnsVirtNetworkV6": null,
"Foreground": false,
"WithPprof": false,
"DnsCache": {
"Address": "127.0.0.1:9028"
},
"log": {
"level": "info"
},
"yosGuard": {
"host": "__ip__",
"port": 7788
},
"controller": {
"protocol": "https",
"host": "hs-gateway-controller",
"registerHost": "hs-gateway-register-controller",
"port": 443,
"registerRetry": 30,
"tokenFilePath": "/etc/yizhisec/token"
},
"wg": {
"private_key": "qPfOaNKrV11kzaGQiNQNyiu6wMQGUpIM+/xqboVAnng=",
"private_network": "246.0.0.1/8",
"listen_port": 23209,
"mtu": 1380,
"obf_key": 0,
"keep_alive": 60,
"tun_name": "wg_tun"
},
"mqtt": {
"protocol": "tls",
"host": "mqtt.yizhisec.com",
"port": 443,
"cert": "/yizhisec/hs_net/conf/ssl/mqtt.client.crt",
"key": "/yizhisec/hs_net/conf/ssl/mqtt.client.key",
"keep_alive": 60
},
"paseto_key": "TtKVnSzEHO3jRv/GWg3f5k3H1OVfMnPZ1Ke9E6MSCXk=",
"resource_server": "hs-gateway-controller",
"gatewayVersionFile": "/etc/yizhisec/gateway_version.json",
"lastVersion": null,
"workDir": "/yizhisec/hs_net/workspace",
"dns_cache": {
"Address": "127.0.0.1:9028"
}
}
`
_url = "https://artifactory.yizhisec.com/artifactory/yizhisec-release/hs_net/release/2.1.0-std/package.tar.gz"
)
var (
err error
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "hs-net")
err error
workdir = filepath.Join(m.workdir, "dependency", "hs_net")
)
if err = os.MkdirAll(location, 0755); err != nil {
logger.Error("MakeHSNet: 创建目录失败s")
logger.Debug("MakeHSNet: 创建目录失败: %s", err.Error())
logger.Info("☑️ MakeHSNet: 开始构建 hs-net, workdir = %s", workdir)
if err = os.MkdirAll(workdir, 0755); err != nil {
logger.Debug("❌ MakeHSNet: 创建目录失败: %s", err.Error())
return err
}
logger.Fatal("MakeHSNet: 构建 hs-net 失败!!!(怎么完善,怎么完善,怎么完善???)")
if err = archiver.DownloadAndExtract(ctx, _url, workdir); err != nil {
logger.Debug("❌ MakeHSNet: 下载和解压失败: %s", err.Error())
return err
}
// mv workdir/package/server workdir/
// mv workdir/package/server_aes workdir/
if err = os.Rename(filepath.Join(workdir, "package", "server"), filepath.Join(workdir, "server")); err != nil {
logger.Debug("❌ MakeHSNet: 重命名文件失败: %s", err.Error())
return err
}
if err = os.Rename(filepath.Join(workdir, "package", "server_aes"), filepath.Join(workdir, "server_aes")); err != nil {
logger.Debug("❌ MakeHSNet: 重命名文件失败: %s", err.Error())
return err
}
// write down conf_out to server.conf
if err = os.WriteFile(filepath.Join(workdir, "server.conf"), []byte(_conf_out), 0644); err != nil {
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
return err
}
// write down conf_in to conf/server.conf
if err = os.MkdirAll(filepath.Join(workdir, "conf", "ssl"), 0755); err != nil {
logger.Debug("❌ MakeHSNet: 创建目录失败: %s", err.Error())
return err
}
if err = os.WriteFile(filepath.Join(workdir, "conf", "server.conf"), []byte(_conf_in), 0644); err != nil {
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
return err
}
// write resource.SSLMQTTClientCrt
if err = os.WriteFile(filepath.Join(workdir, "conf", "ssl", "mqtt.client.crt"), resource.SSLMQTTClientCrt, 0644); err != nil {
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
return err
}
if err = os.WriteFile(filepath.Join(workdir, "conf", "ssl", "mqtt.client.key"), resource.SSLMQTTClientKey, 0644); err != nil {
logger.Debug("❌ MakeHSNet: 写入配置文件失败: %s", err.Error())
return err
}
// mkdir workspace
if err = os.MkdirAll(filepath.Join(workdir, "workspace"), 0755); err != nil {
logger.Debug("❌ MakeHSNet: 创建目录失败: %s", err.Error())
return err
}
// new empty file lastVersion.txt
if err = os.WriteFile(filepath.Join(workdir, "lastVersion.txt"), []byte{}, 0644); err != nil {
logger.Debug("❌ MakeHSNet: 创建空文件失败: %s", err.Error())
return err
}
imgName := "hub.yizhisec.com/hybridscope/hsnet:release_2.1.0-std"
imgPath := filepath.Join(workdir, "hs-net.tar")
logger.Debug("☑️ MakeHSNet: 构建镜像 %s 到 %s", imgName, imgPath)
if err = m.Image(ctx, imgName, WithImageSave(imgPath), WithImageForcePull(true)); err != nil {
logger.Debug("❌ MakeHSNet: 构建镜像失败: %s", err.Error())
return err
}
logger.Debug("✅ MakeHSNet: 构建镜像 %s 到 %s 成功", imgName, imgPath)
// write hs-net.service
if err = os.WriteFile(filepath.Join(workdir, "hs-net.service"), []byte(_service), 0644); err != nil {
logger.Debug("❌ MakeHSNet: 写入服务文件失败: %s", err.Error())
return err
}
// upsert.sh script
const _upsert = `#!/bin/bash
set -e
echo "Starting hs-net deployment..."
# 1. Copy token file
echo "Copying token file..."
mkdir -p /etc/yizhisec
cp ../../configmap/token /etc/yizhisec/token
echo "Token file copied successfully"
# 2. Get local IP address
echo "Detecting local IP address..."
LocalIP=$(ip route get 1.1.1.1 | grep -oP 'src \K\S+')
if [ -z "$LocalIP" ]; then
echo "Error: Failed to detect local IP address"
exit 1
fi
echo "Local IP detected: $LocalIP"
# 3. Update /etc/hosts with required entries
echo "Updating /etc/hosts..."
for host in "hs-gateway-register-controller" "hs-gateway-controller" "mqtt.yizhisec.com"; do
if grep -q "$host" /etc/hosts; then
sed -i "/$host/d" /etc/hosts
fi
echo "$LocalIP $host" >> /etc/hosts
echo "Added: $LocalIP $host"
done
echo "/etc/hosts updated successfully"
# 4. Replace __ip__ in server.conf
echo "Updating server.conf..."
sed -i "s/__ip__/$LocalIP/g" server.conf
echo "server.conf updated successfully"
# 5. Replace __ip__ in conf/server.conf
echo "Updating conf/server.conf..."
sed -i "s/__ip__/$LocalIP/g" conf/server.conf
echo "conf/server.conf updated successfully"
# 6. Create /mnt/huge directory
echo "Creating /mnt/huge directory..."
mkdir -p /mnt/huge
echo "/mnt/huge directory created successfully"
# 7. Create workspace directories
echo "Creating workspace directories..."
mkdir -p /yizhisec/hs_net/workspace/log
mkdir -p /yizhisec/hs_net/conf
echo "Workspace directories created successfully"
# 8. Copy configuration files
echo "Copying configuration files..."
cp -r conf/* /yizhisec/hs_net/conf/
echo "Configuration files copied successfully"
# 9. Copy binaries based on CPU AVX support
echo "Detecting CPU AVX support..."
if grep -q avx /proc/cpuinfo; then
echo "AVX support detected, using server_aes"
cp server_aes /yizhisec/hs_net/server
chmod +x /yizhisec/hs_net/server
echo "server_aes copied to /yizhisec/hs_net/server with execute permission"
else
echo "AVX not supported, using server"
cp server /yizhisec/hs_net/server
chmod +x /yizhisec/hs_net/server
echo "server copied to /yizhisec/hs_net/server with execute permission"
fi
echo "Binary copied successfully"
# 10. Copy lastVersion.txt
echo "Copying lastVersion.txt..."
cp lastVersion.txt /yizhisec/hs_net/
echo "lastVersion.txt copied successfully"
# 11. Load container image
echo "Loading hs-net container image..."
k0s ctr -n hs-net images import hs-net.tar
echo "Container image loaded successfully"
# 12. Install and enable systemd service
echo "Installing hs-net systemd service..."
cp hs-net.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable hs-net.service
echo "hs-net service installed and enabled"
# 13. Start the service
echo "Starting hs-net service..."
systemctl restart hs-net.service
echo "hs-net service started successfully"
echo "hs-net deployment completed successfully!"
echo "You can check the service status with: systemctl status hs-net.service"
`
// Write upsert.sh
logger.Debug("☑️ MakeHSNet: 写入 upsert.sh 脚本...")
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), []byte(_upsert), 0755); err != nil {
logger.Debug("❌ MakeHSNet: 写入 upsert.sh 失败: %s", err.Error())
return err
}
logger.Debug("✅ MakeHSNet: 写入 upsert.sh 成功")
logger.Info("✅ MakeHSNet: 构建 hs-net 成功, workdir = %s", workdir)
return nil
}

View File

@@ -8,7 +8,7 @@ import (
"gitea.loveuer.com/yizhisec/pkg3/logger"
"github.com/samber/lo"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/model"
)
type imageOpt struct {
@@ -32,9 +32,11 @@ func WithImageSave(filename string) ImageOpt {
}
}
func WithImageForcePull() ImageOpt {
func WithImageForcePull(force bool) ImageOpt {
return func(o *imageOpt) {
o.ForcePull = true
if force {
o.ForcePull = true
}
}
}
@@ -107,53 +109,11 @@ SAVE:
}
func (m *maker) Images(ctx context.Context) error {
type Images struct {
Name string
Fallback string
Save string
Force bool
}
var images = []*Images{
{Name: "quay.io/k0sproject/apiserver-network-proxy-agent:v0.32.0", Fallback: "hub.yizhisec.com/external/apiserver-network-proxy-agent:v0.32.0", Save: "k0s.apiserver-network-proxy-agent.tar"},
{Name: "quay.io/k0sproject/cni-node:1.7.1-k0s.0", Fallback: "", Save: "k0s.cni-node.tar"},
{Name: "quay.io/k0sproject/coredns:1.12.2", Fallback: "", Save: "k0s.coredns.tar"},
{Name: "quay.io/k0sproject/kube-proxy:v1.33.4", Fallback: "", Save: "k0s.kube-proxy.tar"},
{Name: "quay.io/k0sproject/kube-router:v2.5.0-iptables1.8.11-0", Fallback: "", Save: "k0s.kube-router.tar"},
{Name: "quay.io/k0sproject/metrics-server:v0.7.2-0", Fallback: "", Save: "k0s.metrics-server.tar"},
{Name: "quay.io/k0sproject/pause:3.10.1", Fallback: "", Save: "k0s.pause.tar"},
{Name: "ghcr.io/flannel-io/flannel:v0.27.4", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel:v0.27.4", Save: "flannel.tar"},
{Name: "ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Fallback: "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/flannel-io/flannel-cni-plugin:v1.8.0-flannel1", Save: "flannel-cni-plugin.tar"},
{Name: "docker.io/longhornio/longhorn-engine:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-engine:v1.10.0", Save: "longhorn.longhorn-engine.tar"},
{Name: "docker.io/longhornio/longhorn-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-manager:v1.10.0", Save: "longhorn.longhorn-manager.tar"},
{Name: "docker.io/longhornio/longhorn-instance-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-instance-manager:v1.10.0", Save: "longhorn.longhorn-instance-manager.tar"},
{Name: "docker.io/longhornio/longhorn-share-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-share-manager:v1.10.0", Save: "longhorn.longhorn-share-manager.tar"},
{Name: "docker.io/longhornio/longhorn-ui:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-ui:v1.10.0", Save: "longhorn.longhorn-ui.tar"},
{Name: "docker.io/longhornio/csi-snapshotter:v8.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-snapshotter:v8.3.0-20250826", Save: "longhorn.csi-snapshotter.tar"},
{Name: "docker.io/longhornio/csi-resizer:v1.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-resizer:v1.14.0-20250826", Save: "longhorn.csi-resizer.tar"},
{Name: "docker.io/longhornio/csi-provisioner:v5.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-provisioner:v5.3.0-20250826", Save: "longhorn.csi-provisioner.tar"},
{Name: "docker.io/longhornio/livenessprobe:v2.16.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/livenessprobe:v2.16.0-20250826", Save: "longhorn.livenessprobe.tar"},
{Name: "docker.io/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Save: "longhorn.csi-node-driver-registrar.tar"},
{Name: "docker.io/longhornio/csi-attacher:v4.9.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-attacher:v4.9.0-20250826", Save: "longhorn.csi-attacher.tar"},
var images = []*model.Image{
{Name: "hub.yizhisec.com/external/alpine:3.22.2", Fallback: "", Save: "alpine.tar", Force: true},
{Name: "hub.yizhisec.com/external/nginx:1.29.1-alpine3.22", Fallback: "", Save: "nginx.1.29.1-alpine3.22.tar"},
{Name: "docker.io/bitpoke/mysql-operator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator:v0.6.3", Save: "bitpoke.mysql-operator.v0.6.3.tar"},
{Name: "docker.io/bitpoke/mysql-operator-orchestrator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-orchestrator:v0.6.3", Save: "bitpoke.mysql-operator-orchestrator.v0.6.3.tar"},
{Name: "docker.io/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Save: "bitpoke.mysql-operator-sidecar-8.0.v0.6.3.tar"},
{Name: "docker.io/library/percona:8.0.28-20", Fallback: "docker-mirror.yizhisec.com/library/percona:8.0.28-20", Save: "percona.8.0.28-20.tar"},
{Name: "docker.io/prom/mysqld-exporter:v0.13.0", Fallback: "docker-mirror.yizhisec.com/prom/mysqld-exporter:v0.13.0", Save: "prom.mysqld-exporter.v0.13.0.tar"},
{Name: "docker.io/bitnami/redis:8.2.2", Fallback: "hub.yizhisec.com/external/bitnami/redis:8.2.2", Save: "bitnami.redis.8.2.2.tar"},
{Name: "hub.yizhisec.com/external/elasticsearch:7.17.28", Fallback: "", Save: "elasticsearch.7.17.28.tar"},
{Name: "hub.yizhisec.com/external/kibana:7.17.28", Fallback: "", Save: "kibana.7.17.28.tar"},
{Name: "hub.yizhisec.com/external/emqx:5.1", Fallback: "", Save: "emqx.5.1.tar"},
{Name: "hub.yizhisec.com/build/hybirdscope/front/admin:latest", Fallback: "", Save: "app.front.admin.tar", Force: true},
{Name: "hub.yizhisec.com/hybridscope/v2/front-user:latest", Fallback: "", Save: "app.front.user.tar", Force: true},
{Name: "hub.yizhisec.com/hybridscope/user_management:latest", Fallback: "", Save: "app.user.tar", Force: true},
{Name: "hub.yizhisec.com/hybridscope/gateway_controller:latest", Fallback: "", Save: "app.gateway.tar", Force: true},
{Name: "hub.yizhisec.com/hybridscope/client_server:latest", Fallback: "", Save: "app.client.tar", Force: true},
@@ -163,7 +123,7 @@ func (m *maker) Images(ctx context.Context) error {
}
for _, image := range images {
image.Save = filepath.Join(opt.Cfg.Make.Dir, "dependency", "image", image.Save)
image.Save = filepath.Join(m.workdir, "dependency", "image", image.Save)
}
logger.Info("☑️ 开始获取镜像(s)...")
@@ -173,10 +133,7 @@ func (m *maker) Images(ctx context.Context) error {
opts := []ImageOpt{
WithImageFallback(image.Fallback),
WithImageSave(image.Save),
}
if image.Force {
opts = append(opts, WithImageForcePull())
WithImageForcePull(image.Force),
}
logger.Info("☑️ 获取镜像: %s", image.Name)

View File

@@ -8,7 +8,7 @@ import (
func TestImage(t *testing.T) {
logger.SetLogLevel(logger.LogLevelDebug)
m := NewMaker()
m := NewMaker("./x-data")
m.Image(t.Context(), "docker.io/nginx:1.29.3-alpine3.22",
WithImageFallback("swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.29.3-alpine3.22"),
WithImageSave("/root/nginx.tar"),

View File

@@ -6,14 +6,13 @@ import (
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) LessDNS(ctx context.Context) error {
var (
err error
location = filepath.Join(opt.Cfg.Make.Dir, "dependency", "less-dns")
location = filepath.Join(m.workdir, "dependency", "less-dns")
)
logger.Info("☑️ maker.LessDNS: 开始构建 less-dns...")

View File

@@ -7,8 +7,8 @@ import (
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/downloader"
"yizhisec.com/hsv2/forge/pkg/model"
)
func (m *maker) Longhorn(ctx context.Context, replica int) error {
@@ -24,18 +24,18 @@ persistence:
)
var (
err error
chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/longhorn-1.10.0.tgz"
longhornDir = filepath.Join(opt.Cfg.Make.Dir, "dependency", "longhorn")
chartFile = filepath.Join(longhornDir, "longhorn-1.10.0.tgz")
valuesFile = filepath.Join(longhornDir, "values.yaml")
err error
chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/longhorn-1.10.0.tgz"
location = filepath.Join(m.workdir, "dependency", "longhorn")
chartFile = filepath.Join(location, "longhorn-1.10.0.tgz")
valuesFile = filepath.Join(location, "values.yaml")
)
logger.Info("☑️ 开始准备 Longhorn 资源...")
logger.Debug("下载地址: %s", chartURL)
logger.Debug("目标目录: %s", longhornDir)
logger.Debug("目标目录: %s", location)
if err = os.MkdirAll(filepath.Join(opt.Cfg.Make.Dir, "dependency", "longhorn"), 0755); err != nil {
if err = os.MkdirAll(filepath.Join(m.workdir, "dependency", "longhorn"), 0755); err != nil {
return err
}
@@ -58,6 +58,36 @@ persistence:
return err
}
var images = []*model.Image{
{Name: "docker.io/longhornio/longhorn-engine:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-engine:v1.10.0", Save: "longhorn.longhorn-engine.tar"},
{Name: "docker.io/longhornio/longhorn-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-manager:v1.10.0", Save: "longhorn.longhorn-manager.tar"},
{Name: "docker.io/longhornio/longhorn-instance-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-instance-manager:v1.10.0", Save: "longhorn.longhorn-instance-manager.tar"},
{Name: "docker.io/longhornio/longhorn-share-manager:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-share-manager:v1.10.0", Save: "longhorn.longhorn-share-manager.tar"},
{Name: "docker.io/longhornio/longhorn-ui:v1.10.0", Fallback: "docker-mirror.yizhisec.com/longhornio/longhorn-ui:v1.10.0", Save: "longhorn.longhorn-ui.tar"},
{Name: "docker.io/longhornio/csi-snapshotter:v8.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-snapshotter:v8.3.0-20250826", Save: "longhorn.csi-snapshotter.tar"},
{Name: "docker.io/longhornio/csi-resizer:v1.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-resizer:v1.14.0-20250826", Save: "longhorn.csi-resizer.tar"},
{Name: "docker.io/longhornio/csi-provisioner:v5.3.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-provisioner:v5.3.0-20250826", Save: "longhorn.csi-provisioner.tar"},
{Name: "docker.io/longhornio/livenessprobe:v2.16.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/livenessprobe:v2.16.0-20250826", Save: "longhorn.livenessprobe.tar"},
{Name: "docker.io/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-node-driver-registrar:v2.14.0-20250826", Save: "longhorn.csi-node-driver-registrar.tar"},
{Name: "docker.io/longhornio/csi-attacher:v4.9.0-20250826", Fallback: "docker-mirror.yizhisec.com/longhornio/csi-attacher:v4.9.0-20250826", Save: "longhorn.csi-attacher.tar"},
}
logger.Debug("MakeLonghorn: 开始获取镜像...")
for _, image := range images {
opts := []ImageOpt{
WithImageFallback(image.Fallback),
WithImageSave(filepath.Join(location, image.Save)),
WithImageForcePull(image.Force),
}
if err := m.Image(ctx, image.Name, opts...); err != nil {
logger.Error("❌ MakeLonghorn: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
logger.Debug("❌ MakeLonghorn: 获取镜像失败: %s, %v", image.Name, err)
return err
}
}
logger.Debug("MakeLonghorn: 获取镜像成功!!!")
logger.Info("✅ 成功创建 longhorn 资源!!!")
return nil

View File

@@ -1,7 +1,9 @@
package maker
type maker struct{}
func NewMaker() *maker {
return &maker{}
type maker struct {
workdir string
}
func NewMaker(workdir string) *maker {
return &maker{workdir: workdir}
}

View File

@@ -0,0 +1,62 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/model"
"yizhisec.com/hsv2/forge/pkg/resource"
)
// todo, remake minio-init image
func (m *maker) Minio(ctx context.Context, storage string) error {
var (
err error
location = filepath.Join(m.workdir, "dependency", "minio")
)
logger.Info("☑️ maker.Minio: 开始构建 minio 依赖, workdir = %s", location)
logger.Debug("☑️ maker.Minio: 构建工作目录, workdir = %s", location)
if err = os.MkdirAll(location, 0755); err != nil {
logger.Debug("❌ maker.Minio: 创建工作目录失败, workdir = %s, err = %v", location, err)
return err
}
logger.Debug("✅ maker.Minio: 创建工作目录成功, workdir = %s", location)
filename := filepath.Join(location, "minio.yaml")
logger.Debug("☑️ maker.Minio: 准备资源文件, filename = %s, storage = %s", filename, storage)
bs := []byte(fmt.Sprintf(resource.YAMLMinIO, storage))
if err = os.WriteFile(filename, bs, 0644); err != nil {
logger.Debug("❌ maker.Minio: 写入资源文件失败, filename = %s, err = %v", filename, err)
return err
}
logger.Debug("✅ maker.Minio: 准备资源文件成功, filename = %s", filename)
logger.Debug("☑️ maker.Minio: 开始获取所需镜像...")
var images = []*model.Image{
{Name: "hub.yizhisec.com/hybridscope/v3/minio-init:latest", Fallback: "", Save: "dep.minio-init.tar"},
{Name: "hub.yizhisec.com/external/minio:RELEASE.2025-03-12T18-04-18Z", Fallback: "", Save: "dep.minio.tar"},
}
for _, image := range images {
opts := []ImageOpt{
WithImageFallback(image.Fallback),
WithImageSave(filepath.Join(location, image.Save)),
WithImageForcePull(image.Force),
}
if err := m.Image(ctx, image.Name, opts...); err != nil {
logger.Error("❌ maker.Minio: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
logger.Debug("❌ maker.Minio: 获取镜像失败: %s, %v", image.Name, err)
return err
}
}
logger.Debug("✅ maker.Minio: 获取所需镜像成功!!!")
logger.Info("✅ maker.Minio: 构建 minio 依赖成功, workdir = %s", location)
return nil
}

View File

@@ -6,11 +6,11 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/downloader"
"yizhisec.com/hsv2/forge/pkg/model"
)
type MysqlOpt func(*mysqlOpt)
@@ -30,7 +30,7 @@ func WithMySQLReplica(replica int) MysqlOpt {
func WithMySQLStorage(storage string) MysqlOpt {
return func(o *mysqlOpt) {
// validate Kubernetes storage size string (e.g., "50Gi", "100Mi")
if matched, _ := regexp.MatchString(`^\d+(\.\d+)?[EPTGMK]i?$`, storage); matched {
if opt.StorageSizeReg.MatchString(storage) {
o.Storage = storage
}
}
@@ -74,14 +74,14 @@ spec:
resources:
requests:
storage: %s
podSpec:
resources:
requests:
cpu: "250m"
memory: "250Mi"
limits:
cpu: "2"
memory: "4Gi"
podSpec:
resources:
requests:
cpu: "250m"
memory: "250Mi"
limits:
cpu: "2"
memory: "4Gi"
# affinity:
# podAntiAffinity:
# requiredDuringSchedulingIgnoredDuringExecution: # 强制规则
@@ -95,7 +95,6 @@ spec:
# values: ["mysql-cluster"]
# topologyKey: "kubernetes.io/hostname" # 确保不同节点
---
apiVersion: mysql.presslabs.org/v1alpha1
kind: MysqlDatabase
metadata:
@@ -118,9 +117,9 @@ spec:
err error
chartURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv3/charts/mysql-operator-0.6.3.tgz"
mysqlDir = filepath.Join(opt.Cfg.Make.Dir, "dependency", "mysql")
chartFile = filepath.Join(mysqlDir, "mysql-operator-0.6.3.tgz")
clusterFile = filepath.Join(mysqlDir, "cluster.yaml")
location = filepath.Join(m.workdir, "dependency", "mysql")
chartFile = filepath.Join(location, "mysql-operator-0.6.3.tgz")
clusterFile = filepath.Join(location, "cluster.yaml")
)
for _, fn := range opts {
@@ -129,13 +128,13 @@ spec:
logger.Info("☑️ 开始构建 MySQL 资源...")
if err = os.MkdirAll(mysqlDir, 0755); err != nil {
if err = os.MkdirAll(location, 0755); err != nil {
return err
}
o.Password = base64.StdEncoding.EncodeToString([]byte(o.Password))
logger.Debug("正在下载 MySQL operator chart..., url = %s, dir = %s", chartURL, mysqlDir)
logger.Debug("正在下载 MySQL operator chart..., url = %s, dir = %s", chartURL, location)
if err = downloader.Download(
ctx,
chartURL,
@@ -158,6 +157,31 @@ spec:
logger.Debug("✅ 成功创建 database.yaml")
logger.Debug("☑️ MakeMysql: 开始获取所需镜像...")
var images = []*model.Image{
{Name: "docker.io/bitpoke/mysql-operator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator:v0.6.3", Save: "bitpoke.mysql-operator.v0.6.3.tar"},
{Name: "docker.io/bitpoke/mysql-operator-orchestrator:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-orchestrator:v0.6.3", Save: "bitpoke.mysql-operator-orchestrator.v0.6.3.tar"},
{Name: "docker.io/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Fallback: "docker-mirror.yizhisec.com/bitpoke/mysql-operator-sidecar-8.0:v0.6.3", Save: "bitpoke.mysql-operator-sidecar-8.0.v0.6.3.tar"},
{Name: "docker.io/library/percona:8.0.28-20", Fallback: "docker-mirror.yizhisec.com/library/percona:8.0.28-20", Save: "percona.8.0.28-20.tar"},
{Name: "docker.io/prom/mysqld-exporter:v0.13.0", Fallback: "docker-mirror.yizhisec.com/prom/mysqld-exporter:v0.13.0", Save: "prom.mysqld-exporter.v0.13.0.tar"},
}
for _, image := range images {
opts := []ImageOpt{
WithImageFallback(image.Fallback),
WithImageSave(filepath.Join(location, image.Save)),
WithImageForcePull(image.Force),
}
if err := m.Image(ctx, image.Name, opts...); err != nil {
logger.Error("❌ MakeMysql: 获取镜像失败: %s, 可以手动获取后重试", image.Name)
logger.Debug("❌ MakeMysql: 获取镜像失败: %s, %v", image.Name, err)
return err
}
}
logger.Debug("✅ MakeMysql: 获取镜像成功!!!")
logger.Info("✅ MySQL 资源构建成功!!!")
return nil

View File

@@ -0,0 +1,147 @@
package maker
import (
"context"
"encoding/json"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/downloader"
"yizhisec.com/hsv2/forge/pkg/model"
)
func (m *maker) Proxy(ctx context.Context) error {
const (
binURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/caddy"
systemdSvc = `[Unit]
Description=YiZhiSec Caddy Reverse Proxy
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/caddy run --config /etc/caddy/caddy.json
StandardOutput=journal
StandardError=journal
Nice=-20
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target`
)
location := filepath.Join(m.workdir, "dependency", "proxy")
logger.Info("☑️ maker.Proxy: 开始构建 caddy 反向代理...")
logger.Debug("☑️ maker.Proxy: 创建目录 %s", location)
if err := os.MkdirAll(location, 0755); err != nil {
logger.Debug("❌ maker.Proxy: 创建目录失败: %v", err)
return err
}
logger.Debug("✅ maker.Proxy: 创建目录 %s 成功", location)
logger.Debug("☑️ maker.Proxy: 下载 caddy 二进制..., url = %s, dest = %s", binURL, filepath.Join(location, "caddy"))
if err := downloader.Download(
ctx,
binURL,
filepath.Join(location, "caddy"),
downloader.WithInsecureSkipVerify(),
downloader.WithFileMode(0755),
); err != nil {
logger.Debug("❌ maker.Proxy: 下载 caddy 失败, url = %s, err = %v", binURL, err)
return err
}
logger.Debug("✅ maker.Proxy: 下载 caddy 成功, url = %s", binURL)
logger.Debug("☑️ maker.Proxy: 写入 caddy.json 文件..., dest = %s", filepath.Join(location, "caddy.json"))
caddyConfig := model.CaddyConfig{
"apps": &model.CaddyApp{
Layer4: &model.CaddyLayer4{
Servers: map[string]*model.CaddyServer{
"proxy_8443": {
Listen: []string{":8443"},
Routes: []*model.CaddyRoute{
{
Handle: []*model.CaddyHandle{
{
Handler: "proxy",
Upstreams: []*model.CaddyUpstream{
{Dial: []string{"__ip_1__:32443"}},
{Dial: []string{"__ip_2__:32443"}},
},
HealthChecks: &model.CaddyHealthCheck{
Active: &model.CaddyActive{
Interval: "10s",
Timeout: "2s",
Port: 32443,
},
Passive: &model.CaddyPassive{
FailDuration: "30s",
MaxFails: 2,
},
},
LoadBalancing: &model.CaddyLoadBalancing{
Selection: &model.CaddySelection{
Policy: "round_robin",
},
},
},
},
},
},
},
"proxy_443": {
Listen: []string{":443"},
Routes: []*model.CaddyRoute{
{
Handle: []*model.CaddyHandle{
{
Handler: "proxy",
Upstreams: []*model.CaddyUpstream{
{Dial: []string{"__ip_1__:31443"}},
{Dial: []string{"__ip_2__:31443"}},
},
HealthChecks: &model.CaddyHealthCheck{
Active: &model.CaddyActive{
Interval: "10s",
Timeout: "2s",
Port: 31443,
},
Passive: &model.CaddyPassive{
FailDuration: "30s",
MaxFails: 2,
},
},
LoadBalancing: &model.CaddyLoadBalancing{
Selection: &model.CaddySelection{
Policy: "round_robin",
},
},
},
},
},
},
},
},
},
},
}
bs, _ := json.MarshalIndent(caddyConfig, "", " ")
if err := os.WriteFile(filepath.Join(location, "caddy.json"), []byte(bs), 0644); err != nil {
logger.Debug("❌ maker.Proxy: 写入 Caddyfile 失败, dest = %s, err = %v", filepath.Join(location, "caddy.json"), err)
return err
}
logger.Debug("✅ maker.Proxy: 写入 Caddyfile 文件成功, dest = %s", filepath.Join(location, "caddy.json"))
logger.Debug("☑️ maker.Proxy: 写入 caddy.service 文件..., dest = %s", filepath.Join(location, "caddy.service"))
if err := os.WriteFile(filepath.Join(location, "caddy.service"), []byte(systemdSvc), 0644); err != nil {
logger.Debug("❌ maker.Proxy: 写入 caddy.service 失败, dest = %s, err = %v", filepath.Join(location, "caddy.service"), err)
return err
}
logger.Debug("✅ maker.Proxy: 写入 caddy.service 文件成功, dest = %s", filepath.Join(location, "caddy.service"))
logger.Info("✅ maker.Proxy: 构建 caddy 反向代理成功!!!")
return nil
}

View File

@@ -5,11 +5,11 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/downloader"
"yizhisec.com/hsv2/forge/pkg/model"
)
type RedisOpt func(*redisOpt)
@@ -38,7 +38,7 @@ func WithRedisPassword(password string) RedisOpt {
func WithRedisStorage(storage string) RedisOpt {
return func(o *redisOpt) {
if matched, _ := regexp.MatchString(`^\d+(\.\d+)?[EPTGMK]i?$`, storage); matched {
if opt.StorageSizeReg.MatchString(storage) {
o.Storage = storage
}
}
@@ -91,18 +91,18 @@ metrics:
fn(o)
}
redisDir := filepath.Join(opt.Cfg.Make.Dir, "dependency", "redis")
chartFile := filepath.Join(redisDir, chartFilename)
valuesFile := filepath.Join(redisDir, "values.yaml")
location := filepath.Join(m.workdir, "dependency", "redis")
chartFile := filepath.Join(location, chartFilename)
valuesFile := filepath.Join(location, "values.yaml")
logger.Info("☑️ maker.Redis: 开始构建 Redis 资源...")
logger.Debug("☑️ maker.Redis: 开始构建 Redis 资源..., opt = %#v", o)
logger.Debug("☑️ maker.Redis: 创建目录 %s", redisDir)
if err := os.MkdirAll(redisDir, 0755); err != nil {
logger.Debug("☑️ maker.Redis: 创建目录 %s", location)
if err := os.MkdirAll(location, 0755); err != nil {
logger.Debug("❌ maker.Redis: 创建目录失败: %v", err)
return err
}
logger.Debug("✅ maker.Redis: 创建目录 %s 成功", redisDir)
logger.Debug("✅ maker.Redis: 创建目录 %s 成功", location)
logger.Debug("☑️ maker.Redis: 下载 Redis chart..., url = %s, dest = %s", chartURL, chartFile)
if err := downloader.Download(
@@ -124,6 +124,14 @@ metrics:
}
logger.Debug("✅ maker.Redis: 写入 values.yaml 文件成功, dest = %s", valuesFile)
logger.Debug("☑️ maker.Redis: 开始获取镜像...")
img := &model.Image{Name: "docker.io/bitnami/redis:8.2.2", Fallback: "hub.yizhisec.com/external/bitnami/redis:8.2.2", Save: "bitnami.redis.8.2.2.tar"}
if err := m.Image(ctx, img.Name, WithImageFallback(img.Fallback), WithImageSave(filepath.Join(location, img.Save))); err != nil {
logger.Debug("❌ maker.Redis: 获取镜像失败: %s, %v", img.Name, err)
return err
}
logger.Debug("✅ maker.Redis: 获取镜像成功!!!")
logger.Info("✅ maker.Redis: 构建 Redis 资源成功!!!")
return nil

View File

@@ -0,0 +1,73 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/resource"
)
func (m *maker) Registry(ctx context.Context, storage string) error {
const (
_registryToml = `
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."10.96.123.45:80"]
endpoint = ["http://10.96.123.45:80"]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."10.96.123.45:80".tls]
insecure_skip_verify = true
[plugins."io.containerd.grpc.v1.cri".registry.configs."10.96.123.45:80".auth]`
)
var (
err error
location = filepath.Join(m.workdir, "dependency", "registry")
imgName = "docker.io/library/registry:2.8.3"
uiImgName = "docker.io/quiq/registry-ui:0.11.0"
imgFallback = "docker-mirror.yizhisec.com/library/registry:2.8.3"
uiImgFallback = "docker-mirror.yizhisec.com/quiq/registry-ui:0.11.0"
)
logger.Info("☑️ 开始创建依赖: registry...")
logger.Debug("☑️ 创建 dir: %s ...", location)
if err = os.MkdirAll(location, 0755); err != nil {
logger.Debug("❌ 创建 dir %s 失败: %v", location, err)
return err
}
yamlFile := filepath.Join(location, "registry.yaml")
content := fmt.Sprintf(resource.YAMLRegistry, storage)
logger.Debug("写入 yaml 文件: %s ...", yamlFile)
if os.WriteFile(yamlFile, []byte(content), 0644); err != nil {
logger.Debug("❌ 写入 yaml 文件 %s 失败: %v", yamlFile, err)
return err
}
logger.Debug("开始准备镜像...")
imgFile := filepath.Join(location, "registry.tar")
if err = m.Image(ctx, imgName, WithImageFallback(imgFallback), WithImageSave(imgFile)); err != nil {
logger.Debug("❌ 准备镜像 %s 失败: %v", imgName, err)
return err
}
imgFile = filepath.Join(location, "registry-ui.tar")
if err = m.Image(ctx, uiImgName, WithImageFallback(uiImgFallback), WithImageSave(imgFile)); err != nil {
logger.Debug("❌ 准备镜像 %s 失败: %v", uiImgName, err)
return err
}
logger.Debug("写入 registry.toml 文件: %s ...", filepath.Join(location, "registry.toml"))
if os.WriteFile(filepath.Join(location, "registry.toml"), []byte(_registryToml), 0644); err != nil {
logger.Debug("❌ 写入 registry.toml 文件 %s 失败: %v", filepath.Join(location, "registry.toml"), err)
return err
}
logger.Info("✅ 创建依赖成功: registry!!!")
return nil
}

View File

@@ -0,0 +1,212 @@
package maker
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/model"
"yizhisec.com/hsv2/forge/pkg/resource"
)
type SeafileOpt func(*seafileOpt)
type seafileOpt struct {
DBHost string
DBPassword string
AdminEmail string
AdminPassword string
ServerHostname string
Storage string
}
func WithSeafileStorage(storage string) SeafileOpt {
return func(o *seafileOpt) {
if opt.StorageSizeReg.MatchString(storage) {
o.Storage = storage
}
}
}
func WithSeafileDBHost(host string) SeafileOpt {
return func(o *seafileOpt) {
if o.DBHost == "" {
o.DBHost = host
}
}
}
func WithSeafileDBPassword(password string) SeafileOpt {
return func(o *seafileOpt) {
if o.DBPassword == "" {
o.DBPassword = password
}
}
}
func WithSeafileAdminEmail(email string) SeafileOpt {
return func(o *seafileOpt) {
if opt.EmailReg.MatchString(email) {
o.AdminEmail = email
}
}
}
func WithSeafileAdminPassword(password string) SeafileOpt {
return func(o *seafileOpt) {
if o.AdminPassword == "" {
o.AdminPassword = password
}
}
}
func WithSeafileHostname(hostname string) SeafileOpt {
return func(o *seafileOpt) {
if o.ServerHostname == "" {
o.ServerHostname = hostname
}
}
}
func (m *maker) Seafile(ctx context.Context, opts ...SeafileOpt) error {
const (
_config = `
ControllerServer:
UserManagement: user-service.hsv2:9013
Database:
Mysql:
Address: %s:3306
DBName: backup_server
Password: %s
SeafileDBName: seafile_db
UserName: root
Log:
Dir: ./log
Level: 1
Name: hs_backup_seafile
SeafileServer:
Admin: %s
AdminPassword: %s
BackupDir: /seafile/backup_data
Host: seafile-service
Port: 80
StorageDir: /seafile/storage
Sentry:
DSN: https://fd7149f063c211eda2b50242ac15001c@sentry.yizhisec.com:13443/7
TracesSampleRate: 1
Web:
Host: 0.0.0.0
Mode: release
Port: 9027
WorkDir: /yizhisec/hs_backup_seafile/workspace
YosGuard:
Host: 172.17.0.1
Port: 7788`
_upsert = `#!/bin/bash
kubectl create configmap config-backup-seafile --namespace seafile --from-file=config.yml=./config.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap nginx-seafile --namespace hsv2 --from-file=seafile.conf=./nginx.conf --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deployment.yaml
kubectl rollout restart deployment backup-seafile-deployment -n seafile`
)
var (
err error
o = &seafileOpt{
DBHost: "mysql-cluster-mysql-master.db-mysql",
DBPassword: "L0hMysql.",
AdminEmail: "admin@yizhisec.com",
AdminPassword: "asecret",
ServerHostname: "cloud.hybridscope.com",
Storage: "50Gi",
}
workdir = filepath.Join(m.workdir, "dependency", "seafile")
)
for _, fn := range opts {
fn(o)
}
logger.Info("☑️ maker.Seafile: 开始构建 seafile 依赖, dir = %s", workdir)
// 1. 准备工作目录
logger.Debug("☑️ make.Seafile: 准备工作目录: %s", workdir)
if err = os.MkdirAll(workdir, 0755); err != nil {
logger.Error("❌ make.Seafile: 准备工作目录: %s 失败, err = %v", workdir, err)
return err
}
logger.Debug("✅ make.Seafile: 准备工作目录: %s 成功", workdir)
// 2. seafile yaml
logger.Debug("☑️ make.Seafile: 准备 seafile yaml")
bs := []byte(fmt.Sprintf(resource.YAMLSeafile, o.DBHost, o.DBPassword, o.AdminEmail, o.AdminPassword, o.ServerHostname, o.Storage))
if err = os.WriteFile(filepath.Join(workdir, "seafile.yaml"), bs, 0644); err != nil {
logger.Error("❌ make.Seafile: 准备 seafile yaml: %s 失败, err = %v", filepath.Join(workdir, "seafile.yaml"), err)
return err
}
logger.Debug("✅ make.Seafile: 准备 seafile yaml 成功")
// 3. backup-seafile deployment
logger.Debug("☑️ make.Seafile: 准备 backup-seafile deployment")
bs = []byte(resource.YAMLBackupSeafile)
if err = os.WriteFile(filepath.Join(workdir, "deployment.yaml"), bs, 0644); err != nil {
logger.Error("❌ make.Seafile: 准备 backup-seafile deployment: %s 失败, err = %v", filepath.Join(workdir, "deployment.yaml"), err)
return err
}
logger.Debug("✅ make.Seafile: 准备 backup-seafile deployment 成功")
// 4. config.yml
logger.Debug("☑️ make.Seafile: 准备 config.yml")
bs = []byte(fmt.Sprintf(_config, o.DBHost, o.DBPassword, o.AdminEmail, o.AdminPassword))
if err = os.WriteFile(filepath.Join(workdir, "config.yml"), bs, 0644); err != nil {
logger.Error("❌ make.Seafile: 准备 config.yml: %s 失败, err = %v", filepath.Join(workdir, "config.yml"), err)
return err
}
logger.Debug("✅ make.Seafile: 准备 config.yml 成功")
// 5. seafile.conf
logger.Debug("☑️ make.Seafile: 准备 seafile.conf")
bs = resource.NGINXSeafile
if err = os.WriteFile(filepath.Join(workdir, "seafile.conf"), bs, 0644); err != nil {
logger.Error("❌ make.Seafile: 准备 seafile.conf: %s 失败, err = %v", filepath.Join(workdir, "seafile.conf"), err)
return err
}
logger.Debug("✅ make.Seafile: 准备 seafile.conf 成功")
// 6. upsert.sh
logger.Debug("☑️ make.Seafile: 准备 upsert.sh")
bs = []byte(_upsert)
if err = os.WriteFile(filepath.Join(workdir, "upsert.sh"), bs, 0755); err != nil {
logger.Error("❌ make.Seafile: 准备 upsert.sh: %s 失败, err = %v", filepath.Join(workdir, "upsert.sh"), err)
return err
}
logger.Debug("✅ make.Seafile: 准备 upsert.sh 成功")
// 7. prepare images
logger.Debug("☑️ make.Seafile: 准备 images")
imgDir := filepath.Join(m.workdir, "dependency", "image")
if err = os.MkdirAll(imgDir, 0755); err != nil {
logger.Error("❌ make.Seafile: 准备 images 目录: %s 失败, err = %v", imgDir, err)
return err
}
var images = []*model.Image{
{Name: "hub.yizhisec.com/hybridscope/hs_backup_seafile:latest", Fallback: "", Save: "seafile.backup_seafile.tar", Force: true},
{Name: "hub.yizhisec.com/product/hybridscope/memcached:latest", Fallback: "", Save: "seafile.memcached.tar"},
{Name: "hub.yizhisec.com/product/hybridscope/seafile-mc:latest", Fallback: "", Save: "seafile.seafile_mc.tar"},
}
for _, img := range images {
img.Save = filepath.Join(imgDir, img.Save)
if err = m.Image(ctx, img.Name, WithImageSave(img.Save), WithImageForcePull(img.Force)); err != nil {
logger.Error("❌ make.Seafile: 准备 image: %s 失败, err = %v", img.Name, err)
return err
}
}
logger.Debug("✅ make.Seafile: 准备 images 成功")
logger.Info("✅ maker.Seafile: 构建 seafile 依赖成功!!!")
return nil
}

View File

@@ -6,7 +6,6 @@ import (
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/internal/opt"
"yizhisec.com/hsv2/forge/pkg/downloader"
"yizhisec.com/hsv2/forge/pkg/resource"
)
@@ -17,32 +16,21 @@ type yosguardOpt struct{}
func (m *maker) Yosguard(ctx context.Context, opts ...YosguardOpt) error {
const (
configTemplate = `
Web:
# default listen in docker0
Host: 172.17.0.1
Port: 7788
UUIDFilePath: /etc/yosguard/uuid
# 心跳间隔: 单位秒,默认为5
HeartbeatDuration: 5
# 控制器 yosguard 地址
_config = `
AsController: true
AsGateway: false
ControllerServer:
Host: dasheng.zhsftech.debug
Port: 443
# True: 作为控制器运行; False: 不作为控制器运行
AsController: true
# True: 作为网关运行; False: 不作为网关运行
AsGateway: false
Database:
SQLite:
DBPath: "/etc/yosguard/db/yosguard.db"
SQLPath: "/etc/yosguard/db/create.sql"`
DBPath: /etc/yosguard/db/yosguard.db
HeartbeatDuration: 5
UUIDFilePath: /etc/yosguard/uuid
Web:
Host: __ip__
Port: 7788
`
systemdService = `
[Unit]
@@ -64,7 +52,7 @@ WantedBy=multi-user.target`
binURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/yosguard"
)
location := filepath.Join(opt.Cfg.Make.Dir, "dependency", "yosguard")
location := filepath.Join(m.workdir, "dependency", "yosguard")
logger.Info("☑️ maker.Yosguard: 开始构建 yosguard...")
logger.Debug("☑️ maker.Yosguard: 创建目录 %s", location)
@@ -87,12 +75,12 @@ WantedBy=multi-user.target`
}
logger.Debug("✅ maker.Yosguard: 下载 yosguard 成功, url = %s", binURL)
logger.Debug("☑️ maker.Yosguard: 写入 config_template.yml 文件..., dest = %s", filepath.Join(location, "config_template.yml"))
if err := os.WriteFile(filepath.Join(location, "config_template.yml"), []byte(configTemplate), 0644); err != nil {
logger.Debug("❌ maker.Yosguard: 写入 config_template.yml 失败, dest = %s, err = %v", filepath.Join(location, "config_template.yml"), err)
logger.Debug("☑️ maker.Yosguard: 写入 config.yml 文件..., dest = %s", filepath.Join(location, "config.yml"))
if err := os.WriteFile(filepath.Join(location, "config.yml"), []byte(_config), 0644); err != nil {
logger.Debug("❌ maker.Yosguard: 写入 config.yml 失败, dest = %s, err = %v", filepath.Join(location, "config.yml"), err)
return err
}
logger.Debug("✅ maker.Yosguard: 写入 config_template.yml 文件成功, dest = %s", filepath.Join(location, "config_template.yml"))
logger.Debug("✅ maker.Yosguard: 写入 config.yml 文件成功, dest = %s", filepath.Join(location, "config.yml"))
logger.Debug("☑️ maker.Yosguard: 写入 create.sql 文件..., dest = %s", filepath.Join(location, "create.sql"))
if err := os.WriteFile(filepath.Join(location, "create.sql"), resource.SQLYosguard, 0644); err != nil {

View File

@@ -1,5 +1,7 @@
package opt
import "regexp"
type config struct {
Debug bool
Make struct {
@@ -11,3 +13,12 @@ type config struct {
var (
Cfg = &config{}
)
const (
DefaultWorkdir = "/root/hsv2-installation"
)
var (
StorageSizeReg = regexp.MustCompile(`^\d+(\.\d+)?[EPTGMK]i?$`)
EmailReg = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
)

View File

@@ -5,10 +5,15 @@ import (
"os"
"os/signal"
"syscall"
"time"
"yizhisec.com/hsv2/forge/internal/cmd"
)
func init() {
time.Local = time.FixedZone("CST", 8*60*60)
}
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

648
pkg/imager/pull.go Normal file
View File

@@ -0,0 +1,648 @@
package imager
import (
"archive/tar"
"compress/gzip"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"gitea.loveuer.com/yizhisec/pkg3/logger"
)
// PullOption is a functional option for configuring image pull
type PullOption func(*pullOption)
type pullOption struct {
Proxy string // http or socks5 proxy
Rename string // pull image and rename it
PlainHTTP bool // use http instead of https
SkipTLSVerify bool // skip TLS certificate verification
Username string // registry username for authentication
Password string // registry password for authentication
RetryTimes int // retry times for sync/proxy registries, default is 3
RetryDelay time.Duration // delay between retries, default is 2 seconds
}
// WithProxy sets the proxy for pulling images
func WithProxy(proxy string) PullOption {
return func(o *pullOption) {
o.Proxy = proxy
}
}
// WithRename sets a new name for the pulled image
func WithRename(name string) PullOption {
return func(o *pullOption) {
o.Rename = name
}
}
// WithPlainHTTP enables plain HTTP (no TLS)
func WithPlainHTTP() PullOption {
return func(o *pullOption) {
o.PlainHTTP = true
}
}
// WithSkipTLSVerify skips TLS certificate verification
func WithSkipTLSVerify() PullOption {
return func(o *pullOption) {
o.SkipTLSVerify = true
}
}
// WithAuth sets authentication credentials
func WithAuth(username, password string) PullOption {
return func(o *pullOption) {
o.Username = username
o.Password = password
}
}
// WithRetry sets the retry times for pulling images
func WithRetry(times int) PullOption {
return func(o *pullOption) {
if times < 0 {
times = 0
}
o.RetryTimes = times
}
}
// WithRetryDelay sets the delay between retries
func WithRetryDelay(delay time.Duration) PullOption {
return func(o *pullOption) {
o.RetryDelay = delay
}
}
// manifestV2 represents Docker manifest v2 schema
type manifestV2 struct {
SchemaVersion int `json:"schemaVersion"`
MediaType string `json:"mediaType"`
Config struct {
MediaType string `json:"mediaType"`
Size int64 `json:"size"`
Digest string `json:"digest"`
} `json:"config"`
Layers []struct {
MediaType string `json:"mediaType"`
Size int64 `json:"size"`
Digest string `json:"digest"`
} `json:"layers"`
}
// imageReference parses image reference into registry, repository, and tag
type imageReference struct {
Registry string
Repository string
Tag string
}
// parseImageReference parses an image name into its components
func parseImageReference(name string) (*imageReference, error) {
ref := &imageReference{
Tag: "latest",
}
// First, split by / to separate registry from repository
// This handles cases like localhost:5000/myimage:tag
slashParts := strings.SplitN(name, "/", 2)
var registryPart, repoPart string
if len(slashParts) == 1 {
// No slash, it's just image:tag (e.g., alpine:3.19)
repoPart = slashParts[0]
ref.Registry = "registry-1.docker.io"
} else {
// Has slash, check if first part is a registry
// Registry contains . or : (for host:port)
if strings.Contains(slashParts[0], ".") || strings.Contains(slashParts[0], ":") {
// First part is a registry
registryPart = slashParts[0]
repoPart = slashParts[1]
ref.Registry = registryPart
} else {
// First part is namespace (e.g., library/alpine)
repoPart = name
ref.Registry = "registry-1.docker.io"
}
}
// Now parse the repository and tag from repoPart
// Split on last : to get tag (in case repository name contains /)
lastColon := strings.LastIndex(repoPart, ":")
if lastColon > 0 {
ref.Repository = repoPart[:lastColon]
ref.Tag = repoPart[lastColon+1:]
} else {
ref.Repository = repoPart
}
// For docker.io, add library/ prefix if no namespace
if ref.Registry == "registry-1.docker.io" && !strings.Contains(ref.Repository, "/") {
ref.Repository = "library/" + ref.Repository
}
return ref, nil
}
// PullImage pulls an OCI image using HTTP requests and saves it as a tar archive
func PullImage(ctx context.Context, name string, store io.Writer, opts ...PullOption) error {
options := &pullOption{
RetryTimes: 3, // default retry times
RetryDelay: 2 * time.Second, // default retry delay
}
for _, opt := range opts {
opt(options)
}
logger.Debug("Pulling image: %s with options: %+v", name, options)
// Parse image reference
ref, err := parseImageReference(name)
if err != nil {
return fmt.Errorf("failed to parse image reference: %w", err)
}
logger.Debug("Parsed image reference: registry=%s, repository=%s, tag=%s", ref.Registry, ref.Repository, ref.Tag)
// Create HTTP client
client := createHTTPClient(options)
// Retry logic for sync/proxy registries
var lastErr error
for attempt := 0; attempt <= options.RetryTimes; attempt++ {
if attempt > 0 {
logger.Debug("Retry attempt %d/%d for image: %s", attempt, options.RetryTimes, name)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(options.RetryDelay):
// Continue after delay
}
}
// Get auth token if needed
token, err := getAuthToken(ctx, client, ref, options)
if err != nil {
lastErr = fmt.Errorf("failed to get auth token: %w", err)
logger.Debug("Attempt %d failed: %v", attempt+1, lastErr)
continue
}
// Get manifest
manifest, err := getManifest(ctx, client, ref, token, options)
if err != nil {
lastErr = fmt.Errorf("failed to get manifest: %w", err)
logger.Debug("Attempt %d failed: %v", attempt+1, lastErr)
continue
}
logger.Debug("Got manifest with %d layers", len(manifest.Layers))
// Download and create tar archive
if err := createImageTar(ctx, client, ref, manifest, token, store, name, options); err != nil {
lastErr = fmt.Errorf("failed to create image tar: %w", err)
logger.Debug("Attempt %d failed: %v", attempt+1, lastErr)
continue
}
// Success!
logger.Info("Successfully pulled image: %s", name)
return nil
}
// All retries exhausted
if options.RetryTimes > 0 {
return fmt.Errorf("failed to pull image after %d retries: %w", options.RetryTimes+1, lastErr)
}
return lastErr
}
// createHTTPClient creates an HTTP client with the given options
func createHTTPClient(options *pullOption) *http.Client {
client := &http.Client{}
// Configure TLS
if options.SkipTLSVerify {
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client.Transport = transport
}
// Configure proxy
if options.Proxy != "" {
proxyURL, err := url.Parse(options.Proxy)
if err == nil {
if client.Transport == nil {
client.Transport = &http.Transport{}
}
if transport, ok := client.Transport.(*http.Transport); ok {
transport.Proxy = http.ProxyURL(proxyURL)
}
}
}
return client
}
// getAuthToken gets authentication token from registry
func getAuthToken(ctx context.Context, client *http.Client, ref *imageReference, options *pullOption) (string, error) {
// Try to access registry API v2 to get auth challenge
scheme := "https"
if options.PlainHTTP {
scheme = "http"
}
apiURL := fmt.Sprintf("%s://%s/v2/", scheme, ref.Registry)
req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
if err != nil {
return "", err
}
// Add basic auth if provided
if options.Username != "" && options.Password != "" {
req.SetBasicAuth(options.Username, options.Password)
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
// If 200 OK, no auth needed
if resp.StatusCode == http.StatusOK {
return "", nil
}
// Check for WWW-Authenticate header
authHeader := resp.Header.Get("WWW-Authenticate")
if authHeader == "" {
return "", nil
}
// Parse auth challenge
token, err := fetchToken(ctx, client, authHeader, ref, options)
if err != nil {
return "", err
}
return token, nil
}
// fetchToken fetches authentication token from auth server
func fetchToken(ctx context.Context, client *http.Client, authHeader string, ref *imageReference, options *pullOption) (string, error) {
// Parse WWW-Authenticate header
// Format: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/alpine:pull"
if !strings.HasPrefix(authHeader, "Bearer ") {
return "", nil
}
params := make(map[string]string)
parts := strings.Split(authHeader[7:], ",")
for _, part := range parts {
part = strings.TrimSpace(part)
idx := strings.Index(part, "=")
if idx > 0 {
key := part[:idx]
value := strings.Trim(part[idx+1:], "\"")
params[key] = value
}
}
realm, ok := params["realm"]
if !ok {
return "", fmt.Errorf("no realm in auth header")
}
// Build token URL
tokenURL := realm
if service, ok := params["service"]; ok {
tokenURL += "?service=" + url.QueryEscape(service)
}
scope := fmt.Sprintf("repository:%s:pull", ref.Repository)
if params["scope"] != "" {
scope = params["scope"]
}
tokenURL += "&scope=" + url.QueryEscape(scope)
req, err := http.NewRequestWithContext(ctx, "GET", tokenURL, nil)
if err != nil {
return "", err
}
// Add basic auth if provided
if options.Username != "" && options.Password != "" {
req.SetBasicAuth(options.Username, options.Password)
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to get token: %s", resp.Status)
}
var tokenResp struct {
Token string `json:"token"`
}
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
return "", err
}
return tokenResp.Token, nil
}
// getManifest fetches the image manifest
func getManifest(ctx context.Context, client *http.Client, ref *imageReference, token string, options *pullOption) (*manifestV2, error) {
scheme := "https"
if options.PlainHTTP {
scheme = "http"
}
manifestURL := fmt.Sprintf("%s://%s/v2/%s/manifests/%s", scheme, ref.Registry, ref.Repository, ref.Tag)
req, err := http.NewRequestWithContext(ctx, "GET", manifestURL, nil)
if err != nil {
return nil, err
}
// Set accept headers for manifest v2 (add both types)
req.Header.Add("Accept", "application/vnd.docker.distribution.manifest.v2+json")
req.Header.Add("Accept", "application/vnd.oci.image.manifest.v1+json")
req.Header.Add("Accept", "application/vnd.docker.distribution.manifest.list.v2+json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
logger.Debug("Fetching manifest from: %s", manifestURL)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
logger.Debug("Manifest request failed: %s, body: %s", resp.Status, string(body))
return nil, fmt.Errorf("failed to get manifest: %s", resp.Status)
}
contentType := resp.Header.Get("Content-Type")
logger.Debug("Manifest Content-Type: %s", contentType)
// Read response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Check if it's a manifest list (multi-arch)
if strings.Contains(contentType, "manifest.list") || strings.Contains(contentType, "image.index") {
// Parse manifest list and select amd64/linux
var manifestList struct {
SchemaVersion int `json:"schemaVersion"`
Manifests []struct {
Digest string `json:"digest"`
Platform struct {
Architecture string `json:"architecture"`
OS string `json:"os"`
} `json:"platform"`
} `json:"manifests"`
}
if err := json.Unmarshal(body, &manifestList); err != nil {
return nil, fmt.Errorf("failed to parse manifest list: %w", err)
}
logger.Debug("Found manifest list with %d manifests", len(manifestList.Manifests))
// Find amd64/linux manifest
var targetDigest string
for _, m := range manifestList.Manifests {
logger.Debug("Checking manifest: arch=%s, os=%s, digest=%s",
m.Platform.Architecture, m.Platform.OS, m.Digest)
if m.Platform.Architecture == "amd64" && m.Platform.OS == "linux" {
targetDigest = m.Digest
break
}
}
if targetDigest == "" && len(manifestList.Manifests) > 0 {
// Fallback to first manifest with known platform
for _, m := range manifestList.Manifests {
if m.Platform.Architecture != "unknown" && m.Platform.OS != "unknown" {
targetDigest = m.Digest
logger.Debug("Using fallback manifest: arch=%s, os=%s",
m.Platform.Architecture, m.Platform.OS)
break
}
}
}
if targetDigest == "" {
return nil, fmt.Errorf("no suitable manifest found in manifest list")
}
logger.Debug("Selected manifest digest: %s", targetDigest)
// Fetch the actual manifest by digest
return getManifestByDigest(ctx, client, ref, targetDigest, token, options)
}
var manifest manifestV2
if err := json.Unmarshal(body, &manifest); err != nil {
return nil, fmt.Errorf("failed to parse manifest: %w", err)
}
logger.Debug("Parsed manifest: schemaVersion=%d, config.digest=%s, layers=%d",
manifest.SchemaVersion, manifest.Config.Digest, len(manifest.Layers))
return &manifest, nil
}
// getManifestByDigest fetches a manifest by its digest
func getManifestByDigest(ctx context.Context, client *http.Client, ref *imageReference, digest, token string, options *pullOption) (*manifestV2, error) {
scheme := "https"
if options.PlainHTTP {
scheme = "http"
}
manifestURL := fmt.Sprintf("%s://%s/v2/%s/manifests/%s", scheme, ref.Registry, ref.Repository, digest)
req, err := http.NewRequestWithContext(ctx, "GET", manifestURL, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/vnd.docker.distribution.manifest.v2+json")
req.Header.Add("Accept", "application/vnd.oci.image.manifest.v1+json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get manifest by digest: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var manifest manifestV2
if err := json.Unmarshal(body, &manifest); err != nil {
return nil, fmt.Errorf("failed to parse manifest: %w", err)
}
return &manifest, nil
}
// createImageTar downloads layers and creates a Docker-compatible tar archive
func createImageTar(ctx context.Context, client *http.Client, ref *imageReference, manifest *manifestV2, token string, store io.Writer, imageName string, options *pullOption) error {
// Create tar writer
tw := tar.NewWriter(store)
defer tw.Close()
scheme := "https"
if options.PlainHTTP {
scheme = "http"
}
// Download config blob
logger.Debug("Downloading config blob: %s", manifest.Config.Digest)
configBlob, err := downloadBlob(ctx, client, scheme, ref.Registry, ref.Repository, manifest.Config.Digest, token)
if err != nil {
return fmt.Errorf("failed to download config: %w", err)
}
// Write config as json file
configFileName := strings.TrimPrefix(manifest.Config.Digest, "sha256:") + ".json"
if err := writeTarEntry(tw, configFileName, configBlob); err != nil {
return err
}
// Download and write each layer
var layerFiles []string
for i, layer := range manifest.Layers {
logger.Debug("Downloading layer %d/%d: %s", i+1, len(manifest.Layers), layer.Digest)
layerBlob, err := downloadBlob(ctx, client, scheme, ref.Registry, ref.Repository, layer.Digest, token)
if err != nil {
return fmt.Errorf("failed to download layer %s: %w", layer.Digest, err)
}
layerFileName := strings.TrimPrefix(layer.Digest, "sha256:") + "/layer.tar"
layerFiles = append(layerFiles, layerFileName)
if err := writeTarEntry(tw, layerFileName, layerBlob); err != nil {
return err
}
}
// Create manifest.json for Docker compatibility
if options.Rename != "" {
imageName = options.Rename
}
manifestJSON := []map[string]interface{}{
{
"Config": configFileName,
"RepoTags": []string{imageName},
"Layers": layerFiles,
},
}
manifestData, err := json.Marshal(manifestJSON)
if err != nil {
return err
}
if err := writeTarEntry(tw, "manifest.json", manifestData); err != nil {
return err
}
// Create repositories file (legacy)
repositories := make(map[string]map[string]string)
repositories[imageName] = map[string]string{ref.Tag: strings.TrimPrefix(manifest.Config.Digest, "sha256:")[:12]}
reposData, err := json.Marshal(repositories)
if err != nil {
return err
}
if err := writeTarEntry(tw, "repositories", reposData); err != nil {
return err
}
return nil
}
// downloadBlob downloads a blob from registry
func downloadBlob(ctx context.Context, client *http.Client, scheme, registry, repository, digest, token string) ([]byte, error) {
blobURL := fmt.Sprintf("%s://%s/v2/%s/blobs/%s", scheme, registry, repository, digest)
req, err := http.NewRequestWithContext(ctx, "GET", blobURL, nil)
if err != nil {
return nil, err
}
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to download blob: %s", resp.Status)
}
// Check if content is gzipped
var reader io.Reader = resp.Body
if resp.Header.Get("Content-Type") == "application/vnd.docker.image.rootfs.diff.tar.gzip" ||
strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
gzReader, err := gzip.NewReader(resp.Body)
if err == nil {
defer gzReader.Close()
reader = gzReader
}
}
return io.ReadAll(reader)
}
// writeTarEntry writes a file entry to tar archive
func writeTarEntry(tw *tar.Writer, name string, data []byte) error {
hdr := &tar.Header{
Name: name,
Mode: 0644,
Size: int64(len(data)),
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if _, err := tw.Write(data); err != nil {
return err
}
return nil
}

429
pkg/imager/pull_test.go Normal file
View File

@@ -0,0 +1,429 @@
package imager
import (
"context"
"os"
"path/filepath"
"testing"
"time"
"gitea.loveuer.com/yizhisec/pkg3/logger"
)
// TestPullImage_PublicImage tests pulling a public image from Docker Hub
func TestPullImage_PublicImage(t *testing.T) {
logger.SetLogLevel(logger.LogLevelDebug)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// Create temp directory for test output
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "alpine.tar")
outputFile = "./redis.alpine.tar"
// Create output file
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
// Pull alpine image (small and commonly available)
imageName := "docker-mirror.yizhisec.com/library/redis:alpine"
err = PullImage(ctx, imageName, f)
if err != nil {
t.Fatalf("failed to pull image %s: %v", imageName, err)
}
// Verify file was created and has content
if err := f.Close(); err != nil {
t.Fatalf("failed to close file: %v", err)
}
info, err := os.Stat(outputFile)
if err != nil {
t.Fatalf("failed to stat output file: %v", err)
}
if info.Size() == 0 {
t.Error("output file is empty")
}
t.Logf("Successfully pulled %s, size: %d bytes", imageName, info.Size())
}
// TestPullImage_WithRegistry tests pulling from a specific registry
func TestPullImage_WithRegistry(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "nginx.tar")
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
// Pull from docker.io with explicit registry
imageName := "docker.io/library/nginx:alpine"
err = PullImage(ctx, imageName, f)
if err != nil {
t.Fatalf("failed to pull image %s: %v", imageName, err)
}
if err := f.Close(); err != nil {
t.Fatalf("failed to close file: %v", err)
}
info, err := os.Stat(outputFile)
if err != nil {
t.Fatalf("failed to stat output file: %v", err)
}
if info.Size() == 0 {
t.Error("output file is empty")
}
t.Logf("Successfully pulled %s, size: %d bytes", imageName, info.Size())
}
// TestPullImage_WithRename tests pulling and renaming an image
func TestPullImage_WithRename(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "renamed.tar")
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
imageName := "alpine:3.19"
newName := "my-custom-alpine:latest"
err = PullImage(ctx, imageName, f, WithRename(newName))
if err != nil {
t.Fatalf("failed to pull image %s: %v", imageName, err)
}
if err := f.Close(); err != nil {
t.Fatalf("failed to close file: %v", err)
}
info, err := os.Stat(outputFile)
if err != nil {
t.Fatalf("failed to stat output file: %v", err)
}
if info.Size() == 0 {
t.Error("output file is empty")
}
t.Logf("Successfully pulled %s and renamed to %s, size: %d bytes", imageName, newName, info.Size())
}
// TestPullImage_WithAuth tests pulling with authentication (skip if no credentials)
func TestPullImage_WithAuth(t *testing.T) {
// This test requires actual credentials, so it's skipped by default
t.Skip("Skipping authentication test - requires valid credentials")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "private.tar")
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
// Replace with your private registry details
imageName := "your-registry.com/your-repo/your-image:tag"
username := "your-username"
password := "your-password"
err = PullImage(ctx, imageName, f, WithAuth(username, password))
if err != nil {
t.Fatalf("failed to pull private image %s: %v", imageName, err)
}
if err := f.Close(); err != nil {
t.Fatalf("failed to close file: %v", err)
}
info, err := os.Stat(outputFile)
if err != nil {
t.Fatalf("failed to stat output file: %v", err)
}
if info.Size() == 0 {
t.Error("output file is empty")
}
t.Logf("Successfully pulled private image %s, size: %d bytes", imageName, info.Size())
}
// TestPullImage_WithRetry tests retry functionality
func TestPullImage_WithRetry(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "retry.tar")
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
// Pull with custom retry settings
imageName := "alpine:3.19"
err = PullImage(ctx, imageName, f,
WithRetry(5), // 5 retries
WithRetryDelay(1*time.Second), // 1 second delay
)
if err != nil {
t.Fatalf("failed to pull image %s: %v", imageName, err)
}
if err := f.Close(); err != nil {
t.Fatalf("failed to close file: %v", err)
}
info, err := os.Stat(outputFile)
if err != nil {
t.Fatalf("failed to stat output file: %v", err)
}
if info.Size() == 0 {
t.Error("output file is empty")
}
t.Logf("Successfully pulled %s with retry, size: %d bytes", imageName, info.Size())
}
// TestPullImage_WithZeroRetry tests with retry disabled
func TestPullImage_WithZeroRetry(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "no-retry.tar")
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
// Pull with retry disabled
imageName := "alpine:3.19"
err = PullImage(ctx, imageName, f, WithRetry(0))
if err != nil {
t.Fatalf("failed to pull image %s: %v", imageName, err)
}
if err := f.Close(); err != nil {
t.Fatalf("failed to close file: %v", err)
}
info, err := os.Stat(outputFile)
if err != nil {
t.Fatalf("failed to stat output file: %v", err)
}
if info.Size() == 0 {
t.Error("output file is empty")
}
t.Logf("Successfully pulled %s without retry, size: %d bytes", imageName, info.Size())
}
// TestPullImage_WithSkipTLSVerify tests pulling with TLS verification disabled
func TestPullImage_WithSkipTLSVerify(t *testing.T) {
// This test is for registries with self-signed certificates
t.Skip("Skipping TLS skip test - requires a registry with self-signed cert")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "insecure.tar")
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
imageName := "your-insecure-registry.local/image:tag"
err = PullImage(ctx, imageName, f, WithSkipTLSVerify())
if err != nil {
t.Fatalf("failed to pull image %s: %v", imageName, err)
}
if err := f.Close(); err != nil {
t.Fatalf("failed to close file: %v", err)
}
info, err := os.Stat(outputFile)
if err != nil {
t.Fatalf("failed to stat output file: %v", err)
}
if info.Size() == 0 {
t.Error("output file is empty")
}
t.Logf("Successfully pulled %s with TLS verification skipped, size: %d bytes", imageName, info.Size())
}
// TestParseImageReference tests the image reference parsing logic
func TestParseImageReference(t *testing.T) {
tests := []struct {
name string
imageName string
wantRegistry string
wantRepository string
wantTag string
wantErr bool
}{
{
name: "simple image with tag",
imageName: "alpine:3.19",
wantRegistry: "registry-1.docker.io",
wantRepository: "library/alpine",
wantTag: "3.19",
wantErr: false,
},
{
name: "simple image without tag",
imageName: "alpine",
wantRegistry: "registry-1.docker.io",
wantRepository: "library/alpine",
wantTag: "latest",
wantErr: false,
},
{
name: "image with namespace",
imageName: "library/nginx:alpine",
wantRegistry: "registry-1.docker.io",
wantRepository: "library/nginx",
wantTag: "alpine",
wantErr: false,
},
{
name: "custom registry",
imageName: "hub.yizhisec.com/external/alpine:3.22.2",
wantRegistry: "hub.yizhisec.com",
wantRepository: "external/alpine",
wantTag: "3.22.2",
wantErr: false,
},
{
name: "docker.io explicit",
imageName: "docker.io/library/redis:8.2.2",
wantRegistry: "docker.io",
wantRepository: "library/redis",
wantTag: "8.2.2",
wantErr: false,
},
{
name: "registry with port",
imageName: "localhost:5000/myimage:v1.0",
wantRegistry: "localhost:5000",
wantRepository: "myimage",
wantTag: "v1.0",
wantErr: false,
},
{
name: "quay.io registry",
imageName: "quay.io/k0sproject/pause:3.10.1",
wantRegistry: "quay.io",
wantRepository: "k0sproject/pause",
wantTag: "3.10.1",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ref, err := parseImageReference(tt.imageName)
if (err != nil) != tt.wantErr {
t.Errorf("parseImageReference() error = %v, wantErr %v", err, tt.wantErr)
return
}
if err != nil {
return
}
if ref.Registry != tt.wantRegistry {
t.Errorf("parseImageReference() registry = %v, want %v", ref.Registry, tt.wantRegistry)
}
if ref.Repository != tt.wantRepository {
t.Errorf("parseImageReference() repository = %v, want %v", ref.Repository, tt.wantRepository)
}
if ref.Tag != tt.wantTag {
t.Errorf("parseImageReference() tag = %v, want %v", ref.Tag, tt.wantTag)
}
})
}
}
// TestPullImage_ContextCancellation tests that context cancellation works
func TestPullImage_ContextCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir, "cancelled.tar")
f, err := os.Create(outputFile)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer f.Close()
// Cancel context immediately
cancel()
imageName := "alpine:3.19"
err = PullImage(ctx, imageName, f)
if err == nil {
t.Error("expected error due to cancelled context, got nil")
}
t.Logf("Context cancellation correctly handled: %v", err)
}
// BenchmarkPullImage benchmarks image pulling performance
func BenchmarkPullImage(b *testing.B) {
ctx := context.Background()
tmpDir := b.TempDir()
imageName := "alpine:3.19"
b.ResetTimer()
for i := 0; i < b.N; i++ {
outputFile := filepath.Join(tmpDir, "alpine_bench.tar")
f, err := os.Create(outputFile)
if err != nil {
b.Fatalf("failed to create output file: %v", err)
}
err = PullImage(ctx, imageName, f)
if err != nil {
b.Fatalf("failed to pull image: %v", err)
}
f.Close()
os.Remove(outputFile)
}
}

55
pkg/model/caddy.go Normal file
View File

@@ -0,0 +1,55 @@
package model
type CaddyUpstream struct {
Dial []string `json:"dial"`
}
type CaddyActive struct {
Interval string `json:"interval"`
Timeout string `json:"timeout"`
Port int `json:"port"`
}
type CaddyPassive struct {
MaxFails int `json:"max_fails"`
FailDuration string `json:"fail_duration"`
}
type CaddyHealthCheck struct {
Active *CaddyActive `json:"active"`
Passive *CaddyPassive `json:"passive"`
}
type CaddySelection struct {
Policy string `json:"policy"`
}
type CaddyLoadBalancing struct {
Selection *CaddySelection `json:"selection"`
}
type CaddyHandle struct {
Handler string `json:"handler"`
Upstreams []*CaddyUpstream `json:"upstreams"`
HealthChecks *CaddyHealthCheck `json:"health_checks"`
LoadBalancing *CaddyLoadBalancing `json:"load_balancing"`
}
type CaddyRoute struct {
Handle []*CaddyHandle `json:"handle"`
}
type CaddyServer struct {
Listen []string `json:"listen"`
Routes []*CaddyRoute `json:"routes"`
}
type CaddyLayer4 struct {
Servers map[string]*CaddyServer `json:"servers"`
}
type CaddyApp struct {
Layer4 *CaddyLayer4 `json:"layer4"`
}
type CaddyConfig map[string]*CaddyApp

8
pkg/model/image.go Normal file
View File

@@ -0,0 +1,8 @@
package model
type Image struct {
Name string
Fallback string
Save string
Force bool
}

58
pkg/model/vendor.go Normal file
View File

@@ -0,0 +1,58 @@
package model
type Vendor struct {
Name string
OEMUrl string
OEMDir string
AppFrontUserImageName string
AppFrontAdminImageName string
}
var (
vendorMap = map[string]*Vendor{
"standard": &Vendor{
Name: "Standard",
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem.tar.gz",
OEMDir: "oem",
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user:latest",
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
},
"elink": &Vendor{
Name: "elink",
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_csgElink.tar.gz",
OEMDir: "oem_csgElink",
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user-elink:latest",
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
},
"noah": &Vendor{
Name: "noah",
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_noah.tar.gz",
OEMDir: "oem_noah",
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user:latest",
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
},
"heishuimeng": &Vendor{
Name: "heishuimeng",
OEMUrl: "https://artifactory.yizhisec.com/artifactory/yizhisec-release/oem/release/2.1.0-std/oem_heishuimeng.tar.gz",
OEMDir: "oem_heishuimeng",
AppFrontUserImageName: "hub.yizhisec.com/hybridscope/v2/front-user:latest",
AppFrontAdminImageName: "hub.yizhisec.com/build/hybirdscope/front/admin:latest",
},
}
)
func GetVendor(name string) *Vendor {
if vendor, ok := vendorMap[name]; ok {
return vendor
}
return nil
}
func GetVendorNames() []string {
names := make([]string, 0, len(vendorMap))
for name := range vendorMap {
names = append(names, name)
}
return names
}

View File

@@ -0,0 +1,73 @@
{
"apps": {
"layer4": {
"servers": {
"proxy_8443_tcp_backends": {
"listen": [":8443"],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [
{"dial": ["10.118.2.11:32443"]},
{"dial": ["10.118.2.12:32443"]}
],
"health_checks": {
"active": {
"interval": "5s",
"timeout": "2s",
"port": 32443
},
"passive": {
"max_fails": 1,
"fail_duration": "30s"
}
},
"load_balancing": {
"selection": {
"policy": "round_robin"
}
}
}
]
}
]
},
"proxy_443_tcp_backends": {
"listen": [":443"],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [
{"dial": ["10.118.2.11:31443"]},
{"dial": ["10.118.2.12:31443"]}
],
"health_checks": {
"active": {
"interval": "5s",
"timeout": "2s",
"port": 31443
},
"passive": {
"max_fails": 1,
"fail_duration": "30s"
}
},
"load_balancing": {
"selection": {
"policy": "round_robin"
}
}
}
]
}
]
}
}
}
}
}

View File

@@ -0,0 +1,254 @@
upstream hs-client-server {
least_conn;
server client-service:9129 max_fails=3 fail_timeout=10s;
}
upstream hs-client-without-auth-server {
least_conn;
server client-service:9024 max_fails=3 fail_timeout=10s;
}
upstream hs-client-message-server {
least_conn;
server client-service:9025 max_fails=3 fail_timeout=10s;
}
server {
listen 443 ssl
proxy_protocol;
server_name hs-client-api-server hs.client.api.server;
ssl_certificate /etc/nginx/ssl/client.server.crt;
ssl_certificate_key /etc/nginx/ssl/client.server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 50M;
location /api/v2_2 {
proxy_pass http://u-api-service;
}
location /api/v1/pkg/archive {
proxy_pass http://u-api-service/api/v2_2/client/download/check;
}
location /api/v1/version {
proxy_pass http://u-api-service/api/v2_2/client/version;
}
location /api/ {
proxy_pass http://hs-client-server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v1/ {
proxy_pass http://hs-client-without-auth-server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v1/dl/ {
proxy_pass http://hs-client-without-auth-server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v2/admin/ {
proxy_pass http://hs-client-message-server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
# location /app-store {
# auth_request /app-store-auth;
# rewrite ^/app-store(.*)$ $1 break;
# proxy_pass http://hs-resource-server:19980;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $proxy_protocol_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_read_timeout 300s;
# }
location /app-store-auth {
internal;
proxy_pass http://hs-client-server/auth$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Original-IP $remote_addr;
proxy_set_header Query-Data $http_query_data;
}
# location /wm/api {
# proxy_pass http://hs-watermark-server;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $proxy_protocol_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_read_timeout 300s;
# }
location /static/config/ {
alias /static/config/;
}
location /static/resource/ {
alias /static/resource/;
}
location /user/avatar/ {
alias /static/avatar/;
add_header Cache-Control public;
}
}
server {
listen 443 ssl proxy_protocol;
server_name hs-client-update-server hs.client.update.server;
ssl_certificate /etc/nginx/ssl/client.server.crt;
ssl_certificate_key /etc/nginx/ssl/client.server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 50M;
location /api/v2_2 {
proxy_pass http://u-api-service;
}
location /api/v1/pkg/archive {
proxy_pass http://u-api-service/api/v2_2/client/download/check;
}
location /api/v1/pkg/archive/version {
proxy_pass http://u-api-service/api/v2_2/client/version;
}
location /api/v1/version {
proxy_pass http://u-api-service/api/v2_2/client/version;
}
location /static/config/rc.json {
proxy_pass http://u-api-service/api/v2_2/client/rc/json?os=win;
}
# location = /api/v1/version {
# proxy_pass http://hs-client-without-auth-server;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $proxy_protocol_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_read_timeout 300s;
# }
location /api/v1/pkg {
proxy_pass http://hs-client-without-auth-server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v1/dl/ {
proxy_pass http://hs-client-without-auth-server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /user/avatar/ {
alias /static/avatar/;
expires 7d;
add_header Cache-Control public;
}
location /static/config/ {
alias /static/config/;
}
location /static/resource/ {
alias /static/resource/;
}
}
server {
listen 9118 ssl
proxy_protocol;
ssl_certificate /etc/nginx/ssl/client.server.crt;
ssl_certificate_key /etc/nginx/ssl/client.server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 50M;
location = /api/v1/version {
proxy_pass http://hs-client-without-auth-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v1/pkg {
proxy_pass http://hs-client-without-auth-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v1/dl/ {
proxy_pass http://hs-client-without-auth-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
}

View File

@@ -0,0 +1,28 @@
user root;
worker_processes auto;
events {
worker_connections 1024;
}
http {
log_format custom '$time_iso8601 - $remote_addr - $http_host - $status - $request_time - $request_method - $request_uri';
access_log /var/log/nginx/access.log custom;
include /etc/nginx/mime.types;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
server {
listen 80;
location %s {
alias /data;
try_files $uri $uri/ =404;
}
}
}

View File

@@ -0,0 +1,213 @@
ssl_certificate /etc/nginx/ssl/web.server.crt;
ssl_certificate_key /etc/nginx/ssl/web.server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_dhparam /etc/nginx/ssl/ffdhe2048.txt;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
client_header_buffer_size 1k;
client_max_body_size 50M;
location = /token_auth {
internal;
proxy_pass http://hs-api/api/tokenauth;
proxy_http_version 1.1;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
location /client/dl/android {
proxy_set_header Cookie $http_cookie;
default_type application/octet-stream;
alias /static/client/android;
if ($arg_attname ~ "^(.+)") {
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
}
}
location /client/dl/ {
# remove download client auth verify
# auth_request /token_auth;
proxy_set_header Cookie $http_cookie;
default_type application/octet-stream;
alias /static/client/;
if ($arg_attname ~ "^(.+)") {
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
}
}
location /user/avatar/ {
auth_request /token_auth;
proxy_set_header Cookie $http_cookie;
alias /static/avatar/;
expires 7d;
add_header Cache-Control public;
}
location /file/share/ {
auth_request /token_auth;
proxy_set_header Cookie $http_cookie;
alias /static/share_file/;
if ($arg_attname ~ "^(.+)") {
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
}
}
location /file/public/ {
auth_request /token_auth;
proxy_set_header Cookie $http_cookie;
alias /static/public_folder/;
if ($arg_attname ~ "^(.+)") {
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
}
}
location /file/clipboard/ {
auth_request /token_auth;
proxy_set_header Cookie $http_cookie;
alias /static/clipboard_file/;
}
location /file/uploaded/ {
auth_request /token_auth;
proxy_set_header Cookie $http_cookie;
alias /static/uploaded_files/;
if ($arg_attname ~ "^(.+)") {
add_header Content-Disposition "attachment;filename*=utf-8''$arg_attname";
}
}
location /resource/update_log.csv {
auth_request /token_auth;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Cookie $http_cookie;
default_type application/octet-stream;
alias /static/resource/update_log.csv;
}
location /resource/update_timestamp.txt {
auth_request /token_auth;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Cookie $http_cookie;
default_type application/octet-stream;
alias /static/resource/update_timestamp.txt;
}
location /resource/ {
default_type application/octet-stream;
alias /static/resource/;
}
location /api/ {
proxy_pass http://hs-api;
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_read_timeout 300s;
}
# location /network-disk {
# set $arg_token ''; # 声明 $arg_token 变量
# if ($args ~* "token=(.*?)(&|$)") {
# set $arg_token $1;
# }
# auth_request /token_auth;
# set $auth $http_authorization;
# if ($http_authorization = "") {
# set $auth "token $arg_authorization";
# }
# rewrite ^/network-disk(.*)$ $1 break;
# proxy_pass http://hs-resource-server:19980;
# proxy_http_version 1.1;
# proxy_set_header Authorization $auth;
# }
# location /app-store {
# set $arg_token ''; # 声明 $arg_token 变量
# if ($args ~* "token=(.*?)(&|$)") {
# set $arg_token $1;
# }
# auth_request /app-store-auth;
# set $auth $http_authorization;
# if ($http_authorization = "") {
# set $auth "token $arg_authorization";
# }
# rewrite ^/app-store(.*)$ $1 break;
# proxy_pass http://hs-resource-server:19980;
# proxy_http_version 1.1;
# proxy_set_header Authorization $auth;
# }
location /app-store-auth {
internal;
set $hs_token $http_hs_token;
if ($hs_token = "") {
set $hs_token $arg_token;
}
proxy_set_header Hs-Token $hs_token;
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_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Original-IP $remote_addr;
proxy_set_header Query-Data $http_query_data;
proxy_pass http://hs-api/api$request_uri;
}
error_page 502 /502.json;
error_page 503 /503.json;
location /503.json {
return 503 '{"code": -2, "msg": "服务器未响应", "err": "hs-nginx err"}';
}
error_page 504 /504.json;
location /504.json {
return 504 '{"code": -3, "msg": "服务器未响应", "err": "hs-nginx err"}';
}
error_page 497 301 =307 https://$http_host$request_uri;
error_page 401 @my_401;
error_page 403 @my_403;
error_page 404 @my_404;
error_page 502 @my_502;
location @my_401 {
default_type text/html;
return 401 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>401</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>401 Unauthorized</h1>';
}
location @my_403 {
default_type text/html;
return 403 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>403</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>403 Forbidden</h1>';
}
location @my_404 {
default_type text/html;
return 404 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>404</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>404 Not_Found</h1>';
}
location @my_502 {
default_type text/html;
return 502 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>502</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>502 Bad_Gateway</h1>';
}

View File

@@ -0,0 +1,75 @@
upstream hs-gateway-controller {
least_conn;
server gateway-service:9012 max_fails=3 fail_timeout=10s;
}
server {
listen 443 ssl proxy_protocol;
server_name hs-gateway-controller;
ssl_certificate /yizhisec/ssl/server.crt;
ssl_certificate_key /yizhisec/ssl/server.key;
ssl_client_certificate /yizhisec/ssl/ca.crt;
ssl_verify_client on;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
ssl_dhparam /etc/nginx/ssl/ffdhe2048.txt;
# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 50M;
location / {
proxy_pass http://hs-gateway-controller;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
}
server {
listen 443 ssl proxy_protocol;
server_name hs-gateway-register-controller;
ssl_certificate /yizhisec/ssl/server.crt;
ssl_certificate_key /yizhisec/ssl/server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_dhparam /etc/nginx/ssl/ffdhe2048.txt;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 50M;
location = /api/v1/gateway/setting {
if ($request_method != POST ) {
return 502 '{"code": -1, "msg": "invalid request"}';
}
proxy_pass http://hs-gateway-controller;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
}

View File

@@ -0,0 +1,63 @@
user root;
worker_processes auto;
events {
worker_connections 1024;
}
stream {
error_log /var/log/nginx/error.log error;
map $ssl_preread_server_name $backend {
mqtt.yizhisec.com 127.0.0.1:27443;
mqtt-yizhisec-com 127.0.0.1:27443;
default web;
}
upstream web {
server 127.0.0.1:443;
}
server {
listen 27443 ssl proxy_protocol;
# ssl_session_timeout 10m;
ssl_certificate /etc/nginx/ssl/mqtt.server.crt;
ssl_certificate_key /etc/nginx/ssl/mqtt.server.key;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
proxy_pass emqx-service.db-emqx:1883;
}
server {
listen 23443;
proxy_pass $backend;
ssl_preread on;
proxy_protocol on;
}
}
http {
log_format custom '$time_iso8601 - $remote_addr - $http_host - $status - $request_time - $request_method - $request_uri';
access_log /var/log/nginx/access.log custom;
include /etc/nginx/sites-enabled/*.conf;
include mime.types;
default_type application/octet-stream;
sendfile on;
sendfile_max_chunk 512k;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_vary on;
gzip_http_version 1.0;
gzip_min_length 1000;
gzip_comp_level 6;
gzip_disable msie6;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/javascript text/xml application/xml application/rss+xml application/atom+xml;
keepalive_timeout 65;
}

View File

@@ -0,0 +1,81 @@
server {
listen 443 ssl proxy_protocol;
server_name seafile.yizhisec.com cloud.hybridscope.com seafile-yizhise-com cloud-hybridscope-com;
ssl_certificate /etc/nginx/ssl/client.server.crt;
ssl_certificate_key /etc/nginx/ssl/client.server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=63072000" always;
ssl_trusted_certificate /etc/nginx/ssl/client.ca.crt;
ssl_client_certificate /etc/nginx/ssl/client.ca.crt;
ssl_verify_client on;
client_max_body_size 500M;
# location = /auth-sharing {
# internal;
# proxy_pass http://client-service:9129/api/auth-sharing;
# proxy_http_version 1.1;
# proxy_pass_request_body off;
# proxy_set_header Content-Length "";
# proxy_set_header X-Original-URI $request_uri;
# }
location /f/ {
rewrite ^(.+[^/])$ $1/ last; # 补上末尾的 /,避免重定向两次
# auth_request /auth-sharing;
# proxy_pass http://hs-openresty:13381;
proxy_pass http://seafile-service.seafile;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v1/ {
proxy_pass http://backup-seafile-service.seafile:9027;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api2/ {
# proxy_pass http://hs-resource-server:19980;
proxy_pass http://seafile-service.seafile;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location /api/v2.1/ {
# proxy_pass http://hs-resource-server:19980;
proxy_pass http://seafile-service.seafile;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_read_timeout 300s;
}
location /seafhttp/ {
# proxy_pass http://hs-resource-server:19980;
proxy_pass http://seafile-service.seafile;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
}

View File

@@ -0,0 +1,17 @@
server {
listen 80;
server_name hs-user-management-controller;
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 50M;
location / {
proxy_pass http://user-service:9013;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
}

157
pkg/resource/nginx/web.conf Normal file
View File

@@ -0,0 +1,157 @@
server {
listen 80;
return 301 https://$host$request_uri;
}
# upstream hs-backup-server {
# least_conn;
# server hs-backup-server:9349 max_fails=3 fail_timeout=10s;
# }
upstream hs-api {
server api-service:9002;
}
server {
listen 9002;
location / {
proxy_pass http://hs-api;
}
}
server {
listen 443 ssl default_server;
location /api/admin/ {
return 404;
}
location /oem {
proxy_pass http://oem-service;
}
location /api/my/sys/client/installer {
proxy_pass http://u-api-service/api/v2_2/client/download/list;
}
location /api/system/version {
proxy_pass http://u-api-service/api/v2_2/system/version;
}
location /api/v2_2/client {
proxy_pass http://u-api-service;
}
location /api/v2_2/system {
proxy_pass http://u-api-service;
}
location / {
proxy_pass http://front-user-service;
}
include /etc/nginx/common/common.conf;
error_page 497 301 =307 https://$http_host$request_uri;
}
server {
listen 8443 ssl;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN"; # 或 "DENY"
add_header Content-Security-Policy "img-src * data:; frame-ancestors 'none';" always;
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Permitted-Cross-Domain-Policies "none";
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Download-Options "noopen" always;
add_header X-Content-Type-Options "nosniff" always;
server_tokens off;
location /api/system/version {
proxy_pass http://u-api-service/api/v2_2/system/version;
}
location /oem {
proxy_pass http://oem-service;
}
# location /wm/ {
# alias /data/wm/;
# expires 30d;
# add_header Cache-Control public;
# }
location / {
proxy_pass http://front-admin-service;
}
location /api/v2_1/user {
proxy_pass http://user-service:9013;
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_read_timeout 300s;
}
include /etc/nginx/common/common.conf;
location /ws {
proxy_pass http://hs-api/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
# location /backup {
# proxy_pass http://hs-backup-server;
# 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_read_timeout 300s;
# }
location /api/local/user/import/template {
auth_request /token_auth;
alias /static/resource/local_user_import_template.xlsx;
}
# location /wm/api {
# proxy_pass http://hs-watermark-server;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $proxy_protocol_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_read_timeout 300s;
# }
location @my_401 {
default_type text/html;
return 401 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>401</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>401 Unauthorized</h1>';
}
location @my_403 {
default_type text/html;
return 403 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>403</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>403 Forbidden</h1>';
}
location @my_404 {
default_type text/html;
return 404 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>404</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>404 Not_Found</h1>';
}
location @my_502 {
default_type text/html;
return 502 '<!doctypehtml><html lang=en><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>502</title><style>body{display:flex;flex-direction:column;align-items:center;justify-content:center}</style><h1>502 Bad_Gateway</h1>';
}
error_page 497 301 =307 https://$http_host$request_uri;
error_page 401 @my_401;
error_page 403 @my_403;
error_page 404 @my_404;
error_page 502 @my_502;
}

View File

@@ -5,24 +5,132 @@ import (
)
var (
//go:embed flannel.yaml
//go:embed yaml/registry.yaml
YAMLRegistry string
//go:embed yaml/flannel.yaml
YAMLFlannel string
//go:embed es.yaml
//go:embed yaml/es.yaml
YAMLES string
//go:embed kibana.yaml
//go:embed yaml/kibana.yaml
YAMLKibana []byte
//go:embed es.init.sh
//go:embed sh/es.init.sh
BashESInit []byte
//go:embed emqx.yaml
//go:embed yaml/emqx.yaml
YAMLEMQX []byte
//go:embed yosguard.create.sql
//go:embed yaml/minio.yaml
YAMLMinIO string
//go:embed sql/yosguard.create.sql
SQLYosguard []byte
//go:embed less-dns.yaml
//go:embed yaml/less-dns.yaml
YAMLLessDNS []byte
//go:embed yaml/seafile.yaml
YAMLSeafile string
//go:embed yaml/backup-seafile.yaml
YAMLBackupSeafile string
//go:embed yaml/app.user.yaml
YAMLAppUser string
//go:embed yaml/app.gateway.yaml
YAMLAppGateway string
//go:embed yaml/app.client.yaml
YAMLAppClient string
//go:embed yaml/app.mie.api.yaml
YAMLAppMieAPI string
//go:embed yaml/app.mie.worker.yaml
YAMLAppMieWorker []byte
//go:embed yaml/app.mie.cron.yaml
YAMLAppMieCron []byte
//go:embed yaml/app.mie.sweeper.yaml
YAMLAppMieSweeper []byte
//go:embed yaml/app.oem.yaml
YAMLAppOEM string
//go:embed yaml/app.front.user.yaml
YAMLAppFrontUser string
//go:embed yaml/app.front.admin.yaml
YAMLAppFrontAdmin string
//go:embed yaml/app.nginx.yaml
YAMLAppNGINX string
//go:embed yaml/client.pkg.yaml
YAMLClientPKG string
//go:embed ssl/ca.crt
SSLCaCrt string
//go:embed ssl/client.server.crt
SSLClientServerCrt string
//go:embed ssl/client.server.key
SSLClientServerKey string
//go:embed ssl/ffdhe2048.txt
SSLFFDHE2048 string
//go:embed ssl/mqtt.server.crt
SSLMQTTServerCrt string
//go:embed ssl/mqtt.server.key
SSLMQTTServerKey string
//go:embed ssl/mqtt.client.crt
SSLMQTTClientCrt []byte
//go:embed ssl/mqtt.client.key
SSLMQTTClientKey []byte
//go:embed ssl/server.crt
SSLServerCrt string
//go:embed ssl/server.key
SSLServerKey string
//go:embed ssl/web.server.crt
SSLWebServerCrt string
//go:embed ssl/web.server.key
SSLWebServerKey string
//go:embed nginx/seafile.conf
NGINXSeafile []byte
//go:embed nginx/common.conf
NGINXCommon []byte
//go:embed nginx/gateway.conf
NGINXGateway []byte
//go:embed nginx/web.conf
NGINXWeb []byte
//go:embed nginx/client.conf
NGINXClient []byte
//go:embed nginx/nginx.conf
NGINXMain []byte
//go:embed nginx/user.conf
NGINXUser []byte
//go:embed nginx/client_pkg.conf
NGINXClientPKG string
)

13
pkg/resource/ssl/ca.crt Normal file
View File

@@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIB7zCCAZWgAwIBAgIUZvlcdld7K4q8gQ1iS7DCv8dAuAcwCgYIKoZIzj0EAwIw
TTELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzESMBAGA1UEBwwJR3Vh
bmd6aG91MRYwFAYDVQQKDA1ZaVpoaSBSb290IENBMB4XDTIyMTIwMjEwMTMxNVoX
DTMyMTEyOTEwMTMxNVowTTELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9u
ZzESMBAGA1UEBwwJR3Vhbmd6aG91MRYwFAYDVQQKDA1ZaVpoaSBSb290IENBMFkw
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAu+EOUpD8tO1KA6MXkvjfo3iD5dEEezY
kRL+sM9uCB2jKDcMiq2QNa/GE1NRbgQ04fpwVcvJkeMKrlEQWdqCEKNTMFEwHQYD
VR0OBBYEFCJhDR/vXpS4Mlo1y5sk/XWloNR6MB8GA1UdIwQYMBaAFCJhDR/vXpS4
Mlo1y5sk/XWloNR6MA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIg
IPCDJQOAvuR1LcTc/1G0nmcZLJA8mk7PSpzc7dp7kO4CIQC41hyfKwEYkfvxthLR
f4vSt3qR8cz4cBaWaSJ9sZRHoQ==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBejCCASACAQEwCgYIKoZIzj0EAwIwTTELMAkGA1UEBhMCQ04xEjAQBgNVBAgM
CUd1YW5nZG9uZzESMBAGA1UEBwwJR3Vhbmd6aG91MRYwFAYDVQQKDA1ZaVpoaSBS
b290IENBMB4XDTIyMDcyNjA3MDUxOFoXDTMyMDcyMzA3MDUxOFowRTELMAkGA1UE
BhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzESMBAGA1UEBwwJR3Vhbmd6aG91MQ4w
DAYDVQQKDAVZaVpoaTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD01GbM70jdF
hKz1Mc8ZdZ0PbseeZFO6X5hRR6MpOXl0KKIhqmEFb6vIUk7putv2NPp+1ifLXx2+
4Gg6X7VP53QwCgYIKoZIzj0EAwIDSAAwRQIhAKCf/+9sG5Y2muvjAS92kRd3Cxwa
1JkEGsiSnc3KtuD9AiAPAc1yuZaQuv8oTct1xJZpPE3vgVbKhU/mP+O3dDIr2Q==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICfD/l/g3ERF2gwJwQhC2bmIzeUWlyzizYpwlw9y19/1oAoGCCqGSM49
AwEHoUQDQgAEPTUZszvSN0WErPUxzxl1nQ9ux55kU7pfmFFHoyk5eXQooiGqYQVv
q8hSTum62/Y0+n7WJ8tfHb7gaDpftU/ndA==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----

View File

@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBvDCCAWOgAwIBAgIBATAKBggqhkjOPQQDAjBNMQswCQYDVQQGEwJDTjESMBAG
A1UECAwJR3Vhbmdkb25nMRIwEAYDVQQHDAlHdWFuZ3pob3UxFjAUBgNVBAoMDVlp
WmhpIFJvb3QgQ0EwHhcNMjMxMjEwMTU0MzU0WhcNMzMxMjA3MTU0MzU0WjBhMQsw
CQYDVQQGEwJDTjESMBAGA1UECAwJR3Vhbmdkb25nMRIwEAYDVQQHDAlHdWFuZ3po
b3UxDjAMBgNVBAoMBVlpWmhpMRowGAYDVQQDDBFtcXR0Lnlpemhpc2VjLmNvbTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABGOtQocjlPkUHD5opIt/V4tIQw0QSjJL
G9q+OkUEWil40ZNc9au3zbl78lZfZqiT92+s4qWSl0LNoLQEJ06WXxOjIDAeMBwG
A1UdEQQVMBOCEW1xdHQueWl6aGlzZWMuY29tMAoGCCqGSM49BAMCA0cAMEQCICOs
mhP29LIAuJJtYYsMwi21oGZlhI5pXVXu/R0VbLpDAiBvYkEq3A9UA5jRYwq2YXo4
fEbcPuEWU0LFZ6RN4dTebA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICWabo6fxyjFq2CgDjLCvecNWLoNPWVxL5oM3ugG08NxoAoGCCqGSM49
AwEHoUQDQgAEY61ChyOU+RQcPmiki39Xi0hDDRBKMksb2r46RQRaKXjRk1z1q7fN
uXvyVl9mqJP3b6zipZKXQs2gtAQnTpZfEw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBvTCCAWOgAwIBAgIBATAKBggqhkjOPQQDAjBNMQswCQYDVQQGEwJDTjESMBAG
A1UECAwJR3Vhbmdkb25nMRIwEAYDVQQHDAlHdWFuZ3pob3UxFjAUBgNVBAoMDVlp
WmhpIFJvb3QgQ0EwHhcNMjMxMjEwMTUyNDM3WhcNMzMxMjA3MTUyNDM3WjBhMQsw
CQYDVQQGEwJDTjESMBAGA1UECAwJR3Vhbmdkb25nMRIwEAYDVQQHDAlHdWFuZ3po
b3UxDjAMBgNVBAoMBVlpWmhpMRowGAYDVQQDDBFtcXR0Lnlpemhpc2VjLmNvbTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABPKwi96F+XaxPzOhkDkTCvcT/150GYJo
ExTvEFf0xfnmutDkkQw8RoQOe8AgExsxwXy75QgE5d3i7Igh4EJN2MSjIDAeMBwG
A1UdEQQVMBOCEW1xdHQueWl6aGlzZWMuY29tMAoGCCqGSM49BAMCA0gAMEUCICmm
3xfwGmdY8TOUFYJsTu1QyWnhLIl1zRPSEgKprPNEAiEAnaBn8Oq1qdx6K2PKAaT/
8Cad6JPsoBTxqW/QLYmp89o=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIPKnMTtmsu90SKIWpeW9OaxyKntsHDvGoP/JSIM/zMKHoAoGCCqGSM49
AwEHoUQDQgAE8rCL3oX5drE/M6GQORMK9xP/XnQZgmgTFO8QV/TF+ea60OSRDDxG
hA57wCATGzHBfLvlCATl3eLsiCHgQk3YxA==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBejCCASACAQEwCgYIKoZIzj0EAwIwTTELMAkGA1UEBhMCQ04xEjAQBgNVBAgM
CUd1YW5nZG9uZzESMBAGA1UEBwwJR3Vhbmd6aG91MRYwFAYDVQQKDA1ZaVpoaSBS
b290IENBMB4XDTIyMTIwMjEwMTMxNloXDTMyMTEyOTEwMTMxNlowRTELMAkGA1UE
BhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzESMBAGA1UEBwwJR3Vhbmd6aG91MQ4w
DAYDVQQKDAVZaVpoaTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNPLXfr++g44
7TROHOLF8BIoYM9UTzGCOTA4gDoAgYXkVs077fKLFAJKJH72mpMOw0laZUElmcCw
sBKKWLshyHQwCgYIKoZIzj0EAwIDSAAwRQIhALH9PCuZtfHAMZuDEanJOC7hf3BC
wPq2CXKG7lzHASLzAiAT6C/rlyN9IYYNiy0RXFsgDtsQQJy9RH6cPyvk/xh6eA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDKTGdd21zcq4j2rbvTX4G7anjzBJdOgkvSu7uvj0oBZoAoGCCqGSM49
AwEHoUQDQgAE08td+v76DjjtNE4c4sXwEihgz1RPMYI5MDiAOgCBheRWzTvt8osU
Akokfvaakw7DSVplQSWZwLCwEopYuyHIdA==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBeTCCASACAQEwCgYIKoZIzj0EAwIwTTELMAkGA1UEBhMCQ04xEjAQBgNVBAgM
CUd1YW5nZG9uZzESMBAGA1UEBwwJR3Vhbmd6aG91MRYwFAYDVQQKDA1ZaVpoaSBS
b290IENBMB4XDTIyMDgyMTA4MjEzMloXDTMyMDgxODA4MjEzMlowRTELMAkGA1UE
BhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzESMBAGA1UEBwwJR3Vhbmd6aG91MQ4w
DAYDVQQKDAVZaVpoaTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOI4Fy+rT8ca
AuW390kWqhfqtv1a9+KISsESg/tuUiNYile3Tl7ndMzZmBJDlIOGXt8KcFc8t7kU
Lx/nUF3g4rcwCgYIKoZIzj0EAwIDRwAwRAIgFc6wgYlcdUoFtfZDEeW8a2xloUA3
HaPnkqCPZlKzwlACIARWSaWA64UTC+et/n3LZY9ZGWRatzxhhALToM33pewH
-----END CERTIFICATE-----

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEu0E+YkAH+Qg7yuOpqh2w56JOgjzSuxqZl7uFQkpLAVoAoGCCqGSM49
AwEHoUQDQgAE4jgXL6tPxxoC5bf3SRaqF+q2/Vr34ohKwRKD+25SI1iKV7dOXud0
zNmYEkOUg4Ze3wpwVzy3uRQvH+dQXeDitw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,71 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-deployment
namespace: hsv2
spec:
replicas: %d
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: client
containers:
- name: client
image: hub.yizhisec.com/hybridscope/client_server:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: ssl-pub-crt
mountPath: /etc/yizhisec/license/pub_key
subPath: pub_key
readOnly: true
- name: config-volume
mountPath: /etc/client_server/config.yml
subPath: config.yml
readOnly: true
volumes:
- name: ssl-pub-crt
configMap:
name: ssl-pub-crt
items:
- key: pub_key
path: pub_key
- name: config-volume
configMap:
name: config-client
items:
- key: config.yml
path: config.yml
---
apiVersion: v1
kind: Service
metadata:
name: client-service
namespace: hsv2
spec:
selector:
app: client
ports:
- protocol: TCP
name: web
port: 9129
targetPort: 9129
- protocol: TCP
name: web2
port: 9024
targetPort: 9024
- protocol: TCP
name: web-message
port: 9025
targetPort: 9025
type: ClusterIP

View File

@@ -0,0 +1,42 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: front-admin-deployment
namespace: hsv2
spec:
replicas: %d
selector:
matchLabels:
app: front-admin
template:
metadata:
labels:
app: front-admin
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: front-admin
containers:
- name: front-admin
image: hub.yizhisec.com/build/hybirdscope/front/admin:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: front-admin-service
namespace: hsv2
spec:
selector:
app: front-admin
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP

View File

@@ -0,0 +1,42 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: front-user-deployment
namespace: hsv2
spec:
replicas: %d
selector:
matchLabels:
app: front-user
template:
metadata:
labels:
app: front-user
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: front-user
containers:
- name: front-user
image: hub.yizhisec.com/hybridscope/v2/front-user:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: front-user-service
namespace: hsv2
spec:
selector:
app: front-user
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP

View File

@@ -0,0 +1,83 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway-deployment
namespace: hsv2
spec:
replicas: %d
selector:
matchLabels:
app: gateway
template:
metadata:
labels:
app: gateway
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: gateway
containers:
- name: gateway
image: hub.yizhisec.com/hybridscope/gateway_controller:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: config-volume
mountPath: /etc/gateway_controller/config.yml
subPath: config.yml
readOnly: true
- name: config-token
mountPath: /etc/yizhisec/token
subPath: token
readOnly: true
- name: ssl-client-crt
mountPath: /yizhisec/ssl/client.crt
subPath: client.crt
readOnly: true
- name: ssl-client-key
mountPath: /yizhisec/ssl/client.key
subPath: client.key
readOnly: true
volumes:
- name: config-volume
configMap:
name: config-gateway
items:
- key: config.yml
path: config.yml
- name: config-token
configMap:
name: config-token
items:
- key: token
path: token
- name: ssl-client-crt
configMap:
name: ssl-client-crt
items:
- key: client.crt
path: client.crt
- name: ssl-client-key
configMap:
name: ssl-client-key
items:
- key: client.key
path: client.key
---
apiVersion: v1
kind: Service
metadata:
name: gateway-service
namespace: hsv2
spec:
selector:
app: gateway
ports:
- protocol: TCP
name: gateway-api
port: 9012
targetPort: 9012
type: ClusterIP

Some files were not shown because too many files have changed in this diff Show More