Skip to content
Snippets Groups Projects
Commit d6fd6491 authored by Maarten de Waard's avatar Maarten de Waard :angel:
Browse files

Merge branch '12-repartition-root-disk-when-adding-encryption' into 'master'

Repartition and populate config partition on init

Closes #12

See merge request !5
parents fa8b8241 9489fe86
No related branches found
No related tags found
No related merge requests found
/**
* Callback function for encrypting an unencrypted device.
* It proceeds as follows:
* 1. check if the device isn't already encrypted;
* 2. mount the unencrypted device;
* 3. determine the total size of the files on the device, and guess if they
* will fit into memory;
* 4. rsync all files from the unencrypted device to memory;
* 5. unmount the unencrypted device;
* 6. initialise a new encrypted container on the same device, replacing the
unencrypted contents, and unlock it for use;
* 7. create a new filesystem inside the new container, and mount it;
* 8. rsync all files back from memory to this newly encrypted device;
* 9. unmount the new filesystem, and lock the encrypted container.
* 1. check if the device isn't already encrypted;
* 2. mount the unencrypted device;
* 3. determine the total size of the files on the device, and guess if they
* will fit into memory;
* 4. rsync all files from the unencrypted device to memory;
* 5. check if an authorized_keys file can be found on the root device;
* 6. unmount the unencrypted device;
* 7. repartition the device:
* . an info partition to hold unencrypted data for use from the initrd,
* like the dropbear ssh host keys and user's ssh public keys;
* . a data partition for the actual encrypted root device;
* 8. create a filesystem on the info partition;
* 9. copy ssh host keys and the authorized_keys file from the initrd
* to the info partition;
* 10. initialise a new encrypted container on the data partition, and unlock it
* for use;
* 11. create a new filesystem on this new encrypted data partition,
* and mount it;
* 12. rsync all files back from memory to the encrypted data partition;
* 13. unmount the new filesystem, and lock the encrypted container;
* 14. reboot.
* @param[in] request incoming HTTP request
* @param[out] response HTTP response to the request
* @param[in] user_data extra data to pass between main thread and callbacks
* @param[in] user_data extra data to pass between handler and main thread
* @return internal status code
*/
int callback_encryption_add(const struct _u_request * request,
struct _u_response * response, void * user_data)
{
bool * reboot = (bool *)user_data;
int r;
// Read the encryption password from the request body.
......@@ -32,7 +43,7 @@ int callback_encryption_add(const struct _u_request * request,
}
// Check if the device isn't already encrypted.
if (is_encrypted_device(CONTAINER_DEVICE))
if (is_encrypted_device(ROOT_DEVICE))
{
// The device is already encrypted; we don't want to encrypt it again.
return send_simple_response(response, 500, "error",
......@@ -40,13 +51,13 @@ int callback_encryption_add(const struct _u_request * request,
}
// Mount the filesystem on the unencrypted device.
r = temporary_mount(CONTAINER_DEVICE, UNENCRYPTED_MOUNTPOINT,
r = temporary_mount(ROOT_DEVICE, UNENCRYPTED_MOUNTPOINT,
FILESYSTEM_TYPE);
if (r != 0)
{
printf("mounting root disk failed: return code %d", r);
printf("mounting root device failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"mounting root disk failed");
"mounting root device failed");
}
// Determine the filesystem usage of the device to be encrypted.
......@@ -54,11 +65,11 @@ int callback_encryption_add(const struct _u_request * request,
if (size < 0)
{
// Something went wrong in determining the filesystem usage.
printf("determining filesystem usage failed: return code %lu", size);
printf("determining filesystem usage failed: return code %lu\n", size);
return send_simple_response(response, 500, "error",
"determining filesystem usage failed");
}
printf("root disk usage: %lu bytes\n", size);
printf("root device usage: %lu bytes\n", size);
// Determine the available memory.
unsigned long memory = available_memory();
......@@ -73,16 +84,16 @@ int callback_encryption_add(const struct _u_request * request,
}
// Copy device contents to temporary filesystem.
printf("copying existing root disk contents to memory\n");
printf("copying existing root device contents to memory\n");
char * command = NULL;
asprintf(&command, "rsync -a %s/ %s", UNENCRYPTED_MOUNTPOINT, TMP_LOCATION);
r = system(command);
if(r != 0)
{
printf("copying rootdisk contents into memory failed: return code %d",
r);
printf("copying root device contents into memory failed"
": return code %d\n", r);
return send_simple_response(response, 500, "error",
"copying rootdisk contents into memory failed");
"copying root device contents into memory failed");
}
// Unmount unencrypted device.
......@@ -90,27 +101,144 @@ int callback_encryption_add(const struct _u_request * request,
r = umount(UNENCRYPTED_MOUNTPOINT);
if (r != 0)
{
printf("unmounting encrypted device failed: return code %d", r);
printf("unmounting encrypted device failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"unmounting unencrypted disk failed");
"unmounting unencrypted device failed");
}
// Initialise encrypted container, overwriting existing device.
printf("creating encrypted container at %s\n", CONTAINER_DEVICE);
r = create_encrypted_device(CONTAINER_DEVICE, password);
if(r != 0)
// Check whether the authorized_keys file exists on root device.
char * authorized_keys_path = NULL;
asprintf(&authorized_keys_path, "%s%s",
TMP_LOCATION, AUTHORIZED_KEYS_PATH);
struct stat st = {0};
if (stat(authorized_keys_path, &st) == -1)
{
printf("authorized_keys not found on root device at %s\n",
authorized_keys_path);
return send_simple_response(response, 500, "error",
"authorized_keys not found on root device");
}
// Re-partition device.
printf("repartitioning device %s\n", ROOT_DEVICE);
command = NULL;
asprintf(&command, "sgdisk -a 8192 -n 1:0:48M -N 2 %s", ROOT_DEVICE);
r = system(command);
if (r != 0)
{
printf("partitioning root device failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"partitioning root device failed");
}
// Inform kernel of partitioning changes.
printf("running partprobe\n");
command = NULL;
asprintf(&command, "partprobe");
r = system(command);
if (r != 0)
{
printf("creating encrypted container failed: return code %d", r);
printf("partprobe: return code %d\n", r);
return send_simple_response(response, 500, "error",
"partprobe failed");
}
// Sleep for a little while.
// This seems necessary for the newly created partitions to appear as
// devices.
printf("waiting a bit...\n");
sleep(2);
// Create filesystem on the info partition.
printf("creating filesystem on info partition\n");
command = NULL;
asprintf(&command, "mkfs -t %s %s", FILESYSTEM_TYPE,
INFO_PARTITION_DEVICE);
printf("command: %s\n", command);
r = system(command);
if (r != 0)
{
printf("creating filesystem failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"creating filesystem inside encrypted container failed");
}
// Mount the info partition.
printf("mounting info partition\n");
r = temporary_mount(INFO_PARTITION_DEVICE, INFO_MOUNTPOINT,
FILESYSTEM_TYPE);
if (r != 0)
{
printf("mounting encrypted device failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"mounting encrypted root device failed");
}
// Create some directories in the info partition.
printf("creating directories in info partition\n");
command = NULL;
asprintf(&command, "mkdir -p %s%s %s%s", INFO_MOUNTPOINT,
AUTHORIZED_KEYS_DIR, INFO_MOUNTPOINT, SSH_HOST_KEY_DIR);
r = system(command);
if (r != 0)
{
printf("creating directories failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"creating directories in info partition failed");
}
// Copy authorized_keys file to the info partition.
printf("copying authorized_keys to info partition\n");
command = NULL;
asprintf(&command, "cp %s %s%s",
AUTHORIZED_KEYS_PATH, INFO_MOUNTPOINT, AUTHORIZED_KEYS_PATH);
r = system(command);
if (r != 0)
{
printf("copying authorized_keys failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"copying authorized_keys failed");
}
// Copy dropbear ssh host keys from the initrd to the info partition.
printf("copying dropbear ssh host keys\n");
command = NULL;
asprintf(&command, "cp /etc/dropbear/* %s%s/", INFO_MOUNTPOINT,
SSH_HOST_KEY_DIR);
r = system(command);
if (r != 0)
{
printf("copying dropbear ssh host keys failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"copying dropbear ssh host keys failed");
}
// Unmount info partition.
printf("unmounting info partition at %s\n", INFO_MOUNTPOINT);
r = umount(INFO_MOUNTPOINT);
if (r != 0)
{
printf("unmounting failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"unmounting configuration partition failed");
}
// Initialise encrypted container on data partition.
printf("creating encrypted container at %s\n", DATA_PARTITION_DEVICE);
r = create_encrypted_device(DATA_PARTITION_DEVICE, password);
if (r != 0)
{
printf("creating encrypted container failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"creating encryption container failed");
}
// Unlock the new container.
printf("unlocking encrypted device\n");
r = encryption_unlock(CONTAINER_DEVICE, MAPPED_DEVICE_NAME, password);
if(r != 0)
r = encryption_unlock(DATA_PARTITION_DEVICE, MAPPED_DEVICE_NAME,
password);
if (r != 0)
{
printf("unlocking encrypted container failed: return code %d", r);
printf("unlocking encrypted container failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"unlocking new encryption container failed");
}
......@@ -120,56 +248,61 @@ int callback_encryption_add(const struct _u_request * request,
command = NULL;
asprintf(&command, "mkfs -t %s %s", FILESYSTEM_TYPE, MAPPED_DEVICE_PATH);
r = system(command);
if(r != 0)
if (r != 0)
{
printf("creating filesystem failed: return code %d", r);
printf("creating filesystem failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"creating filesystem inside encrypted container failed");
}
// Mount the unlocked container.
printf("mounting new filesystem\n");
r = temporary_mount(MAPPED_DEVICE_PATH, ENCRYPTED_MOUNTPOINT,
r = temporary_mount(MAPPED_DEVICE_PATH, DATA_MOUNTPOINT,
FILESYSTEM_TYPE);
if (r != 0)
{
printf("mounting encrypted disk failed: return code %d", r);
printf("mounting encrypted device failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"mounting encrypted root disk failed");
"mounting encrypted root device failed");
}
// Copy device contents from temporary filesystem to encrypted container.
printf("copying root disk contents from memory\n");
printf("copying root device contents from memory\n");
command = NULL;
asprintf(&command, "rsync -a %s/ %s", TMP_LOCATION, ENCRYPTED_MOUNTPOINT);
asprintf(&command, "rsync -a %s/ %s", TMP_LOCATION, DATA_MOUNTPOINT);
r = system(command);
if(r != 0)
if (r != 0)
{
printf("copying from memory to encrypted disk failed: return code %d",
r);
printf("copying from memory to encrypted device failed:"
" return code %d\n", r);
return send_simple_response(response, 500, "error",
"copying rootdisk contents from memory failed");
"copying root device contents from memory failed");
}
// Unmount encrypted device.
printf("unmounting encrypted device at %s\n", ENCRYPTED_MOUNTPOINT);
r = umount(ENCRYPTED_MOUNTPOINT);
// Unmount filesystem on encrypted data partition.
printf("unmounting encrypted device at %s\n", DATA_MOUNTPOINT);
r = umount(DATA_MOUNTPOINT);
if (r != 0)
{
printf("unmounting encrypted disk failed: return code %d", r);
printf("unmounting encrypted device failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"unmounting encrypted disk failed");
"unmounting encrypted device failed");
}
// Lock the container.
printf("locking encrypted device\n");
r = encryption_lock(MAPPED_DEVICE_NAME);
if(r != 0)
if (r != 0)
{
printf("locking encrypted container failed: return code %d", r);
printf("locking encrypted container failed: return code %d\n", r);
return send_simple_response(response, 500, "error",
"locking container failed");
}
return send_simple_response(response, 200, "status", "ok");
// Record that we want to reboot the machine.
*reboot = true;
r = send_simple_response(response, 200, "status", "ok");
stop_server();
return r;
}
......@@ -18,8 +18,8 @@ int callback_encryption_unlock(const struct _u_request * request,
return send_simple_response(response, 400, "error", "missing password");
}
int unlock_status = encryption_unlock(CONTAINER_DEVICE, MAPPED_DEVICE_NAME,
password);
int unlock_status = encryption_unlock(DATA_PARTITION_DEVICE,
MAPPED_DEVICE_NAME, password);
if (unlock_status == -1)
{
......
......@@ -131,3 +131,21 @@ int temporary_mount(char * device_path, char * mount_path,
mount_flags, "");
return r;
}
/**
* Reboot the system. We cannot simply use the `reboot` command because
* we're running as init (pid 0).
*/
void reboot_initrd()
{
pid_t pid;
pid = vfork();
if (pid == 0)
{
// Child.
reboot(RB_AUTOBOOT);
_exit(EXIT_SUCCESS);
}
// Parent (init) waits.
while (1);
}
......@@ -28,8 +28,9 @@ int main(int argc, char ** argv)
instance.max_post_body_size = 1024;
// Add api endpoints.
bool reboot = false;
ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/add",
0, &callback_encryption_add, NULL);
0, &callback_encryption_add, &reboot);
ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/encryption/unlock",
0, &callback_encryption_unlock, NULL);
ulfius_add_endpoint_by_val(&instance, "GET" , PREFIX, "/ssh/keys",
......@@ -83,6 +84,13 @@ int main(int argc, char ** argv)
ulfius_stop_framework(&instance);
ulfius_clean_instance(&instance);
// Check if the encryption/add said that we should reboot.
if (reboot)
{
printf("rebooting...");
reboot_initrd();
}
return 0;
}
......@@ -10,6 +10,14 @@
static int container_initialise(struct crypt_device ** cd, const char * path,
const bool debug)
{
// Check if the device exists.
struct stat st = {0};
if (stat(path, &st) == -1)
{
printf("device does not exist: %s.\n", path);
return 1;
}
// Let LUKS initialise the encrypted device.
int r = crypt_init(cd, path);
if (r < 0)
......@@ -65,9 +73,10 @@ static int encryption_unlock(const char * path, const char * device_name,
int r = container_initialise(&cd, path, true);
if (r < 0)
{
printf("crypt_load() failed on device %s.\n",
crypt_get_device_name(cd));
crypt_free(cd);
}
if (r != 0)
{
return r;
}
......
......@@ -7,6 +7,8 @@
#include <sys/statvfs.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/reboot.h>
#define U_DISABLE_CURL
#define U_DISABLE_WEBSOCKET
......
#define PREFIX "/cryptops/v0"
#define PORT 8000
#define CONTAINER_DEVICE "/dev/xvda1"
#define ROOT_DEVICE "/dev/xvda"
#define INFO_PARTITION_DEVICE ROOT_DEVICE "1"
#define DATA_PARTITION_DEVICE ROOT_DEVICE "2"
#define MAPPED_DEVICE_NAME "xvda1_crypt"
#define MAPPED_DEVICE_PATH "/dev/mapper/" MAPPED_DEVICE_NAME
#define FILESYSTEM_TYPE "xfs"
#define MEMORY_USAGE 0.8
#define MEMORY_USAGE 0.9
#define UNENCRYPTED_MOUNTPOINT "/tmp/mnt-plain"
#define ENCRYPTED_MOUNTPOINT "/tmp/mnt-encrypted"
#define INFO_MOUNTPOINT "/tmp/mnt-info"
#define DATA_MOUNTPOINT "/tmp/mnt-data"
#define TMP_LOCATION "/tmp/" MAPPED_DEVICE_NAME
#define AUTHORIZED_KEYS_DIR "/root/.ssh"
#define AUTHORIZED_KEYS_PATH AUTHORIZED_KEYS_DIR "/authorized_keys"
#define SSH_HOST_KEY_DIR "/dropbear"
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment