From 24013683163b6fa15c26567660ccaf09f41c7251 Mon Sep 17 00:00:00 2001 From: Max Hutz Date: Sun, 12 Oct 2025 17:32:07 -0400 Subject: [PATCH] feat: nginx proxy manager --- Taskfile.yml | 2 +- playbooks/deploy.yml | 139 ++++++++++++++++++++++++++++++++++ playbooks/deployment.yml | 30 -------- playbooks/provision.yml | 20 +++++ requirements.txt | 4 + terraform/.terraform.lock.hcl | 22 ++++++ terraform/main.tf | 22 ++++-- terraform/outputs.tf | 6 ++ terraform/providers.tf | 6 ++ terraform/routing.tf | 14 ++++ terraform/variables.tf | 19 ++++- variables.yml | 2 + vault.yml | 90 +++++++++++++++------- 13 files changed, 309 insertions(+), 67 deletions(-) create mode 100644 playbooks/deploy.yml delete mode 100644 playbooks/deployment.yml create mode 100644 terraform/routing.tf create mode 100644 variables.yml diff --git a/Taskfile.yml b/Taskfile.yml index d7011f4..510fb32 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -10,4 +10,4 @@ tasks: cmd: ssh -i {{.KEY}} root@{{.IP}} vars: KEY: { sh: ansible-vault view vault.yml | yq -r ".secret.private_ssh_key_path" } - IP: { sh: cat dist/terraform_outputs.yml | jq -r ".server_ip.value" } + IP: { sh: cat dist/terraform_outputs.yml | jq -r ".proxy_ip.value" } diff --git a/playbooks/deploy.yml b/playbooks/deploy.yml new file mode 100644 index 0000000..77fc1a1 --- /dev/null +++ b/playbooks/deploy.yml @@ -0,0 +1,139 @@ +- name: Set up real host. + gather_facts: false + hosts: localhost + tags: + - deploy + vars_files: + - ../vault.yml + - ../dist/terraform_outputs.yml + - ../variables.yml + tasks: + - name: Add remote host. + ansible.builtin.add_host: + name: server + ansible_ssh_host: "{{ variables.proxy_host }}" + +- name: Set-up NAT. + gather_facts: false + hosts: server + vars_files: + - ../vault.yml + - ../dist/terraform_outputs.yml + tasks: + - name: Install PIP. + ansible.builtin.apt: + name: + - python3-pip + - ifupdown + state: present + update_cache: true + + - name: Install needed packages. + ansible.builtin.pip: + name: + - botocore + - boto3 + - packaging + state: present + break_system_packages: true + + - name: Set-up the network interfaces. + ansible.builtin.blockinfile: + dest: /etc/network/interfaces + marker: "# NAT CONFIG {marker}" + content: | + auto eth0 + iface eth0 inet dhcp + post-up echo 1 > /proc/sys/net/ipv4/ip_forward + post-up iptables -t nat -A POSTROUTING -s '{{ network_cidr.value }}' -o eth0 -j MASQUERADE + +- name: Install Docker. + gather_facts: true + hosts: server + vars_files: + - ../vault.yml + - ../dist/terraform_outputs.yml + tasks: + - name: Download Docker repository key. + ansible.builtin.apt_key: + url: https://download.docker.com/linux/debian/gpg + state: present + + - name: Download Docker repository. + ansible.builtin.apt_repository: + repo: "deb https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable" + state: present + + - name: Remove bad packages. + ansible.builtin.apt: + state: absent + package: + - docker.io + - docker-doc + - docker-compose + - podman-docker + - containerd + - runc + + - name: Download Docker dependencies. + ansible.builtin.apt: + state: present + package: + - ca-certificates + - curl + + - name: Download Docker packages. + ansible.builtin.apt: + state: present + update_cache: true + package: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + +- name: Set-up reverse proxy. + gather_facts: false + hosts: server + vars_files: + - ../vault.yml + - ../dist/terraform_outputs.yml + tasks: + - name: Set-up folders. + ansible.builtin.file: + path: "{{ item }}" + state: directory + recurse: true + loop: [/root/data, /root/letsencrypt] + + - name: Set-up manager. + community.docker.docker_container: + name: proxy-manager + image: 'jc21/nginx-proxy-manager:latest' + state: started + restart_policy: unless-stopped + ports: ['80:80', '443:443', '81:81'] + labels: { docker-volume-backup.stop-during-backup: "true" } + volumes: + - /root/data:/data + - /root/letsencrypt:/etc/letsencrypt + + - name: Run backup. + community.docker.docker_container: + name: proxy-backup + image: offen/docker-volume-backup:v2 + state: started + restart_policy: unless-stopped + volumes: + - /root/data:/backup/data:ro + - /root/letsencrypt:/backup/letsencrypt:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + env: + AWS_S3_BUCKET_NAME: "{{ secret.bucket.name }}" + AWS_S3_PATH: "{{ secret.bucket.key }}" + AWS_REGION: "{{ secret.bucket.region }}" + AWS_ACCESS_KEY_ID: "{{ secret.bucket.access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ secret.bucket.secret_key }}" + AWS_ENDPOINT: "{{ secret.bucket.endpoint }}" + BACKUP_CRON_EXPRESSION: "0 0 * * *" diff --git a/playbooks/deployment.yml b/playbooks/deployment.yml deleted file mode 100644 index f0e0c1b..0000000 --- a/playbooks/deployment.yml +++ /dev/null @@ -1,30 +0,0 @@ -- 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/playbooks/provision.yml b/playbooks/provision.yml index 7896087..90c8df0 100644 --- a/playbooks/provision.yml +++ b/playbooks/provision.yml @@ -37,3 +37,23 @@ content: "{{ terraform_apply.outputs }}" dest: ../dist/terraform_outputs.yml mode: '0755' + + +- name: Update SSH config. + hosts: localhost + gather_facts: false + tags: hosts + vars_files: + - ../vault.yml + - ../dist/terraform_outputs.yml + - ../variables.yml + tasks: + - name: Add proxy host. + community.general.ssh_config: + host: "{{ variables.proxy_host }}" + hostname: "{{ proxy_ip.value }}" + remote_user: root + forward_agent: true + user: user + port: 22 + identity_file: "{{ secret.private_ssh_key_path }}" diff --git a/requirements.txt b/requirements.txt index 01250a0..fcd0e64 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ ansible-lint==24.12.2 argcomplete==3.6.2 attrs==24.3.0 awscli-local==0.22.0 +bcrypt==5.0.0 black==24.10.0 boto3==1.35.95 botocore==1.35.95 @@ -18,6 +19,7 @@ filelock==3.16.1 go-task-bin==3.44.1 idna==3.10 importlib_metadata==8.5.0 +invoke==2.2.1 Jinja2==3.1.5 jmespath==1.0.1 jsonschema==4.23.0 @@ -26,9 +28,11 @@ localstack-client==2.7 MarkupSafe==3.0.2 mypy-extensions==1.0.0 packaging==24.2 +paramiko==4.0.0 pathspec==0.12.1 platformdirs==4.3.6 pycparser==2.22 +PyNaCl==1.6.0 python-dateutil==2.9.0.post0 PyYAML==6.0.2 referencing==0.35.1 diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl index 90c62f1..40358c7 100644 --- a/terraform/.terraform.lock.hcl +++ b/terraform/.terraform.lock.hcl @@ -1,6 +1,28 @@ # This file is maintained automatically by "terraform init". # Manual edits may be lost in future updates. +provider "registry.terraform.io/hashicorp/aws" { + version = "6.16.0" + hashes = [ + "h1:eBjQq1U3AZ+mkEgE6cC8z6Qw4DIV23tNmM8tCcuqXuk=", + "zh:11b5c11fc47aa7537d3acfb3156c9206ce465c2c0db4478914d1ba9493a27f38", + "zh:1de5c4ef8096ab6a4fe8e528c5a1d772a57de74ef4de98996071987d0d6a7696", + "zh:1eaaaa02503e34e57494831ea32b3327482857b01011b40753ec37c502719ee0", + "zh:367159ac72b344802e72631505894b1e7c04211f59d17c137cc9528acfb3b940", + "zh:449bb91e861d16ce80aabe148b40fa20ee4250c934cf467f6c21cf2206be1b5f", + "zh:45b4757e15a9887bf1d6dce07cbbbff365399759bb920456cf30cae47f0b0170", + "zh:4d2824050f8f2d3916a3363e0eeeab6c2c5a0912323029c4c7dc6e93ff3cfbc1", + "zh:6f363f811d20d7bd3e558d6da2cff0506c78ccea5956f919e531b22fdc7300c8", + "zh:7ab0990fc172a1343e4af6d7540be43adba989ee1b422b9d54c3369247155cea", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9eaae66cc57aa07a392eb9cb3fc115a5f446ffe9f51a7f45ffaefc7a64c17b31", + "zh:aee7dbba84823ed9ca93afb0579c78605588f9355e23ec1aafa22ac4a77c45c3", + "zh:be18792d2a52bbc06e6d21dc69c7ec7134e7aaf9e8bca5fd48d2edc8c1f9085e", + "zh:d2fb28162a6ed080fefe1d16b20be86652568e930aa777f186ecfcac66af6c43", + "zh:e1ffb80f46b64c26742417abe454af9c0d3920a8636698574c0a558e66cad535", + ] +} + provider "registry.terraform.io/hetznercloud/hcloud" { version = "1.54.0" constraints = "~> 1.45" diff --git a/terraform/main.tf b/terraform/main.tf index 11b85a4..6f6ef2e 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -10,6 +10,12 @@ resource "hcloud_network_subnet" "subnet" { ip_range = local.subnet_cidr } +resource "hcloud_network_route" "privNet" { + network_id = hcloud_network.network.id + destination = "0.0.0.0/0" + gateway = local.proxy_ip +} + /* -------------------------------------------------------------------------- */ resource "hcloud_primary_ip" "public_ip" { @@ -75,12 +81,16 @@ resource "hcloud_firewall" "server_firewall" { source_ips = [local.network_cidr] } - # Poke holes for SSH. - rule { - direction = "in" - protocol = "tcp" - port = "22" - source_ips = ["0.0.0.0/0", "::/0"] + # Poke holes for applications, and SSH. + dynamic "rule" { + for_each = ["80", "443", "22", "81"] + + content { + direction = "in" + protocol = "tcp" + port = rule.value + source_ips = ["0.0.0.0/0", "::/0"] + } } } diff --git a/terraform/outputs.tf b/terraform/outputs.tf index 84d2df3..337692e 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -3,3 +3,9 @@ output "proxy_ip" { value = hcloud_server.server_instance.ipv4_address sensitive = false } + +output "network_cidr" { + description = "The CIDR of the private network." + value = local.network_cidr + sensitive = false +} \ No newline at end of file diff --git a/terraform/providers.tf b/terraform/providers.tf index f0603d5..fe6666a 100644 --- a/terraform/providers.tf +++ b/terraform/providers.tf @@ -16,3 +16,9 @@ terraform { provider "hcloud" { token = var.hcloud_token } + +provider "aws" { + region = var.aws_region + access_key = var.aws_access_key + secret_key = var.aws_secret_key +} diff --git a/terraform/routing.tf b/terraform/routing.tf new file mode 100644 index 0000000..6dc96fc --- /dev/null +++ b/terraform/routing.tf @@ -0,0 +1,14 @@ +# The Route53 DNS zone. +data "aws_route53_zone" "main" { + name = local.domain +} + +# Push all domain traffic through the reverse proxy. +resource "aws_route53_record" "domain" { + zone_id = data.aws_route53_zone.main.zone_id + name = "*.${data.aws_route53_zone.main.name}" + type = "A" + ttl = "60" + records = [hcloud_primary_ip.public_ip.ip_address] +} + diff --git a/terraform/variables.tf b/terraform/variables.tf index a91c272..d853526 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -4,7 +4,6 @@ locals { server_image = "debian-12" domain = "maximhutz.com" - subdomain = "git" network_cidr = "10.10.0.0/16" subnet_cidr = "10.10.0.0/24" @@ -23,3 +22,21 @@ variable "public_ssh_key_path" { description = "The location of the public key used to access the repository Gitea server." type = string } + +variable "aws_region" { + description = "The region of the AWS account." + type = string + sensitive = true +} + +variable "aws_access_key" { + description = "The access key of the account." + type = string + sensitive = true +} + +variable "aws_secret_key" { + description = "The secret key of the account." + type = string + sensitive = true +} \ No newline at end of file diff --git a/variables.yml b/variables.yml new file mode 100644 index 0000000..73b8c73 --- /dev/null +++ b/variables.yml @@ -0,0 +1,2 @@ +variables: + proxy_host: proxy \ No newline at end of file diff --git a/vault.yml b/vault.yml index b267195..a377f02 100644 --- a/vault.yml +++ b/vault.yml @@ -1,30 +1,62 @@ $ANSIBLE_VAULT;1.1;AES256 -34333538313033663865386437623137393162623332646635333033333131323735656639376432 -3464336363356463613262363535306537643930646432650a316261313532613962663436346130 -66613065343261313539613766333066643234356638346433326234303964356330366633656531 -3435373363356264370a366637613463363931643761623130623136336536616139376330396230 -34363664666439333937396537666338363531343965386336646538333464373663393233343334 -61346130323335396331623535386631643061663762333061373635643961376535616631613438 -61623163313136386530313465383764646238663635616666383235353265323036343864653733 -63613961313532626532366363323733333233313034663431303934323736366637366265393237 -36336238343563313136376366356631336636626236666630326436366533333363383664346262 -37353434346362613162613465356537343830313030646665623436646334626435366366623634 -37623232313934336661366364643266376166633238343361343738383331306636353764376439 -32623962636463616336633862376635316364663362356633613839336236316331633531386561 -33313637646161616634663039636337636635383866633961643637313661643338343463363037 -38313636666435666563316233376465333534303732653061623763316237393463623437613430 -64343233396665613032313936623538373031613266613534396530613534653331396437633261 -32313938386431363433306334656539663461613539346330613837356166336134326434336436 -30393665313230373966663263326433363765646164333035633035303831336136376137626334 -33373066343732353064306133653939326535383531633233366332396662363138626162353334 -38383632623238643831393332316135393336653063343330353839393935383736663531393966 -33373430303035396238633037346430333138363038653665343531356439623165353831626161 -30313666326366333636346434373034656432343238353061333166353834383562393532333565 -35306631363330313432353232313234356233326261653938373863356237363961346134346237 -64346237366530323234393732396430323930356635343431323831616264363263653532383537 -63353635643334363131653637636666613037363438326132633833313964656163396638326466 -39656632646639386161643764353362356333656466366263663939633863386330313931336363 -36666631646134376264303565333665653264306338303065653838643133396333613033623366 -61643730333439343365383333623434323535613562363435666138323165383939653832313262 -36393936386566653738626666396363623737613533306466653639656664323032643631313961 -6465 +33396339353933616166653538396231653965383432663433313161393539383937303635393565 +6262633463303635663231313032613236326336316233380a373536633834303438313236366438 +61313430323738393534343262653064663437663734393633643062363064366639393565353764 +3930323766656531630a666362306538323930656330386261633264636130386134643965616534 +32633036323661336238633464316637643138623766333832666331363831316235616161656231 +34663333326531306232326136383033393664373130353135396633346462353865316461616334 +66633237366163303936653137323939363464383065373566613163656635333636653339306237 +35356163623537376637386262633163623538613261323566656566323938336264313636383936 +66323839343336666638303830396163306666633461343934356333643230326433363761636231 +65343436353463396536636663366362653065353163303636306164366638353964363930333562 +30323661323935643065653062393265386536656134356632323764656634316562363734323165 +35656465343763336636626461343436366363306530653434303637623737613034626362303931 +31303736623361616363643661646335383039356531626239353830646566333135653264303239 +64303163666166386336646434333237653433656565353032616634313261336535633365373961 +38633936363266303264663033376662383632616362363939326565656535386231316237623761 +32613830383036626534623631313961323534303931356637326461633739623430353734373530 +35376663326130393534373735653562373234396437653238323630333733313365636330393835 +32613230303035656530346238623034336236356630653236353362633131663237386330303933 +30376465646261663065663737613639393361633466316538656166396432663432656430653438 +66396439323339646662306436643062346339373463343031643563366531356432336562333537 +31326337613739343333326136336439306563333433613736383762656332313632346335613836 +39396330373030373661373565373361626333333631386433316566646661376263633866303530 +63626462366437383539666434313866373838333863633934313235363265626132646535303638 +39343966613234326661313539386232646230363961353537613931633839303635623866316534 +61623639613336633037343135663363343664633233656466326132363262336235316237333561 +38613038386439363865636665333939346662383235393662303335633866633664386264386432 +30303636346533633234313833303339633134393131396232633734626130393137363361393764 +65366234626532633066656131653439333639353162363931393035343462646261383238636230 +38363330313137366234306334353264343232333035646236616537313136313332363966653435 +33663538393064373430383030653331623039363635643138363333373532656432326338346336 +65626363616430636464316334303664353633363364313136636665323263373461663339323166 +30636438626265316433363861363763623263303533656632373961333966613561373436303361 +66323732353230343238353438386563393264333265363864383861366233393830623863376664 +65353930303338323533326337616636323433646235346161613437643964396231656231623935 +34663365343131373434343766366132656534343732623639663138663863383337303830636638 +64323537656165363061346265636266333838633163663963643564616234373930366632373762 +65393936383538643331366232333637353133326266383933393561383539393562353366366435 +31356163386633623039373830336432356361663632356233653865656531663162383135333533 +64306331376461393566353433633238316637366538613635383862656262663461303335663831 +63623839373462336531316462373138323136383463666561383661633761613832623633663664 +65663232666537663433653662613363623336346161363832643662373666383436396634616462 +35656230383739326537323737636266303563653232623634643036613765646335303137303733 +65616339333366653835383331643332376635613837663763616235663838663762386130663732 +39376433303633383463623139366237666230333137383838343432626130386161653936353662 +63323661376538356339626431333036633538633065643665323832303431393166383163633737 +36323464616138616633326464633238303861643235303431336137666432313261616536663039 +61306231663661396265333438376164333561616661386538313462366466623537393263643464 +32326439343863613464313365613630383133353262343732323339616664383261343364643566 +65363234383062356131343439383266343733656661653063366139303766356264626262613532 +32633135626633656166376639303738303435643662313439663330643530393965663363616166 +38653438306361623134666631653230306136336438366662303264666531633539343635393130 +37346539363533386465623936326232613062633336633262353235303762623530653333623639 +62376131646164343339656636353332303263636531626230313766636661636233366136316638 +35383436636266636237333231613930366466346432356134613232306163653839346637356562 +62633931303432616436396131666434333238363563353465323236336162643339376434393736 +36663735313866616633666638363832656234363835383961323561313662643438316666646535 +37626266353932373063633963616431666535306531393037616431653061323436316636653962 +30393537363232666634343663346435616362663731653834613831303531376566303038376436 +38303934633233396361623534356631383536363461356137323865373434363437653330383538 +38646161613439346232326434646136663861383963336363643931303830613237353363306663 +6133