Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions device-discovery-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,17 @@ The Device Discovery Agent can be configured using multiple sources. Configurati
The configuration file uses KEY=VALUE format (not YAML):

```bash
# Service Endpoints
# Service Endpoints (Required)
OBM_SVC=localhost
OBS_SVC=localhost
OBM_PORT=50051
KEYCLOAK_URL=keycloak.example.com
CA_CERT=/etc/intel_edge_node/orch-ca-cert/orch-ca.pem

# Auto-detection
AUTO_DETECT=true

# Optional Settings
# CA_CERT=/etc/intel_edge_node/orch-ca-cert/orch-ca.pem # Optional - uses system CAs if not provided
DEBUG=false
TIMEOUT=5m
DISABLE_INTERACTIVE=false
Expand Down Expand Up @@ -194,14 +194,16 @@ The following flags are required unless specified in a configuration file:
- `-obs-svc` - Onboarding stream service address (hostname or IP)
- `-obm-port` - Onboarding manager port (default: 50051)
- `-keycloak-url` - Keycloak authentication URL (hostname or IP)
- `-ca-cert` - Path to CA certificate (required for TLS)
- `-mac` - MAC address of the device (required unless using `-auto-detect`)

### Optional Flags

**Configuration File:**
- `-config` - Path to configuration file in KEY=VALUE format (e.g., `/etc/edge-node/node/confs/device-discovery-agent.env`)

**TLS Configuration:**
- `-ca-cert` - Path to CA certificate (optional, uses system default CAs if not provided)

**Device Information (auto-detected if not provided):**
- `-serial` - Serial number (auto-detected using dmidecode)
- `-uuid` - System UUID (auto-detected using dmidecode)
Expand All @@ -224,43 +226,51 @@ The following flags are required unless specified in a configuration file:
./device-discovery-agent -config /etc/edge-node/node/confs/device-discovery-agent.env
```

#### 2. Auto-detect all system information
#### 2. Auto-detect all system information (using system default CAs)
```bash
./device-discovery-agent \
-obm-svc obm.example.com \
-obs-svc obs.example.com \
-obm-port 50051 \
-keycloak-url keycloak.example.com \
-ca-cert /etc/intel_edge_node/orch-ca-cert/orch-ca.pem \
-auto-detect
```

#### 3. Specify MAC address, auto-detect other info
#### 3. Auto-detect with custom CA certificate
```bash
./device-discovery-agent \
-obm-svc obm.example.com \
-obs-svc obs.example.com \
-obm-port 50051 \
-keycloak-url keycloak.example.com \
-ca-cert /etc/intel_edge_node/orch-ca-cert/orch-ca.pem \
-auto-detect
```

#### 4. Specify MAC address, auto-detect other info
```bash
./device-discovery-agent \
-obm-svc obm.example.com \
-obs-svc obs.example.com \
-obm-port 50051 \
-keycloak-url keycloak.example.com \
-mac 00:11:22:33:44:55
```

#### 4. Fully manual configuration
#### 5. Fully manual configuration
```bash
./device-discovery-agent \
-obm-svc obm.example.com \
-obs-svc obs.example.com \
-obm-port 50051 \
-keycloak-url keycloak.example.com \
-ca-cert /etc/intel_edge_node/orch-ca-cert/orch-ca.pem \
-mac 00:11:22:33:44:55 \
-serial ABC123 \
-uuid 12345678-1234-1234-1234-123456789012 \
-ip 192.168.1.100
```

#### 5. With debug mode and extra hosts
#### 6. With debug mode and extra hosts
```bash
./device-discovery-agent \
-obm-svc obm.example.com \
Expand All @@ -274,7 +284,7 @@ The following flags are required unless specified in a configuration file:
-extra-hosts "registry.local:10.0.0.1,api.local:10.0.0.2"
```

