diff --git a/ansible/justfile b/ansible/justfile index e69de29..ff367a6 100644 --- a/ansible/justfile +++ b/ansible/justfile @@ -0,0 +1,6 @@ +run INVENTORY PLAYBOOK: + ansible-playbook -i inventory/{{ INVENTORY }}.yaml playbooks/{{ PLAYBOOK }}.yaml + +prod PLAYBOOK: (run "prod" PLAYBOOK) + +prod-all: (prod "base") (prod "pg") (prod "docker") (prod "aerbim") (prod "angie") diff --git a/ansible/playbooks/aerbim.yaml b/ansible/playbooks/aerbim.yaml index e69de29..69c4a7d 100644 --- a/ansible/playbooks/aerbim.yaml +++ b/ansible/playbooks/aerbim.yaml @@ -0,0 +1,70 @@ +- name: Aerbim service + hosts: servers + tasks: + - name: Preinstall packages + ansible.builtin.package: + name: + - python3-requests + + - name: Docker compose project dir + ansible.builtin.file: + path: "/etc/docker/compose/aerbim" + state: directory + + - name: Static dir + ansible.builtin.file: + path: "/home/{{ user }}/static" + state: directory + owner: "{{ user }}" + + - name: Media dir + ansible.builtin.file: + path: "/home/{{ user }}/media" + state: directory + owner: "{{ user }}" + + - name: Log into gitlab registry + become: true + become_user: "{{ user }}" + community.docker.docker_login: + registry_url: registry.gitlab.com + username: "{{ registry_user }}" + password: "{{ registry_token }}" + reauthorize: true + + - name: Docker compose file + ansible.builtin.template: + src: aerbim/docker-compose.yml + dest: "/etc/docker/compose/aerbim/docker-compose.yml" + notify: Restart compose service + + - name: Systemd service + ansible.builtin.systemd: + name: "compose@aerbim" + state: started + daemon_reload: true + enabled: true + notify: Restart compose service + + - name: Pull images + become: true + become_user: "{{ user }}" + community.docker.docker_compose_v2_pull: + project_src: /etc/docker/compose/aerbim + notify: + - Restart compose service + - Prune docker + + handlers: + - name: Restart compose service + ansible.builtin.service: + name: "compose@aerbim" + state: restarted + + - name: Prune docker + community.docker.docker_prune: + containers: true + images: true + networks: true + volumes: true + builder_cache: true diff --git a/ansible/playbooks/angie.yaml b/ansible/playbooks/angie.yaml index e69de29..3b00ea8 100644 --- a/ansible/playbooks/angie.yaml +++ b/ansible/playbooks/angie.yaml @@ -0,0 +1,44 @@ +- name: Angie web server + hosts: servers + tasks: + - name: Install packages + ansible.builtin.package: + name: + - ca-certificates + - curl + - gnupg + + - name: Add apt repo key + ansible.builtin.apt_key: + url: https://angie.software/keys/angie-signing.gpg + state: present + + - name: Add apt repo + ansible.builtin.apt_repository: + repo: "deb https://download.angie.software/angie/\ + {{ ansible_facts['distribution'] | lower }}/{{ ansible_facts['distribution_major_version'] }} \ + {{ ansible_facts['distribution_release'] }} main" + filename: angie + update_cache: true + + - name: Install Angie + ansible.builtin.package: + name: angie + + - name: Configure virtual host + ansible.builtin.template: + src: angie/{{ app }}.conf + dest: "/etc/angie/http.d/{{ app }}.conf" + mode: "0644" + notify: Restart angie + + - name: Remove default virtual host + ansible.builtin.file: + path: /etc/angie/http.d/default.conf + state: absent + + handlers: + - name: Restart angie + ansible.builtin.service: + name: angie + state: restarted diff --git a/ansible/playbooks/angie/aerbim.conf b/ansible/playbooks/angie/aerbim.conf index e69de29..e379a7e 100644 --- a/ansible/playbooks/angie/aerbim.conf +++ b/ansible/playbooks/angie/aerbim.conf @@ -0,0 +1,139 @@ + +resolver 8.8.8.8; + +{% if enable_https %} +acme_client {{ app }} https://acme-v02.api.letsencrypt.org/directory; +{% endif %} + +upstream app-backend { + server 127.0.0.1:8000; +} +upstream app-frontend { + server 127.0.0.1:3000; +} + +server { + {% if enable_https %} + listen 443 ssl; + http2 on; + + acme {{ app }}; + ssl_certificate $acme_cert_{{ app }}; + ssl_certificate_key $acme_cert_key_{{ app }}; + + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 5m; + ssl_stapling on; + {% else %} + listen 80; + {% endif %} + server_name {{ domain }}; + + gzip on; + gzip_comp_level 6; + gzip_types image/svg+xml text/plain text/html text/xml text/css text/javascript application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript application/x-font-ttf application/vnd.ms-fontobject font/opentype font/ttf font/eot font/otf; + gzip_disable "MSIE [1-6]\. "; + + add_header Strict-Transport-Security "max-age=31536000;includeSubDomains" always;#Secure header + + if ($host = www.{{domain}}) { + return 301 https://{{domain}}$request_uri; + } + if ($host != {{domain}}) { + return 410; + } + + #access_log /var/log/angie/host.access.log main; + + location /status/ { + api /status/; + allow 127.0.0.1; + deny all; + } + + location /static { + alias /home/{{ user }}/static; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;#Secure header + expires 1y; + add_header Cache-Control "public "; + etag on; + } + + location /media { + alias /home/{{ user }}/media; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;#Secure header + expires 1w; + add_header Cache-Control "public "; + etag on; + } + + location /api/v1 { + proxy_pass http://app-backend; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location /admin { + proxy_pass http://app-backend; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location / { + proxy_pass http://app-frontend; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + + #error_page 404 /404.html; + + client_max_body_size 50M; + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/angie/html; + } + +} + +{% if enable_https %} +server { + listen 80; + listen [::]:80; + + server_name {{ domain }}; + + if ($host != {{domain}}) { + return 410; + } + return 301 https://{{ domain }}$request_uri; +} +{% endif %} + diff --git a/ansible/playbooks/base.yaml b/ansible/playbooks/base.yaml index e69de29..fc2f551 100644 --- a/ansible/playbooks/base.yaml +++ b/ansible/playbooks/base.yaml @@ -0,0 +1,43 @@ +- name: Base + hosts: servers + tasks: + - name: Install packages + ansible.builtin.package: + name: + - curl + - git + - htop + - sudo + - tmux + - vim + state: present + - name: Timezone + community.general.timezone: + name: "{{ timezone }}" + - name: Create user + ansible.builtin.user: + name: "{{ user }}" + shell: /usr/bin/bash + groups: sudo + append: true + - name: Sudo config + ansible.builtin.copy: + dest: /etc/sudoers.d/sudo_no_pass + mode: "0644" + content: | + %sudo ALL=(ALL:ALL) NOPASSWD:ALL + - name: Ssh dir + ansible.builtin.file: + path: "/home/{{ user }}/.ssh" + state: directory + mode: "0755" + owner: "{{ user }}" + group: "{{ user }}" + - name: Ssh keys + ansible.builtin.copy: + dest: "/home/{{ user }}/.ssh/authorized_keys" + content: "{{ ssh_keys }}" + owner: "{{ user }}" + group: "{{ user }}" + mode: "0600" + force: true diff --git a/ansible/playbooks/docker.yaml b/ansible/playbooks/docker.yaml index e69de29..7c6ad92 100644 --- a/ansible/playbooks/docker.yaml +++ b/ansible/playbooks/docker.yaml @@ -0,0 +1,63 @@ +- name: Docker + hosts: servers + tasks: + - name: Preinstall packages + ansible.builtin.package: + name: + - gpg + + - name: Key dir + ansible.builtin.file: + path: "/etc/apt/keyrings" + state: directory + + - name: Add apt key + ansible.builtin.get_url: + url: https://download.docker.com/linux/debian/gpg + dest: /etc/apt/keyrings/docker.asc + + - name: Add docker repo + ansible.builtin.apt_repository: + repo: deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable + state: present + + - name: Install packages + ansible.builtin.package: + name: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + + - name: Docker config + ansible.builtin.copy: + dest: /etc/docker/daemon.json + content: | + { + "log-driver": "journald" + } + notify: + - Restart docker service + + - name: Add user to docker group + ansible.builtin.user: + name: "{{ user }}" + groups: docker + append: true + + - name: Docker compose dir + ansible.builtin.file: + path: "/etc/docker/compose" + state: directory + + - name: Docker compose systemd service + ansible.builtin.template: + src: docker/compose.service + dest: "/etc/systemd/system/compose@.service" + + handlers: + - name: Restart docker service + ansible.builtin.service: + name: "docker" + state: restarted diff --git a/ansible/playbooks/docker/compose.service b/ansible/playbooks/docker/compose.service index e69de29..b63981d 100644 --- a/ansible/playbooks/docker/compose.service +++ b/ansible/playbooks/docker/compose.service @@ -0,0 +1,15 @@ +[Unit] +Description=%i service with docker compose +PartOf=docker.service +After=docker.service + +[Service] +User={{ user }} +Type=oneshot +RemainAfterExit=true +WorkingDirectory=/etc/docker/compose/%i +ExecStart=/usr/bin/docker compose up -d --remove-orphans +ExecStop=/usr/bin/docker compose down + +[Install] +WantedBy=multi-user.target diff --git a/ansible/playbooks/pg.yaml b/ansible/playbooks/pg.yaml index e69de29..81b271f 100644 --- a/ansible/playbooks/pg.yaml +++ b/ansible/playbooks/pg.yaml @@ -0,0 +1,70 @@ +- name: Postgresql + hosts: servers + tasks: + - name: Preinstall packages + ansible.builtin.package: + name: + - gpg + + - name: Key dir + ansible.builtin.file: + path: "/usr/share/postgresql-common/pgdg" + state: directory + + - name: Add apt key + ansible.builtin.get_url: + url: https://www.postgresql.org/media/keys/ACCC4CF8.asc + dest: /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc + + - name: Add postgresql repo + ansible.builtin.apt_repository: + repo: deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main + state: present + + - name: Install packages + ansible.builtin.package: + name: + - postgresql-17 + - libpq-dev + - python3-psycopg2 + + - name: Set cluster listen addresses + become: true + become_user: postgres + ansible.builtin.lineinfile: + path: /etc/postgresql/17/main/postgresql.conf + regexp: "^#?listen_addresses" + line: "listen_addresses = '127.0.0.1, 172.56.0.1'" + notify: Restart postgres + + - name: Create user + become: true + become_user: postgres + community.postgresql.postgresql_user: + name: "{{ db_user }}" + password: "{{ db_password }}" + + - name: Create database + become: true + become_user: postgres + community.postgresql.postgresql_db: + name: "{{ db_name }}" + owner: "{{ db_user }}" + + - name: Grant users access to databases + become: true + become_user: postgres + community.postgresql.postgresql_pg_hba: + dest: /etc/postgresql/17/main/pg_hba.conf + contype: host + users: "{{ db_user }}" + source: 172.56.0.0/24 + method: scram-sha-256 + create: true + notify: Restart postgres + + handlers: + - name: Restart postgres + ansible.builtin.service: + name: postgresql + state: restarted