A Kubernetes operator that bridges Hardware Security Module (HSM) data storage with Kubernetes Secrets, providing true secret portability th
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix

+160 -7
+1 -1
Dockerfile
··· 11 11 # and so that source changes don't invalidate our downloaded layer 12 12 RUN go mod download 13 13 14 - COPY cmd/ cmd/ 14 + COPY cmd/manager cmd/manager 15 15 COPY api/ api/ 16 16 COPY internal/ internal/ 17 17
+26 -1
Dockerfile.discovery
··· 19 19 # Build discovery binary only 20 20 RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o discovery cmd/discovery/main.go 21 21 22 + FROM alpine:3.22 AS base 23 + 24 + # Update Alpine packages 25 + RUN apk update 26 + 27 + # Install compilation tools 28 + RUN apk add --no-cache \ 29 + git \ 30 + gcc \ 31 + g++ \ 32 + make \ 33 + cmake \ 34 + pkgconfig \ 35 + openssl-dev \ 36 + pcsc-lite-dev \ 37 + libusb-dev \ 38 + autoconf \ 39 + automake \ 40 + libtool 41 + 42 + RUN cd / && git clone https://github.com/CardContact/sc-hsm-embedded.git 43 + WORKDIR /sc-hsm-embedded 44 + RUN autoreconf -fi && ./configure 45 + RUN make && make install 46 + 22 47 FROM alpine:3.22 23 48 RUN apk add --no-cache opensc-dev ccid pcsc-lite openssl libtool libusb 24 49 ··· 30 55 COPY --from=builder /workspace/manager . 31 56 USER 65532:65532 32 57 33 - ENTRYPOINT ["/manager"] 58 + ENTRYPOINT ["/manager"]
+133 -5
internal/discovery/usb.go
··· 274 274 return devices, nil 275 275 } 276 276 277 - // findHSMDevicePath looks for HSM device paths without requiring root access 278 - func (u *USBDiscoverer) findHSMDevicePath(vendorID, productID string) string { 277 + // findHSMDevicePath looks for HSM device paths using sysfs information 278 + func (u *USBDiscoverer) findHSMDevicePath(sysfsPath, vendorID, productID string) string { 279 + // Method 1: Extract bus/device numbers and construct /dev/bus/usb path 280 + if usbDevPath := u.findUSBDevicePath(sysfsPath); usbDevPath != "" { 281 + u.logger.V(2).Info("Found USB device path", "path", usbDevPath, "vendorId", vendorID, "productId", productID) 282 + return usbDevPath 283 + } 284 + 285 + // Method 2: Check for serial device nodes (ttyUSB, ttyACM) 286 + if serialPath := u.findSerialDevicePath(sysfsPath); serialPath != "" { 287 + u.logger.V(2).Info("Found serial device path", "path", serialPath, "vendorId", vendorID, "productId", productID) 288 + return serialPath 289 + } 290 + 291 + // Method 3: Check for HID device nodes (hidraw) 292 + if hidPath := u.findHIDDevicePath(sysfsPath); hidPath != "" { 293 + u.logger.V(2).Info("Found HID device path", "path", hidPath, "vendorId", vendorID, "productId", productID) 294 + return hidPath 295 + } 296 + 297 + // Method 4: Fallback to common paths (legacy compatibility) 298 + if commonPath := u.findCommonDevicePath(vendorID, productID); commonPath != "" { 299 + u.logger.V(2).Info("Found common device path", "path", commonPath, "vendorId", vendorID, "productId", productID) 300 + return commonPath 301 + } 302 + 303 + return "" 304 + } 305 + 306 + // findUSBDevicePath constructs /dev/bus/usb path from sysfs path 307 + func (u *USBDiscoverer) findUSBDevicePath(sysfsPath string) string { 308 + // Extract bus and device numbers from sysfs path 309 + // sysfsPath looks like: /sys/bus/usb/devices/1-6 310 + // We need to read busnum and devnum files 311 + 312 + busNumPath := filepath.Join(sysfsPath, "busnum") 313 + devNumPath := filepath.Join(sysfsPath, "devnum") 314 + 315 + busNum, err1 := u.readSysfsFile(busNumPath) 316 + devNum, err2 := u.readSysfsFile(devNumPath) 317 + 318 + if err1 != nil || err2 != nil { 319 + u.logger.V(2).Info("Could not read bus/dev numbers", "sysfsPath", sysfsPath, "busErr", err1, "devErr", err2) 320 + return "" 321 + } 322 + 323 + busNum = strings.TrimSpace(busNum) 324 + devNum = strings.TrimSpace(devNum) 325 + 326 + // Format as 3-digit numbers for /dev/bus/usb path 327 + if len(busNum) == 1 { 328 + busNum = "00" + busNum 329 + } else if len(busNum) == 2 { 330 + busNum = "0" + busNum 331 + } 332 + 333 + if len(devNum) == 1 { 334 + devNum = "00" + devNum 335 + } else if len(devNum) == 2 { 336 + devNum = "0" + devNum 337 + } 338 + 339 + // Construct device path 340 + usbDevPath := fmt.Sprintf("/dev/bus/usb/%s/%s", busNum, devNum) 341 + 342 + // Check both container and host-mounted paths 343 + if u.deviceExists(usbDevPath) { 344 + return usbDevPath 345 + } 346 + 347 + return "" 348 + } 349 + 350 + // findSerialDevicePath looks for ttyUSB/ttyACM device nodes 351 + func (u *USBDiscoverer) findSerialDevicePath(sysfsPath string) string { 352 + // Look for tty subdirectories in sysfs 353 + ttyPattern := filepath.Join(sysfsPath, "*", "tty", "tty*") 354 + matches, err := filepath.Glob(ttyPattern) 355 + if err != nil { 356 + return "" 357 + } 358 + 359 + for _, match := range matches { 360 + devName := filepath.Base(match) 361 + devPath := "/dev/" + devName 362 + if u.deviceExists(devPath) { 363 + return devPath 364 + } 365 + } 366 + 367 + return "" 368 + } 369 + 370 + // findHIDDevicePath looks for hidraw device nodes 371 + func (u *USBDiscoverer) findHIDDevicePath(sysfsPath string) string { 372 + // Look for hidraw subdirectories in sysfs 373 + hidPattern := filepath.Join(sysfsPath, "*", "hidraw", "hidraw*") 374 + matches, err := filepath.Glob(hidPattern) 375 + if err != nil { 376 + return "" 377 + } 378 + 379 + for _, match := range matches { 380 + devName := filepath.Base(match) 381 + devPath := "/dev/" + devName 382 + if u.deviceExists(devPath) { 383 + return devPath 384 + } 385 + } 386 + 387 + return "" 388 + } 389 + 390 + // findCommonDevicePath checks common device paths (legacy fallback) 391 + func (u *USBDiscoverer) findCommonDevicePath(vendorID, _ string) string { 279 392 // Common HSM device paths that might be accessible without root 280 393 commonPaths := []string{ 281 394 "/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2", "/dev/ttyUSB3", ··· 294 407 } 295 408 296 409 for _, path := range commonPaths { 297 - if _, err := os.Stat(path); err == nil { 298 - u.logger.V(2).Info("Found HSM device path", "path", path, "vendorId", vendorID, "productId", productID) 410 + if u.deviceExists(path) { 299 411 return path 300 412 } 301 413 } ··· 303 415 return "" 304 416 } 305 417 418 + // deviceExists checks if a device path exists, trying both container and host-mounted paths 419 + func (u *USBDiscoverer) deviceExists(devicePath string) bool { 420 + // Try both container and host-mounted paths 421 + paths := []string{ 422 + devicePath, // /dev/ttyUSB0 or /dev/bus/usb/001/002 423 + "/host" + devicePath, // /host/dev/ttyUSB0 or /host/dev/bus/usb/001/002 424 + } 425 + 426 + for _, path := range paths { 427 + if _, err := os.Stat(path); err == nil { 428 + return true 429 + } 430 + } 431 + return false 432 + } 433 + 306 434 // parseUSBDeviceFromSysfs parses USB device information directly from sysfs (native lsusb equivalent) 307 435 func (u *USBDiscoverer) parseUSBDeviceFromSysfs(devicePath string) *USBDevice { 308 436 device := &USBDevice{ ··· 340 468 } 341 469 342 470 // Try to find associated device paths 343 - device.DevicePath = u.findHSMDevicePath(device.VendorID, device.ProductID) 471 + device.DevicePath = u.findHSMDevicePath(devicePath, device.VendorID, device.ProductID) 344 472 345 473 // Add additional device info 346 474 device.DeviceInfo["sysfs-path"] = devicePath