map
This commit is contained in:
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# === Terraform state ===
|
||||||
|
*.tfstate.backup
|
||||||
|
|
||||||
|
# Crash logs
|
||||||
|
crash.log
|
||||||
|
crash.*.log
|
||||||
|
|
||||||
|
# === Terraform working dir ===
|
||||||
|
.terraform/
|
||||||
|
.terraform.*
|
||||||
|
|
||||||
|
# === Terraform lock files ===
|
||||||
|
# Keep the lock file if you want reproducible provider versions
|
||||||
|
# Comment out the next line if you do want to commit it
|
||||||
|
.terraform.lock.hcl
|
||||||
|
|
||||||
|
# === Local override files ===
|
||||||
|
# These are machine/developer specific, never commit them
|
||||||
|
override.tf
|
||||||
|
override.tf.json
|
||||||
|
*_override.tf
|
||||||
|
*_override.tf.json
|
||||||
|
|
||||||
|
# === Sensitive variable files ===
|
||||||
|
# (add your own if you keep secrets in *.tfvars)
|
||||||
|
*.tfvars
|
||||||
|
*.tfvars.json
|
||||||
|
*.auto.tfvars
|
||||||
|
*.auto.tfvars.json
|
||||||
|
# === Other noise ===
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*.tmp
|
||||||
299
.gitlab-ci.yml
Normal file
299
.gitlab-ci.yml
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
stages:
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
deploy_watchtower:
|
||||||
|
stage: deploy
|
||||||
|
tags:
|
||||||
|
- shared
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== [1] Preparing deploy directory ==="
|
||||||
|
mkdir -p /root/docker
|
||||||
|
rm -rf /root/docker/watchtower
|
||||||
|
cp -r watchtower /root/docker/watchtower
|
||||||
|
|
||||||
|
echo "=== [2] Bringing up Watchtower with docker compose ==="
|
||||||
|
cd /root/docker/watchtower
|
||||||
|
docker compose -f watchtower.yml up -d
|
||||||
|
|
||||||
|
CONTAINER_NAME="watchtower"
|
||||||
|
|
||||||
|
echo "=== [3] Waiting a bit for container to (re)start ==="
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
echo "=== [4] Checking container state ==="
|
||||||
|
docker ps -a --filter "name=${CONTAINER_NAME}"
|
||||||
|
STATUS="$(docker inspect -f '{{.State.Status}}' "${CONTAINER_NAME}")" || STATUS="unknown"
|
||||||
|
echo "Container '${CONTAINER_NAME}' status: ${STATUS}"
|
||||||
|
|
||||||
|
if [ "${STATUS}" != "running" ]; then
|
||||||
|
echo "ERROR: Container '${CONTAINER_NAME}' is not running (status=${STATUS})."
|
||||||
|
echo "Recent logs for ${CONTAINER_NAME}:"
|
||||||
|
docker logs --tail=100 "${CONTAINER_NAME}" || echo "No logs found for ${CONTAINER_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Container '${CONTAINER_NAME}' is running ✅"
|
||||||
|
|
||||||
|
echo "=== [5] Waiting for HEALTHCHECK to become healthy (if defined) ==="
|
||||||
|
MAX_WAIT_SECONDS=120
|
||||||
|
SLEEP_INTERVAL=5
|
||||||
|
ELAPSED=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
HEALTH_STATUS="$(docker inspect -f '{{ if .State.Health }}{{ .State.Health.Status }}{{ end }}' "${CONTAINER_NAME}" || true)"
|
||||||
|
|
||||||
|
if [ -z "${HEALTH_STATUS}" ]; then
|
||||||
|
echo "No HEALTHCHECK defined for '${CONTAINER_NAME}', skipping health verification."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Current health status for '${CONTAINER_NAME}': ${HEALTH_STATUS} (elapsed: ${ELAPSED}s)"
|
||||||
|
|
||||||
|
if [ "${HEALTH_STATUS}" = "healthy" ]; then
|
||||||
|
echo "Container '${CONTAINER_NAME}' health is healthy ✅"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${HEALTH_STATUS}" = "unhealthy" ]; then
|
||||||
|
echo "ERROR: Container '${CONTAINER_NAME}' health is 'unhealthy'."
|
||||||
|
docker inspect "${CONTAINER_NAME}" | grep -A5 -B2 '"Health"' || true
|
||||||
|
docker logs --tail=100 "${CONTAINER_NAME}" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${ELAPSED}" -ge "${MAX_WAIT_SECONDS}" ]; then
|
||||||
|
echo "ERROR: Container '${CONTAINER_NAME}' health did not become 'healthy' within ${MAX_WAIT_SECONDS}s (last status='${HEALTH_STATUS}')."
|
||||||
|
docker inspect "${CONTAINER_NAME}" | grep -A5 -B2 '"Health"' || true
|
||||||
|
docker logs --tail=100 "${CONTAINER_NAME}" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${SLEEP_INTERVAL}"
|
||||||
|
ELAPSED=$((ELAPSED + SLEEP_INTERVAL))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== [6] Deployment completed successfully ✅ ==="
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
deploy_jellyfin:
|
||||||
|
stage: deploy
|
||||||
|
tags:
|
||||||
|
- shared
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DEPLOY_DIR="/root/docker/jellyfin"
|
||||||
|
COMPOSE_FILE="${DEPLOY_DIR}/jellyfin.yml"
|
||||||
|
CONTAINER_NAME="jellyfin"
|
||||||
|
|
||||||
|
echo "=== [1] Preparing deploy directory (safe) ==="
|
||||||
|
# We ONLY touch /root/docker/jellyfin, never your data directories.
|
||||||
|
mkdir -p "${DEPLOY_DIR}"
|
||||||
|
|
||||||
|
# Copy just the compose file from the repo to the deploy dir
|
||||||
|
cp jellyfin/jellyfin.yml "${COMPOSE_FILE}"
|
||||||
|
|
||||||
|
echo "=== [2] Bringing up Jellyfin with docker compose ==="
|
||||||
|
cd "${DEPLOY_DIR}"
|
||||||
|
docker compose -f jellyfin.yml pull
|
||||||
|
docker compose -f jellyfin.yml up -d
|
||||||
|
|
||||||
|
echo "=== [3] Checking container state ==="
|
||||||
|
docker ps -a --filter "name=${CONTAINER_NAME}"
|
||||||
|
|
||||||
|
STATUS="$(docker inspect -f '{{.State.Status}}' "${CONTAINER_NAME}")" || STATUS="unknown"
|
||||||
|
echo "Container '${CONTAINER_NAME}' status: ${STATUS}"
|
||||||
|
|
||||||
|
if [ "${STATUS}" != "running" ]; then
|
||||||
|
echo "ERROR: Container '${CONTAINER_NAME}' is not running (status=${STATUS})."
|
||||||
|
echo "Recent logs for ${CONTAINER_NAME}:"
|
||||||
|
docker logs --tail=100 "${CONTAINER_NAME}" || echo "No logs found for ${CONTAINER_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Container '${CONTAINER_NAME}' is running ✅"
|
||||||
|
|
||||||
|
echo "=== [4] Deployment of Jellyfin completed successfully ✅ ==="
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
deploy_plex:
|
||||||
|
stage: deploy
|
||||||
|
tags:
|
||||||
|
- shared
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DEPLOY_DIR="/root/docker/plex"
|
||||||
|
COMPOSE_FILE="${DEPLOY_DIR}/plex.yml"
|
||||||
|
CONTAINER_NAME="plex"
|
||||||
|
|
||||||
|
echo "=== [1] Preparing deploy directory for Plex (safe) ==="
|
||||||
|
# Only ever touch /root/docker/plex, never your media/config paths.
|
||||||
|
mkdir -p "${DEPLOY_DIR}"
|
||||||
|
|
||||||
|
# Copy just the compose file from the repo into the deploy dir
|
||||||
|
cp plex/plex.yml "${COMPOSE_FILE}"
|
||||||
|
|
||||||
|
echo "=== [2] Bringing up Plex with docker compose ==="
|
||||||
|
cd "${DEPLOY_DIR}"
|
||||||
|
docker compose -f plex.yml pull
|
||||||
|
docker compose -f plex.yml up -d
|
||||||
|
|
||||||
|
echo "=== [3] Checking Plex container state ==="
|
||||||
|
docker ps -a --filter "name=${CONTAINER_NAME}"
|
||||||
|
|
||||||
|
STATUS="$(docker inspect -f '{{.State.Status}}' "${CONTAINER_NAME}")" || STATUS="unknown"
|
||||||
|
echo "Container '${CONTAINER_NAME}' status: ${STATUS}"
|
||||||
|
|
||||||
|
if [ "${STATUS}" != "running" ]; then
|
||||||
|
echo "ERROR: Container '${CONTAINER_NAME}' is not running (status=${STATUS})."
|
||||||
|
echo "Recent logs for ${CONTAINER_NAME}:"
|
||||||
|
docker logs --tail=100 "${CONTAINER_NAME}" || echo "No logs found for ${CONTAINER_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Plex container '${CONTAINER_NAME}' is running ✅"
|
||||||
|
echo "=== [4] Plex deployment completed successfully ✅ ==="
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
|
||||||
|
deploy_wg_easy:
|
||||||
|
stage: deploy
|
||||||
|
tags:
|
||||||
|
- shared
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DEPLOY_DIR="/root/docker/wg-easy"
|
||||||
|
COMPOSE_FILE="${DEPLOY_DIR}/wg-easy.yml"
|
||||||
|
CONTAINER_NAME="wg-easy"
|
||||||
|
|
||||||
|
echo "=== [1] Preparing deploy directory for wg-easy (safe) ==="
|
||||||
|
mkdir -p "${DEPLOY_DIR}"
|
||||||
|
cp wg-easy/wg-easy.yml "${COMPOSE_FILE}"
|
||||||
|
|
||||||
|
echo "=== [2] Bringing up wg-easy with docker compose ==="
|
||||||
|
cd "${DEPLOY_DIR}"
|
||||||
|
docker compose -f wg-easy.yml pull
|
||||||
|
docker compose -f wg-easy.yml up -d
|
||||||
|
|
||||||
|
echo "=== [3] Checking wg-easy container state ==="
|
||||||
|
docker ps -a --filter "name=${CONTAINER_NAME}"
|
||||||
|
STATUS="$(docker inspect -f '{{.State.Status}}' "${CONTAINER_NAME}")" || STATUS="unknown"
|
||||||
|
echo "Container '${CONTAINER_NAME}' status: ${STATUS}"
|
||||||
|
|
||||||
|
if [ "${STATUS}" != "running" ]; then
|
||||||
|
echo "ERROR: Container '${CONTAINER_NAME}' is not running (status=${STATUS})."
|
||||||
|
echo "Recent logs for ${CONTAINER_NAME}:"
|
||||||
|
docker logs --tail=100 "${CONTAINER_NAME}" || echo "No logs found for ${CONTAINER_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "wg-easy container '${CONTAINER_NAME}' is running ✅"
|
||||||
|
echo "=== [4] wg-easy deployment completed successfully ✅ ==="
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
deploy_adguard:
|
||||||
|
stage: deploy
|
||||||
|
tags:
|
||||||
|
- shared # make sure your runner has this tag
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DEPLOY_DIR="/root/docker/adguard"
|
||||||
|
COMPOSE_FILE="${DEPLOY_DIR}/adguard.yml"
|
||||||
|
CONTAINER_NAME="adguardhome"
|
||||||
|
|
||||||
|
echo "=== [1] Preparing deploy directory for AdGuard Home ==="
|
||||||
|
# Only manage /root/docker/adguard — we do NOT touch Docker volumes.
|
||||||
|
mkdir -p "${DEPLOY_DIR}"
|
||||||
|
|
||||||
|
# Copy the compose file from the repo to the deploy directory
|
||||||
|
cp adguard/adguard.yml "${COMPOSE_FILE}"
|
||||||
|
|
||||||
|
echo "=== [2] Running docker compose (pull + up -d) ==="
|
||||||
|
cd "${DEPLOY_DIR}"
|
||||||
|
docker compose -f adguard.yml pull
|
||||||
|
docker compose -f adguard.yml up -d
|
||||||
|
|
||||||
|
echo "=== [3] Checking container state ==="
|
||||||
|
docker ps -a --filter "name=${CONTAINER_NAME}"
|
||||||
|
STATUS="$(docker inspect -f '{{.State.Status}}' "${CONTAINER_NAME}")" || STATUS="unknown"
|
||||||
|
echo "Container '${CONTAINER_NAME}' status: ${STATUS}"
|
||||||
|
|
||||||
|
if [ "${STATUS}" != "running" ]; then
|
||||||
|
echo "❌ ERROR: AdGuard Home is not running (status=${STATUS})"
|
||||||
|
echo "Recent logs:"
|
||||||
|
docker logs --tail=100 "${CONTAINER_NAME}" || echo "No logs found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ AdGuard Home container is running."
|
||||||
|
|
||||||
|
echo "=== [4] Verifying static IP on hurricane network ==="
|
||||||
|
IP_ON_HURRICANE="$(docker inspect -f '{{range .NetworkSettings.Networks}}{{if eq .NetworkID (index (docker network inspect -f \"{{.Id}}\" hurricane) 0)}}{{.IPAddress}}{{end}}{{end}}' "${CONTAINER_NAME}" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
# Fallback simple check if the above is too fancy:
|
||||||
|
if [ -z "${IP_ON_HURRICANE}" ]; then
|
||||||
|
IP_ON_HURRICANE="$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${CONTAINER_NAME}" || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "AdGuard Home IP (as seen by Docker): ${IP_ON_HURRICANE}"
|
||||||
|
|
||||||
|
echo "=== [5] Deployment completed successfully ✅ ==="
|
||||||
|
only:
|
||||||
|
- main # run this deploy only when pushing to main
|
||||||
|
|
||||||
|
deploy_portainer:
|
||||||
|
stage: deploy
|
||||||
|
tags:
|
||||||
|
- shared
|
||||||
|
script: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== [1] Preparing deploy directory for Portainer ==="
|
||||||
|
mkdir -p /root/docker/portainer
|
||||||
|
cp portainer/portainer.yml /root/docker/portainer/portainer.yml
|
||||||
|
|
||||||
|
echo "=== [2] Bringing up Portainer ==="
|
||||||
|
cd /root/docker/portainer
|
||||||
|
docker compose -f portainer.yml pull
|
||||||
|
docker compose -f portainer.yml up -d
|
||||||
|
|
||||||
|
echo "=== [3] Checking container status ==="
|
||||||
|
sleep 3
|
||||||
|
docker ps --filter "name=portainer"
|
||||||
|
only:
|
||||||
|
- main
|
||||||
|
|
||||||
|
deploy_nextcloud:
|
||||||
|
stage: deploy
|
||||||
|
tags:
|
||||||
|
- shared
|
||||||
|
script: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== [1] Preparing nextcloud deploy directory ==="
|
||||||
|
mkdir -p /root/docker/nextcloud
|
||||||
|
|
||||||
|
echo "Copying compose and env files..."
|
||||||
|
cp nextcloud/nextcloud.yml /root/docker/nextcloud/nextcloud.yml
|
||||||
|
cp nextcloud/.env /root/docker/nextcloud/.env
|
||||||
|
|
||||||
|
echo "=== [2] Bringing up Nextcloud with docker compose ==="
|
||||||
|
cd /root/docker/nextcloud
|
||||||
|
docker compose -f nextcloud.yml pull
|
||||||
|
docker compose -f nextcloud.yml up -d
|
||||||
|
|
||||||
|
echo "=== [3] Checking Nextcloud container status ==="
|
||||||
|
sleep 5
|
||||||
|
docker ps --filter "name=nextcloud"
|
||||||
|
only:
|
||||||
|
- main
|
||||||
3
ansible/ansible.cfg
Normal file
3
ansible/ansible.cfg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[defaults]
|
||||||
|
interpreter_python = auto_silent
|
||||||
|
host_key_checking = False
|
||||||
12
ansible/inventory.ini
Normal file
12
ansible/inventory.ini
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[linux]
|
||||||
|
general ansible_host=100.120.57.49 ansible_port=54321
|
||||||
|
k3s1 ansible_host=100.117.166.126 ansible_port=54321
|
||||||
|
k3s2 ansible_host=100.64.200.58 ansible_port=54321
|
||||||
|
k3s3 ansible_host=100.83.32.18 ansible_port=54321
|
||||||
|
loadbalancer ansible_host=100.75.102.81 ansible_port=54321
|
||||||
|
nl ansible_host=100.92.32.17 ansible_port=54321
|
||||||
|
pve ansible_host=100.102.23.33
|
||||||
|
storage01 ansible_host=100.92.109.78
|
||||||
|
ovh ansible_host=p.h-y.st
|
||||||
|
uk ansible_host=100.122.107.18 ansible_port=54321
|
||||||
|
us ansible_host=100.126.105.9 ansible_port=54321
|
||||||
4
ansible/mount.sh
Normal file
4
ansible/mount.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
ansible-playbook -i inventory.ini ssh.yml --vault-password-file ~/.vault_pass.txt
|
||||||
|
ansible-playbook -i inventory.ini update.yml --vault-password-file ~/.vault_pass.txt
|
||||||
|
ansible-playbook -i inventory.ini fail2ban.yml --vault-password-file ~/.vault_pass.txt
|
||||||
|
ansible-playbook -i inventory.ini smb.yml --vault-password-file ~/.vault_pass.txt
|
||||||
108
ansible/ssh.yml
Normal file
108
ansible/ssh.yml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
- name: Phase 1 - Bootstrap SSH Keys and Packages
|
||||||
|
hosts: linux
|
||||||
|
remote_user: root
|
||||||
|
vars_files:
|
||||||
|
- vault.yml
|
||||||
|
tasks:
|
||||||
|
- name: Ensure .ssh directory exists
|
||||||
|
file:
|
||||||
|
path: /root/.ssh
|
||||||
|
state: directory
|
||||||
|
mode: '0700'
|
||||||
|
|
||||||
|
- name: Deploy SSH Identity (Private and Public Keys)
|
||||||
|
copy:
|
||||||
|
dest: "/root/.ssh/{{ item.file }}"
|
||||||
|
content: "{{ item.content }}"
|
||||||
|
mode: "{{ item.mode }}"
|
||||||
|
loop:
|
||||||
|
- { file: 'id_ed25519', content: "{{ my_private_key }}", mode: '0400' }
|
||||||
|
- { file: 'id_ed25519.pub', content: "{{ my_public_key }}", mode: '0644' }
|
||||||
|
when: my_private_key is defined
|
||||||
|
|
||||||
|
- name: Authorize Public Key for Root
|
||||||
|
authorized_key:
|
||||||
|
user: root
|
||||||
|
key: "{{ my_public_key }}"
|
||||||
|
when: my_public_key is defined
|
||||||
|
|
||||||
|
- name: Configure Passwordless Sudo for Zeshan
|
||||||
|
copy:
|
||||||
|
dest: /etc/sudoers.d/zeshan
|
||||||
|
content: "zeshan ALL=(ALL) NOPASSWD: ALL"
|
||||||
|
mode: '0440'
|
||||||
|
|
||||||
|
- name: Phase 2 - Secure SSH Port
|
||||||
|
hosts: linux
|
||||||
|
become: yes
|
||||||
|
vars:
|
||||||
|
new_ssh_port: 54321
|
||||||
|
tasks:
|
||||||
|
- name: Handle SELinux for custom SSH port (RHEL)
|
||||||
|
block:
|
||||||
|
- name: Install SELinux management tools
|
||||||
|
package:
|
||||||
|
name: policycoreutils-python-utils
|
||||||
|
state: present
|
||||||
|
- name: Allow SSH on custom port in SELinux
|
||||||
|
seport:
|
||||||
|
ports: "{{ new_ssh_port }}"
|
||||||
|
proto: tcp
|
||||||
|
setype: ssh_port_t
|
||||||
|
state: present
|
||||||
|
when: ansible_os_family == 'RedHat'
|
||||||
|
|
||||||
|
- name: Configure SSH Port in sshd_config
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#?Port\s+'
|
||||||
|
line: "Port {{ new_ssh_port }}"
|
||||||
|
notify: Restart SSH
|
||||||
|
|
||||||
|
- name: Handle Systemd Socket Activation (Debian/Ubuntu)
|
||||||
|
block:
|
||||||
|
- name: Check if SSH socket exists
|
||||||
|
stat:
|
||||||
|
path: /lib/systemd/system/ssh.socket
|
||||||
|
register: ssh_socket_file
|
||||||
|
|
||||||
|
- name: Create socket override directory
|
||||||
|
file:
|
||||||
|
path: /etc/systemd/system/ssh.socket.d
|
||||||
|
state: directory
|
||||||
|
when: ssh_socket_file.stat.exists
|
||||||
|
|
||||||
|
- name: Set Port in Systemd Socket Override
|
||||||
|
copy:
|
||||||
|
dest: /etc/systemd/system/ssh.socket.d/addresses.conf
|
||||||
|
content: |
|
||||||
|
[Socket]
|
||||||
|
ListenStream=
|
||||||
|
ListenStream={{ new_ssh_port }}
|
||||||
|
when: ssh_socket_file.stat.exists
|
||||||
|
notify:
|
||||||
|
- Reload Systemd
|
||||||
|
- Stop SSH Service
|
||||||
|
- Restart SSH Socket
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: Reload Systemd
|
||||||
|
systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
|
||||||
|
- name: Stop SSH Service
|
||||||
|
service:
|
||||||
|
name: ssh
|
||||||
|
state: stopped
|
||||||
|
when: ansible_os_family == 'Debian'
|
||||||
|
|
||||||
|
- name: Restart SSH Socket
|
||||||
|
service:
|
||||||
|
name: ssh.socket
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: Restart SSH
|
||||||
|
service:
|
||||||
|
name: "{{ (ansible_os_family == 'Debian') | ternary('ssh', 'sshd') }}"
|
||||||
|
state: restarted
|
||||||
65
ansible/update.yml
Normal file
65
ansible/update.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
- name: Universal Linux System Maintenance
|
||||||
|
hosts: linux
|
||||||
|
remote_user: root
|
||||||
|
# Gather facts once at the start to determine OS family
|
||||||
|
gather_facts: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# --- DEBIAN / UBUNTU / PROXMOX ---
|
||||||
|
- name: Debian-based Maintenance
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
block:
|
||||||
|
- name: Update apt cache and upgrade all packages
|
||||||
|
apt:
|
||||||
|
upgrade: dist
|
||||||
|
update_cache: yes
|
||||||
|
cache_valid_time: 3600
|
||||||
|
|
||||||
|
- name: Install baseline toolset (Debian)
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- htop
|
||||||
|
- make
|
||||||
|
- git
|
||||||
|
- curl
|
||||||
|
- samba
|
||||||
|
- fail2ban
|
||||||
|
- sshpass
|
||||||
|
- sudo
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Remove obsolete packages and kernels
|
||||||
|
apt:
|
||||||
|
autoremove: yes
|
||||||
|
autoclean: yes
|
||||||
|
|
||||||
|
# --- RHEL / ALMALINUX / ROCKY ---
|
||||||
|
- name: RedHat-based Maintenance
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
block:
|
||||||
|
- name: Upgrade all packages (DNF)
|
||||||
|
dnf:
|
||||||
|
name: "*"
|
||||||
|
state: latest
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Install baseline toolset (RHEL)
|
||||||
|
dnf:
|
||||||
|
name: [htop, make, nano, git, curl, fail2ban, samba, sshpass]
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Clean DNF metadata and cache
|
||||||
|
command: dnf clean all
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
# --- FINAL CHECK ---
|
||||||
|
- name: Check if reboot is required
|
||||||
|
stat:
|
||||||
|
path: /var/run/reboot-required
|
||||||
|
register: reboot_required_file
|
||||||
|
|
||||||
|
- name: Notify if reboot is needed
|
||||||
|
debug:
|
||||||
|
msg: "Host {{ inventory_hostname }} requires a reboot to apply updates."
|
||||||
|
when: reboot_required_file.stat.exists
|
||||||
50
ansible/vault.yml
Normal file
50
ansible/vault.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
64376334353039653233386464663633646238333537623265623334633061633337353161376638
|
||||||
|
6532373239376635333664653866343239393062316439650a353063653131363166353931333237
|
||||||
|
66646661393663376263323565626331353137323330343664633230373732616566353231623631
|
||||||
|
6263376364633036630a393839306239383066623436356235393263373438623630396139326536
|
||||||
|
32636630336136646636623932383337386331616230663063366337303836633065346239616261
|
||||||
|
32333439363664306235366366346238653735383231393530633833323131333937663065353738
|
||||||
|
34666135656366313633373839376138396566653132353637616261343264366436326337373130
|
||||||
|
30323435613438363432393431313666653063323333633338626265313531356164633764343837
|
||||||
|
34363466616462626436633939303538333531336537393131306136336663636538303739343030
|
||||||
|
63393930343530626363666364626537636231343334393132386537386537356237396634323234
|
||||||
|
34366464313864636639623037613666326430376239386439646665333966653938613465366565
|
||||||
|
35616462333762386532616634656534616365643030653335353132636462633666363639353738
|
||||||
|
31636435623333656262323565383161316164363239643531616162623865666266616639366365
|
||||||
|
36376634386535333765383366323939386133633230373539343936376239646465373266313635
|
||||||
|
31303266353732616533663433626635383962626566396233323265316437326238326131336538
|
||||||
|
30623365633765383138306537303266336436386631396435663366626531656230336565376630
|
||||||
|
32303933343131346161633437363738363336336666666264653532316334633833653134383738
|
||||||
|
32373730343833376334376439376166376666356265366565306562303539333061633061353861
|
||||||
|
62353533326238313961613864346432313937356430373134626265366638393036373930326364
|
||||||
|
34323630313964396165393761613764643633643738353838626238636433366134613333633565
|
||||||
|
65346635356538386164303034323332303736353038656364373435343037336536363439393733
|
||||||
|
39333833623033393062346261376666643236663132343265613438313639326532383132636338
|
||||||
|
36623463383935306164656435383938376138323637653964306364313534306564363562633130
|
||||||
|
39386236373333643165363231306132663036373232653236316230356533373338363636306639
|
||||||
|
39313130626337353663633465643030363466356238626364363132353033366237353834376564
|
||||||
|
65346638383538313861343163653435633734343230313737623136373832323537616438633130
|
||||||
|
62623536343935333539646537383930633339316535343364623230353861336239373535633136
|
||||||
|
64366231656238633233333834663831366538653837613137643163613330333538373362343733
|
||||||
|
62373839396666366266646435336561643031393763396366646262666165616130656437396238
|
||||||
|
64666236623534353536356562616132646561623165346265326231363963323365393336316565
|
||||||
|
66363963346164623962393463646130323138613132383436396562613137343034303634323237
|
||||||
|
36326364333731633233393265306462303038383762356233346266663363653034623164353236
|
||||||
|
31656339353264316337633166383734343335303331376261626531366165326239363836316566
|
||||||
|
36666466653230393137316234306363356433613964393065313563303062363065393232613265
|
||||||
|
34333764336434373361326462643739363361376231623265323564653963313337616363353936
|
||||||
|
32313934343037333836653235653761623133373635626433313935613734653336376633653933
|
||||||
|
65353063323431646437643032383835613865343130336366646338663430346336396163396538
|
||||||
|
30386339663930666539346666303664663836353435666164346635383237343431633730616336
|
||||||
|
34636539343466346433373138323664653561343532383536313738633831613931383635323434
|
||||||
|
30316136383434346437633562383934353764366537646566646239653136623163393130326538
|
||||||
|
37393439383232363266646564623134323439353834353139303066633539363738303932623436
|
||||||
|
36373439363961613337666532633933336566386330353534306363313436333763333465373861
|
||||||
|
33386537346462656430373363303235663565313538353732303064363365343734626237393830
|
||||||
|
30316362333738636237393733313234336536643338626134613065363862643962333836663639
|
||||||
|
37343637326538363635343032353936333938666430346633323966653438636265356532313066
|
||||||
|
37336131396138636438383163613933353130623837346561386638366562343862376266323833
|
||||||
|
37316231346532623934303962633365656433663661333062303033656133336563356435333164
|
||||||
|
34653735303865396330653931336362353334373935653566326166623863616461616635636231
|
||||||
|
3239
|
||||||
78
client.ovpn
Normal file
78
client.ovpn
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
client
|
||||||
|
proto udp
|
||||||
|
explicit-exit-notify
|
||||||
|
remote 154.41.135.47 8080
|
||||||
|
dev tun
|
||||||
|
resolv-retry infinite
|
||||||
|
nobind
|
||||||
|
persist-key
|
||||||
|
persist-tun
|
||||||
|
remote-cert-tls server
|
||||||
|
verify-x509-name server_cyni6qc4D05RIAyn name
|
||||||
|
auth SHA256
|
||||||
|
auth-nocache
|
||||||
|
cipher AES-128-GCM
|
||||||
|
tls-client
|
||||||
|
tls-version-min 1.2
|
||||||
|
tls-cipher TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256
|
||||||
|
ignore-unknown-option block-outside-dns
|
||||||
|
setenv opt block-outside-dns # Prevent Windows 10 DNS leak
|
||||||
|
verb 3
|
||||||
|
<ca>
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB1zCCAX2gAwIBAgIUcv+sBGhEfd1XbXBndZcYoaVvB4cwCgYIKoZIzj0EAwIw
|
||||||
|
HjEcMBoGA1UEAwwTY25fcDhocDdNWGV3eFZzdWtJQTAeFw0yNTEwMDUxODIyNTJa
|
||||||
|
Fw0zNTEwMDMxODIyNTJaMB4xHDAaBgNVBAMME2NuX3A4aHA3TVhld3hWc3VrSUEw
|
||||||
|
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATBFzEmd5ULOahTH2L7cB87t/WqkmOw
|
||||||
|
035tgz6BmPzuIeJhPfnlbSUO1PwtdcjVbAVYxIUiWUGFa+3Y7kVXz1C3o4GYMIGV
|
||||||
|
MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFLsU5suHhUaH2i86fkSeKlC8BZFnMFkG
|
||||||
|
A1UdIwRSMFCAFLsU5suHhUaH2i86fkSeKlC8BZFnoSKkIDAeMRwwGgYDVQQDDBNj
|
||||||
|
bl9wOGhwN01YZXd4VnN1a0lBghRy/6wEaER93VdtcGd1lxihpW8HhzALBgNVHQ8E
|
||||||
|
BAMCAQYwCgYIKoZIzj0EAwIDSAAwRQIhAKTF9TUEK6qvR9eKdsm+g+jUOPgSa2oz
|
||||||
|
8hCMVmYBFE5TAiA+RRfPqAHwzKeXXodSrw7PsACKpdPeUX29U6QRxdqvrA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
</ca>
|
||||||
|
<cert>
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB2TCCAX+gAwIBAgIRAOrCPSzfYpHv2Bbgwps7LbYwCgYIKoZIzj0EAwIwHjEc
|
||||||
|
MBoGA1UEAwwTY25fcDhocDdNWGV3eFZzdWtJQTAeFw0yNTEwMDUxODIzMDJaFw0z
|
||||||
|
NTEwMDMxODIzMDJaMBExDzANBgNVBAMMBnJvdXRlcjBZMBMGByqGSM49AgEGCCqG
|
||||||
|
SM49AwEHA0IABFjSYA3bpnjB0fm/o45ay71tSCqvlllj7YllWEdwI2NDqtmSA2S6
|
||||||
|
MjA4doQ1hiKCxLuk4tj1bZHNy5a2805nmiajgaowgacwCQYDVR0TBAIwADAdBgNV
|
||||||
|
HQ4EFgQUl2uced7PlC3MB/2OhiVPEbaNcWkwWQYDVR0jBFIwUIAUuxTmy4eFRofa
|
||||||
|
Lzp+RJ4qULwFkWehIqQgMB4xHDAaBgNVBAMME2NuX3A4aHA3TVhld3hWc3VrSUGC
|
||||||
|
FHL/rARoRH3dV21wZ3WXGKGlbweHMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1Ud
|
||||||
|
DwQEAwIHgDAKBggqhkjOPQQDAgNIADBFAiAycBNgtvXYK103GWt1pr11EyqrFc37
|
||||||
|
/g+5LzRN4E+CbAIhANCS7h72bw+t1Swk7UPSYiUOJPS6ZjxETXOr0W78FHwx
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
</cert>
|
||||||
|
<key>
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgRZ9xWBze4ypq7ipO
|
||||||
|
klOaIuJDTiiKocZT6SxVZT41LEmhRANCAARY0mAN26Z4wdH5v6OOWsu9bUgqr5ZZ
|
||||||
|
Y+2JZVhHcCNjQ6rZkgNkujIwOHaENYYigsS7pOLY9W2RzcuWtvNOZ5om
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
</key>
|
||||||
|
<tls-crypt>
|
||||||
|
#
|
||||||
|
# 2048 bit OpenVPN static key
|
||||||
|
#
|
||||||
|
-----BEGIN OpenVPN Static key V1-----
|
||||||
|
6d95023c7fe6dd1793518359f8bfce71
|
||||||
|
5de38910df988a47d4253bf3fe68629b
|
||||||
|
71d27ac6b49aaef68a22f6d3b80a63a3
|
||||||
|
d1d57565d78f78ae4cae0b782169facd
|
||||||
|
9a3245acbbb4a80059c758bb066433e8
|
||||||
|
b080ed19a1523344f0b3785a429b5f76
|
||||||
|
b0c2051a3d90437b624c09b1959e3b73
|
||||||
|
dddcf6b34282a5e84595b053f0d50340
|
||||||
|
d8ac7b9fd4fecd4a45350cede6c55fe2
|
||||||
|
22ded165b513c567adebc6181cd4fef9
|
||||||
|
dbd5387e25b83f9b23be67e1866c22ac
|
||||||
|
3cd5961b401dcc6d6a022248dbe927e4
|
||||||
|
56ba8a804f01804ea8c7d48e19e96e55
|
||||||
|
6c47f25b9affcc71a6ae6beadd287e53
|
||||||
|
0aaa69d91c004cc5a22cf8a53bdf7b76
|
||||||
|
f1a3de39124c9cdc90f04ba549e578e4
|
||||||
|
-----END OpenVPN Static key V1-----
|
||||||
|
</tls-crypt>
|
||||||
18
cloudflare/Dockerfile
Normal file
18
cloudflare/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Install tools
|
||||||
|
RUN apk add --no-cache curl bash jq
|
||||||
|
|
||||||
|
# Copy the script
|
||||||
|
COPY cloudflare-ddns.sh /usr/local/bin/cloudflare-ddns.sh
|
||||||
|
|
||||||
|
# --- FIX: This line removes Windows line endings if they exist ---
|
||||||
|
RUN sed -i 's/\r$//' /usr/local/bin/cloudflare-ddns.sh
|
||||||
|
|
||||||
|
# Make it executable
|
||||||
|
RUN chmod +x /usr/local/bin/cloudflare-ddns.sh
|
||||||
|
|
||||||
|
# Setup cron (Runs every 5 minutes and logs to Docker)
|
||||||
|
RUN echo "*/5 * * * * /usr/local/bin/cloudflare-ddns.sh > /proc/1/fd/1 2>&1" > /etc/crontabs/root
|
||||||
|
|
||||||
|
CMD ["crond", "-f", "-l", "2"]
|
||||||
75
cloudflare/checkip.sh
Normal file
75
cloudflare/checkip.sh
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
LOG_FILE="/var/log/public_ip_monitor.log"
|
||||||
|
LAST_IP_FILE="/var/log/last_ip.txt"
|
||||||
|
MAKE_DIR="/root/hurricane/cloudflare/zones" # CHANGE THIS to your Makefile directory
|
||||||
|
|
||||||
|
# Make sure log files exist
|
||||||
|
touch "$LOG_FILE"
|
||||||
|
touch "$LAST_IP_FILE"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
||||||
|
echo "============================================" | tee -a "$LOG_FILE"
|
||||||
|
echo "[$TIMESTAMP] Checking public IP..." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
IP=""
|
||||||
|
METHOD=""
|
||||||
|
|
||||||
|
# Try api.ipify.org
|
||||||
|
echo "[$TIMESTAMP] Trying api.ipify.org..." | tee -a "$LOG_FILE"
|
||||||
|
IP=$(curl -s --max-time 10 https://api.ipify.org)
|
||||||
|
|
||||||
|
if [[ -n "$IP" ]]; then
|
||||||
|
METHOD="api.ipify.org"
|
||||||
|
echo "[$TIMESTAMP] SUCCESS: Retrieved IP: $IP" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "[$TIMESTAMP] FAILED: api.ipify.org did not return an IP." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[$TIMESTAMP] Trying ifconfig.me..." | tee -a "$LOG_FILE"
|
||||||
|
IP=$(curl -s --max-time 10 http://ifconfig.io)
|
||||||
|
|
||||||
|
if [[ -n "$IP" ]]; then
|
||||||
|
METHOD="ifconfig.me"
|
||||||
|
echo "[$TIMESTAMP] SUCCESS: Retrieved IP: $IP" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "[$TIMESTAMP] FAILED: ifconfig.me did not return an IP." | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[$TIMESTAMP] Trying dig opendns..." | tee -a "$LOG_FILE"
|
||||||
|
IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
|
||||||
|
|
||||||
|
if [[ -n "$IP" ]]; then
|
||||||
|
METHOD="dig opendns"
|
||||||
|
echo "[$TIMESTAMP] SUCCESS: Retrieved IP: $IP" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
IP="FAILED TO RESOLVE"
|
||||||
|
METHOD="All methods failed"
|
||||||
|
echo "[$TIMESTAMP] ERROR: All methods failed to retrieve IP." | tee -a "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$IP" == "FAILED TO RESOLVE" ]]; then
|
||||||
|
echo "[$TIMESTAMP] ERROR: Could not determine public IP." | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
# Read previous IP
|
||||||
|
LAST_IP=$(cat "$LAST_IP_FILE")
|
||||||
|
|
||||||
|
if [[ "$IP" != "$LAST_IP" ]]; then
|
||||||
|
echo "[$TIMESTAMP] Detected IP change: $LAST_IP --> $IP" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[$TIMESTAMP] Running 'make apply' in $MAKE_DIR..." | tee -a "$LOG_FILE"
|
||||||
|
cd "$MAKE_DIR"
|
||||||
|
make apply >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
echo "$IP" > "$LAST_IP_FILE"
|
||||||
|
echo "[$TIMESTAMP] 'make apply' completed." | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "[$TIMESTAMP] No change detected. No action taken." | tee -a "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[$TIMESTAMP] Sleeping 5 minutes..." | tee -a "$LOG_FILE"
|
||||||
|
sleep 300
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
51
cloudflare/cloudflare-ddns.sh
Normal file
51
cloudflare/cloudflare-ddns.sh
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "[$(date)] --- DDNS Check Started ---"
|
||||||
|
|
||||||
|
# 1. Validate variables are not empty
|
||||||
|
if [ -z "$CF_API_TOKEN" ] || [ -z "$CF_ZONE_ID" ] || [ -z "$CF_RECORD_NAME" ]; then
|
||||||
|
echo "ERROR: One or more environment variables (TOKEN, ZONE_ID, RECORD_NAME) are missing!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Get current public IP (Trying two services for reliability)
|
||||||
|
NEW_IP=$(curl -s https://api.ipify.org || curl -s https://ifconfig.me/ip)
|
||||||
|
|
||||||
|
if [[ ! $NEW_IP =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
|
||||||
|
echo "ERROR: Could not get a valid Public IP."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Get the current Record info from Cloudflare
|
||||||
|
RECORD_DATA=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=$CF_RECORD_NAME&type=A" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json")
|
||||||
|
|
||||||
|
# Extract Record ID and Current IP from the JSON response
|
||||||
|
RECORD_ID=$(echo "$RECORD_DATA" | jq -r '.result[0].id // empty')
|
||||||
|
OLD_IP=$(echo "$RECORD_DATA" | jq -r '.result[0].content // empty')
|
||||||
|
|
||||||
|
if [ -z "$RECORD_ID" ]; then
|
||||||
|
echo "ERROR: Could not find DNS record for $CF_RECORD_NAME. Check your Zone ID and Name."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Compare and Update
|
||||||
|
if [ "$NEW_IP" = "$OLD_IP" ]; then
|
||||||
|
echo "IP is still $OLD_IP. No update needed."
|
||||||
|
else
|
||||||
|
echo "IP changed from $OLD_IP to $NEW_IP. Updating Cloudflare..."
|
||||||
|
|
||||||
|
UPDATE_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
|
||||||
|
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{\"type\":\"A\",\"name\":\"$CF_RECORD_NAME\",\"content\":\"$NEW_IP\",\"ttl\":120,\"proxied\":false}")
|
||||||
|
|
||||||
|
SUCCESS=$(echo "$UPDATE_RESPONSE" | jq -r '.success')
|
||||||
|
|
||||||
|
if [ "$SUCCESS" = "true" ]; then
|
||||||
|
echo "SUCCESS: Cloudflare updated to $NEW_IP"
|
||||||
|
else
|
||||||
|
echo "FAILURE: Update failed. Response: $UPDATE_RESPONSE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
9
cloudflare/docker-compose.yml
Normal file
9
cloudflare/docker-compose.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
cloudflare-updater:
|
||||||
|
build: .
|
||||||
|
container_name: cloudflare-ddns
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- CF_API_TOKEN=tDRW0bR8oiRI3xLYAOIGT_FVqIejif7hqk93W2Sc
|
||||||
|
- CF_ZONE_ID=0f670677e7c36e9fe8f8e6a1d1c72cbf
|
||||||
|
- CF_RECORD_NAME=home.ztariq.com
|
||||||
27
cloudflare/terraform.sh
Normal file
27
cloudflare/terraform.sh
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
docker run --rm -it -v "$PWD":/app -w /app hashicorp/terraform:latest init
|
||||||
|
export AWS_ACCESS_KEY_ID="696EwxMMRUABP"
|
||||||
|
export AWS_SECRET_ACCESS_KEY="Ow5uqEka8Uzk0ea4Ag4wPacO4tiz5MsQV3JF4GuK"
|
||||||
|
export AWS_DEFAULT_REGION="eus3"
|
||||||
|
export AWS_EC2_METADATA_DISABLED=true
|
||||||
|
aws --endpoint-url https://YOUR-S3-ENDPOINT s3 ls s3://terraform
|
||||||
|
|
||||||
|
|
||||||
|
docker run --rm -it \
|
||||||
|
-v "$PWD":/app -w /app \
|
||||||
|
-e AWS_ACCESS_KEY_ID \
|
||||||
|
-e AWS_SECRET_ACCESS_KEY \
|
||||||
|
-e AWS_DEFAULT_REGION=us-east-1 \
|
||||||
|
hashicorp/terraform:latest apply
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name syncthing \
|
||||||
|
-e PUID=1000 \
|
||||||
|
-e PGID=1000 \
|
||||||
|
-e TZ=Europe/London \
|
||||||
|
-p 8384:8384 \ # Web UI
|
||||||
|
-p 22000:22000/tcp \ # Sync (TCP)
|
||||||
|
-p 22000:22000/udp \ # QUIC (UDP)
|
||||||
|
-p 21027:21027/udp \ # Local discovery
|
||||||
|
-v ./sync/config:/config \
|
||||||
|
-v ./sync/data:/data \
|
||||||
|
lscr.io/linuxserver/syncthing:latest
|
||||||
0
cloudflare/zones/azuredevops/backend.tf
Normal file
0
cloudflare/zones/azuredevops/backend.tf
Normal file
41
cloudflare/zones/azuredevops/main.tf
Normal file
41
cloudflare/zones/azuredevops/main.tf
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
cloudflare = {
|
||||||
|
source = "cloudflare/cloudflare"
|
||||||
|
version = "~> 5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "cloudflare" {
|
||||||
|
api_token = var.cloudflare_apitoken
|
||||||
|
}
|
||||||
|
locals {
|
||||||
|
azure_records = {
|
||||||
|
root_a = { name = "@", type = "A", content = "185.139.7.37", ttl = 1, proxied = true }
|
||||||
|
root_uk_aaaa = { name = "@", type = "AAAA", content = "2a12:ab46:5344:fd::a", ttl = 1, proxied = true }
|
||||||
|
|
||||||
|
autodiscover = { name = "autodiscover", type = "CNAME", content = "eu1.workspace.org.", ttl = 360, proxied = false }
|
||||||
|
mail = { name = "mail", type = "CNAME", content = "eu1.workspace.org.", ttl = 360, proxied = false }
|
||||||
|
mail_mx = { name = "@", type = "MX", content = "eu1.workspace.org.", priority = 10, proxied = false }
|
||||||
|
|
||||||
|
spf_txt = { name = "@", content = "v=spf1 include:_spf.workspace.org -all", type = "TXT", ttl = 3600 }
|
||||||
|
verify_txt = { name = "workspace-verification", content = "44856072-5cde-458d-86c9-c8f86c0ab7bd", type = "TXT", ttl = 360 }
|
||||||
|
dkim_txt = { name = "cf8DDF69382578883._domainKey", content = "v=DKIM1;k=rsa;h=sha256;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr/Mu/P1bfiMIGkHNnvhLB1oVcAaSOg4QoKTCF9N6F/eVV7JCoERTSSHiMyS74V/xq0i3kUJYjspFgrXKicVaEl6jHmRJ4jSyb2b52frWzLakW1SB9LJwXZ/n0PDm90iSPToQOEvQTSl+pg9B9RWfhqr3Tv5hz9YvsjQP1tn7yNwJSbyhU944PWZimu0ryqwAQyLGNP+CsIeMTinwe0B8Rdtc52TusInwhcMddL9XgGYi/IsWsuri85R5yvzIOKk/sklfuDHOSQoCap7RW+Lm22B/DzC0spdjV42n0k4tGtv6Rz0bYT/2DpcqRVIQd9EAcTeUFq3qOYZCHsN0Q+iS2QIDAQAB", type = "TXT", ttl = 3600 }
|
||||||
|
dmarc_txt = { name = "_dmarc", content = "v=DMARC1; p=quarantine; rua=mailto:postmaster@azuredevops.co.uk; ruf=mailto:postmaster@azuredevops.co.uk; fo=1; adkim=s; aspf=s", type = "TXT", ttl = 3600 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
resource "cloudflare_dns_record" "this" {
|
||||||
|
for_each = local.azure_records
|
||||||
|
|
||||||
|
zone_id = var.zone_id
|
||||||
|
name = each.value.name
|
||||||
|
content = each.value.content
|
||||||
|
type = each.value.type
|
||||||
|
ttl = each.value.ttl
|
||||||
|
|
||||||
|
proxied = lookup(each.value, "proxied", false)
|
||||||
|
priority = lookup(each.value, "priority", null)
|
||||||
|
}
|
||||||
258
cloudflare/zones/azuredevops/terraform.tfstate
Normal file
258
cloudflare/zones/azuredevops/terraform.tfstate
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"terraform_version": "1.13.3",
|
||||||
|
"serial": 59,
|
||||||
|
"lineage": "87d4a795-db19-508c-0f53-1e6bdd9b5d93",
|
||||||
|
"outputs": {},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "cloudflare_dns_record",
|
||||||
|
"name": "this",
|
||||||
|
"provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]",
|
||||||
|
"instances": [
|
||||||
|
{
|
||||||
|
"index_key": "autodiscover",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "eu1.workspace.org.",
|
||||||
|
"created_on": "2025-10-03T10:35:51Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "2c03ba416645db773ec3b0bdc7514d1e",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-04T13:37:12Z",
|
||||||
|
"name": "autodiscover.azuredevops.co.uk",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": false,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "CNAME",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "dkim_txt",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "v=DKIM1;k=rsa;h=sha256;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr/Mu/P1bfiMIGkHNnvhLB1oVcAaSOg4QoKTCF9N6F/eVV7JCoERTSSHiMyS74V/xq0i3kUJYjspFgrXKicVaEl6jHmRJ4jSyb2b52frWzLakW1SB9LJwXZ/n0PDm90iSPToQOEvQTSl+pg9B9RWfhqr3Tv5hz9YvsjQP1tn7yNwJSbyhU944PWZimu0ryqwAQyLGNP+CsIeMTinwe0B8Rdtc52TusInwhcMddL9XgGYi/IsWsuri85R5yvzIOKk/sklfuDHOSQoCap7RW+Lm22B/DzC0spdjV42n0k4tGtv6Rz0bYT/2DpcqRVIQd9EAcTeUFq3qOYZCHsN0Q+iS2QIDAQAB",
|
||||||
|
"created_on": "2025-10-03T10:35:51Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "d5bf0503497e76584ffbe03173f8d644",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:35:51Z",
|
||||||
|
"name": "cf8DDF69382578883._domainKey",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 3600,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "dmarc_txt",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "v=DMARC1; p=quarantine; rua=mailto:postmaster@azuredevops.co.uk; ruf=mailto:postmaster@azuredevops.co.uk; fo=1; adkim=s; aspf=s",
|
||||||
|
"created_on": "2025-10-03T10:35:50Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "50d45619b699b9482803d7f4a624127a",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:35:50Z",
|
||||||
|
"name": "_dmarc.azuredevops.co.uk",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 3600,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "mail",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "eu1.workspace.org.",
|
||||||
|
"created_on": "2025-10-03T10:35:51Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "9f0db2a3b0ad84d1037580611c84a348",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-04T13:37:12Z",
|
||||||
|
"name": "mail.azuredevops.co.uk",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": false,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "CNAME",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "root_a",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "185.139.7.37",
|
||||||
|
"created_on": "2025-10-03T10:35:51Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "9868e7bf3f8cc58583eda37a9f45434f",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-04T13:37:12Z",
|
||||||
|
"name": "azuredevops.co.uk",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": true,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "root_uk_aaaa",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "2a12:ab46:5344:fd::a",
|
||||||
|
"created_on": "2025-10-04T13:33:21Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "a9db6975674cf2e19cc3e99cf79a2904",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-04T13:37:12Z",
|
||||||
|
"name": "azuredevops.co.uk",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": true,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "AAAA",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "spf_txt",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "v=spf1 include:_spf.workspace.org -all",
|
||||||
|
"created_on": "2025-10-03T10:35:50Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "7fef7251bb88c2b501343951705cdb3b",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:35:50Z",
|
||||||
|
"name": "azuredevops.co.uk",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 3600,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "verify_txt",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "44856072-5cde-458d-86c9-c8f86c0ab7bd",
|
||||||
|
"created_on": "2025-10-03T10:35:50Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "43850a81936261408d3cd135c064c199",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:35:50Z",
|
||||||
|
"name": "workspace-verification.azuredevops.co.uk",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "d2697ef5d69f322186bdbc812fdad150"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"check_results": null
|
||||||
|
}
|
||||||
15
cloudflare/zones/azuredevops/variables.tf
Normal file
15
cloudflare/zones/azuredevops/variables.tf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
variable "cloudflare_apitoken" {
|
||||||
|
description = "Cloudflare API token"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "zone_id" {
|
||||||
|
description = "Cloudflare zone ID"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "current_ip" {
|
||||||
|
description = "Current public IP address"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
6
cloudflare/zones/dreamartdecor/backend.tf
Normal file
6
cloudflare/zones/dreamartdecor/backend.tf
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
terraform {
|
||||||
|
backend "pg" {
|
||||||
|
conn_str = "postgres://zeshan:Shan33779488@100.115.152.20:5432/terraform?sslmode=disable"
|
||||||
|
schema_name = "dreamartdecor-state"
|
||||||
|
}
|
||||||
|
}
|
||||||
78
cloudflare/zones/dreamartdecor/main.tf
Normal file
78
cloudflare/zones/dreamartdecor/main.tf
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
cloudflare = {
|
||||||
|
source = "cloudflare/cloudflare"
|
||||||
|
version = "5.8.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "cloudflare" {
|
||||||
|
api_token = var.cloudflare_apitoken
|
||||||
|
}
|
||||||
|
locals {
|
||||||
|
dream_records = {
|
||||||
|
dream_mail = {
|
||||||
|
name = "mail"
|
||||||
|
content = "168.119.13.219"
|
||||||
|
type = "A"
|
||||||
|
ttl = 86400
|
||||||
|
}
|
||||||
|
dream_mx = {
|
||||||
|
name = "@"
|
||||||
|
content = "mail.dreamartdecor.com"
|
||||||
|
type = "MX"
|
||||||
|
ttl = 86400
|
||||||
|
priority = 10
|
||||||
|
}
|
||||||
|
dream_autoconfig = {
|
||||||
|
name = "autoconfig"
|
||||||
|
content = "mail.dreamartdecor.com"
|
||||||
|
type = "CNAME"
|
||||||
|
ttl = 86400
|
||||||
|
}
|
||||||
|
dream_mail_aaaa = {
|
||||||
|
name = "mail"
|
||||||
|
content = "2a01:4f8:242:4460::2"
|
||||||
|
type = "AAAA"
|
||||||
|
ttl = 86400
|
||||||
|
}
|
||||||
|
dream_txt_spf = {
|
||||||
|
name = "@"
|
||||||
|
content = "v=spf1 a mx ip4:168.119.13.219 ip6:2a01:4f8:242:4460::2 ~all"
|
||||||
|
type = "TXT"
|
||||||
|
ttl = 86400
|
||||||
|
}
|
||||||
|
dream_txt_dkim = {
|
||||||
|
name = "x._domainkey"
|
||||||
|
content = "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1GW9inpeYtcxMWY3JjUnVFzsPgKCZOfOCETVvk5wWOYZr9LJGz0YnJu3xGIZeJiFWDOgGV/xorzlcAzDqumh58cYPkDIzYVgbOp8vw1qS+a3iKMtRM99kyadEmUDyKjHk11HiCADNaEAgCD1vaKlQzRGAmdP15XhFC7xprSPQPAi6z/l2Iy3wsLdMpYR9P+tiSpS0msI86PBj4Kj5JRuzyHMw4YCqLRKMOIXTKO/zBWOAJOc/eKbjMyTT/iUJe9YE5yuzUHSZNT57aTHIGGadhFhMrkCNVFMyuCGZFt7fCF+Xzvu0iljYK/Uw4Zru73fTaUtq8SMcnvLjj7lm0fpvwIDAQAB"
|
||||||
|
type = "TXT"
|
||||||
|
ttl = 86400
|
||||||
|
}
|
||||||
|
dream_dmarc = {
|
||||||
|
name = "_dmarc"
|
||||||
|
content = "v=DMARC1; p=none; rua=mailto:4a937e10a8e144c89cb11f1272c159c0@dmarc-reports.cloudflare.net"
|
||||||
|
type = "TXT"
|
||||||
|
ttl = 86400
|
||||||
|
}
|
||||||
|
dream_www = {
|
||||||
|
name = "www"
|
||||||
|
content = "dreamartdecor.com"
|
||||||
|
type = "CNAME"
|
||||||
|
ttl = 1
|
||||||
|
proxied = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource "cloudflare_dns_record" "this" {
|
||||||
|
for_each = local.dream_records
|
||||||
|
|
||||||
|
zone_id = var.zone_id
|
||||||
|
name = each.value.name
|
||||||
|
content = each.value.content
|
||||||
|
type = each.value.type
|
||||||
|
ttl = each.value.ttl
|
||||||
|
|
||||||
|
proxied = lookup(each.value, "proxied", false)
|
||||||
|
priority = lookup(each.value, "priority", null)
|
||||||
|
}
|
||||||
15
cloudflare/zones/dreamartdecor/variables.tf
Normal file
15
cloudflare/zones/dreamartdecor/variables.tf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
variable "cloudflare_apitoken" {
|
||||||
|
description = "Cloudflare API token"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "zone_id" {
|
||||||
|
description = "Cloudflare zone ID"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "current_ip" {
|
||||||
|
description = "Current public IP address"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
0
cloudflare/zones/ztariq/backend.tf
Normal file
0
cloudflare/zones/ztariq/backend.tf
Normal file
83
cloudflare/zones/ztariq/main.tf
Normal file
83
cloudflare/zones/ztariq/main.tf
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
cloudflare = {
|
||||||
|
source = "cloudflare/cloudflare"
|
||||||
|
version = "~> 5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "cloudflare" {
|
||||||
|
api_token = var.cloudflare_apitoken
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
ztariq_records = {
|
||||||
|
# --- A Records ---
|
||||||
|
beszel = { name = "beszel", type = "A", content = "198.23.169.195", ttl = 1, proxied = true }
|
||||||
|
ca = { name = "ca", type = "A", content = "154.12.117.17", ttl = 1, proxied = false }
|
||||||
|
nc = { name = "nc", type = "A", content = "154.12.117.17", ttl = 1, proxied = false }
|
||||||
|
nl = { name = "nl", type = "A", content = "62.84.172.70", ttl = 1, proxied = false }
|
||||||
|
reg = { name = "reg", type = "A", content = "154.12.117.17", ttl = 1, proxied = false }
|
||||||
|
tea = { name = "tea", type = "A", content = "198.23.169.195", ttl = 1, proxied = false }
|
||||||
|
uk = { name = "uk", type = "A", content = "185.139.7.37", ttl = 1, proxied = false }
|
||||||
|
uk2 = { name = "uk2", type = "A", content = "154.41.135.47", ttl = 1, proxied = false }
|
||||||
|
uptime = { name = "uptime", type = "A", content = "198.23.169.195", ttl = 1, proxied = true }
|
||||||
|
us = { name = "us", type = "A", content = "198.23.169.195", ttl = 1, proxied = false }
|
||||||
|
root_a = { name = "@", type = "A", content = "185.139.7.37", ttl = 1, proxied = true }
|
||||||
|
# --- AAAA Records ---
|
||||||
|
nl_aaaa = { name = "nl", type = "AAAA", content = "2a12:bec4:1821:f0::a", ttl = 1, proxied = false }
|
||||||
|
root_uk_aaaa = { name = "@", type = "AAAA", content = "2a12:ab46:5344:fd::a", ttl = 1, proxied = true }
|
||||||
|
root_uk_uk = { name = "uk", type = "AAAA", content = "2a12:ab46:5344:fd::a", ttl = 1, proxied = false }
|
||||||
|
|
||||||
|
# --- CNAME Records ---
|
||||||
|
autodiscover = { name = "autodiscover", type = "CNAME", content = "eu1.workspace.org.", ttl = 360, proxied = false }
|
||||||
|
mail = { name = "mail", type = "CNAME", content = "eu1.workspace.org.", ttl = 360, proxied = false }
|
||||||
|
|
||||||
|
# --- MX Records ---
|
||||||
|
mx_root = { name = "@", type = "MX", content = "eu1.workspace.org.", priority = 10, ttl = 360 }
|
||||||
|
|
||||||
|
# --- TXT Records ---
|
||||||
|
dmarc = {
|
||||||
|
name = "_dmarc"
|
||||||
|
type = "TXT"
|
||||||
|
content = "v=DMARC1; p=quarantine; rua=mailto:postmaster@ztariq.com; ruf=mailto:postmaster@ztariq.com; fo=1; adkim=s; aspf=s"
|
||||||
|
ttl = 3600
|
||||||
|
}
|
||||||
|
dkim = {
|
||||||
|
name = "nd8ddf6995beebee4._domainkey"
|
||||||
|
type = "TXT"
|
||||||
|
content = "v=DKIM1; k=rsa; h=sha256; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoi3yX1W5V6a9QbEXo00k9JCZ8Vew5rQEanHLIY0cOxCauAIZZIrhQsexZ0j45EFVtfMrBHeddUtolVSSDHvvJg49HzJqWsKOsN061uBgmdN69JEtzme04pRmz/7H+3Y0QDUSYDd+ffYzWaouplFqGuhYkQ5QG2J1JzofcetuAkQICIgWStcOO+av5WoyTdxfqsY64d/XFP4PZJJHX0XA1P2YaSuyNF5c7nv/+a9A6F5+OrgZhFNNWjUurkKKhFzhbR82BUPTXVuG3EI5wSQcIYjhXgINagsmvVyPL1XP584qtnq0ScGysSkh0T3Vhg/Kob9eHX1du7mZj7G0z3PHmwIDAQAB"
|
||||||
|
ttl = 360
|
||||||
|
}
|
||||||
|
workspace_verification = {
|
||||||
|
name = "workspace-verification"
|
||||||
|
type = "TXT"
|
||||||
|
content = "f23716dd-2ad6-4dd4-8867-112e3c4c318d"
|
||||||
|
ttl = 360
|
||||||
|
}
|
||||||
|
spf = {
|
||||||
|
name = "@"
|
||||||
|
type = "TXT"
|
||||||
|
content = "v=spf1 include:_spf.workspace.org -all"
|
||||||
|
ttl = 360
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "cloudflare_dns_record" "ztariq" {
|
||||||
|
for_each = local.ztariq_records
|
||||||
|
|
||||||
|
zone_id = var.zone_id
|
||||||
|
name = each.value.name
|
||||||
|
type = each.value.type
|
||||||
|
content = each.value.content
|
||||||
|
ttl = each.value.ttl
|
||||||
|
|
||||||
|
proxied = lookup(each.value, "proxied", null)
|
||||||
|
priority = lookup(each.value, "priority", null)
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = false
|
||||||
|
}
|
||||||
|
}
|
||||||
648
cloudflare/zones/ztariq/terraform.tfstate
Normal file
648
cloudflare/zones/ztariq/terraform.tfstate
Normal file
@@ -0,0 +1,648 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"terraform_version": "1.13.3",
|
||||||
|
"serial": 67,
|
||||||
|
"lineage": "86dbab99-bb75-e967-6f01-8134ccc693e6",
|
||||||
|
"outputs": {},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "cloudflare_dns_record",
|
||||||
|
"name": "ztariq",
|
||||||
|
"provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]",
|
||||||
|
"instances": [
|
||||||
|
{
|
||||||
|
"index_key": "autodiscover",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "eu1.workspace.org.",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "1da92323f9f6e5a00e02df0edac16554",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-09T08:38:58Z",
|
||||||
|
"name": "autodiscover.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": false,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "CNAME",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "beszel",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "198.23.169.195",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "e1824f42449cb3fed3024633819bd345",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:38Z",
|
||||||
|
"name": "beszel.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": true,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "ca",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "154.12.117.17",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "8eedfb649e973cacfcb117155ccbca61",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:37Z",
|
||||||
|
"name": "ca.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "dkim",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "v=DKIM1; k=rsa; h=sha256; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoi3yX1W5V6a9QbEXo00k9JCZ8Vew5rQEanHLIY0cOxCauAIZZIrhQsexZ0j45EFVtfMrBHeddUtolVSSDHvvJg49HzJqWsKOsN061uBgmdN69JEtzme04pRmz/7H+3Y0QDUSYDd+ffYzWaouplFqGuhYkQ5QG2J1JzofcetuAkQICIgWStcOO+av5WoyTdxfqsY64d/XFP4PZJJHX0XA1P2YaSuyNF5c7nv/+a9A6F5+OrgZhFNNWjUurkKKhFzhbR82BUPTXVuG3EI5wSQcIYjhXgINagsmvVyPL1XP584qtnq0ScGysSkh0T3Vhg/Kob9eHX1du7mZj7G0z3PHmwIDAQAB",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "e7d739b620e7de9ddf4acc1d35d9104e",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:38Z",
|
||||||
|
"name": "nd8ddf6995beebee4._domainkey.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "dmarc",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "v=DMARC1; p=quarantine; rua=mailto:postmaster@ztariq.com; ruf=mailto:postmaster@ztariq.com; fo=1; adkim=s; aspf=s",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "d687c8ca6f4cd2057d1607c77797b8dd",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:38Z",
|
||||||
|
"name": "_dmarc.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 3600,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "mail",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "eu1.workspace.org.",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "8adb370489931da4b8726c8142b468b2",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-09T08:38:58Z",
|
||||||
|
"name": "mail.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": false,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "CNAME",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "mx_root",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "eu1.workspace.org.",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "1853b26b8d52a41c5ef0f4212c41696f",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:38Z",
|
||||||
|
"name": "ztariq.com",
|
||||||
|
"priority": 10,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "MX",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "nc",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "154.12.117.17",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "9188fe0d253b8094f19320d4afff3d9a",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:37Z",
|
||||||
|
"name": "nc.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "nl",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "62.84.172.70",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "18b04c8ced7b1ae3bcf5fc873e1fbdf8",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:38Z",
|
||||||
|
"name": "nl.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "nl_aaaa",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "2a12:bec4:1821:f0::a",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "f6bffdbdb7f94832d39186a147687fe8",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:37Z",
|
||||||
|
"name": "nl.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "AAAA",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "reg",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "154.12.117.17",
|
||||||
|
"created_on": "2025-10-09T08:38:58Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "2f92ab4eb7475d3c9b678f49abbc9ae3",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-09T08:38:58Z",
|
||||||
|
"name": "reg",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "root_a",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "185.139.7.37",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "1d1e80fd88cbed6b00ddf0ac4d856e0f",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-04T13:37:26Z",
|
||||||
|
"name": "ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": true,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "root_uk_aaaa",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "2a12:ab46:5344:fd::a",
|
||||||
|
"created_on": "2025-10-04T13:30:31Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "69fa5646418278ab2d865b509349f85d",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-04T14:52:55Z",
|
||||||
|
"name": "ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": true,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "AAAA",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "root_uk_uk",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "2a12:ab46:5344:fd::a",
|
||||||
|
"created_on": "2025-10-04T13:32:56Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "041c4d5e021eeb72b16ce82b3215d114",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-04T14:52:55Z",
|
||||||
|
"name": "uk.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "AAAA",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "spf",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "v=spf1 include:_spf.workspace.org -all",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "a7dc36bb0d2542c1d9534e70af352a70",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:38Z",
|
||||||
|
"name": "ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "tea",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "198.23.169.195",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "12be831a0a9cfcaac555f82acbabec70",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:37Z",
|
||||||
|
"name": "tea.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "uk",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "185.139.7.37",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "c932fd20294dd7e63dd49bcbb42dd46d",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:37Z",
|
||||||
|
"name": "uk.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "uk2",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "154.41.135.47",
|
||||||
|
"created_on": "2025-10-05T13:44:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "3e85a02d4d60fdd2746eded881daf70b",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-05T14:03:16Z",
|
||||||
|
"name": "uk2.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "uptime",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "198.23.169.195",
|
||||||
|
"created_on": "2025-10-03T10:41:38Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "79f47fe2d5ec8575b215fddd6bbb1f6b",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:38Z",
|
||||||
|
"name": "uptime.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": true,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "us",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "198.23.169.195",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "3532e6cb018f85319c15430387aba340",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:37Z",
|
||||||
|
"name": "us.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 1,
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index_key": "workspace_verification",
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"comment": null,
|
||||||
|
"comment_modified_on": null,
|
||||||
|
"content": "f23716dd-2ad6-4dd4-8867-112e3c4c318d",
|
||||||
|
"created_on": "2025-10-03T10:41:37Z",
|
||||||
|
"data": null,
|
||||||
|
"id": "396e522c02d0fe0b716f174d2cdca4e3",
|
||||||
|
"meta": "{}",
|
||||||
|
"modified_on": "2025-10-03T10:41:37Z",
|
||||||
|
"name": "workspace-verification.ztariq.com",
|
||||||
|
"priority": null,
|
||||||
|
"proxiable": false,
|
||||||
|
"proxied": false,
|
||||||
|
"settings": {
|
||||||
|
"flatten_cname": null,
|
||||||
|
"ipv4_only": null,
|
||||||
|
"ipv6_only": null
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"tags_modified_on": null,
|
||||||
|
"ttl": 360,
|
||||||
|
"type": "TXT",
|
||||||
|
"zone_id": "0f670677e7c36e9fe8f8e6a1d1c72cbf"
|
||||||
|
},
|
||||||
|
"sensitive_attributes": [],
|
||||||
|
"identity_schema_version": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"check_results": null
|
||||||
|
}
|
||||||
15
cloudflare/zones/ztariq/variables.tf
Normal file
15
cloudflare/zones/ztariq/variables.tf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
variable "cloudflare_apitoken" {
|
||||||
|
description = "Cloudflare API token"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "zone_id" {
|
||||||
|
description = "Cloudflare zone ID"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "current_ip" {
|
||||||
|
description = "Current public IP address"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
497
cv archive/index2.html
Normal file
497
cv archive/index2.html
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Zeshan Tariq – DevOps · SRE · SOC</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #020617;
|
||||||
|
--bg-alt: #020617;
|
||||||
|
--accent-1: #22d3ee;
|
||||||
|
--accent-2: #a855f7;
|
||||||
|
--accent-3: #4ade80;
|
||||||
|
--text-main: #e5e7eb;
|
||||||
|
--text-soft: #9ca3af;
|
||||||
|
--text-muted: #6b7280;
|
||||||
|
--border-subtle: rgba(148,163,184,0.35);
|
||||||
|
--font-main: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
|
||||||
|
"Segoe UI", sans-serif;
|
||||||
|
--font-mono: ui-monospace, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; margin:0; padding:0; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
font-family: var(--font-main);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top, #0b1120 0, #020617 45%, #000 100%);
|
||||||
|
color: var(--text-main);
|
||||||
|
padding: 32px 16px;
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TOP NAV / HEADER TAG */
|
||||||
|
.top-bar {
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
align-items:center;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
gap:12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-mark {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.22em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-mark span {
|
||||||
|
color: var(--accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-chip {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
background: rgba(15,23,42,0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HERO */
|
||||||
|
.hero {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-name {
|
||||||
|
font-size: clamp(26px, 4vw, 36px);
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-name span {
|
||||||
|
background: linear-gradient(120deg, var(--accent-1), var(--accent-2));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-tags {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
letter-spacing: 0.3em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-sub {
|
||||||
|
max-width: 620px;
|
||||||
|
margin: 0 auto 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-sub strong {
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-pills {
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
gap:8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 5px 11px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
background: rgba(15,23,42,0.9);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-pill {
|
||||||
|
display:inline-flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--accent-3);
|
||||||
|
box-shadow: 0 0 0 3px rgba(74,222,128,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-actions {
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
gap:10px;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
margin-bottom: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
text-decoration:none;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 9px 20px;
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-main);
|
||||||
|
border: 1px solid rgba(34,211,238,0.85);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top left, rgba(34,211,238,0.28), rgba(15,23,42,1));
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(34,211,238,0.4),
|
||||||
|
0 18px 40px rgba(8,47,73,0.9);
|
||||||
|
display:inline-flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:8px;
|
||||||
|
transition:
|
||||||
|
transform 160ms ease-out,
|
||||||
|
box-shadow 160ms ease-out,
|
||||||
|
background 160ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-1px) scale(1.02);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top left, rgba(34,211,238,0.4), rgba(15,23,42,1));
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(34,211,238,0.7),
|
||||||
|
0 24px 60px rgba(15,23,42,0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MAIN VISUAL */
|
||||||
|
.visual-wrap {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-card {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 28px;
|
||||||
|
border: 1px solid rgba(148,163,184,0.35);
|
||||||
|
overflow: hidden;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 0% 0%, rgba(34,211,238,0.16), transparent 55%),
|
||||||
|
radial-gradient(circle at 100% 100%, rgba(168,85,247,0.25), transparent 55%),
|
||||||
|
#020617;
|
||||||
|
box-shadow: 0 32px 90px rgba(15,23,42,0.95);
|
||||||
|
padding: 18px 18px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-label {
|
||||||
|
position:absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 20px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
z-index: 2;
|
||||||
|
display:flex;
|
||||||
|
gap:8px;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-label-dot {
|
||||||
|
width:6px;
|
||||||
|
height:6px;
|
||||||
|
border-radius:999px;
|
||||||
|
background: var(--accent-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-card svg {
|
||||||
|
width:100%;
|
||||||
|
height:auto;
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CONTACT SECTION */
|
||||||
|
.contact {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 18px;
|
||||||
|
border-top: 1px solid rgba(15,23,42,0.85);
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
align-items:flex-start;
|
||||||
|
gap:18px;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-title {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 0.24em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-soft);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-body {
|
||||||
|
color: var(--text-soft);
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links {
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links span {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links a {
|
||||||
|
color: var(--accent-1);
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links a:hover {
|
||||||
|
text-decoration:underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
gap:8px;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:640px) {
|
||||||
|
.top-bar { flex-direction:column; align-items:flex-start; }
|
||||||
|
.visual-card { border-radius:22px; padding:14px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell">
|
||||||
|
<!-- TOP STRIP -->
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="brand-mark">
|
||||||
|
<span>ZESHAN</span> · AZURE · KUBERNETES
|
||||||
|
</div>
|
||||||
|
<div class="nav-chip">
|
||||||
|
Single-page profile · CV first
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- HERO -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1 class="hero-name">
|
||||||
|
<span>Zeshan</span> Tariq
|
||||||
|
</h1>
|
||||||
|
<div class="hero-tags">
|
||||||
|
SOC · SRE · DEVOPS · DEVSECOPS · KUBERNETES
|
||||||
|
</div>
|
||||||
|
<p class="hero-sub">
|
||||||
|
Cloud & platform engineer focused on <strong>Azure</strong>, <strong>Kubernetes</strong>,
|
||||||
|
and <strong>secure automation</strong>. This page is the signal — the details live in my CV.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="hero-pills">
|
||||||
|
<div class="pill status-pill">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
Available for remote roles
|
||||||
|
</div>
|
||||||
|
<div class="pill">Azure · AKS · Terraform</div>
|
||||||
|
<div class="pill">CI/CD · GitHub · GitLab</div>
|
||||||
|
<div class="pill">SOC · Sentinel · SRE</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-actions">
|
||||||
|
<!-- change filename to your real CV -->
|
||||||
|
<a href="cv-zeshan-tariq.pdf" class="btn-primary" download>
|
||||||
|
<span>Download CV</span>
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path
|
||||||
|
d="M12 3v12m0 0 4-4m-4 4-4-4M5 18h14"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.6"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- BIG CODE / PLATFORM VISUAL -->
|
||||||
|
<section class="visual-wrap" aria-hidden="true">
|
||||||
|
<div class="visual-card">
|
||||||
|
<div class="visual-label">
|
||||||
|
<span class="visual-label-dot"></span>
|
||||||
|
azure-kubernetes-engineer.ts
|
||||||
|
</div>
|
||||||
|
<svg viewBox="0 0 960 420">
|
||||||
|
<!-- background halo -->
|
||||||
|
<defs>
|
||||||
|
<radialGradient id="halo" cx="50%" cy="0%" r="80%">
|
||||||
|
<stop offset="0%" stop-color="#22d3ee" stop-opacity="0.45"/>
|
||||||
|
<stop offset="40%" stop-color="#0f172a" stop-opacity="0.9"/>
|
||||||
|
<stop offset="100%" stop-color="#020617" stop-opacity="1"/>
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient id="frameStroke" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#22d3ee" stop-opacity="0.9"/>
|
||||||
|
<stop offset="50%" stop-color="#4ade80" stop-opacity="0.8"/>
|
||||||
|
<stop offset="100%" stop-color="#a855f7" stop-opacity="0.9"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- halo -->
|
||||||
|
<circle cx="480" cy="30" r="260" fill="url(#halo)" opacity="0.7"/>
|
||||||
|
|
||||||
|
<!-- central rounded frame -->
|
||||||
|
<rect x="80" y="80" width="800" height="260" rx="26"
|
||||||
|
fill="#020617" stroke="url(#frameStroke)" stroke-width="1.6"/>
|
||||||
|
|
||||||
|
<!-- subtle grid -->
|
||||||
|
<g stroke="#0b1120" stroke-width="0.8">
|
||||||
|
<line x1="120" y1="120" x2="840" y2="120"/>
|
||||||
|
<line x1="120" y1="150" x2="840" y2="150"/>
|
||||||
|
<line x1="120" y1="180" x2="840" y2="180"/>
|
||||||
|
<line x1="120" y1="210" x2="840" y2="210"/>
|
||||||
|
<line x1="120" y1="240" x2="840" y2="240"/>
|
||||||
|
<line x1="120" y1="270" x2="840" y2="270"/>
|
||||||
|
<line x1="120" y1="300" x2="840" y2="300"/>
|
||||||
|
|
||||||
|
<line x1="180" y1="110" x2="180" y2="320"/>
|
||||||
|
<line x1="260" y1="110" x2="260" y2="320"/>
|
||||||
|
<line x1="340" y1="110" x2="340" y2="320"/>
|
||||||
|
<line x1="500" y1="110" x2="500" y2="320"/>
|
||||||
|
<line x1="660" y1="110" x2="660" y2="320"/>
|
||||||
|
<line x1="780" y1="110" x2="780" y2="320"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- left code panel -->
|
||||||
|
<rect x="120" y="120" width="380" height="220" rx="18"
|
||||||
|
fill="#020617" stroke="#0f172a" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- code text (reduced to fit) -->
|
||||||
|
<g font-family="monospace" font-size="12">
|
||||||
|
<text x="140" y="148" fill="#4ade80">
|
||||||
|
import { AzureKubernetesServices } from '@azure/aks';
|
||||||
|
</text>
|
||||||
|
<text x="140" y="168" fill="#4ade80">
|
||||||
|
import { Engineer, Experience } from '@professional/core';
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<text x="140" y="194" fill="#4ade80">export</text>
|
||||||
|
<text x="192" y="194" fill="#4ade80">class</text>
|
||||||
|
<text x="244" y="194" fill="#22d3ee">AzureKubernetesEngineer</text>
|
||||||
|
<text x="140" y="214" fill="#e5e7eb">implements Engineer {</text>
|
||||||
|
|
||||||
|
<text x="156" y="236" fill="#e5e7eb">
|
||||||
|
constructor(private azure: AzureKubernetesServices) {}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<text x="156" y="260" fill="#4ade80">experience</text>
|
||||||
|
<text x="244" y="260" fill="#e5e7eb">: Experience = {</text>
|
||||||
|
<text x="176" y="280" fill="#4ade80">years</text>
|
||||||
|
<text x="224" y="280" fill="#e5e7eb">:</text>
|
||||||
|
<text x="236" y="280" fill="#22d3ee">8</text>
|
||||||
|
<text x="252" y="280" fill="#e5e7eb">,</text>
|
||||||
|
<text x="176" y="300" fill="#4ade80">specialties</text>
|
||||||
|
<text x="272" y="300" fill="#e5e7eb">:</text>
|
||||||
|
<text x="284" y="300" fill="#22d3ee">
|
||||||
|
['devops','sre','soc','devsecops']
|
||||||
|
</text>
|
||||||
|
<text x="156" y="320" fill="#e5e7eb">};</text>
|
||||||
|
|
||||||
|
<text x="156" y="344" fill="#22d3ee">deploySolution(solution: any) {</text>
|
||||||
|
<text x="176" y="364" fill="#4ade80">return</text>
|
||||||
|
<text x="232" y="364" fill="#22d3ee">
|
||||||
|
this.azure.deploy({ solution, orchestration: 'Kubernetes',
|
||||||
|
</text>
|
||||||
|
<text x="176" y="384" fill="#22d3ee">
|
||||||
|
cloud: 'Azure' });
|
||||||
|
</text>
|
||||||
|
<text x="156" y="404" fill="#e5e7eb">}</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- right "radar" metrics panel -->
|
||||||
|
<rect x="540" y="120" width="260" height="220" rx="18"
|
||||||
|
fill="#020617" stroke="#0f172a" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- concentric circles -->
|
||||||
|
<circle cx="670" cy="220" r="70" fill="#020617" stroke="#0f172a" stroke-width="1"/>
|
||||||
|
<circle cx="670" cy="220" r="52" fill="none" stroke="#0f172a" stroke-width="0.8"/>
|
||||||
|
<circle cx="670" cy="220" r="34" fill="none" stroke="#0f172a" stroke-width="0.8"/>
|
||||||
|
<circle cx="670" cy="220" r="18" fill="none" stroke="#0f172a" stroke-width="0.8"/>
|
||||||
|
|
||||||
|
<!-- sweep -->
|
||||||
|
<path d="M670 220 L720 210 A70 70 0 0 0 670 150z"
|
||||||
|
fill="rgba(34,211,238,0.55)"/>
|
||||||
|
<circle cx="670" cy="220" r="5" fill="#4ade80"/>
|
||||||
|
<circle cx="702" cy="194" r="4" fill="#22d3ee"/>
|
||||||
|
<circle cx="648" cy="196" r="4" fill="#a855f7"/>
|
||||||
|
<circle cx="694" cy="244" r="4" fill="#facc15"/>
|
||||||
|
|
||||||
|
<!-- labels -->
|
||||||
|
<g font-family="var(--font-mono)" font-size="11" fill="#9ca3af">
|
||||||
|
<text x="582" y="150">MTTR < 30m</text>
|
||||||
|
<text x="582" y="168">SLO 99.9%</text>
|
||||||
|
<text x="582" y="186">AKS clusters: 12</text>
|
||||||
|
<text x="582" y="204">Pipelines: 40+</text>
|
||||||
|
<text x="582" y="222">Alerts tuned: yes</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- CONTACT -->
|
||||||
|
<section class="contact" id="contact">
|
||||||
|
<div>
|
||||||
|
<div class="contact-title">Contact</div>
|
||||||
|
<div class="contact-body">
|
||||||
|
For remote DevOps, SRE, SOC or DevSecOps work, email me or visit my site.
|
||||||
|
The CV contains full experience, clients and stack details.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="contact-links">
|
||||||
|
<span>e: <a href="mailto:zeshan@azuredevops.co.uk">zeshan@azuredevops.co.uk</a></span>
|
||||||
|
<span>w: <a href="https://azuredevops.co.uk" target="_blank" rel="noopener">azuredevops.co.uk</a></span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<span>© <span id="year"></span> Zeshan Tariq</span>
|
||||||
|
<span>Dark single-page · SVG-driven hero</span>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("year").textContent = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
331
cv archive/index3.html
Normal file
331
cv archive/index3.html
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Zeshan Tariq – DevOps · SRE · SOC</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #020617;
|
||||||
|
--card: #020617;
|
||||||
|
--border: #1f2933;
|
||||||
|
--accent: #38bdf8;
|
||||||
|
--accent-soft: #0ea5e9;
|
||||||
|
--text-main: #e5e7eb;
|
||||||
|
--text-soft: #9ca3af;
|
||||||
|
--text-muted: #6b7280;
|
||||||
|
--font-main: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
|
||||||
|
--font-mono: ui-monospace, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-family: var(--font-main);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 780px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 22px;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagline {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
letter-spacing: 0.24em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagline strong {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
padding: 4px 10px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-text {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-text p {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
max-width: 520px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-text p strong {
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
padding: 8px 18px;
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #020617;
|
||||||
|
color: var(--text-main);
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: #0b1724;
|
||||||
|
border-color: var(--accent-soft);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-card {
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--card);
|
||||||
|
padding: 14px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-label {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-dot {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-svg {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact {
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding-top: 14px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links span {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.contact {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell">
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<div class="name">Zeshan Tariq</div>
|
||||||
|
<div class="tagline">
|
||||||
|
<strong>SOC</strong> / SRE / DEVOPS / DEVSECOPS / KUBERNETES
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
Available for remote roles
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-text">
|
||||||
|
<p>
|
||||||
|
Cloud & platform engineer focused on <strong>Azure</strong>,
|
||||||
|
<strong>Kubernetes</strong>, and <strong>secure automation</strong>.
|
||||||
|
For full details, see the CV.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<!-- Change filename to your real CV name -->
|
||||||
|
<a href="cv-zeshan-tariq.pdf" class="btn-primary" download>
|
||||||
|
<span>Download CV</span>
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path
|
||||||
|
d="M12 3v12m0 0 4-4m-4 4-4-4M5 18h14"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.6"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="code-card" aria-hidden="true">
|
||||||
|
<div class="code-label">
|
||||||
|
<span class="code-dot"></span>
|
||||||
|
azure-kubernetes-engineer.ts
|
||||||
|
</div>
|
||||||
|
<!-- Simple SVG "code box" using your snippet, reduced to fit -->
|
||||||
|
<svg class="code-svg" viewBox="0 0 640 200">
|
||||||
|
<rect x="0.5" y="0.5" width="639" height="199" rx="10" fill="#020617" stroke="#1f2933"/>
|
||||||
|
<!-- header bar -->
|
||||||
|
<rect x="10" y="10" width="620" height="22" rx="7" fill="#020617" />
|
||||||
|
<circle cx="24" cy="21" r="4" fill="#f97316"/>
|
||||||
|
<circle cx="38" cy="21" r="4" fill="#facc15"/>
|
||||||
|
<circle cx="52" cy="21" r="4" fill="#22c55e"/>
|
||||||
|
<text x="70" y="25" fill="#6b7280" font-family="monospace" font-size="11">
|
||||||
|
src/azure-kubernetes-engineer.ts
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- code text (shrunk version of your snippet) -->
|
||||||
|
<g font-family="monospace" font-size="11">
|
||||||
|
<text x="20" y="50" fill="#22c55e">
|
||||||
|
import { AzureKubernetesServices } from '@azure/kubernetes-engine';
|
||||||
|
</text>
|
||||||
|
<text x="20" y="68" fill="#22c55e">
|
||||||
|
import { Engineer, Experience } from '@professional/core';
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<text x="20" y="92" fill="#22c55e">export</text>
|
||||||
|
<text x="70" y="92" fill="#22c55e">class</text>
|
||||||
|
<text x="130" y="92" fill="#38bdf8">AzureKubernetesEngineer</text>
|
||||||
|
<text x="325" y="92" fill="#22c55e">implements</text>
|
||||||
|
<text x="410" y="92" fill="#e5e7eb">Engineer {</text>
|
||||||
|
|
||||||
|
<text x="36" y="112" fill="#e5e7eb">
|
||||||
|
constructor(private azure: AzureKubernetesServices) {}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<text x="36" y="132" fill="#22c55e">experience</text>
|
||||||
|
<text x="120" y="132" fill="#e5e7eb">: Experience = { years: 8,</text>
|
||||||
|
<text x="36" y="150" fill="#22c55e">specialties</text>
|
||||||
|
<text x="130" y="150" fill="#e5e7eb">
|
||||||
|
: ['application dev','config mgmt','cloud']
|
||||||
|
</text>
|
||||||
|
<text x="36" y="168" fill="#e5e7eb">};</text>
|
||||||
|
|
||||||
|
<text x="36" y="188" fill="#38bdf8">
|
||||||
|
deploySolution(solution: any) { return this.azure.deploy({ solution }); }
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="contact" id="contact">
|
||||||
|
<div>
|
||||||
|
<div style="font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--text-soft); margin-bottom: 4px;">
|
||||||
|
Contact
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
For remote DevOps, SRE, SOC or DevSecOps work, email me or visit my site.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="contact-links">
|
||||||
|
<span>e: <a href="mailto:zeshan@azuredevops.co.uk">zeshan@azuredevops.co.uk</a></span>
|
||||||
|
<span>w: <a href="https://azuredevops.co.uk" target="_blank" rel="noopener">azuredevops.co.uk</a></span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
© <span id="year"></span> Zeshan Tariq · Minimal dark profile
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("year").textContent = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
281
cv archive/index4.html
Normal file
281
cv archive/index4.html
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>ZT – DevOps · SRE · SOC</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
/* ChatGPT-like calm neutral palette */
|
||||||
|
--bg: #141618;
|
||||||
|
--bg-alt: #1a1c1e;
|
||||||
|
--card: #1b1d1f;
|
||||||
|
--border: #2a2c2e;
|
||||||
|
--accent: #10b981; /* green like ChatGPT’s “accept” button */
|
||||||
|
--accent-soft: #34d399;
|
||||||
|
--text-main: #e5e7eb;
|
||||||
|
--text-soft: #bfc4c9;
|
||||||
|
--text-muted: #8b8f94;
|
||||||
|
--font-main: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-mono: ui-monospace, Menlo, Consolas, "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { margin:0; padding:0; box-sizing:border-box; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height:100vh;
|
||||||
|
background: radial-gradient(circle at top, #1c1e20 0%, #141618 70%);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-family: var(--font-main);
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
padding:28px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell { width:100%; max-width:780px; }
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin-bottom: 26px;
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
gap:10px;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size:22px;
|
||||||
|
letter-spacing:0.18em;
|
||||||
|
text-transform:uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagline {
|
||||||
|
font-size:11px;
|
||||||
|
letter-spacing:0.24em;
|
||||||
|
text-transform:uppercase;
|
||||||
|
color: var(--text-soft);
|
||||||
|
margin-top:4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagline strong { color:var(--accent); }
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size:11px;
|
||||||
|
color:var(--text-soft);
|
||||||
|
border-radius:999px;
|
||||||
|
border:1px solid var(--border);
|
||||||
|
padding:5px 12px;
|
||||||
|
display:inline-flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:6px;
|
||||||
|
white-space:nowrap;
|
||||||
|
background:#1a1c1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width:8px; height:8px;
|
||||||
|
border-radius:50%;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero { margin-bottom:24px; }
|
||||||
|
|
||||||
|
.hero-text p {
|
||||||
|
font-size:13px;
|
||||||
|
color:var(--text-soft);
|
||||||
|
max-width:520px;
|
||||||
|
margin-bottom:16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-text p strong { color:var(--text-main); }
|
||||||
|
|
||||||
|
.actions { display:flex; gap:10px; flex-wrap:wrap; }
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
border-radius:999px;
|
||||||
|
padding:8px 18px;
|
||||||
|
font-size:11px;
|
||||||
|
letter-spacing:0.14em;
|
||||||
|
text-transform:uppercase;
|
||||||
|
font-weight:600;
|
||||||
|
border:1px solid var(--accent);
|
||||||
|
background:#121415;
|
||||||
|
color:var(--text-main);
|
||||||
|
text-decoration:none;
|
||||||
|
display:inline-flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:8px;
|
||||||
|
transition:0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background:#161819;
|
||||||
|
border-color:var(--accent-soft);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary svg { width:14px; height:14px; }
|
||||||
|
|
||||||
|
.code-card {
|
||||||
|
border-radius:16px;
|
||||||
|
border:1px solid var(--border);
|
||||||
|
background: var(--card);
|
||||||
|
padding:14px;
|
||||||
|
margin-bottom:24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-label {
|
||||||
|
font-family:var(--font-mono);
|
||||||
|
font-size:11px;
|
||||||
|
color:var(--text-muted);
|
||||||
|
margin-bottom:8px;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-dot {
|
||||||
|
width:7px; height:7px;
|
||||||
|
border-radius:50%;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-svg { width:100%; display:block; }
|
||||||
|
|
||||||
|
.contact {
|
||||||
|
border-top:1px solid var(--border);
|
||||||
|
padding-top:14px;
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
gap:12px;
|
||||||
|
font-size:13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links { display:flex; flex-direction:column; gap:4px; }
|
||||||
|
|
||||||
|
.contact-links span { color:var(--text-muted); }
|
||||||
|
|
||||||
|
a { color:var(--accent); }
|
||||||
|
a:hover { text-decoration:underline; }
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top:10px;
|
||||||
|
font-size:11px;
|
||||||
|
color:var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width:640px){
|
||||||
|
header { flex-direction:column; align-items:flex-start; }
|
||||||
|
.contact { flex-direction:column; align-items:flex-start; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<div class="name">Zeshan Tariq</div>
|
||||||
|
<div class="tagline">
|
||||||
|
<strong>SOC</strong> / SRE / DEVOPS / DEVSECOPS / KUBERNETES
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
Available for remote roles · contract & permanent
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-text">
|
||||||
|
<p>
|
||||||
|
Cloud & platform engineer focused on <strong>Azure</strong>,
|
||||||
|
<strong>Kubernetes</strong>, and <strong>secure automation</strong>.
|
||||||
|
For full details, see the CV.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<a href="cv-zeshan-tariq.pdf" class="btn-primary" download>
|
||||||
|
Download CV
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M12 3v12m0 0 4-4m-4 4-4-4M5 18h14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="code-card">
|
||||||
|
<div class="code-label">
|
||||||
|
<span class="code-dot"></span> azure-kubernetes-engineer.ts
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<svg class="code-svg" viewBox="0 0 640 200">
|
||||||
|
<rect x="0.5" y="0.5" width="639" height="199" rx="10" fill="#1b1d1f" stroke="#2a2c2e"/>
|
||||||
|
<rect x="10" y="10" width="620" height="22" rx="7" fill="#1b1d1f"/>
|
||||||
|
<circle cx="24" cy="21" r="4" fill="#ef4444"/>
|
||||||
|
<circle cx="38" cy="21" r="4" fill="#f59e0b"/>
|
||||||
|
<circle cx="52" cy="21" r="4" fill="#10b981"/>
|
||||||
|
<text x="70" y="25" fill="#777" font-family="monospace" font-size="11">
|
||||||
|
src/azure-kubernetes-engineer.ts
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<g font-family="monospace" font-size="11">
|
||||||
|
<text x="20" y="50" fill="#10b981">
|
||||||
|
import { AzureKubernetesServices } from '@azure/kubernetes-engine';
|
||||||
|
</text>
|
||||||
|
<text x="20" y="68" fill="#10b981">
|
||||||
|
import { Engineer, Experience } from '@professional/core';
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<text x="20" y="92" fill="#10b981">export</text>
|
||||||
|
<text x="70" y="92" fill="#10b981">class</text>
|
||||||
|
<text x="130" y="92" fill="#38bdf8">AzureKubernetesEngineer</text>
|
||||||
|
<text x="325" y="92" fill="#10b981">implements</text>
|
||||||
|
<text x="410" y="92" fill="#e5e7eb">Engineer {</text>
|
||||||
|
|
||||||
|
<text x="36" y="112" fill="#e5e7eb">
|
||||||
|
constructor(private azure: AzureKubernetesServices) {}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<text x="36" y="132" fill="#10b981">experience</text>
|
||||||
|
<text x="120" y="132" fill="#e5e7eb">: Experience = { years: 8,</text>
|
||||||
|
<text x="36" y="150" fill="#10b981">specialties</text>
|
||||||
|
<text x="130" y="150" fill="#e5e7eb">
|
||||||
|
: ['application dev','config mgmt','cloud']
|
||||||
|
</text>
|
||||||
|
<text x="36" y="168" fill="#e5e7eb">};</text>
|
||||||
|
|
||||||
|
<text x="36" y="188" fill="#38bdf8">
|
||||||
|
deploySolution(solution: any) { return this.azure.deploy({ solution }); }
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="contact">
|
||||||
|
<div>
|
||||||
|
<div style="font-family:var(--font-mono); font-size:12px; letter-spacing:0.22em; text-transform:uppercase; color:var(--text-soft); margin-bottom:4px;">
|
||||||
|
Contact
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For remote DevOps, SRE, SOC or DevSecOps work, email me or visit my site.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="contact-links">
|
||||||
|
<span>e: <a href="mailto:zeshan@azuredevops.co.uk">zeshan@azuredevops.co.uk</a></span>
|
||||||
|
<span>w: <a href="https://azuredevops.co.uk" target="_blank" rel="noopener">azuredevops.co.uk</a></span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
© <span id="year"></span> Zeshan Tariq
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("year").textContent = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
491
cv archive/index5.html
Normal file
491
cv archive/index5.html
Normal file
@@ -0,0 +1,491 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Zeshan Tariq – DevOps · SRE · SOC</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #141618;
|
||||||
|
--bg-alt: #1a1c1e;
|
||||||
|
--bg-chat: #181a1c;
|
||||||
|
--bubble-assistant: #202326;
|
||||||
|
--bubble-user: #191b1e;
|
||||||
|
--bubble-system: #111315;
|
||||||
|
--border-subtle: #2a2c2e;
|
||||||
|
--accent: #10b981;
|
||||||
|
--accent-soft: #34d399;
|
||||||
|
--text-main: #e5e7eb;
|
||||||
|
--text-soft: #c2c6cb;
|
||||||
|
--text-muted: #8b8f94;
|
||||||
|
--font-main: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-mono: ui-monospace, Menlo, Consolas, "Courier New", monospace;
|
||||||
|
--radius-bubble: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top, #1d1f21 0%, #141618 60%, #111315 100%);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-family: var(--font-main);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header / top bar */
|
||||||
|
.top-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-title {
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-title span {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-status {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
background: #151719;
|
||||||
|
color: var(--text-soft);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat container */
|
||||||
|
.chat-shell {
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--bg-chat);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
padding: 16px 0 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-inner {
|
||||||
|
padding: 0 12px 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.system { justify-content: center; }
|
||||||
|
|
||||||
|
.row.user {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.user .bubble {
|
||||||
|
background: var(--bubble-user);
|
||||||
|
border-radius: var(--radius-bubble) var(--radius-bubble) 4px var(--radius-bubble);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.assistant {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.assistant .bubble {
|
||||||
|
background: var(--bubble-assistant);
|
||||||
|
border-radius: var(--radius-bubble) var(--radius-bubble) var(--radius-bubble) 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.system .bubble {
|
||||||
|
background: var(--bubble-system);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding-inline: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 999px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.assistant {
|
||||||
|
background: #101112;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.user {
|
||||||
|
background: #0f766e;
|
||||||
|
color: #ecfdf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
padding: 10px 12px;
|
||||||
|
max-width: 100%;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
border: 1px solid rgba(42, 44, 46, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble strong {
|
||||||
|
color: var(--text-main);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble h1 {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble small {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CV button inside bubble */
|
||||||
|
.btn-primary {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 7px 16px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
background: #111315;
|
||||||
|
color: var(--text-main);
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: #151719;
|
||||||
|
border-color: var(--accent-soft);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code bubble */
|
||||||
|
pre {
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #111315;
|
||||||
|
border: 1px solid #242628;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
overflow-x: auto;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Contact list inside bubble */
|
||||||
|
.contact-list {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-list div {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent-soft);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.top-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="app">
|
||||||
|
<!-- Top bar -->
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="top-title">
|
||||||
|
<span>Zeshan Tariq</span> · DevOps · SRE · SOC
|
||||||
|
</div>
|
||||||
|
<div class="top-status">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
Available for remote roles · contract & permanent
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat UI -->
|
||||||
|
<div class="chat-shell">
|
||||||
|
<div class="chat-inner">
|
||||||
|
<!-- System-ish intro -->
|
||||||
|
<div class="row system">
|
||||||
|
<div class="bubble">
|
||||||
|
Profile loaded: <strong>SOC / SRE / DevOps / DevSecOps / Kubernetes</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User asks who you are -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Who are you and what do you do?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assistant: intro -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
<h1>Zeshan Tariq</h1>
|
||||||
|
<small>SOC · SRE · DevOps · DevSecOps · Kubernetes</small>
|
||||||
|
<p style="margin-top:6px;">
|
||||||
|
I’m a cloud & platform engineer focused on <strong>Azure</strong>,
|
||||||
|
<strong>Kubernetes</strong>, and <strong>secure automation</strong> — combining
|
||||||
|
<strong>SOC practices</strong> with <strong>SRE principles</strong> to keep systems
|
||||||
|
reliable and secure.
|
||||||
|
</p>
|
||||||
|
<div class="chip-row">
|
||||||
|
<div class="chip">Azure · AKS</div>
|
||||||
|
<div class="chip">Kubernetes · Docker</div>
|
||||||
|
<div class="chip">Terraform · CI/CD</div>
|
||||||
|
<div class="chip">Sentinel · SOC</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User asks for CV -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Can I see the full details of your experience?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assistant: CV download -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
Absolutely. The CV carries all the detail (roles, dates, full stack, and examples).
|
||||||
|
<br />
|
||||||
|
<a href="cv-zeshan-tariq.pdf" class="btn-primary" download>
|
||||||
|
Download CV
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M12 3v12m0 0 4-4m-4 4-4-4M5 18h14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User asks what you actually do -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
In a sentence or two, what kind of work do you do?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assistant: short skills summary -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
I design, build and operate <strong>Azure</strong> and <strong>Kubernetes</strong> platforms,
|
||||||
|
automate infrastructure with <strong>Terraform/Bicep</strong>, run
|
||||||
|
<strong>CI/CD pipelines</strong>, and use <strong>SOC tooling</strong>
|
||||||
|
and <strong>SRE metrics</strong> to keep services healthy.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User asks for code sample -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Show me a small code sample that represents how you think.
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assistant: your TypeScript snippet in a code bubble -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
Here’s a condensed TypeScript-style example that reflects what I do with Azure, AKS and
|
||||||
|
translating requirements into platform configuration:
|
||||||
|
<pre><code>import { AzureKubernetesServices } from '@azure/kubernetes-engine';
|
||||||
|
import { Engineer, Experience } from '@professional/core';
|
||||||
|
|
||||||
|
export class AzureKubernetesEngineer implements Engineer {
|
||||||
|
constructor(private azureServices: AzureKubernetesServices) {}
|
||||||
|
|
||||||
|
experience: Experience = {
|
||||||
|
years: 8,
|
||||||
|
sectors: ['government', 'consultancy', 'finance'],
|
||||||
|
specialties: [
|
||||||
|
'application development',
|
||||||
|
'configuration management',
|
||||||
|
'public cloud deployment',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
deploySolution(solution: any) {
|
||||||
|
return this.azureServices.deploy({
|
||||||
|
solution,
|
||||||
|
orchestration: 'Kubernetes',
|
||||||
|
cloud: 'Azure',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeBusinessAndTechnology(requirements: any) {
|
||||||
|
return this.azureServices.configure(requirements);
|
||||||
|
}
|
||||||
|
}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User asks how to contact -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
How do I contact you if I want to talk about a role?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assistant: contact bubble -->
|
||||||
|
<div class="row assistant" id="contact">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
The easiest way is email, but you can also find more context on my site.
|
||||||
|
<div class="contact-list">
|
||||||
|
<div>e: <a href="mailto:zeshan@azuredevops.co.uk">zeshan@azuredevops.co.uk</a></div>
|
||||||
|
<div>w: <a href="https://azuredevops.co.uk" target="_blank" rel="noopener">azuredevops.co.uk</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<span>© <span id="year"></span> Zeshan Tariq</span>
|
||||||
|
<span>Minimal chat-style profile · CV-first</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("year").textContent = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2
cv/Dockerfile
Normal file
2
cv/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
COPY . /usr/share/nginx/html/
|
||||||
11
cv/compose.yml
Normal file
11
cv/compose.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
cvsite:
|
||||||
|
image: r.h-y.st/cv:latest
|
||||||
|
container_name: zeshan
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- hurricane
|
||||||
|
|
||||||
|
networks:
|
||||||
|
hurricane:
|
||||||
|
external: true
|
||||||
564
cv/index.html
Normal file
564
cv/index.html
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Zeshan Tariq – Azure DevOps · SRE · SOC · Kubernetes Engineer</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<!-- META DESCRIPTION -->
|
||||||
|
<meta name="description" content="Zeshan Tariq – Azure DevOps, SRE, SOC and DevSecOps engineer specialising in Azure, Kubernetes (AKS), Terraform, CI/CD and secure cloud automation. Available for remote contract & permanent roles.">
|
||||||
|
|
||||||
|
<!-- KEYWORDS -->
|
||||||
|
<meta name="keywords" content="Azure DevOps Engineer, SRE, SOC Engineer, DevSecOps, Kubernetes Engineer, AKS, Terraform, Cloud Engineer, Remote DevOps, Platform Engineer">
|
||||||
|
|
||||||
|
<!-- CANONICAL URL -->
|
||||||
|
<link rel="canonical" href="https://azuredevops.co.uk" />
|
||||||
|
|
||||||
|
<!-- OPEN GRAPH (SOCIAL SHARING) -->
|
||||||
|
<meta property="og:title" content="Zeshan Tariq – Azure DevOps & SRE Engineer">
|
||||||
|
<meta property="og:description" content="Cloud & platform engineer specialising in Azure, Kubernetes, DevOps, SOC and secure automation. Available for remote contract & permanent roles.">
|
||||||
|
<meta property="og:url" content="https://azuredevops.co.uk">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<!-- Optional: update this to your real preview image -->
|
||||||
|
<meta property="og:image" content="https://azuredevops.co.uk/preview.jpg">
|
||||||
|
|
||||||
|
<!-- TWITTER CARD -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="Zeshan Tariq – DevOps · SRE · SOC">
|
||||||
|
<meta name="twitter:description" content="Azure DevOps, SRE, SOC and DevSecOps engineer. Kubernetes, AKS, CI/CD, Terraform — available for remote roles.">
|
||||||
|
<meta name="twitter:image" content="https://azuredevops.co.uk/preview.jpg">
|
||||||
|
|
||||||
|
<!-- STRUCTURED DATA: PERSON -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Zeshan Tariq",
|
||||||
|
"jobTitle": "Azure DevOps / SRE / SOC Engineer",
|
||||||
|
"url": "https://azuredevops.co.uk",
|
||||||
|
"email": "mailto:zeshan@azuredevops.co.uk",
|
||||||
|
"description": "Cloud & platform engineer specialising in Azure, Kubernetes (AKS), Terraform, CI/CD and secure cloud automation.",
|
||||||
|
"knowsAbout": [
|
||||||
|
"Azure",
|
||||||
|
"Kubernetes",
|
||||||
|
"AKS",
|
||||||
|
"DevOps",
|
||||||
|
"SRE",
|
||||||
|
"SOC",
|
||||||
|
"DevSecOps",
|
||||||
|
"Terraform",
|
||||||
|
"CI/CD Pipelines",
|
||||||
|
"Cloud Security"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #141618;
|
||||||
|
--bg-chat: #181a1c;
|
||||||
|
--bubble-assistant: #202326;
|
||||||
|
--bubble-user: #191b1e;
|
||||||
|
--bubble-system: #111315;
|
||||||
|
--border-subtle: #2a2c2e;
|
||||||
|
--accent: #10b981; /* green */
|
||||||
|
--accent-soft: #34d399;
|
||||||
|
--text-main: #e5e7eb;
|
||||||
|
--text-soft: #c2c6cb;
|
||||||
|
--text-muted: #8b8f94;
|
||||||
|
--font-main: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-mono: ui-monospace, Menlo, Consolas, "Courier New", monospace;
|
||||||
|
--radius-bubble: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top, #1d1f21 0%, #141618 60%, #111315 100%);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-family: var(--font-main);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* soft bottom vignette */
|
||||||
|
body::after {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 120px;
|
||||||
|
background: linear-gradient(to top, rgba(0,0,0,0.55), transparent);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top bar */
|
||||||
|
.top-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-title {
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-title span {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-status {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
background: #151719;
|
||||||
|
color: var(--text-soft);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat container */
|
||||||
|
.chat-shell {
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--bg-chat);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
padding: 14px 0 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-inner {
|
||||||
|
padding: 0 12px 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.system {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.user {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.assistant {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 999px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.assistant {
|
||||||
|
background: #101112;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.user {
|
||||||
|
background: #0f766e;
|
||||||
|
color: #ecfdf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
padding: 10px 12px;
|
||||||
|
max-width: 100%;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
border: 1px solid rgba(42, 44, 46, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.system .bubble {
|
||||||
|
background: var(--bubble-system);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding-inline: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.user .bubble {
|
||||||
|
background: var(--bubble-user);
|
||||||
|
border-radius: var(--radius-bubble) var(--radius-bubble) 4px var(--radius-bubble);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.assistant .bubble {
|
||||||
|
background: var(--bubble-assistant);
|
||||||
|
border-radius: var(--radius-bubble) var(--radius-bubble) var(--radius-bubble) 4px;
|
||||||
|
border-left: 3px solid var(--accent); /* ChatGPT-style accent bar */
|
||||||
|
padding-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble h1 {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble small {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble strong {
|
||||||
|
color: var(--text-main);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CV button */
|
||||||
|
.btn-primary {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 7px 16px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
background: #111315;
|
||||||
|
color: var(--text-main);
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: #151719;
|
||||||
|
border-color: var(--accent-soft);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code bubble */
|
||||||
|
pre {
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #111315;
|
||||||
|
border: 1px solid #242628;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
overflow-x: auto;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-list {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-list div {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent-soft);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.top-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Hidden H1 for SEO (not visible, but indexable) -->
|
||||||
|
<h1 style="position:absolute; left:-9999px; top:-9999px;">
|
||||||
|
Zeshan Tariq – Azure DevOps, SRE, SOC & Kubernetes Engineer
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="app">
|
||||||
|
<!-- Top bar -->
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="top-title">
|
||||||
|
<span>Zeshan Tariq</span> · DevOps · SRE · SOC
|
||||||
|
</div>
|
||||||
|
<div class="top-status">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
Available for remote contract & permanent roles
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat UI -->
|
||||||
|
<div class="chat-shell" aria-label="Profile overview of Zeshan Tariq, Azure DevOps, SRE, SOC and Kubernetes Engineer">
|
||||||
|
<div class="chat-inner">
|
||||||
|
<!-- System / intro -->
|
||||||
|
<div class="row system">
|
||||||
|
<div class="bubble">
|
||||||
|
Profile: <strong>SOC / SRE / DevOps / DevSecOps / Kubernetes</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: who are you -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Who are you and what do you do?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: intro + quick summary -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
<h1>Zeshan Tariq</h1>
|
||||||
|
<small>SOC · SRE · DevOps · DevSecOps · Kubernetes</small>
|
||||||
|
<p style="margin-top:6px;">
|
||||||
|
I’m a cloud & platform engineer focused on <strong>Azure</strong>,
|
||||||
|
<strong>Kubernetes</strong>, and <strong>secure automation</strong> —
|
||||||
|
combining <strong>SOC practices</strong> with <strong>SRE principles</strong>
|
||||||
|
to keep systems reliable and secure.
|
||||||
|
</p>
|
||||||
|
<div class="chip-row">
|
||||||
|
<div class="chip">Azure · AKS</div>
|
||||||
|
<div class="chip">Kubernetes · Docker</div>
|
||||||
|
<div class="chip">Terraform · CI/CD</div>
|
||||||
|
<div class="chip">Sentinel · SOC</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: CV -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Where can I see your full experience and roles?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: CV download -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
The CV has all the detail — roles, dates, clients, and full stack.
|
||||||
|
<br />
|
||||||
|
<!-- replace filename with your real CV if needed -->
|
||||||
|
<a href="zt1125.docx" class="btn-primary" download>
|
||||||
|
Download CV
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M12 3v12m0 0 4-4m-4 4-4-4M5 18h14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: what kind of work -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
In practical terms, what kind of work do you do?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: concise work summary -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
I design and operate <strong>Azure & AKS platforms</strong>, automate
|
||||||
|
infrastructure with <strong>Terraform/Bicep</strong>, build
|
||||||
|
<strong>CI/CD pipelines</strong>, and use <strong>SOC tooling</strong>
|
||||||
|
and <strong>SRE metrics</strong> to keep services secure and reliable.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: code sample -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Show me a small code sample that represents how you think.
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: TypeScript snippet -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
A condensed TypeScript-style example that reflects what I do with Azure, AKS,
|
||||||
|
and translating requirements into platform configuration:
|
||||||
|
<pre><code>import { AzureKubernetesServices } from '@azure/kubernetes-engine';
|
||||||
|
import { Engineer, Experience } from '@professional/core';
|
||||||
|
|
||||||
|
export class AzureKubernetesEngineer implements Engineer {
|
||||||
|
constructor(private azureServices: AzureKubernetesServices) {}
|
||||||
|
|
||||||
|
experience: Experience = {
|
||||||
|
years: 8,
|
||||||
|
sectors: ['government', 'consultancy', 'finance'],
|
||||||
|
specialties: [
|
||||||
|
'application development',
|
||||||
|
'configuration management',
|
||||||
|
'public cloud deployment',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
deploySolution(solution: any) {
|
||||||
|
return this.azureServices.deploy({
|
||||||
|
solution,
|
||||||
|
orchestration: 'Kubernetes',
|
||||||
|
cloud: 'Azure',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeBusinessAndTechnology(requirements: any) {
|
||||||
|
return this.azureServices.configure(requirements);
|
||||||
|
}
|
||||||
|
}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: contact -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
How do I contact you about a role?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: contact info -->
|
||||||
|
<div class="row assistant" id="contact">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
The simplest way is email — the site and CV give extra context if you need it.
|
||||||
|
<div class="contact-list">
|
||||||
|
<div>e: <a href="mailto:zeshan@azuredevops.co.uk">zeshan@azuredevops.co.uk</a></div>
|
||||||
|
<div>w: <a href="https://azuredevops.co.uk" target="_blank" rel="noopener">azuredevops.co.uk</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<span>© <span id="year"></span> Zeshan Tariq</span>
|
||||||
|
<span>Chat-style profile · CV-first</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("year").textContent = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
cv/zt1125.docx
Normal file
BIN
cv/zt1125.docx
Normal file
Binary file not shown.
68
hurricane/.env
Normal file
68
hurricane/.env
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# General settings
|
||||||
|
WG_HOST=39.33779.xyz
|
||||||
|
PASSWORD=c0bba9d2-7207-4c18-8133-9c3b01c7514c
|
||||||
|
TZ=Europe/London
|
||||||
|
WEBPASSWORD=c0bba9d2-7207-4c18-8133-9c3b01c7514c
|
||||||
|
PIHOLE=C759tw1j
|
||||||
|
|
||||||
|
# Nginx Proxy Manager settings
|
||||||
|
NGINX_HTTP_PORT=80
|
||||||
|
NGINX_HTTPS_PORT=443
|
||||||
|
NGINX_ADMIN_PORT=81
|
||||||
|
GITLAB_ROOT_PASSWORD=Shan33779488@!
|
||||||
|
POSTGRES_PASSWORD=Shan33779488@!
|
||||||
|
SMTP_PASSWORD=Shan33779488@!
|
||||||
|
# WireGuard settings
|
||||||
|
WG_UDP_PORT=51820
|
||||||
|
WG_TCP_PORT=51821
|
||||||
|
|
||||||
|
# Homepage service settings
|
||||||
|
HOMEPAGE_PORT=3000
|
||||||
|
|
||||||
|
# gitlab
|
||||||
|
GITLAB_EXTERNAL_URL=https://41.33779.xyz
|
||||||
|
DB_NAME=gitlabhq_production
|
||||||
|
GITLAB_ROOT=KGdB3tMltcRMl/JghwO/jYM5xbomWSziDjcW2pVh/H0=
|
||||||
|
DB_USER=gitlab
|
||||||
|
DB_PASS=a656c343-5deb-4355-80c2-c6ad7e5bbf93
|
||||||
|
POSTGRES_VERSION=latest
|
||||||
|
# gitlab Email configuration
|
||||||
|
EMAIL_ENABLED=true
|
||||||
|
SMTP_HOST=mail.zeshan.uk
|
||||||
|
SMTP_PORT=465
|
||||||
|
SMTP_USER=tariq@zeshan.uk
|
||||||
|
SMTP_PASS=Shan33779488@!
|
||||||
|
SMTP_DOMAIN=zeshan.uk
|
||||||
|
SMTP_AUTHENTICATION=login
|
||||||
|
SMTP_ENABLE_STARTTLS_AUTO=true
|
||||||
|
SMTP_TLS=true
|
||||||
|
SMTP_OPENSSL_VERIFY_MODE=none
|
||||||
|
GITLAB_EMAIL_FROM=tariq@zeshan.uk
|
||||||
|
GITLAB_EMAIL_REPLY_TO=noreply@zeshan.uk
|
||||||
|
HOSTBRR=^;B*3Al+w.5{
|
||||||
|
SMTP_ADDRESS=smtp.example.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER_NAME=smtp_user@example.com
|
||||||
|
SMTP_PASSWORD=smtp_password
|
||||||
|
SMTP_DOMAIN=example.com
|
||||||
|
|
||||||
|
# MariaDB settings for Nextcloud
|
||||||
|
MYSQL_ROOT_PASSWORD=ce5ef083-99c2-4a8d-bbaa-47728ea144b1
|
||||||
|
MYSQL_PASSWORD=d4808430-7aab-4637-b883-3a6c757fde0f
|
||||||
|
MYSQL_DATABASE=nextcloud
|
||||||
|
MYSQL_USER=nextcloud
|
||||||
|
ex
|
||||||
|
# Nextcloud settings
|
||||||
|
TRUSTED_PROXIES=ncloud.zeshan.uk
|
||||||
|
OVERWRITECLIURL=https://ncloud.zeshan.uk
|
||||||
|
OVERWRITEPROTOCOL=https
|
||||||
|
NEXTCLOUD_TRUSTED_DOMAINS=ncloud.zeshan.uk
|
||||||
|
OVERWRITEHOST=ncloud.zeshan.uk
|
||||||
|
|
||||||
|
# code-server
|
||||||
|
CODE_SERVER_PASSWORD=b681e77c-9ac8-480c-a1dd-1f5c5542f4fd
|
||||||
|
CODE_SERVER_SUDO_PASSWORD=b681e77c-9ac8-480c-a1dd-1f5c5542f4fd
|
||||||
|
PROXY_DOMAIN=code.zeshan.uk
|
||||||
|
# Adjust the workspace path as needed
|
||||||
|
WORKSPACE_PATH=/mnt/data/code-server/workspace
|
||||||
|
CODE_SERVER_CONFIF_PATH=/mnt/data/code-server/config
|
||||||
38
hurricane/Makefile
Normal file
38
hurricane/Makefile
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Makefile for restic backups
|
||||||
|
|
||||||
|
# Hard-coded settings
|
||||||
|
RESTIC_REPOSITORY := /mnt/data/OneDrive/backup/us
|
||||||
|
RESTIC_PASSWORD := Shan33779488
|
||||||
|
RESTIC_BIN := /usr/bin/restic
|
||||||
|
|
||||||
|
# Sources to back up
|
||||||
|
SOURCES := /root/docker/ /var/lib/docker/volumes/
|
||||||
|
|
||||||
|
# Common flags
|
||||||
|
BACKUP_FLAGS := --verbose
|
||||||
|
CHECK_FLAGS := --read-data-subset=10%
|
||||||
|
FORGET_FLAGS := --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --prune
|
||||||
|
|
||||||
|
.PHONY: backup check forget-prune init print-env cron-install cron-remove
|
||||||
|
|
||||||
|
print-env:
|
||||||
|
@echo "Repository: $(RESTIC_REPOSITORY)"; echo "Sources: $(SOURCES)"; echo "Restic: $(RESTIC_BIN)"; echo "Backup flags: $(BACKUP_FLAGS)" # [web:70]
|
||||||
|
|
||||||
|
init:
|
||||||
|
RESTIC_REPOSITORY=$(RESTIC_REPOSITORY) RESTIC_PASSWORD=$(RESTIC_PASSWORD) $(RESTIC_BIN) init || true # [web:57]
|
||||||
|
|
||||||
|
backup:
|
||||||
|
RESTIC_REPOSITORY=$(RESTIC_REPOSITORY) RESTIC_PASSWORD=$(RESTIC_PASSWORD) $(RESTIC_BIN) backup $(SOURCES) $(BACKUP_FLAGS) # [web:57]
|
||||||
|
|
||||||
|
check:
|
||||||
|
RESTIC_REPOSITORY=$(RESTIC_REPOSITORY) RESTIC_PASSWORD=$(RESTIC_PASSWORD) $(RESTIC_BIN) check $(CHECK_FLAGS) # [web:57]
|
||||||
|
|
||||||
|
forget-prune:
|
||||||
|
RESTIC_REPOSITORY=$(RESTIC_REPOSITORY) RESTIC_PASSWORD=$(RESTIC_PASSWORD) $(RESTIC_BIN) forget $(FORGET_FLAGS) # [web:57]
|
||||||
|
|
||||||
|
# Install a root crontab entry to run backup every 6 hours at minute 0, with logging
|
||||||
|
cron-install:
|
||||||
|
@(crontab -l 2>/dev/null; echo '0 */6 * * * RESTIC_REPOSITORY=$(RESTIC_REPOSITORY) RESTIC_PASSWORD=$(RESTIC_PASSWORD) $(RESTIC_BIN) backup $(SOURCES) $(BACKUP_FLAGS) >> /var/log/restic-backup.log 2>&1') | crontab - # [web:40]
|
||||||
|
|
||||||
|
cron-remove:
|
||||||
|
@crontab -l | grep -v '$(RESTIC_BIN) backup' | crontab - || true # [web:40]
|
||||||
61
hurricane/ac_runner_mac.sh
Normal file
61
hurricane/ac_runner_mac.sh
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# === CONFIG ===
|
||||||
|
GITEA_URL="${GITEA_URL:-https://git.azuredevops.co.uk}" # or export GITEA_URL=...
|
||||||
|
REG_TOKEN="tQ6K7y2adPYg7Nh1gt0pWrYbBOxkPPcHjfcvfqHE" # must export REG_TOKEN
|
||||||
|
HOSTNAME_VAL="$(hostname)"
|
||||||
|
RUNNER_NAME="${RUNNER_NAME:-${HOSTNAME_VAL}-runner}"
|
||||||
|
RUNNER_LABELS="${RUNNER_LABELS:-${HOSTNAME_VAL}}"
|
||||||
|
WORK_DIR="${WORK_DIR:-$HOME/act_runner}"
|
||||||
|
VERSION="v0.2.13"
|
||||||
|
FILE_VER="${VERSION#v}"
|
||||||
|
ARCH="darwin-amd64" # for Intel Macs. Use darwin-arm64 on Apple Silicon
|
||||||
|
BINARY_URL="https://gitea.com/gitea/act_runner/releases/download/${VERSION}/act_runner-${FILE_VER}-${ARCH}"
|
||||||
|
|
||||||
|
if [[ -z "$REG_TOKEN" ]]; then
|
||||||
|
echo "❌ REG_TOKEN not set. export REG_TOKEN=<runner token> and re-run."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
cd "$WORK_DIR"
|
||||||
|
|
||||||
|
# --- download binary ---
|
||||||
|
if [[ ! -x "${WORK_DIR}/act_runner" ]]; then
|
||||||
|
echo "⬇️ Downloading act_runner ${VERSION} for ${ARCH}…"
|
||||||
|
curl -fL "$BINARY_URL" -o act_runner
|
||||||
|
chmod +x act_runner
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- config.yml ---
|
||||||
|
if [[ ! -f "${WORK_DIR}/config.yml" ]]; then
|
||||||
|
cat > "${WORK_DIR}/config.yml" <<EOF
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
runner:
|
||||||
|
capacity: 1
|
||||||
|
envs: {}
|
||||||
|
labels:
|
||||||
|
- "${HOSTNAME_VAL}:docker://ztariq129/hurricane:alma-arm"
|
||||||
|
fetch_timeout: 30s
|
||||||
|
idle_timeout: 30s
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- register runner (only once) ---
|
||||||
|
if [[ ! -f "${WORK_DIR}/.runner" ]]; then
|
||||||
|
echo "📝 Registering runner ${RUNNER_NAME} @ ${GITEA_URL}"
|
||||||
|
./act_runner register --no-interactive \
|
||||||
|
--instance "$GITEA_URL" \
|
||||||
|
--token "$REG_TOKEN" \
|
||||||
|
--name "$RUNNER_NAME" \
|
||||||
|
--labels "$RUNNER_LABELS"
|
||||||
|
else
|
||||||
|
echo "ℹ️ Runner already registered (.runner exists)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "✅ Install complete."
|
||||||
|
echo "➡️ Start runner manually with: ${WORK_DIR}/act_runner daemon"
|
||||||
|
echo " (Optional) To run at login, create a launchd plist in ~/Library/LaunchAgents/"
|
||||||
84
hurricane/act-runner.sh
Normal file
84
hurricane/act-runner.sh
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# === CONFIG ===
|
||||||
|
GITEA_URL="${GITEA_URL:-https://git.azuredevops.co.uk}" # or export GITEA_URL=...
|
||||||
|
REG_TOKEN="tQ6K7y2adPYg7Nh1gt0pWrYbBOxkPPcHjfcvfqHE" # export REG_TOKEN=... (required)
|
||||||
|
HOSTNAME_VAL="$(hostname)"
|
||||||
|
RUNNER_NAME="${RUNNER_NAME:-${HOSTNAME_VAL}-runner}"
|
||||||
|
RUNNER_LABELS="${RUNNER_LABELS:-${HOSTNAME_VAL}}"
|
||||||
|
WORK_DIR="${WORK_DIR:-/root/act_runner}"
|
||||||
|
VERSION="v0.2.13"
|
||||||
|
FILE_VER="${VERSION#v}"
|
||||||
|
BINARY_URL="https://gitea.com/gitea/act_runner/releases/download/${VERSION}/act_runner-${FILE_VER}-linux-amd64"
|
||||||
|
SERVICE_NAME="gitea-act-runner"
|
||||||
|
|
||||||
|
if [[ -z "$REG_TOKEN" ]]; then
|
||||||
|
echo "❌ REG_TOKEN not set. export REG_TOKEN=<runner token> and re-run."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
cd "$WORK_DIR"
|
||||||
|
|
||||||
|
# --- download binary ---
|
||||||
|
if [[ ! -x "${WORK_DIR}/act_runner" ]]; then
|
||||||
|
echo "⬇️ Downloading act_runner ${VERSION}…"
|
||||||
|
curl -fL "$BINARY_URL" -o act_runner
|
||||||
|
chmod +x act_runner
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- config.yml (Docker default image + hostname label) ---
|
||||||
|
if [[ ! -f "${WORK_DIR}/config.yml" ]]; then
|
||||||
|
cat > "${WORK_DIR}/config.yml" <<EOF
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
runner:
|
||||||
|
capacity: 1
|
||||||
|
envs: {}
|
||||||
|
labels:
|
||||||
|
- "${HOSTNAME_VAL}:docker://reg.azuredevops.co.uk/alma:latest"
|
||||||
|
fetch_timeout: 30s
|
||||||
|
idle_timeout: 30s
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- register runner (only once) ---
|
||||||
|
if [[ ! -f "${WORK_DIR}/.runner" ]]; then
|
||||||
|
echo "📝 Registering runner ${RUNNER_NAME} @ ${GITEA_URL}"
|
||||||
|
./act_runner register --no-interactive \
|
||||||
|
--instance "$GITEA_URL" \
|
||||||
|
--token "$REG_TOKEN" \
|
||||||
|
--name "$RUNNER_NAME" \
|
||||||
|
--labels "$RUNNER_LABELS"
|
||||||
|
else
|
||||||
|
echo "ℹ️ Runner already registered (.runner exists)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- systemd unit (runs as root) ---
|
||||||
|
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<UNIT
|
||||||
|
[Unit]
|
||||||
|
Description=Gitea Act Runner
|
||||||
|
Wants=network-online.target docker.service
|
||||||
|
After=network-online.target docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=${WORK_DIR}
|
||||||
|
ExecStart=${WORK_DIR}/act_runner daemon
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
UNIT
|
||||||
|
|
||||||
|
# --- enable & start ---
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now "${SERVICE_NAME}"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "✅ Service installed & started: ${SERVICE_NAME}"
|
||||||
|
echo " Status: systemctl status ${SERVICE_NAME}"
|
||||||
|
echo " Logs: journalctl -u ${SERVICE_NAME} -f"
|
||||||
85
hurricane/actrunner-host.sh
Normal file
85
hurricane/actrunner-host.sh
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# === CONFIG ===
|
||||||
|
GITEA_URL="${GITEA_URL:-https://git.azuredevops.co.uk}"
|
||||||
|
REG_TOKEN="${REG_TOKEN:-}" # must be set
|
||||||
|
HOSTNAME_VAL="$(hostname)"
|
||||||
|
RUNNER_NAME="${RUNNER_NAME:-${HOSTNAME_VAL}-runner}"
|
||||||
|
RUNNER_LABELS="leaseweb"
|
||||||
|
WORK_DIR="${WORK_DIR:-/root/act_runner}"
|
||||||
|
VERSION="v0.2.13"
|
||||||
|
FILE_VER="${VERSION#v}"
|
||||||
|
BINARY_URL="https://gitea.com/gitea/act_runner/releases/download/${VERSION}/act_runner-${FILE_VER}-linux-amd64"
|
||||||
|
SERVICE_NAME="gitea-act-runner"
|
||||||
|
|
||||||
|
if [[ -z "$REG_TOKEN" ]]; then
|
||||||
|
echo "❌ REG_TOKEN not set. export REG_TOKEN=<runner token> and re-run."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
cd "$WORK_DIR"
|
||||||
|
|
||||||
|
# --- download binary ---
|
||||||
|
if [[ ! -x "${WORK_DIR}/act_runner" ]]; then
|
||||||
|
echo "⬇️ Downloading act_runner ${VERSION}…"
|
||||||
|
curl -fL "$BINARY_URL" -o act_runner
|
||||||
|
chmod +x act_runner
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- config.yml (host executor + leaseweb label) ---
|
||||||
|
if [[ ! -f "${WORK_DIR}/config.yml" ]]; then
|
||||||
|
cat > "${WORK_DIR}/config.yml" <<EOF
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
runner:
|
||||||
|
capacity: 1
|
||||||
|
executor: host
|
||||||
|
envs: {}
|
||||||
|
labels:
|
||||||
|
- "leaseweb"
|
||||||
|
fetch_timeout: 30s
|
||||||
|
idle_timeout: 30s
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- register runner (only once) ---
|
||||||
|
if [[ ! -f "${WORK_DIR}/.runner" ]]; then
|
||||||
|
echo "📝 Registering runner ${RUNNER_NAME} @ ${GITEA_URL}"
|
||||||
|
./act_runner register --no-interactive \
|
||||||
|
--instance "$GITEA_URL" \
|
||||||
|
--token "$REG_TOKEN" \
|
||||||
|
--name "$RUNNER_NAME" \
|
||||||
|
--labels "$RUNNER_LABELS"
|
||||||
|
else
|
||||||
|
echo "ℹ️ Runner already registered (.runner exists)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- systemd unit (runs as root) ---
|
||||||
|
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<UNIT
|
||||||
|
[Unit]
|
||||||
|
Description=Gitea Act Runner (Host Executor)
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=${WORK_DIR}
|
||||||
|
ExecStart=${WORK_DIR}/act_runner daemon
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
UNIT
|
||||||
|
|
||||||
|
# --- enable & start ---
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now "${SERVICE_NAME}"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "✅ Service installed & started: ${SERVICE_NAME}"
|
||||||
|
echo " Status: systemctl status ${SERVICE_NAME}"
|
||||||
|
echo " Logs: journalctl -u ${SERVICE_NAME} -f"
|
||||||
18
hurricane/autowinlogon.ps1
Normal file
18
hurricane/autowinlogon.ps1
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Set Variables
|
||||||
|
$Username = Read-Host -Prompt "Enter the username for automatic login"
|
||||||
|
$Password = Read-Host -Prompt "Enter the password for automatic login" -AsSecureString
|
||||||
|
$PlainPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
||||||
|
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
|
||||||
|
)
|
||||||
|
$Domain = "." # Use '.' for local machine or specify domain if applicable
|
||||||
|
|
||||||
|
# Registry Path for auto-login
|
||||||
|
$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
|
||||||
|
|
||||||
|
# Set registry values for auto-login
|
||||||
|
Set-ItemProperty -Path $RegPath -Name "AutoAdminLogon" -Value "1"
|
||||||
|
Set-ItemProperty -Path $RegPath -Name "DefaultUsername" -Value $Username
|
||||||
|
Set-ItemProperty -Path $RegPath -Name "DefaultPassword" -Value $PlainPassword
|
||||||
|
Set-ItemProperty -Path $RegPath -Name "DefaultDomainName" -Value $Domain
|
||||||
|
|
||||||
|
Write-Host "Automatic login has been configured. Restart the computer to test."
|
||||||
8
hurricane/duckdns.yml
Normal file
8
hurricane/duckdns.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
duckdns:
|
||||||
|
image: lscr.io/linuxserver/duckdns:latest
|
||||||
|
container_name: duckdns
|
||||||
|
environment:
|
||||||
|
- SUBDOMAINS=zeshan,vpn-uk
|
||||||
|
- TOKEN=f87d112b-ae6a-4e88-b93e-a148dd4493b7
|
||||||
|
restart: always
|
||||||
34
hurricane/fail2ban.sh
Normal file
34
hurricane/fail2ban.sh
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Fail2Ban Installation and Configuration Script
|
||||||
|
# Blocks incorrect logins permanently after the first failed attempt
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Updating package lists..."
|
||||||
|
sudo apt update
|
||||||
|
|
||||||
|
echo "Installing Fail2Ban..."
|
||||||
|
sudo apt install fail2ban -y
|
||||||
|
|
||||||
|
echo "Creating Fail2Ban configuration for permanent blocking..."
|
||||||
|
|
||||||
|
# Create jail.local configuration file
|
||||||
|
sudo bash -c 'cat > /etc/fail2ban/jail.local' <<EOF
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = ssh
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
maxretry = 1
|
||||||
|
bantime = -1 # Permanent ban
|
||||||
|
findtime = 10m
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Enabling and starting Fail2Ban service..."
|
||||||
|
sudo systemctl enable fail2ban
|
||||||
|
sudo systemctl start fail2ban
|
||||||
|
|
||||||
|
echo "Checking Fail2Ban service status..."
|
||||||
|
sudo systemctl status fail2ban --no-pager
|
||||||
|
|
||||||
|
echo "Fail2Ban has been installed and configured successfully."
|
||||||
30
hurricane/gitlab-arm.sh
Normal file
30
hurricane/gitlab-arm.sh
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
docker run \
|
||||||
|
--detach \
|
||||||
|
--restart unless-stopped \
|
||||||
|
--name gitlab-ce \
|
||||||
|
--privileged \
|
||||||
|
--memory 4G \
|
||||||
|
--publish 222:22 \
|
||||||
|
--publish 80:80 \
|
||||||
|
--hostname raspberrypi.tail872446.ts.net \
|
||||||
|
--env GITLAB_ROOT_PASSWORD="" \
|
||||||
|
--env GITLAB_OMNIBUS_CONFIG="\
|
||||||
|
gitlab_rails['smtp_enable'] = true; \
|
||||||
|
gitlab_rails['smtp_address'] = 'mail.azuredevops.co.uk'; \
|
||||||
|
gitlab_rails['smtp_port'] = 587; \
|
||||||
|
gitlab_rails['smtp_user_name'] = 'zeshan@azuredevops.co.uk'; \
|
||||||
|
gitlab_rails['smtp_password'] = ''; \
|
||||||
|
gitlab_rails['smtp_domain'] = 'mail.azuredevops.co.uk'; \
|
||||||
|
gitlab_rails['smtp_authentication'] = 'login'; \
|
||||||
|
gitlab_rails['smtp_enable_starttls_auto'] = true; \
|
||||||
|
gitlab_rails['smtp_tls'] = false; \
|
||||||
|
gitlab_rails['gitlab_email_from'] = 'zeshan@azuredevops.co.uk'; \
|
||||||
|
gitlab_rails['gitlab_shell_ssh_port'] = 222; "\
|
||||||
|
--volume /srv/gitlab-ce/conf:/etc/gitlab:z \
|
||||||
|
--volume /srv/gitlab-ce/logs:/var/log/gitlab:z \
|
||||||
|
--volume /srv/gitlab-ce/data:/var/opt/gitlab:z \
|
||||||
|
yrzr/gitlab-ce-arm64v8:latest
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1081@Happy
|
||||||
6
hurricane/hostbrr-mount.sh
Normal file
6
hurricane/hostbrr-mount.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
mkdir -p /mnt/hostbrr && printf '%s' 'Shan33779488@@' | sshfs -p 53211 -o password_stdin -o PreferredAuthentications=password -o PubkeyAuthentication=no -o KbdInteractiveAuthentication=no -o StrictHostKeyChecking=no dreamart@ftp2.hostypanel.com:/home/dreamart /mnt/hostbrr
|
||||||
|
|
||||||
|
export RESTIC_REPOSITORY=/mnt/hostbrr/netcup
|
||||||
|
export RESTIC_PASSWORD="Shan33779488@@"
|
||||||
|
restic backup /root/docker /var/lib/docker
|
||||||
|
restic init
|
||||||
35
hurricane/hostname-change.sh
Normal file
35
hurricane/hostname-change.sh
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Display current hostname
|
||||||
|
current_hostname=$(hostname)
|
||||||
|
echo "Current hostname: $current_hostname"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Suggest options or prompt for input
|
||||||
|
echo "Choose a new hostname:"
|
||||||
|
PS3="Enter the number or type your own custom hostname: "
|
||||||
|
options=("web01" "db01" "storage01" "k3s-node" "Custom")
|
||||||
|
|
||||||
|
select opt in "${options[@]}"; do
|
||||||
|
if [[ "$opt" == "Custom" ]]; then
|
||||||
|
read -rp "Enter custom hostname: " new_hostname
|
||||||
|
break
|
||||||
|
elif [[ -n "$opt" ]]; then
|
||||||
|
new_hostname=$opt
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "Invalid choice. Try again."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Confirm choice
|
||||||
|
echo "You selected: $new_hostname"
|
||||||
|
read -rp "Apply new hostname? [y/N]: " confirm
|
||||||
|
|
||||||
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
hostnamectl set-hostname "$new_hostname"
|
||||||
|
echo "127.0.1.1 $new_hostname" >> /etc/hosts
|
||||||
|
echo "Hostname changed to $new_hostname. You may need to re-login for prompt update."
|
||||||
|
else
|
||||||
|
echo "Hostname change aborted."
|
||||||
|
fi
|
||||||
18
hurricane/jellyfin.yml
Normal file
18
hurricane/jellyfin.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
services:
|
||||||
|
jellyfin:
|
||||||
|
image: lscr.io/linuxserver/jellyfin:latest
|
||||||
|
container_name: jellyfin
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/London
|
||||||
|
volumes:
|
||||||
|
- /root/jellyfin/config:/config
|
||||||
|
- /mnt/media/shared/media//tv:/data/tvshows
|
||||||
|
- /mnt/media/shared/media//movies:/data/movies
|
||||||
|
ports:
|
||||||
|
- "8096:8096" # HTTP Web UI
|
||||||
|
- "8920:8920" # HTTPS Web UI (optional)
|
||||||
|
- "7359:7359/udp" # Service discovery (optional)
|
||||||
|
- "1900:1900/udp" # DLNA (optional)
|
||||||
|
restart: unless-stopped
|
||||||
21
hurricane/minio.yml
Normal file
21
hurricane/minio.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
minio:
|
||||||
|
image: minio/minio:latest
|
||||||
|
container_name: minio
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
|
# Set these if you’ll access MinIO via a reverse proxy (highly recommended)
|
||||||
|
MINIO_SERVER_URL: ${MINIO_SERVER_URL:-} # e.g. https://s3.ztariq.com
|
||||||
|
MINIO_BROWSER_REDIRECT_URL: ${MINIO_BROWSER_REDIRECT_URL:-} # e.g. https://console.ztariq.com
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-fsS", "http://localhost:9000/minio/health/live"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 20
|
||||||
|
volumes:
|
||||||
|
- ./minio_data:/data
|
||||||
|
- ./minio_config:/root/.minio
|
||||||
|
networks:
|
||||||
|
- hurricane
|
||||||
69
hurricane/mount.sh
Normal file
69
hurricane/mount.sh
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Prompt the user for the filesystem type
|
||||||
|
echo "Select the filesystem type:"
|
||||||
|
echo "1. NTFS"
|
||||||
|
echo "2. EXT4"
|
||||||
|
read -p "Enter your choice (1 or 2): " FS_CHOICE
|
||||||
|
|
||||||
|
# Set filesystem type and default mount options based on user choice
|
||||||
|
case $FS_CHOICE in
|
||||||
|
1)
|
||||||
|
FS_TYPE="ntfs"
|
||||||
|
MOUNT_OPTIONS="defaults"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
FS_TYPE="ext4"
|
||||||
|
MOUNT_OPTIONS="defaults"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Invalid choice. Exiting."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Prompt the user for the UUID
|
||||||
|
read -p "Enter the UUID: " UUID
|
||||||
|
|
||||||
|
# Prompt the user for the mount point
|
||||||
|
read -p "Enter the mount point (e.g., /mnt/data): " MOUNT_POINT
|
||||||
|
|
||||||
|
# Check if the mount point directory exists, and if not, create it
|
||||||
|
if [ ! -d "$MOUNT_POINT" ]; then
|
||||||
|
echo "Creating mount point directory..."
|
||||||
|
sudo mkdir -p "$MOUNT_POINT"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to create mount point directory. Please check your permissions."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the UUID is already in /etc/fstab
|
||||||
|
if grep -q "$UUID" /etc/fstab; then
|
||||||
|
echo "UUID $UUID is already in /etc/fstab. Skipping."
|
||||||
|
else
|
||||||
|
# Add the volume to /etc/fstab
|
||||||
|
echo "UUID=$UUID $MOUNT_POINT $FS_TYPE $MOUNT_OPTIONS 0 0" | sudo tee -a /etc/fstab > /dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Added UUID $UUID to /etc/fstab for a $FS_TYPE volume."
|
||||||
|
else
|
||||||
|
echo "Failed to add UUID to /etc/fstab. Please check your permissions."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mount the volume
|
||||||
|
echo "Attempting to mount the $FS_TYPE volume..."
|
||||||
|
sudo mount -a
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to mount. Please check the UUID and your permissions."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the mount was successful
|
||||||
|
if mount | grep -q "$MOUNT_POINT"; then
|
||||||
|
echo "$FS_TYPE volume with UUID $UUID has been successfully mounted to $MOUNT_POINT."
|
||||||
|
else
|
||||||
|
echo "Failed to mount $FS_TYPE volume with UUID $UUID to $MOUNT_POINT."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
89
hurricane/mount_smb.sh
Normal file
89
hurricane/mount_smb.sh
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Script to mount multiple CIFS shares and handle credentials separately per mount
|
||||||
|
# Compatible with Ubuntu, Debian, RHEL, CentOS, Fedora, AlmaLinux
|
||||||
|
|
||||||
|
# Detect OS type
|
||||||
|
detect_os() {
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
echo "Detected OS: $NAME ($ID)"
|
||||||
|
OS=$ID
|
||||||
|
else
|
||||||
|
echo "Unsupported OS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install CIFS utilities
|
||||||
|
install_cifs_utils() {
|
||||||
|
echo "Installing CIFS utilities..."
|
||||||
|
if [[ "$OS" == "ubuntu" || "$OS" == "debian" ]]; then
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y cifs-utils samba
|
||||||
|
elif [[ "$OS" == "rhel" || "$OS" == "centos" || "$OS" == "fedora" || "$OS" == "almalinux" ]]; then
|
||||||
|
yum update -y
|
||||||
|
yum install -y cifs-utils samba
|
||||||
|
else
|
||||||
|
echo "Unsupported OS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompt user for multiple CIFS mount inputs
|
||||||
|
handle_mounts() {
|
||||||
|
while true; do
|
||||||
|
echo
|
||||||
|
read -p "Enter the CIFS share address (e.g., //server/share): " cifs_share
|
||||||
|
read -p "Enter the mount point directory (default: /mnt/media): " mount_point
|
||||||
|
mount_point=${mount_point:-/mnt/media}
|
||||||
|
|
||||||
|
mkdir -p "$mount_point"
|
||||||
|
|
||||||
|
read -p "Enter the username: " username
|
||||||
|
read -sp "Enter the password: " password
|
||||||
|
echo
|
||||||
|
|
||||||
|
cred_file="/etc/samba/credentials_$(basename "$mount_point")"
|
||||||
|
echo -e "username=$username\npassword=$password" > "$cred_file"
|
||||||
|
chmod 600 "$cred_file"
|
||||||
|
echo "Credentials stored at $cred_file"
|
||||||
|
|
||||||
|
echo "Mounting $cifs_share at $mount_point..."
|
||||||
|
|
||||||
|
# Try SMB 3.0 first
|
||||||
|
if ! mount -t cifs "$cifs_share" "$mount_point" \
|
||||||
|
-o credentials="$cred_file",vers=3.0,iocharset=utf8,uid=1000,gid=1000,file_mode=0660,dir_mode=0770; then
|
||||||
|
echo "SMB 3.0 failed, retrying with SMB 3.1.1..."
|
||||||
|
if ! mount -t cifs "$cifs_share" "$mount_point" \
|
||||||
|
-o credentials="$cred_file",vers=3.1.1,iocharset=utf8,uid=1000,gid=1000,file_mode=0660,dir_mode=0770; then
|
||||||
|
echo "❌ Failed to mount $cifs_share, please check credentials or network."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Mounted successfully."
|
||||||
|
|
||||||
|
read -p "Add this mount to /etc/fstab for automount at boot? (y/n): " add_fstab
|
||||||
|
if [[ "$add_fstab" =~ ^[Yy]$ ]]; then
|
||||||
|
fstab_entry="$cifs_share $mount_point cifs credentials=$cred_file,vers=3.0,iocharset=utf8,uid=1000,gid=1000,file_mode=0660,dir_mode=0770 0 0"
|
||||||
|
if ! grep -qsF "$fstab_entry" /etc/fstab; then
|
||||||
|
echo "$fstab_entry" >> /etc/fstab
|
||||||
|
echo "Added to /etc/fstab"
|
||||||
|
else
|
||||||
|
echo "Entry already exists in /etc/fstab"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "Do you want to add another CIFS mount? (y/n): " more
|
||||||
|
[[ "$more" =~ ^[Yy]$ ]] || break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
detect_os
|
||||||
|
install_cifs_utils
|
||||||
|
handle_mounts
|
||||||
|
|
||||||
|
echo "🎉 All operations completed."
|
||||||
29
hurricane/nc-backup.sh
Normal file
29
hurricane/nc-backup.sh
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BACKUP_DIR="/mnt/s3bucket/nc-bk"
|
||||||
|
|
||||||
|
echo "🛑 Stopping all containers..."
|
||||||
|
docker stop $(docker ps -q) || true
|
||||||
|
|
||||||
|
echo "🧹 Clearing old backup..."
|
||||||
|
rm -rf "$BACKUP_DIR"
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
for vol in nextcloud_aio_apache \
|
||||||
|
nextcloud_aio_database \
|
||||||
|
nextcloud_aio_database_dump \
|
||||||
|
nextcloud_aio_mastercontainer \
|
||||||
|
nextcloud_aio_nextcloud \
|
||||||
|
nextcloud_aio_nextcloud_data \
|
||||||
|
nextcloud_aio_onlyoffice \
|
||||||
|
nextcloud_aio_redis; do
|
||||||
|
echo "📦 Backing up $vol..."
|
||||||
|
docker run --rm \
|
||||||
|
-v ${vol}:/volume \
|
||||||
|
-v "$BACKUP_DIR":/backup \
|
||||||
|
alpine tar -czf /backup/${vol}.tar.gz -C /volume .
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✅ Backup complete! Files saved in: $BACKUP_DIR"
|
||||||
|
ls -lh "$BACKUP_DIR"
|
||||||
46
hurricane/nc-restore.sh
Normal file
46
hurricane/nc-restore.sh
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BACKUP_DIR="/mnt/s3bucket/nc-bk"
|
||||||
|
|
||||||
|
VOLUMES=(
|
||||||
|
nextcloud_aio_apache
|
||||||
|
nextcloud_aio_database
|
||||||
|
nextcloud_aio_database_dump
|
||||||
|
nextcloud_aio_mastercontainer
|
||||||
|
nextcloud_aio_nextcloud
|
||||||
|
nextcloud_aio_nextcloud_data
|
||||||
|
nextcloud_aio_onlyoffice
|
||||||
|
nextcloud_aio_redis
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "🛑 Stopping all containers..."
|
||||||
|
docker stop $(docker ps -q) || true
|
||||||
|
|
||||||
|
for vol in "${VOLUMES[@]}"; do
|
||||||
|
ARCHIVE="$BACKUP_DIR/${vol}.tar.gz"
|
||||||
|
|
||||||
|
# Check if volume exists
|
||||||
|
if docker volume inspect "$vol" >/dev/null 2>&1; then
|
||||||
|
echo "📂 Volume $vol already exists"
|
||||||
|
else
|
||||||
|
echo "🆕 Creating volume $vol..."
|
||||||
|
docker volume create "$vol" >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restore from archive if present
|
||||||
|
if [ -f "$ARCHIVE" ]; then
|
||||||
|
echo "♻️ Restoring $vol from $ARCHIVE..."
|
||||||
|
docker run --rm \
|
||||||
|
-v ${vol}:/volume \
|
||||||
|
-v "$BACKUP_DIR":/backup \
|
||||||
|
alpine sh -c "rm -rf /volume/* && tar -xzf /backup/${vol}.tar.gz -C /volume"
|
||||||
|
else
|
||||||
|
echo "⚠️ No archive found for $vol, skipping."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "🚀 Starting all containers..."
|
||||||
|
docker start $(docker ps -aq) || true
|
||||||
|
|
||||||
|
echo "✅ Restore complete and containers started!"
|
||||||
29
hurricane/pi-hole.yml
Normal file
29
hurricane/pi-hole.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
adguardhome:
|
||||||
|
image: adguard/adguardhome
|
||||||
|
container_name: adguardhome
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- adguard_work:/opt/adguardhome/work
|
||||||
|
- adguard_conf:/opt/adguardhome/conf
|
||||||
|
ports:
|
||||||
|
- "53:53/tcp"
|
||||||
|
- "53:53/udp"
|
||||||
|
- "67:67/udp"
|
||||||
|
- "68:68/udp"
|
||||||
|
- "8082:80/tcp"
|
||||||
|
- "8443:443/tcp"
|
||||||
|
- "8443:443/udp"
|
||||||
|
- "3000:3000/tcp"
|
||||||
|
- "853:853/tcp"
|
||||||
|
- "784:784/udp"
|
||||||
|
- "853:853/udp"
|
||||||
|
- "8853:8853/udp"
|
||||||
|
- "5443:5443/tcp"
|
||||||
|
- "5443:5443/udp"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
adguard_work:
|
||||||
|
adguard_conf:
|
||||||
185
hurricane/pivpn-fedora-nas-setup.md
Normal file
185
hurricane/pivpn-fedora-nas-setup.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# 📘 Guide: Remote Access via PiVPN, Fedora Gateway, and Home NAS
|
||||||
|
|
||||||
|
## 1. 🎯 Goal
|
||||||
|
|
||||||
|
Allow a remote machine (like a VPS in the US or a laptop in Spain) to connect into your **PiVPN WireGuard server** and then securely access devices on your **home LAN** (such as a NAS).
|
||||||
|
|
||||||
|
**End Flow:**
|
||||||
|
```
|
||||||
|
Remote Client (Spain/US VPS)
|
||||||
|
⇩ (WireGuard VPN)
|
||||||
|
PiVPN Server (UK, 10.138.135.1)
|
||||||
|
⇩ (peer routing)
|
||||||
|
Fedora VM (VPN: 10.138.135.3 / LAN: 192.168.1.40)
|
||||||
|
⇩
|
||||||
|
NAS (192.168.1.207, 192.168.1.216, etc.)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 🛠️ Prerequisites
|
||||||
|
|
||||||
|
Before starting, make sure you have:
|
||||||
|
|
||||||
|
### Knowledge
|
||||||
|
- Ability to log into your servers via SSH.
|
||||||
|
- Basic Linux familiarity (running commands, editing files).
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
- A **PiVPN server** already set up and working with WireGuard.
|
||||||
|
- A **Fedora VM** on your home LAN with:
|
||||||
|
- One interface in the VPN (`10.138.135.3`).
|
||||||
|
- One interface in the home LAN (`192.168.1.40`).
|
||||||
|
- A **NAS** or devices on your home LAN (`192.168.1.x`).
|
||||||
|
- At least one **remote client** (US VPS, laptop in Spain, etc.).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 🚦 Step-by-Step Setup
|
||||||
|
|
||||||
|
### Step 1: Connect Remote Client to VPN
|
||||||
|
- Place config in `/etc/wireguard/vps.conf`.
|
||||||
|
- Bring it up:
|
||||||
|
```bash
|
||||||
|
sudo wg-quick up vps
|
||||||
|
```
|
||||||
|
- Verify:
|
||||||
|
```bash
|
||||||
|
sudo wg show vps
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ You should see a handshake with the PiVPN server.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 2: Ensure PiVPN allows peer-to-peer
|
||||||
|
By default, VPN clients can only talk to the server. Enable forwarding:
|
||||||
|
|
||||||
|
1. On **PiVPN server** (`/etc/wireguard/wg0.conf`):
|
||||||
|
```ini
|
||||||
|
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
|
||||||
|
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
|
||||||
|
```
|
||||||
|
2. Apply:
|
||||||
|
```bash
|
||||||
|
sudo sysctl -w net.ipv4.ip_forward=1
|
||||||
|
sudo systemctl restart wg-quick@wg0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 3: Advertise the home LAN via Fedora
|
||||||
|
On the **PiVPN server**, edit the Fedora peer:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Peer]
|
||||||
|
PublicKey = <FedoraPublicKey>
|
||||||
|
AllowedIPs = 10.138.135.3/32, 192.168.1.0/24
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells the VPN server: *traffic for 192.168.1.x should go to Fedora*.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 4: Route home LAN via VPN clients
|
||||||
|
On your **remote client** (US VPS, Spain laptop), edit config:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Peer]
|
||||||
|
PublicKey = <ServerPublicKey>
|
||||||
|
AllowedIPs = 10.138.135.0/24, 192.168.1.0/24
|
||||||
|
```
|
||||||
|
|
||||||
|
Reconnect:
|
||||||
|
```bash
|
||||||
|
sudo wg-quick down vps
|
||||||
|
sudo wg-quick up vps
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the client knows to send home LAN traffic into the VPN.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 5: Enable forwarding & NAT on Fedora
|
||||||
|
Fedora needs to pass packets between VPN (`fedora`) and LAN (`ens18`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable forwarding
|
||||||
|
sudo sysctl -w net.ipv4.ip_forward=1
|
||||||
|
|
||||||
|
# Allow forwarding
|
||||||
|
sudo iptables -A FORWARD -i fedora -o ens18 -j ACCEPT
|
||||||
|
sudo iptables -A FORWARD -i ens18 -o fedora -j ACCEPT
|
||||||
|
|
||||||
|
# NAT VPN subnet → Fedora LAN IP
|
||||||
|
sudo iptables -t nat -A POSTROUTING -s 10.138.135.0/24 -o ens18 -j MASQUERADE
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 6: Test connectivity
|
||||||
|
From **remote client**:
|
||||||
|
```bash
|
||||||
|
ping 192.168.1.40 # Fedora LAN IP
|
||||||
|
ping 192.168.1.207 # NAS
|
||||||
|
ping 192.168.1.216 # Another LAN device
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ If Fedora responds but NAS doesn’t → NAT rule missing.
|
||||||
|
✅ If all respond → setup complete.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 🔄 Making NAT Permanent (Fedora)
|
||||||
|
|
||||||
|
By default, Fedora forgets iptables rules after reboot.
|
||||||
|
|
||||||
|
### Option A (simple, legacy)
|
||||||
|
```bash
|
||||||
|
sudo dnf install -y iptables-services
|
||||||
|
sudo service iptables save
|
||||||
|
sudo systemctl enable iptables
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B (modern, nftables)
|
||||||
|
Edit `/etc/nftables.conf`:
|
||||||
|
```nft
|
||||||
|
table ip nat {
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority 100;
|
||||||
|
ip saddr 10.138.135.0/24 oifname "ens18" masquerade
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply:
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable nftables
|
||||||
|
sudo systemctl restart nftables
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 🔎 Troubleshooting Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[1] VPN up? → sudo wg show
|
||||||
|
↓
|
||||||
|
[2] Can ping Fedora VPN IP (10.138.135.3)?
|
||||||
|
↓
|
||||||
|
[3] Can ping Fedora LAN IP (192.168.1.40)?
|
||||||
|
↓
|
||||||
|
[4] Can ping NAS (192.168.1.x)?
|
||||||
|
↙ ↘
|
||||||
|
No → Check NAT Yes → Success 🎉
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. ✅ End State
|
||||||
|
|
||||||
|
- Remote clients reach Fedora and all devices on home LAN.
|
||||||
|
- Fedora acts as NAT gateway.
|
||||||
|
- NAT persists across reboot.
|
||||||
|
- SSH works on Fedora via port `54321`.
|
||||||
|
- No DNS/IP leaks — traffic exits via PiVPN server.
|
||||||
22
hurricane/qbt.sh
Normal file
22
hurricane/qbt.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
docker run -d \
|
||||||
|
--name=qbittorrent \
|
||||||
|
-e PUID=1000 \
|
||||||
|
-e PGID=1000 \
|
||||||
|
-e TZ="Europe/London" \
|
||||||
|
-e VPN_ENABLED=true \
|
||||||
|
-e VPN_PROVIDER=pia \
|
||||||
|
-e VPN_PIA_USER=p0363376 \
|
||||||
|
-e VPN_PIA_PASS=cq89D59uVf \
|
||||||
|
-e VPN_PIA_PREFERRED_REGION \
|
||||||
|
-e VPN_AUTO_PORT_FORWARD=true \
|
||||||
|
-e VPN_LAN_NETWORK="100.64.0.0/10,172.18.0.0/16,192.168.0.0/24" \
|
||||||
|
-e VPN_NAMESERVERS="194.169.169.169" \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-v ./qbittorrent:/config \
|
||||||
|
--cap-add=NET_ADMIN \
|
||||||
|
--device /dev/net/tun:/dev/net/tun \
|
||||||
|
--sysctl net.ipv4.conf.all.src_valid_mark=1 \
|
||||||
|
--sysctl net.ipv6.conf.all.disable_ipv6=1 \
|
||||||
|
--sysctl net.ipv4.conf.all.rp_filter=2 \
|
||||||
|
--sysctl net.ipv4.conf.default.rp_filter=2 \
|
||||||
|
ghcr.io/hotio/qbittorrent:latest
|
||||||
39
hurricane/qbt.yml
Normal file
39
hurricane/qbt.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
services:
|
||||||
|
qbittorrent:
|
||||||
|
container_name: qbittorrent
|
||||||
|
image: ghcr.io/hotio/qbittorrent
|
||||||
|
ports:
|
||||||
|
- "8000:8080"
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- UMASK=002
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
- WEBUI_PORTS=8080/tcp,8080/udp
|
||||||
|
- VPN_ENABLED=true
|
||||||
|
- VPN_CONF=wg0
|
||||||
|
- VPN_PROVIDER=pia
|
||||||
|
- VPN_LAN_NETWORK=192.168.4.0/24
|
||||||
|
- VPN_LAN_LEAK_ENABLED=false
|
||||||
|
- VPN_EXPOSE_PORTS_ON_LAN
|
||||||
|
- VPN_AUTO_PORT_FORWARD=true
|
||||||
|
- VPN_AUTO_PORT_FORWARD_TO_PORTS=
|
||||||
|
- VPN_KEEP_LOCAL_DNS=false
|
||||||
|
- VPN_FIREWALL_TYPE=auto
|
||||||
|
- VPN_HEALTHCHECK_ENABLED=true
|
||||||
|
- VPN_PIA_USER=p0363376
|
||||||
|
- VPN_PIA_PASS=cq89D59uVf
|
||||||
|
- VPN_PIA_PREFERRED_REGION
|
||||||
|
- VPN_PIA_DIP_TOKEN=no
|
||||||
|
- VPN_PIA_PORT_FORWARD_PERSIST=false
|
||||||
|
- PRIVOXY_ENABLED=false
|
||||||
|
- UNBOUND_ENABLED=false
|
||||||
|
volumes:
|
||||||
|
- /root/data/qbt/config:/config
|
||||||
|
- /root/data/qbt/data:/data
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
sysctls:
|
||||||
|
- net.ipv4.conf.all.src_valid_mark=1
|
||||||
|
- net.ipv6.conf.all.disable_ipv6=1
|
||||||
|
restart: always
|
||||||
26
hurricane/restic-backup.sh
Normal file
26
hurricane/restic-backup.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# === Restic Config ===
|
||||||
|
export RESTIC_REPOSITORY="/mnt/windows/OneDrive/backup/pi"
|
||||||
|
export RESTIC_PASSWORD_FILE="/root/.restic-pass"
|
||||||
|
|
||||||
|
# === Logging ===
|
||||||
|
LOGFILE="/var/log/restic-backup.log"
|
||||||
|
|
||||||
|
echo "[$(date)] === Restic backup started ===" | tee -a "$LOGFILE"
|
||||||
|
|
||||||
|
# === Run backup ===
|
||||||
|
restic backup \
|
||||||
|
/mnt/data/docker/ \
|
||||||
|
/mnt/data/nextcloud/ \
|
||||||
|
>> "$LOGFILE" 2>&1
|
||||||
|
|
||||||
|
# === Retention: Keep only last 2 days ===
|
||||||
|
restic forget \
|
||||||
|
--keep-within 48h \
|
||||||
|
--prune \
|
||||||
|
>> "$LOGFILE" 2>&1
|
||||||
|
|
||||||
|
echo "[$(date)] === Restic backup finished ===" | tee -a "$LOGFILE"
|
||||||
31
hurricane/restic.sh
Normal file
31
hurricane/restic.sh
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export RESTIC_PASSWORD='Shan33779488@@'
|
||||||
|
export RESTIC_REPOSITORY='/mnt/raid1/backup/restic'
|
||||||
|
|
||||||
|
LOG_FILE="/var/log/restic_backup.log"
|
||||||
|
BACKUP_DATE=$(date +%F)
|
||||||
|
|
||||||
|
# Safety check
|
||||||
|
if ! mountpoint -q /mnt/raid1; then
|
||||||
|
echo "$(date): RAID not mounted. Aborting." >> "$LOG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Init repo if not exists
|
||||||
|
if [ ! -d "$RESTIC_REPOSITORY" ]; then
|
||||||
|
restic init >> "$LOG_FILE" 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "$(date): Starting Restic backup..."
|
||||||
|
restic backup /var/lib/nextcloud /var/lib/docker --tag "$BACKUP_DATE" --verbose
|
||||||
|
|
||||||
|
echo "$(date): Forgetting old backups..."
|
||||||
|
restic forget --keep-last 1 --prune
|
||||||
|
|
||||||
|
echo "$(date): Checking repo integrity..."
|
||||||
|
restic check
|
||||||
|
|
||||||
|
echo "$(date): Backup complete."
|
||||||
|
} >> "$LOG_FILE" 2>&1
|
||||||
129
hurricane/setup-samba.sh
Normal file
129
hurricane/setup-samba.sh
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# === PROMPT USER ===
|
||||||
|
read -rp "📂 Enter the folder you want to share (absolute path): " SHARE_PATH
|
||||||
|
read -rp "📍 Enter the mount point name (e.g., data, backups, media): " MOUNT_POINT
|
||||||
|
read -rp "👤 Enter the Samba username: " SMB_USER
|
||||||
|
|
||||||
|
# Ensure SHARE_PATH is absolute
|
||||||
|
if [[ "$SHARE_PATH" != /* ]]; then
|
||||||
|
echo "❌ Please provide an absolute path (starting with /)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect distro family
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
DISTRO_FAMILY=$ID
|
||||||
|
else
|
||||||
|
echo "❌ Cannot detect OS type"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_samba() {
|
||||||
|
if ! command -v smbd >/dev/null 2>&1 && ! command -v smb >/dev/null 2>&1; then
|
||||||
|
echo "📦 Installing Samba..."
|
||||||
|
case "$DISTRO_FAMILY" in
|
||||||
|
ubuntu|debian)
|
||||||
|
apt-get update -y
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y samba samba-common-bin
|
||||||
|
;;
|
||||||
|
almalinux|rocky|centos|rhel|fedora)
|
||||||
|
dnf install -y samba samba-client
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "❌ Unsupported distro: $DISTRO_FAMILY"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install Samba
|
||||||
|
install_samba
|
||||||
|
|
||||||
|
# Stop AD-DC service if it’s installed
|
||||||
|
systemctl disable --now samba-ad-dc 2>/dev/null || true
|
||||||
|
|
||||||
|
# Ensure directory exists
|
||||||
|
mkdir -p "$SHARE_PATH"
|
||||||
|
|
||||||
|
# Ensure user exists (no home, no login)
|
||||||
|
if ! id -u "$SMB_USER" >/dev/null 2>&1; then
|
||||||
|
echo "ℹ️ Creating system user $SMB_USER (no home, no login)"
|
||||||
|
useradd -M -s /usr/sbin/nologin "$SMB_USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set directory ownership
|
||||||
|
chown -R "$SMB_USER":"$SMB_USER" "$SHARE_PATH"
|
||||||
|
|
||||||
|
# Backup smb.conf once
|
||||||
|
if [ -f /etc/samba/smb.conf ] && [ ! -f /etc/samba/smb.conf.bak ]; then
|
||||||
|
cp -a /etc/samba/smb.conf /etc/samba/smb.conf.bak
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use the mount point name as the share name
|
||||||
|
SHARE_NAME="$MOUNT_POINT"
|
||||||
|
|
||||||
|
# Remove any old section with same name
|
||||||
|
awk -v name="[$SHARE_NAME]" '
|
||||||
|
BEGIN {skip=0}
|
||||||
|
/^\[/ {
|
||||||
|
if ($0 == name) {skip=1; next}
|
||||||
|
else {skip=0}
|
||||||
|
}
|
||||||
|
skip==0 {print}
|
||||||
|
' /etc/samba/smb.conf > /etc/samba/smb.conf.tmp
|
||||||
|
mv /etc/samba/smb.conf.tmp /etc/samba/smb.conf
|
||||||
|
|
||||||
|
# Append new share config
|
||||||
|
cat <<EOF >> /etc/samba/smb.conf
|
||||||
|
|
||||||
|
[$SHARE_NAME]
|
||||||
|
path = $SHARE_PATH
|
||||||
|
browseable = yes
|
||||||
|
read only = no
|
||||||
|
guest ok = no
|
||||||
|
valid users = $SMB_USER
|
||||||
|
create mask = 0664
|
||||||
|
directory mask = 0775
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Validate config
|
||||||
|
testparm -s >/dev/null
|
||||||
|
|
||||||
|
# Prompt for Samba password
|
||||||
|
echo "🔑 Set a Samba password for $SMB_USER:"
|
||||||
|
smbpasswd -a "$SMB_USER"
|
||||||
|
smbpasswd -e "$SMB_USER"
|
||||||
|
|
||||||
|
# Open firewall
|
||||||
|
if command -v ufw >/dev/null 2>&1; then
|
||||||
|
ufw allow Samba || true
|
||||||
|
elif command -v firewall-cmd >/dev/null 2>&1; then
|
||||||
|
firewall-cmd --permanent --add-service=samba || true
|
||||||
|
firewall-cmd --reload || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable and start services
|
||||||
|
case "$DISTRO_FAMILY" in
|
||||||
|
ubuntu|debian)
|
||||||
|
systemctl enable --now smbd
|
||||||
|
systemctl enable --now nmbd 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
almalinux|rocky|centos|rhel|fedora)
|
||||||
|
systemctl enable --now smb
|
||||||
|
systemctl enable --now nmb
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
IP_ADDR="$(hostname -I | awk '{print $1}')"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "✅ Samba share created!"
|
||||||
|
echo " Path: $SHARE_PATH"
|
||||||
|
echo " User: $SMB_USER"
|
||||||
|
echo " Share: \\\\$IP_ADDR\\$SHARE_NAME"
|
||||||
|
echo
|
||||||
|
echo "Test with: smbclient -L //$IP_ADDR -U $SMB_USER"
|
||||||
1
hurricane/sshfs_mount.sh
Normal file
1
hurricane/sshfs_mount.sh
Normal file
@@ -0,0 +1 @@
|
|||||||
|
sudo sshfs -o allow_other,port=54321,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 root@hurricane.tail872446.ts.net:/mnt/raid /mnt/raid
|
||||||
31
hurricane/wg-easy-hostbrr.yml
Normal file
31
hurricane/wg-easy-hostbrr.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
services:
|
||||||
|
wg-easy:
|
||||||
|
container_name: wg-easy
|
||||||
|
image: ghcr.io/wg-easy/wg-easy
|
||||||
|
environment:
|
||||||
|
WG_HOST: hostbrr.azuredevops.co.uk
|
||||||
|
PASSWORD_HASH: '$$2a$$12$$nMu2fSWPkmefVLdIf68Qle/QK/9oZx/Jmp2HfjZmo6R8V/AtVKD82'
|
||||||
|
PORT: 51821
|
||||||
|
WG_PORT: 53298
|
||||||
|
LANG: en
|
||||||
|
WG_DEFAULT_DNS: '94.140.14.14, 94.140.15.15'
|
||||||
|
ports:
|
||||||
|
- "53298:53298/udp" # Changed from 51820 to 53298
|
||||||
|
- "51821:51821/tcp"
|
||||||
|
volumes:
|
||||||
|
- wg-easy-data:/etc/wireguard
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
- SYS_MODULE
|
||||||
|
sysctls:
|
||||||
|
net.ipv4.conf.all.src_valid_mark: 1
|
||||||
|
net.ipv4.ip_forward: 1
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- hostbrr_hurricane
|
||||||
|
volumes:
|
||||||
|
wg-easy-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
hostbrr_hurricane:
|
||||||
|
external: true # This assumes the network already exists. You can create it with: docker network create hurricane
|
||||||
19
jellyfin/jellyfin.yml
Normal file
19
jellyfin/jellyfin.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
services:
|
||||||
|
jellyfin:
|
||||||
|
image: lscr.io/linuxserver/jellyfin:latest
|
||||||
|
container_name: jellyfin
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/London
|
||||||
|
volumes:
|
||||||
|
- /root/docker/jellyfin/config:/config
|
||||||
|
- /mnt/data/media/tv:/data/tvshows
|
||||||
|
- /mnt/data/media/movies:/data/movies
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- hurricane
|
||||||
|
|
||||||
|
networks:
|
||||||
|
hurricane:
|
||||||
|
external: true
|
||||||
0
ovh/compose.yml
Normal file
0
ovh/compose.yml
Normal file
6
zeshancv/Dockerfile
Normal file
6
zeshancv/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
FROM caddy:2-alpine
|
||||||
|
|
||||||
|
# Copy your static site into Caddy's default web root
|
||||||
|
COPY . /usr/share/caddy
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
13
zeshancv/docker-compose.yml
Normal file
13
zeshancv/docker-compose.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
zeshancv:
|
||||||
|
image: zeshan
|
||||||
|
container_name: zeshancv
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- "80" # internal only; NPM will reach this over the huricane network
|
||||||
|
networks:
|
||||||
|
- hurricane
|
||||||
|
|
||||||
|
networks:
|
||||||
|
hurricane:
|
||||||
|
external: true # assumes the network already exists: `docker network create huricane`
|
||||||
564
zeshancv/index.html
Normal file
564
zeshancv/index.html
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Zeshan Tariq – Azure DevOps · SRE · SOC · Kubernetes Engineer</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<!-- META DESCRIPTION -->
|
||||||
|
<meta name="description" content="Zeshan Tariq – Azure DevOps, SRE, SOC and DevSecOps engineer specialising in Azure, Kubernetes (AKS), Terraform, CI/CD and secure cloud automation. Available for remote contract & permanent roles.">
|
||||||
|
|
||||||
|
<!-- KEYWORDS -->
|
||||||
|
<meta name="keywords" content="Azure DevOps Engineer, SRE, SOC Engineer, DevSecOps, Kubernetes Engineer, AKS, Terraform, Cloud Engineer, Remote DevOps, Platform Engineer">
|
||||||
|
|
||||||
|
<!-- CANONICAL URL -->
|
||||||
|
<link rel="canonical" href="https://azuredevops.co.uk" />
|
||||||
|
|
||||||
|
<!-- OPEN GRAPH (SOCIAL SHARING) -->
|
||||||
|
<meta property="og:title" content="Zeshan Tariq – Azure DevOps & SRE Engineer">
|
||||||
|
<meta property="og:description" content="Cloud & platform engineer specialising in Azure, Kubernetes, DevOps, SOC and secure automation. Available for remote contract & permanent roles.">
|
||||||
|
<meta property="og:url" content="https://azuredevops.co.uk">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<!-- Optional: update this to your real preview image -->
|
||||||
|
<meta property="og:image" content="https://azuredevops.co.uk/preview.jpg">
|
||||||
|
|
||||||
|
<!-- TWITTER CARD -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="Zeshan Tariq – DevOps · SRE · SOC">
|
||||||
|
<meta name="twitter:description" content="Azure DevOps, SRE, SOC and DevSecOps engineer. Kubernetes, AKS, CI/CD, Terraform — available for remote roles.">
|
||||||
|
<meta name="twitter:image" content="https://azuredevops.co.uk/preview.jpg">
|
||||||
|
|
||||||
|
<!-- STRUCTURED DATA: PERSON -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "Zeshan Tariq",
|
||||||
|
"jobTitle": "Azure DevOps / SRE / SOC Engineer",
|
||||||
|
"url": "https://azuredevops.co.uk",
|
||||||
|
"email": "mailto:zeshan@azuredevops.co.uk",
|
||||||
|
"description": "Cloud & platform engineer specialising in Azure, Kubernetes (AKS), Terraform, CI/CD and secure cloud automation.",
|
||||||
|
"knowsAbout": [
|
||||||
|
"Azure",
|
||||||
|
"Kubernetes",
|
||||||
|
"AKS",
|
||||||
|
"DevOps",
|
||||||
|
"SRE",
|
||||||
|
"SOC",
|
||||||
|
"DevSecOps",
|
||||||
|
"Terraform",
|
||||||
|
"CI/CD Pipelines",
|
||||||
|
"Cloud Security"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #141618;
|
||||||
|
--bg-chat: #181a1c;
|
||||||
|
--bubble-assistant: #202326;
|
||||||
|
--bubble-user: #191b1e;
|
||||||
|
--bubble-system: #111315;
|
||||||
|
--border-subtle: #2a2c2e;
|
||||||
|
--accent: #10b981; /* green */
|
||||||
|
--accent-soft: #34d399;
|
||||||
|
--text-main: #e5e7eb;
|
||||||
|
--text-soft: #c2c6cb;
|
||||||
|
--text-muted: #8b8f94;
|
||||||
|
--font-main: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-mono: ui-monospace, Menlo, Consolas, "Courier New", monospace;
|
||||||
|
--radius-bubble: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top, #1d1f21 0%, #141618 60%, #111315 100%);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-family: var(--font-main);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* soft bottom vignette */
|
||||||
|
body::after {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 120px;
|
||||||
|
background: linear-gradient(to top, rgba(0,0,0,0.55), transparent);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top bar */
|
||||||
|
.top-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-title {
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-title span {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-status {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
background: #151719;
|
||||||
|
color: var(--text-soft);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat container */
|
||||||
|
.chat-shell {
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--bg-chat);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
padding: 14px 0 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-inner {
|
||||||
|
padding: 0 12px 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.system {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.user {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.assistant {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 999px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.assistant {
|
||||||
|
background: #101112;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.user {
|
||||||
|
background: #0f766e;
|
||||||
|
color: #ecfdf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
padding: 10px 12px;
|
||||||
|
max-width: 100%;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
border: 1px solid rgba(42, 44, 46, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.system .bubble {
|
||||||
|
background: var(--bubble-system);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding-inline: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.user .bubble {
|
||||||
|
background: var(--bubble-user);
|
||||||
|
border-radius: var(--radius-bubble) var(--radius-bubble) 4px var(--radius-bubble);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.assistant .bubble {
|
||||||
|
background: var(--bubble-assistant);
|
||||||
|
border-radius: var(--radius-bubble) var(--radius-bubble) var(--radius-bubble) 4px;
|
||||||
|
border-left: 3px solid var(--accent); /* ChatGPT-style accent bar */
|
||||||
|
padding-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble h1 {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble small {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble strong {
|
||||||
|
color: var(--text-main);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CV button */
|
||||||
|
.btn-primary {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 7px 16px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
background: #111315;
|
||||||
|
color: var(--text-main);
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: #151719;
|
||||||
|
border-color: var(--accent-soft);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code bubble */
|
||||||
|
pre {
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #111315;
|
||||||
|
border: 1px solid #242628;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
overflow-x: auto;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-list {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-list div {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent-soft);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.top-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Hidden H1 for SEO (not visible, but indexable) -->
|
||||||
|
<h1 style="position:absolute; left:-9999px; top:-9999px;">
|
||||||
|
Zeshan Tariq – Azure DevOps, SRE, SOC & Kubernetes Engineer
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="app">
|
||||||
|
<!-- Top bar -->
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="top-title">
|
||||||
|
<span>Zeshan Tariq</span> · DevOps · SRE · SOC
|
||||||
|
</div>
|
||||||
|
<div class="top-status">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
Available for remote contract & permanent roles
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat UI -->
|
||||||
|
<div class="chat-shell" aria-label="Profile overview of Zeshan Tariq, Azure DevOps, SRE, SOC and Kubernetes Engineer">
|
||||||
|
<div class="chat-inner">
|
||||||
|
<!-- System / intro -->
|
||||||
|
<div class="row system">
|
||||||
|
<div class="bubble">
|
||||||
|
Profile: <strong>SOC / SRE / DevOps / DevSecOps / Kubernetes</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: who are you -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Who are you and what do you do?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: intro + quick summary -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
<h1>Zeshan Tariq</h1>
|
||||||
|
<small>SOC · SRE · DevOps · DevSecOps · Kubernetes</small>
|
||||||
|
<p style="margin-top:6px;">
|
||||||
|
I’m a cloud & platform engineer focused on <strong>Azure</strong>,
|
||||||
|
<strong>Kubernetes</strong>, and <strong>secure automation</strong> —
|
||||||
|
combining <strong>SOC practices</strong> with <strong>SRE principles</strong>
|
||||||
|
to keep systems reliable and secure.
|
||||||
|
</p>
|
||||||
|
<div class="chip-row">
|
||||||
|
<div class="chip">Azure · AKS</div>
|
||||||
|
<div class="chip">Kubernetes · Docker</div>
|
||||||
|
<div class="chip">Terraform · CI/CD</div>
|
||||||
|
<div class="chip">Sentinel · SOC</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: CV -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Where can I see your full experience and roles?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: CV download -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
The CV has all the detail — roles, dates, clients, and full stack.
|
||||||
|
<br />
|
||||||
|
<!-- replace filename with your real CV if needed -->
|
||||||
|
<a href="zt1125.docx" class="btn-primary" download>
|
||||||
|
Download CV
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M12 3v12m0 0 4-4m-4 4-4-4M5 18h14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: what kind of work -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
In practical terms, what kind of work do you do?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: concise work summary -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
I design and operate <strong>Azure & AKS platforms</strong>, automate
|
||||||
|
infrastructure with <strong>Terraform/Bicep</strong>, build
|
||||||
|
<strong>CI/CD pipelines</strong>, and use <strong>SOC tooling</strong>
|
||||||
|
and <strong>SRE metrics</strong> to keep services secure and reliable.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: code sample -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
Show me a small code sample that represents how you think.
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: TypeScript snippet -->
|
||||||
|
<div class="row assistant">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
A condensed TypeScript-style example that reflects what I do with Azure, AKS,
|
||||||
|
and translating requirements into platform configuration:
|
||||||
|
<pre><code>import { AzureKubernetesServices } from '@azure/kubernetes-engine';
|
||||||
|
import { Engineer, Experience } from '@professional/core';
|
||||||
|
|
||||||
|
export class AzureKubernetesEngineer implements Engineer {
|
||||||
|
constructor(private azureServices: AzureKubernetesServices) {}
|
||||||
|
|
||||||
|
experience: Experience = {
|
||||||
|
years: 8,
|
||||||
|
sectors: ['government', 'consultancy', 'finance'],
|
||||||
|
specialties: [
|
||||||
|
'application development',
|
||||||
|
'configuration management',
|
||||||
|
'public cloud deployment',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
deploySolution(solution: any) {
|
||||||
|
return this.azureServices.deploy({
|
||||||
|
solution,
|
||||||
|
orchestration: 'Kubernetes',
|
||||||
|
cloud: 'Azure',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeBusinessAndTechnology(requirements: any) {
|
||||||
|
return this.azureServices.configure(requirements);
|
||||||
|
}
|
||||||
|
}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER: contact -->
|
||||||
|
<div class="row user">
|
||||||
|
<div class="bubble">
|
||||||
|
How do I contact you about a role?
|
||||||
|
</div>
|
||||||
|
<div class="avatar user">U</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASSISTANT: contact info -->
|
||||||
|
<div class="row assistant" id="contact">
|
||||||
|
<div class="avatar assistant">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
<path d="M8 16.5c1-.9 2.3-1.5 4-1.5s3 .6 4 1.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="10" r="3" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="bubble">
|
||||||
|
The simplest way is email — the site and CV give extra context if you need it.
|
||||||
|
<div class="contact-list">
|
||||||
|
<div>e: <a href="mailto:zeshan@azuredevops.co.uk">zeshan@azuredevops.co.uk</a></div>
|
||||||
|
<div>w: <a href="https://azuredevops.co.uk" target="_blank" rel="noopener">azuredevops.co.uk</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<span>© <span id="year"></span> Zeshan Tariq</span>
|
||||||
|
<span>CV</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("year").textContent = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
zeshancv/zt1125.docx
Normal file
BIN
zeshancv/zt1125.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user