#### 6. Override config file values with CLI flags
#### 7. Override config file values with CLI flags
```bash
# Config file has OBM_SVC=localhost, but we override it
./device-discovery-agent \
Expand Down
7 changes: 4 additions & 3 deletions device-discovery-agent/cmd/device-discovery/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func parseFinalFlags(cfg *config.Config) {

// Optional configuration - use current values as defaults
flag.StringVar(&cfg.ExtraHosts, "extra-hosts", cfg.ExtraHosts, "Additional host mappings (comma-separated: 'host1:ip1,host2:ip2')")
flag.StringVar(&cfg.CaCertPath, "ca-cert", cfg.CaCertPath, "Path to CA certificate (required)")
flag.StringVar(&cfg.CaCertPath, "ca-cert", cfg.CaCertPath, "Path to CA certificate (optional, uses system CAs if not provided)")
flag.BoolVar(&cfg.Debug, "debug", cfg.Debug, "Enable debug mode with timeout")
flag.DurationVar(&cfg.Timeout, "timeout", cfg.Timeout, "Timeout duration for debug mode")
flag.BoolVar(&cfg.DisableInteractiveMode, "disable-interactive", cfg.DisableInteractiveMode, "Disable interactive mode fallback")
Expand All @@ -167,10 +167,11 @@ func printUsage() {
fmt.Fprintf(os.Stderr, " Onboarding manager port\n")
fmt.Fprintf(os.Stderr, " -keycloak-url string\n")
fmt.Fprintf(os.Stderr, " Keycloak authentication URL\n")
fmt.Fprintf(os.Stderr, " -ca-cert string\n")
fmt.Fprintf(os.Stderr, " Path to CA certificate\n")
fmt.Fprintf(os.Stderr, " -mac string\n")
fmt.Fprintf(os.Stderr, " MAC address of the device (required unless -auto-detect is used)\n")
fmt.Fprintf(os.Stderr, "\nOptional Configuration:\n")
fmt.Fprintf(os.Stderr, " -ca-cert string\n")
fmt.Fprintf(os.Stderr, " Path to CA certificate (optional, uses system CAs if not provided)\n")
fmt.Fprintf(os.Stderr, "\nOptional Device Information:\n")
fmt.Fprintf(os.Stderr, " -serial string\n")
fmt.Fprintf(os.Stderr, " Serial number (auto-detected if not provided)\n")
Expand Down
19 changes: 12 additions & 7 deletions device-discovery-agent/configs/device-discovery-agent.env
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ OBM_PORT=50051
# CLI flag: -keycloak-url
KEYCLOAK_URL=keycloak.example.com

# CA Certificate Path (required for TLS verification)
# CLI flag: -ca-cert
CA_CERT=/etc/intel_edge_node/orch-ca-cert/orch-ca.pem

# ============================================================================
# REQUIRED (unless AUTO_DETECT=true): Device Information
# ============================================================================
Expand Down Expand Up @@ -75,6 +71,11 @@ AUTO_DETECT=true
# OPTIONAL: Additional Configuration
# ============================================================================

# CA Certificate Path (optional for TLS verification)
# CLI flag: -ca-cert
# Note: If not provided, system default CA certificates will be used
# CA_CERT=/etc/intel_edge_node/orch-ca-cert/orch-ca.pem

# Additional host-to-IP mappings for /etc/hosts
# CLI flag: -extra-hosts
# Format: host1:ip1,host2:ip2,host3:ip3
Expand Down Expand Up @@ -120,9 +121,13 @@ USE_KERNEL_ARGS=false
# PRIORITY ORDER (highest to lowest)
# ============================================================================
# 1. CLI flags (highest priority)
# 2. Kernel command line arguments (if -use-kernel-args is passed)
# 3. This configuration file (loaded via -config flag)
# 4. Default values hardcoded in the application
# 2. This configuration file (loaded via -config flag)
# 3. Kernel command line arguments (if -use-kernel-args is passed)
# 4. Default values hardcoded in the application (lowest priority)
#
# Note: The config file overrides kernel arguments because the config file
# is reloaded after kernel args are parsed (unless -use-kernel-args was
# specified directly on CLI, in which case kernel args take precedence).

# ============================================================================
# USAGE EXAMPLES
Expand Down
58 changes: 40 additions & 18 deletions device-discovery-agent/internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,29 @@ func fetchAccessToken(keycloakURL string, clientID string, clientSecret string,
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

// Load the CA certificate
caCertPool, err := loadCACertPool(caCertPath)
if err != nil {
return "", fmt.Errorf("error loading CA certificate: %v", err)
}

// Create an HTTP client with the CA certificate
// Configure TLS
var tlsConfig *tls.Config
if caCertPath != "" {
// Load the CA certificate from provided path
caCertPool, err := loadCACertPool(caCertPath)
if err != nil {
return "", fmt.Errorf("error loading CA certificate: %v", err)
}
tlsConfig = &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
} else {
// Use system default CA certificates
tlsConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
}

// Create an HTTP client with TLS configuration
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
TLSClientConfig: tlsConfig,
},
}

Expand Down Expand Up @@ -92,10 +103,23 @@ func fetchReleaseToken(releaseServerURL string, accessToken string, caCertPath s
return "", fmt.Errorf("access token is required")
}

// Load CA certificate
caCertPool, err := loadCACertPool(caCertPath)
if err != nil {
return "", fmt.Errorf("error loading CA certificate: %v", err)
// Configure TLS
var tlsConfig *tls.Config
if caCertPath != "" {
// Load CA certificate from provided path
caCertPool, err := loadCACertPool(caCertPath)
if err != nil {
return "", fmt.Errorf("error loading CA certificate: %v", err)
}
tlsConfig = &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
} else {
// Use system default CA certificates
tlsConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
}

// Construct the HTTP request
Expand All @@ -107,12 +131,10 @@ func fetchReleaseToken(releaseServerURL string, accessToken string, caCertPath s
// Add the authorization header with the bearer token
req.Header.Set("Authorization", "Bearer "+accessToken)

// Create an HTTP client with CA certificate
// Create an HTTP client with TLS configuration
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
TLSClientConfig: tlsConfig,
},
}

Expand Down
4 changes: 1 addition & 3 deletions device-discovery-agent/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,7 @@ func Validate(cfg *Config) error {
if cfg.KeycloakURL == "" {
missing = append(missing, "KEYCLOAK_URL")
}
if cfg.CaCertPath == "" {
missing = append(missing, "CA_CERT")
}
// CA_CERT is optional - if not provided, system default CAs will be used
if cfg.MacAddr == "" {
missing = append(missing, "MAC")
}
Expand Down
6 changes: 2 additions & 4 deletions device-discovery-agent/internal/config/config_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,8 @@ func FuzzValidate(f *testing.F) {
if cfg.KeycloakURL == "" {
t.Error("Validation passed but KeycloakURL is empty")
}
if cfg.CaCertPath == "" {
t.Error("Validation passed but CaCertPath is empty")
}
// Note: MAC, SERIAL, UUID, IPAddress can be auto-detected
// CaCertPath is optional - system default CAs will be used if empty
// MAC, SERIAL, UUID, IPAddress can be auto-detected
// so validation may pass even if they're empty
}
})
Expand Down
22 changes: 5 additions & 17 deletions device-discovery-agent/internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,32 +419,20 @@ func TestValidate_MissingCriticalFields(t *testing.T) {
{
name: "missing KeycloakURL",
cfg: &Config{
ObmSvc: "obm.example.com",
ObsSvc: "obs.example.com",
ObmPort: 50051,
CaCertPath: "/path/to/cert",
ObmSvc: "obm.example.com",
ObsSvc: "obs.example.com",
ObmPort: 50051,
},
wantErr: true,
},
{
name: "missing CaCertPath",
name: "missing non-critical fields (Serial, UUID, IP, MAC, CaCertPath)",
cfg: &Config{
ObmSvc: "obm.example.com",
ObsSvc: "obs.example.com",
ObmPort: 50051,
KeycloakURL: "keycloak.example.com",
},
wantErr: true,
},
{
name: "missing non-critical fields (Serial, UUID, IP, MAC)",
cfg: &Config{
ObmSvc: "obm.example.com",
ObsSvc: "obs.example.com",
ObmPort: 50051,
KeycloakURL: "keycloak.example.com",
CaCertPath: "/path/to/cert",
// SerialNumber, UUID, IPAddress, MacAddr can be auto-detected
// SerialNumber, UUID, IPAddress, MacAddr, CaCertPath can be auto-detected or use system defaults
},
wantErr: false,
},
Expand Down
34 changes: 22 additions & 12 deletions device-discovery-agent/internal/mode/noninteractive/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package noninteractive

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
Expand Down Expand Up @@ -55,20 +56,29 @@ func NewClient(address string, port int, mac, uuid, serial, ipAddress, caCertPat

// createSecureConnection creates a secure gRPC connection with TLS.
func createSecureConnection(target string, caCertPath string) (*grpc.ClientConn, error) {
// Load the CA certificate
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, fmt.Errorf("failed to read CA certificate: %v", err)
}
var creds credentials.TransportCredentials

// Create a certificate pool from the CA certificate
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(caCert) {
return nil, fmt.Errorf("failed to append CA certificate to cert pool")
}
if caCertPath != "" {
// Load the CA certificate from the provided path
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, fmt.Errorf("failed to read CA certificate: %v", err)
}

// Create a certificate pool from the CA certificate
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(caCert) {
return nil, fmt.Errorf("failed to append CA certificate to cert pool")
}

// Create the credentials using the certificate pool
creds := credentials.NewClientTLSFromCert(certPool, "")
// Create the credentials using the certificate pool
creds = credentials.NewClientTLSFromCert(certPool, "")
} else {
// Use system default CA certificates
creds = credentials.NewTLS(&tls.Config{
MinVersion: tls.VersionTLS12,
})
}

// Create the gRPC connection with TLS credentials
conn, err := grpc.NewClient(
Expand Down