mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
don't clear bootstrap token when doing MDM cert renewals (#43098)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #41167 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops - [x] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit # Release Notes * **Bug Fixes** * Fixed an issue preventing device wipes after certificate renewal. The bootstrap token is now properly preserved during the certificate renewal process, ensuring reliable device wipe operations following renewal. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
ed13c58ea7
commit
7bcc2c6894
4 changed files with 216 additions and 94 deletions
1
changes/41167-skip-bootstrap-clear-on-cert-renewal
Normal file
1
changes/41167-skip-bootstrap-clear-on-cert-renewal
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Fixed an issue where trying to wipe a device after its certificate was renewed could fail due to a missing bootstrap token. _Note: The device might still have wiped_
|
||||
|
|
@ -2,6 +2,8 @@ package mysql
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
|
@ -26,6 +28,7 @@ func TestNanoMDMStorage(t *testing.T) {
|
|||
{"TestGetPendingLockCommand", testGetPendingLockCommand},
|
||||
{"TestEnqueueDeviceLockCommandRaceCondition", testEnqueueDeviceLockCommandRaceCondition},
|
||||
{"TestEnqueueDeviceUnlockCommand", testEnqueueDeviceUnlockCommand},
|
||||
{"TestStoreAuthenticatePreservesBootstrapTokenDuringSCEPRenewal", testStoreAuthenticatePreservesBootstrapTokenDuringSCEPRenewal},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
|
@ -234,6 +237,128 @@ func testGetPendingLockCommand(t *testing.T, ds *Datastore) {
|
|||
require.Empty(t, pin)
|
||||
}
|
||||
|
||||
// testStoreAuthenticatePreservesBootstrapTokenDuringSCEPRenewal verifies that
|
||||
// StoreAuthenticate does NOT clear the bootstrap token when a SCEP renewal is
|
||||
// in progress (renew_command_uuid is set in nano_cert_auth_associations), and
|
||||
// DOES clear it on a normal (re-)enrollment.
|
||||
// See https://github.com/fleetdm/fleet/issues/41167
|
||||
func testStoreAuthenticatePreservesBootstrapTokenDuringSCEPRenewal(t *testing.T, ds *Datastore) {
|
||||
ctx := t.Context()
|
||||
ns, err := ds.NewMDMAppleMDMStorage()
|
||||
require.NoError(t, err)
|
||||
|
||||
deviceUUID := uuid.NewString()
|
||||
|
||||
// --- Set up device with a bootstrap token ---
|
||||
|
||||
// Insert into nano_devices with a bootstrap token.
|
||||
bootstrapToken := base64.StdEncoding.EncodeToString([]byte("my-secret-bootstrap-token"))
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
`INSERT INTO nano_devices (id, serial_number, authenticate, authenticate_at, bootstrap_token_b64, bootstrap_token_at)
|
||||
VALUES (?, 'SERIAL1', 'auth-raw', CURRENT_TIMESTAMP, ?, CURRENT_TIMESTAMP)`,
|
||||
deviceUUID, bootstrapToken)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert a nano_enrollment so cert auth association can reference it.
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
`INSERT INTO nano_enrollments (id, device_id, type, topic, push_magic, token_hex, token_update_tally, last_seen_at)
|
||||
VALUES (?, ?, 'Device', 'topic', 'magic', 'deadbeef', 1, NOW())`,
|
||||
deviceUUID, deviceUUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert cert auth association (no SCEP renewal yet).
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
`INSERT INTO nano_cert_auth_associations (id, sha256) VALUES (?, '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')`,
|
||||
deviceUUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Helper to read bootstrap_token_b64 from nano_devices.
|
||||
getBootstrapToken := func() sql.NullString {
|
||||
var token sql.NullString
|
||||
err := ds.writer(ctx).QueryRowContext(ctx,
|
||||
`SELECT bootstrap_token_b64 FROM nano_devices WHERE id = ?`, deviceUUID,
|
||||
).Scan(&token)
|
||||
require.NoError(t, err)
|
||||
return token
|
||||
}
|
||||
|
||||
// Verify token is set.
|
||||
token := getBootstrapToken()
|
||||
require.True(t, token.Valid)
|
||||
require.Equal(t, bootstrapToken, token.String)
|
||||
|
||||
// --- Case 1: Normal re-enrollment (no SCEP renewal) should clear the bootstrap token ---
|
||||
|
||||
authMsg := &mdm.Authenticate{
|
||||
Enrollment: mdm.Enrollment{UDID: deviceUUID},
|
||||
Raw: []byte("auth-raw-reenroll"),
|
||||
}
|
||||
authMsg.SerialNumber = "SERIAL1"
|
||||
req := &mdm.Request{
|
||||
EnrollID: &mdm.EnrollID{ID: deviceUUID, Type: mdm.Device},
|
||||
Context: ctx,
|
||||
}
|
||||
|
||||
err = ns.StoreAuthenticate(req, authMsg)
|
||||
require.NoError(t, err)
|
||||
|
||||
token = getBootstrapToken()
|
||||
require.False(t, token.Valid, "bootstrap token should be cleared on normal re-enrollment")
|
||||
|
||||
// --- Restore the bootstrap token for the next test case ---
|
||||
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
`UPDATE nano_devices SET bootstrap_token_b64 = ?, bootstrap_token_at = CURRENT_TIMESTAMP WHERE id = ?`,
|
||||
bootstrapToken, deviceUUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// --- Case 2: SCEP renewal in progress should preserve the bootstrap token ---
|
||||
|
||||
// Simulate SCEP renewal by inserting a nano_command and setting renew_command_uuid.
|
||||
renewCmdUUID := uuid.NewString()
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
`INSERT INTO nano_commands (command_uuid, request_type, command) VALUES (?, 'InstallProfile', '<?xml version="1.0"?>')`,
|
||||
renewCmdUUID)
|
||||
require.NoError(t, err)
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
`UPDATE nano_cert_auth_associations SET renew_command_uuid = ? WHERE id = ?`,
|
||||
renewCmdUUID, deviceUUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now call StoreAuthenticate again — this simulates the device checking in during SCEP renewal.
|
||||
authMsg2 := &mdm.Authenticate{
|
||||
Enrollment: mdm.Enrollment{UDID: deviceUUID},
|
||||
Raw: []byte("auth-raw-scep-renewal"),
|
||||
}
|
||||
authMsg2.SerialNumber = "SERIAL1"
|
||||
|
||||
err = ns.StoreAuthenticate(req, authMsg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
token = getBootstrapToken()
|
||||
require.True(t, token.Valid, "bootstrap token should be preserved during SCEP renewal")
|
||||
require.Equal(t, bootstrapToken, token.String)
|
||||
|
||||
// --- Case 3: After SCEP renewal completes (renew_command_uuid cleared), token should be cleared again ---
|
||||
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
`UPDATE nano_cert_auth_associations SET renew_command_uuid = NULL WHERE id = ?`,
|
||||
deviceUUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
authMsg3 := &mdm.Authenticate{
|
||||
Enrollment: mdm.Enrollment{UDID: deviceUUID},
|
||||
Raw: []byte("auth-raw-post-renewal"),
|
||||
}
|
||||
authMsg3.SerialNumber = "SERIAL1"
|
||||
|
||||
err = ns.StoreAuthenticate(req, authMsg3)
|
||||
require.NoError(t, err)
|
||||
|
||||
token = getBootstrapToken()
|
||||
require.False(t, token.Valid, "bootstrap token should be cleared after SCEP renewal completes")
|
||||
}
|
||||
|
||||
func testEnqueueDeviceLockCommandRaceCondition(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,10 @@ func (s *MySQLStorage) StoreAuthenticate(r *mdm.Request, msg *mdm.Authenticate)
|
|||
if r.Certificate != nil {
|
||||
pemCert = cryptoutil.PEMCertificate(r.Certificate.Raw)
|
||||
}
|
||||
// When a device undergoes SCEP certificate renewal, it sends a new
|
||||
// Authenticate message. We must preserve the existing bootstrap token
|
||||
// during renewal; clearing it causes commands that depend on it (e.g.
|
||||
// EraseDevice) to fail. See https://github.com/fleetdm/fleet/issues/41167
|
||||
_, err := s.db.ExecContext(
|
||||
r.Context, `
|
||||
INSERT INTO nano_devices
|
||||
|
|
@ -152,11 +156,19 @@ ON DUPLICATE KEY
|
|||
UPDATE
|
||||
identity_cert = VALUES(identity_cert),
|
||||
serial_number = VALUES(serial_number),
|
||||
bootstrap_token_b64 = NULL,
|
||||
bootstrap_token_at = NULL,
|
||||
bootstrap_token_b64 = IF(
|
||||
EXISTS(SELECT 1 FROM nano_cert_auth_associations nca WHERE nca.id = ? AND nca.renew_command_uuid IS NOT NULL),
|
||||
bootstrap_token_b64,
|
||||
NULL
|
||||
),
|
||||
bootstrap_token_at = IF(
|
||||
EXISTS(SELECT 1 FROM nano_cert_auth_associations nca WHERE nca.id = ? AND nca.renew_command_uuid IS NOT NULL),
|
||||
bootstrap_token_at,
|
||||
NULL
|
||||
),
|
||||
authenticate = VALUES(authenticate),
|
||||
authenticate_at = CURRENT_TIMESTAMP;`,
|
||||
r.ID, pemCert, nullEmptyString(msg.SerialNumber), msg.Raw,
|
||||
r.ID, pemCert, nullEmptyString(msg.SerialNumber), msg.Raw, r.ID, r.ID,
|
||||
)
|
||||
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
CREATE TABLE nano_devices (
|
||||
id VARCHAR(255) NOT NULL,
|
||||
|
||||
|
|
@ -5,13 +6,13 @@ CREATE TABLE nano_devices (
|
|||
|
||||
serial_number VARCHAR(127) NULL,
|
||||
|
||||
-- If the (iOS, iPadOS) device sent an UnlockToken in the TokenUpdate
|
||||
-- TODO: Consider using a TEXT field and encoding the binary
|
||||
unlock_token MEDIUMBLOB NULL,
|
||||
unlock_token_at TIMESTAMP NULL,
|
||||
-- If the (iOS, iPadOS) device sent an UnlockToken in the TokenUpdate
|
||||
-- TODO: Consider using a TEXT field and encoding the binary
|
||||
unlock_token MEDIUMBLOB NULL, unlock_token_at TIMESTAMP NULL,
|
||||
|
||||
-- The last raw Authenticate for this device
|
||||
authenticate TEXT NOT NULL,
|
||||
-- The last raw Authenticate for this device
|
||||
|
||||
authenticate TEXT NOT NULL,
|
||||
authenticate_at TIMESTAMP NOT NULL,
|
||||
-- The last raw TokenUpdate for this device
|
||||
token_update TEXT NULL,
|
||||
|
|
@ -47,12 +48,12 @@ CREATE TABLE nano_users (
|
|||
user_short_name VARCHAR(255) NULL,
|
||||
user_long_name VARCHAR(255) NULL,
|
||||
|
||||
-- The last raw TokenUpdate for this user
|
||||
token_update TEXT NULL,
|
||||
token_update_at TIMESTAMP NULL,
|
||||
-- The last raw TokenUpdate for this user
|
||||
token_update TEXT NULL, token_update_at TIMESTAMP NULL,
|
||||
|
||||
-- The last raw UserAuthenticate (and optional digest) for this user
|
||||
user_authenticate TEXT NULL,
|
||||
-- The last raw UserAuthenticate (and optional digest) for this user
|
||||
|
||||
user_authenticate TEXT NULL,
|
||||
user_authenticate_at TIMESTAMP NULL,
|
||||
user_authenticate_digest TEXT NULL,
|
||||
user_authenticate_digest_at TIMESTAMP NULL,
|
||||
|
|
@ -75,10 +76,9 @@ CREATE TABLE nano_users (
|
|||
CHECK (user_authenticate_digest IS NULL OR user_authenticate_digest != '')
|
||||
);
|
||||
|
||||
|
||||
/* This table represents enrollments which are an amalgamation of
|
||||
* both device and user enrollments.
|
||||
*/
|
||||
* both device and user enrollments.
|
||||
*/
|
||||
CREATE TABLE nano_enrollments (
|
||||
-- The enrollment ID of this enrollment
|
||||
id VARCHAR(255) NOT NULL,
|
||||
|
|
@ -91,11 +91,12 @@ CREATE TABLE nano_enrollments (
|
|||
-- NULL in the case of a device enrollment.
|
||||
user_id VARCHAR(255) NULL,
|
||||
|
||||
-- Textual representation of the type of device enrollment.
|
||||
type VARCHAR(31) NOT NULL,
|
||||
-- Textual representation of the type of device enrollment.
|
||||
type VARCHAR(31) NOT NULL,
|
||||
|
||||
-- The MDM APNs push trifecta.
|
||||
topic VARCHAR(255) NOT NULL,
|
||||
-- The MDM APNs push trifecta.
|
||||
|
||||
topic VARCHAR(255) NOT NULL,
|
||||
push_magic VARCHAR(127) NOT NULL,
|
||||
token_hex VARCHAR(255) NOT NULL, -- TODO: Perhaps just CHAR(64)?
|
||||
|
||||
|
|
@ -129,42 +130,43 @@ CREATE TABLE nano_enrollments (
|
|||
CHECK (token_hex != '')
|
||||
);
|
||||
|
||||
|
||||
/* Commands stand alone. By themsevles they aren't associated with
|
||||
* a device, a result (response), etc. Joining other tables is required
|
||||
* for more context.
|
||||
*/
|
||||
* a device, a result (response), etc. Joining other tables is required
|
||||
* for more context.
|
||||
*/
|
||||
CREATE TABLE nano_commands (
|
||||
command_uuid VARCHAR(127) NOT NULL,
|
||||
request_type VARCHAR(63) NOT NULL,
|
||||
request_type VARCHAR(63) NOT NULL,
|
||||
-- Raw command Plist
|
||||
command MEDIUMTEXT NOT NULL,
|
||||
|
||||
command MEDIUMTEXT NOT NULL,
|
||||
created_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6),
|
||||
updated_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
|
||||
subtype ENUM('None','ProfileWithSecrets') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'None',
|
||||
subtype ENUM('None', 'ProfileWithSecrets') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'None',
|
||||
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (command_uuid),
|
||||
|
||||
CHECK (command_uuid != ''),
|
||||
CHECK (request_type != ''),
|
||||
CHECK (SUBSTRING(command FROM 1 FOR 5) = '<?xml')
|
||||
CHECK (
|
||||
SUBSTRING(
|
||||
command
|
||||
FROM 1 FOR 5
|
||||
) = '<?xml'
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
/* Results are enrollment responses to device commands.
|
||||
*
|
||||
* The choice for the PK being just the enrollment ID and command UUID
|
||||
* was under consideration. The PK could have included for example the
|
||||
* status in which case we could have separate status updates for
|
||||
* a NotNow vs. an Acknowledge. However this might be non-intuitive to
|
||||
* then query against to find if a given command had a response or not
|
||||
* (i.e. the queue view would be more complicated). In the end this
|
||||
* means we lose insight into when NotNows happen once a command is
|
||||
* Acknowledged.
|
||||
*/
|
||||
*
|
||||
* The choice for the PK being just the enrollment ID and command UUID
|
||||
* was under consideration. The PK could have included for example the
|
||||
* status in which case we could have separate status updates for
|
||||
* a NotNow vs. an Acknowledge. However this might be non-intuitive to
|
||||
* then query against to find if a given command had a response or not
|
||||
* (i.e. the queue view would be more complicated). In the end this
|
||||
* means we lose insight into when NotNows happen once a command is
|
||||
* Acknowledged.
|
||||
*/
|
||||
|
||||
|
||||
CREATE TABLE nano_command_results (
|
||||
id VARCHAR(255) NOT NULL,
|
||||
command_uuid VARCHAR(127) NOT NULL,
|
||||
|
|
@ -187,42 +189,31 @@ CREATE TABLE nano_command_results (
|
|||
REFERENCES nano_commands (command_uuid)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
|
||||
-- considering not enforcing these CHECKs to make sure we always
|
||||
-- capture results in the case they're malformed.
|
||||
CHECK (status != ''),
|
||||
-- considering not enforcing these CHECKs to make sure we always
|
||||
-- capture results in the case they're malformed.
|
||||
CHECK (status != ''),
|
||||
INDEX (status),
|
||||
CHECK (SUBSTRING(result FROM 1 FOR 5) = '<?xml')
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE nano_enrollment_queue (
|
||||
id VARCHAR(255) NOT NULL,
|
||||
id VARCHAR(255) NOT NULL,
|
||||
command_uuid VARCHAR(127) NOT NULL,
|
||||
|
||||
active BOOLEAN NOT NULL DEFAULT 1,
|
||||
active BOOLEAN NOT NULL DEFAULT 1,
|
||||
priority TINYINT NOT NULL DEFAULT 0,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
PRIMARY KEY (id, command_uuid),
|
||||
|
||||
INDEX (priority DESC, created_at),
|
||||
|
||||
FOREIGN KEY (id)
|
||||
REFERENCES nano_enrollments (id)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
|
||||
FOREIGN KEY (command_uuid)
|
||||
REFERENCES nano_commands (command_uuid)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
FOREIGN KEY (id) REFERENCES nano_enrollments (id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY (command_uuid) REFERENCES nano_commands (command_uuid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
/* An enrollment's queue is a view into commands, enrollment queued
|
||||
* commands, and any results received. Outstanding queue items (i.e.
|
||||
* those that have received no result yet) will have a status of NULL
|
||||
* (due to the LEFT JOIN against results).
|
||||
*/
|
||||
* commands, and any results received. Outstanding queue items (i.e.
|
||||
* those that have received no result yet) will have a status of NULL
|
||||
* (due to the LEFT JOIN against results).
|
||||
*/
|
||||
CREATE OR REPLACE VIEW nano_view_queue AS
|
||||
SELECT
|
||||
q.id,
|
||||
|
|
@ -238,15 +229,10 @@ SELECT
|
|||
r.result
|
||||
FROM
|
||||
nano_enrollment_queue AS q
|
||||
|
||||
INNER JOIN nano_commands AS c
|
||||
ON q.command_uuid = c.command_uuid
|
||||
|
||||
LEFT JOIN nano_command_results r
|
||||
ON r.command_uuid = q.command_uuid AND r.id = q.id
|
||||
ORDER BY
|
||||
q.priority DESC,
|
||||
q.created_at;
|
||||
INNER JOIN nano_commands AS c ON q.command_uuid = c.command_uuid
|
||||
LEFT JOIN nano_command_results r ON r.command_uuid = q.command_uuid
|
||||
AND r.id = q.id
|
||||
ORDER BY q.priority DESC, q.created_at;
|
||||
|
||||
|
||||
CREATE TABLE nano_push_certs (
|
||||
|
|
@ -255,13 +241,14 @@ CREATE TABLE nano_push_certs (
|
|||
cert_pem TEXT NOT NULL,
|
||||
key_pem TEXT NOT NULL,
|
||||
|
||||
/* stale_token is a simple value that coordinates push certificates
|
||||
* across the SQL backend. The push service checks this value
|
||||
* every time push info is requested. This value should be updated
|
||||
* every time a push cert is updated (i.e. renwals) and so all
|
||||
* push services using this table will know the certificate has
|
||||
* changed and reload it. This is managed by the MySQL backend. */
|
||||
stale_token INTEGER NOT NULL,
|
||||
/* stale_token is a simple value that coordinates push certificates
|
||||
* across the SQL backend. The push service checks this value
|
||||
* every time push info is requested. This value should be updated
|
||||
* every time a push cert is updated (i.e. renwals) and so all
|
||||
* push services using this table will know the certificate has
|
||||
* changed and reload it. This is managed by the MySQL backend. */
|
||||
|
||||
stale_token INTEGER NOT NULL,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
|
@ -273,17 +260,14 @@ CREATE TABLE nano_push_certs (
|
|||
CHECK (SUBSTRING(key_pem FROM 1 FOR 5) = '-----')
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE nano_cert_auth_associations (
|
||||
id VARCHAR(255) NOT NULL,
|
||||
sha256 CHAR(64) NOT NULL,
|
||||
|
||||
id VARCHAR(255) NOT NULL,
|
||||
sha256 CHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
cert_not_valid_after timestamp NULL DEFAULT NULL,
|
||||
|
||||
renew_command_uuid VARCHAR(127) NULL,
|
||||
PRIMARY KEY (id, sha256),
|
||||
|
||||
CHECK (id != ''),
|
||||
CHECK (sha256 != '')
|
||||
);
|
||||
|
|
@ -293,10 +277,10 @@ CREATE TABLE nano_cert_auth_associations (
|
|||
* but this is needed to support cross-references against ACME during nanomdm-only tests and because I couldn't find a cleaner way to do this
|
||||
*/
|
||||
CREATE TABLE acme_orders (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
issued_certificate_serial BIGINT DEFAULT NULL,
|
||||
created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY idx_issued_certificate_serial (issued_certificate_serial)
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
issued_certificate_serial BIGINT DEFAULT NULL,
|
||||
created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY idx_issued_certificate_serial (issued_certificate_serial)
|
||||
);
|
||||
Loading…
Reference in a new issue