diff --git a/config/conf.go b/config/conf.go index 03c8aa8..37b6337 100644 --- a/config/conf.go +++ b/config/conf.go @@ -40,7 +40,7 @@ func GetConfig() *Config { return instance } -func SetConfig(mongoUrl string, database string, natsUrl string, lokiUrl string, logLevel string, port int) *Config { +func SetConfig(mongoUrl string, database string, natsUrl string, lokiUrl string, logLevel string, port int, pkPath, ppPath string) *Config { GetConfig().MongoUrl = mongoUrl GetConfig().MongoDatabase = database GetConfig().NATSUrl = natsUrl @@ -48,5 +48,7 @@ func SetConfig(mongoUrl string, database string, natsUrl string, lokiUrl string, GetConfig().LogLevel = logLevel GetConfig().Whitelist = true GetConfig().APIPort = port + GetConfig().PrivateKeyPath = pkPath + GetConfig().PublicKeyPath = ppPath return GetConfig() } diff --git a/entrypoint.go b/entrypoint.go index 379d9e0..73ce136 100644 --- a/entrypoint.go +++ b/entrypoint.go @@ -149,6 +149,8 @@ func InitDaemon(appName string) { o.GetStringDefault("LOKI_URL", ""), o.GetStringDefault("LOG_LEVEL", "info"), o.GetIntDefault("API_PORT", 8080), + o.GetStringDefault("PUBLIC_KEY_PATH", "./pem/public.pem"), + o.GetStringDefault("PRIVATE_KEY_PATH", "./pem/private.pem"), ) // Beego init beego.BConfig.AppName = appName @@ -236,8 +238,8 @@ func GetLogger() zerolog.Logger { * @param logLevel string * @return *Config */ -func SetConfig(mongoUrl string, database string, natsUrl string, lokiUrl string, logLevel string, port int) *config.Config { - cfg := config.SetConfig(mongoUrl, database, natsUrl, lokiUrl, logLevel, port) +func SetConfig(mongoUrl string, database string, natsUrl string, lokiUrl string, logLevel string, port int, pkpath string, pppath string) *config.Config { + cfg := config.SetConfig(mongoUrl, database, natsUrl, lokiUrl, logLevel, port, pkpath, pppath) defer func() { if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in Init : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) diff --git a/go.mod b/go.mod index 760da97..5c02ddd 100755 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module cloud.o-forge.io/core/oc-lib -go 1.22.0 +go 1.24.6 require ( github.com/beego/beego/v2 v2.3.1 @@ -13,6 +13,7 @@ require ( ) require ( + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -22,7 +23,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect go.mongodb.org/mongo-driver v1.16.0 - golang.org/x/sys v0.22.0 // indirect + golang.org/x/sys v0.33.0 // indirect ) require ( @@ -38,6 +39,7 @@ require ( github.com/klauspost/compress v1.17.9 // indirect github.com/kr/text v0.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/libp2p/go-libp2p/core v0.43.0-rc2 github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -50,10 +52,10 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect - golang.org/x/crypto v0.25.0 // indirect + golang.org/x/crypto v0.39.0 // indirect golang.org/x/net v0.27.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/text v0.26.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 983c5ff..30cc2b5 100755 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/etcd-io/etcd v3.3.17+incompatible/go.mod h1:cdZ77EstHBwVtD6iTgzgvogwcjo9m4iOqoijouPJ4bs= @@ -54,6 +56,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/libp2p/go-libp2p/core v0.43.0-rc2 h1:1X1aDJNWhMfodJ/ynbaGLkgnC8f+hfBIqQDrzxFZOqI= +github.com/libp2p/go-libp2p/core v0.43.0-rc2/go.mod h1:NYeJ9lvyBv9nbDk2IuGb8gFKEOkIv/W5YRIy1pAJB2Q= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -123,6 +127,8 @@ golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -135,6 +141,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -147,6 +155,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -155,6 +165,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -162,6 +174,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/models/resources/resource.go b/models/resources/resource.go index 9bd3f17..a4bb215 100755 --- a/models/resources/resource.go +++ b/models/resources/resource.go @@ -1,6 +1,7 @@ package resources import ( + "crypto/sha256" "encoding/json" "errors" "slices" @@ -27,7 +28,21 @@ type AbstractResource struct { Owners []utils.Owner `json:"owners,omitempty" bson:"owners,omitempty"` // Owners is the list of owners of the resource UsageRestrictions string `bson:"usage_restrictions,omitempty" json:"usage_restrictions,omitempty"` AllowedBookingModes map[booking.BookingMode]*pricing.PricingVariation `bson:"allowed_booking_modes" json:"allowed_booking_modes"` - Signature []byte `bson:"signature" json:"signature"` + Signature []byte `bson:"signature,omitempty" json:"signature,omitempty"` +} + +func (r *AbstractResource) Unsign() { + r.Signature = nil +} + +func (r *AbstractResource) Sign() { + priv, err := tools.LoadKeyFromFilePrivate() // your node private key + if err != nil { + return + } + b, _ := json.Marshal(r) + hash := sha256.Sum256(b) + r.Signature, err = priv.Sign(hash[:]) } func (abs *AbstractResource) GetSignature() []byte { diff --git a/models/resources/resource_accessor.go b/models/resources/resource_accessor.go index caad2d3..412d98d 100755 --- a/models/resources/resource_accessor.go +++ b/models/resources/resource_accessor.go @@ -46,7 +46,14 @@ func (dca *ResourceMongoAccessor[T]) UpdateOne(set utils.DBObject, id string) (u return nil, 404, errors.New("can't update a non existing computing units resource not reported onto compute units catalog") } set.(T).Trim() - return utils.GenericUpdateOne(set, id, dca, dca.generateData()) + + if d, c, err := utils.GenericUpdateOne(set, id, dca, dca.generateData()); err != nil { + return d, c, err + } else { + d.Unsign() + d.Sign() + return utils.GenericUpdateOne(set, id, dca, dca.generateData()) + } } func (dca *ResourceMongoAccessor[T]) StoreOne(data utils.DBObject) (utils.DBObject, int, error) { @@ -54,7 +61,11 @@ func (dca *ResourceMongoAccessor[T]) StoreOne(data utils.DBObject) (utils.DBObje return nil, 404, errors.New("can't create a non existing computing units resource not reported onto compute units catalog") } data.(T).Trim() - return utils.GenericStoreOne(data, dca) + d, c, err := utils.GenericStoreOne(data, dca) + if err != nil { + return d, c, err + } + return dca.UpdateOne(d, d.GetID()) } func (dca *ResourceMongoAccessor[T]) CopyOne(data utils.DBObject) (utils.DBObject, int, error) { diff --git a/models/utils/abstracts.go b/models/utils/abstracts.go index 5fcc525..dd04f41 100755 --- a/models/utils/abstracts.go +++ b/models/utils/abstracts.go @@ -43,6 +43,10 @@ func (ri *AbstractObject) GetAccessor(request *tools.APIRequest) Accessor { return nil } +func (r *AbstractObject) Unsign() {} + +func (r *AbstractObject) Sign() {} + func (r *AbstractObject) SetID(id string) { r.UUID = id } diff --git a/models/utils/interfaces.go b/models/utils/interfaces.go index f40c2cc..0c0d979 100755 --- a/models/utils/interfaces.go +++ b/models/utils/interfaces.go @@ -32,6 +32,8 @@ type DBObject interface { Serialize(obj DBObject) map[string]interface{} GetAccessor(request *tools.APIRequest) Accessor Deserialize(j map[string]interface{}, obj DBObject) DBObject + Sign() + Unsign() } // Accessor is an interface that defines the basic methods for an Accessor diff --git a/tools/crypto.go b/tools/crypto.go new file mode 100644 index 0000000..caf0fdb --- /dev/null +++ b/tools/crypto.go @@ -0,0 +1,51 @@ +package tools + +import ( + "crypto/ed25519" + "crypto/x509" + "encoding/pem" + "fmt" + "os" + + "cloud.o-forge.io/core/oc-lib/config" + "github.com/libp2p/go-libp2p/core/crypto" +) + +func LoadKeyFromFilePrivate() (crypto.PrivKey, error) { + path := config.GetConfig().PrivateKeyPath + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + block, _ := pem.Decode(data) + keyAny, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + + edKey, ok := keyAny.(ed25519.PrivateKey) + if !ok { + return nil, fmt.Errorf("not an ed25519 key") + } + return crypto.UnmarshalEd25519PrivateKey(edKey) +} + +func LoadKeyFromFilePublic() (crypto.PubKey, error) { + path := config.GetConfig().PublicKeyPath + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + block, _ := pem.Decode(data) + keyAny, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + + edKey, ok := keyAny.(ed25519.PublicKey) + if !ok { + return nil, fmt.Errorf("not an ed25519 key") + } + // Try to unmarshal as libp2p private key (supports ed25519, rsa, etc.) + return crypto.UnmarshalEd25519PublicKey(edKey) +}