import sources
This commit is contained in:
162
internal/config/config.go
Normal file
162
internal/config/config.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"certmanager/pkg/client"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultUsername = "worker"
|
||||
defaultPassword = "worker"
|
||||
defaultHostname = "localhost"
|
||||
|
||||
configFilename = "certmanagerd.yaml"
|
||||
logFilename = "certmanager.log"
|
||||
pidFilename = "certmanager.pid"
|
||||
)
|
||||
|
||||
var (
|
||||
buildVersion = "NONE"
|
||||
)
|
||||
|
||||
type WserviceConfig struct {
|
||||
PortNum int `json:"port" yaml:"port"`
|
||||
}
|
||||
|
||||
type GserviceConfig struct {
|
||||
PortNum int `json:"port" yaml:"port"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username" yaml:"username"`
|
||||
Password string `json:"password" yaml:"password"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Wservice WserviceConfig `json:"wservice" yaml:"wservice"`
|
||||
Gservice GserviceConfig `json:"gservice" yaml:"gservice"`
|
||||
Auths []AuthConfig `json:"auths" yaml:"auths"`
|
||||
Hostname string `json:"hostname" yaml:"hostname"`
|
||||
Debug bool `json:"debug" yaml:"debug"`
|
||||
Build string `json:"build" yaml:"build"`
|
||||
LogPath string `json:"logfile" yaml:"logfile"`
|
||||
RunPath string `json:"runfile" yaml:"runfile"`
|
||||
DataDir string `json:"datadir" yaml:"datadir"`
|
||||
Daemon bool `json:"daemon" yaml:"daemon"`
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
conf := &Config{
|
||||
Gservice: GserviceConfig{
|
||||
PortNum: client.DefaultGrpcPort,
|
||||
},
|
||||
|
||||
Wservice: WserviceConfig{
|
||||
PortNum: client.DefaultWrpcPort,
|
||||
},
|
||||
|
||||
Auths: make([]AuthConfig, 0),
|
||||
|
||||
DataDir: datadirPath,
|
||||
Debug: false,
|
||||
Hostname: defaultHostname,
|
||||
Build: buildVersion,
|
||||
Daemon: true,
|
||||
}
|
||||
|
||||
conf.LogPath = filepath.Join(logdirPath, logFilename)
|
||||
conf.RunPath = filepath.Join(rundirPath, pidFilename)
|
||||
|
||||
return conf
|
||||
}
|
||||
|
||||
func (conf *Config) ReadFile() error {
|
||||
var err error
|
||||
|
||||
configPath := filepath.Join(confdirPath, configFilename)
|
||||
confBytes, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = yaml.Unmarshal(confBytes, conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf.Normalize()
|
||||
err = conf.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conf *Config) ReadEnv() error {
|
||||
var err error
|
||||
return err
|
||||
}
|
||||
|
||||
func (conf *Config) ReadOpts() error {
|
||||
var err error
|
||||
|
||||
exeName := filepath.Base(os.Args[0])
|
||||
|
||||
flag.IntVar(&conf.Wservice.PortNum, "wport", conf.Wservice.PortNum, "wrpc listen port")
|
||||
flag.IntVar(&conf.Gservice.PortNum, "gport", conf.Gservice.PortNum, "grpc listen port")
|
||||
flag.BoolVar(&conf.Daemon, "daemon", conf.Daemon, "run as daemon")
|
||||
flag.BoolVar(&conf.Debug, "debug", conf.Debug, "on debug mode")
|
||||
|
||||
help := func() {
|
||||
fmt.Println("")
|
||||
fmt.Printf("Usage: %s [option]\n", exeName)
|
||||
fmt.Println("")
|
||||
fmt.Println("Options:")
|
||||
flag.PrintDefaults()
|
||||
fmt.Println("")
|
||||
}
|
||||
flag.Usage = help
|
||||
flag.Parse()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (conf *Config) Yaml() (string, error) {
|
||||
var err error
|
||||
var res string
|
||||
yamlBytes, err := yaml.Marshal(conf)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
res = string(yamlBytes)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (conf *Config) Normalize() {
|
||||
if conf.Gservice.PortNum == 0 {
|
||||
conf.Gservice.PortNum = client.DefaultGrpcPort
|
||||
}
|
||||
if conf.Wservice.PortNum == 0 {
|
||||
conf.Wservice.PortNum = client.DefaultWrpcPort
|
||||
}
|
||||
}
|
||||
|
||||
func (conf *Config) Validate() error {
|
||||
var err []error
|
||||
|
||||
for i := range conf.Auths {
|
||||
if conf.Auths[i].Username == "" {
|
||||
err = append(err, errors.New("Username must be set"))
|
||||
}
|
||||
if conf.Auths[i].Password == "" {
|
||||
err = append(err, errors.New("Password must be set"))
|
||||
}
|
||||
}
|
||||
return errors.Join(err...)
|
||||
}
|
||||
9
internal/config/path.go
Normal file
9
internal/config/path.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
const (
|
||||
confdirPath = "/home/ziggi/projects/certman/etc/certmanager"
|
||||
rundirPath = "/home/ziggi/projects/certman/tmp.run"
|
||||
logdirPath = "/home/ziggi/projects/certman/tmp.log"
|
||||
datadirPath = "/home/ziggi/projects/certman/tmp.data"
|
||||
)
|
||||
|
||||
9
internal/config/path.go.in
Normal file
9
internal/config/path.go.in
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
const (
|
||||
confdirPath = "@srv_confdir@"
|
||||
rundirPath = "@srv_rundir@"
|
||||
logdirPath = "@srv_logdir@"
|
||||
datadirPath = "@srv_datadir@"
|
||||
)
|
||||
|
||||
70
internal/database/database.go
Normal file
70
internal/database/database.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"certmanager/pkg/logger"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
const schema = `
|
||||
--- DROP TABLE IF EXISTS manifests;
|
||||
CREATE TABLE IF NOT EXISTS manifests (
|
||||
id VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
reference VARCHAR(255) NOT NULL,
|
||||
contentType VARCHAR(255) NOT NULL,
|
||||
payload VARCHAR(4096) NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS manifest_index
|
||||
ON manifests(name, reference);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS blobs (
|
||||
id VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
digest VARCHAR(255) NOT NULL,
|
||||
used INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS blobs_index
|
||||
ON blobs(name, digest);
|
||||
|
||||
|
||||
`
|
||||
|
||||
type Database struct {
|
||||
datapath string
|
||||
db *sqlx.DB
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewDatabase(datapath string) (*Database, error) {
|
||||
var err error
|
||||
db := &Database{
|
||||
datapath: datapath,
|
||||
}
|
||||
db.log = logger.NewLogger("database")
|
||||
return db, err
|
||||
|
||||
}
|
||||
|
||||
func (db *Database) InitDatabase() error {
|
||||
var err error
|
||||
dbPath := filepath.Join(db.datapath, "certmanager.db")
|
||||
db.db, err = sqlx.Open("sqlite3", dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = db.db.Ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.db.Exec(schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
3
internal/descriptor/descriptor.go
Normal file
3
internal/descriptor/descriptor.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package descriptor
|
||||
|
||||
|
||||
31
internal/grpc/handler/handler.go
Normal file
31
internal/grpc/handler/handler.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"certmanager/api/certmanagercontrol"
|
||||
"certmanager/internal/logic"
|
||||
"certmanager/pkg/logger"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type HandlerConfig struct {
|
||||
Logic *logic.Logic
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
certmanagercontrol.UnimplementedControlServer
|
||||
lg *logic.Logic
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewHandler(conf *HandlerConfig) *Handler {
|
||||
hand := Handler{
|
||||
lg: conf.Logic,
|
||||
}
|
||||
hand.log = logger.NewLogger("ghandler")
|
||||
return &hand
|
||||
}
|
||||
|
||||
func (hand *Handler) Register(gsrv *grpc.Server) {
|
||||
certmanagercontrol.RegisterControlServer(gsrv, hand)
|
||||
}
|
||||
14
internal/grpc/handler/status.go
Normal file
14
internal/grpc/handler/status.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"certmanager/api/certmanagercontrol"
|
||||
)
|
||||
|
||||
func (hand *Handler) GetStatus(ctx context.Context, req *certmanagercontrol.GetStatusParams) (*certmanagercontrol.GetStatusResult, error) {
|
||||
var err error
|
||||
hand.log.Debugf("Handle getStatus request")
|
||||
res, err := hand.lg.GetStatus(ctx, req)
|
||||
return res, err
|
||||
}
|
||||
137
internal/grpc/service/service.go
Normal file
137
internal/grpc/service/service.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"certmanager/internal/grpc/handler"
|
||||
"certmanager/internal/logic"
|
||||
"certmanager/pkg/logger"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type ServiceConfig struct {
|
||||
Handler *handler.Handler
|
||||
Logic *logic.Logic
|
||||
PortNum int
|
||||
Hostname string
|
||||
X509Cert []byte
|
||||
X509Key []byte
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
gsrv *grpc.Server
|
||||
hand *handler.Handler
|
||||
lg *logic.Logic
|
||||
log *logger.Logger
|
||||
portnum int
|
||||
hostname string
|
||||
|
||||
username string
|
||||
password string
|
||||
x509Cert []byte
|
||||
x509Key []byte
|
||||
}
|
||||
|
||||
func NewService(conf *ServiceConfig) *Service {
|
||||
svc := Service{
|
||||
hand: conf.Handler,
|
||||
lg: conf.Logic,
|
||||
portnum: conf.PortNum,
|
||||
hostname: conf.Hostname,
|
||||
x509Cert: conf.X509Cert,
|
||||
x509Key: conf.X509Key,
|
||||
}
|
||||
svc.log = logger.NewLogger("gservice")
|
||||
|
||||
return &svc
|
||||
}
|
||||
|
||||
func (svc *Service) Run() error {
|
||||
var err error
|
||||
svc.log.Infof("Service run")
|
||||
|
||||
listenSpec := fmt.Sprintf(":%d", svc.portnum)
|
||||
listener, err := net.Listen("tcp", listenSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(svc.x509Cert, svc.x509Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsConfig := tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
tlsCredentials := credentials.NewTLS(&tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gsrvOpts := []grpc.ServerOption{
|
||||
grpc.Creds(tlsCredentials),
|
||||
grpc.UnaryInterceptor(svc.authInterceptor),
|
||||
}
|
||||
svc.gsrv = grpc.NewServer(gsrvOpts...)
|
||||
|
||||
svc.hand.Register(svc.gsrv)
|
||||
|
||||
svc.log.Infof("Service listening at %v", listener.Addr())
|
||||
err = svc.gsrv.Serve(listener)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) authInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
svc.log.Debugf("Called unary interceptor with method: %v", info.FullMethod)
|
||||
meta, _ := metadata.FromIncomingContext(ctx)
|
||||
svc.log.Debugf("Reqest username: %s", meta["username"])
|
||||
svc.log.Debugf("Reqest password: %s", meta["password"])
|
||||
usernameArr := meta["username"]
|
||||
passwordArr := meta["password"]
|
||||
if len(usernameArr) == 0 || len(passwordArr) == 0 {
|
||||
err := status.Errorf(codes.PermissionDenied, "Empty auth data")
|
||||
return nil, err
|
||||
}
|
||||
username := meta["username"][0]
|
||||
password := meta["password"][0]
|
||||
if !svc.lg.ValidateUser(username, password) {
|
||||
err := status.Errorf(codes.PermissionDenied, "Wrong auth data")
|
||||
return nil, err
|
||||
}
|
||||
reqBinary, err := json.Marshal(req)
|
||||
if err == nil {
|
||||
svc.log.Debugf("Request: %s", string(reqBinary))
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
func (svc *Service) logInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
var err error
|
||||
svc.log.Debugf("Called unary interceptor with method: %v", info.FullMethod)
|
||||
|
||||
reqBinary, err := json.Marshal(req)
|
||||
if err == nil {
|
||||
svc.log.Debugf("Request: %f", string(reqBinary))
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
func (svc *Service) Stop() {
|
||||
svc.log.Infof("Stopping service")
|
||||
svc.gsrv.GracefulStop()
|
||||
}
|
||||
10
internal/logic/auth.go
Normal file
10
internal/logic/auth.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package logic
|
||||
|
||||
func (lg *Logic) ValidateUser(username, password string) bool {
|
||||
for i := range lg.auths {
|
||||
if username == lg.auths[i].Username && password == lg.auths[i].Password {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
28
internal/logic/logic.go
Normal file
28
internal/logic/logic.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"certmanager/internal/config"
|
||||
"certmanager/internal/database"
|
||||
"certmanager/pkg/logger"
|
||||
)
|
||||
|
||||
type LogicConfig struct {
|
||||
Database *database.Database
|
||||
Auths []config.AuthConfig
|
||||
}
|
||||
|
||||
type Logic struct {
|
||||
auths []config.AuthConfig
|
||||
log *logger.Logger
|
||||
db *database.Database
|
||||
}
|
||||
|
||||
func NewLogic(conf *LogicConfig) (*Logic, error) {
|
||||
var err error
|
||||
lg := &Logic{
|
||||
db: conf.Database,
|
||||
auths: conf.Auths,
|
||||
}
|
||||
lg.log = logger.NewLogger("logic")
|
||||
return lg, err
|
||||
}
|
||||
15
internal/logic/status.go
Normal file
15
internal/logic/status.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"certmanager/api/certmanagercontrol"
|
||||
)
|
||||
|
||||
func (lg *Logic) GetStatus(ctx context.Context, nReq *certmanagercontrol.GetStatusParams) (*certmanagercontrol.GetStatusResult, error) {
|
||||
var err error
|
||||
nRes := &certmanagercontrol.GetStatusResult{
|
||||
Message: "Hello",
|
||||
}
|
||||
return nRes, err
|
||||
}
|
||||
294
internal/server/server.go
Normal file
294
internal/server/server.go
Normal file
@@ -0,0 +1,294 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"certmanager/internal/config"
|
||||
"certmanager/internal/database"
|
||||
"certmanager/internal/logic"
|
||||
"certmanager/pkg/aux509"
|
||||
"certmanager/pkg/logger"
|
||||
|
||||
ghandler "certmanager/internal/grpc/handler"
|
||||
gservice "certmanager/internal/grpc/service"
|
||||
whandler "certmanager/internal/wrpc/handler"
|
||||
wservice "certmanager/internal/wrpc/service"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
conf *config.Config
|
||||
lg *logic.Logic
|
||||
wsvc *wservice.Service
|
||||
whand *whandler.Handler
|
||||
gsvc *gservice.Service
|
||||
ghand *ghandler.Handler
|
||||
log *logger.Logger
|
||||
db *database.Database
|
||||
x509cert []byte
|
||||
x509key []byte
|
||||
x509caCert []byte
|
||||
x509caKey []byte
|
||||
}
|
||||
|
||||
func NewServer() (*Server, error) {
|
||||
var err error
|
||||
srv := &Server{}
|
||||
srv.log = logger.NewLogger("server")
|
||||
return srv, err
|
||||
}
|
||||
|
||||
func (srv *Server) Configure() error {
|
||||
var err error
|
||||
srv.conf = config.NewConfig()
|
||||
|
||||
err = srv.conf.ReadFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = srv.conf.ReadEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = srv.conf.ReadOpts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) Build() error {
|
||||
var err error
|
||||
srv.log.Infof("Build server")
|
||||
|
||||
// Create X509 certs
|
||||
srv.x509caCert, srv.x509caKey, err = aux509.CreateX509CACert(srv.conf.Hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.x509cert, srv.x509key, err = aux509.CreateX509Cert(srv.conf.Hostname, srv.x509caKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create database
|
||||
db, err := database.NewDatabase(srv.conf.DataDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.db = db
|
||||
|
||||
// Create logic
|
||||
logicConfig := &logic.LogicConfig{
|
||||
Auths: srv.conf.Auths,
|
||||
Database: srv.db,
|
||||
}
|
||||
srv.lg, err = logic.NewLogic(logicConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create whandler
|
||||
whandlerConfig := &whandler.HandlerConfig{
|
||||
Logic: srv.lg,
|
||||
}
|
||||
srv.whand, err = whandler.NewHandler(whandlerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create wservice
|
||||
wserviceConfig := &wservice.ServiceConfig{
|
||||
PortNum: srv.conf.Wservice.PortNum,
|
||||
Hostname: srv.conf.Hostname,
|
||||
Handler: srv.whand,
|
||||
X509Cert: srv.x509cert,
|
||||
X509Key: srv.x509key,
|
||||
}
|
||||
srv.wsvc, err = wservice.NewService(wserviceConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = srv.wsvc.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create ghandler
|
||||
ghandlerConfig := &ghandler.HandlerConfig{
|
||||
Logic: srv.lg,
|
||||
}
|
||||
srv.ghand = ghandler.NewHandler(ghandlerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create gservice
|
||||
gserviceConfig := &gservice.ServiceConfig{
|
||||
PortNum: srv.conf.Gservice.PortNum,
|
||||
Hostname: srv.conf.Hostname,
|
||||
Handler: srv.ghand,
|
||||
Logic: srv.lg,
|
||||
X509Cert: srv.x509cert,
|
||||
X509Key: srv.x509key,
|
||||
}
|
||||
srv.gsvc = gservice.NewService(gserviceConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) Run() error {
|
||||
var err error
|
||||
// Log configuration
|
||||
yamlConfig, err := srv.conf.Yaml()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.log.Debugf("Server configuration:\n%s\n", yamlConfig)
|
||||
|
||||
// Show current user
|
||||
currUser, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.log.Infof("Running server as user %s", currUser.Username)
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
gdone := make(chan error, 1)
|
||||
wdone := make(chan error, 1)
|
||||
|
||||
// Run wservice
|
||||
startWservice := func(svc *wservice.Service, done chan error) {
|
||||
err = svc.Run()
|
||||
if err != nil {
|
||||
srv.log.Errorf("Service error: %v", err)
|
||||
done <- err
|
||||
}
|
||||
}
|
||||
go startWservice(srv.wsvc, wdone)
|
||||
|
||||
// Run gservice
|
||||
startGservice := func(svc *gservice.Service, done chan error) {
|
||||
err = svc.Run()
|
||||
if err != nil {
|
||||
srv.log.Errorf("Service error: %v", err)
|
||||
done <- err
|
||||
}
|
||||
}
|
||||
go startGservice(srv.gsvc, gdone)
|
||||
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
var signal os.Signal
|
||||
|
||||
select {
|
||||
case signal = <-sigs:
|
||||
srv.log.Infof("Services stopped by signal: %v", signal)
|
||||
srv.wsvc.Stop()
|
||||
srv.gsvc.Stop()
|
||||
case err = <-gdone:
|
||||
srv.log.Infof("Service stopped by gservice error: %v", err)
|
||||
srv.wsvc.Stop()
|
||||
case err = <-wdone:
|
||||
srv.log.Infof("Service stopped by wservice error: %v", err)
|
||||
srv.gsvc.Stop()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) PseudoFork() error {
|
||||
const successExit int = 0
|
||||
var keyEnv string = "IMX0LTSELMRF8KASWER"
|
||||
var err error
|
||||
|
||||
_, isChild := os.LookupEnv(keyEnv)
|
||||
switch {
|
||||
case !isChild:
|
||||
os.Setenv(keyEnv, "TRUE")
|
||||
|
||||
procAttr := syscall.ProcAttr{}
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sysFiles = make([]uintptr, 3)
|
||||
sysFiles[0] = uintptr(syscall.Stdin)
|
||||
sysFiles[1] = uintptr(syscall.Stdout)
|
||||
sysFiles[2] = uintptr(syscall.Stderr)
|
||||
|
||||
procAttr.Files = sysFiles
|
||||
procAttr.Env = os.Environ()
|
||||
procAttr.Dir = cwd
|
||||
|
||||
_, err = syscall.ForkExec(os.Args[0], os.Args, &procAttr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Exit(successExit)
|
||||
case isChild:
|
||||
_, err = syscall.Setsid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
os.Unsetenv(keyEnv)
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) Daemonize() error {
|
||||
var err error
|
||||
if srv.conf.Daemon {
|
||||
// Restart process process
|
||||
err = srv.PseudoFork()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Redirect stdin
|
||||
nullFile, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = syscall.Dup2(int(nullFile.Fd()), int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Redirect stderr and stout
|
||||
logdir := filepath.Dir(srv.conf.LogPath)
|
||||
err = os.MkdirAll(logdir, 0750)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logFile, err := os.OpenFile(srv.conf.LogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = syscall.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Write process ID
|
||||
rundir := filepath.Dir(srv.conf.RunPath)
|
||||
err = os.MkdirAll(rundir, 0750)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pidFile, err := os.OpenFile(srv.conf.RunPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer pidFile.Close()
|
||||
currPid := os.Getpid()
|
||||
_, err = pidFile.WriteString(strconv.Itoa(currPid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
32
internal/wrpc/handler/basauth.go
Normal file
32
internal/wrpc/handler/basauth.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"certmanager/pkg/auxhttp"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (hand *Handler) BasicAuth(gctx *gin.Context) {
|
||||
var err error
|
||||
authHeader := gctx.Request.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
err = errors.New("Cannot found autentification data")
|
||||
auxhttp.SendError(gctx, err)
|
||||
return
|
||||
}
|
||||
username, password, err := auxhttp.ParseAuthBasicHeader(authHeader)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Authorization error: %s", err)
|
||||
auxhttp.SendError(gctx, err)
|
||||
return
|
||||
}
|
||||
if !hand.lg.ValidateUser(username, password) {
|
||||
err = errors.New("Incorrect autentification data")
|
||||
auxhttp.SendError(gctx, err)
|
||||
return
|
||||
}
|
||||
gctx.Next()
|
||||
}
|
||||
24
internal/wrpc/handler/handler.go
Normal file
24
internal/wrpc/handler/handler.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"certmanager/internal/logic"
|
||||
"certmanager/pkg/logger"
|
||||
)
|
||||
|
||||
type HandlerConfig struct {
|
||||
Logic *logic.Logic
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
log *logger.Logger
|
||||
lg *logic.Logic
|
||||
}
|
||||
|
||||
func NewHandler(conf *HandlerConfig) (*Handler, error) {
|
||||
var err error
|
||||
hand := &Handler{
|
||||
log: logger.NewLogger("whandler"),
|
||||
lg: conf.Logic,
|
||||
}
|
||||
return hand, err
|
||||
}
|
||||
31
internal/wrpc/handler/status.go
Normal file
31
internal/wrpc/handler/status.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"certmanager/api/certmanagercontrol"
|
||||
"certmanager/pkg/auxhttp"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (hand *Handler) GetStatus(gctx *gin.Context) {
|
||||
var err error
|
||||
nReq := &certmanagercontrol.GetStatusParams{}
|
||||
// Bind request
|
||||
err = gctx.ShouldBind(nReq)
|
||||
if err != nil {
|
||||
hand.log.Errorf("Cannot bind: %v", err)
|
||||
auxhttp.SendError(gctx, err)
|
||||
return
|
||||
}
|
||||
// Call logic
|
||||
ctx := gctx.Request.Context()
|
||||
lgRes, err := hand.lg.GetStatus(ctx, nReq)
|
||||
if err != nil {
|
||||
hand.log.Errorf("Got error: %v", err)
|
||||
auxhttp.SendError(gctx, err)
|
||||
return
|
||||
}
|
||||
// Send result
|
||||
nRes := lgRes
|
||||
auxhttp.SendResult(gctx, nRes)
|
||||
}
|
||||
132
internal/wrpc/service/service.go
Normal file
132
internal/wrpc/service/service.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"certmanager/internal/wrpc/handler"
|
||||
"certmanager/pkg/auxgin"
|
||||
"certmanager/pkg/logger"
|
||||
|
||||
"certmanager/pkg/auxhttp"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
httpTimeout = 360
|
||||
)
|
||||
|
||||
type ServiceConfig struct {
|
||||
Handler *handler.Handler
|
||||
PortNum int
|
||||
Hostname string
|
||||
X509Cert []byte
|
||||
X509Key []byte
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
hand *handler.Handler
|
||||
hsrv *http.Server
|
||||
log *logger.Logger
|
||||
engine *gin.Engine
|
||||
|
||||
portnum int
|
||||
hostname string
|
||||
x509Cert []byte
|
||||
x509Key []byte
|
||||
}
|
||||
|
||||
func NewService(conf *ServiceConfig) (*Service, error) {
|
||||
var err error
|
||||
svc := &Service{
|
||||
hand: conf.Handler,
|
||||
portnum: conf.PortNum,
|
||||
hostname: conf.Hostname,
|
||||
x509Cert: conf.X509Cert,
|
||||
x509Key: conf.X509Key,
|
||||
}
|
||||
svc.log = logger.NewLogger("wservice")
|
||||
return svc, err
|
||||
}
|
||||
|
||||
func (svc *Service) Build() error {
|
||||
var err error
|
||||
svc.log.Debugf("Build service")
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
gin.DisableConsoleColor()
|
||||
|
||||
svc.engine = gin.New()
|
||||
svc.engine.Use(gin.Recovery())
|
||||
svc.engine.Use(auxgin.CorsMiddleware())
|
||||
svc.engine.Use(auxgin.LogMiddleware())
|
||||
svc.engine.Use(auxgin.RequestLogMiddleware())
|
||||
|
||||
apiGroup := svc.engine.Group("api")
|
||||
v1Group := apiGroup.Group("v1")
|
||||
{
|
||||
statusGroup := v1Group.Group("status")
|
||||
statusGroup.POST("get", svc.hand.GetStatus)
|
||||
/*
|
||||
forwardingGroup := v1Group.Group("forwarding")
|
||||
forwardingGroup.POST("create", svc.hand.CreateForwarding)
|
||||
forwardingGroup.POST("list", svc.hand.ListForwardings)
|
||||
forwardingGroup.POST("delete", svc.hand.DeleteForwarding)
|
||||
|
||||
defaultsGroup := v1Group.Group("defaults")
|
||||
defaultsGroup.POST("set", svc.hand.SetDefaults)
|
||||
defaultsGroup.POST("get", svc.hand.GetDefaults)
|
||||
|
||||
proxyGroup := v1Group.Group("proxy")
|
||||
proxyGroup.POST("reset", svc.hand.ResetProxy)
|
||||
*/
|
||||
}
|
||||
noRouteFunc := func(gctx *gin.Context) {
|
||||
err := fmt.Errorf("No route")
|
||||
auxhttp.SendError(gctx, err)
|
||||
}
|
||||
svc.engine.NoRoute(noRouteFunc)
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(svc.x509Cert, svc.x509Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsConfig := tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
listenAddr := fmt.Sprintf(":%d", svc.portnum)
|
||||
svc.hsrv = &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: svc.engine,
|
||||
TLSConfig: &tlsConfig,
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) Run() error {
|
||||
var err error
|
||||
for _, route := range svc.engine.Routes() {
|
||||
svc.log.Debugf("The route is registered: %s %s", route.Method, route.Path)
|
||||
}
|
||||
svc.log.Infof("Service listening at %d port", svc.portnum)
|
||||
err = svc.hsrv.ListenAndServeTLS("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) Stop() {
|
||||
svc.log.Infof("Stopping service")
|
||||
if svc.hsrv != nil {
|
||||
downWaiting := 5 * time.Second
|
||||
ctx, _ := context.WithTimeout(context.Background(), downWaiting)
|
||||
svc.hsrv.Shutdown(ctx)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user