Skip to content

Conversation

@gtannous-spec
Copy link

@gtannous-spec gtannous-spec commented Nov 3, 2025

PTP TLV Authentication Support with Standardized Secret Mounting

Summary

This PR implements comprehensive support for PTP TLV (Type-Length-Value) message authentication (IEEE 1588-2019 Annex P) in the ptp-operator. It introduces a standardized approach for mounting authentication secrets, adds webhook validation for security configurations, and includes end-to-end conformance tests for authentication scenarios.

Motivation

PTP networks require authentication to prevent unauthorized devices from affecting time synchronization. IEEE 1588-2019 defines TLV-based authentication using Security Association (SA) files that contain cryptographic keys. This PR enables operators to:

  1. Configure PTP authentication via Kubernetes Secrets
  2. Automatically mount secrets to linuxptp-daemon pods
  3. Validate security configurations at admission time
  4. Test authentication scenarios including negative tests (SPP mismatch, rogue clients)

Changes

Webhook Validation (api/v1/ptpconfig_webhook.go)

New validation functions:

  • validateSaFile() - Validates sa_file path format (must start with /etc/ptp-secret-mount/)
  • validateKeyInSecret() - Verifies referenced secret key exists
  • validateSppInSecretKey() - Validates SPP value exists in the referenced secret file
  • GetSecretNameFromSaFilePath() / GetSecretKeyFromSaFilePath() - Path parsing helpers

Validation flow:

  • Extracts sa_file and spp from [global] section of ptp4lConf
  • Validates path structure: /etc/ptp-secret-mount/<secret-name>/<secret-key>
  • Queries Kubernetes API to verify secret and key exist
  • Parses secret content to validate SPP value matches

Controller Changes (controllers/ptpconfig_controller.go)

New syncLinuxptpDaemonSecrets() function:

  • Scans all PtpConfigs for {secretName, sa_file} pairs
  • Deduplicates secrets (multiple profiles can reference same secret)
  • Injects secret volumes into linuxptp-daemon DaemonSet

Secret mounting pattern:

  • Volume name: <secret-name>-tlv-auth
  • Mount path: /etc/ptp-secret-mount/<secret-name>/
  • All keys in secret mounted as files (enables Kubernetes atomic updates)

removeSecurityVolumesFromDaemonSet() - Cleans up old security volumes before re-injection

Merge Logic (pkg/apply/merge.go)

Context-based merge strategy:

  • SourcePtpConfig context: Uses security items from updated (supports deletion)
  • SourcePtpOperatorConfig context: Preserves security items from current

New helper functions:

  • isSecurityItem() - Detects -tlv-auth suffix
  • isSecurityAnnotation() - Detects ptp.openshift.io/secret-hash- prefix
  • Filter/extract functions for volumes, annotations, and mounts

RBAC Updates

New namespace-scoped roles:

  • secrets-manager-role - Read secrets in openshift-ptp
  • daemonsets-manager-role - Update DaemonSets in openshift-ptp

Conformance Tests (test/conformance/serial/ptp.go)

  • SPP Mismatch Test - Verifies slave fails to sync when grandmaster SPP changes
  • Rogue Client Test - Verifies unauthenticated client cannot sync with authenticated network
  • Authentication Flow Test - End-to-end authentication with proper key distribution

CI/Scripts

  • configure-switch-ptp-security.sh - Configures switch1 with authentication
  • ptp-security.yaml - Example security secret with multiple SPP configurations
  • ptpswitchconfig_auth.cfg - Switch configuration with auth settings
  • run-ci-github.sh - Updated to run auth-enabled tests

Configuration Example

1. Create Secret with SA file content:

apiVersion: v1
kind: Secret
metadata:
  name: ptp-security-conf
  namespace: openshift-ptp
type: Opaque
stringData:
  ptp-security.conf: |
    [security_association]
    spp 1
    1 AES128 HEX:FAF48EBA01E7C5966A76CB787AED4E7B
    2 AES256 B64:PKTF9VQz94qPaoAzW4eE3JtFoQ8Ov1OTQSojalWyMbs=

2. Reference in PtpConfig ptp4lConf:

