working commit

This commit is contained in:
2026-06-05 18:25:03 +02:00
commit 7d8abba003
82 changed files with 21863 additions and 0 deletions
+133
View File
@@ -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
}
+29
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
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()
}
}
}
+60
View File
@@ -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)
}
+34
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
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())
}
}
}
+19
View File
@@ -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
}
+30
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
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
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
}
+32
View File
@@ -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
}
+111
View File
@@ -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
}
+40
View File
@@ -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)
}
}
+37
View File
@@ -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
}
}
}
}
+31
View File
@@ -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()
}
+22
View File
@@ -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)
}
+22
View File
@@ -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)
}
+13
View File
@@ -0,0 +1,13 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package auxtool
import (
"time"
)
func TimeNow() string {
return time.Now().Format(time.RFC3339)
}
+27
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
}
+51
View File
@@ -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
}
+103
View File
@@ -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
}
+35
View File
@@ -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)
}
+42
View File
@@ -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-----
+29
View File
@@ -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-----
+31
View File
@@ -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-----
+31
View File
@@ -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-----
+480
View File
@@ -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
}
+48
View File
@@ -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)
}
}
+1
View File
@@ -0,0 +1 @@
package common
+78
View File
@@ -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()
}
+41
View File
@@ -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
+452
View File
@@ -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",
}
+190
View File
@@ -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
}
+77
View File
@@ -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)
}