diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml index 424a62b..2d531d3 100644 --- a/ansible/group_vars/all.yml +++ b/ansible/group_vars/all.yml @@ -1,3 +1,4 @@ +--- ansible_python_interpreter: /usr/bin/python3 # role @@ -11,3 +12,5 @@ ghcr_token: "{{ lookup('env', 'GHCR_TOKEN') }}" # node-exporter node_exporter_image: "prom/node-exporter:v1.8.1" node_exporter_port: 9100 +... + diff --git a/ansible/group_vars/app.yml b/ansible/group_vars/app.yml index ee15808..de2b433 100644 --- a/ansible/group_vars/app.yml +++ b/ansible/group_vars/app.yml @@ -1,4 +1,11 @@ +--- app_image: "ghcr.io/tysker/cloud_devops_app:0950da9" app_container_name: "cloud-devops-app" app_container_port: 5000 -app_public_port: 80 +app_public_port: 5000 + +# caddy +caddy_config_dir: "/opt/caddy/config" +caddy_data_dir: "/opt/caddy/data" +caddy_image: "caddy:2.10.0-alpine" +... diff --git a/ansible/group_vars/monitoring.yml b/ansible/group_vars/monitoring.yml index a21633e..3cf6f59 100644 --- a/ansible/group_vars/monitoring.yml +++ b/ansible/group_vars/monitoring.yml @@ -1,3 +1,4 @@ +--- # prometheus prometheus_image: "prom/prometheus:v2.52.0" prometheus_port: 9090 @@ -8,3 +9,4 @@ prometheus_data_dir: "/opt/prometheus/data" grafana_image: "grafana/grafana:10.4.3" grafana_port: 3000 grafana_data_dir: "/opt/grafana/data" +... diff --git a/ansible/playbooks/bootstrap_1.yml b/ansible/playbooks/bootstrap_1.yml index c1ffdec..ebba616 100644 --- a/ansible/playbooks/bootstrap_1.yml +++ b/ansible/playbooks/bootstrap_1.yml @@ -1,6 +1,8 @@ +--- - name: Bootstrap all server (initial) hosts: all remote_user: root roles: - common - bootstrap_user +... diff --git a/ansible/playbooks/bootstrap_2.yml b/ansible/playbooks/bootstrap_2.yml index a604758..4773e80 100644 --- a/ansible/playbooks/bootstrap_2.yml +++ b/ansible/playbooks/bootstrap_2.yml @@ -1,3 +1,4 @@ +--- - name: Harden SSH (after devops exists) hosts: all remote_user: devops @@ -12,3 +13,4 @@ become: true roles: - docker +... diff --git a/ansible/playbooks/caddy.yml b/ansible/playbooks/caddy.yml new file mode 100644 index 0000000..1d5f588 --- /dev/null +++ b/ansible/playbooks/caddy.yml @@ -0,0 +1,8 @@ +--- +- name: Caddy directories for domain certification + hosts: app + remote_user: devops + become: true + roles: + - caddy +... diff --git a/ansible/playbooks/deploy_app.yml b/ansible/playbooks/deploy_app.yml index 0e4fe3b..39da42e 100644 --- a/ansible/playbooks/deploy_app.yml +++ b/ansible/playbooks/deploy_app.yml @@ -1,3 +1,4 @@ +--- - name: Deploy Flask app container hosts: app remote_user: devops @@ -5,3 +6,4 @@ become: true roles: - deploy_app +... diff --git a/ansible/playbooks/monitoring_grafana.yml b/ansible/playbooks/monitoring_grafana.yml index ed6e604..75c560e 100644 --- a/ansible/playbooks/monitoring_grafana.yml +++ b/ansible/playbooks/monitoring_grafana.yml @@ -1,3 +1,4 @@ +--- - name: Deploy Grafana on monitoring server hosts: monitoring remote_user: devops @@ -5,3 +6,4 @@ become: true roles: - grafana +... diff --git a/ansible/playbooks/monitoring_node_exporter.yml b/ansible/playbooks/monitoring_node_exporter.yml index 5039c37..7ea8e6b 100644 --- a/ansible/playbooks/monitoring_node_exporter.yml +++ b/ansible/playbooks/monitoring_node_exporter.yml @@ -1,3 +1,4 @@ +--- - name: Deploy Node Exporter on app and monitoring servers hosts: app:monitoring remote_user: devops @@ -5,3 +6,4 @@ become: true roles: - node_exporter +... diff --git a/ansible/playbooks/monitoring_prometheus.yml b/ansible/playbooks/monitoring_prometheus.yml index 81a8674..3a41c18 100644 --- a/ansible/playbooks/monitoring_prometheus.yml +++ b/ansible/playbooks/monitoring_prometheus.yml @@ -1,3 +1,4 @@ +--- - name: Deploy Prometheus on monitoring server hosts: monitoring gather_facts: true @@ -5,3 +6,4 @@ become: true roles: - prometheus +... diff --git a/ansible/roles/bootstrap_user/tasks/main.yml b/ansible/roles/bootstrap_user/tasks/main.yml index 54de1ba..4867635 100644 --- a/ansible/roles/bootstrap_user/tasks/main.yml +++ b/ansible/roles/bootstrap_user/tasks/main.yml @@ -1,3 +1,4 @@ +--- - name: Ensure devops user exists ansible.builtin.user: name: "{{ devops_user }}" @@ -19,3 +20,4 @@ user: "{{ devops_user }}" key: "{{ devops_public_key }}" state: present +... diff --git a/ansible/roles/caddy/tasks/main.yml b/ansible/roles/caddy/tasks/main.yml new file mode 100644 index 0000000..7184fb6 --- /dev/null +++ b/ansible/roles/caddy/tasks/main.yml @@ -0,0 +1,34 @@ +--- +- name: Ensure Caddy directories exist + become: true + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: "0755" + loop: + - "{{ caddy_config_dir }}" + - "{{ caddy_data_dir }}" + +- name: Render Caddy configuration + ansible.builtin.template: + src: caddyfile.yml.j2 + dest: "/opt/caddy/Caddyfile" + owner: root + group: root + mode: "0644" + +- name: Ensure Caddy container is running + community.docker.docker_container: + name: caddy + image: "{{ caddy_image }}" + state: started + restart_policy: unless-stopped + network_mode: host + recreate: true + volumes: + - "/opt/caddy/Caddyfile:/etc/caddy/Caddyfile:ro" + - "{{ caddy_config_dir }}:/config" + - "{{ caddy_data_dir }}:/data" +... diff --git a/ansible/roles/caddy/templates/caddyfile.yml.j2 b/ansible/roles/caddy/templates/caddyfile.yml.j2 new file mode 100644 index 0000000..59dc841 --- /dev/null +++ b/ansible/roles/caddy/templates/caddyfile.yml.j2 @@ -0,0 +1,18 @@ +(cloud_devops_headers) { + header { + X-Content-Type-Options "nosniff" + X-Frame-Options "DENY" + Referrer-Policy "no-referrer" + } +} + +clouddevopslab.eu, www.clouddevopslab.eu { + import cloud_devops_headers + reverse_proxy 127.0.0.1:5000 +} + +http://{{ hostvars[inventory_hostname].ansible_host }} { + reverse_proxy 127.0.0.1:5000 +} + + diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml index 6e6deb8..b50eed9 100644 --- a/ansible/roles/common/tasks/main.yml +++ b/ansible/roles/common/tasks/main.yml @@ -1,2 +1,4 @@ +--- - name: Ensure system is reachable ping: +... diff --git a/ansible/roles/deploy_app/tasks/main.yml b/ansible/roles/deploy_app/tasks/main.yml index 8316bda..a0f935d 100644 --- a/ansible/roles/deploy_app/tasks/main.yml +++ b/ansible/roles/deploy_app/tasks/main.yml @@ -1,3 +1,4 @@ +--- - name: (Optional) Login to GHCR community.docker.docker_login: registry_url: ghcr.io @@ -19,14 +20,16 @@ image: "{{ app_image }}" state: started restart_policy: unless-stopped + recreate: true ports: - - "{{ app_public_port }}:{{ app_container_port }}" + - "127.0.0.1:{{ app_public_port }}:{{ app_container_port }}" - name: Wait for /health to return 200 on the server ansible.builtin.uri: - url: "http://localhost/health" + url: "http://127.0.0.1:{{ app_container_port }}/health" status_code: 200 register: healthcheck retries: 10 delay: 2 until: healthcheck.status == 200 +... diff --git a/ansible/roles/docker/handlers/main.yml b/ansible/roles/docker/handlers/main.yml deleted file mode 100644 index e69de29..0000000 diff --git a/ansible/roles/docker/tasks/main.yml b/ansible/roles/docker/tasks/main.yml index e5db197..a22a9b7 100644 --- a/ansible/roles/docker/tasks/main.yml +++ b/ansible/roles/docker/tasks/main.yml @@ -1,3 +1,4 @@ +--- - name: Install prerequisites ansible.builtin.apt: name: @@ -51,3 +52,4 @@ name: "{{ devops_user }}" groups: docker append: true +... diff --git a/ansible/roles/grafana/tasks/main.yml b/ansible/roles/grafana/tasks/main.yml index 3869f7d..afbbfb3 100644 --- a/ansible/roles/grafana/tasks/main.yml +++ b/ansible/roles/grafana/tasks/main.yml @@ -1,3 +1,4 @@ +--- - name: Ensure Grafana data directory exists ansible.builtin.file: path: "{{ grafana_data_dir }}" @@ -19,3 +20,4 @@ GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: admin GF_USERS_ALLOW_SIGN_UP: "false" +... diff --git a/ansible/roles/node_exporter/tasks/main.yml b/ansible/roles/node_exporter/tasks/main.yml index ba1a5a6..4334c30 100644 --- a/ansible/roles/node_exporter/tasks/main.yml +++ b/ansible/roles/node_exporter/tasks/main.yml @@ -1,3 +1,4 @@ +--- - name: Ensure Node Exporter container is running community.docker.docker_container: name: node-exporter @@ -10,3 +11,4 @@ command: ["--path.rootfs=/host"] volumes: - "/:/host:ro,rslave" +... diff --git a/ansible/roles/prometheus/tasks/main.yml b/ansible/roles/prometheus/tasks/main.yml index bb595c6..17e6f2f 100644 --- a/ansible/roles/prometheus/tasks/main.yml +++ b/ansible/roles/prometheus/tasks/main.yml @@ -1,3 +1,4 @@ +--- - name: Ensure Prometheus directories exist become: true ansible.builtin.file: @@ -36,3 +37,4 @@ read_only: true tmpfs: - /tmp +... diff --git a/ansible/roles/ssh_hardening/handlers/main.yml b/ansible/roles/ssh_hardening/handlers/main.yml index 6db167d..c50a001 100644 --- a/ansible/roles/ssh_hardening/handlers/main.yml +++ b/ansible/roles/ssh_hardening/handlers/main.yml @@ -1,4 +1,6 @@ +--- - name: Restart SSH ansible.builtin.service: name: ssh state: restarted +... diff --git a/ansible/roles/ssh_hardening/tasks/main.yml b/ansible/roles/ssh_hardening/tasks/main.yml index 94f0fe6..6ed55d9 100644 --- a/ansible/roles/ssh_hardening/tasks/main.yml +++ b/ansible/roles/ssh_hardening/tasks/main.yml @@ -1,10 +1,11 @@ +--- - name: Disable SSH password authentication ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#?PasswordAuthentication" line: "PasswordAuthentication no" state: present - backup: yes + backup: true notify: Restart SSH - name: Ensure ChallengeResponseAuthentication is disabled @@ -29,7 +30,7 @@ regexp: "^#?AllowUsers" line: "AllowUsers devops" state: present - backup: yes + backup: true notify: Restart SSH - name: Disable SSH root login @@ -38,5 +39,6 @@ regexp: "^#?PermitRootLogin" line: "PermitRootLogin no" state: present - backup: yes + backup: true notify: Restart SSH +... diff --git a/infrastructure/terraform/main.tf b/infrastructure/terraform/main.tf index 6f14484..c145acc 100644 --- a/infrastructure/terraform/main.tf +++ b/infrastructure/terraform/main.tf @@ -64,6 +64,14 @@ resource "linode_firewall" "app_fw" { ipv4 = ["192.168.0.0/16"] } + inbound { + label = "allow-https" + action = "ACCEPT" + protocol = "TCP" + ports = "443" + ipv4 = ["0.0.0.0/0"] + } + inbound { label = "allow-http" action = "ACCEPT"