spec:
  profile:
  - name: grandmaster
    ptp4lConf: |
      [global]
      sa_file /etc/ptp-secret-mount/ptp-security-conf/ptp-security.conf
      spp 1
      active_key_id 2
      ...

Path Format

/etc/ptp-secret-mount/<secret-name>/<secret-key>
                     │              │
                     │              └── File name (key from Secret.data)
                     └── Secret name in openshift-ptp namespace

Files Changed

File Lines Description
api/v1/ptpconfig_webhook.go +208 Webhook validation for security configs
controllers/ptpconfig_controller.go +186 Secret volume injection logic
pkg/apply/merge.go +421 Context-based merge for security items
config/rbac/*.yaml +58 Namespace-scoped RBAC for secrets/daemonsets
test/conformance/serial/ptp.go +529 Authentication conformance tests
scripts/*.sh, *.yaml, *.cfg +324 CI scripts and example configs

Testing

  • Webhook rejects invalid sa_file paths
  • Webhook rejects missing secrets/keys
  • Webhook validates SPP exists in referenced secret
  • Controller injects secret volumes correctly
  • Secrets are deduplicated across profiles
  • Authentication works end-to-end (GM ↔ BC ↔ Slave)
  • SPP mismatch prevents synchronization (negative test)
  • Rogue client without auth cannot sync (negative test)

@gtannous-spec gtannous-spec marked this pull request as draft November 3, 2025 14:52
Copy link
Collaborator

@edcdavid edcdavid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First batch of comment, location of changes is correct but need to improve logic to support one secret and sa_file per profile

"net/url"
"os"
"path/filepath"
"sort"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert these changes


// List all PtpConfigs to find which ones reference this secret
ptpConfigList := &ptpv1.PtpConfigList{}
if err := r.List(ctx, ptpConfigList); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

search only in the openshift-ptp namespace (names.namespace)

if profile.PtpSecretName != nil && *profile.PtpSecretName != "" {
secretName := *profile.PtpSecretName
glog.Infof("Found PtpSecretName in profile %s: %s", profileName, secretName)
if chosenSecretName == "" {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code picks the fist secret name from the list of secret names across the all ptpconfig->profiles in the openshift ptp namespace. Instead, the code should get a pair of {secret name, sa_file} for each ptpconfig->profile. Each profile of each ptp config can configure a different sa file and secret.

chosenSecretKeys = map[string]struct{}{k: {}}
glog.Infof("Found PTP security secret '%s' with key '%s'", chosenSecretName, k)
}
} else if len(sec.Data) > 1 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if multiple keys exist in the secret, print a warning that the additional keys will be ignored, no need to process them.
Also rename "chosenSecretKeys" to secretFileContent


// extractSaFileFromPtp4lConf scans the [global] section for a line starting with 'sa_file'
// and returns the remainder of the line as the path.
func extractSaFileFromPtp4lConf(conf string) (string, bool) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is redundant, use populatePtp4lConf to parse the ptp4l config instead

}

// Extract filename from the full path - this will be used as subPath
filename := path.Base(fullPath)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subpath is just the filename in sa_path

}

// MergeDaemonSetForUpdate preserves ptp-security-volume added by ptpconfig_controller.
// This volume is dynamically managed based on PtpConfig CRs and should not be
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here multiple ptp-security volumes are added to the daemonset template, not just one

return ctrl.NewControllerManagedBy(mgr).
For(&ptpv1.PtpConfig{}).
Watches(
&corev1.Secret{},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

watch only the openshift-ptp namespace

// MergeDaemonSetForUpdate preserves ptp-security-volume added by ptpconfig_controller.
// This volume is dynamically managed based on PtpConfig CRs and should not be
// overwritten when PtpOperatorConfig reconciles the DaemonSet template.
func MergeDaemonSetForUpdate(current, updated *uns.Unstructured) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should simply merge the 2 daemonset templates, specifically the volumes.

injectPtpSecurityVolume(daemonSet, chosenSecretName, chosenSecretKey, chosenSecretKeys, saFilePaths, secretHash)

// 7. Update the DaemonSet
if err := r.Update(ctx, daemonSet); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of updating the daemonset directly, use the merge function MergeObjectForUpdate which is called by apply.ApplyObject as in func (r *PtpOperatorConfigReconciler) syncLinuxptpDaemon(ctx context.Context, defaultCfg *ptpv1.PtpOperatorConfig, nodeList *corev1.NodeList) error {:
link
After your change, that function is merging its updated template content with the current template. Now this function needs to do the same. This way both updates are preserved.
This function should first remove old security volumes (because of changing the sa_file name or secret name), then add the new ones, otherwise there will be leftovers.

//+kubebuilder:rbac:groups=ptp.openshift.io,resources=ptpconfigs/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=ptp.openshift.io,resources=ptpconfigs/finalizers,verbs=update
//+kubebuilder:rbac:groups=config.openshift.io,resources=infrastructures,verbs=get;list;watch
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning: this adds secrets list/get/watch to the controller's ClusterRole that grants the controller access to all the secrets in any namespace of the cluster. I think maybe we should create a separate role and rolebinding in config/rbac so that the controller can only be granted access to secrets in the operator's namespace.

Makefile Outdated
## Tool Versions
KUSTOMIZE_VERSION ?= v4.5.7
CONTROLLER_TOOLS_VERSION ?= v0.15.0
CONTROLLER_TOOLS_VERSION ?= latest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might work for you in your arm64 VM, but I'm not sure this is a safe move, as a new version might break the controller build process due to dependencies mismatch.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted to original version

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can test it with v0.19.0. This is the latest and seems to work fine with arm64

@gtannous-spec gtannous-spec force-pushed the test2-auth branch 2 times, most recently from ea8d467 to bb504e8 Compare November 6, 2025 18:37
Copy link
Collaborator

@edcdavid edcdavid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some more comments!

chosenSecretKeys[k] = struct{}{}
}

// 2. Deduplicate mounts (multiple profiles might use same secret and sa_file)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this section since the validation webhook should not allow this to happen

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I removed this code, and left the validation of the ptpconfig in the webhook. in case there is a collision once updating/creating a ptpconfig it will log an error.

}

// Determine which security volumes to use
var securityVolumesToUse []interface{}
Copy link
Collaborator

@edcdavid edcdavid Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic seems too complicated for the merge, try the following

  • remove all the security volumes from the original (before update) template
  • calculates the updated volumes/annotations/mounts for all ptpconfigs in the updated template
  • insert the new volumes/annotations/mounts list in the original linuxptp-daemon template

This way will support adding and deleting any sa_file or secrets and will keep non security changes from original template.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Scheme *runtime.Scheme
Log logr.Logger
Scheme *runtime.Scheme
APIReader client.Reader
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need for this APIReader field. The embedded interface client.Client already has the Reader already.


// Load secret and compute hash
sec := &corev1.Secret{}
if err := r.APIReader.Get(ctx, types.NamespacedName{Namespace: names.Namespace, Name: secretName}, sec); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use r.Get(...) directly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I got rid of APIReader

@gtannous-spec gtannous-spec force-pushed the test2-auth branch 4 times, most recently from eb90b39 to 8b14b1d Compare November 17, 2025 21:36
Copy link
Collaborator

@edcdavid edcdavid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some new comments

$(KUSTOMIZE) build ../config/default | kubectl apply -f -
$(KUSTOMIZE) build ../config/custom | kubectl apply -f -
kubectl patch ptpoperatorconfig default -nopenshift-ptp --type=merge --patch '{"spec": {"ptpEventConfig": {"enableEventPublisher": true, "transportHost": "http://ptp-event-publisher-service-NODE_NAME.openshift-ptp.svc.cluster.local:9043", "storageType": "local-sc"}, "daemonNodeSelector": {"node-role.kubernetes.io/worker": ""}}}'
rebuild-ptpop:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add these changes, these are already covered by make podman-build-ptpop and make-podman-push-ptpop

@@ -0,0 +1,174 @@
#!/bin/bash
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command is too complex for a test. I would follow a similar pattern as for the original switch configuration but with a new configuration including authentication(ptpswitchconfig_auth.cfg and the sa_file maybe derived from the secret ptp-security.yaml):

podman cp ptpswitchconfig_auth.cfg switch1:/etc/ptp4l.conf

$(podman exec switch1 systemctl enable --now ptp4l) || {
    status=$?
    echo "❌ command failed with code $status"
    podman exec switch1 systemctl start ptp4l || true
    podman exec switch1 systemctl status ptp4l
    podman exec switch1 journalctl -u ptp4l
    exit $status
}

In real operation, the switch config will be likely duplicated and not copied from the cluster secret anyways, so not to far from e2e workflow.

PTP_TEST_MODE=dualfollower ginkgo --skip=".*The interfaces supporting ptp can be discovered correctly.*" --skip="Negative - run pmc in a new unprivileged pod on the slave node.*" -v --keep-going --output-dir=$JUNIT_OUTPUT_DIR --junit-report=$JUNIT_OUTPUT_FILE -v "$SUITE"/serial

# Configure switch1 for authentication testing
kubectl apply -f ptp-security.yaml
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional: add all code responsible for enabling authentication in the switch in a function called enable_switch_auth

export CNF_TESTS_IMAGE=test:lptpd

# Configure switch1 for authentication testing
podman cp ptpswitchconfig.cfg switch1:/etc/ptp4l.conf
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional: add all code responsible for disabling authentication in the switch in a function called disable_switch_auth

validSpps, err := parseValidSppsFromSecret(secret)
if err != nil {
ptpconfiglog.Error(err, "failed to parse SPPs from secret", "secret", secret.Name, "profile", profileName)
// Fail open - don't block if we can't parse
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can't parse the secret to extract the spp, we should fail in my opinion. The goal of this function is to validate the spp, if no spp is found, we should not succeed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if no spp is found in the ptpconfig, then the configuration of the ptpconfig should fail?

// parseValidSppsFromSecret extracts all valid SPP numbers from a PTP security secret
// It looks for any line starting with "spp <number>" regardless of structure or sections
func parseValidSppsFromSecret(secret *corev1.Secret) ([]string, error) {
var validSpps []string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format of the secret seems to be the same as the format of the ptp4l config file. Shouldn't we reuse this function to parse it : populatePtp4lConf

Copy link
Author

@gtannous-spec gtannous-spec Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PopulatePtp4lConf parses the ptp4lconf in a ptpconfig and returns a map of sections which points to a map of options inside a specific section..
for instance ptp4lconf has a section global which maps every line to a key, value.
in our test case, the secret has the same section twice [security_association] which includes the spp values, so if we use PopulatePtp4lConf it will duplicate the key.. only the first section will be considered, hence only one spp value will be checked ! (the first one)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I see. We can keep the current code then.

return uns.SetNestedSlice(updated.Object, mergedVolumes, "spec", "template", "spec", "volumes")
}

// mergeSecurityAnnotations implements simplified merge logic for annotations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the comments to make sure they are descriptive of the logic in the function (not just this comment). Explain that annotations are set to detect changes in the secret content.

// - PtpConfig controller: use all volumes/annotations/mounts from updated (even if empty)
// - PtpOperatorConfig controller: use base from updated + preserve security from current
// This supports adding/deleting sa_file or secrets while preserving non-security changes.
func MergeDaemonSetForUpdate(ctx context.Context, current, updated *uns.Unstructured) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following functions are doing mostly the same thing but slightly differently which make them nt good candidate to share more code using template or similar. However , we should at least split them into logical pieces to help with understanding.

mergeSecurityVolumes
mergeSecurityAnnotations
mergeSecurityVolumeMounts

This is for volumes for instance, similar workflow for annotations and volumen mount. I would created helper function to abstract each logical step:
get volumes from current
get volumes from updated
if ptpoperatorconfig -> keep security changes
if ptpconfig -> ovverwrite security changes

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will push a new commit soon with more helper functions for each piece of logic

metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.19.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a couple of problems to build your project because you used controller-gen v0.19.0.
I also think it might be the reason for discrepancies on make manifests because the new version seems to "optimize" the roles by combining several API versions into one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted to the previous version.
so that issue should be fixed

@gtannous-spec gtannous-spec force-pushed the test2-auth branch 3 times, most recently from ccea7ba to 65cd51c Compare November 18, 2025 22:21
Copy link
Contributor

@greyerof greyerof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the new functions validateSecretConflicts() and validateSppInSecret() are iterating throught the ptpconfig profiles and using the populatePtp4lConf to get the ptp config to validate things. Why not doing the secret and spp param validation inside the r.validate(), as it's already iteraring through profiles and populating the ptpconf that you need to review?

}

// contains checks if a string slice contains a specific string
func contains(slice []string, item string) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this func, just use golang's standard slices' package func "slices.Contains()" as in line 192.

return nil
}

for _, profile := range r.Spec.Profile {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original r.validate() func that is called from ValidateCreate() and ValidateUpdate() already iterates over the Profiles. Isn't it possible to check the PtpSecretName there?


// Parse ptp4lConf to get spp value from [global] section
conf := &ptp4lConf{}
if err := conf.populatePtp4lConf(profile.Ptp4lConf, profile.Ptp4lOpts); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The r.validate() func is already populating the ptp4lConf when it iterates over the profiles. Isn't it possible to reuse that one?

secretName, profileName)
}
// For other errors (like permission issues), log but don't block
ptpconfiglog.Error(err, "failed to verify secret existence", "secret", secretName, "profile", profileName)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to return nil here? Shouldn't this be an error too?

@gtannous-spec gtannous-spec force-pushed the test2-auth branch 3 times, most recently from 8f44e16 to 5e31f28 Compare November 24, 2025 21:26
@gtannous-spec gtannous-spec force-pushed the test2-auth branch 2 times, most recently from 6ef17a7 to ff2846c Compare November 25, 2025 10:49
Copy link
Contributor

@greyerof greyerof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more comments.

}

// Skip if no secret specified
if profile.PtpSecretName == nil || *profile.PtpSecretName == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can safely remove this check, as it was already done at the caller at line 221.

if err != nil {
if apierrors.IsNotFound(err) {
return fmt.Errorf("secret '%s' referenced by profile '%s' does not exist in namespace 'openshift-ptp'. Please create the secret before referencing it in PtpConfig",
secretName, profileName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and at line 351 are the only places where the profileName is used... and it's not actually needed. You can return an error like:

  return fmt.Errorf("secret %q for security TLV not found", secretName)

Them, in the caller (L223) you can wrap ip inside a new error:

  if err := r.validateSecretExistsForProfile(conf); err != nil {
    	return fmt.Error("failed to validate profile %s: %w", profile.Name, error)
  }

}

// validateSecretExistsForProfile checks that a single profile's ptpSecretName references an existing secret
func (r *PtpConfig) validateSecretExistsForProfile(ctx context.Context, profile PtpProfile) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but here are some suggestions for improvements:

  • should this function really be a method of the PtpConfig? It's not actually using/modifying anything from it. Maybe it can be just a standalone func.
  • Seems like the only parameters that it actually needs are the spp value (that can be easily obtained outside from the already populated conf struct) and the secret name. A separate func like this could use that conf pointer and just return the spp value.
// Returns the sppValue or empty string if spp key not found. Returns error if the "spp" key is found without any value.
func getSppValueFromPtp4lConf(conf *Ptp4lConf) (string, error) 
  • For the ctx var, I think it can just be created on the fly with context.Background() for the webhookclient.Get():
	err := webhookClient.Get(context.Background(), types.NamespacedName{
		Namespace: "openshift-ptp",
		Name:      secretName,
	}, secret)

Putting all together, the function might just work like this:

func validateSecretExistsForSpp(sppValue, secretName string) error {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the logic behind that, but I don't really see much difference between the current function validateSecretExistsForSpp which uses 'getSppValueFromPtp4lConf' (inline) and that is why we need the ptpProfile argument also


// validateSppInSecret checks that the spp value in ptp4lConf exists in the referenced secret
// Combines parsing and validation in one pass for efficiency
func (r *PtpConfig) validateSppInSecret(profile PtpProfile, secret *corev1.Secret) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this function be a method of the PtpConfig pointer also? The "r" pointer is never used. Also, is the whole profile struct really needed? Seems like it only needs the sppValue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ptpconfig r pointer is used at line 357 inside the function validateSecretExistsForProfile
But you're right we can use a pointer r to call the validateSecretExistsForProfile only, but anyways I think we need the ptpProfile argument as it gets the same profile of the function validateSecretExistsForProfile

Add validateSecretExists() to verify ptpSecretName references exist
Add validateSppInSecret() to validate SPP numbers against secret data
Implement context-based merge to distinguish controller sources
PtpConfig controller: use security volumes from updated (supports deletion)
PtpOperatorConfig controller: preserve security volumes from current
Update merge logic for volumes, annotations, and volume mounts
Add controller source constants to apply package
Update all tests to pass context parameter

Add PTP authentication testing support with conditional enablement
Add configure-switch-ptp-security.sh script to configure external switch
Add ptp-security.yaml template with security associations (SPP 0 and 1)
Add GetPtp4lConfigWithAuth() to conditionally inject auth settings
Add PtpSecretName field conditionally based on PTP_AUTH_ENABLED
Add negative test for SPP mismatch validation (2-minute check)
Update run-ci-github.sh to configure switch and run auth-enabled tests
Apply auth settings to all config creation functions (GM, slave, BC, etc)

Add PTP authentication security tests for attack scenarios
Add rogue client injection test: verifies unauthenticated clients are blocked
Add MITM protection test: verifies tampered messages with wrong keys are dropped
Add replay attack test: verifies seqid_window is configured for anti-replay
Implement robust cleanup with delete and recreate for test-slave1
Add pod stabilization waits between tests to prevent race conditions
Include optional log validation for authentication failure messages
Add helper functions for security item detection (isSecurityItem, isSecurityAnnotation)
Extract filter/extract operations into reusable functions for volumes, annotations, and mounts
Improve SPP validation to fail-closed (reject on parse errors instead of allowing)
Refactor parseValidSppsFromSecret to reuse ptp4lConf parser for consistency
Add detailed comments explaining secret hash change detection mechanism
Downgrade controller-gen from v0.19.0 to v0.15.0 for compatibility
Add kubebuilder marker to skip auto-generation for Ptp4lConf type
Regenerate CRDs and RBAC with ptpSecretName field support
Export ptp4lConfSection to Ptp4lConfSection (public)
Remove wrapper pattern: Ptp4lConf now directly contains sections field
Update all references from p.conf.X to p.X (direct access)
Change populatePtp4lConf receiver from private to public type
Remove manual DeepCopy implementations (now auto-generated)
Regenerate DeepCopy methods for public Ptp4lConf and Ptp4lConfSection
- Move Secret and DaemonSet RBAC from ClusterRole to namespace-scoped Roles
- Add secrets_role.yaml and daemonsets_role.yaml with RoleBindings
- Configure manager cache to watch Secrets only in openshift-ptp namespace
- Remove cluster-scoped kubebuilder RBAC markers from controller
- Update CSV manifests to reflect new RBAC structure

This fixes RBAC errors where the controller attempted cluster-wide Secret
watching but only had namespace-scoped permissions.
Enable UDS pmc tests with PTP authentication:
   - Mount ptp-security-conf Secret in test pods when PTP_AUTH_ENABLED=true
   - Allows pmc from test pods to authenticate to UDS socket
- Convert validateSecretExistsForProfile to standalone function
- Convert validateSecretConflictsForProfile to validateSaFileSecretConflicts
- Convert validateSppInSecret to standalone function
- Extract getSppFromPtp4lConf and getSaFileFromPtp4lConf helpers
- Remove redundant nil checks already performed by caller
- Simplify function signatures to accept only necessary parameters
- Remove unused apierrors import
- Flatten nested if statements for improved readability
- Add PTP_SEC_FOLDER constant for mount path /etc/ptp-secret-mount/
- Add helper functions to extract secret name and key from sa_file path
- Add webhook validation for sa_file path format and secret key existence
- Implement deduplication in controller for unique secret mounts
- Refactor validateSppInSecret to use minimal arguments
- Mount entire secret as volume (all keys available as files)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants