import sources

This commit is contained in:
Олег Бородин
2024-07-30 09:49:53 +02:00
commit e9d4d1ef07
51 changed files with 15484 additions and 0 deletions

176
pkg/aux509/x509cert.go Normal file
View File

@@ -0,0 +1,176 @@
package aux509
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"net"
"time"
)
func CreateX509SelfSignedCert(subject string, commonNames ...string) ([]byte, []byte, error) {
var err error
certPem := make([]byte, 0)
keyPem := make([]byte, 0)
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
key, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %v", err)
return certPem, keyPem, err
}
keyPemBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
keyPem = pem.EncodeToMemory(&keyPemBlock)
dnsNames := make([]string, 0)
dnsNames = append(dnsNames, subject)
dnsNames = append(dnsNames, commonNames...)
tml := x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: pkix.Name{
CommonName: subject,
},
DNSNames: dnsNames,
IPAddresses: []net.IP{net.ParseIP("192.168.57.1")},
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err)
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem = pem.EncodeToMemory(&certPemBlock)
if err != nil {
return certPem, keyPem, err
}
return certPem, keyPem, err
}
func CreateX509CACert(commonName string) ([]byte, []byte, error) {
var err error
certPem := make([]byte, 0)
keyPem := make([]byte, 0)
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
key, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %v", err)
return certPem, keyPem, err
}
keyPemBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
keyPem = pem.EncodeToMemory(&keyPemBlock)
tml := x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: pkix.Name{
CommonName: commonName,
},
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err)
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem = pem.EncodeToMemory(&certPemBlock)
if err != nil {
return certPem, keyPem, err
}
return certPem, keyPem, err
}
func CreateX509Cert(commonName string, caKeyPem []byte, dnsNames ...string) ([]byte, []byte, error) {
var err error
certPem := make([]byte, 0)
keyPem := make([]byte, 0)
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
key, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %v", err)
return certPem, keyPem, err
}
keyPemBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
keyPem = pem.EncodeToMemory(&keyPemBlock)
pemBlock, _ := pem.Decode(caKeyPem)
if pemBlock == nil {
err := fmt.Errorf("Can't parse a CA private key block")
return certPem, keyPem, err
}
caKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
if err != nil {
err := fmt.Errorf("Can't parse a CA private key")
return certPem, keyPem, err
}
tml := x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: pkix.Name{
CommonName: commonName,
},
DNSNames: append([]string{commonName}, dnsNames...),
IsCA: false,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, caKey)
if err != nil {
return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err)
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem = pem.EncodeToMemory(&certPemBlock)
if err != nil {
return certPem, keyPem, err
}
return certPem, keyPem, err
}

View File

@@ -0,0 +1,29 @@
package aux509
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestCert(t *testing.T) {
{
//caCert, caKey, err := CreateX509SelfSignedCert("test1")
//require.NoError(t, err)
//fmt.Println(string(caCert))
//fmt.Println(string(caKey))
}
{
caCert, caKey, err := CreateX509CACert("test1")
require.NoError(t, err)
fmt.Println(string(caCert))
fmt.Println(string(caKey))
caCert, caKey, err = CreateX509Cert("test1", caKey)
require.NoError(t, err)
fmt.Println(string(caCert))
fmt.Println(string(caKey))
}
}

31
pkg/auxgin/corsmw.go Normal file
View File

@@ -0,0 +1,31 @@
package auxgin
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
func CorsMiddleware() gin.HandlerFunc {
headers := []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization"}
headerList := strings.Join(headers, ",")
methods := []string{"POST", "GET", "OPTIONS", "PUT", "DELETE", "UPDATE"}
methodList := strings.Join(methods, ",")
return func(gctx *gin.Context) {
gctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
gctx.Writer.Header().Set("Access-Control-Max-Age", "86400")
gctx.Writer.Header().Set("Access-Control-Allow-Methods", methodList)
gctx.Writer.Header().Set("Access-Control-Allow-Headers", headerList)
gctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
if gctx.Request.Method == "OPTIONS" {
gctx.AbortWithStatus(http.StatusOK)
} else {
gctx.Next()
}
}
}

59
pkg/auxgin/logmw.go Normal file
View File

