Add infrastructure bucket for storing assets

This commit is contained in:
2024-09-28 19:15:56 +02:00
parent 52d6d85203
commit 4de62d7334
17 changed files with 414 additions and 28 deletions
+7 -6
View File
@@ -80,9 +80,10 @@ jobs:
SSH_AUTH_SOCK: /tmp/ssh_agent.sock
run: |
cd ansible
sectool exec ansible-playbook -u $ANSIBLE_USER -i inventory/hosts playbook_hardening.yml --skip-tags roles::cis::suid
sectool exec ansible-playbook -u $ANSIBLE_USER -i inventory/hosts playbook_microk8s.yml
sectool exec ansible-playbook -u $ANSIBLE_USER -i inventory/hosts playbook_gitea.yml
sectool exec ansible-playbook -u $ANSIBLE_USER -i inventory/hosts playbook_nextcloud.yml
sectool exec ansible-playbook -u $ANSIBLE_USER -i inventory/hosts playbook_wordpress.yml
sectool exec ansible-playbook -u $ANSIBLE_USER -i inventory/hosts playbook_m3uproxy.yml
sectool exec ansible-playbook -u $ANSIBLE_USER playbook_encryption.yml
sectool exec ansible-playbook -u $ANSIBLE_USER playbook_hardening.yml --skip-tags roles::cis::suid
sectool exec ansible-playbook -u $ANSIBLE_USER playbook_microk8s.yml
sectool exec ansible-playbook -u $ANSIBLE_USER playbook_gitea.yml
sectool exec ansible-playbook -u $ANSIBLE_USER playbook_nextcloud.yml
sectool exec ansible-playbook -u $ANSIBLE_USER playbook_wordpress.yml
sectool exec ansible-playbook -u $ANSIBLE_USER playbook_m3uproxy.yml
@@ -0,0 +1,39 @@
name: Terraform Apply
on:
push:
branches:
- main
paths:
- "terraform/iac/**"
- .github/workflows/terraform-iac-apply.yaml
jobs:
terraform-apply:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.9.6
- name: Setup Sectool
uses: a13labs/setup-sectool@v1
- name: Terraform Init
env:
VAULT_MASTER_PASSWORD: ${{ secrets.VAULT_MASTER_PASSWORD }}
run: |
cd terraform/iac
sectool exec terraform init
- name: Terraform Apply
env:
VAULT_MASTER_PASSWORD: ${{ secrets.VAULT_MASTER_PASSWORD }}
run: |
cd terraform/iac
sectool exec terraform apply --auto-approve
@@ -0,0 +1,47 @@
name: Terraform Validate
on:
pull_request:
branches:
- main
paths:
- "terraform/iac/**"
- .github/workflows/terraform-iac-validate.yaml
types:
- opened
- synchronize
- reopened
jobs:
terraform-validate:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.9.6
- name: Setup Sectool
uses: a13labs/setup-sectool@v1
- name: Terraform Init
env:
VAULT_MASTER_PASSWORD: ${{ secrets.VAULT_MASTER_PASSWORD }}
run: |
cd terraform/iac
sectool exec terraform init
- name: Terraform Plan
env:
VAULT_MASTER_PASSWORD: ${{ secrets.VAULT_MASTER_PASSWORD }}
run: |
cd terraform/iac
sectool exec -n terraform plan -out=tfplan
- name: Show Terraform Plan
run: |
cd terraform/iac
terraform show tfplan
+118
View File
@@ -0,0 +1,118 @@
---
- name: Create LUKS encrypted file system with key file fetched from HTTP server
hosts: contabo
become: yes
vars:
disk_image_path: /a13labs.img
disk_image_size: 1G
luks_device_name: luks_device
mount_point: /a13labs
key_file_path: /root/luks_keyfile
key_file_url: https://a13labs-infra-{{ lookup('env','BUCKET_SUFFIX') }}.s3.fr-par.scw.cloud
key_fetch_service: fetch-a13labs-key.service
tasks:
- name: Install cryptsetup and curl
apt:
name:
- cryptsetup
- curl
state: present
when: ansible_os_family == 'Debian'
- name: Create disk image
ansible.builtin.command: fallocate -l {{ disk_image_size }} {{ disk_image_path }}
args:
creates: "{{ disk_image_path }}"
register: disk_image
- name: Create encrypted volume
when: disk_image is changed
block:
- name: Get mac address
ansible.builtin.shell: |
set -o pipefail
ip link show eth0 | awk '/ether/ {print $2}'
args:
executable: /bin/bash
register: mac_address
changed_when: false
- name: Sanitize mac address
set_fact:
mac_address_sanitized: "{{ mac_address.stdout | regex_replace('[:]', '%3A') }}"
changed_when: false
- name: Create bucket URL
set_fact:
key_file_url: "{{ key_file_url }}/keys/{{ mac_address_sanitized }}.key"
changed_when: false
- name: Download key file from HTTP server
ansible.builtin.uri:
url: "{{ key_file_url }}"
dest: "{{ key_file_path }}"
mode: '0600'
args:
creates: "{{ key_file_path }}"
- name: Set up LUKS encryption with key file
ansible.builtin.command: cryptsetup -q luksFormat {{ disk_image_path }} {{ key_file_path }}
- name: Open LUKS device with key file
ansible.builtin.command: cryptsetup luksOpen {{ disk_image_path }} {{ luks_device_name }} -d {{ key_file_path }}
register: luks_device
- name: Delete key file
ansible.builtin.file:
path: "{{ key_file_path }}"
state: absent
- name: Create file system
ansible.builtin.command: mkfs.ext4 /dev/mapper/{{ luks_device_name }}
- name: Create mount point
file:
path: "{{ mount_point }}"
state: directory
- name: Mount the file system
ansible.builtin.command: mount /dev/mapper/{{ luks_device_name }} {{ mount_point }}
- name: Ensure the file system is mounted
mount:
path: "{{ mount_point }}"
src: /dev/mapper/{{ luks_device_name }}
fstype: ext4
state: mounted
- name: Create systemd service to fetch key file and open LUKS device
copy:
dest: /etc/systemd/system/{{ key_fetch_service }}
content: |
[Unit]
Description=Fetch LUKS key file from HTTP server and open LUKS device
After=network-online.target
Before=cryptsetup.target
[Service]
Type=oneshot
ExecStartPre=/usr/bin/curl -o {{ key_file_path }} {{ key_file_url }}
ExecStart=/usr/bin/cryptsetup luksOpen {{ disk_image_path }} {{ luks_device_name }} -d {{ key_file_path }}
ExecStartPost=/bin/rm -f {{ key_file_path }}
[Install]
WantedBy=multi-user.target
- name: Reload systemd daemon
ansible.builtin.command: systemctl daemon-reload
- name: Enable the key fetch service
ansible.builtin.command: systemctl enable {{ key_fetch_service }}
- name: Add entry to /etc/fstab
lineinfile:
path: /etc/fstab
line: "/dev/mapper/{{ luks_device_name }} {{ mount_point }} ext4 defaults 0 2"
create: yes
-1
View File
@@ -2,4 +2,3 @@
collections:
- ansible.posix
- community.general
- community.libvirt
+1
View File
@@ -5,3 +5,4 @@ MAXMIND_LICENSE_KEY=$MAXMIND_LICENSE_KEY
USER_PASSWORD_SALT=$USER_PASSWORD_SALT
USER_PASSWORD_OPERATOR=$USER_PASSWORD_OPERATOR
ANSIBLE_FORCE_COLOR=True
BUCKET_SUFFIX=$BUCKET_SUFFIX
+1 -1
View File
@@ -1 +1 @@
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
+1
View File
@@ -0,0 +1 @@
../../repository.vault
+7
View File
@@ -0,0 +1,7 @@
terraform {
backend "s3" {
bucket = "a13labs.infra"
key = "a13labs-iac.tfstate"
region = "us-east-1"
}
}
+52
View File
@@ -0,0 +1,52 @@
resource "scaleway_object_bucket" "infra_assets" {
name = "a13labs-infra-${var.bucket_suffix}"
project_id = var.scaleway_project_id
region = "fr-par"
}
resource "scaleway_object_bucket_policy" "policy" {
bucket = scaleway_object_bucket.infra_assets.name
policy = jsonencode({
Version = "2023-04-17",
Id = "AllowMachineDownload",
Statement = [
{
Effect = "Allow"
Action = [
"s3:ListBucket",
"s3:GetObject",
]
Principal = "*"
Resource = [
scaleway_object_bucket.infra_assets.name,
"${scaleway_object_bucket.infra_assets.name}/*",
]
Condition = {
IpAddress = {
"aws:SourceIp" = local.instance_ips
}
},
},
{
Effect = "Allow"
Action = [
"s3:*",
]
Principal = {
SCW = "user_id:${var.scaleway_user_id}"
}
Resource = [
scaleway_object_bucket.infra_assets.name,
"${scaleway_object_bucket.infra_assets.name}/*",
]
},
]
})
}
resource "scaleway_object" "encryption_key" {
bucket = scaleway_object_bucket.infra_assets.id
key = "keys/${data.contabo_instance.microk8s.mac_address}.key"
content_base64 = base64encode(var.encryption_key)
}
+5
View File
@@ -0,0 +1,5 @@
locals {
instance_ips = [
"${data.contabo_instance.microk8s.ip_config[0].v4[0].ip}/32",
]
}
+3
View File
@@ -0,0 +1,3 @@
data "contabo_instance" "microk8s" {
id = "201912751"
}
+52
View File
@@ -0,0 +1,52 @@
terraform {
required_version = "~>1.8"
required_providers {
random = {
source = "hashicorp/random"
version = "~>3.4"
}
null = {
source = "hashicorp/null"
version = "~>3.2"
}
external = {
source = "hashicorp/external"
version = "~>2.2"
}
time = {
source = "hashicorp/time"
version = "~>0.9"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~>2.18"
}
scaleway = {
source = "scaleway/scaleway"
version = "~>2.13"
}
contabo = {
source = "contabo/contabo"
version = "~>0.1"
}
}
}
provider "scaleway" {
zone = "fr-par-2"
region = "fr-par"
access_key = var.scaleway_access_key
secret_key = var.scaleway_secret_key
organization_id = var.scaleway_organization_id
}
# Configure your Contabo API credentials
provider "contabo" {
oauth2_client_id = var.contabo_client_id
oauth2_client_secret = var.contabo_client_secret
oauth2_user = var.contabo_api_user
oauth2_pass = var.contabo_api_password
}
+13
View File
@@ -0,0 +1,13 @@
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY
AWS_SECRET_ACCESS_KEY=$AWS_SECRET_KEY
TF_VAR_scaleway_access_key=$SCALEWAY_ACCESS_KEY
TF_VAR_scaleway_secret_key=$SCALEWAY_SECRET_KEY
TF_VAR_scaleway_project_id=$SCALEWAY_PROJECT_ID
TF_VAR_scaleway_organization_id=$SCALEWAY_ORGANIZATION_ID
TF_VAR_scaleway_user_id=$SCALEWAY_USER_ID
TF_VAR_contabo_client_id=$CONTABO_CLIENT_ID
TF_VAR_contabo_client_secret=$CONTABO_CLIENT_SECRET
TF_VAR_contabo_api_password=$CONTABO_API_PASSWORD
TF_VAR_contabo_api_user=$CONTABO_USER_NAME
TF_VAR_bucket_suffix=$BUCKET_SUFFIX
TF_VAR_encryption_key=$ENCRYPT_KEY
View File
+65
View File
@@ -0,0 +1,65 @@
variable "scaleway_access_key" {
description = "Scaleway Access key"
type = string
sensitive = true
}
variable "scaleway_secret_key" {
description = "Scaleway Secret key"
type = string
sensitive = true
}
variable "scaleway_project_id" {
description = "Scaleway Project Id"
type = string
sensitive = true
}
variable "scaleway_organization_id" {
description = "Scaleway Organization Id"
type = string
sensitive = true
}
variable "scaleway_user_id" {
description = "Scaleway User Id"
type = string
sensitive = true
}
variable "contabo_client_id" {
description = "Contabo Client ID"
type = string
sensitive = true
}
variable "contabo_client_secret" {
description = "Contabo Client Secret"
type = string
sensitive = true
}
variable "contabo_api_password" {
description = "Contabo API Password"
type = string
sensitive = true
}
variable "contabo_api_user" {
description = "Contabo API User"
type = string
}
variable "encryption_key" {
description = "Encryption key for sensitive data"
type = string
sensitive = true
}
variable "bucket_suffix" {
description = "Suffix for bucket names"
type = string
sensitive = true
}
-17
View File
@@ -1,20 +1,3 @@
#
# Copyright 2019 Alexandre Pires (alexandre.pires@mov.ai)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# File: backend.tf
terraform {
backend "s3" {
bucket = "a13labs.infra"