Skip to content

Commit

Permalink
Move certs & helper code from /internal/examples to /internal (#201)
Browse files Browse the repository at this point in the history
Fixes #199
  • Loading branch information
srikanthccv authored Sep 21, 2023
1 parent fd3066f commit b24302d
Show file tree
Hide file tree
Showing 20 changed files with 183 additions and 161 deletions.
164 changes: 164 additions & 0 deletions internal/certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package internal

// This file contains helper functions to read and
// create TLS configs/certificates. Currently used in
// the example client and server and
// in the tests. Not intended for any other use.

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"math/big"
"net"
"os"
"time"

"github.com/open-telemetry/opamp-go/protobufs"
)

func CreateClientTLSConfig(clientCert *tls.Certificate, caCertPath string) (*tls.Config, error) {
// Read the CA's public key. This is the CA that signs the server's certificate.
caCertBytes, err := os.ReadFile(caCertPath)
if err != nil {
return nil, err
}

// Create a certificate pool and make our CA trusted.
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCertBytes); !ok {
return nil, errors.New("cannot append ca.cert.pem")
}

cfg := &tls.Config{
RootCAs: caCertPool,
}
if clientCert != nil {
// If there is a client-side certificate use it for connection too.
cfg.Certificates = []tls.Certificate{*clientCert}
}
return cfg, nil
}

func CreateServerTLSConfig(caCertPath, serverCertPath, serverKeyPath string) (*tls.Config, error) {
// Read the CA's public key. This is the CA that signs the server's certificate.
caCertBytes, err := os.ReadFile(caCertPath)
if err != nil {
return nil, err
}

// Create a certificate pool and make our CA trusted.
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCertBytes); !ok {
return nil, errors.New("cannot append ca.cert.pem")
}

// Load server's certificate.
cert, err := tls.LoadX509KeyPair(
serverCertPath,
serverKeyPath,
)
if err != nil {
return nil, fmt.Errorf("tls.LoadX509KeyPair failed: %v", err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
// TODO: verify client cert manually, and allow TOFU option. See manual
// verification example: https://dev.to/living_syn/validating-client-certificate-sans-in-go-i5p
// Instead, we use VerifyClientCertIfGiven which will automatically verify the provided certificate
// is signed by our CA (so TOFU with self-generated client certificate will not work).
ClientAuth: tls.VerifyClientCertIfGiven,
// Allow insecure connections for demo purposes.
InsecureSkipVerify: true,
ClientCAs: caCertPool,
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}

func CreateTLSCert(caCertPath, caKeyPath string) (*protobufs.TLSCertificate, error) {

// Load CA Cert.
caCertBytes, err := ioutil.ReadFile(caCertPath)
if err != nil {
return nil, fmt.Errorf("cannot read CA cert: %v", err)
}

caKeyBytes, err := ioutil.ReadFile(caKeyPath)
if err != nil {
return nil, fmt.Errorf("cannot read CA key: %v", err)
}

caCertPB, _ := pem.Decode(caCertBytes)
caKeyPB, _ := pem.Decode(caKeyBytes)
caCert, err := x509.ParseCertificate(caCertPB.Bytes)
if err != nil {
return nil, fmt.Errorf("cannot parse CA cert: %v", err)
}

caPrivKey, err := x509.ParsePKCS1PrivateKey(caKeyPB.Bytes)
if err != nil {
return nil, fmt.Errorf("cannot parse CA key: %v", err)
}

// Generate a private key for new client cert.
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
err := fmt.Errorf("cannot generate private key: %v", err)
return nil, err
}

// Prepare certificate template.
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "OpAMP Example Client",
Organization: []string{"OpAMP Example"},
Country: []string{"CA"},
Province: []string{"ON"},
Locality: []string{"City"},
StreetAddress: []string{""},
PostalCode: []string{""},
},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 1000),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}

// Create the client cert. Sign it using CA cert.
certBytes, err := x509.CreateCertificate(rand.Reader, template, caCert, &certPrivKey.PublicKey, caPrivKey)
if err != nil {
err := fmt.Errorf("cannot create certificate: %v", err)
return nil, err
}

publicKeyPEM := new(bytes.Buffer)
pem.Encode(publicKeyPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})

privateKeyPEM := new(bytes.Buffer)
pem.Encode(privateKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
})

// We have a client certificate with a public and private key.
certificate := &protobufs.TLSCertificate{
PublicKey: publicKeyPEM.Bytes(),
PrivateKey: privateKeyPEM.Bytes(),
CaPublicKey: caCertBytes,
}

return certificate, nil
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
37 changes: 11 additions & 26 deletions internal/examples/agent/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"log"
"math/rand"
"os"
"runtime"
Expand All @@ -20,6 +19,7 @@ import (

"github.com/open-telemetry/opamp-go/client"
"github.com/open-telemetry/opamp-go/client/types"
"github.com/open-telemetry/opamp-go/internal"
"github.com/open-telemetry/opamp-go/protobufs"
)

