BYOD Certificate Management with Vault and dsec
Complete operational runbook for issuing, tracking, and managing BYOD device certificates using HashiCorp Vault PKI with dsec-integrated password management.
Overview
This runbook provides enterprise-grade procedures for BYOD certificate management in the Domus home enterprise environment, integrating:
-
HashiCorp Vault PKI - Certificate issuance via
domus-byodrole (90-day TTL) -
dsec Secrets Management - Secure PKCS#12 password storage
-
Automated Tooling -
issue-byod-certscript with enterprise security controls -
ISE Integration - 802.1X EAP-TLS authentication to Domus-Secure SSID
Architecture
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ certmgr-01 │────▶│ Vault PKI │────▶│ PKCS#12 │
│ (issue) │ │ {vault-pki-int}/ │ │ Bundle │
└──────────────┘ │ {vault-byod-role} │ └──────────────┘
└─────────────────┘ │
│
┌─────────────────────────────────────────────┘
│
▼
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Workstation │────▶│ dsec │ │ Device │
│ (password) │ │ {dsec-vault}/{dsec-byod-path}/*/ │ │ (import) │
└──────────────┘ └─────────────────┘ └──────────────┘
Security Model
Password Storage Strategy
Enterprise Security Principle: PKCS#12 passwords are stored in dsec, NOT on filesystem.
| Component | Storage Location | Access Control |
|---|---|---|
Certificate |
certmgr-01.inside.domusdigitalis.dev: |
File permissions (600) |
Private Key |
Embedded in PKCS#12 (encrypted) |
PKCS#12 password |
PKCS#12 Password |
dsec: |
Age encryption + DSEC_SECURITY_MODE |
Metadata |
certmgr-01.inside.domusdigitalis.dev: |
File permissions (600), NO passwords |
CA Chain |
Embedded in PKCS#12 |
Public (trusted) |
Why dsec for Password Storage?
-
Centralized secrets management - Consistent with ISE credentials, Vault tokens
-
Encrypted at rest - Age encryption with master key
-
Access control - DSEC_SECURITY_MODE enforcement
-
Audit trail - Track password access
-
Backup/DR - Part of enterprise secrets backup strategy
-
No plaintext on disk - Passwords never stored in filesystem
Prerequisites
-
SSH access to certmgr-01.inside.domusdigitalis.dev
-
Vault token with
pki_int/issue/domus-byodpermissions -
dsec configured on workstation (for password storage/retrieval)
-
netapi configured (for ISE verification)
-
vaultCLI (installed) -
issue-byod-certscript (/home/ansible/bin/issue-byod-cert) -
openssl,jq,shred(system tools)
-
dsecCLI (configured with d000 vault) -
netapiCLI (ISE API access) -
scp(file transfer)
Certificate Issuance Workflow
Step 1: SSH to certmgr-01.inside.domusdigitalis.dev
# From workstation
ssh certmgr-01.inside.domusdigitalis.dev
Step 2: Set Vault Environment
|
dsource is NOT available on certmgr-01.inside.domusdigitalis.dev - it’s a workstation tool. You must get the Vault token from your workstation BEFORE SSHing to certmgr-01.inside.domusdigitalis.dev.
|
# Now on certmgr-01.inside.domusdigitalis.dev:
# Set Vault connection
export VAULT_ADDR='http://127.0.0.1:8200'
# Paste the token from your workstation
export VAULT_TOKEN='<paste token here>'
# Verify Vault status
vault status
# Expected: Sealed: false
Step 3: Issue Certificate
# Syntax: issue-byod-cert <device-model> <owner-name> [platform]
# Platform: android or ios (default: android)
# Example for Samsung Z Fold 7
issue-byod-cert <device-model> <owner-name> android
# Example for iPhone
issue-byod-cert <device-model> <owner-name> ios
╔════════════════════════════════════════════════════════════════╗
║ BYOD Certificate Issuance - Home Enterprise Edition ║
╚════════════════════════════════════════════════════════════════╝
→ Device: <device-model>
→ Owner: <owner-name>
→ Platform: android
→ Common Name: <device-model>-<owner-name>.byod.inside.domusdigitalis.dev
[1/7] Issuing certificate from Vault PKI...
✓ Certificate issued successfully
Serial: 6f:3a:d1:8c:2e:95:4b:7a:1d:f2:8e:6c:9b:4a:5e:3f:2c:7d:9e:1a
Expires: 2026-05-29 14:23:45 PDT
[2/7] Extracting certificate components...
✓ Components extracted
[3/7] Generating secure PKCS#12 password...
✓ Password generated (see below for storage)
[4/7] Creating PKCS#12 bundle...
✓ PKCS#12 created: zfold7-evanusmodestus.p12
[5/7] Securely deleting intermediate files...
✓ Cleanup complete
[6/7] Saving certificate metadata...
✓ Metadata saved (password NOT stored on filesystem)
[7/7] Enterprise password management...
╔════════════════════════════════════════════════════════════════╗
║ Certificate Issued ║
╚════════════════════════════════════════════════════════════════╝
Device: <device-model>-<owner-name>
File: /home/ansible/byod-certs/<device-model>/<device-model>-<owner-name>.p12
Expires: 2026-05-29 14:23:45 PDT
╔════════════════════════════════════════════════════════════════╗
║ PKCS#12 IMPORT PASSWORD ║
╠════════════════════════════════════════════════════════════════╣
║ XfR7mK2pL9nQ3vB8wZ4c ║
╚════════════════════════════════════════════════════════════════╝
▸ Store password in dsec (run from workstation):
echo "<p12-password>" | dsec set d000 byod/<device-model>-<owner-name>/p12-password
▸ Retrieve password later:
dsec show d000 byod/<device-model>-<owner-name>/p12-password
▸ Next Steps:
1. Store password in dsec (command above)
2. Transfer <device-model>-<owner-name>.p12 to your workstation:
scp certmgr-01.inside.domusdigitalis.dev:/home/ansible/byod-certs/<device-model>/<device-model>-<owner-name>.p12 ~/Downloads/
3. Transfer to device and import
4. Configure WiFi: Domus-Secure, EAP-TLS, select certificate
✓ Certificate issuance complete!
Step 4: Store Password in dsec
CRITICAL: The password is displayed but NOT stored on certmgr-01.inside.domusdigitalis.dev.
# Exit from certmgr-01.inside.domusdigitalis.dev
exit
# Back on your workstation, store the password in dsec
echo "<p12-password>" | dsec set d000 byod/<device-model>-<owner-name>/p12-password
# Verify storage
dsec show d000 byod/<device-model>-<owner-name>/p12-password
# Should output: <p12-password>
|
Copy-paste the exact command from the script output - it includes the device name and password automatically. |
Step 5: Transfer PKCS#12 to Workstation
# From workstation
scp certmgr-01.inside.domusdigitalis.dev:/home/ansible/byod-certs/<device-model>/<device-model>-<owner-name>.p12 ~/Downloads/
Step 6: Transfer to Device and Import
Android
-
Transfer .p12 via
adb pushor email -
Settings → Security → Install from storage
-
Select the .p12 file, enter password from dsec
-
Name the certificate (e.g., "Domus-BYOD")
-
Connect to Domus-Secure, select EAP-TLS, choose certificate
iOS/iPadOS (Requires mobileconfig Profile)
iOS does not expose EAP-TLS in WiFi settings. Create a .mobileconfig profile:
# Get base64 of the .p12
P12_B64=$(ssh certmgr-01.inside.domusdigitalis.dev "base64 -w0 /home/ansible/byod-certs/ipad/ipad-<owner-name>.p12")
# Create profile (unquoted ENDPROFILE so variable expands)
cat > ~/Downloads/domus-byod-ios.mobileconfig << ENDPROFILE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>Password</key>
<string><p12-password></string>
<key>PayloadCertificateFileName</key>
<string>ipad-<owner-name>.p12</string>
<key>PayloadContent</key>
<data>\$<base64-encoded-p12></data>
<key>PayloadIdentifier</key>
<string>com.domusdigitalis.cert</string>
<key>PayloadType</key>
<string>com.apple.security.pkcs12</string>
<key>PayloadUUID</key>
<string>11111111-1111-1111-1111-111111111111</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
<dict>
<key>AutoJoin</key>
<true/>
<key>EAPClientConfiguration</key>
<dict>
<key>AcceptEAPTypes</key>
<array>
<integer>13</integer>
</array>
<key>PayloadCertificateAnchorUUID</key>
<array>
<string>11111111-1111-1111-1111-111111111111</string>
</array>
</dict>
<key>EncryptionType</key>
<string>WPA2</string>
<key>SSID_STR</key>
<string>Domus-Secure</string>
<key>PayloadIdentifier</key>
<string>com.domusdigitalis.wifi</string>
<key>PayloadType</key>
<string>com.apple.wifi.managed</string>
<key>PayloadUUID</key>
<string>22222222-2222-2222-2222-222222222222</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>Domus BYOD</string>
<key>PayloadIdentifier</key>
<string>com.domusdigitalis.byod</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>33333333-3333-3333-3333-333333333333</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
ENDPROFILE
# Verify cert data embedded (~8000+ chars)
grep -o '<data>.*</data>' ~/Downloads/domus-byod-ios.mobileconfig | wc -c
-
Email
domus-byod-ios.mobileconfigto yourself -
Tap attachment on iOS device
-
Ajustes → Perfil descargado → Instalar
-
Enter device passcode
-
Device auto-connects to Domus-Secure
|
Privacy Warning: iOS shows "Advertencia de privacidad" when using real MAC. This is normal for enterprise WiFi. Random MAC works with cert-auth but don’t change it mid-session. |
Password Management
Retrieve Password
# From workstation
dsec show d000 byod/<device>-<owner>/p12-password
# Example
dsec show d000 byod/<device-model>-<owner-name>/p12-password
List All BYOD Passwords
# List all stored BYOD passwords
dsec list d000 byod/
# Expected output:
# byod/<device-model>-<owner-name>/p12-password
# byod/iphone16-<owner-name>/p12-password
Delete Password After Import
|
After the device successfully imports the certificate, the PKCS#12 password is no longer needed. Consider deleting it from dsec for security:
Trade-off: If user needs to re-import (e.g., factory reset), you’ll need to re-export the certificate with a new password. |
Certificate Tracking
View Issued Certificates
# On certmgr-01.inside.domusdigitalis.dev
cat /home/ansible/byod-certs/.metadata/certificates.json | jq -r '.[] | "\(.device)-\(.owner) | \(.platform) | \(.expiration_date) | \(.status)"' | column -t -s '|'
zfold7-evanusmodestus android 2026-05-29 active
iphone16-evanusmodestus ios 2026-06-15 active
Check Expiring Certificates
# Find certificates expiring in next 30 days
cat /home/ansible/byod-certs/.metadata/certificates.json | jq -r --argjson now "$(date +%s)" --argjson days 30 '
.[] | select((.expiration_timestamp) <= ($now + ($days * 86400))) |
"\(.device)-\(.owner) expires in \(((.expiration_timestamp - $now) / 86400) | floor) days"
'
Certificate Renewal
Use Case: Certificate approaching 90-day expiration.
# Issue new certificate (same process as initial issuance)
ssh certmgr-01.inside.domusdigitalis.dev
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='<your-vault-token>'
# Issue replacement certificate
issue-byod-cert <device-model> <owner-name> android
# Store new password in dsec (overwrites old password)
echo "<p12-password>" | dsec set d000 byod/<device-model>-<owner-name>/p12-password
# User imports new certificate
# Old certificate automatically stops working after expiration
|
Do NOT revoke the old certificate until user confirms new certificate is working. |
Certificate Revocation
Use Case: Device lost/stolen, employee termination, security incident.
Step 1: Identify Certificate Serial Number
# On certmgr-01.inside.domusdigitalis.dev
cat /home/ansible/byod-certs/.metadata/certificates.json | jq -r '.[] | select(.device + "-" + .owner == "<device-model>-<owner-name>") | .serial_number'
# Example output: <serial-number>
Step 2: Revoke in Vault
# On certmgr-01.inside.domusdigitalis.dev
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='<your-vault-token>'
# Revoke certificate
vault write pki_int/revoke serial_number="<serial-number>"
# Verify revocation
vault read pki_int/cert/<serial-number>
# Should show: revocation_time: <timestamp>
Step 3: Block Device in ISE (Immediate Effect)
|
CRL/OCSP Not Yet Configured ISE does not currently check Vault CRL. Revoked certificates will still authenticate until ISE is configured to check revocation status. For immediate blocking, use ISE endpoint blocking. |
# From workstation, get device MAC address
netapi ise mnt sessions | grep -i "<device-model>-<owner-name>"
# Block endpoint
netapi ise block-endpoint <mac-address> --reason "Certificate revoked - device lost"
Step 4: Delete Password from dsec
# From workstation
dsec delete d000 byod/<device-model>-<owner-name>/p12-password
Step 5: Update Metadata
# On certmgr-01.inside.domusdigitalis.dev
cat /home/ansible/byod-certs/.metadata/certificates.json | jq '
(.[] | select(.serial_number == "<serial-number>")) |= . + {status: "revoked"}
' > /tmp/updated.json
mv /tmp/updated.json /home/ansible/byod-certs/.metadata/certificates.json
Troubleshooting
Password Not Found in dsec
Symptoms: dsec show returns empty or error.
Causes: 1. Password was never stored (forgot Step 4 during issuance) 2. Password was deleted 3. Wrong dsec path
Resolution:
# Check if password exists
dsec list d000 byod/ | grep -i "<device>"
# If not found, re-export certificate with new password:
# (Requires original cert data on certmgr-01.inside.domusdigitalis.dev)
ssh certmgr-01.inside.domusdigitalis.dev
cd /home/ansible/byod-certs/<device>
# Check if original .p12 still exists
ls -lh *.p12
# If exists, user can re-import with original password
# If not, must issue new certificate
Cannot Retrieve Vault Token
Symptoms: vault commands fail with "permission denied".
Resolution:
# From workstation, get Vault token from dsec
DSEC_SECURITY_MODE=permissive dsec show d000 dev/vault
# Look for vault_token or similar field
# Copy token and export on certmgr-01.inside.domusdigitalis.dev
export VAULT_TOKEN='<your-vault-token>'
PKCS#12 File Not Found
Symptoms: scp fails to transfer .p12 file from certmgr-01.inside.domusdigitalis.dev.
Resolution:
# SSH to certmgr-01.inside.domusdigitalis.dev and verify file location
ssh certmgr-01.inside.domusdigitalis.dev
find /home/ansible/byod-certs -name "*.p12" -ls
# Files are in /home/ansible/byod-certs/<device-model>/<device>-<owner>.p12
# Example: /home/ansible/byod-certs/<device-model>/<device-model>-<owner-name>.p12
# Use correct path in scp
scp certmgr-01.inside.domusdigitalis.dev:/home/ansible/byod-certs/<device-model>/<device-model>-<owner-name>.p12 ~/Downloads/
Backup and Disaster Recovery
What to Backup
| Component | Location | Backup Method |
|---|---|---|
Certificates metadata |
certmgr-01.inside.domusdigitalis.dev: |
Daily rsync to backup server |
PKCS#12 passwords |
dsec: |
Included in dsec vault backups |
Issued PKCS#12 files |
certmgr-01.inside.domusdigitalis.dev: |
Optional (can be re-issued) |
Vault PKI data |
certmgr-01.inside.domusdigitalis.dev: |
Daily encrypted backup (see Runbook: Backup All Infrastructure) |
Recovery Scenarios
Scenario 1: certmgr-01.inside.domusdigitalis.dev disk failure
# Restore Vault data from backup
# Unseal Vault with Shamir keys
# Metadata is lost, but can be rebuilt:
# For each device still using certificates:
# 1. Get serial number from ISE sessions
# 2. Query Vault for certificate details
# 3. Rebuild metadata file
Scenario 2: dsec vault corruption
# Restore dsec vault from backup
# Passwords are recovered
# PKCS#12 files on certmgr-01 are unaffected
Scenario 3: Lost all PKCS#12 passwords
# If dsec backup is lost and passwords are gone:
# 1. Devices currently connected continue to work (cert in device keystore)
# 2. For new imports: Must issue NEW certificates
# 3. Cannot re-import existing PKCS#12 files without password
Security Considerations
Password Lifetime
One-time use: PKCS#12 passwords are only needed during certificate import. After successful import, the certificate is stored in the device’s secure hardware keystore (Android Keystore, iOS Secure Enclave).
Recommendation: Delete passwords from dsec 24 hours after device confirms successful authentication to WiFi.
Appendix: Quick Reference
# 1. On certmgr-01.inside.domusdigitalis.dev: Issue certificate
ssh certmgr-01.inside.domusdigitalis.dev
issue-byod-cert <device-model> <owner-name> android
# Copy the password from output
# 2. On workstation: Store password in dsec
echo "<p12-password>" | dsec set d000 byod/<device-model>-<owner-name>/p12-password
# 3. On workstation: Transfer .p12 file
scp certmgr-01.inside.domusdigitalis.dev:/home/ansible/byod-certs/<device-model>/<device-model>-<owner-name>.p12 ~/Downloads/
dsec show d000 byod/<device>-<owner>/p12-password
# 1. Revoke in Vault
vault write pki_int/revoke serial_number="<serial-number>"
# 2. Block in ISE
netapi ise block-endpoint <mac-address>
# 3. Delete password
dsec delete d000 byod/<device>-<owner>/p12-password