@@ -0,0 +1,59 @@
package auxgin
import (
"bytes"
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func LogMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
start := time.Now()
ctx.Next()
var reqSize int64
var method string
var reqURI string
if ctx.Request != nil {
reqSize = ctx.Request.ContentLength
method = ctx.Request.Method
reqURI = ctx.Request.RequestURI
}
duration := time.Since(start).Microseconds()
remAddr := ctx.RemoteIP()
var resCode int
var resSize int
if ctx.Writer != nil {
resCode = ctx.Writer.Status()
resSize = ctx.Writer.Size()
}
logString := fmt.Sprintf("%s %s %s in=%d out=%d res=%d %dms",
remAddr, method, reqURI, reqSize, resSize, resCode, duration)
logger := logrus.WithField("object", "accesslog")
logger.Infoln(logString)
}
}
type LogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (lw LogWriter) Write(data []byte) (int, error) {
lw.body.Write(data)
return lw.ResponseWriter.Write(data)
}
func (lw LogWriter) WriteString(data string) (int, error) {
lw.body.WriteString(data)
return lw.ResponseWriter.WriteString(data)
}

34
pkg/auxgin/reqlog.go Normal file
View File

@@ -0,0 +1,34 @@
package auxgin
import (
"bytes"
"encoding/json"
"io/ioutil"
"strings"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func RequestLogMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
contentType := context.GetHeader("Content-Type")
contentType = strings.ToLower(contentType)
var requestBody []byte
if context.Request.Body != nil {
requestBody, _ = ioutil.ReadAll(context.Request.Body)
}
if strings.Contains(contentType, "application/json") && context.Request.Method == "POST" {
buffer := bytes.NewBuffer(nil)
json.Indent(buffer, requestBody, "", " ")
logger := logrus.WithField("object", "requestlog")
logger.Infoln("request:\n", buffer.String())
}
context.Request.Body = ioutil.NopCloser(bytes.NewReader(requestBody))
context.Next()
}
}

32
pkg/auxgin/reslog.go Normal file
View File

@@ -0,0 +1,32 @@
package auxgin
import (
"bytes"
"encoding/json"
"strings"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func ResponseLogMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
contentType := context.GetHeader("Content-Type")
contentType = strings.ToLower(contentType)
writer := &LogWriter{
body: bytes.NewBuffer(nil),
ResponseWriter: context.Writer,
}
context.Writer = writer
context.Next()
if strings.Contains(contentType, "application/json") {
buffer := bytes.NewBuffer(nil)
json.Indent(buffer, writer.body.Bytes(), "", " ")
logger := logrus.WithField("object", "responselog")
logger.Infoln("request:\n", buffer.String())
}
}
}

30
pkg/auxhttp/genres.go Normal file
View File

@@ -0,0 +1,30 @@
package auxhttp
import (
"net/http"
"github.com/gin-gonic/gin"
)
type GenericResponse[T any] struct {
Result T `json:"result,omitempty"`
Message string `json:"message,omitempty"`
Error bool `json:"error"`
ErrorCode int64 `json:"errorCode"`
}
func SendError(c *gin.Context, err error) {
var response GenericResponse[interface{}]
response.Error = true
if err != nil {
response.Message = err.Error()
response.ErrorCode = 101
}
c.AbortWithStatusJSON(http.StatusOK, response)
}
func SendResult(c *gin.Context, result any) {
var response GenericResponse[any]
response.Result = result
c.JSON(http.StatusOK, response)
}

57
pkg/auxhttp/getbearer.go Normal file
View File

@@ -0,0 +1,57 @@
package auxhttp
import (
"errors"
"fmt"
"strings"
)
func GetBearerToken(authHeader string) (string, error) {
var err error
var res string
const bearerKey = "Bearer"
const numWords = 2
authData := strings.SplitN(authHeader, " ", numWords)
if len(authData) < numWords {
err = errors.New("Authorization key and value not found")
return res, err
}
authKey := strings.TrimSpace(authData[0])
if authKey != bearerKey {
err = fmt.Errorf("Authorization type is different from %s", bearerKey)
return res, err
}
token := authData[1]
token = strings.TrimSpace(token)
if len(token) == 0 {
return res, errors.New("Lenght of authorization token must be greater zero")
}
res = token
return res, err
}
func HaveBearerToken(authHeader string) bool {
const bearerKey = "Bearer"
const numWords = 2
authData := strings.SplitN(authHeader, " ", numWords)
if len(authData) < numWords {
return false
}
authKey := strings.TrimSpace(authData[0])
if authKey != bearerKey {
return false
}
token := authData[1]
token = strings.TrimSpace(token)
if len(token) == 0 {
return false
}
return true
}

47
pkg/auxhttp/parseauth.go Normal file
View File