Expand Down Expand Up @@ -89,9 +89,17 @@ func NewAgent(logger types.Logger, agentType string, agentVersion string) *Agent
func (agent *Agent) connect() error {
agent.opampClient = client.NewWebSocket(agent.logger)

tlsConfig, err := internal.CreateClientTLSConfig(
agent.opampClientCert,
"../../certs/certs/ca.cert.pem",
)
if err != nil {
return err
}

settings := types.StartSettings{
OpAMPServerURL: "wss://127.0.0.1:4320/v1/opamp",
TLSConfig: createClientTLSConfig(agent.opampClientCert),
TLSConfig: tlsConfig,
InstanceUid: agent.instanceId.String(),
Callbacks: types.CallbacksStruct{
OnConnectFunc: func() {
Expand Down Expand Up @@ -120,7 +128,7 @@ func (agent *Agent) connect() error {
protobufs.AgentCapabilities_AgentCapabilities_AcceptsOpAMPConnectionSettings,
}

err := agent.opampClient.SetAgentDescription(agent.agentDescription)
err = agent.opampClient.SetAgentDescription(agent.agentDescription)
if err != nil {
return err
}
Expand All @@ -137,29 +145,6 @@ func (agent *Agent) connect() error {
return nil
}

func createClientTLSConfig(clientCert *tls.Certificate) *tls.Config {
// Read the CA's public key. This is the CA that signs the server's certificate.
caCertBytes, err := os.ReadFile("../certs/certs/ca.cert.pem")
if err != nil {
log.Fatalln(err)
}

// Create a certificate pool and make our CA trusted.
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCertBytes); !ok {
log.Fatalln("Cannot append ca.cert.pem")
}

cfg := &tls.Config{
RootCAs: caCertPool,
}
if clientCert != nil {
// If there is a client-side certificate use it for connection too.
cfg.Certificates = []tls.Certificate{*clientCert}
}
return cfg
}

func (agent *Agent) disconnect() {
agent.logger.Debugf("Disconnecting from server...")
agent.opampClient.Stop(context.Background())
Expand Down
49 changes: 6 additions & 43 deletions internal/examples/server/opampsrv/opampsrv.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@ package opampsrv

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"log"
"net/http"
"os"
"path"

"github.com/open-telemetry/opamp-go/internal"
"github.com/open-telemetry/opamp-go/internal/examples/server/data"
"github.com/open-telemetry/opamp-go/protobufs"
"github.com/open-telemetry/opamp-go/server"
Expand Down Expand Up @@ -59,7 +54,11 @@ func (srv *Server) Start() {
},
ListenEndpoint: "127.0.0.1:4320",
}
tlsConfig, err := createServerTLSConfig("../certs")
tlsConfig, err := internal.CreateServerTLSConfig(
"../../certs/certs/ca.cert.pem",
"../../certs/server_certs/server.cert.pem",
"../../certs/server_certs/server.key.pem",
)
if err != nil {
srv.logger.Debugf("Could not load TLS config, working without TLS: %v", err.Error())
}
Expand All @@ -68,42 +67,6 @@ func (srv *Server) Start() {
srv.opampSrv.Start(settings)
}

func createServerTLSConfig(certsDir string) (*tls.Config, error) {
// Read the CA's public key. This is the CA that signs the server's certificate.
caCertBytes, err := os.ReadFile(path.Join(certsDir, "certs/ca.cert.pem"))
if err != nil {
return nil, err
}

// Create a certificate pool and make our CA trusted.
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCertBytes); !ok {
return nil, errors.New("cannot append ca.cert.pem")
}

// Load server's certificate.
cert, err := tls.LoadX509KeyPair(
path.Join(certsDir, "server_certs/server.cert.pem"),
path.Join(certsDir, "server_certs/server.key.pem"),
)
if err != nil {
return nil, fmt.Errorf("tls.LoadX509KeyPair failed: %v", err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
// TODO: verify client cert manually, and allow TOFU option. See manual
// verification example: https://dev.to/living_syn/validating-client-certificate-sans-in-go-i5p
// Instead, we use VerifyClientCertIfGiven which will automatically verify the provided certificate
// is signed by our CA (so TOFU with self-generated client certificate will not work).
ClientAuth: tls.VerifyClientCertIfGiven,
// Allow insecure connections for demo purposes.
InsecureSkipVerify: true,
ClientCAs: caCertPool,
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}

func (srv *Server) Stop() {
srv.opampSrv.Stop(context.Background())
}
Expand Down
Loading

0 comments on commit b24302d

Please sign in to comment.