fleet/server/datastore/mysql/migrations/tables/20251106000000_AddConditionalAccessSCEPTables.go
Victor Lyuboslavsky 7c9c5b9a2e
Okta SCEP endpoint (#34721)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34542

- Added SCEP endpoint for issuing certs for conditional access for Okta.
Functionally similar to host identity and Apple MDM SCEP endpoints.
- Changes file will be added later (this is a sub-task of the feature).
- A standard SCEP payload can be used to get a cert to an Apple device:

```
<!-- SCEP Configuration -->
<dict>
	<key>PayloadContent</key>
	<dict>
		<key>URL</key>
		<string>https://myfleet.example.com/api/fleet/conditional_access/scep</string>
		<key>Challenge</key>
		<string>ENROLLMENT_SECRET</string>
		<key>Keysize</key>
		<integer>2048</integer>
		<key>Key Type</key>
		<string>RSA</string>
		<key>Key Usage</key>
		<integer>5</integer>
              <key>ExtendedKeyUsage</key>
              <array>
                  <string>1.3.6.1.5.5.7.3.2</string>
              </array>
		<key>Subject</key>
		<array>
			<array>
				<array>
					<string>CN</string>
					<string>Fleet conditional access for Okta</string>
				</array>
			</array>
		</array>
		<key>SubjectAltName</key>
		<dict>
			<key>uniformResourceIdentifier</key>
			<array>
				<string>urn:device:apple:uuid:%HardwareUUID%</string>
			</array>
		</dict>
		<key>Retries</key>
		<integer>3</integer>
		<key>RetryDelay</key>
		<integer>10</integer>
              <!-- ACL for browser access -->
              <key>AllowAllAppsAccess</key>
              <true/>
              <!-- Set true for Safari access. Set false if Safari support not needed. -->
              <key>KeyIsExtractable</key>
              <false/>
	</dict>
	<key>PayloadDescription</key>
	<string>Configures SCEP for Fleet conditional access for Okta certificate</string>
	<key>PayloadDisplayName</key>
	<string>Fleet conditional access SCEP</string>
	<key>PayloadIdentifier</key>
	<string>com.fleetdm.conditional-access-scep</string>
	<key>PayloadType</key>
	<string>com.apple.security.scep</string>
	<key>PayloadUUID</key>
	<string>B2C3D4E5-F6A7-4B6C-9D8E-0F1A2B3C4D5E</string>
	<key>PayloadVersion</key>
	<integer>1</integer>
</dict>
```

# Checklist for submitter

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

## Database migrations

- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## New Features
* Adds Conditional Access SCEP certificate enrollment support, enabling
hosts to obtain device identity certificates through secure certificate
enrollment protocol endpoints.
* Implements rate limiting for certificate enrollment requests to
prevent abuse.

## Tests
* Adds comprehensive integration tests for Conditional Access SCEP
functionality, including certificate operations, rate limiting
validation, and edge cases.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-06 17:07:17 -06:00

53 lines
1.8 KiB
Go

package tables
import (
"database/sql"
"fmt"
)
func init() {
MigrationClient.AddMigration(Up_20251106000000, Down_20251106000000)
}
func Up_20251106000000(tx *sql.Tx) error {
// Create conditional_access_scep_serials table first (referenced by foreign key)
// Reserve serial number 1 for system use, similar to host identity SCEP
_, err := tx.Exec(`
CREATE TABLE conditional_access_scep_serials (
serial bigint unsigned NOT NULL AUTO_INCREMENT,
created_at DATETIME(6) NULL DEFAULT NOW(6),
PRIMARY KEY (serial)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`)
if err != nil {
return fmt.Errorf("failed to create conditional_access_scep_serials table: %w", err)
}
// Create conditional_access_scep_certificates table
_, err = tx.Exec(`
CREATE TABLE conditional_access_scep_certificates (
serial bigint unsigned NOT NULL,
host_id int unsigned NOT NULL,
name varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
not_valid_before datetime NOT NULL,
not_valid_after datetime NOT NULL,
certificate_pem text COLLATE utf8mb4_unicode_ci NOT NULL,
revoked tinyint(1) NOT NULL DEFAULT '0',
created_at DATETIME(6) NULL DEFAULT NOW(6),
updated_at DATETIME(6) NULL DEFAULT NOW(6) ON UPDATE NOW(6),
PRIMARY KEY (serial),
KEY idx_conditional_access_host_id (host_id),
CONSTRAINT conditional_access_scep_certificates_ibfk_1 FOREIGN KEY (serial) REFERENCES conditional_access_scep_serials (serial),
CONSTRAINT conditional_access_scep_certificates_chk_1 CHECK ((substr(certificate_pem,1,27) = _utf8mb4'-----BEGIN CERTIFICATE-----'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`)
if err != nil {
return fmt.Errorf("failed to create conditional_access_scep_certificates table: %w", err)
}
return nil
}
func Down_20251106000000(_ *sql.Tx) error {
return nil
}