diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f0e3525 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: +- package-ecosystem: "pip" + directory: "/ansible/" + schedule: + interval: "cron" + cronjob: "41 6 * * *" + assignees: + - "james-otten" +- package-ecosystem: "terraform" + directory: "/terraform/" + schedule: + interval: "cron" + cronjob: "4 6 * * *" + assignees: + - "james-otten" diff --git a/.github/workflows/ansible_lint.yaml b/.github/workflows/ansible_lint.yaml new file mode 100644 index 0000000..6f02746 --- /dev/null +++ b/.github/workflows/ansible_lint.yaml @@ -0,0 +1,16 @@ +name: ansible-lint +on: + pull_request: +jobs: + build: + name: Ansible Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run ansible-lint + uses: ansible/ansible-lint@c629b235398065e24ff44b5f1138028642c74a03 + with: + args: "--exclude .ansible/collections/" + setup_python: "true" + working_directory: "./ansible/" + requirements_file: "" diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..a8ea276 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,37 @@ +name: Deploy Environments +permissions: read-all + +on: + push: + # branches: + # - main + workflow_dispatch: + branches: + - main + +jobs: + # deploy_prod3: + # name: Deploy prod3 + # uses: ./.github/workflows/deploy_ntp.yaml + # with: + # environment: prod3 + # secrets: inherit + # #if: github.ref == 'refs/heads/main' + + deploy_prod2: + name: Deploy prod2 + uses: ./.github/workflows/deploy_ntp.yaml + with: + environment: prod2 + secrets: inherit + # needs: deploy_prod3 + #if: github.ref == 'refs/heads/main' + + deploy_prod1: + name: Deploy prod1 + uses: ./.github/workflows/deploy_ntp.yaml + with: + environment: prod1 + secrets: inherit + needs: deploy_prod2 + if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/deploy_ntp.yaml b/.github/workflows/deploy_ntp.yaml new file mode 100644 index 0000000..94b6cfb --- /dev/null +++ b/.github/workflows/deploy_ntp.yaml @@ -0,0 +1,92 @@ +name: Deploy NTP Server +permissions: read-all + +on: + workflow_call: + inputs: + environment: + required: true + type: string + +env: + # Secrets + TF_VAR_proxmox_host: ${{ secrets.TF_VAR_PROXMOX_HOST }} + TF_VAR_proxmox_token_id: ${{ secrets.TF_VAR_PROXMOX_TOKEN_ID }} + TF_VAR_proxmox_token_secret: ${{ secrets.TF_VAR_PROXMOX_TOKEN_SECRET }} + TF_VAR_local_password: ${{ secrets.TF_VAR_LOCAL_PASSWORD }} + TF_VAR_datadog_api_key: ${{ secrets.DATADOG_API_KEY }} + TF_VAR_datadog_site: ${{ secrets.DATADOG_SITE }} + # Credentials for deployment to AWS + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # S3 bucket for the Terraform state + BUCKET_TF_STATE: ${{ secrets.BUCKET_TF_STATE}} + +jobs: + deploy: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + steps: + - name: Checkout + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # @v4 + + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d #@v5 + with: + python-version: '3.11' + + - name: Setup ansible + run: pip install -r ansible/requirements.txt && export PATH="$HOME/.local/bin:$PATH" && ansible-galaxy collection install -r ansible/roles/requirements.yml + + - name: Setup Terraform with specified version on the runner + uses: hashicorp/setup-terraform@651471c36a6092792c552e8b1bef71e592b462d8 # @v3 + with: + terraform_version: 1.8.3 + + - name: Setup backend + run: | + echo "bucket = \"${{ secrets.BUCKET_TF_STATE }}\"" > backend.tfvars + echo "key = \"terraform/state/ntp-${{ inputs.environment }}.tfstate\"" >> backend.tfvars + working-directory: ./terraform/ + + - name: Terraform init + id: init + run: terraform init -backend-config=backend.tfvars + working-directory: ./terraform/ + + - name: Terraform format + id: fmt + run: terraform fmt -check + working-directory: ./terraform/ + + - name: Terraform validate + run: | + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ntp + echo "${{ secrets.SSH_PUBLIC_KEY }}" > ntp.pub + chmod 600 ntp + chmod 600 ntp.pub + terraform validate + working-directory: ./terraform/ + + - name: Setup WireGuard + uses: nycmeshnet/nycmesh-vpn-action@main + with: + config-name: ghntp + private-key: ${{ secrets.WIREGUARD_PRIVATE_KEY }} + server-preference: '10,3,11' + # run: | + # sudo apt-get update && sudo apt-get install -y wireguard + # echo "${{ secrets.WIREGUARD_PRIVATE_KEY }}" > privatekey + # sudo ip link add dev wg0 type wireguard + # sudo ip address add dev wg0 ${{ secrets.WIREGUARD_OVERLAY_NETWORK_IP }} peer ${{ secrets.WIREGUARD_PEER }} + # sudo wg set wg0 listen-port 48123 private-key privatekey peer ${{ secrets.WIREGUARD_PEER_PUBLIC_KEY }} allowed-ips 0.0.0.0/0 endpoint ${{ secrets.WIREGUARD_ENDPOINT }} + # sudo ip link set up dev wg0 + # rm privatekey + + - name: Terraform Apply + run: | + terraform apply -auto-approve -input=false -var-file=${{ inputs.environment }}.tfvars + working-directory: ./terraform/ + + - name: Run playbook + run: sleep 20 && export PATH="$HOME/.local/bin:$PATH" && ansible-playbook -i inventory.yaml ntp_servers.yaml + working-directory: ./ansible/ diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml new file mode 100644 index 0000000..36988dd --- /dev/null +++ b/.github/workflows/scorecard.yaml @@ -0,0 +1,64 @@ +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '30 15 * * 6' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. + if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + + steps: + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index adbb97d..39ef9b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -data/ \ No newline at end of file +data/ +.vscode/ diff --git a/ansible/.ansible-lint-ignore b/ansible/.ansible-lint-ignore new file mode 100644 index 0000000..b0f1a48 --- /dev/null +++ b/ansible/.ansible-lint-ignore @@ -0,0 +1 @@ +roles/chrony_server/tasks/main.yaml no-changed-when diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 0000000..87d5391 --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,8 @@ +[defaults] +host_key_checking = False +callbacks_enabled = timer, profile_tasks, profile_roles +gathering = 'explicit' +pipelining = True + +[ssh_connection] +ssh_args = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o CheckHostIP=no -o ControlMaster=auto -o ControlPersist=60s' diff --git a/ansible/inventory.yaml b/ansible/inventory.yaml new file mode 100644 index 0000000..4d39cd4 --- /dev/null +++ b/ansible/inventory.yaml @@ -0,0 +1,3 @@ +--- +plugin: cloud.terraform.terraform_provider +project_path: "../terraform" diff --git a/ansible/ntp_servers.yaml b/ansible/ntp_servers.yaml new file mode 100644 index 0000000..08bebdf --- /dev/null +++ b/ansible/ntp_servers.yaml @@ -0,0 +1,5 @@ +- name: NTP Servers + hosts: ntp_mgt + become: true + roles: + - role: chrony_server diff --git a/ansible/requirements.txt b/ansible/requirements.txt new file mode 100644 index 0000000..f4d108a --- /dev/null +++ b/ansible/requirements.txt @@ -0,0 +1,28 @@ +ansible==11.6.0 \ + --hash=sha256:5b9c19d6a1080011c14c821bc7e6f8fd5b2a392219cbf2ced9be05e6d447d8cd +ansible-core==2.18.6 \ + --hash=sha256:12a34749a7b20f0f1536bd3e3b2e137341867e4642e351273e96647161f595c0 +cffi==1.17.1 \ + --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ + --hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \ + --hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 +cryptography==45.0.4 \ + --hash=sha256:2882338b2a6e0bd337052e8b9007ced85c637da19ef9ecaf437744495c8c2999 +Jinja2==3.1.6 \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 +MarkupSafe==3.0.2 \ + --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ + --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ + --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 +packaging==25.0 \ + --hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 +passlib==1.7.4 \ + --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 +pycparser==2.22 \ + --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc +PyYAML==6.0.2 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 +resolvelib==1.0.1 \ + --hash=sha256:f80de38ae744bcf4e918e27a681a5c6cb63a08d9a926c0989c0730bcdd089049 \ + --hash=sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf diff --git a/ansible/roles/chrony_server/files/configured_servers.sources b/ansible/roles/chrony_server/files/configured_servers.sources new file mode 100644 index 0000000..36da7d0 --- /dev/null +++ b/ansible/roles/chrony_server/files/configured_servers.sources @@ -0,0 +1,6 @@ +# Managed by ansible + +# Cloudflare NTS +server time.cloudflare.com nts iburst +server virginia.time.system76.com nts iburst +server ohio.time.system76.com nts iburst diff --git a/ansible/roles/chrony_server/tasks/main.yaml b/ansible/roles/chrony_server/tasks/main.yaml new file mode 100644 index 0000000..3da6e7a --- /dev/null +++ b/ansible/roles/chrony_server/tasks/main.yaml @@ -0,0 +1,104 @@ +- name: Install deps + ansible.builtin.apt: + lock_timeout: 240 + update_cache: true + pkg: + - ca-certificates + - iptables-persistent + - chrony + +- name: Import the ssh_config role from the nycmesh.common collection + ansible.builtin.import_role: + name: nycmesh.common.ssh_config + +- name: Import the mesh_dns role from the nycmesh.common collection + ansible.builtin.import_role: + name: nycmesh.common.mesh_dns + vars: + extra_resolvers: "9.9.9.9" + +- name: Import the support_account role from the nycmesh.common collection + ansible.builtin.import_role: + name: nycmesh.common.support_account + +- name: Import the motd role from the nycmesh.common collection + ansible.builtin.import_role: + name: nycmesh.common.motd + vars: + github_repo: https://github.com/nycmeshnet/ntp-infra + +- name: Allow restarting of chrony + ansible.builtin.lineinfile: + path: /lib/systemd/system/chrony.service + insertafter: '\[Service\]' + search_string: Restart= + line: "Restart=always" + +- name: Import the Datadog Agent role from the Datadog collection + ansible.builtin.import_role: + name: datadog.dd.agent + vars: + datadog_api_key: "{{ DATADOG_API_KEY }}" + datadog_site: "{{ DATADOG_SITE }}" + datadog_config: + hostname: "{{ VM_HOSTNAME }}" + logs_enabled: true + datadog_additional_groups: "systemd-journal" + datadog_checks: + journald: + logs: + - type: journald + path: /var/log/journal/ + +- name: Reload datadog + ansible.builtin.systemd_service: + name: datadog-agent + state: restarted + enabled: true + daemon_reload: true + +- name: Iptables rules + ansible.builtin.template: + src: iptables.j2 + dest: /etc/iptables/rules.v4 + mode: "600" + +- name: Restore iptables rules + ansible.builtin.command: + cmd: "bash -c '/sbin/iptables-restore < /etc/iptables/rules.v4'" + +- name: Import the netplan_loopback role from the nycmesh.common collection + ansible.builtin.import_role: + name: nycmesh.common.netplan_loopback + vars: + netplan_loopback_ips: "{{ NTP_IP }};{{ bird_router_id }}" + +- name: Restart and enable iptables service + ansible.builtin.service: + name: netfilter-persistent + state: restarted + enabled: true + +- name: Chrony config + ansible.builtin.template: + src: chrony.conf.j2 + dest: /etc/chrony/chrony.conf + mode: "0644" + +- name: Chrony sources + ansible.builtin.copy: + src: configured_servers.sources + dest: /etc/chrony/sources.d/configured_servers.sources + mode: "0644" + +- name: Restart chrony + ansible.builtin.systemd_service: + name: chrony + state: restarted + enabled: true + +- name: Import the bird_basic role from the nycmesh.common collection + ansible.builtin.import_role: + name: nycmesh.common.bird2_basic + vars: + bird_binds_to_service: "chrony.service" diff --git a/ansible/roles/chrony_server/templates/chrony.conf.j2 b/ansible/roles/chrony_server/templates/chrony.conf.j2 new file mode 100644 index 0000000..aba9602 --- /dev/null +++ b/ansible/roles/chrony_server/templates/chrony.conf.j2 @@ -0,0 +1,55 @@ +# Managed by ansible + +# Welcome to the chrony configuration file. See chrony.conf(5) for more +# information about usable directives. + +# Include configuration files found in /etc/chrony/conf.d. +confdir /etc/chrony/conf.d + +# Use Debian vendor zone. +#pool 2.debian.pool.ntp.org iburst + +# Use NTP sources found in /etc/chrony/sources.d. +sourcedir /etc/chrony/sources.d + +# This directive specify the location of the file containing ID/key pairs for +# NTP authentication. +keyfile /etc/chrony/chrony.keys + +# This directive specify the file into which chronyd will store the rate +# information. +driftfile /var/lib/chrony/chrony.drift + +# Save NTS keys and cookies. +ntsdumpdir /var/lib/chrony + +# Uncomment the following line to turn logging on. +#log tracking measurements statistics + +# Log files location. +logdir /var/log/chrony + +# Stop bad estimates upsetting machine clock. +maxupdateskew 100.0 + +# This directive enables kernel synchronisation (every 11 minutes) of the +# real-time clock. Note that it can't be used along with the 'rtcfile' directive. +rtcsync + +# Step the system clock instead of slewing it if the adjustment is larger than +# one second, but only in the first three clock updates. +makestep 1 3 + +# Get TAI-UTC offset and leap seconds from the system tz database. +# This directive must be commented out when using time sources serving +# leap-smeared time. +leapsectz right/UTC + +# Operate as an NTP server for the mesh +allow 10.0.0.0/8 +allow 23.158.16.0/24 +allow 199.167.59.0/24 +allow 199.170.132.0/24 +allow 208.68.5.0/24 + +bindaddress {{ NTP_IP }} diff --git a/ansible/roles/chrony_server/templates/iptables.j2 b/ansible/roles/chrony_server/templates/iptables.j2 new file mode 100644 index 0000000..bd5f500 --- /dev/null +++ b/ansible/roles/chrony_server/templates/iptables.j2 @@ -0,0 +1,29 @@ +# Managed by ansible + +*filter +:INPUT ACCEPT [0:0] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [0:0] + +# Allow ICMP traffic from Mesh +-A INPUT -p icmp -s 10.0.0.0/8 -j ACCEPT +-A INPUT -p icmp -s 23.158.16.0/24 -j ACCEPT +-A INPUT -p icmp -s 199.167.59.0/24 -j ACCEPT +-A INPUT -p icmp -s 199.170.132.0/24 -j ACCEPT +-A INPUT -p icmp -s 208.68.5.0/24 -j ACCEPT + +# Allow SSH to the mgt address only +-A INPUT -d {{ inventory_hostname }}/{{ INTERNAL_NETWORK_RANGE }} -p tcp -m tcp --dport 22 -j ACCEPT + +# Allow NTP traffic +-A INPUT -d {{ NTP_IP }}/32 -p udp -m udp --dport 123 -j ACCEPT +-A INPUT -d {{ NTP_IP }}/32 -p tcp -m tcp --dport 123 -j ACCEPT +-A INPUT -d {{ NTP_IP }}/32 -p udp -m udp --dport 4460 -j ACCEPT +-A INPUT -d {{ NTP_IP }}/32 -p tcp -m tcp --dport 4460 -j ACCEPT +-A INPUT -d {{ NTP_IP }}/32 -j DROP + +-A FORWARD -d {{ NTP_IP }}/32 -p udp -m udp --dport 123 -j ACCEPT +-A FORWARD -d {{ NTP_IP }}/32 -p tcp -m tcp --dport 123 -j ACCEPT +#-A FORWARD -d {{ NTP_IP }}/32 -j DROP + +COMMIT diff --git a/ansible/roles/requirements.yml b/ansible/roles/requirements.yml new file mode 100644 index 0000000..ae2158b --- /dev/null +++ b/ansible/roles/requirements.yml @@ -0,0 +1,10 @@ +--- +collections: + - name: datadog.dd + version: 5.8.0 + - name: cloud.terraform + version: 3.0.0 + - name: nycmesh.common + source: git+https://github.com/nycmeshnet/nycmesh-ansible.git + type: git + version: main diff --git a/terraform/ansible.tf b/terraform/ansible.tf new file mode 100644 index 0000000..09dbe45 --- /dev/null +++ b/terraform/ansible.tf @@ -0,0 +1,21 @@ +resource "ansible_group" "ntp_mgt_group" { + name = "ntp_mgt" + variables = { + ansible_user = var.mesh_local_user + ansible_ssh_private_key_file = "../terraform/ntp" + ansible_ssh_common_args = "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" + DATADOG_API_KEY = var.datadog_api_key + DATADOG_SITE = var.datadog_site + VM_HOSTNAME = var.hostname + bird_router_id = var.router_id + bird_network = var.bird_network + NTP_IP = var.ntp_ip + INTERNAL_NETWORK_RANGE = var.internal_host_identifier + local_password = var.local_password + } +} + +resource "ansible_host" "ntp_mgt" { + name = var.vm_mgt_ip + groups = [ansible_group.ntp_mgt_group.name] +} diff --git a/terraform/prod1.tfvars b/terraform/prod1.tfvars new file mode 100644 index 0000000..688da1d --- /dev/null +++ b/terraform/prod1.tfvars @@ -0,0 +1,9 @@ +proxmox_node = "nycmesh-713-r640-02" +proxmox_storage_location = "local-lvm" +hostname = "nycmesh-713-ntp-3" +vm_mgt_ip = "10.70.90.204" +vm_nic = "vmbr0v32" +vm_mgt_default_gateway = "10.70.90.1" +router_id = "10.70.90.205" +bird_network = "10.69.0.0/16" +ntp_ip = "10.70.90.123" diff --git a/terraform/prod2.tfvars b/terraform/prod2.tfvars new file mode 100644 index 0000000..1cebfcf --- /dev/null +++ b/terraform/prod2.tfvars @@ -0,0 +1,9 @@ +proxmox_node = "nycmesh-10-r630-01" +proxmox_storage_location = "local-lvm" +vm_nic = "vmbr1" +hostname = "nycmesh-10-ntp-2" +vm_mgt_ip = "10.70.100.58" +vm_mgt_default_gateway = "10.70.100.1" +router_id = "10.70.100.59" +bird_network = "10.69.0.0/16" +ntp_ip = "10.70.90.123" diff --git a/terraform/prod3.tfvars b/terraform/prod3.tfvars new file mode 100644 index 0000000..ddbf2d3 --- /dev/null +++ b/terraform/prod3.tfvars @@ -0,0 +1,8 @@ +proxmox_node = "jon" +proxmox_storage_location = "local-lvm" +hostname = "nycmesh-713-ntp-1" +vm_mgt_ip = "10.70.90.54" +vm_mgt_default_gateway = "10.70.90.1" +router_id = "10.70.90.55" +bird_network = "10.69.0.0/16" +ntp_ip = "10.70.90.123" diff --git a/terraform/provider.tf b/terraform/provider.tf new file mode 100644 index 0000000..cf9dc01 --- /dev/null +++ b/terraform/provider.tf @@ -0,0 +1,24 @@ +terraform { + backend "s3" { + region = "us-east-1" + } + required_providers { + ansible = { + source = "ansible/ansible" + version = "1.3.0" + } + proxmox = { + source = "telmate/proxmox" + version = "3.0.1-rc1" + } + } +} +provider "proxmox" { + # Configuration options + pm_api_url = "https://${var.proxmox_host}:8006/api2/json" + # TODO: Setup cert + pm_tls_insecure = true + pm_debug = true + pm_api_token_id = var.proxmox_token_id + pm_api_token_secret = var.proxmox_token_secret +} diff --git a/terraform/vars.tf b/terraform/vars.tf new file mode 100644 index 0000000..2fdb516 --- /dev/null +++ b/terraform/vars.tf @@ -0,0 +1,99 @@ +variable "proxmox_host" { + type = string + description = "proxmox host" +} + +variable "proxmox_token_id" { + type = string + description = "proxmox token id" + sensitive = true +} + +variable "proxmox_token_secret" { + type = string + description = "proxmox token secret" + sensitive = true +} + +variable "proxmox_node" { + type = string + description = "name of the proxmox node" +} + +variable "proxmox_storage_location" { + type = string + description = "target resource pool on the proxmox server" +} + +variable "hostname" { + type = string + description = "hostname of the lxc" +} + +variable "mesh_proxmox_template_image" { + type = string + description = "name of the template you have already setup in proxmox" + default = "debian-cloud" +} + +variable "mesh_local_user" { + type = string + description = "local user username" + default = "debian" +} + +variable "local_password" { + type = string + description = "password for the local user" + sensitive = true +} + +variable "vm_nic" { + type = string + description = "nic for the vm" + default = "vmbr0" +} + +variable "vm_mgt_ip" { + type = string + description = "IP for the managment interface" +} + +variable "internal_host_identifier" { + type = string + description = "Host identifier for the internal network interface eth0" + default = "24" +} + +variable "vm_mgt_default_gateway" { + type = string + description = "IP of the default gateway of the managment interface" +} + +variable "router_id" { + type = string + description = "IP to use for the router id" +} + +variable "bird_network" { + type = string + description = "ospf network" +} + +variable "ntp_ip" { + type = string + description = "IP to use for the ntp service" + default = "10.69.0.0/16" +} + +variable "datadog_api_key" { + type = string + description = "datadog API key" + sensitive = true +} + +variable "datadog_site" { + type = string + description = "datadog site url" + sensitive = true +} diff --git a/terraform/vm.tf b/terraform/vm.tf new file mode 100644 index 0000000..ff18baa --- /dev/null +++ b/terraform/vm.tf @@ -0,0 +1,56 @@ +resource "proxmox_vm_qemu" "ntp_server" { + name = var.hostname + desc = "NTP server managed IaC via https://github.com/nycmesh/ntp-infra" + target_node = var.proxmox_node + + clone = var.mesh_proxmox_template_image + + cores = 1 + sockets = 1 + memory = 1024 + onboot = true + os_type = "cloud-init" + agent = 1 + cloudinit_cdrom_storage = var.proxmox_storage_location + ciuser = var.mesh_local_user + cipassword = var.local_password + + scsihw = "virtio-scsi-pci" + + disks { + scsi { + scsi0 { + disk { + backup = false + size = 20 + storage = var.proxmox_storage_location + } + } + } + } + + network { + bridge = var.vm_nic + model = "virtio" + } + + ipconfig0 = "ip=${var.vm_mgt_ip}/${var.internal_host_identifier},gw=${var.vm_mgt_default_gateway}" + + ssh_user = "root" + ssh_private_key = file("${path.root}/ntp") + + sshkeys = file("${path.root}/ntp.pub") + + serial { + id = 0 + type = "socket" + } + + tags = "managed_by_iac,ntp" + + lifecycle { + ignore_changes = [ + qemu_os, + ] + } +}