@@ -0,0 +1,47 @@
package auxhttp
import (
"encoding/base64"
"errors"
"strings"
)
func ParseAuthBasicHeader(header string) (string, string, error) {
var err error
var username string
var password string
authData := strings.SplitN(header, " ", 2)
if len(authData) < 2 {
err = errors.New("Wrong authentification header")
return username, password, err
}
authType := strings.TrimSpace(authData[0])
if authType != "Basic" {
err = errors.New("Authentification type is different from basic")
return username, password, err
}
authPair := strings.TrimSpace(authData[1])
pairEncoded, err := base64.StdEncoding.DecodeString(authPair)
if err != nil {
return username, password, err
}
pair := strings.SplitN(string(pairEncoded), ":", 2)
if len(pair) < 2 {
err = errors.New("Wrong authentification pair")
return username, password, err
}
username = strings.TrimSpace(pair[0])
password = strings.TrimSpace(pair[1])
if username == "" {
err = errors.New("autentification username is null")
return username, password, err
}
if password == "" {
err = errors.New("autentification password is null")
return username, password, err
}
return username, password, err
}

27
pkg/client/auth.go Normal file
View File

@@ -0,0 +1,27 @@
package client
import (
"context"
)
type AuthCredential struct {
Payload map[string]string
}
func NewAuthCredential(username, password string) *AuthCredential {
payload := make(map[string]string)
payload["username"] = username
payload["password"] = password
return &AuthCredential{
Payload: payload,
}
}
func (cred *AuthCredential) GetRequestMetadata(ctx context.Context, data ...string) (map[string]string, error) {
var err error
return cred.Payload, err
}
func (cred *AuthCredential) RequireTransportSecurity() bool {
return false
}

58
pkg/client/control.go Normal file
View File

@@ -0,0 +1,58 @@
package client
import (
"context"
"crypto/tls"
"fmt"
"time"
"certmanager/api/certmanagercontrol"
//"certmanager/internal/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
DefaultWrpcPort int = 20107
DefaultGrpcPort int = 20108
)
type Access struct {
Hostname string
Port int
Username string
Password string
}
func NewClient(access *Access) (certmanagercontrol.ControlClient, error) {
var err error
var cli certmanagercontrol.ControlClient
if access.Port == 0 {
access.Port = DefaultGrpcPort
}
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
const dialTimeout time.Duration = 5 * time.Second
const idleTimeout time.Duration = 10 * time.Second
authCred := NewAuthCredential(access.Username, access.Password)
dialOpts := []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
grpc.WithPerRPCCredentials(authCred),
grpc.WithBlock(),
grpc.WithIdleTimeout(idleTimeout),
}
address := fmt.Sprintf("%s:%d", access.Hostname, access.Port)
ctx, _ := context.WithTimeout(context.Background(), dialTimeout)
conn, err := grpc.DialContext(ctx, address, dialOpts...)
if err != nil {
return cli, fmt.Errorf("Dial error: %v", err)
}
cli = certmanagercontrol.NewControlClient(conn)
return cli, err
}

2
pkg/common/common.go Normal file
View File

@@ -0,0 +1,2 @@
package common

76
pkg/logger/logger.go Normal file
View File

@@ -0,0 +1,76 @@
package logger
import (
"fmt"
"os"
"time"
"github.com/sirupsen/logrus"
)
type LogFormatter struct {
}
func (lf *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
var err error
timeStamp := time.Now().Format(time.RFC3339)
levelString := entry.Level.String()
labelString := ""
for key, value := range entry.Data {
labelString += fmt.Sprintf("<%s:%v>", key, value)
}
if labelString != "" {
message := fmt.Sprintf("%s %s %s [%s]\n", timeStamp, levelString, labelString, entry.Message)
return []byte(message), err
}
message := fmt.Sprintf("%s %s [%s]\n", timeStamp, levelString, entry.Message)
return []byte(message), err
}
func init() {
logrus.SetOutput(os.Stdout)
logrus.SetFormatter(&LogFormatter{})
logrus.SetLevel(logrus.DebugLevel)
}
type Logger struct {
logrus *logrus.Entry
}
func NewLogger(label string) *Logger {
return &Logger{
logrus: logrus.WithField("object", label),
}
}
func (log *Logger) Errorf(format string, args ...any) {
log.logrus.Errorf(format, args...)
}
func (log *Logger) Debugf(format string, args ...any) {
log.logrus.Debugf(format, args...)
}
func (log *Logger) Warningf(format string, args ...any) {
log.logrus.Warningf(format, args...)
}
func (log *Logger) Infof(format string, args ...any) {
log.logrus.Infof(format, args...)
}
func (log *Logger) Error(args ...any) {
log.logrus.Error(args...)
}
func (log *Logger) Debug(args ...any) {
log.logrus.Debug(args...)
}
func (log *Logger) Warning(args ...any) {
log.logrus.Warning(args...)
}
func (log *Logger) Info(args ...any) {
log.logrus.Info(args...)
}