diff --git a/.gitignore b/.gitignore index 6166869..b8332c7 100644 --- a/.gitignore +++ b/.gitignore @@ -210,3 +210,18 @@ override.tf.json .terraformrc terraform.rc +# Custom ignores. +boot +ssh.pem +.DS_Store +*secret* +*.tfbackend +*.env +*.tar.gz +*.tar.xz +*.tar +*.pem +.venv +.vscode +tmp +node_modules \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..97ac20d --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,15 @@ +version: 3 + +includes: + tf: { taskfile: terraform, dir: terraform } + +tasks: + deploy: ansible-playbook playbooks/deploy.yml + + enter: + cmd: aws ssm start-session --target $INSTANCE_ID + env: + INSTANCE_ID: { sh: jq -r .instance_id.value < config/infrastructure.secret.json } + AWS_REGION: { sh: jq -r .aws_region < config/ansible.secret.json } + AWS_ACCESS_KEY_ID: { sh: jq -r .aws_access_key < config/ansible.secret.json } + AWS_SECRET_ACCESS_KEY: { sh: jq -r .aws_secret_key < config/ansible.secret.json } diff --git a/playbooks/deployment.yml b/playbooks/deployment.yml new file mode 100644 index 0000000..f0e0c1b --- /dev/null +++ b/playbooks/deployment.yml @@ -0,0 +1,30 @@ +- name: Deploy artifact to instance. + hosts: localhost + vars_files: + - ../config/proxy.json + - ../secrets/infrastructure.secret.json + vars: + ansible_connection: aws_ssm + ansible_python_interpreter: /usr/bin/python3 + ansible_aws_ssm_plugin: "{{ ssm_plugin }}" + ansible_aws_ssm_bucket_name: "{{ image_bucket }}" + ansible_aws_ssm_instance_id: "{{ public_instance_id.value }}" + + ansible_aws_ssm_region: "{{ aws_region }}" + ansible_aws_ssm_access_key_id: "{{ aws_access_key }}" + ansible_aws_ssm_secret_access_key: "{{ aws_secret_key }}" + tasks: + - name: Run image. + community.docker.docker_container: + name: server + image: "jc21/nginx-proxy-manager:latest" + state: started + recreate: true + restart_policy: always + ports: ["80:80", "443:443", "81:81", "22:22"] + env: + INITIAL_ADMIN_EMAIL: "{{ email }}" + INITIAL_ADMIN_PASSWORD: "{{ password }}" + volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f1246e9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,41 @@ +ansible==11.1.0 +ansible-compat==24.10.0 +ansible-core==2.18.1 +ansible-lint==24.12.2 +attrs==24.3.0 +black==24.10.0 +boto3==1.35.95 +botocore==1.35.95 +bracex==2.5.post1 +certifi==2024.12.14 +cffi==1.17.1 +charset-normalizer==3.4.1 +click==8.1.8 +cryptography==44.0.0 +filelock==3.16.1 +idna==3.10 +importlib_metadata==8.5.0 +Jinja2==3.1.5 +jmespath==1.0.1 +jsonschema==4.23.0 +jsonschema-specifications==2024.10.1 +MarkupSafe==3.0.2 +mypy-extensions==1.0.0 +packaging==24.2 +pathspec==0.12.1 +platformdirs==4.3.6 +pycparser==2.22 +python-dateutil==2.9.0.post0 +PyYAML==6.0.2 +referencing==0.35.1 +requests==2.32.3 +resolvelib==1.0.1 +rpds-py==0.22.3 +ruamel.yaml==0.18.10 +s3transfer==0.10.4 +six==1.17.0 +subprocess-tee==0.4.2 +urllib3==2.3.0 +wcmatch==10.0 +yamllint==1.35.1 +zipp==3.21.0 diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..97507b9 --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.87.0" + hashes = [ + "h1:IYq3by7O/eJuXzJwOF920z2nZEkw08PkDFdw2xkyhrs=", + "zh:017f237466875c919330b9e214fb33af14fffbff830d3755e8976d8fa3c963c2", + "zh:0776d1e60aa93c85ecbb01144aed2789c8e180bb0f1c811a0aba17ca7247b26c", + "zh:0dfa5c6cfb3724494fdc73f7d042515e88a20da8968959f48b3ec0b937bd8c8f", + "zh:1707a5ead36a7980cb3f83e8b69a67a14ae725bfc990ddfcc209b59400b57b04", + "zh:1c71f54fdd6adcbe547d6577dbb843d72a30fef0ab882d0afbeb8a7b348bc442", + "zh:3563c850a29790957ec3f4d3ba203bfa2e084ac7319035b3f43b91f818a2c9b4", + "zh:520bf6cef53785a92226651d5bebacbbf9314bdbc3211d0bf0903bce4e45149d", + "zh:56f9778575830f6e5c23462c2eccbf2c9afaddb00a69275fcfb33cd1a6d17f4d", + "zh:73e381cb0b1e76d471d7b0952f3d2a80350b507d15bda9b7041ea69077e3b5b5", + "zh:7da74b48f8fa088be758a92407980400cb4b039a8d9ba3c108907e4055e9ad6f", + "zh:8dacfa9623ba2e0197fe7db6faaaa0820a3b91fe00ba9e5d8a646340522bc8dd", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9c2ebd21d697e1a611fe201788dc9e1678949a088afc85d4589563bca484d835", + "zh:ac5d0bbf36f9a6cedbfb63993f6baf0aabdaf21c8d7fc3b1e69ba8cbf344b5f3", + "zh:c2329644179f78a0458b6cf2dd5eaadca4c610fc3577a1b50620544d92df13e8", + ] +} diff --git a/terraform/Taskfile.yml b/terraform/Taskfile.yml new file mode 100644 index 0000000..4f51aec --- /dev/null +++ b/terraform/Taskfile.yml @@ -0,0 +1,18 @@ +version: 3 +silent: true + +vars: + BACKEND: ../config/backend.secret.json + VARIABLES: ../config/variables.secret.json + OUTPUT: ../config/infrastructure.secret.json + +tasks: + init: terraform init -backend-config={{.BACKEND}} + plan: terraform plan -var-file={{.VARIABLES}} + destroy: terraform destroy + format: terraform fmt -recursive + out: terraform output -json > {{.OUTPUT}} + apply: + - terraform apply -var-file={{.VARIABLES}} + - task: out + import: terraform import -var-file={{.VARIABLES}} {{.CLI_ARGS}} \ No newline at end of file diff --git a/terraform/install.sh b/terraform/install.sh new file mode 100644 index 0000000..bcb2efd --- /dev/null +++ b/terraform/install.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +rpm --rebuilddb +amazon-linux-extras install docker ansible2 python3.8 -y + +# Make Docker work. +systemctl enable docker +systemctl start docker + +# Set up the correct version of Python (for Ansible). +ln -sf /usr/bin/python3.8 /usr/bin/python3 +ln -sf /usr/bin/pip3.8 /usr/bin/pip3 +pip3 install botocore boto3 requests packaging +python3 -m pip install -U pip + +# Add some swap space. +dd if=/dev/zero of=/swapfile bs=128M count=8 +chmod 600 /swapfile +mkswap /swapfile +swapon /swapfile + +# Stop SSH (because we have SSM.) +service sshd stop + +# Install Docker Compose. +curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose + +# ERROR: SSM User not created yet. +sudo usermod -aG docker ssm-user diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..cfed970 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,65 @@ +data "aws_vpc" "main" { + tags = { Name = "Main" } +} + +data "aws_subnet" "public" { + tags = { SubnetOf = "Main", SubnetType = "Public" } +} + +# An instance profile for access via AWS SSM. +data "aws_iam_instance_profile" "ssm" { + name = "SSMInstanceProfile" +} + +data "aws_security_group" "public" { + tags = { GroupOf = "Main", GroupType = "Public" } +} + +data "aws_route_table" "public" { + tags = { TableOf = "Main", TableType = "Public" } +} + +# Give the private subnet full access to the internet, too. +module "fck-nat" { + source = "RaJiska/fck-nat/aws" + + name = "NatInstance" + vpc_id = data.aws_vpc.main.id + subnet_id = data.aws_subnet.public.id + instance_type = "t4g.nano" + + update_route_table = true + route_table_id = data.aws_route_table.public.id + + tags = { + Name = "Codebase: Nat" + } +} + +# An elastic IP, so if the reverse proxy is modified, the route tables won't. +resource "aws_eip" "public" { + instance = aws_instance.proxy.id + domain = "vpc" +} + +# The reverse proxy. +resource "aws_instance" "proxy" { + ami = "ami-0adec96dc0cdc7bca" + instance_type = "t4g.nano" + subnet_id = data.aws_subnet.public.id + vpc_security_group_ids = [data.aws_security_group.public.id] + + user_data = file("install.sh") + user_data_replace_on_change = true + + iam_instance_profile = data.aws_iam_instance_profile.ssm.name + + root_block_device { + volume_type = "gp3" + volume_size = 8 + } + + tags = { + Name = "Codebase: Reverse Proxy" + } +} \ No newline at end of file diff --git a/terraform/output.tf b/terraform/output.tf new file mode 100644 index 0000000..82c09e2 --- /dev/null +++ b/terraform/output.tf @@ -0,0 +1,4 @@ +output "instance_id" { + value = aws_instance.proxy.id + description = "The instance ID of the Gitea instance." +} \ No newline at end of file diff --git a/terraform/provider.tf b/terraform/provider.tf new file mode 100644 index 0000000..d97d5b4 --- /dev/null +++ b/terraform/provider.tf @@ -0,0 +1,11 @@ +terraform { + # The backend is stored in an S3 bucket. + backend "s3" {} +} + +# Access AWS through the IaC roles. +provider "aws" { + region = var.aws_region + access_key = var.aws_access + secret_key = var.aws_secret +} \ No newline at end of file diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..6565f7e --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,14 @@ +variable "aws_region" { + type = string + description = "The AWS region things are created in." +} + +variable "aws_access" { + type = string + description = "The access key to generate the Gitea instance." +} + +variable "aws_secret" { + type = string + description = "The access secret to generate the Gitea instance." +} \ No newline at end of file