working commit
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package aux509
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateX509SelfSignedCert(subject string, hostNames ...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)
|
||||
|
||||
certSubject := pkix.Name{
|
||||
CommonName: subject,
|
||||
}
|
||||
certIssuer := certSubject
|
||||
|
||||
dnsNames := make([]string, 0)
|
||||
dnsNames = append(dnsNames, subject)
|
||||
dnsNames = append(dnsNames, hostNames...)
|
||||
tml := x509.Certificate{
|
||||
SerialNumber: big.NewInt(now.Unix()),
|
||||
NotBefore: now,
|
||||
NotAfter: now.AddDate(yearsAfter, 0, 0),
|
||||
Subject: certSubject,
|
||||
Issuer: certIssuer,
|
||||
DNSNames: dnsNames,
|
||||
BasicConstraintsValid: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature |
|
||||
x509.KeyUsageContentCommitment |
|
||||
x509.KeyUsageKeyEncipherment |
|
||||
x509.KeyUsageDataEncipherment,
|
||||
}
|
||||
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)
|
||||
|
||||
certSubject := pkix.Name{
|
||||
CommonName: commonName,
|
||||
}
|
||||
certIssuer := certSubject
|
||||
|
||||
tml := x509.Certificate{
|
||||
SerialNumber: big.NewInt(now.Unix()),
|
||||
NotBefore: now,
|
||||
NotAfter: now.AddDate(yearsAfter, 0, 0),
|
||||
Subject: certSubject,
|
||||
Issuer: certIssuer,
|
||||
IsCA: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageClientAuth,
|
||||
x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature |
|
||||
x509.KeyUsageCertSign |
|
||||
x509.KeyUsageKeyEncipherment |
|
||||
x509.KeyUsageCRLSign,
|
||||
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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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
|
||||
var remAddr string
|
||||
if ctx.Request != nil {
|
||||
reqSize = ctx.Request.ContentLength
|
||||
method = ctx.Request.Method
|
||||
reqURI = ctx.Request.RequestURI
|
||||
remAddr = ctx.RemoteIP()
|
||||
}
|
||||
|
||||
duration := time.Since(start).Microseconds()
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package auxgrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func FmtError(err error) error {
|
||||
if err != nil {
|
||||
st, ok := status.FromError(err)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
err := fmt.Errorf("Return code %d, %s", st.Code(), st.Message())
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package auxid
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
var (
|
||||
idMtx sync.Mutex
|
||||
lastID int64
|
||||
)
|
||||
|
||||
func GenID() int64 {
|
||||
// 53 bit limit for js
|
||||
// See https://stackoverflow.com/questions/1379934/large-numbers-erroneously-rounded-in-javascript
|
||||
idMtx.Lock()
|
||||
defer idMtx.Unlock()
|
||||
for {
|
||||
id := (time.Now().UnixNano() / 1000) // - 10000000000000
|
||||
if id != lastID {
|
||||
lastID = id
|
||||
return id
|
||||
}
|
||||
time.Sleep(1 * time.Microsecond)
|
||||
}
|
||||
//10467328383814
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package auxpwd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var rnd *rand.Rand
|
||||
|
||||
const (
|
||||
sha256Prefix = "sha256pwd"
|
||||
sha512Prefix = "sha512pwd"
|
||||
saltSize = 16
|
||||
)
|
||||
|
||||
func init() {
|
||||
src := rand.NewSource(time.Now().UnixNano())
|
||||
rnd = rand.New(src)
|
||||
}
|
||||
|
||||
func MakeSHA256Hash(passwd []byte) string {
|
||||
var res string
|
||||
salt := hex.EncodeToString(randomBytes(saltSize))
|
||||
passwdString := hex.EncodeToString(passwd)
|
||||
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
|
||||
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(passwdString))
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
res = fmt.Sprintf("%s:%s:%s", sha256Prefix, salt, checksum)
|
||||
return res
|
||||
}
|
||||
|
||||
func MakeSHA512Hash(passwd []byte) string {
|
||||
var res string
|
||||
salt := hex.EncodeToString(randomBytes(saltSize))
|
||||
passwdString := hex.EncodeToString(passwd)
|
||||
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
|
||||
|
||||
hasher := sha512.New()
|
||||
hasher.Write([]byte(passwdString))
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
res = fmt.Sprintf("%s:%s:%s", sha512Prefix, salt, checksum)
|
||||
return res
|
||||
}
|
||||
|
||||
func PasswordMatchCompat(passwd []byte, hash string) bool {
|
||||
if !strings.HasPrefix(hash, sha256Prefix) && !strings.HasPrefix(hash, sha512Prefix) {
|
||||
if bytes.Equal(passwd, []byte(hash)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
hashComponents := strings.Split(hash, ":")
|
||||
if len(hashComponents) != 3 {
|
||||
return false
|
||||
}
|
||||
method := hashComponents[0]
|
||||
salt := hashComponents[1]
|
||||
controlChecksum := hashComponents[2]
|
||||
|
||||
switch method {
|
||||
case sha256Prefix:
|
||||
passwdString := hex.EncodeToString(passwd)
|
||||
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(passwdString))
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
if checksum != controlChecksum {
|
||||
return false
|
||||
}
|
||||
case sha512Prefix:
|
||||
passwdString := hex.EncodeToString(passwd)
|
||||
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
|
||||
hasher := sha512.New()
|
||||
hasher.Write([]byte(passwdString))
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
if checksum != controlChecksum {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func randomString(n int) string {
|
||||
const letters = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
arr := make([]byte, n)
|
||||
lettersArrayLen := len(letters)
|
||||
for i := range arr {
|
||||
arr[i] = letters[rnd.Intn(lettersArrayLen)]
|
||||
}
|
||||
return string(arr)
|
||||
}
|
||||
|
||||
func randomBytes(n int) []byte {
|
||||
arr := make([]byte, n)
|
||||
for i := range arr {
|
||||
arr[i] = byte(rnd.Intn(256) & 0xFF)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package auxpwd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPasswd256(t *testing.T) {
|
||||
password := []byte("123456781")
|
||||
wrongPasswd := []byte("qwerty")
|
||||
|
||||
hash := MakeSHA256Hash(password)
|
||||
fmt.Printf("%s\n", hash)
|
||||
{
|
||||
match := PasswordMatchCompat(password, hash)
|
||||
require.Equal(t, true, match)
|
||||
}
|
||||
{
|
||||
match := PasswordMatchCompat(wrongPasswd, hash)
|
||||
require.NotEqual(t, true, match)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswd512(t *testing.T) {
|
||||
password := []byte("123456781")
|
||||
wrongPasswd := []byte("qwerty")
|
||||
|
||||
hash := MakeSHA512Hash(password)
|
||||
fmt.Printf("%s\n", hash)
|
||||
{
|
||||
match := PasswordMatchCompat(password, hash)
|
||||
require.Equal(t, true, match)
|
||||
}
|
||||
{
|
||||
match := PasswordMatchCompat(wrongPasswd, hash)
|
||||
require.NotEqual(t, true, match)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||
*/
|
||||
package auxtool
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Clean only overbase elements of dir path if possible
|
||||
func CleanDirs(basedir, datadir string) {
|
||||
separator := string(os.PathSeparator)
|
||||
|
||||
basedir = filepath.Clean(separator + basedir)
|
||||
datadir = filepath.Clean(separator + datadir)
|
||||
|
||||
items := strings.Split(datadir, separator)
|
||||
for i := len(items); i > 0; i-- {
|
||||
p := filepath.Join(items[0:i]...)
|
||||
p = filepath.Clean(separator + p)
|
||||
if p == basedir {
|
||||
break
|
||||
}
|
||||
fileInfo, err := os.Stat(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if fileInfo.IsDir() {
|
||||
err = os.Remove(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package auxtool
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func FileExists(name string) bool {
|
||||
fileStat, err := os.Stat(name)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if fileStat.IsDir() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func DirExists(name string) bool {
|
||||
fileStat, err := os.Stat(name)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if fileStat == nil {
|
||||
return false
|
||||
}
|
||||
return fileStat.IsDir()
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||
*
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*/
|
||||
|
||||
package auxtool
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func RandomString(n int) string {
|
||||
const letters = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
arr := make([]byte, n)
|
||||
for i := range arr {
|
||||
arr[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(arr)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||
*/
|
||||
package auxtool
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func MakeTmpFilename(prefix string) string {
|
||||
randBytes := make([]byte, 6)
|
||||
rand.Read(randBytes)
|
||||
suffix := hex.EncodeToString(randBytes)
|
||||
return fmt.Sprintf("%s.tmp.%s", prefix, suffix)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||
*/
|
||||
package auxtool
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TimeNow() string {
|
||||
return time.Now().Format(time.RFC3339)
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"mbase/pkg/mbctl"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultPort uint32 = 1027
|
||||
)
|
||||
|
||||
type Access struct {
|
||||
Hostname string
|
||||
Port uint32
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func NewClient(access *Access) (*grpc.ClientConn, mbctl.ControlClient, error) {
|
||||
var err error
|
||||
var cli mbctl.ControlClient
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
const dialTimeout time.Duration = 10 * time.Second
|
||||
const idleTimeout time.Duration = 30 * 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 conn, cli, fmt.Errorf("Dial error: %v", err)
|
||||
}
|
||||
cli = mbctl.NewControlClient(conn)
|
||||
return conn, cli, err
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package cm509
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func EncryptAES256(b64data string, key string) (string, error) {
|
||||
var res string
|
||||
var err error
|
||||
|
||||
const aes256KeyLen = 32
|
||||
|
||||
bKey := []byte(key)
|
||||
keyLen := len(bKey)
|
||||
switch {
|
||||
case keyLen > aes256KeyLen:
|
||||
bKey = bKey[:aes256KeyLen]
|
||||
case keyLen < aes256KeyLen:
|
||||
padding := make([]byte, aes256KeyLen-keyLen)
|
||||
bKey = append(bKey, padding...)
|
||||
case keyLen == 0:
|
||||
return res, fmt.Errorf("Zero lenght key")
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(b64data)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(bKey)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, 12)
|
||||
_, err = io.ReadFull(rand.Reader, nonce)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
ciphertext := aesgcm.Seal(nil, nonce, data, nil)
|
||||
ciphertext = append(nonce, ciphertext...)
|
||||
|
||||
res = base64.StdEncoding.EncodeToString(ciphertext)
|
||||
|
||||
return res, err
|
||||
|
||||
}
|
||||
|
||||
func DecryptAES256(b64ciphertext string, key string) (string, error) {
|
||||
var res string
|
||||
var err error
|
||||
|
||||
const aes256KeyLen = 32
|
||||
|
||||
bKey := []byte(key)
|
||||
keyLen := len(bKey)
|
||||
switch {
|
||||
case keyLen > aes256KeyLen:
|
||||
bKey = bKey[:aes256KeyLen]
|
||||
case keyLen < aes256KeyLen:
|
||||
padding := make([]byte, aes256KeyLen-keyLen)
|
||||
bKey = append(bKey, padding...)
|
||||
case keyLen == 0:
|
||||
return res, fmt.Errorf("Zero lenght key")
|
||||
}
|
||||
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(b64ciphertext)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(bKey)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
nonceSize := aesgcm.NonceSize()
|
||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
|
||||
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
res = base64.StdEncoding.EncodeToString(plaintext)
|
||||
return res, err
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cm509
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAES(t *testing.T) {
|
||||
var err error
|
||||
|
||||
src := "1234567890123456789012345678901234567890"
|
||||
b64src := base64.StdEncoding.EncodeToString([]byte(src))
|
||||
key := "12345678901234"
|
||||
encSrc, err := EncryptAES256(b64src, key)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, len(encSrc))
|
||||
|
||||
decSrc, err := DecryptAES256(encSrc, key)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, len(decSrc))
|
||||
require.Equal(t, b64src, decSrc)
|
||||
|
||||
dst, err := base64.StdEncoding.DecodeString(decSrc)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(src), len(dst))
|
||||
require.Equal(t, string(src), string(dst))
|
||||
|
||||
fmt.Printf("%s\n", src)
|
||||
fmt.Printf("%s\n", dst)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHZDCCBkygAwIBAgISA0TPqlhFqMMjfL8lwr1WdKCiMA0GCSqGSIb3DQEBCwUA
|
||||
MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD
|
||||
EwNSMTEwHhcNMjQwNzE1MDcwNTQyWhcNMjQxMDEzMDcwNTQxWjAYMRYwFAYDVQQD
|
||||
Ew1oYW0udW5peDcub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
xDw3k4983QmRIqV0PsXFfG3x1wkamrMrY9sMz+M+CR9h1iozv4OQdm5wFJp/8ert
|
||||
7x+JS07v4vabYoLyVsdteRXHrqXlSDJMuZaReHIQVqKk1BYZ9miIH64ExUA6vd3r
|
||||
8AmilipIPW+UJihaZnP7wPy80PUdCiq0tnSewKN+wfzka5yehWXeaTbDeDoUl1Cf
|
||||
5Q3CO9KhbIYNwG8GBm+4YKiuewjjIU4sPEaCPvpvCTcwA4Lcqf1awU/nRdTLXO1e
|
||||
L4LezTEPb7KSS7hEZSHs2aQbjVpoV0IcuSYI1beb7XSdsv/jDIzOpu/Sx+AqMUy+
|
||||
WE4sO0Yj3ap8mbsY9HTC7QIDAQABo4IEizCCBIcwDgYDVR0PAQH/BAQDAgWgMB0G
|
||||
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud
|
||||
DgQWBBR3smydoErE3KVUBr0/ohKZ0ghK0jAfBgNVHSMEGDAWgBTFz0ak6vTDwHps
|
||||
lcQtsF6SLybjuTBXBggrBgEFBQcBAQRLMEkwIgYIKwYBBQUHMAGGFmh0dHA6Ly9y
|
||||
MTEuby5sZW5jci5vcmcwIwYIKwYBBQUHMAKGF2h0dHA6Ly9yMTEuaS5sZW5jci5v
|
||||
cmcvMIICkAYDVR0RBIIChzCCAoOCEmFpcmZvcmNlLnVuaXg3Lm9yZ4IOYXJ0cy51
|
||||
bml4Ny5vcmeCDWRlNC51bml4Ny5vcmeCDWRlNS51bml4Ny5vcmeCDWRlNi51bml4
|
||||
Ny5vcmeCDWRlNy51bml4Ny5vcmeCDWRlYi51bml4Ny5vcmeCDWRldi51bml4Ny5v
|
||||
cmeCDmRuczUudW5peDcub3Jngg1lZHUudW5peDcub3JnghBlZHVtYXgudW5peDcu
|
||||
b3Jngg1naXQudW5peDcub3Jngg9nbWFpbC51bml4Ny5vcmeCDWhhbS51bml4Ny5v
|
||||
cmeCDmhhc2gudW5peDcub3JnggxoZC51bml4Ny5vcmeCDmhlYXAudW5peDcub3Jn
|
||||
ghJob21lZGVzay51bml4Ny5vcmeCDWh1Yi51bml4Ny5vcmeCEGl0ZGVzay51bml4
|
||||
Ny5vcmeCD2xhcGlzLnVuaXg3Lm9yZ4IPbG9yZW0udW5peDcub3Jngg5tYWlsLnVu
|
||||
aXg3Lm9yZ4IMbXcudW5peDcub3JnggxteC51bml4Ny5vcmeCDnBkbnMudW5peDcu
|
||||
b3Jngg5waWtpLnVuaXg3Lm9yZ4IPcHJveHkudW5peDcub3JnghFyZWRtaW5lLnVu
|
||||
aXg3Lm9yZ4IMcm0udW5peDcub3Jngg9zbGFjay51bml4Ny5vcmeCEHNwcmluZy51
|
||||
bml4Ny5vcmeCDnNydjcudW5peDcub3JnghBzdG9yZXgudW5peDcub3JnghB0YW5h
|
||||
a2gudW5peDcub3Jnggx2NS51bml4Ny5vcmeCDXcxMi51bml4Ny5vcmeCDXdjbS51
|
||||
bml4Ny5vcmeCDndpa2kudW5peDcub3Jngg13d3cudW5peDcub3JnMBMGA1UdIAQM
|
||||
MAowCAYGZ4EMAQIBMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcASLDja9qmRzQP
|
||||
5WoC+p0w6xxSActW3SyB2bu/qznYhHMAAAGQtW0RVQAABAMASDBGAiEAjQvvzpOR
|
||||
urMOiqV5g0EuAK1A9CuAKOdWJp6/s3/SKBMCIQCbTBj13lhMnTr6bSHwoBtSwoiY
|
||||
aeDJzGBmDTpbgGpFdQB1AN/hVuuqBa+1nA+GcY2owDJOrlbZbqf1pWoB0cE7vlJc
|
||||
AAABkLVtEicAAAQDAEYwRAIgWe0pdXXB6UmMmeSgYLDncdkS2aKHAHDdOqKOoL9x
|
||||
Kx8CIBqobR/Ve1IZMTLrRN54vh8kNmF0OkVjXtrh+ste6cKUMA0GCSqGSIb3DQEB
|
||||
CwUAA4IBAQA3c88cXIejS9vy4XUow6dOuud6qqkNX1osSq2vRtYkKMZb2JVuhPAr
|
||||
hQsozwptJPm5lOEDQPD8676yZNVgGdjmMvA0ewdWEp9HZ7x+RFlI7RC9CqMSOWPO
|
||||
p60RMiBQlK7Els38WurmW2GzZkfykzpZZ/0lIrCNjT7aB9VjGVDOjxo/xHapHSwJ
|
||||
GOxq4TTU1KaFfbFl5A2F9bRVrAAWfih+DzUzhlhZBHODzadgs2CimVYIp8gEVf1j
|
||||
dGetiU2j+cvOpvQnfX7Xr8Cf2YK7E0j6lfC3RPvK9oA1bP5KBMWELXsGBrwIxgUB
|
||||
f2/jRcxrQoGoEYwYW4D/JViKwelABzfc
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,29 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFBjCCAu6gAwIBAgIRAIp9PhPWLzDvI4a9KQdrNPgwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
|
||||
WhcNMjcwMzEyMjM1OTU5WjAzMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
|
||||
RW5jcnlwdDEMMAoGA1UEAxMDUjExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEAuoe8XBsAOcvKCs3UZxD5ATylTqVhyybKUvsVAbe5KPUoHu0nsyQYOWcJ
|
||||
DAjs4DqwO3cOvfPlOVRBDE6uQdaZdN5R2+97/1i9qLcT9t4x1fJyyXJqC4N0lZxG
|
||||
AGQUmfOx2SLZzaiSqhwmej/+71gFewiVgdtxD4774zEJuwm+UE1fj5F2PVqdnoPy
|
||||
6cRms+EGZkNIGIBloDcYmpuEMpexsr3E+BUAnSeI++JjF5ZsmydnS8TbKF5pwnnw
|
||||
SVzgJFDhxLyhBax7QG0AtMJBP6dYuC/FXJuluwme8f7rsIU5/agK70XEeOtlKsLP
|
||||
Xzze41xNG/cLJyuqC0J3U095ah2H2QIDAQABo4H4MIH1MA4GA1UdDwEB/wQEAwIB
|
||||
hjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB
|
||||
/wIBADAdBgNVHQ4EFgQUxc9GpOr0w8B6bJXELbBeki8m47kwHwYDVR0jBBgwFoAU
|
||||
ebRZ5nu25eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAC
|
||||
hhZodHRwOi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcG
|
||||
A1UdHwQgMB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcN
|
||||
AQELBQADggIBAE7iiV0KAxyQOND1H/lxXPjDj7I3iHpvsCUf7b632IYGjukJhM1y
|
||||
v4Hz/MrPU0jtvfZpQtSlET41yBOykh0FX+ou1Nj4ScOt9ZmWnO8m2OG0JAtIIE38
|
||||
01S0qcYhyOE2G/93ZCkXufBL713qzXnQv5C/viOykNpKqUgxdKlEC+Hi9i2DcaR1
|
||||
e9KUwQUZRhy5j/PEdEglKg3l9dtD4tuTm7kZtB8v32oOjzHTYw+7KdzdZiw/sBtn
|
||||
UfhBPORNuay4pJxmY/WrhSMdzFO2q3Gu3MUBcdo27goYKjL9CTF8j/Zz55yctUoV
|
||||
aneCWs/ajUX+HypkBTA+c8LGDLnWO2NKq0YD/pnARkAnYGPfUDoHR9gVSp/qRx+Z
|
||||
WghiDLZsMwhN1zjtSC0uBWiugF3vTNzYIEFfaPG7Ws3jDrAMMYebQ95JQ+HIBD/R
|
||||
PBuHRTBpqKlyDnkSHDHYPiNX3adPoPAcgdF3H2/W0rmoswMWgTlLn1Wu0mrks7/q
|
||||
pdWfS6PJ1jty80r2VKsM/Dj3YIDfbjXKdaFU5C+8bhfJGqU3taKauuz0wHVGT3eo
|
||||
6FlWkWYtbt4pgdamlwVeZEW+LM7qZEJEsMNPrfC03APKmZsJgpWCDWOKZvkZcvjV
|
||||
uYkQ4omYCTX5ohy+knMjdOmdH9c7SpqEWBDC86fiNex+O0XOMEZSa8DA
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
|
||||
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
|
||||
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
|
||||
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
|
||||
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
|
||||
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
|
||||
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
|
||||
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
|
||||
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
|
||||
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
|
||||
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
|
||||
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
|
||||
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
|
||||
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
|
||||
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
|
||||
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
|
||||
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
|
||||
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
|
||||
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
|
||||
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
|
||||
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
|
||||
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
|
||||
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
|
||||
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
|
||||
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVDCCBDygAwIBAgIRAKLBiJX85huxZJB/Ylc2Y5gwDQYJKoZIhvcNAQEFBQAw
|
||||
gakxCzAJBgNVBAYTAnVzMQ0wCwYDVQQIEwRVdGFoMRcwFQYDVQQHEw5TYWx0IExh
|
||||
a2UgQ2l0eTEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
|
||||
DwYDVQQLEwhEU1RDQSBYMTEWMBQGA1UEAxMNRFNUIFJvb3RDQSBYMTEhMB8GCSqG
|
||||
SIb3DQEJARYSY2FAZGlnc2lndHJ1c3QuY29tMB4XDTA0MDkwODE0NDM0NVoXDTA4
|
||||
MTEyODEzMDI1OVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qg
|
||||
Q28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+Do
|
||||
M3ZJKuM/IUmTrE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwG
|
||||
MoOifooUMM0RoOEqOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOe
|
||||
DNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqc
|
||||
kh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr
|
||||
2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaOCAd4wggHaMA8GA1Ud
|
||||
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMIG7BgNVHR8EgbMwgbAwga2ggaqg
|
||||
gaeGgaRsZGFwOi8vbGRhcC5kaWdzaWd0cnVzdC5jb20vY249RFNUJTIwUm9vdENB
|
||||
JTIwWDEsb3U9RFNUQ0ElMjBYMSxvPURpZ2l0YWwlMjBTaWduYXR1cmUlMjBUcnVz
|
||||
dCUyMENvLixsPVNhbHQlMjBMYWtlJTIwQ2l0eSxTPVV0YWgsYz11cz9jZXJ0aWZp
|
||||
Y2F0ZVJldm9jYXRpb25MaXN0O2JpbmFyeTCBuAYIKwYBBQUHAQEEgaswgagwgaUG
|
||||
CCsGAQUFBzAChoGYbGRhcDovL2xkYXAuZGlnc2lndHJ1c3QuY29tL2NuPURTVCUy
|
||||
MFJvb3RDQSUyMFgxLG91PURTVENBJTIwWDEsbz1EaWdpdGFsJTIwU2lnbmF0dXJl
|
||||
JTIwVHJ1c3QlMjBDby4sbD1TYWx0JTIwTGFrZSUyMENpdHksUz1VdGFoLGM9dXM/
|
||||
Y0FDZXJ0aWZpY2F0ZTtiaW5hcnkwHwYDVR0jBBgwFoAUaU2asPSCd8A2GzVVCRQa
|
||||
/goSAAowHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqGSIb3DQEB
|
||||
BQUAA4IBAQCpFXMtsChLFvN/Z+mwiodooamIW0qjMoVGHeN9CcDdUIGQI7cgbT10
|
||||
tsJuEZx3opp2s4LoM7Gn/o0T/rysLlT34vPwI4Ei/df3aG0ite5ehqWgMuc65n1P
|
||||
tadwl5JFFx3l8B0YWrOv5xJ0kY+br4FGI2OGqxagBtH2y7Uak4Iq2xipTHhlvx6a
|
||||
DWAzGQHovRdLf1c4cFti11gU29QYPgDXsJSuriq3xcItGzjYZT9V45WiIlmdvez0
|
||||
vCr6wcnjifctVfQ7z2o2+yl7A1+ijNhDhyfcrdPqctwx0Nk8IDitss+vNMsqoEHx
|
||||
uBoIoQw0DQhKAEYri9bw+9wqZhotOjMw
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,480 @@
|
||||
package cm509
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DoubleEncodedCertKeyMatch(cert, key string) error {
|
||||
var err error
|
||||
certPEM, err := base64.StdEncoding.DecodeString(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyPEM, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type CreateIssuerPairParams struct {
|
||||
OrganizationName string
|
||||
OrganizationalUnitName string
|
||||
CommonName string
|
||||
SignerCert string
|
||||
SignerKey string
|
||||
SerialNumber int64
|
||||
}
|
||||
type CreateIssuerPairResult struct {
|
||||
Name string
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
|
||||
func CreateIssuerPair(params *CreateIssuerPairParams) (*CreateIssuerPairResult, error) {
|
||||
var err error
|
||||
res := &CreateIssuerPairResult{}
|
||||
|
||||
if params.SignerKey != "" && params.SignerCert == "" {
|
||||
err = fmt.Errorf("The signature key and certificate must be defined together")
|
||||
return res, err
|
||||
}
|
||||
if params.SignerKey == "" && params.SignerCert != "" {
|
||||
err = fmt.Errorf("The signature key and certificate must be defined together")
|
||||
return res, err
|
||||
}
|
||||
|
||||
var signerKey any
|
||||
if params.SignerKey != "" {
|
||||
signerKey, err = ParseDoubleEncodedKey(params.SignerKey)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
var signerCert *x509.Certificate
|
||||
if params.SignerCert != "" {
|
||||
signerCert, err = ParseDoubleEncodedCerificate(params.SignerCert)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
certPem := make([]byte, 0)
|
||||
keyPem := make([]byte, 0)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
const yearsAfter int = 10
|
||||
const keySize int = 2048
|
||||
|
||||
certKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Can't create a private key: %v", err)
|
||||
return res, err
|
||||
|
||||
}
|
||||
keyPemBlock := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(certKey),
|
||||
}
|
||||
keyPem = pem.EncodeToMemory(keyPemBlock)
|
||||
|
||||
certSubject := pkix.Name{
|
||||
CommonName: params.CommonName,
|
||||
}
|
||||
if params.OrganizationName != "" {
|
||||
certSubject.Organization = []string{params.OrganizationName}
|
||||
}
|
||||
if params.OrganizationalUnitName != "" {
|
||||
certSubject.OrganizationalUnit = []string{params.OrganizationalUnitName}
|
||||
}
|
||||
|
||||
certIssuer := certSubject
|
||||
if signerCert != nil {
|
||||
certIssuer = signerCert.Subject
|
||||
}
|
||||
|
||||
var issuerKey any = certKey
|
||||
if signerKey != nil {
|
||||
issuerKey = signerKey
|
||||
}
|
||||
|
||||
res.Name = certSubject.String()
|
||||
|
||||
serialNumber := big.NewInt(now.UnixNano())
|
||||
if params.SerialNumber != 0 {
|
||||
serialNumber = big.NewInt(params.SerialNumber)
|
||||
}
|
||||
|
||||
certTempl := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: now,
|
||||
NotAfter: now.AddDate(yearsAfter, 0, 0),
|
||||
Subject: certSubject,
|
||||
Issuer: certIssuer,
|
||||
IsCA: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign |
|
||||
x509.KeyUsageKeyEncipherment | x509.KeyUsageCRLSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
parentCert := certTempl
|
||||
if signerCert != nil {
|
||||
parentCert = signerCert
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, parentCert, &certKey.PublicKey, issuerKey)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Can't create a certificate: %v", err)
|
||||
return res, err
|
||||
|
||||
}
|
||||
certPemBlock := pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certBytes,
|
||||
}
|
||||
certPem = pem.EncodeToMemory(&certPemBlock)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
res.Cert = base64.StdEncoding.EncodeToString(certPem)
|
||||
res.Key = base64.StdEncoding.EncodeToString(keyPem)
|
||||
return res, err
|
||||
}
|
||||
|
||||
type CreateServicePairParams struct {
|
||||
OrganizationName string
|
||||
OrganizationalUnitName string
|
||||
CommonName string
|
||||
DNSNames []string
|
||||
IPAddresses []string
|
||||
IssuerKey string
|
||||
IssuerCert string
|
||||
SerialNumber int64
|
||||
}
|
||||
type CreateServicePairResult struct {
|
||||
Name string
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
|
||||
func CreateServicePair(params *CreateServicePairParams) (*CreateServicePairResult, error) {
|
||||
var err error
|
||||
res := &CreateServicePairResult{}
|
||||
|
||||
if params.IssuerKey != "" && params.IssuerCert == "" {
|
||||
err = fmt.Errorf("The signature key and certificate must be defined together")
|
||||
return res, err
|
||||
}
|
||||
if params.IssuerKey == "" && params.IssuerCert != "" {
|
||||
err = fmt.Errorf("The signature key and certificate must be defined together")
|
||||
return res, err
|
||||
}
|
||||
|
||||
var signerKey any
|
||||
if params.IssuerKey != "" {
|
||||
signerKey, err = ParseDoubleEncodedKey(params.IssuerKey)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
var signerCert *x509.Certificate
|
||||
if params.IssuerCert != "" {
|
||||
signerCert, err = ParseDoubleEncodedCerificate(params.IssuerCert)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
certPem := make([]byte, 0)
|
||||
keyPem := make([]byte, 0)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
const yearsAfter int = 10
|
||||
const keySize int = 2048
|
||||
|
||||
certKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Can't create a private key: %v", err)
|
||||
return res, err
|
||||
|
||||
}
|
||||
keyPemBlock := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(certKey),
|
||||
}
|
||||
keyPem = pem.EncodeToMemory(keyPemBlock)
|
||||
|
||||
certSubject := pkix.Name{
|
||||
CommonName: params.CommonName,
|
||||
}
|
||||
if params.OrganizationName != "" {
|
||||
certSubject.Organization = []string{params.OrganizationName}
|
||||
}
|
||||
if params.OrganizationalUnitName != "" {
|
||||
certSubject.OrganizationalUnit = []string{params.OrganizationalUnitName}
|
||||
}
|
||||
|
||||
certIssuer := certSubject
|
||||
if signerCert != nil {
|
||||
certIssuer = signerCert.Subject
|
||||
}
|
||||
|
||||
var issuerKey any = certKey
|
||||
if signerKey != nil {
|
||||
issuerKey = signerKey
|
||||
}
|
||||
|
||||
res.Name = certSubject.String()
|
||||
|
||||
var netAddresses []net.IP
|
||||
if params.IPAddresses != nil && len(params.IPAddresses) > 0 {
|
||||
netAddresses = make([]net.IP, 0)
|
||||
for _, ipAddress := range params.IPAddresses {
|
||||
netAddress := net.ParseIP(ipAddress)
|
||||
netAddresses = append(netAddresses, netAddress)
|
||||
}
|
||||
}
|
||||
|
||||
var dnsNames []string
|
||||
if params.DNSNames != nil && len(params.DNSNames) > 0 {
|
||||
dnsNames = make([]string, 0)
|
||||
dnsNames = append(dnsNames, params.DNSNames...)
|
||||
}
|
||||
|
||||
serialNumber := big.NewInt(now.UnixNano())
|
||||
if params.SerialNumber != 0 {
|
||||
serialNumber = big.NewInt(params.SerialNumber)
|
||||
}
|
||||
|
||||
certTempl := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: now,
|
||||
NotAfter: now.AddDate(yearsAfter, 0, 0),
|
||||
Subject: certSubject,
|
||||
Issuer: certIssuer,
|
||||
DNSNames: dnsNames,
|
||||
IPAddresses: netAddresses,
|
||||
IsCA: false,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
parentCert := certTempl
|
||||
if signerCert != nil {
|
||||
parentCert = signerCert
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, parentCert, &certKey.PublicKey, issuerKey)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Can't create a certificate: %v", err)
|
||||
return res, err
|
||||
|
||||
}
|
||||
certPemBlock := pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certBytes,
|
||||
}
|
||||
certPem = pem.EncodeToMemory(&certPemBlock)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
res.Cert = base64.StdEncoding.EncodeToString(certPem)
|
||||
res.Key = base64.StdEncoding.EncodeToString(keyPem)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func ParseDoubleEncodedCerificate(certString string) (*x509.Certificate, error) {
|
||||
var err error
|
||||
res := &x509.Certificate{}
|
||||
|
||||
certPEM, err := base64.StdEncoding.DecodeString(certString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to parse base64 certificate string: %v", err)
|
||||
return res, err
|
||||
}
|
||||
certBlock, _ := pem.Decode([]byte(certPEM))
|
||||
if certBlock == nil {
|
||||
err := fmt.Errorf("Failed to parse certificate PEM")
|
||||
return res, err
|
||||
}
|
||||
if certBlock.Type != "CERTIFICATE" {
|
||||
err := fmt.Errorf("Unknown PEM certificate type: %s", certBlock.Type)
|
||||
return res, err
|
||||
}
|
||||
if len(certBlock.Bytes) == 0 {
|
||||
err := fmt.Errorf("Empty PEM certificate block")
|
||||
return res, err
|
||||
}
|
||||
|
||||
res, err = x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func ParseEncodedCerificate(certPEM string) (*x509.Certificate, error) {
|
||||
var err error
|
||||
res := &x509.Certificate{}
|
||||
|
||||
certBlock, _ := pem.Decode([]byte(certPEM))
|
||||
if certBlock == nil {
|
||||
err := fmt.Errorf("Failed to parse certificate PEM")
|
||||
return res, err
|
||||
}
|
||||
if certBlock.Type != "CERTIFICATE" {
|
||||
err := fmt.Errorf("Unknown PEM certificate type: %s", certBlock.Type)
|
||||
return res, err
|
||||
}
|
||||
if len(certBlock.Bytes) == 0 {
|
||||
err := fmt.Errorf("Empty PEM certificate block")
|
||||
return res, err
|
||||
}
|
||||
res, err = x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func ParseDoubleEncodedKey(keyString string) (any, error) {
|
||||
var err error
|
||||
var res any
|
||||
|
||||
keyPEM, err := base64.StdEncoding.DecodeString(keyString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to parse base64 key string: %v", err)
|
||||
return res, err
|
||||
}
|
||||
keyBlock, _ := pem.Decode([]byte(keyPEM))
|
||||
if keyBlock == nil {
|
||||
err := fmt.Errorf("Failed to parse key PEM")
|
||||
return res, err
|
||||
}
|
||||
switch keyBlock.Type {
|
||||
case "PRIVATE KEY":
|
||||
res, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
case "RSA PRIVATE KEY":
|
||||
res, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
case "EC PRIVATE KEY":
|
||||
res, err = x509.ParseECPrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("Unknown PEM key type: %s", keyBlock.Type)
|
||||
return res, err
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func ParseEncodedKey(keyPEM string) (any, error) {
|
||||
var err error
|
||||
var res any
|
||||
|
||||
keyBlock, _ := pem.Decode([]byte(keyPEM))
|
||||
if keyBlock == nil {
|
||||
err := fmt.Errorf("Failed to parse key PEM")
|
||||
return res, err
|
||||
}
|
||||
switch keyBlock.Type {
|
||||
case "PRIVATE KEY":
|
||||
res, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
case "RSA PRIVATE KEY":
|
||||
res, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
case "EC PRIVATE KEY":
|
||||
res, err = x509.ParseECPrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("Unknown PEM key type: %s", keyBlock.Type)
|
||||
return res, err
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func CheckDoubleEncodedCertificateChain(topIssuerCN string, certStrings []string) ([]string, error) {
|
||||
var err error
|
||||
res := make([]string, 0)
|
||||
|
||||
certObjs := make([]*x509.Certificate, 0)
|
||||
for _, certString := range certStrings {
|
||||
certObj, err := ParseDoubleEncodedCerificate(certString)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
certObjs = append(certObjs, certObj)
|
||||
}
|
||||
|
||||
issuerFound := false
|
||||
issuerIndex := -1
|
||||
for i, certObj := range certObjs {
|
||||
if topIssuerCN == certObj.Subject.String() {
|
||||
issuerIndex = i
|
||||
issuerFound = true
|
||||
}
|
||||
}
|
||||
if !issuerFound {
|
||||
err := fmt.Errorf("Issuer for %s cannot found", topIssuerCN)
|
||||
return res, err
|
||||
}
|
||||
interCertObj := certObjs[issuerIndex]
|
||||
interCertString := certStrings[issuerIndex]
|
||||
if !interCertObj.IsCA {
|
||||
err := fmt.Errorf("Issuer %s is not CA", interCertObj.Subject.String())
|
||||
return res, err
|
||||
}
|
||||
expired := interCertObj.NotAfter.Before(time.Now())
|
||||
if !expired {
|
||||
err := fmt.Errorf("Issuer %s expired %v", interCertObj.Subject.String(), interCertObj.NotAfter)
|
||||
return res, err
|
||||
}
|
||||
|
||||
res = append(res, interCertString)
|
||||
if interCertObj.Subject.String() == interCertObj.Issuer.String() {
|
||||
return res, err
|
||||
}
|
||||
updatedCertStrings := append(certStrings[:issuerIndex], certStrings[issuerIndex+1:]...)
|
||||
topIssuerCN = interCertObj.Issuer.String()
|
||||
|
||||
certStringsTail, err := CheckDoubleEncodedCertificateChain(topIssuerCN, updatedCertStrings)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
res = append(res, certStringsTail...)
|
||||
return res, err
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cm509
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestCertChainCheckerErr(t *testing.T) {
|
||||
var err error
|
||||
|
||||
certBytes, err := os.ReadFile("testchain_a01.crt")
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, len(certBytes))
|
||||
certObj, err := ParseEncodedCerificate(string(certBytes))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, certObj)
|
||||
|
||||
certStrings := make([]string, 0)
|
||||
for i := 1; i < 4; i++ {
|
||||
certBytes, err := os.ReadFile(fmt.Sprintf("testchain_a%02d.crt", i))
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, len(certBytes))
|
||||
certString := base64.StdEncoding.EncodeToString(certBytes)
|
||||
certStrings = append(certStrings, certString)
|
||||
}
|
||||
topIssuerCN := certObj.Issuer.String()
|
||||
|
||||
_, err = CheckDoubleEncodedCertificateChain(topIssuerCN, certStrings)
|
||||
require.Error(t, err)
|
||||
//require.NotNil(t, resString)
|
||||
//require.NotZero(t, len(resString))
|
||||
}
|
||||
|
||||
func printObj(label string, obj any) {
|
||||
objBytes, _ := yaml.Marshal(obj)
|
||||
objString := string(objBytes)
|
||||
if strings.Count(objString, "\n") < 2 {
|
||||
fmt.Printf("==== %s: %s\n", label, objString)
|
||||
} else {
|
||||
fmt.Printf("==== %s ::\n%s\n", label, objString)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package common
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||
*/
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
mtx sync.Mutex
|
||||
output io.WriteCloser = os.Stderr
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
subject string
|
||||
writer io.WriteCloser
|
||||
mtx *sync.Mutex
|
||||
}
|
||||
|
||||
func NewLogger(subj string) *Logger {
|
||||
return &Logger{
|
||||
subject: subj,
|
||||
writer: output,
|
||||
mtx: &mtx,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func SetWriter(newOut io.WriteCloser) {
|
||||
mtx.Lock()
|
||||
output = newOut
|
||||
mtx.Unlock()
|
||||
}
|
||||
|
||||
func (logg *Logger) SetWriter(newOut io.WriteCloser) {
|
||||
mtx.Lock()
|
||||
logg.writer = newOut
|
||||
var newMtx sync.Mutex
|
||||
logg.mtx = &newMtx
|
||||
mtx.Unlock()
|
||||
}
|
||||
|
||||
func (logg *Logger) Debugf(message string, args ...any) {
|
||||
logg.printf("debug", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) Infof(message string, args ...any) {
|
||||
logg.printf("info", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) Warningf(message string, args ...any) {
|
||||
logg.printf("warning", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) Errorf(message string, args ...any) {
|
||||
logg.printf("error", message, args...)
|
||||
}
|
||||
|
||||
func (logg *Logger) printf(level, message string, args ...any) {
|
||||
timestamp := time.Now().Format(time.RFC3339)
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
if logg.subject != "" {
|
||||
fmt.Fprintf(buffer, "%s %s.%s: ", timestamp, logg.subject, level)
|
||||
} else {
|
||||
fmt.Fprintf(buffer, "%s %s: ", timestamp, level)
|
||||
}
|
||||
fmt.Fprintf(buffer, message, args...)
|
||||
fmt.Fprintf(buffer, "\n")
|
||||
logg.mtx.Lock()
|
||||
fmt.Fprint(output, buffer.String())
|
||||
logg.mtx.Unlock()
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||
*/
|
||||
package logger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"os"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666)
|
||||
require.NoError(t, err)
|
||||
SetWriter(devNull)
|
||||
logg := NewLogger("test")
|
||||
logg.Debugf("foo: %s", "bar")
|
||||
}
|
||||
|
||||
func BenchmarkLoggerL(b *testing.B) {
|
||||
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666)
|
||||
require.NoError(b, err)
|
||||
SetWriter(devNull)
|
||||
logg := NewLogger("test")
|
||||
for i := 0; i < b.N; i++ {
|
||||
logg.Debugf("foo: %s", "bar")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLoggerP(b *testing.B) {
|
||||
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666)
|
||||
require.NoError(b, err)
|
||||
logg := NewLogger("test")
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
logg.Debugf("foo: %s", "bar")
|
||||
}
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,452 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc v3.21.12
|
||||
// source: mbctl.proto
|
||||
|
||||
package mbctl
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.62.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion8
|
||||
|
||||
const (
|
||||
Control_GetHello_FullMethodName = "/mbasecontrol.Control/getHello"
|
||||
Control_CreateAccount_FullMethodName = "/mbasecontrol.Control/createAccount"
|
||||
Control_DeleteAccount_FullMethodName = "/mbasecontrol.Control/deleteAccount"
|
||||
Control_UpdateAccount_FullMethodName = "/mbasecontrol.Control/updateAccount"
|
||||
Control_GetAccount_FullMethodName = "/mbasecontrol.Control/getAccount"
|
||||
Control_ListAccounts_FullMethodName = "/mbasecontrol.Control/listAccounts"
|
||||
Control_SetGrant_FullMethodName = "/mbasecontrol.Control/setGrant"
|
||||
Control_DeleteGrant_FullMethodName = "/mbasecontrol.Control/deleteGrant"
|
||||
Control_GetDump_FullMethodName = "/mbasecontrol.Control/getDump"
|
||||
Control_RestoreDump_FullMethodName = "/mbasecontrol.Control/restoreDump"
|
||||
)
|
||||
|
||||
// ControlClient is the client API for Control service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type ControlClient interface {
|
||||
GetHello(ctx context.Context, in *GetHelloParams, opts ...grpc.CallOption) (*GetHelloResult, error)
|
||||
CreateAccount(ctx context.Context, in *CreateAccountParams, opts ...grpc.CallOption) (*CreateAccountResult, error)
|
||||
DeleteAccount(ctx context.Context, in *DeleteAccountParams, opts ...grpc.CallOption) (*DeleteAccountResult, error)
|
||||
UpdateAccount(ctx context.Context, in *UpdateAccountParams, opts ...grpc.CallOption) (*UpdateAccountResult, error)
|
||||
GetAccount(ctx context.Context, in *GetAccountParams, opts ...grpc.CallOption) (*GetAccountResult, error)
|
||||
ListAccounts(ctx context.Context, in *ListAccountsParams, opts ...grpc.CallOption) (*ListAccountsResult, error)
|
||||
SetGrant(ctx context.Context, in *SetGrantParams, opts ...grpc.CallOption) (*SetGrantResult, error)
|
||||
DeleteGrant(ctx context.Context, in *DeleteGrantParams, opts ...grpc.CallOption) (*DeleteGrantResult, error)
|
||||
GetDump(ctx context.Context, in *GetDumpParams, opts ...grpc.CallOption) (*GetDumpResult, error)
|
||||
RestoreDump(ctx context.Context, in *RestoreDumpParams, opts ...grpc.CallOption) (*RestoreDumpResult, error)
|
||||
}
|
||||
|
||||
type controlClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewControlClient(cc grpc.ClientConnInterface) ControlClient {
|
||||
return &controlClient{cc}
|
||||
}
|
||||
|
||||
func (c *controlClient) GetHello(ctx context.Context, in *GetHelloParams, opts ...grpc.CallOption) (*GetHelloResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetHelloResult)
|
||||
err := c.cc.Invoke(ctx, Control_GetHello_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) CreateAccount(ctx context.Context, in *CreateAccountParams, opts ...grpc.CallOption) (*CreateAccountResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CreateAccountResult)
|
||||
err := c.cc.Invoke(ctx, Control_CreateAccount_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) DeleteAccount(ctx context.Context, in *DeleteAccountParams, opts ...grpc.CallOption) (*DeleteAccountResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(DeleteAccountResult)
|
||||
err := c.cc.Invoke(ctx, Control_DeleteAccount_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) UpdateAccount(ctx context.Context, in *UpdateAccountParams, opts ...grpc.CallOption) (*UpdateAccountResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateAccountResult)
|
||||
err := c.cc.Invoke(ctx, Control_UpdateAccount_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) GetAccount(ctx context.Context, in *GetAccountParams, opts ...grpc.CallOption) (*GetAccountResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetAccountResult)
|
||||
err := c.cc.Invoke(ctx, Control_GetAccount_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) ListAccounts(ctx context.Context, in *ListAccountsParams, opts ...grpc.CallOption) (*ListAccountsResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListAccountsResult)
|
||||
err := c.cc.Invoke(ctx, Control_ListAccounts_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) SetGrant(ctx context.Context, in *SetGrantParams, opts ...grpc.CallOption) (*SetGrantResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SetGrantResult)
|
||||
err := c.cc.Invoke(ctx, Control_SetGrant_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) DeleteGrant(ctx context.Context, in *DeleteGrantParams, opts ...grpc.CallOption) (*DeleteGrantResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(DeleteGrantResult)
|
||||
err := c.cc.Invoke(ctx, Control_DeleteGrant_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) GetDump(ctx context.Context, in *GetDumpParams, opts ...grpc.CallOption) (*GetDumpResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetDumpResult)
|
||||
err := c.cc.Invoke(ctx, Control_GetDump_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlClient) RestoreDump(ctx context.Context, in *RestoreDumpParams, opts ...grpc.CallOption) (*RestoreDumpResult, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RestoreDumpResult)
|
||||
err := c.cc.Invoke(ctx, Control_RestoreDump_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ControlServer is the server API for Control service.
|
||||
// All implementations must embed UnimplementedControlServer
|
||||
// for forward compatibility
|
||||
type ControlServer interface {
|
||||
GetHello(context.Context, *GetHelloParams) (*GetHelloResult, error)
|
||||
CreateAccount(context.Context, *CreateAccountParams) (*CreateAccountResult, error)
|
||||
DeleteAccount(context.Context, *DeleteAccountParams) (*DeleteAccountResult, error)
|
||||
UpdateAccount(context.Context, *UpdateAccountParams) (*UpdateAccountResult, error)
|
||||
GetAccount(context.Context, *GetAccountParams) (*GetAccountResult, error)
|
||||
ListAccounts(context.Context, *ListAccountsParams) (*ListAccountsResult, error)
|
||||
SetGrant(context.Context, *SetGrantParams) (*SetGrantResult, error)
|
||||
DeleteGrant(context.Context, *DeleteGrantParams) (*DeleteGrantResult, error)
|
||||
GetDump(context.Context, *GetDumpParams) (*GetDumpResult, error)
|
||||
RestoreDump(context.Context, *RestoreDumpParams) (*RestoreDumpResult, error)
|
||||
mustEmbedUnimplementedControlServer()
|
||||
}
|
||||
|
||||
// UnimplementedControlServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedControlServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedControlServer) GetHello(context.Context, *GetHelloParams) (*GetHelloResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetHello not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) CreateAccount(context.Context, *CreateAccountParams) (*CreateAccountResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateAccount not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) DeleteAccount(context.Context, *DeleteAccountParams) (*DeleteAccountResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteAccount not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) UpdateAccount(context.Context, *UpdateAccountParams) (*UpdateAccountResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateAccount not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) GetAccount(context.Context, *GetAccountParams) (*GetAccountResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetAccount not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) ListAccounts(context.Context, *ListAccountsParams) (*ListAccountsResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListAccounts not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) SetGrant(context.Context, *SetGrantParams) (*SetGrantResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetGrant not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) DeleteGrant(context.Context, *DeleteGrantParams) (*DeleteGrantResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteGrant not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) GetDump(context.Context, *GetDumpParams) (*GetDumpResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetDump not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) RestoreDump(context.Context, *RestoreDumpParams) (*RestoreDumpResult, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RestoreDump not implemented")
|
||||
}
|
||||
func (UnimplementedControlServer) mustEmbedUnimplementedControlServer() {}
|
||||
|
||||
// UnsafeControlServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ControlServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeControlServer interface {
|
||||
mustEmbedUnimplementedControlServer()
|
||||
}
|
||||
|
||||
func RegisterControlServer(s grpc.ServiceRegistrar, srv ControlServer) {
|
||||
s.RegisterService(&Control_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Control_GetHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetHelloParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).GetHello(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_GetHello_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).GetHello(ctx, req.(*GetHelloParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_CreateAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateAccountParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).CreateAccount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_CreateAccount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).CreateAccount(ctx, req.(*CreateAccountParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_DeleteAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteAccountParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).DeleteAccount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_DeleteAccount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).DeleteAccount(ctx, req.(*DeleteAccountParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_UpdateAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateAccountParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).UpdateAccount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_UpdateAccount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).UpdateAccount(ctx, req.(*UpdateAccountParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_GetAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetAccountParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).GetAccount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_GetAccount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).GetAccount(ctx, req.(*GetAccountParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_ListAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListAccountsParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).ListAccounts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_ListAccounts_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).ListAccounts(ctx, req.(*ListAccountsParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_SetGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SetGrantParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).SetGrant(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_SetGrant_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).SetGrant(ctx, req.(*SetGrantParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_DeleteGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteGrantParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).DeleteGrant(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_DeleteGrant_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).DeleteGrant(ctx, req.(*DeleteGrantParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_GetDump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetDumpParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).GetDump(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_GetDump_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).GetDump(ctx, req.(*GetDumpParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Control_RestoreDump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RestoreDumpParams)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServer).RestoreDump(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Control_RestoreDump_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServer).RestoreDump(ctx, req.(*RestoreDumpParams))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Control_ServiceDesc is the grpc.ServiceDesc for Control service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Control_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "mbasecontrol.Control",
|
||||
HandlerType: (*ControlServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "getHello",
|
||||
Handler: _Control_GetHello_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "createAccount",
|
||||
Handler: _Control_CreateAccount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "deleteAccount",
|
||||
Handler: _Control_DeleteAccount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "updateAccount",
|
||||
Handler: _Control_UpdateAccount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "getAccount",
|
||||
Handler: _Control_GetAccount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "listAccounts",
|
||||
Handler: _Control_ListAccounts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "setGrant",
|
||||
Handler: _Control_SetGrant_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "deleteGrant",
|
||||
Handler: _Control_DeleteGrant_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "getDump",
|
||||
Handler: _Control_GetDump_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "restoreDump",
|
||||
Handler: _Control_RestoreDump_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "mbctl.proto",
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package netacl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type NetACL struct {
|
||||
enabledAddresses []netip.Addr
|
||||
enabledNetworks []netip.Prefix
|
||||
disabledAddresses []netip.Addr
|
||||
disabledNetworks []netip.Prefix
|
||||
}
|
||||
|
||||
func NewNetACL() *NetACL {
|
||||
return &NetACL{
|
||||
enabledAddresses: make([]netip.Addr, 0),
|
||||
enabledNetworks: make([]netip.Prefix, 0),
|
||||
disabledAddresses: make([]netip.Addr, 0),
|
||||
disabledNetworks: make([]netip.Prefix, 0),
|
||||
}
|
||||
}
|
||||
|
||||
type netACL struct {
|
||||
EnabledAddresses []string `json:"enabledAddresses" yaml:"enabledAddresses"`
|
||||
EnabledNetworks []string `json:"enabledNetworks" yaml:"enabledNetworks"`
|
||||
DisabledAddresses []string `json:"disabledAddresses" yaml:"disabledAddresses"`
|
||||
DisabledNetworks []string `json:"disabledNetworks" yaml:"disabledNetworks"`
|
||||
}
|
||||
|
||||
func newNetACL() *netACL {
|
||||
return &netACL{
|
||||
EnabledAddresses: make([]string, 0),
|
||||
EnabledNetworks: make([]string, 0),
|
||||
DisabledAddresses: make([]string, 0),
|
||||
DisabledNetworks: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (al *NetACL) MarshalJSON() ([]byte, error) {
|
||||
alDescr := newNetACL()
|
||||
for _, disabledAddress := range al.disabledAddresses {
|
||||
alDescr.DisabledAddresses = append(alDescr.DisabledAddresses, disabledAddress.String())
|
||||
}
|
||||
for _, disabledNetwork := range al.disabledNetworks {
|
||||
alDescr.DisabledNetworks = append(alDescr.DisabledNetworks, disabledNetwork.String())
|
||||
}
|
||||
for _, enabledAddress := range al.enabledAddresses {
|
||||
alDescr.EnabledAddresses = append(alDescr.EnabledAddresses, enabledAddress.String())
|
||||
}
|
||||
for _, enabledNetwork := range al.enabledNetworks {
|
||||
alDescr.EnabledNetworks = append(alDescr.EnabledNetworks, enabledNetwork.String())
|
||||
}
|
||||
return json.Marshal(alDescr)
|
||||
}
|
||||
|
||||
func (al *NetACL) UnmarshalJSON(data []byte) error {
|
||||
var err error
|
||||
return err
|
||||
}
|
||||
|
||||
func (al *NetACL) MarshalYAML() ([]byte, error) {
|
||||
alDescr := newNetACL()
|
||||
for _, disabledAddress := range al.disabledAddresses {
|
||||
alDescr.DisabledAddresses = append(alDescr.DisabledAddresses, disabledAddress.String())
|
||||
}
|
||||
for _, disabledNetwork := range al.disabledNetworks {
|
||||
alDescr.DisabledNetworks = append(alDescr.DisabledNetworks, disabledNetwork.String())
|
||||
}
|
||||
for _, enabledAddress := range al.enabledAddresses {
|
||||
alDescr.EnabledAddresses = append(alDescr.EnabledAddresses, enabledAddress.String())
|
||||
}
|
||||
for _, enabledNetwork := range al.enabledNetworks {
|
||||
alDescr.EnabledNetworks = append(alDescr.EnabledNetworks, enabledNetwork.String())
|
||||
}
|
||||
return yaml.Marshal(alDescr)
|
||||
}
|
||||
|
||||
func (al *NetACL) UnmarshalYAML(data []byte) error {
|
||||
var err error
|
||||
return err
|
||||
}
|
||||
|
||||
func (al *NetACL) AddressIsEnabled(address string) (bool, error) {
|
||||
var err error
|
||||
var res bool
|
||||
addr, err := netip.ParseAddr(address)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, disabledAddr := range al.disabledAddresses {
|
||||
if disabledAddr.Compare(addr) == 0 {
|
||||
res = false
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
for _, disabledNetwork := range al.disabledNetworks {
|
||||
if disabledNetwork.Contains(addr) {
|
||||
res = false
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
for _, enabledAddr := range al.enabledAddresses {
|
||||
if enabledAddr.Compare(addr) == 0 {
|
||||
res = true
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
for _, enabledNetwork := range al.enabledNetworks {
|
||||
if enabledNetwork.Contains(addr) {
|
||||
res = true
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (al *NetACL) AddrIsEnabled(addr netip.Addr) (bool, error) {
|
||||
var err error
|
||||
var res bool
|
||||
for _, disabledAddr := range al.disabledAddresses {
|
||||
if disabledAddr.Compare(addr) == 0 {
|
||||
res = false
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
for _, disabledNetwork := range al.disabledNetworks {
|
||||
if disabledNetwork.Contains(addr) {
|
||||
res = false
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
for _, enabledAddr := range al.enabledAddresses {
|
||||
if enabledAddr.Compare(addr) == 0 {
|
||||
res = true
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
for _, enabledNetwork := range al.enabledNetworks {
|
||||
if enabledNetwork.Contains(addr) {
|
||||
res = true
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (al *NetACL) AddEnabledAddresses(addresses ...string) error {
|
||||
var err error
|
||||
for _, address := range addresses {
|
||||
addr, addrParseErr := netip.ParseAddr(address)
|
||||
if addrParseErr == nil {
|
||||
al.enabledAddresses = append(al.enabledAddresses, addr)
|
||||
continue
|
||||
}
|
||||
prefix, prefixParseErr := netip.ParsePrefix(address)
|
||||
if prefixParseErr == nil {
|
||||
al.enabledNetworks = append(al.enabledNetworks, prefix)
|
||||
continue
|
||||
}
|
||||
err = errors.Join(addrParseErr, prefixParseErr)
|
||||
err = fmt.Errorf("Address %s is not correct, error: %v", address, err)
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (al *NetACL) AddDisabledAddresses(addresses ...string) error {
|
||||
var err error
|
||||
for _, address := range addresses {
|
||||
addr, addrParseErr := netip.ParseAddr(address)
|
||||
if addrParseErr == nil {
|
||||
al.disabledAddresses = append(al.disabledAddresses, addr)
|
||||
continue
|
||||
}
|
||||
prefix, prefixParseErr := netip.ParsePrefix(address)
|
||||
if prefixParseErr == nil {
|
||||
al.disabledNetworks = append(al.disabledNetworks, prefix)
|
||||
continue
|
||||
}
|
||||
err = errors.Join(addrParseErr, prefixParseErr)
|
||||
err = fmt.Errorf("Address %s is not correct, error: %v", address, err)
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package netacl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestNetACLComplex(t *testing.T) {
|
||||
var err error
|
||||
var enabled bool
|
||||
al := NewNetACL()
|
||||
require.NotNil(t, al)
|
||||
err = al.AddDisabledAddresses("10.0.0.1")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = al.AddDisabledAddresses("192.168.10.0/24")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = al.AddEnabledAddresses("192.168.100.0/16")
|
||||
require.NoError(t, err)
|
||||
|
||||
yamlData, err := yaml.Marshal(al)
|
||||
require.NoError(t, err)
|
||||
fmt.Printf("\n%s\n", string(yamlData))
|
||||
|
||||
enabled, err = al.AddressIsEnabled("8.7.7.7")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, enabled)
|
||||
|
||||
enabled, err = al.AddressIsEnabled("192.168.10.0")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, enabled)
|
||||
|
||||
enabled, err = al.AddressIsEnabled("192.168.100.7")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, enabled)
|
||||
}
|
||||
|
||||
func TestNetACLAllEnabled(t *testing.T) {
|
||||
var err error
|
||||
var enabled bool
|
||||
al := NewNetACL()
|
||||
require.NotNil(t, al)
|
||||
err = al.AddEnabledAddresses("0.0.0.1/0")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = al.AddEnabledAddresses("10.0.1.1/17")
|
||||
require.NoError(t, err)
|
||||
|
||||
yamlData, err := yaml.Marshal(al)
|
||||
require.NoError(t, err)
|
||||
fmt.Printf("\n%s\n", string(yamlData))
|
||||
|
||||
enabled, err = al.AddressIsEnabled("192.168.100.7")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, enabled)
|
||||
}
|
||||
|
||||
func TestNetACLLoopEnabled(t *testing.T) {
|
||||
var err error
|
||||
var enabled bool
|
||||
al := NewNetACL()
|
||||
require.NotNil(t, al)
|
||||
err = al.AddEnabledAddresses("::1/0")
|
||||
require.NoError(t, err)
|
||||
|
||||
yamlData, err := yaml.Marshal(al)
|
||||
require.NoError(t, err)
|
||||
fmt.Printf("\n%s\n", string(yamlData))
|
||||
|
||||
enabled, err = al.AddressIsEnabled("::1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, enabled)
|
||||
}
|
||||
Reference in New Issue
Block a user