feat(encryption): implement AWS KMS integration with encryption provider and update related configurations

This commit is contained in:
2025-05-11 19:41:48 +02:00
parent 3e12b13d95
commit 457f2bd810
17 changed files with 268 additions and 48 deletions
-1
View File
@@ -1 +0,0 @@
../repository.vault
@@ -138,6 +138,10 @@ encryption_volumes:
mount_service_before: snap.microk8s.daemon-kubelite.service
mount_service_wantedby: snap.microk8s.daemon-kubelite.service
mount_permission: "0755"
encryption_aws_access_key_id: "{{ lookup('env', 'KMS_ACCESS_KEY') }}"
encryption_aws_secret_access_key: "{{ lookup('env', 'KMS_SECRET_KEY') }}"
encryption_kms_arn: "arn:aws:kms:eu-west-1:001604727293:key/977d08fd-8230-40bf-8a4f-d8a3264c5990"
encryption_kms_socket: /var/run/kmsplugin/socket.sock
# Kubernetes specific settings
microk8s_version: 1.29
@@ -149,6 +153,7 @@ microk8s_alt_names: "{{ hostname }}"
microk8s_issuer_email: c.alexandre.pires@alexpires.me
microk8s_encryption_cfg: /mnt/microk8s_config/microk8s_encryption.yaml
microk8s_encryption_secret: "{{ lookup('env', 'MICROK8S_ENCRYPTION_SECRET') }}"
microk8s_encryption_kms_socket: /var/run/kmsplugin/socket.sock
microk8s_users:
- username: operator
enabled: true
+2
View File
@@ -8,3 +8,5 @@
tags:
- roles
- roles::encryption
- roles::encryption::kms
- roles::encryption::volume
@@ -2,3 +2,9 @@
encryption_volumes: []
encryption_volumes_path: /var/lib/encrypted_volumes
encryption_volumes_default_size: 1G
encryption_aws_access_key_id:
encryption_aws_secret_access_key:
encryption_kms_arn:
encryption_kms_region: eu-west-1
encryption_kms_socket: /var/run/kmsplugin/socket.sock
Binary file not shown.
+57
View File
@@ -0,0 +1,57 @@
- name: Check if required facts are set
ansible.builtin.fail:
msg: "The following variables are required: encryption_aws_access_key_id, encryption_aws_secret_access_key, encryption_kms_arn"
when:
- encryption_aws_access_key_id is not defined
- encryption_aws_secret_access_key is not defined
- encryption_kms_arn is not defined
- name: Copy aws-encryption-provider binary to the host
become: true
ansible.builtin.copy:
src: aws-encryption-provider
dest: /usr/local/bin/aws-encryption-provider
mode: '0755'
- name: Copy start script to the host
become: true
ansible.builtin.template:
src: start-encryption-provider.sh.j2
dest: /usr/local/bin/start-encryption-provider.sh
mode: '0755'
owner: root
group: root
- name: Create KMS socket directory
become: true
ansible.builtin.file:
path: /var/run/kmsplugin
state: directory
mode: '0755'
owner: root
group: root
- name: Create systemd service file for aws-encryption-provider
become: true
ansible.builtin.template:
src: aws-encryption-provider.service.j2
dest: /etc/systemd/system/aws-encryption-provider.service
mode: '0644'
- name: Reload systemd daemon
become: true
ansible.builtin.systemd:
daemon_reload: true
name: aws-encryption-provider
- name: Enable aws-encryption-provider service
become: true
ansible.builtin.systemd:
name: aws-encryption-provider
enabled: true
- name: Start aws-encryption-provider service
become: true
ansible.builtin.systemd:
name: aws-encryption-provider
state: started
+16
View File
@@ -5,8 +5,12 @@
name:
- cryptsetup
- curl
- libc6
state: present
when: ansible_os_family == 'Debian'
tags:
- roles::encryption::packages
- roles::encryption::volume
- name: Create folder to store volume
become: true
@@ -14,6 +18,8 @@
path: "{{ encryption_volumes_path }}"
state: directory
mode: "0750"
tags:
- roles::encryption::volume
- name: Copy required scripts
become: true
@@ -24,9 +30,19 @@
with_items:
- "open_volume"
- "read_system_id"
tags:
- roles::encryption::volume
- name: Create encrypted volumes
ansible.builtin.include_tasks: volume.yml
loop: "{{ encryption_volumes }}"
loop_control:
loop_var: item
tags:
- roles::encryption::volume
- name: Create KMS service
ansible.builtin.include_tasks: kms.yml
when: encryption_aws_access_key_id is defined and encryption_aws_secret_access_key is defined and encryption_kms_arn is defined
tags:
- roles::encryption::kms
+4 -32
View File
@@ -89,45 +89,17 @@
- name: Create service to fetch key file and open LUKS device
become: true
ansible.builtin.copy:
ansible.builtin.template:
src: key_fetch.service.j2
dest: /etc/systemd/system/{{ key_fetch_service }}
mode: "0644"
content: |
[Unit]
Description={{ item.name }} key fetch service
After={{ fetch_key_service_after }}
Before={{ fetch_key_service_before }}
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/local/bin/open_volume "{{ key_server }}" {{ disk_image_path }} {{ device_name }}
ExecStop=/usr/sbin/cryptsetup close {{ device_name }}
[Install]
WantedBy={{ fetch_key_service_wantedby }}
- name: Create service to mount the local filesystem
become: true
ansible.builtin.copy:
ansible.builtin.template:
src: mount.service.j2
dest: /etc/systemd/system/{{ mount_service }}
mode: "0644"
content: |
[Unit]
Description={{ item.name }} mount service
After={{ key_fetch_service }}
Requires={{ key_fetch_service }}
Before={{ mount_service_before }}
[Mount]
What=/dev/mapper/{{ device_name }}
Where={{ mount_point }}
Type=ext4
Options=defaults
[Install]
WantedBy={{ mount_service_wantedby }}
- name: Reload systemd daemon
become: true
@@ -0,0 +1,13 @@
[Unit]
Description=AWS Encryption Provider
After=network.target
Before=snap.microk8s.daemon-kubelite.service
[Service]
Type=simple
ExecStart=/usr/local/bin/start-encryption-provider.sh
Restart=on-failure
User=root
[Install]
WantedBy=snap.microk8s.daemon-kubelite.service
@@ -0,0 +1,13 @@
[Unit]
Description={{ item.name }} key fetch service
After={{ fetch_key_service_after }}
Before={{ fetch_key_service_before }}
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/local/bin/open_volume "{{ key_server }}" {{ disk_image_path }} {{ device_name }}
ExecStop=/usr/sbin/cryptsetup close {{ device_name }}
[Install]
WantedBy={{ fetch_key_service_wantedby }}
@@ -0,0 +1,14 @@
[Unit]
Description={{ item.name }} mount service
After={{ key_fetch_service }}
Requires={{ key_fetch_service }}
Before={{ mount_service_before }}
[Mount]
What=/dev/mapper/{{ device_name }}
Where={{ mount_point }}
Type=ext4
Options=defaults
[Install]
WantedBy={{ mount_service_wantedby }}
@@ -0,0 +1,6 @@
#!/bin/bash
# This script is used to start the encryption provider for the application.
# Managed by Ansible.
export AWS_ACCESS_KEY_ID={{ encryption_aws_access_key_id }}
export AWS_SECRET_ACCESS_KEY={{ encryption_aws_secret_access_key }}
/usr/local/bin/aws-encryption-provider --key={{ encryption_kms_arn }} --region={{ encryption_kms_region }} --listen={{ encryption_kms_socket }}
+1
View File
@@ -39,3 +39,4 @@ microk8s_iac_user: "provision"
microk8s_encryption_secret: "a13labs"
microk8s_encryption_cfg: "/etc/microk8s_encryption.yaml"
microk8s_encryption_kms_socket:
@@ -7,21 +7,8 @@
- name: Create encryption configuration
become: true
ansible.builtin.copy:
content: |
---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
providers:
- aescbc:
keys:
- name: k8s-crypto
secret: {{ microk8s_encryption_secret }}
- identity: {}
ansible.builtin.template:
src: encryption-provider-config.yml.j2
dest: "{{ microk8s_encryption_cfg }}"
mode: "0600"
@@ -0,0 +1,20 @@
---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
providers:
{% if microk8s_encryption_kms_socket is defined %}
- kms:
apiVersion: v2
name: aws-encryption-provider
endpoint: unix://{{ microk8s_encryption_kms_socket }}
timeout: 3s
{% endif %}
- aescbc:
keys:
- name: k8s-crypto
secret: {{ microk8s_encryption_secret }}
- identity: {}
+3
View File
@@ -7,3 +7,6 @@ USER_PASSWORD_OPERATOR=$USER_PASSWORD_OPERATOR
ANSIBLE_FORCE_COLOR=True
BUCKET_SUFFIX=$BUCKET_SUFFIX
MICROK8S_ENCRYPTION_SECRET=$MICROK8S_ENCRYPTION_SECRET
KMS_ACCESS_KEY=$KMS_ACCESS_KEY
KMS_SECRET_KEY=$KMS_SECRET_KEY
KMS_KEY_ARN=$KMS_KEY_ARN
+106
View File
@@ -0,0 +1,106 @@
data "aws_iam_policy_document" "kms_key_policy" {
statement {
sid = "Enable IAM User Permissions"
effect = "Allow"
actions = ["kms:*"]
resources = ["*"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
}
}
statement {
sid = "Allow IAM User Access"
effect = "Allow"
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
resources = ["*"]
principals {
type = "AWS"
identifiers = [aws_iam_user.k8s_secrets_user.arn]
}
condition {
# Only allow from specific IP addresses
test = "IpAddress"
variable = "aws:SourceIp"
values = ["${join(",", local.instance_ips)}"]
}
}
}
resource "aws_kms_key" "k8s_secrets" {
description = "KMS key for encrypting Kubernetes secrets"
deletion_window_in_days = 30
enable_key_rotation = true
policy = data.aws_iam_policy_document.kms_key_policy.json
}
resource "aws_iam_user" "k8s_secrets_user" {
name = "alexpires-me-microk8s"
}
resource "aws_iam_access_key" "k8s_secrets_user_key" {
user = aws_iam_user.k8s_secrets_user.name
}
data "aws_iam_policy_document" "k8s_secrets_user_policy" {
statement {
effect = "Allow"
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
resources = [aws_kms_key.k8s_secrets.arn]
condition {
# Only allow from specific IP addresses
test = "IpAddress"
variable = "aws:SourceIp"
values = ["${join(",", local.instance_ips)}"]
}
}
}
resource "aws_iam_user_policy" "k8s_secrets_user_policy" {
name = "alexpires-me-microk8s-policy"
user = aws_iam_user.k8s_secrets_user.name
policy = data.aws_iam_policy_document.k8s_secrets_user_policy.json
}
data "aws_caller_identity" "current" {}
locals {
instance_ips = [
"${local.vps_1.ip}/32",
]
vps_1 = {
id = data.terraform_remote_state.contabo.outputs.instances[0].id
name = data.terraform_remote_state.contabo.outputs.instances[0].name
ip = data.terraform_remote_state.contabo.outputs.instances[0].ip_config[0].v4[0].ip
}
}
data "terraform_remote_state" "contabo" {
backend = "s3"
config = {
bucket = "a13labs.infra.eu"
key = "contabo.core.infra.tfstate"
region = "eu-west-1"
}
}