Skip to content

Commit a169c0c

Browse files
committed
feat: enables CIRA capabilities
1 parent 88e6dcf commit a169c0c

File tree

10 files changed

+486
-33
lines changed

10 files changed

+486
-33
lines changed

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
# Config
55
.env
66
.DS_store
7-
*.pem
7+
#certs
8+
**/*.pem
9+
**/*.crt
10+
**/*.key
811
**/*.yml
912
**/*.yaml
1013
!config/config.yml
@@ -18,7 +21,7 @@ bin/
1821
*.db
1922
*.db-journal
2023
__debug_bin*
21-
24+
*.app
2225

2326
# Test binary, built with `go test -c`
2427
*.test

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ FROM golang:1.24-alpine3.20@sha256:3d9132b88a6317b846b55aa8e821821301906fe799932
1010
COPY --from=modules /go/pkg /go/pkg
1111
COPY . /app
1212
WORKDIR /app
13+
RUN go mod tidy
1314
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
1415
go build -o /bin/app ./cmd/app
15-
16+
RUN mkdir -p /.config/device-management-toolkit
1617
# Step 3: Final
1718
FROM scratch
1819
COPY --from=builder /app/config /config
1920
COPY --from=builder /app/internal/app/migrations /migrations
2021
COPY --from=builder /bin/app /app
2122
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
23+
COPY --from=builder /.config/device-management-toolkit /.config/device-management-toolkit
2224
CMD ["/app"]

cmd/app/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ func main() {
3131
log.Fatalf("App init error: %s", err)
3232
}
3333

34+
// root, privateKey, err := certificates.GenerateRootCertificate(true, cfg.CommonName, "US", "open-amt-cloud-toolkit", true)
35+
// if err != nil {
36+
// log.Fatalf("Error generating root certificate: %s", err)
37+
// }
38+
// certificates.IssueWebServerCertificate(certificates.CertAndKeyType{Cert: root, Key: privateKey}, false, cfg.CommonName, "US", "open-amt-cloud-toolkit", true)
39+
3440
handleEncryptionKey(cfg)
3541

3642
if os.Getenv("GIN_MODE") != "debug" {

config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type (
2929
Name string `env-required:"true" yaml:"name" env:"APP_NAME"`
3030
Repo string `env-required:"true" yaml:"repo" env:"APP_REPO"`
3131
Version string `env-required:"true"`
32+
CommonName string `env-required:"true" yaml:"common_name" env:"APP_COMMON_NAME"`
3233
EncryptionKey string `yaml:"encryption_key" env:"APP_ENCRYPTION_KEY"`
3334
AllowInsecureCiphers bool `yaml:"allow_insecure_ciphers" env:"APP_ALLOW_INSECURE_CIPHERS"`
3435
}
@@ -81,6 +82,7 @@ func NewConfig() (*Config, error) {
8182
Name: "console",
8283
Repo: "open-amt-cloud-toolkit/console",
8384
Version: "DEVELOPMENT",
85+
CommonName: "localhost",
8486
EncryptionKey: "",
8587
AllowInsecureCiphers: false,
8688
},

docker-compose.yml

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
services:
2-
postgres:
3-
container_name: postgres
4-
image: postgres
5-
networks:
6-
- openamtnetwork1
7-
volumes:
8-
- pg-data:/var/lib/postgresql/data
9-
environment:
10-
POSTGRES_USER: "postgresadmin"
11-
POSTGRES_PASSWORD: "admin123"
12-
POSTGRES_DB: "rpsdb"
13-
ports:
14-
- 5432:5432
2+
# postgres:
3+
# container_name: postgres
4+
# image: postgres
5+
# networks:
6+
# - openamtnetwork1
7+
# volumes:
8+
# - pg-data:/var/lib/postgresql/data
9+
# environment:
10+
# POSTGRES_USER: "postgresadmin"
11+
# POSTGRES_PASSWORD: "admin123"
12+
# POSTGRES_DB: "rpsdb"
13+
# ports:
14+
# - 5432:5432
1515
app:
1616
build: .
1717
container_name: app
@@ -24,12 +24,13 @@ services:
2424
APP_ENCRYPTION_KEY: "Jf3Q2nXJ+GZzN1dbVQms0wbB4+i/5PjL" # This is a test encryption key for the app
2525
HTTP_HOST: ""
2626
GIN_MODE: "debug"
27-
DB_URL: "postgres://postgresadmin:admin123@postgres:5432/rpsdb"
27+
#DB_URL: "postgres://postgresadmin:admin123@postgres:5432/rpsdb"
2828
AUTH_DISABLED: true
2929
ports:
3030
- 8181:8181
31-
depends_on:
32-
- postgres
31+
- 4433:4433
32+
# depends_on:
33+
# - postgres
3334
# integration:
3435
# build:
3536
# context: .

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.23.0
44

55
toolchain go1.24.0
66

7-
// replace github.com/open-amt-cloud-toolkit/go-wsman-messages/v2 => ../go-wsman-messages
7+
replace github.com/open-amt-cloud-toolkit/go-wsman-messages/v2 => ../go-wsman-messages
88

99
require (
1010
github.com/Masterminds/squirrel v1.5.4

internal/app/app.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/open-amt-cloud-toolkit/console/config"
1717
consolehttp "github.com/open-amt-cloud-toolkit/console/internal/controller/http"
18+
"github.com/open-amt-cloud-toolkit/console/internal/controller/tcp/cira"
1819
wsv1 "github.com/open-amt-cloud-toolkit/console/internal/controller/ws/v1"
1920
"github.com/open-amt-cloud-toolkit/console/internal/usecase"
2021
"github.com/open-amt-cloud-toolkit/console/pkg/db"
@@ -64,6 +65,12 @@ func Run(cfg *config.Config) {
6465
}
6566

6667
wsv1.RegisterRoutes(handler, log, usecases.Devices, upgrader)
68+
69+
ciraServer, err := cira.NewServer("config/cert.pem", "config/key.pem", usecases.Devices)
70+
if err != nil {
71+
log.Fatal("CIRA Server failed: %v", err)
72+
}
73+
6774
httpServer := httpserver.New(handler, httpserver.Port(cfg.HTTP.Host, cfg.HTTP.Port))
6875

6976
// Waiting signal
@@ -75,11 +82,17 @@ func Run(cfg *config.Config) {
7582
log.Info("app - Run - signal: " + s.String())
7683
case err = <-httpServer.Notify():
7784
log.Error(fmt.Errorf("app - Run - httpServer.Notify: %w", err))
85+
case ciraErr := <-ciraServer.Notify():
86+
log.Error(fmt.Errorf("app - Run - ciraServer.Notify: %w", ciraErr))
7887
}
7988

8089
// Shutdown
8190
err = httpServer.Shutdown()
8291
if err != nil {
8392
log.Error(fmt.Errorf("app - Run - httpServer.Shutdown: %w", err))
8493
}
94+
err = ciraServer.Shutdown()
95+
if err != nil {
96+
log.Error(fmt.Errorf("app - Run - ciraServer.Shutdown: %w", err))
97+
}
8598
}

internal/certificates/generate.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package certificates
2+
3+
import (
4+
"crypto/rand"
5+
"crypto/rsa"
6+
"crypto/sha256"
7+
"crypto/x509"
8+
"crypto/x509/pkix"
9+
"encoding/pem"
10+
"fmt"
11+
"math/big"
12+
"net/url"
13+
"os"
14+
"time"
15+
)
16+
17+
func GenerateRootCertificate(addThumbPrintToName bool, commonName, country, organization string, strong bool) (*x509.Certificate, *rsa.PrivateKey, error) {
18+
keyLength := 2048
19+
if strong {
20+
keyLength = 3072
21+
}
22+
23+
// Generate RSA keys
24+
privateKey, err := rsa.GenerateKey(rand.Reader, keyLength)
25+
if err != nil {
26+
return nil, nil, err
27+
}
28+
29+
// Preparing the certificate
30+
var maxValue uint = 128
31+
32+
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), maxValue))
33+
if err != nil {
34+
return nil, nil, err
35+
}
36+
37+
thirtyYears := 30
38+
39+
if addThumbPrintToName {
40+
hash := sha256.New()
41+
hash.Write(privateKey.PublicKey.N.Bytes()) // Simplified approach to get a thumbprint-like result
42+
commonName += "-" + fmt.Sprintf("%x", hash.Sum(nil)[:3])
43+
}
44+
45+
if country == "" {
46+
country = "unknown country"
47+
}
48+
49+
if organization == "" {
50+
organization = "unknown organization"
51+
}
52+
53+
template := x509.Certificate{
54+
SerialNumber: serialNumber,
55+
Subject: pkix.Name{
56+
CommonName: commonName,
57+
Organization: []string{organization},
58+
Country: []string{country},
59+
},
60+
NotBefore: time.Now().AddDate(-1, 0, 0),
61+
NotAfter: time.Now().AddDate(thirtyYears, 0, 0),
62+
63+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
64+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
65+
BasicConstraintsValid: true,
66+
IsCA: true,
67+
}
68+
69+
// Create a self-signed certificate
70+
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
71+
if err != nil {
72+
return nil, nil, err
73+
}
74+
75+
// Encoding certificate to PEM format
76+
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
77+
78+
// Save to files (optional)
79+
certOut, err := os.Create("config/root_cert.pem")
80+
if err != nil {
81+
return nil, nil, err
82+
}
83+
84+
_, err = certOut.Write(certPEM)
85+
if err != nil {
86+
return nil, nil, err
87+
}
88+
89+
certOut.Close()
90+
91+
keyOut, err := os.Create("config/root_key.pem")
92+
if err != nil {
93+
return nil, nil, err
94+
}
95+
96+
err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
97+
if err != nil {
98+
return nil, nil, err
99+
}
100+
101+
keyOut.Close()
102+
103+
return &template, privateKey, nil
104+
}
105+
106+
type CertAndKeyType struct {
107+
Cert *x509.Certificate
108+
Key *rsa.PrivateKey
109+
}
110+
111+
func IssueWebServerCertificate(rootCert CertAndKeyType, addThumbPrintToName bool, commonName, country, organization string, strong bool) (*x509.Certificate, *rsa.PrivateKey, error) {
112+
keyLength := 2048
113+
if strong {
114+
keyLength = 3072
115+
}
116+
117+
// Generate RSA keys
118+
keys, err := rsa.GenerateKey(rand.Reader, keyLength)
119+
if err != nil {
120+
return nil, nil, err
121+
}
122+
123+
var maxValue uint = 128
124+
125+
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), maxValue))
126+
if err != nil {
127+
return nil, nil, err
128+
}
129+
130+
thirtyYears := 30
131+
notBefore := time.Now().AddDate(-1, 0, 0)
132+
notAfter := time.Now().AddDate(thirtyYears, 0, 0)
133+
134+
subject := pkix.Name{
135+
CommonName: commonName,
136+
}
137+
138+
if country != "" {
139+
subject.Country = []string{country}
140+
}
141+
142+
if organization != "" {
143+
subject.Organization = []string{organization}
144+
}
145+
146+
if addThumbPrintToName {
147+
hash := sha256.New()
148+
hash.Write(keys.PublicKey.N.Bytes()) // Simplified approach to get a thumbprint-like result
149+
subject.CommonName += "-" + string(hash.Sum(nil)[:3])
150+
}
151+
152+
hash := sha256.Sum256(keys.PublicKey.N.Bytes())
153+
154+
template := x509.Certificate{
155+
SerialNumber: serialNumber,
156+
Subject: subject,
157+
NotBefore: notBefore,
158+
NotAfter: notAfter,
159+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign | x509.KeyUsageDataEncipherment,
160+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
161+
BasicConstraintsValid: true,
162+
IsCA: false,
163+
SubjectKeyId: hash[:],
164+
}
165+
166+
// Subject Alternative Name
167+
uri, _ := url.Parse("http://" + commonName + "/")
168+
template.DNSNames = []string{commonName, "localhost"}
169+
template.URIs = []*url.URL{uri}
170+
171+
// Sign the certificate with root certificate private key
172+
certBytes, err := x509.CreateCertificate(rand.Reader, &template, rootCert.Cert, &keys.PublicKey, rootCert.Key)
173+
if err != nil {
174+
return nil, nil, err
175+
}
176+
177+
// Encoding certificate to PEM format
178+
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
179+
180+
// Save to files (optional)
181+
certOut, err := os.Create("config/" + commonName + "_cert.pem")
182+
if err != nil {
183+
return nil, nil, err
184+
}
185+
186+
_, err = certOut.Write(certPEM)
187+
if err != nil {
188+
return nil, nil, err
189+
}
190+
191+
certOut.Close()
192+
193+
keyOut, err := os.Create("config/" + commonName + "_key.pem")
194+
if err != nil {
195+
return nil, nil, err
196+
}
197+
198+
err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(keys)})
199+
if err != nil {
200+
return nil, nil, err
201+
}
202+
203+
keyOut.Close()
204+
205+
return &template, keys, nil
206+
}

0 commit comments

Comments
 (0)