238 lines
6.7 KiB
Go
238 lines
6.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/docs"
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/config"
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/grpcsrv/commander"
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/handlers"
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/storage"
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/proto"
|
|
"github.com/gin-gonic/gin"
|
|
swaggerFiles "github.com/swaggo/files"
|
|
ginSwagger "github.com/swaggo/gin-swagger"
|
|
"golang.org/x/sync/errgroup"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
)
|
|
|
|
// @securityDefinitions.apikey Bearer
|
|
// @in header
|
|
// @name Authorization
|
|
// @description Type "Bearer" followed by a space and the JWT token.
|
|
|
|
func main() {
|
|
cfg_path, ok := os.LookupEnv("CONFIG_FILE")
|
|
if !ok {
|
|
cfg_path = "/etc/hellreign/config.yml"
|
|
}
|
|
cfg, err := config.ImportSettings(cfg_path)
|
|
if err != nil {
|
|
log.Fatalf("Err loading config: %v", err)
|
|
}
|
|
|
|
db, err := storage.Open(cfg.Database.Token_db)
|
|
if err != nil {
|
|
log.Fatalf("Err opening database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
h := handlers.New(db)
|
|
|
|
// Initialize registration tokens table
|
|
if err := h.Repo.InitRegistrationTokens(); err != nil {
|
|
log.Printf("Warning: failed to initialize registration tokens table: %v", err)
|
|
}
|
|
|
|
// Initialize jobs table
|
|
jobRepo := repository.NewJobRepository(db)
|
|
if err := jobRepo.Init(context.Background()); err != nil {
|
|
log.Printf("Warning: failed to initialize jobs table: %v", err)
|
|
}
|
|
|
|
cmdr := commander.New(jobRepo)
|
|
|
|
agents := handlers.NewAgentsGroup(h, cmdr)
|
|
auth := handlers.AuthGroup{Handlers: h}
|
|
agentReg := handlers.NewAgentRegistrationGroup(h)
|
|
|
|
// Create admin user from config if not exists
|
|
if cfg.Admin.Admin_login != "" && cfg.Admin.Admin_password != "" {
|
|
if !h.Repo.ExistsByLogin(cfg.Admin.Admin_login) {
|
|
_, err := h.Repo.CreateToken(repository.TokenCreate{
|
|
Name: cfg.Admin.Admin_name,
|
|
LastName: cfg.Admin.Admin_last_name,
|
|
Login: cfg.Admin.Admin_login,
|
|
Password: cfg.Admin.Admin_password,
|
|
PermissionView: true,
|
|
PermissionManage: true,
|
|
PermissionAdmin: true,
|
|
})
|
|
if err != nil {
|
|
log.Printf("Warning: failed to create admin user: %v", err)
|
|
} else {
|
|
log.Println("Admin user created from config")
|
|
}
|
|
}
|
|
}
|
|
|
|
router := gin.Default()
|
|
docs.SwaggerInfo.BasePath = "/api/v1"
|
|
docs.SwaggerInfo.Title = "HellreigN"
|
|
docs.SwaggerInfo.Version = "1.0"
|
|
docs.SwaggerInfo.Description = "API for HellreigN"
|
|
docs.SwaggerInfo.Schemes = []string{"http"}
|
|
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
|
|
|
v1 := router.Group("/api/v1")
|
|
{
|
|
// Auth routes (public)
|
|
authGroup := v1.Group("/auth")
|
|
{
|
|
authGroup.POST("/login", auth.Login)
|
|
}
|
|
|
|
// Auth token management (requires auth)
|
|
authTokenGroup := v1.Group("/auth")
|
|
authTokenGroup.Use(auth.AuthMiddleware())
|
|
{
|
|
authTokenGroup.POST("/token", handlers.RequireAdmin(), auth.CreateToken)
|
|
authTokenGroup.GET("/validate", auth.ValidateToken)
|
|
authTokenGroup.GET("/tokens", handlers.RequireAdmin(), auth.ListTokens)
|
|
authTokenGroup.DELETE("/token", auth.DeleteMyToken)
|
|
authTokenGroup.DELETE("/tokens/:login", handlers.RequireAdmin(), auth.DeleteToken)
|
|
}
|
|
|
|
// Agents (requires manage_agent permission)
|
|
agentsGroup := v1.Group("/agents")
|
|
agentsGroup.Use(auth.AuthMiddleware(), handlers.RequireManageAgent())
|
|
{
|
|
agentsGroup.GET("", agents.List)
|
|
}
|
|
|
|
// Agent registration
|
|
agentRegGroup := v1.Group("/agents")
|
|
{
|
|
agentRegGroup.POST("/register", agentReg.Register)
|
|
}
|
|
agentRegTokenGroup := v1.Group("/agents")
|
|
agentRegTokenGroup.Use(auth.AuthMiddleware(), handlers.RequireManageAgent())
|
|
{
|
|
agentRegTokenGroup.POST("/register-token", agentReg.CreateRegistrationToken)
|
|
}
|
|
|
|
// Logs (requires view permission)
|
|
logsGroup := v1.Group("/logs")
|
|
logsGroup.Use(auth.AuthMiddleware(), handlers.RequireView())
|
|
{
|
|
if cfg.Database.Clickhouse_host != "" {
|
|
chConn, err := storage.OpenClickHouse(storage.ClickHouseConfig{
|
|
Host: cfg.Database.Clickhouse_host,
|
|
User: cfg.Database.Clickhouse_user,
|
|
Password: cfg.Database.Clickhouse_password,
|
|
Database: cfg.Database.Clickhouse_database,
|
|
})
|
|
if err != nil {
|
|
log.Printf("Warning: ClickHouse connection failed: %v", err)
|
|
} else {
|
|
defer chConn.Close()
|
|
|
|
logRepo := repository.NewLogRepository(chConn)
|
|
if err := logRepo.Init(context.Background()); err != nil {
|
|
log.Printf("Warning: Failed to initialize logs table: %v", err)
|
|
}
|
|
|
|
logHandlers := handlers.NewLogHandlers(logRepo)
|
|
logsGroup.POST("", logHandlers.Insert)
|
|
logsGroup.POST("/batch", logHandlers.InsertBatch)
|
|
logsGroup.GET("", logHandlers.Search)
|
|
logsGroup.GET("/services", logHandlers.GetServices)
|
|
logsGroup.GET("/agents", logHandlers.GetAgents)
|
|
logsGroup.GET("/levels", logHandlers.GetLevels)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start gRPC server with mTLS in background
|
|
grpcPort := os.Getenv("GRPC_PORT")
|
|
if grpcPort == "" {
|
|
grpcPort = "9001"
|
|
}
|
|
|
|
certDir := os.Getenv("SSL_CERT_DIR")
|
|
if certDir == "" {
|
|
certDir = "/var/lib/hellreign/ssl"
|
|
}
|
|
|
|
certFile := certDir + "/server.crt"
|
|
keyFile := certDir + "/server.key"
|
|
caFile := certDir + "/ca.crt"
|
|
|
|
// Load server cert
|
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load server cert: %v", err)
|
|
}
|
|
|
|
// Load CA cert for client verification
|
|
caCert, err := os.ReadFile(caFile)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load CA cert: %v", err)
|
|
}
|
|
caCertPool := x509.NewCertPool()
|
|
caCertPool.AppendCertsFromPEM(caCert)
|
|
|
|
tlsConfig := &tls.Config{
|
|
Certificates: []tls.Certificate{cert},
|
|
ClientCAs: caCertPool,
|
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
|
MinVersion: tls.VersionTLS12,
|
|
}
|
|
|
|
grpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)))
|
|
proto.RegisterCommanderServer(grpcServer, cmdr)
|
|
|
|
lis, err := net.Listen("tcp", ":"+grpcPort)
|
|
if err != nil {
|
|
log.Fatalf("Failed to listen on gRPC port %s: %v", grpcPort, err)
|
|
}
|
|
|
|
g, ctx := errgroup.WithContext(context.Background())
|
|
|
|
g.Go(func() error {
|
|
log.Printf("gRPC server starting on port %s with mTLS", grpcPort)
|
|
errCh := make(chan error, 1)
|
|
go func() { errCh <- grpcServer.Serve(lis) }()
|
|
select {
|
|
case err := <-errCh:
|
|
return err
|
|
case <-ctx.Done():
|
|
grpcServer.GracefulStop()
|
|
return nil
|
|
}
|
|
})
|
|
|
|
g.Go(func() error {
|
|
log.Printf("HTTP server starting on :8080")
|
|
errCh := make(chan error, 1)
|
|
go func() { errCh <- router.Run(":8080") }()
|
|
select {
|
|
case err := <-errCh:
|
|
return err
|
|
case <-ctx.Done():
|
|
return nil
|
|
}
|
|
})
|
|
|
|
if err := g.Wait(); err != nil {
|
|
log.Fatalf("Server error: %v", err)
|
|
}
|
|
} |