diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/README.md b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/README.md new file mode 100644 index 000000000..62cbe0a04 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/README.md @@ -0,0 +1,46 @@ +# Introduction + +This is the Ansible to stand up a Jenkins Node that will use harvester-installer to create the artifacts for Harvester, for a pipeline in Jenkins that is capable of running AirGap Harvester & AirGap Rancher provisioning over Vagrant that leverages ipxe-examples (airgap version). + +# Setup New Jenkins + +To setup [Jenkins] on a target host. + +1. Make sure [Ansible] is installed. You can install the latest version + of [Ansible] using [Python PIP]. +2. Copy `settings.yml.sample` to `settings.yml`. +3. Edit `settings.yml` by providing the required configurations. The + configurations are self-documented. +4. Edit `inventory.harvester-ci` to make sure the host IP and Ansible user are + correct. **NOTE:** the Ansible user must have SSH access to the CI host and + have sudo permissions. +5. Run the `install_jenkins.ym` playbook. For example: + +```console +ansible-playbook -i inventor.harvester-ci --private-key install_jenkins.yml +``` + +# Add a Jenkins Slave + +To add a Jenkins Slave. + +1. Make sure [Ansible] is installed. You can install the latest version + of [Ansible] using [Python PIP]. +2. Copy `settings.yml.sample` to `settings.yml`. +3. Edit `settings.yml` by providing the required configurations. The + configurations are self-documented. +4. Edit `inventory.harvester-ci` to make sure the host IP and Ansible user are + correct. **NOTE:** the Ansible user must have SSH access to the CI host and + have sudo permissions. +5. Install the required packages on the Jenkins Slave host by running the + `install_jenkins_slave.yml` playbook. For example: + +```console +ansible-playbook -i inventory.harvester-ci --private-key install_jenkins_slave.yml +``` + +6. Manually add the new node from Jenkins Master. + +[Ansible]: https://www.ansible.com/ +[Jenkins]: https://www.jenkins.io/ +[Python PIP]: https://pip.pypa.io/en/stable/ diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/install_jenkins.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/install_jenkins.yml new file mode 100644 index 000000000..6004a4cbe --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/install_jenkins.yml @@ -0,0 +1,31 @@ +--- +- name: Install Jenkins on localhost + hosts: harvester-ci + become: yes + vars: + GITHUB_PROJECT: harvester/harvester-installer + + tasks: + - name: Include settings + include_vars: + file: settings.yml + + - name: Check for supported OS version + fail: + msg: "OS must be openSUSE or Ubuntu" + when: (ansible_distribution|lower != 'opensuse leap' and + ansible_distribution|lower != 'ubuntu') + + - name: Setup PKI + include_role: + name: pki + when: JENKINS_PROXY_ENABLE_SSL + + - name: Setup Nginx proxy + include_role: + name: nginx + when: JENKINS_USE_PROXY + + - name: Install Jenkins + include_role: + name: jenkins diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/install_jenkins_slave.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/install_jenkins_slave.yml new file mode 100644 index 000000000..cde9e8eb4 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/install_jenkins_slave.yml @@ -0,0 +1,19 @@ +--- +- name: Install Jenkins Slave + hosts: harvester-ci-slave + become: yes + + tasks: + - name: Include settings + include_vars: + file: settings.yml + + - name: Check for supported OS version + fail: + msg: "OS must be openSUSE or Ubuntu" + when: (ansible_distribution|lower != 'opensuse leap' and + ansible_distribution|lower != 'ubuntu') + + - name: Install Jenkins Slave + include_role: + name: jenkins_slave diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/inventory.harvester-ci b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/inventory.harvester-ci new file mode 100644 index 000000000..82647b262 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/inventory.harvester-ci @@ -0,0 +1,6 @@ +[harvester-ci] +master ansible_host= ansible_user=root + +[harvester-ci-slave] +slave ansible_host= ansible_user=root + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/defaults/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/defaults/main.yml new file mode 100644 index 000000000..dee50f225 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/defaults/main.yml @@ -0,0 +1,7 @@ +--- +JENKINS_ADMIN_USERNAME: admin +JENKINS_ADMIN_PASSWORD: jenkins +JENKINS_ADMIN_EMAIL: gyee@suse.com +JENKINS_DEV_USERNAME: harvester +JENKINS_DEV_PASSWORD: harvester +JENKINS_PUBLIC_ENDPOINT: http://localhost:8080 diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker.yml new file mode 100644 index 000000000..2f1f116af --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker.yml @@ -0,0 +1,9 @@ +--- +- name: jenkins | install_docker | Install Docker + include_tasks: install_docker_on_{{ ansible_os_family }}.yml + +- name: jenkins | install_docker | Add jenkins user to docker group + user: + name: jenkins + groups: docker + append: yes diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker_on_Debian.yml new file mode 100644 index 000000000..61aedaa7f --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker_on_Debian.yml @@ -0,0 +1,22 @@ +--- +- name: jenkins | install_docker_on_Debian | + Install prerequisite packages for Docker + apt: + name: [apt-transport-https, ca-certificates, curl, software-properties-common] + state: latest + +- name: jenkins | install_docker_on_Debian | Add Docker apt repo key + apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + state: present + +- name: jenkins | install_docker_on_Debian | Add Docker apt repo + apt_repository: + repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" + state: present + update_cache: true + +- name: jenkins | install_docker_on_Debian | Install docker-ce package + apt: + name: docker-ce + state: latest diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker_on_Suse.yml new file mode 100644 index 000000000..a8ffb6b17 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_docker_on_Suse.yml @@ -0,0 +1,11 @@ +--- +- name: jenkins | install_docker_on_Suse | Install Docker packages + community.general.zypper: + name: [docker, python3-docker-compose] + state: latest + +- name: jenkins | install_docker_on_Suse | Enable docker service + service: + name: docker + enabled: yes + state: started diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins.yml new file mode 100644 index 000000000..befd38785 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins.yml @@ -0,0 +1,137 @@ +--- +- name: jenkins | install_jenkins | Set Jenkins bootstrap credential + set_fact: + JENKINS_BOOTSTRAP_USERNAME: jenkins_bootstrap_user + JENKINS_BOOTSTRAP_PASSWORD: "{{ lookup('password', '/dev/null chars=ascii_letters,digits length=30') }}" + +- name: jenkins | install_jenkins | Install Jenkins + include_tasks: install_jenkins_on_{{ ansible_os_family }}.yml + +- name: jenkins | install_jenkins | Wait for Jenkins to be ready + uri: + url: "http://localhost:8080/cli/" + status_code: 200 + register: get_jenkins_cli_result + until: get_jenkins_cli_result.status == 200 + retries: 10 + delay: 20 + +- name: jenkins | install_jenkins | Install Jenkins CLI + get_url: + url: "http://localhost:8080/jnlpJars/jenkins-cli.jar" + dest: "/opt/jenkins-cli.jar" + register: jarfile_get + until: "'OK' in jarfile_get.msg or '304' in jarfile_get.msg or 'file already exists' in jarfile_get.msg" + retries: 5 + delay: 10 + +- name: jenkins | install_jenkins | Create jenkins CLI to install plugins + template: + src: jenkins.j2 + dest: /usr/bin/jenkins + mode: 0755 + force: yes + vars: + JENKINS_AUTH_USERNAME: "{{ JENKINS_BOOTSTRAP_USERNAME }}" + JENKINS_AUTH_PASSWORD: "{{ JENKINS_BOOTSTRAP_PASSWORD }}" + +- name: jenkins | install_jenkins | Install Jenkins plugins + shell: > + /usr/bin/jenkins install-plugin {{ item }} + with_items: + - ansible + - authorize-project + - build-timeout + - blueocean + - bootstrap5-api + - configuration-as-code + - credentials-binding + - docker-workflow + - email-ext + - ghprb + - git + - github-branch-source + - github-oauth + - htmlpublisher + - job-dsl + - ldap + - mailer + - matrix-auth + - pam-auth + - pipeline-github-lib + - pipeline-stage-view + - pipeline-utility-steps + - ssh-slaves + - timestamper + - workflow-aggregator + - workflow-cps + - workflow-job + - ws-cleanup + +- name: jenkins | install_jenkins | Remove Jenkins security bootstrap scripts + file: + path: /var/lib/jenkins/init.groovy.d/basic-security.groovy + state: absent + +- name: jenkins | install_jenkins | Create Jenkins ansible_playbooks directory + file: + path: /var/lib/jenkins/ansible_playbooks + state: directory + owner: jenkins + group: jenkins + mode: 0755 + when: false + +- name: jenkins | install_jenkins | Create Jenkins config as code directory + file: + path: /var/lib/jenkins/casc_configs + state: directory + owner: jenkins + group: jenkins + mode: 0755 + +# supports airgap rancher airgap harvester pipeline +- name: jenkins | install_jenkins | Build Jenkins .ssh directory + ansible.builtin.file: + path: /var/lib/jenkins/.ssh + state: directory + owner: jenkins + group: jenkins + +- name: jenkins | install_jenkins | Build known_hosts file + ansible.builtin.file: + path: /var/lib/jenkins/.ssh/known_hosts + state: present + +- name: jenkins | install_jenkins | Create Jenkins config as code file + template: + src: config_jenkins_as_code.yaml.j2 + dest: /var/lib/jenkins/casc_configs/config_jenkins_as_code.yaml + owner: jenkins + group: jenkins + mode: 0644 + +- name: jenkins | install_jenkins | Copy pipeline jobs + template: + src: "{{ item }}.j2" + dest: "/var/lib/jenkins/casc_configs/{{ item }}" + owner: jenkins + group: jenkins + mode: 0755 + with_items: + - airgap_rancher_airgap_harvester_pipelinejob.groovy + +- name: jenkins | install_jenkins | Restart jenkins + service: + name: jenkins + state: restarted + +- name: jenkins | install_jenkins | Create jenkins CLI + template: + src: jenkins.j2 + dest: /usr/bin/jenkins + mode: 0755 + force: yes + vars: + JENKINS_AUTH_USERNAME: "{{ JENKINS_ADMIN_USERNAME }}" + JENKINS_AUTH_PASSWORD: "{{ JENKINS_ADMIN_PASSWORD }}" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins_on_Debian.yml new file mode 100644 index 000000000..977fe4def --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins_on_Debian.yml @@ -0,0 +1,85 @@ +--- +- name: jenkins | install_jenkins_on_Debian | + Install prerequisite packages for Jenkins + apt: + name: [ca-certificates, openjdk-11-jdk, curl, apt-transport-https, gnupg, + python3, python3-pip, figlet, sshpass] + state: latest + update_cache: yes + +- name: jenkins | install_jenkins_on_Debian | Install Ansible + pip: + name: [pip, ansible] + state: latest + +- name: jenkins | install_jenkins_on_Debian | Add Jenkins apt repo key + apt_key: + url: https://pkg.jenkins.io/debian-stable/jenkins.io.key + state: present + +- name: jenkins | install_jenkins_on_Debian | Add Jenkins apt repo + apt_repository: + repo: 'deb http://pkg.jenkins.io/debian-stable binary/' + state: present + update_cache: true + +- name: jenkins | install_jenkins_on_Debian | Install Jenkins package + apt: + name: jenkins + state: latest + +- name: jenkins | install_jenkins_on_Debian | Stop jenkins service + service: + name: jenkins + state: stopped + +# NOTE(gyee): running this tasks repeatedly will add the same Java args +# multiple time. But that should be fine so as long as the values are +# consistent. However, if we are using this task to reconfigure any of the +# args with a different value then the result may not be correct. +# If we ever need to reconfigure Jenkins, it may be best to just do re-install. +- name: jenkins | install_jenkins_on_Debian | Add Java options + lineinfile: + path: /etc/default/jenkins + regexp: '^(JAVA_ARGS=\")(.*)$' + line: '\1-Djenkins.install.runSetupWizard=false -Dcasc.jenkins.config=/var/lib/jenkins/casc_configs \2' + state: present + backrefs: yes + mode: 0644 + +- name: jenkins | install_jenkins_on_Debian | Bind to localhost + lineinfile: + path: /etc/default/jenkins + regexp: '^(JENKINS_ARGS=\")(.*)$' + line: '\1--httpListenAddress=127.0.0.1 \2' + state: present + backrefs: yes + mode: 0644 + when: JENKINS_USE_PROXY + +- name: jenkins | install_jenkins_on_Debian | + Create init.groovy.d to bootstrap Jenkins + file: + path: /var/lib/jenkins/init.groovy.d + state: directory + owner: jenkins + group: jenkins + mode: 0775 + +- name: jenkins | install_jenkins_on_Debian | + Configure Jenkins bootstrap credential + template: + src: basic-security.groovy.j2 + dest: /var/lib/jenkins/init.groovy.d/basic-security.groovy + owner: jenkins + group: jenkins + mode: 0755 + +- name: jenkins | install_jenkins_on_Debian | Restart Jenkins + systemd: + name: jenkins + state: restarted + +- name: jenkins | install_jenkins_on_Debian | Enable Jenkins port 8080 + shell: ufw allow 8080 + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins_on_Suse.yml new file mode 100644 index 000000000..76e02e802 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_jenkins_on_Suse.yml @@ -0,0 +1,71 @@ +--- +- name: jenkins | install_jenkins_on_Suse | Install essential packages + community.general.zypper: + name: [python3, python3-pip, figlet] + state: latest + +- name: jenkins | install_jenkins_on_Suse | Install Ansible + pip: + name: [pip, ansible] + state: latest + +- name: jenkins | install_jenkins_on_Suse | Add Jenkins repo for openSUSE + community.general.zypper_repository: + name: jenkins + repo: http://pkg.jenkins.io/opensuse-stable/ + auto_import_keys: yes + runrefresh: yes + state: present + +- name: jenkins | install_jenkins_on_Suse | Install jenkins package + community.general.zypper: + name: jenkins + +# NOTE(gyee): running this tasks repeatedly will add the same Java args +# multiple time. But that should be fine so as long as the values are +# consistent. However, if we are using this task to reconfigure any of the +# args with a different value then the result may not be correct. +# If we ever need to reconfigure Jenkins, it may be best to just do re-install. +- name: jenkins | install_jenkins_on_Suse | Add Java options + lineinfile: + path: /etc/sysconfig/jenkins + regexp: '^(JENKINS_JAVA_OPTIONS=\")(.*)$' + line: '\1-Djenkins.install.runSetupWizard=false -Dcasc.jenkins.config=/var/lib/jenkins/casc_configs \2' + state: present + backrefs: yes + mode: 0644 + +- name: jenkins | install_jenkins_on_Suse | + Create init.groovy.d to bootstrap Jenkins + file: + path: /var/lib/jenkins/init.groovy.d + state: directory + owner: jenkins + group: jenkins + mode: 0775 + +- name: jenkins | install_jenkins_on_Suse | + Configure Jenkins bootstrap credential + template: + src: basic-security.groovy.j2 + dest: /var/lib/jenkins/init.groovy.d/basic-security.groovy + owner: jenkins + group: jenkins + mode: 0755 + +# NOTE(gyee): since jenkins process is running in the jenkins user context, +# it can't use the default HTTP port 80 +- name: jenkins | install_jenkins_on_Suse | Allow HTTP port 8080 + shell: | + firewall-cmd --zone=public --add-port=8080/tcp --permanent + firewall-cmd --zone=public --add-service=http --permanent + firewall-cmd --reload + +- name: jenkins | install_jenkins_on_Suse | Generate jenkins service + shell: > + systemctl enable jenkins + +- name: jenkins | install_jenkins_on_Suse | Start jenkins + systemd: + name: jenkins + state: restarted diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant.yml new file mode 100644 index 000000000..4bc95337d --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant.yml @@ -0,0 +1,37 @@ +--- +- name: jenkins | install_vagrant | + Get the latest vagrant release from github tags + shell: > + curl -s https://api.github.com/repos/hashicorp/vagrant/tags | + grep '"name":' | + sed 's/.*"v//g' | + sed 's/",//g' | + sort -t. -k 1,1nr -k 2,2nr -k 3,3nr | + head -n 1 + register: vagrant_latest_version_lookup_result + until: vagrant_latest_version_lookup_result.stdout + retries: 5 + delay: 30 + +- name: jenkins | install_vagrant | Check latest vagrant version + fail: + msg: "Failed to lookup latest vagrant version: {{ vagrant_latest_version_lookup_result.stdout }}" + when: not vagrant_latest_version_lookup_result.stdout | trim + +- name: jenkins | install_vagrant | Set latest vagrant version + set_fact: + LATEST_VAGRANT_VERSION: "{{ vagrant_latest_version_lookup_result.stdout }}" + +- name: jenkins | install_vagrant | Display latest vagrant version + debug: + msg: "Latest vagrant release is {{ LATEST_VAGRANT_VERSION }}" + +- name: jenkins | install_vagrant | Install Vagrant + include_tasks: install_vagrant_on_{{ ansible_os_family }}.yml + +- name: jenkins | install_vagrant | + Install vagrant-libvirt plugin for jenkins user + become: true + become_user: jenkins + shell: > + vagrant plugin install vagrant-libvirt diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant_on_Debian.yml new file mode 100644 index 000000000..68b83eac1 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant_on_Debian.yml @@ -0,0 +1,21 @@ +--- +- name: jenkins | install_vagrant_on_Debian | Install vagrant package + apt: + deb: https://releases.hashicorp.com/vagrant/{{ LATEST_VAGRANT_VERSION }}/vagrant_{{ LATEST_VAGRANT_VERSION }}_x86_64.deb + +- name: jenkins | install_vagrant_on_Debian | Install build essentials + apt: + name: build-essential + state: latest + +- name: jenkins | install_vagrant_on_Debian | Install KVM + apt: + name: [qemu-kvm, libvirt-daemon-system, libvirt-clients, bridge-utils, virtinst, virt-manager, libvirt-dev] + state: latest + +- name: jenkins | install_vagrant_on_Debian | Add jenkins user to libvirt group + user: + name: jenkins + groups: libvirt + append: yes + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant_on_Suse.yml new file mode 100644 index 000000000..17f90e63b --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/install_vagrant_on_Suse.yml @@ -0,0 +1,22 @@ +--- +- name: jenkins | install_vagrant_on_Suse | Install vagrant package + community.general.zypper: + name: https://releases.hashicorp.com/vagrant/{{ LATEST_VAGRANT_VERSION }}/vagrant_{{ LATEST_VAGRANT_VERSION }}_x86_64.rpm + state: present + +- name: jenkins | install_vagrant_on_Suse | Install KVM + community.general.zypper: + name: [patterns-openSUSE-kvm_server, patterns-server-kvm_tools] + type: pattern + state: latest + +- name: jenkins | install_vagrant_on_Suse | Install bridge-utils + community.general.zypper: + name: bridge-utils + state: latest + +- name: jenkins | install_vagrant_on_Suse | Enable libvirtd service + service: + name: libvirtd + state: started + enabled: yes diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/main.yml new file mode 100644 index 000000000..b1c51b58e --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: jenkins | main | Install Jenkins + include_tasks: install_jenkins.yml + +- name: jenkins | main | Install Docker + include_tasks: install_docker.yml + +- name: jenkins | main | Install Vagrant and vagrant-libvirt plugin + include_tasks: install_vagrant.yml + +- name: jenkins | main | Reboot + reboot: + msg: "Reboot for the permission changes to take effect" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/airgap_rancher_airgap_harvester_pipelinejob.groovy.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/airgap_rancher_airgap_harvester_pipelinejob.groovy.j2 new file mode 100644 index 000000000..2b4eaa3b3 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/airgap_rancher_airgap_harvester_pipelinejob.groovy.j2 @@ -0,0 +1,296 @@ + +pipelineJob('airgap-harvester-airgap-rancher-vagrant-installation-test') { + description('Test airgapped Rancher with airgapped Harvester') + properties { + githubProjectUrl('https://github.com/irishgordo/harvester-installer') + } + logRotator { + numToKeep(-1) + daysToKeep(10) + } + parameters { + booleanParam('keep_environment', false, 'Keep the Vagrant environment around after the test had finish.') + booleanParam('slack_notify', true, 'Send notifications to Slack') + stringParam('WORKSPACE', '/var/lib/jenkins/workspace/airgap-harvester-airgap-rancher-vagrant-installation-test', 'Required: Provide A Workspace') + stringParam('harvester_cluster_nodes', '4', 'default number of nodes - currently just used in dockerized testing') + stringParam('harvester_admin_user', 'admin', 'default admin user for harvester - currently just used in dockerized testing') + stringParam('harvester_admin_password', 'testtesttest', 'default admin user password for harvester - currently just used in dockerized testing') + stringParam('harvester_vip_ip', '192.168.2.131', 'default harvester cluster vip - currently just used in dockerized testing') + stringParam('harvester_node_user', 'rancher', 'default harvester node user - currently just used in dockerized testing') + stringParam('harvester_node_password', 'p@ssword', 'default harvester node password - currently just used in dockerized testing') + stringParam('rancher_install_domain', 'rancher-vagrant-vm.local', 'default rancher node url - currently just used in dockerized testing') + stringParam('rancher_node_ip', '192.168.2.34', 'default rancher and registry node ip - currently just used in dockerized testing') + stringParam('rancher_node_registry_domain', 'myregistry.local', 'default docker registry domain - currently just used in dockerized testing') + stringParam('wait_timeout', '300', 'default wait-timeout in seconds') + + } + triggers { + cron("H 8 * * *") + } + definition { + cps { + script( + ''' +def AdjustBuildURL(String buildurl = 'default', String endpointinfo = 'default'){ + echo "this is AdjustBuildURL Groovy script function..." + echo "this is buildurl: ${buildurl}" + echo "this is endpointinfo: ${endpointinfo}" + String localHost = "localhost" + def result = "" + if (buildurl.contains(localHost)) { + echo "hits if conditional, buildurl does contain localhost" + String actualbuildurl = buildurl.replaceAll(localHost, endpointinfo) + result = actualbuildurl + echo "this is result that will be returned: ${result}" + return result + } else { + echo "hits else block, buildurl does not contain localhost" + result = buildurl + echo "this is result that will be returned: ${result}" + return result + } +} +boolean PROVISIONINGPASSED = false +boolean DOCKERIZEDTESTREPOBUILDPASSED = false +boolean DOCKERIZEDAPITESTSPASSED = false +String DOCKERFAILUREAPI = "" +String DOCKERRESULT = "" +pipeline { + agent any + options{ + timeout(time: 14, unit: 'HOURS') + disableConcurrentBuilds() + } + environment{ + SLACK_CREDENTIALS = credentials('jenkins-slack-token') + JENKINS_ENDPOINT_IP = credentials('jenkins-endpoint-ip') + } + stages { + stage('tell Slack we are starting the pipeline'){ + steps{ + script{ + if(params.slack_notify){ + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + echo 'sending message to slack, letting channel know build has been started...' + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\", \"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_starting_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , first-stage, running... \" } } ]}'""" + } else { + echo 'slack notifications currently turned off...' + } + } + } + } + stage('checkout harvester-installer master branch (temporarily set to featurebranch)') { + steps { + dir('harvester-installer') { + checkout([$class: 'GitSCM', branches: [[name: 'feat/airgap-harvester-airgap-rancher-install-test' ]], userRemoteConfigs: [[url: 'https://github.com/irishgordo/harvester-installer.git']]]) + } + script{ + if(params.slack_notify){ + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + echo 'sending message to slack, letting channel know build has grabbed latest from master branch...' + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , grabbed latest master branch changes... \" } } ]}'""" + } else { + echo 'slack notifications currently turned off...' + } + } + } + } + stage('checkout harvester/tests') { + steps { + script{ + echo 'cloning down tests repo...' + dir("${params['WORKSPACE']}"){ + sh "git clone https://github.com/harvester/tests.git" + echo 'cloned tests repo successfully...' + dir('tests'){ + def baseSettingsYaml = readYaml file: 'config.yml' + sh "echo 'initial YAML Settings that will be parsed and overwritten:'" + print baseSettingsYaml + baseSettingsYaml.endpoint = "https://" + params.harvester_vip_ip + baseSettingsYaml.username = params.harvester_admin_user + baseSettingsYaml.password = params.harvester_admin_password + baseSettingsYaml.harvester_cluster_nodes = params.harvester_cluster_nodes.trim() as Integer + baseSettingsYaml['wait-timeout'] = params.wait_timeout.trim() as Integer + sh "echo 'modified YAML Settings that will be written to config.yml for tests:'" + print baseSettingsYaml + def newBaseSettingsYaml = writeYaml file: 'config.yml', data: baseSettingsYaml, overwrite: true + sh "make build-docker-backend-tests-image" + } + DOCKERIZEDTESTREPOBUILDPASSED = true + } + if(params.slack_notify){ + echo 'sending message to slack, letting channel know jenkins job about dockerized tests repo...' + if(DOCKERIZEDTESTREPOBUILDPASSED){ + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Jenkins-Vagrant-Airgap - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"$urlToUse , pulled harvester/tests and built harvester/tests docker image: *SUCCESS* \" } } ]}'""" + } else { + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Jenkins-Vagrant-AirGap - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"$urlToUse , pulled harvester/tests and built harvester/tests docker image: *FAIULRE* \" } } ]}'""" + } + } else { + echo 'slack notifications currently turned off...' + } + } + } + } + stage('temp: add upstream repo and pull changes'){ + steps{ + dir('harvester-installer'){ + script{ + echo 'temporarily pulling upstream harvester-installer - demoing test environment...' + try { + def addUpstream = sh(script: "git remote add upstream https://github.com/harvester/harvester-installer.git", returnStdout: true).trim() + } catch (Exception e) { + echo 'Exception occurred: ' + e.toString() + } + def fetchUpstream = sh(script: "git fetch upstream", returnStdout: true).trim() + def fetchRemotes = sh(script: "git remote -v", returnStdout: true).trim() + echo 'now we will grab harvester master branch, switch to the master branch on fork, pull down master branch from upstream harvester, then checkout the fork feature branch and merge the origin master that has been updated within it to keep the run up to date...' + def pullUpstreamMaster = sh(script: "git checkout origin/master && git pull upstream master && git checkout feat/airgap-harvester-airgap-rancher-install-test && git merge origin/master", returnStdout: true).trim() + } + } + } + } + stage('Provision an instance of Airgapped Rancher (k3s) with 4 Node Airgapped Harvester') { + steps { + script { + if(params.slack_notify){ + echo 'sending message to slack, letting channel know jenkins job about starting provisioning...' + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\", \"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_started_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , 6ish-hr long provisioning process for 4 Airgapped Harvester Nodes, 1 Airgapped Rancher (on k3s) with Docker-Registry has: *STARTED* \" } } ]}'""" + } else { + echo 'slack notifications currently turned off...' + } + ansiblePlaybook extras: "-e WORKSPACE=${env.WORKSPACE} -e harvester_installer_repo_name=irishgordo/harvester-installer", playbook: "${env.WORKSPACE}/harvester-installer/ci/run_airgap_rancher_and_airgap_harvester_install_test.yml" + PROVISIONINGPASSED = true + } + } + } + stage('Run dockerized harvester/tests API tests against 4 Node Airgapped Harvester'){ + options { + timeout(time: 1, unit: "HOURS") + } + steps { + dir('tests'){ + script { + try { + sh """docker run --name backend-tests-api -v $WORKSPACE/tests:/test-output -t --add-host ${params.rancher_install_domain}:${params.rancher_node_ip} --add-host ${params.rancher_node_registry_domain}:${params.rancher_node_ip} --rm harvester-tests-backend:latest pytest -v -k 'not verify_host_maintenance_mode and not host_mgmt_maintenance_mode and not host_reboot_maintenance_mode and not host_poweroff_state and not create_images_using_terraform and not create_keypairs_using_terraform and not create_edit_network and not create_network_using_terraform and not create_volume_using_terraform and not test_create_with_reuse_display_name and not test_create_with_invalid_url and not test_maintenance_mode' --junitxml="/test-output/result_harvester_api_latest.xml" -m 'not delete_host' harvester_e2e_tests/apis""" + DOCKERIZEDAPITESTSPASSED = true + } catch (err) { + echo "failed within docker image inside command on harvester testing api stage" + DOCKERFAILUREAPI = err.getMessage() + echo err.getMessage() + } + } + } + } + } + } + post { + always { + dir("${env.WORKSPACE}"){ + script{ + if(PROVISIONINGPASSED){ + if(params.slack_notify){ + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + echo 'sending message to slack, letting channel know provisioning passed...' + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester: *SUCCESS* \" } } ]}'""" + } else { + echo 'slack notifications currently turned off...' + } + } else { + if(params.slack_notify){ + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + echo 'sending message to slack, letting channel know provisioning failed...' + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester: *FAIULRE* \" } } ]}'""" + } else { + echo 'slack notifications currently turned off...' + } + } + if(DOCKERIZEDAPITESTSPASSED){ + if(params.slack_notify){ + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + echo 'sending message to slack, letting channel know provisioning passed...' + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester TEST API STAGE: *SUCCESS* \" } } ]}'""" + } else { + echo 'slack notifications currently turned off...' + } + } else { + if(params.slack_notify){ + String urlToUse = AdjustBuildURL("${BUILD_URL}", "${JENKINS_ENDPOINT_IP_PSW}") + echo 'sending message to slack, letting channel know provisioning failed...' + sh """curl --request POST --url https://hooks.slack.com/services/${SLACK_CREDENTIALS_PSW} --header "Content-Type: application/json; charset=utf-8" --data '{\"user\": \"Harvester Jenkins\",\"as_user\": \"true\",\"channel\": \"#proj-harvester-build\",\"blocks\": [ { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - *status:* *_finished_*\" } }, { \"type\": \"divider\" }, { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"Test Instance of _Harvester-Installer CI_ - $urlToUse , provisioning airgap-rancher & airgap-harvester TEST API STAGE: *FAIULRE* \" } } ]}'""" + } else { + echo 'slack notifications currently turned off...' + } + } + if(fileExists("ipxe-examples/vagrant-pxe-airgap-harvester/")){ + if(params.keep_environment){ + echo 'keeping environment...' + } else { + echo 'discarding environment...' + sh "cd ipxe-examples/vagrant-pxe-airgap-harvester/ && ls -alh && vagrant destroy -f" + } + } else { + echo 'no ipxe-examples/vagrant-pxe-airgap-harvester/ folder found...' + sh "ls -alh" + } + echo 'destroying docker image, so next run will re-make with fresh start...' + if(fileExists("tests/")){ + dir('tests'){ + if(fileExists("/")){ + if(fileExists("Makefile")){ + try{ + sh "make destroy-docker-backend-tests-image" + } catch (err) { + echo err.getMessage() + } + } + try { + junit '*.xml' + } catch (err) { + echo 'could not find any xml pytest files...' + echo err.getMessage() + } + } else { + echo 'nothing here...' + } + } + } else { + echo 'no tests folder found...' + sh "ls -alh" + } + echo 'destroying tests folder, so next run will always get the latest...' + try { + sh "cd ${env.WORKSPACE} && rm -rf tests" + } catch (err) { + echo 'issue removing tests folder in workspace...' + echo err.getMessage() + } + echo 'kill running backend-tests-api container...' + try { + sh "docker kill backend-tests-api" + } catch(err) { + echo 'no backend-tests-api to kill..' + echo err.getMessage() + } + echo 'removing the ipxe-examples folder, so next run will always get the latest...' + try { + sh "cd ${env.WORKSPACE} && rm -rf ipxe-examples" + } catch (err) { + echo 'issue removing ipxe-examples folder in workspace...' + echo err.getMessage() + } + echo 'folders should be cleaned up...' + sh "ls -alh" + } + } + } + } +} + ''' + ) + sandbox() + } + } +} \ No newline at end of file diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/basic-security.groovy.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/basic-security.groovy.j2 new file mode 100644 index 000000000..62ae503b4 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/basic-security.groovy.j2 @@ -0,0 +1,28 @@ +#!groovy +import hudson.security.* +import jenkins.model.* + +def instance = Jenkins.getInstance() +def hudsonRealm = new HudsonPrivateSecurityRealm(false) +def users = hudsonRealm.getAllUsers() +users_s = users.collect { it.toString() } + +// Create the admin user account if it doesn't already exist. +if ("{{ JENKINS_BOOTSTRAP_USERNAME }}" in users_s) { + println "Bootstrap user {{ JENKINS_BOOTSTRAP_USERNAME }} already exists - updating password" + + def user = hudson.model.User.get('{{ JENKINS_BOOTSTRAP_USERNAME }}'); + def password = hudson.security.HudsonPrivateSecurityRealm.Details.fromPlainPassword('{{ JENKINS_BOOTSTRAP_PASSWORD }}') + user.addProperty(password) + user.save() +} +else { + println "--> creating local bootstrap user: {{ JENKINS_BOOTSTRAP_USERNAME }}" + + hudsonRealm.createAccount('{{ JENKINS_BOOTSTRAP_USERNAME }}', '{{ JENKINS_BOOTSTRAP_PASSWORD }}') + instance.setSecurityRealm(hudsonRealm) + + def strategy = new FullControlOnceLoggedInAuthorizationStrategy() + instance.setAuthorizationStrategy(strategy) + instance.save() +} diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/config_jenkins_as_code.yaml.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/config_jenkins_as_code.yaml.j2 new file mode 100644 index 000000000..276038953 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/config_jenkins_as_code.yaml.j2 @@ -0,0 +1,60 @@ +# see https://www.digitalocean.com/community/tutorials/how-to-automate-jenkins-setup-with-docker-and-jenkins-configuration-as-code +jenkins: + systemMessage: "Jenkins configured automatically" + securityRealm: + local: + allowsSignup: false + users: + - id: {{ JENKINS_ADMIN_USERNAME }} + password: {{ JENKINS_ADMIN_PASSWORD }} + - id: {{ JENKINS_DEV_USERNAME }} + password: {{ JENKINS_DEV_PASSWORD }} + authorizationStrategy: + globalMatrix: + permissions: + - "Job/Read:{{ JENKINS_DEV_USERNAME }}" + - "Overall/Administer:{{ JENKINS_ADMIN_USERNAME }}" + - "Overall/Read:authenticated" + - "Run/Replay:{{ JENKINS_DEV_USERNAME }}" + remotingSecurity: + enabled: true + + numExecutors: 1 + +credentials: + system: + domainCredentials: + - credentials: + - usernamePassword: + scope: "GLOBAL" + username: "{{ GITHUB_CI_USERNAME }}" + password: "{{ GITHUB_CI_PASSWORD }}" + id: "for-harvester-ci" + - usernamePassword: + scope: "GLOBAL" + username: "SLACK_WEBHOOK_USER" + password: "{{ SLACK_WEBHOOK_TOKEN }}" + id: "jenkins-slack-token" + - usernamePassword: + scope: "GLOBAL" + username: "JENKINS_ENDPOINT_IP" + password: "{{ JENKINS_ENDPOINT_IP }}" + id: "jenkins-endpoint-ip" + +security: + queueItemAuthenticator: + authenticators: + - global: + strategy: "triggeringUsersAuthorizationStrategy" + +jobs: + - file: /var/lib/jenkins/casc_configs/airgap_rancher_airgap_harvester_pipelinejob.groovy + +unclassified: + location: + url: {{ JENKINS_PUBLIC_ENDPOINT }} + adminAddress: {{ JENKINS_ADMIN_EMAIL }} + + ghprbTrigger: + githubAuth: + - credentialsId: "for-harvester-ci" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/jenkins.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/jenkins.j2 new file mode 100644 index 000000000..cab1b2055 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins/templates/jenkins.j2 @@ -0,0 +1,3 @@ +#!/bin/bash + +java -jar /opt/jenkins-cli.jar -auth {{ JENKINS_AUTH_USERNAME }}:{{ JENKINS_AUTH_PASSWORD }} -s http://127.0.0.1:8080 $@ diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/defaults/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/defaults/main.yml new file mode 100644 index 000000000..70559d38a --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/defaults/main.yml @@ -0,0 +1,3 @@ +--- +JENKINS_USER_PASSWORD: jenkins + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker.yml new file mode 100644 index 000000000..2f1f116af --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker.yml @@ -0,0 +1,9 @@ +--- +- name: jenkins | install_docker | Install Docker + include_tasks: install_docker_on_{{ ansible_os_family }}.yml + +- name: jenkins | install_docker | Add jenkins user to docker group + user: + name: jenkins + groups: docker + append: yes diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker_on_Debian.yml new file mode 100644 index 000000000..46f49f642 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker_on_Debian.yml @@ -0,0 +1,23 @@ +--- +- name: jenkins | install_docker_on_Debian | + Install prerequisite packages for Docker + apt: + name: [openjdk-11-jdk, apt-transport-https, ca-certificates, curl, software-properties-common] + state: latest + update_cache: yes + +- name: jenkins | install_docker_on_Debian | Add Docker apt repo key + apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + state: present + +- name: jenkins | install_docker_on_Debian | Add Docker apt repo + apt_repository: + repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" + state: present + update_cache: true + +- name: jenkins | install_docker_on_Debian | Install docker-ce package + apt: + name: docker-ce + state: latest diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker_on_Suse.yml new file mode 100644 index 000000000..a8ffb6b17 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_docker_on_Suse.yml @@ -0,0 +1,11 @@ +--- +- name: jenkins | install_docker_on_Suse | Install Docker packages + community.general.zypper: + name: [docker, python3-docker-compose] + state: latest + +- name: jenkins | install_docker_on_Suse | Enable docker service + service: + name: docker + enabled: yes + state: started diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant.yml new file mode 100644 index 000000000..4bc95337d --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant.yml @@ -0,0 +1,37 @@ +--- +- name: jenkins | install_vagrant | + Get the latest vagrant release from github tags + shell: > + curl -s https://api.github.com/repos/hashicorp/vagrant/tags | + grep '"name":' | + sed 's/.*"v//g' | + sed 's/",//g' | + sort -t. -k 1,1nr -k 2,2nr -k 3,3nr | + head -n 1 + register: vagrant_latest_version_lookup_result + until: vagrant_latest_version_lookup_result.stdout + retries: 5 + delay: 30 + +- name: jenkins | install_vagrant | Check latest vagrant version + fail: + msg: "Failed to lookup latest vagrant version: {{ vagrant_latest_version_lookup_result.stdout }}" + when: not vagrant_latest_version_lookup_result.stdout | trim + +- name: jenkins | install_vagrant | Set latest vagrant version + set_fact: + LATEST_VAGRANT_VERSION: "{{ vagrant_latest_version_lookup_result.stdout }}" + +- name: jenkins | install_vagrant | Display latest vagrant version + debug: + msg: "Latest vagrant release is {{ LATEST_VAGRANT_VERSION }}" + +- name: jenkins | install_vagrant | Install Vagrant + include_tasks: install_vagrant_on_{{ ansible_os_family }}.yml + +- name: jenkins | install_vagrant | + Install vagrant-libvirt plugin for jenkins user + become: true + become_user: jenkins + shell: > + vagrant plugin install vagrant-libvirt diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant_on_Debian.yml new file mode 100644 index 000000000..68b83eac1 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant_on_Debian.yml @@ -0,0 +1,21 @@ +--- +- name: jenkins | install_vagrant_on_Debian | Install vagrant package + apt: + deb: https://releases.hashicorp.com/vagrant/{{ LATEST_VAGRANT_VERSION }}/vagrant_{{ LATEST_VAGRANT_VERSION }}_x86_64.deb + +- name: jenkins | install_vagrant_on_Debian | Install build essentials + apt: + name: build-essential + state: latest + +- name: jenkins | install_vagrant_on_Debian | Install KVM + apt: + name: [qemu-kvm, libvirt-daemon-system, libvirt-clients, bridge-utils, virtinst, virt-manager, libvirt-dev] + state: latest + +- name: jenkins | install_vagrant_on_Debian | Add jenkins user to libvirt group + user: + name: jenkins + groups: libvirt + append: yes + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant_on_Suse.yml new file mode 100644 index 000000000..17f90e63b --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/install_vagrant_on_Suse.yml @@ -0,0 +1,22 @@ +--- +- name: jenkins | install_vagrant_on_Suse | Install vagrant package + community.general.zypper: + name: https://releases.hashicorp.com/vagrant/{{ LATEST_VAGRANT_VERSION }}/vagrant_{{ LATEST_VAGRANT_VERSION }}_x86_64.rpm + state: present + +- name: jenkins | install_vagrant_on_Suse | Install KVM + community.general.zypper: + name: [patterns-openSUSE-kvm_server, patterns-server-kvm_tools] + type: pattern + state: latest + +- name: jenkins | install_vagrant_on_Suse | Install bridge-utils + community.general.zypper: + name: bridge-utils + state: latest + +- name: jenkins | install_vagrant_on_Suse | Enable libvirtd service + service: + name: libvirtd + state: started + enabled: yes diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/main.yml new file mode 100644 index 000000000..add14ca19 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/jenkins_slave/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- name: jenkins | main | Install Docker + include_tasks: install_docker.yml + +- name: jenkins | main | Install Vagrant and vagrant-libvirt plugin + include_tasks: install_vagrant.yml + +- name: jenkins | main | Create jenkins user + user: + name: jenkins + password: "{{ JENKINS_USER_PASSWORD | password_hash('sha512') }}" + shell: /bin/bash + home: /var/lib/jenkins + groups: jenkins,libvirt,docker + append: yes + +- name: jenkins | main | Enable SSH password auth for jenkins user + blockinfile: + path: /etc/ssh/sshd_config + block: | + Match User jenkins + PasswordAuthentication yes + +- name: jenkins | main | Reboot + reboot: + msg: "Reboot for the permission changes to take effect" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/defaults/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/defaults/main.yml new file mode 100644 index 000000000..4759b56eb --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/defaults/main.yml @@ -0,0 +1,11 @@ +--- +nginx_use_ssl: "{{ JENKINS_PROXY_ENABLE_SSL | default(True) }}" +ip: "{{ server_ip | default(ansible_host) }}" + +server_ssl_key_type: rsa +server_ssl_key_size: 2048 +server_ssl_private_key_file: /etc/nginx/server_ssl_private_key.pem +server_ssl_cert_file: /etc/nginx/server_ssl_cert.pem +server_ssl_ca_file: /etc/nginx/ca.pem +server_ssl_dhparams_file: /etc/nginx/dhparams.pem +ssl_server_san: IP:{{ ip }},DNS:ci.harvesterhci.io diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/install_nginx_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/install_nginx_on_Debian.yml new file mode 100644 index 000000000..5aca2bed5 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/install_nginx_on_Debian.yml @@ -0,0 +1,6 @@ +--- +- name: nginx | install_nginx_on_Debian | Install nginx package + apt: + name: ['nginx'] + state: latest + update_cache: yes diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/install_nginx_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/install_nginx_on_Suse.yml new file mode 100644 index 000000000..61f2a6461 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/install_nginx_on_Suse.yml @@ -0,0 +1,6 @@ +--- +- name: nginx | install_nginx_on_Suse | Install nginx package + community.general.zypper: + name: ['nginx'] + state: latest + update_cache: yes diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/main.yml new file mode 100644 index 000000000..eabd07629 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/main.yml @@ -0,0 +1,44 @@ +--- +- name: nginx | main | Include OS family vars + include_vars: "{{ item }}" + with_first_found: + - "{{ ansible_os_family }}.yml" + - "{{ ansible_os_family | lower }}.yml" + - "default.yml" + +- name: nginx | main | Include OS distribution vars + include_vars: "{{ item }}" + with_first_found: + - "{{ ansible_distribution | replace(' ', '_') }}.yml" + - "{{ ansible_distribution | lower | replace(' ', '_') }}.yml" + - "{{ ansible_distribution | replace(' ', '_') }}.yml" + - "default.yml" + +- name: nginx | main | Install nginx package + include_tasks: install_nginx_on_{{ ansible_os_family }}.yml + +- name: nginx | main | Setup SSL certificates + include_tasks: setup_ssl.yml + when: nginx_use_ssl + +- name: nginx | main | Create nginx.conf file + template: + src: nginx.conf.j2 + dest: /etc/nginx/nginx.conf + +- name: nginx | main | Create vhosts.d + file: + path: /etc/nginx/vhosts.d + state: directory + recurse: yes + +- name: nginx | main | Create jenkins vhost file + template: + src: jenkins.conf.j2 + dest: /etc/nginx/vhosts.d/jenkins.conf + +- name: nginx | main | Start nginx service + systemd: + name: nginx + state: started + enabled: yes diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/setup_ssl.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/setup_ssl.yml new file mode 100644 index 000000000..1dfbdc2b9 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/tasks/setup_ssl.yml @@ -0,0 +1,54 @@ +--- +- name: nginx | setup_ssl | Generate SSL private key + include_role: + name: pki + tasks_from: generate_private_key + vars: + key_type: "{{ server_ssl_key_type }}" + file_owner: root + file_group: root + file_mode: 0600 + key_size: "{{ server_ssl_key_size }}" + key_file: "{{ server_ssl_private_key_file }}" + +- name: nginx | setup_ssl | Generate cert request (CSR) + include_role: + name: pki + tasks_from: generate_server_cert_req + vars: + key_file: "{{ server_ssl_private_key_file }}" + +- name: nginx | setup_ssl | Generate server SSL certificate + include_role: + name: pki + tasks_from: issue_server_cert + vars: + server_cert_file: "{{ server_ssl_cert_file }}" + +- name: nginx | setup_ssl | Copy trusted CA certificates + include_role: + name: pki + tasks_from: copy_ca_certs_chain + vars: + file_owner: root + file_group: root + file_mode: 0644 + ca_certs_file: "{{ server_ssl_ca_file }}" + +- name: nginx | setup_ssl | Install trusted CA certificates + include_role: + name: pki + tasks_from: install_ca_certs_chain + +- name: nginx | setup_ssl | Generate dhparams + become: yes + shell: > + openssl dhparam -dsaparam -out {{ server_ssl_dhparams_file }} 4096 + +- name: nginx | setup_ssl | Create SSL server certificate chain + shell: > + for f in "{{ server_ssl_cert_file }}" "{{ server_ssl_ca_file }}"; + do (cat "${f}"; echo) >> cert_chain.pem; done; + mv cert_chain.pem {{ server_ssl_cert_file }} + args: + chdir: /etc/nginx/ diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/templates/jenkins.conf.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/templates/jenkins.conf.j2 new file mode 100644 index 000000000..01641f56d --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/templates/jenkins.conf.j2 @@ -0,0 +1,24 @@ +server { + listen {% if nginx_use_ssl %}443 ssl{% else %}80{% endif %}; + server_name ci.harvesterhci.io; + + error_log /var/log/nginx/jenkins_error.log; + +{% if nginx_use_ssl %} + ssl_certificate {{ server_ssl_cert_file }}; + ssl_certificate_key {{ server_ssl_private_key_file }}; + ssl_dhparam {{ server_ssl_dhparams_file }}; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:20m; + ssl_session_timeout 180m; +{% endif %} + + location / { + include /etc/nginx/proxy_params; + proxy_pass http://127.0.0.1:8080/; + proxy_read_timeout 90s; + proxy_redirect http://127.0.0.1:8080 https://{{ ip }}; + } +} diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/templates/nginx.conf.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/templates/nginx.conf.j2 new file mode 100644 index 000000000..59e33b908 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/templates/nginx.conf.j2 @@ -0,0 +1,49 @@ +#user nginx; +worker_processes 1; + +# load_module lib64/nginx/modules/ngx_http_fancyindex_module.so; +# load_module lib64/nginx/modules/ngx_http_geoip_module.so; +# load_module lib64/nginx/modules/ngx_http_headers_more_filter_module.so; +# load_module lib64/nginx/modules/ngx_http_image_filter_module.so; +# load_module lib64/nginx/modules/ngx_http_perl_module.so; +# load_module lib64/nginx/modules/ngx_http_xslt_filter_module.so; +# load_module lib64/nginx/modules/ngx_mail_module.so; +# load_module lib64/nginx/modules/ngx_rtmp_module.so; +# load_module lib64/nginx/modules/ngx_stream_geoip_module.so; +# load_module lib64/nginx/modules/ngx_stream_module.so; + +#error_log /var/log/nginx/error.log; +#error_log /var/log/nginx/error.log notice; +#error_log /var/log/nginx/error.log info; + +#pid /run/nginx.pid; + + +events { + worker_connections 1024; + use epoll; +} + + +http { + include mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include conf.d/*.conf; + + include vhosts.d/*.conf; + +} diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/vars/default.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/vars/default.yml new file mode 100644 index 000000000..cd21505a4 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/nginx/vars/default.yml @@ -0,0 +1,2 @@ +--- + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/defaults/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/defaults/main.yml new file mode 100644 index 000000000..5525a4c2c --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/defaults/main.yml @@ -0,0 +1,38 @@ +--- + +# global configuration +pki_home: /var/simple-pki +certs_dir: "{{ pki_home }}/certs" +etc_dir: "{{ pki_home }}/etc" + +domain_component_0: com +domain_component_1: suse +organization: cloud +organizational_unit: harvester +root_ca_common_name: Self-Signed Root CA +intermediate_ca_common_name: Intermediate CA + +ca_cert_chain_file: "{{ certs_dir }}/{{ domain_component_1 }}-{{ domain_component_0 }}-ca-certs.pem" + +# root CA configuration +root_ca_dir: "{{ pki_home }}/ca/root" +root_ca_conf_file: "{{ etc_dir }}/root-ca.conf" +root_ca_key_file: "{{ root_ca_dir }}/private/root-ca.key" +root_ca_cert_file: "{{ root_ca_dir }}/certs/root-ca.pem" + +# intermediate CA configuration +intermediate_ca_dir: "{{ pki_home }}/ca/intermedia" +intermediate_ca_csr_dir: "{{ intermediate_ca_dir }}/csr" +intermediate_ca_conf_file: "{{ etc_dir }}/intermediate-ca.conf" +intermediate_ca_key_file: "{{ intermediate_ca_dir }}/private/intermediate-ca.key" +intermediate_ca_cert_file: "{{ intermediate_ca_dir }}/certs/intermediate-ca.pem" +intermediate_ca_csr_file: "{{ intermediate_ca_dir }}/csr/intermediate-ca-csr.pem" + +# All CA configuration +#ssl_server_san: DNS:hostname,DNS:alt-hostname,IP:127.0.0.1 + +key_size: 4096 +# can be either "rsa" or "dsa", must be in lower case +key_type: rsa + +ip: "{{ ssl_server_cert_ip | default(ansible_host) }}" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/files/crlnumber b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/files/crlnumber new file mode 100644 index 000000000..c22ca71cc --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/files/crlnumber @@ -0,0 +1,2 @@ +2000 + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/files/serial b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/files/serial new file mode 100644 index 000000000..b72dbb499 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/files/serial @@ -0,0 +1,2 @@ +1000 + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/meta/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/meta/main.yml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/meta/main.yml @@ -0,0 +1 @@ +--- diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_generate_dsa_private_key.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_generate_dsa_private_key.yml new file mode 100644 index 000000000..1a63e295f --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_generate_dsa_private_key.yml @@ -0,0 +1,10 @@ +--- + +- name: pki | _generate_dsa_private_key | Generate DSA private key + command: | + openssl dsaparam + -noout + -out "{{ key_file }}" + -genkey "{{ key_size }}" + args: + creates: "{{ key_file }}" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_generate_rsa_private_key.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_generate_rsa_private_key.yml new file mode 100644 index 000000000..a07b7eda1 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_generate_rsa_private_key.yml @@ -0,0 +1,8 @@ +--- +- name: pki | _generate_rsa_private_key | Generate RSA private key + command: | + openssl genrsa + -out "{{ key_file }}" + "{{ key_size }}" + args: + creates: "{{ key_file }}" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_ca_certs_chain_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_ca_certs_chain_on_Debian.yml new file mode 100644 index 000000000..ac13a9955 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_ca_certs_chain_on_Debian.yml @@ -0,0 +1,9 @@ +--- +- name: pki | _install_ca_certs_chain_on_Debian | Copy CA certs to trust anchors + copy: + content: "{{ ca_certs_chain_content }}" + dest: "/usr/local/share/ca-certificates/simple_ca_certs.pem" + +- name: pki | _install_ca_certs_chain_on_Debian | Update CA certificates + shell: update-ca-certificates + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_ca_certs_chain_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_ca_certs_chain_on_Suse.yml new file mode 100644 index 000000000..c9ce4fd48 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_ca_certs_chain_on_Suse.yml @@ -0,0 +1,9 @@ +--- +- name: pki | _install_ca_certs_chain_on_Suse | Copy CA certs to trust anchors + copy: + content: "{{ ca_certs_chain_content }}" + dest: "/usr/share/pki/trust/anchors/simple_ca_certs.pem" + +- name: pki | _install_ca_certs_chain_on_Suse | Update CA certificates + shell: update-ca-certificates + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_openssl_on_Debian.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_openssl_on_Debian.yml new file mode 100644 index 000000000..733d40645 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_openssl_on_Debian.yml @@ -0,0 +1,6 @@ +--- + +- name: pki | _install_openssl_on_Debian | Install OpenSSL package + apt: + name: openssl + state: latest diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_openssl_on_Suse.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_openssl_on_Suse.yml new file mode 100644 index 000000000..be214815c --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_install_openssl_on_Suse.yml @@ -0,0 +1,7 @@ +--- + +- name: pki | _install_openssl_on_Suse | Install OpenSSL package + zypper: + name: openssl + state: latest + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_issue_certificate.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_issue_certificate.yml new file mode 100644 index 000000000..b96f29e4f --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_issue_certificate.yml @@ -0,0 +1,22 @@ +--- + +- name: pki | issue_certificate | Issue certificate for {{ csr_file }} + command: | + openssl ca + -config "{{ ca_conf_file }}" + -extensions "{{ ca_extensions }}" + -days 3650 + -notext + -md sha256 + -in "{{ csr_file }}" + -out "{{ cert_file }}" + -batch + args: + creates: "{{ cert_file }}" + +- name: pki | issue_certificate | Verify certificate {{ cert_file }} + command: | + openssl verify + -CAfile "{{ ca_cert_file }}" + "{{ cert_file }}" + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_ca.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_ca.yml new file mode 100644 index 000000000..0ef8c4046 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_ca.yml @@ -0,0 +1,45 @@ +--- + +- name: pki | _setup_ca | Create {{ ca_type }} CA directory hierarchy + file: + path: "{{ ca_dir }}/{{ item }}" + owner: root + group: root + state: directory + mode: 0700 + with_items: + - csr + - certs + - crl + - newcerts + - private + +- name: pki | _setup_ca | Create {{ ca_type }} CA index file + file: + path: "{{ ca_dir }}/index.txt" + owner: root + group: root + state: touch + mode: 0700 + +- name: pki | _setup_ca | Create {{ ca_type }} CA serial file + copy: + dest: "{{ ca_dir }}/serial" + force: no + content: 1000 + mode: 0700 + owner: root + group: root + +- name: pki | _setup_ca | Create {{ ca_type }} CA crlnumber file + copy: + dest: "{{ ca_dir }}/serial" + force: no + content: 1000 + mode: 0700 + owner: root + group: root + +- include: generate_private_key.yml + vars: + key_file: "{{ ca_dir }}/private/{{ ca_type }}-ca.key" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_intermediate_ca.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_intermediate_ca.yml new file mode 100644 index 000000000..04d80b04f --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_intermediate_ca.yml @@ -0,0 +1,40 @@ +--- + +- name: pki | _setup_intermediate_ca | Create intermediate-ca.conf + template: + src: intermediate-ca.conf.j2 + dest: "{{ intermediate_ca_conf_file }}" + +- include: _setup_ca.yml + vars: + ca_type: intermediate + ca_dir: "{{ intermediate_ca_dir }}" + +- name: pki | _setup_intermediate_ca | Create intermediate CA cert req (CSR) + command: | + openssl req + -config "{{ intermediate_ca_conf_file }}" + -new + -sha256 + -key "{{ intermediate_ca_key_file }}" + -out "{{ intermediate_ca_csr_file }}" + -nodes + args: + creates: "{{ intermediate_ca_csr_file }}" + +- include: _issue_certificate.yml + vars: + ca_conf_file: "{{ root_ca_conf_file }}" + ca_extensions: v3_intermediate_ca + csr_file: "{{ intermediate_ca_csr_file }}" + cert_file: "{{ intermediate_ca_cert_file }}" + ca_cert_file: "{{ root_ca_cert_file }}" + +- name: pki | _setup_intermediate_ca | Copy intermediatet CA certificate to {{ certs_dir }} + copy: + remote_src: true + src: "{{ intermediate_ca_cert_file }}" + dest: "{{ certs_dir }}" + owner: root + group: root + mode: 0744 diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_pki.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_pki.yml new file mode 100644 index 000000000..967a859a9 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_pki.yml @@ -0,0 +1,59 @@ +--- + +- name: pki | main | Install openssl + include_tasks: _install_openssl_on_{{ ansible_os_family }}.yml + +- name: pki | main | Create directory hierarchy + file: + path: "{{ item }}" + owner: root + group: root + state: directory + recurse: yes + mode: 0700 + with_items: + - "{{ root_ca_dir }}" + - "{{ intermediate_ca_dir }}" + - "{{ etc_dir }}" + +- name: pki | main | Create certs directory + file: + path: "{{ certs_dir }}" + owner: root + group: root + state: directory + recurse: yes + mode: 0755 + +- name: pki | main | Make sure {{ pki_home }} is world readable + file: + path: "{{ pki_home }}" + mode: 0755 + state: directory + +- include: _setup_root_ca.yml +- include: _setup_intermediate_ca.yml + +# What a horrible way to do simple cat in Ansible! +- name: pki | main | Get the root CA cert + command: cat {{ root_ca_cert_file }} + register: root_ca_cert_out + +- name: pki | main | Get the intermediate CA cert + command: cat {{ intermediate_ca_cert_file }} + register: intermediate_ca_cert_out + +- name: pki | main | Create CA chain {{ ca_cert_chain_file }} + copy: + content: "{{ root_ca_cert_out.stdout }}" + dest: "{{ ca_cert_chain_file }}" + mode: 0755 + owner: root + group: root + +- name: pki | main | Add intermediate CA cert to {{ ca_cert_chain_file }} + lineinfile: + path: "{{ ca_cert_chain_file }}" + insertafter: EOF + line: "{{ intermediate_ca_cert_out.stdout }}" + mode: 0755 diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_root_ca.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_root_ca.yml new file mode 100644 index 000000000..cbaa666f7 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/_setup_root_ca.yml @@ -0,0 +1,34 @@ +--- + +- name: pki | _setup_root_ca | Create root-ca.conf + template: + src: root-ca.conf.j2 + dest: "{{ root_ca_conf_file }}" + +- include: _setup_ca.yml + vars: + ca_type: root + ca_dir: "{{ root_ca_dir }}" + +- name: pki | _setup_root_ca | Create self-signed root CA certificate + command: | + openssl req + -new + -config "{{ root_ca_conf_file }}" + -out "{{ root_ca_cert_file }}" + -key "{{ root_ca_key_file }}" + -x509 + -days 3652 + -sha256 + -extensions v3_ca + args: + creates: "{{ root_ca_cert_file }}" + +- name: pki | _setup_root_ca | Copy root CA certificate to {{ certs_dir }} + copy: + remote_src: true + src: "{{ root_ca_cert_file }}" + dest: "{{ certs_dir }}" + owner: root + group: root + mode: 0744 diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/configure_user_cert.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/configure_user_cert.yml new file mode 100644 index 000000000..8aaad564f --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/configure_user_cert.yml @@ -0,0 +1,33 @@ +--- + +- name: PKI | configure_user_cert | Generate private key + include: generate_private_key.yml + vars: + key_type: "{{ client_ssl_key_type }}" + file_owner: vagrant + file_group: users + file_mode: 0600 + key_size: "{{ client_ssl_key_size }}" + key_file: "{{ client_ssl_private_key_file }}" + +- name: PKI | configure_user_cert | Generate cert request (CSR) + include: generate_user_cert_req.yml + vars: + key_file: "{{ client_ssl_private_key_file }}" + user_name: "{{ client_ssl_user_name }}" + user_domain_name: "{{ client_ssl_user_domain_name }}" + +- name: PKI | configure_user_cert | Generate user certificate + include: issue_user_cert.yml + vars: + user_cert_file: "{{ client_ssl_cert_file }}" + user_name: "{{ client_ssl_user_name }}" + user_domain_name: "{{ client_ssl_user_domain_name }}" + +- name: PKI | configure_user_cert | Copy CA certificates + include: copy_ca_certs_chain.yml + vars: + file_owner: root + file_group: root + file_mode: 0644 + ca_certs_file: "{{ client_ssl_ca_file }}" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/copy_ca_certs_chain.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/copy_ca_certs_chain.yml new file mode 100644 index 000000000..4b22ef220 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/copy_ca_certs_chain.yml @@ -0,0 +1,14 @@ +--- + +- name: pki | copy_ca_certs_chain | Get the CA certificates chain + command: cat {{ ca_cert_chain_file }} + register: ca_cert_chain_out + +- name: pki | copy_ca_certs_chain | Copy CA certificates chain to localhost + copy: + content: "{{ ca_cert_chain_out.stdout }}" + dest: "{{ ca_certs_file }}" + owner: "{{ file_owner }}" + group: "{{ file_group }}" + mode: "{{ file_mode }}" + force: no diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_private_key.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_private_key.yml new file mode 100644 index 000000000..e2295f4d3 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_private_key.yml @@ -0,0 +1,14 @@ +--- + +# must supply the following vars: +# - key_type +# - key_file +- include: _generate_{{key_type}}_private_key.yml + +- name: pki | generate_private_key | Set proper permission for {{ key_file }} + file: + path: "{{ key_file }}" + owner: "{{ file_owner }}" + group: "{{ file_group }}" + mode: "{{ file_mode }}" + when: file_owner is defined diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_server_cert_req.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_server_cert_req.yml new file mode 100644 index 000000000..b8e93999c --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_server_cert_req.yml @@ -0,0 +1,44 @@ +--- + +- set_fact: + timestamp: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" + when: timestamp is not defined + +- set_fact: + ssl_server_host_name: "{{ inventory_hostname }}" + when: ssl_server_host_name is not defined + +- set_fact: + server_cert_conf_file: "/tmp/server_cert.conf_{{ timestamp }}" + +- name: pki | generate_server_cert_req | Create {{ server_cert_conf_file }} + template: + src: server_cert.conf.j2 + dest: "{{ server_cert_conf_file }}" + +- name: pki | generate_server_cert_req | Generate CSR for {{ ssl_server_host_name }} + command: | + openssl req + -config "{{ server_cert_conf_file }}" + -new + -sha256 + -key "{{ key_file }}" + -nodes + register: server_csr + +- name: pki | generate_server_cert_req | Remove {{ server_cert_conf_file }} + file: + path: "{{ server_cert_conf_file }}" + state: absent + +- name: pki | generate_server_cert_req | + Ensure {{ intermediate_ca_csr_dir }} exist + file: + path: "{{ intermediate_ca_csr_dir }}" + state: directory + recurse: yes + +- name: pki | generate_server_cert_req | Copy CSR to {{ groups.pki[0] }} + copy: + content: "{{ server_csr.stdout }}" + dest: "{{ intermediate_ca_csr_dir }}/{{ ssl_server_host_name }}_{{ timestamp }}.csr" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_user_cert_req.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_user_cert_req.yml new file mode 100644 index 000000000..7108ab28f --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/generate_user_cert_req.yml @@ -0,0 +1,33 @@ +--- + +- set_fact: + timestamp: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" + when: timestamp is not defined + +- set_fact: + user_cert_conf_file: "/tmp/user_cert.conf_{{ timestamp }}" + +- name: pki | generate_user_cert_req | Create {{ user_cert_conf_file }} + template: + src: user_cert.conf.j2 + dest: "{{ user_cert_conf_file }}" + +- name: pki | generate_user_cert_req | Generate CSR for {{ user_domain_name }}:{{ user_name }} + command: | + openssl req + -config "{{ user_cert_conf_file }}" + -new + -sha256 + -key "{{ key_file }}" + -nodes + register: user_csr + +- name: pki | generate_user_cert_req | Remove {{ user_cert_conf_file }} + file: + path: "{{ user_cert_conf_file }}" + state: absent + +- name: pki | generate_user_cert_req | Copy CSR to {{ groups.pki[0] }} + copy: + content: "{{ user_csr.stdout }}" + dest: "{{ intermediate_ca_csr_dir }}/{{ user_domain_name }}_{{user_name}}_{{ timestamp }}.csr" diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/install_ca_certs_chain.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/install_ca_certs_chain.yml new file mode 100644 index 000000000..7da212b18 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/install_ca_certs_chain.yml @@ -0,0 +1,27 @@ +--- +# NOTE(gyee): we'll only include the first one that matches. The rest will be ignored. +- name: pki | install_ca_certs_chain | Include OS family vars + include_vars: "{{ item }}" + with_first_found: + - "{{ ansible_os_family }}.yml" + - "{{ ansible_os_family | lower }}.yml" + - "default.yml" + +- name: pki | install_ca_certs_chain | Include OS distribution vars + include_vars: "{{ item }}" + with_first_found: + - "{{ ansible_distribution | replace(' ', '_') }}.yml" + - "{{ ansible_distribution | lower | replace(' ', '_') }}.yml" + - "{{ ansible_distribution | replace(' ', '_') }}.yml" + - "default.yml" + +- name: pki | install_ca_certs_chain | Get the CA certificates chain + command: cat {{ ca_cert_chain_file }} + register: ca_cert_chain_out + +- name: pki | install_ca_certs_chain | Set CA cert chain content fact + set_fact: + ca_certs_chain_content: "{{ ca_cert_chain_out.stdout }}" + +- name: pki | install_ca_certs_chain | Update system trust with new CA certs + include_tasks: _install_ca_certs_chain_on_{{ ansible_os_family }}.yml diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/issue_server_cert.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/issue_server_cert.yml new file mode 100644 index 000000000..5dcb6ec6a --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/issue_server_cert.yml @@ -0,0 +1,33 @@ +--- + +- set_fact: + csr_file: "{{ intermediate_ca_csr_dir }}/{{ ssl_server_host_name }}_{{ timestamp }}.csr" + +- name: pki | issue_server_certificate | Create {{ etc_dir }} + file: + path: "{{ etc_dir }}" + state: directory + recurse: yes + +- name: pki | issue_server_certificate | Create {{ intermediate_ca_conf_file }} + template: + src: intermediate-ca.conf.j2 + dest: "{{ intermediate_ca_conf_file }}" + +- name: pki | issue_server_certificate | Issue certificate for {{ csr_file }} + command: | + openssl ca + -config "{{ intermediate_ca_conf_file }}" + -extensions server_cert + -days 3650 + -notext + -md sha256 + -in "{{ csr_file }}" + -batch + register: server_cert_pem + +- name: pki | issue_server_certificate | Create {{ server_cert_file }} + copy: + content: "{{ server_cert_pem.stdout }}" + dest: "{{ server_cert_file }}" + force: no diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/issue_user_cert.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/issue_user_cert.yml new file mode 100644 index 000000000..77c4a6483 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/issue_user_cert.yml @@ -0,0 +1,22 @@ +--- + +- set_fact: + csr_file: "{{ intermediate_ca_csr_dir }}/{{ user_domain_name }}_{{user_name}}_{{ timestamp }}.csr" + +- name: pki | issue_user_cert | Issue certificate for {{ csr_file }} + command: | + openssl ca + -config "{{ intermediate_ca_conf_file }}" + -extensions user_cert + -days 3650 + -notext + -md sha256 + -in "{{ csr_file }}" + -batch + register: user_cert_pem + +- name: pki | issue_user_cert | Create {{ user_cert_file }} + copy: + content: "{{ user_cert_pem.stdout }}" + dest: "{{ user_cert_file }}" + force: no diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/main.yml b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/main.yml new file mode 100644 index 000000000..97542734e --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/tasks/main.yml @@ -0,0 +1,26 @@ +--- + +- name: pki | main | Check for {{ intermediate_ca_cert_file }} + stat: + path: "{{ intermediate_ca_cert_file }}" + register: intermediate_ca_cert + +- name: pki | main | Check for {{ intermediate_ca_key_file }} + stat: + path: "{{ intermediate_ca_key_file }}" + register: intermediate_ca_key + +- name: pki | main | Check for {{ intermediate_ca_conf_file }} + stat: + path: "{{ intermediate_ca_conf_file }}" + register: intermediate_ca_conf + +- name: pki | main | Check for {{ ca_cert_chain_file }} + stat: + path: "{{ ca_cert_chain_file }}" + register: ca_cert_chain + +- include: _setup_pki.yml + when: not intermediate_ca_cert.stat.exists and + not intermediate_ca_key.stat.exists and + not ca_cert_chain.stat.exists diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/intermediate-ca.conf.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/intermediate-ca.conf.j2 new file mode 100644 index 000000000..540f72396 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/intermediate-ca.conf.j2 @@ -0,0 +1,119 @@ +#jinja2:trim_blocks: False +[ default ] +ca = intermediate-ca + +[ ca ] +ca = intermediate-ca +default_ca = ca_default + +[ ca_default ] +# Directory and file locations. +dir = {{ intermediate_ca_dir }} +certs = $dir/certs +crl_dir = $dir/crl +new_certs_dir = $dir/newcerts +private_dir = $dir/private +database = $dir/index.txt +serial = $dir/serial +RANDFILE = $private_dir/.rand + +# The root key and root certificate. +private_key = $private_dir/$ca.key +certificate = $certs/$ca.pem + +# For certificate revocation lists. +crlnumber = /crlnumber +crl = /crl/intermediate.crl.pem +crl_extensions = crl_ext +default_crl_days = 365 + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +name_opt = ca_default +cert_opt = ca_default +default_days = 3652 +preserve = no +policy = loose_policy + +unique_subject = no + +[ strict_policy ] +domainComponent = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ loose_policy ] +domainComponent = optional +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ req ] +default_bits = 2048 +distinguished_name = intermediate_ca_dn +prompt = no +string_mask = utf8only + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +# Extension to add when the -x509 option is used. +x509_extensions = v3_ca + +[ intermediate_ca_dn ] +0.domainComponent = {{ domain_component_0 }} +1.domainComponent = {{ domain_component_1 }} +organizationName = {{ organization }} +organizationalUnitName = {{ organizational_unit }} +commonName = {{ intermediate_ca_common_name }} + +[ v3_ca ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ v3_intermediate_ca ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true, pathlen:0 +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ user_cert ] +basicConstraints = CA:FALSE +nsCertType = client, email +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection + +[ server_cert ] +basicConstraints = CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth +{%- if ssl_server_san is defined and not ssl_server_san == "" %} +subjectAltName = {{ ssl_server_san }} +{% endif %} + +[ crl_ext ] +authorityKeyIdentifier = keyid:always + +[ ocsp ] +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, digitalSignature +extendedKeyUsage = critical, OCSPSigning + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/root-ca.conf.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/root-ca.conf.j2 new file mode 100644 index 000000000..db70e8f47 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/root-ca.conf.j2 @@ -0,0 +1,116 @@ +[ default ] +ca = root-ca # CA name + +[ req ] +default_bits = 2048 # RSA key size +encrypt_key = no # Protect private key +default_md = sha256 # MD to use +utf8 = yes # Input is UTF-8 +string_mask = utf8only # Emit UTF-8 strings +prompt = no # Don't prompt for DN +distinguished_name = ca_dn # DN section +req_extensions = v3_ca # Desired extensions + +[ ca_dn ] +0.domainComponent = {{ domain_component_0 }} +1.domainComponent = {{ domain_component_1 }} +organizationName = {{ organization }} +organizationalUnitName = {{ organizational_unit }} +commonName = {{ root_ca_common_name }} + +[ v3_ca ] +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +basicConstraints = critical, CA:true +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer + +[ v3_intermediate_ca ] +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +basicConstraints = critical, CA:true, pathlen:0 +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer + +[ user_cert ] +# Extensions for client certificate ('man x509v3_config'). +basicConstraints = CA:FALSE +nsCertType = client, email +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection + +[ server_cert ] +# Extensions for server certificates ('man x509v3_config'). +basicConstraints = CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth + +[ crl_ext ] +# Extension for CRLs ('man x509v3_config'). +authorityKeyIdentifier = keyid:always + +[ ocsp ] +# Extension for OCSP signing certificates ('man ocsp'). +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, digitalSignature +extendedKeyUsage = critical, OSCPSigning + +[ ca ] +default_ca = root_ca # The default CA section + +[ root_ca ] +# Directory and file locations. +dir = {{ root_ca_dir }} +certs = $dir/certs +crl_dir = $dir/crl +private_dir = $dir/private +new_certs_dir = $dir/newcerts +database = $dir/index.txt +serial = $dir/serial +RANDFILE = $private_dir/.rand + +# Root CA key and certificate location +certificate = $certs/$ca.pem # The CA cert +private_key = $private_dir/$ca.key # CA private key + +# CRL +crlnumber = $dir/crlnumber # CRL number file +crl = $crl_dir/ca.crl.pem +crl_extension = crl_ext +default_crl_days = 365 + +unique_subject = no # Require unique subject +default_days = 3652 # How long to certify for +default_md = sha256 # MD to use +policy = strict_policy # Default naming policy +email_in_dn = no # Add email to cert DN +preserve = no # Keep passed DN ordering +name_opt = ca_default # Subject DN display options +cert_opt = ca_default # Certificate display options +copy_extensions = none # Copy extensions from CSR +x509_extensions = signing_ca_ext # Default cert extensions + +[ strict_policy ] +domainComponent = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ loose_policy ] +domainComponent = optional +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/server_cert.conf.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/server_cert.conf.j2 new file mode 100644 index 000000000..36cbc7306 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/server_cert.conf.j2 @@ -0,0 +1,36 @@ +#jinja2:trim_blocks: False +[ req ] +distinguished_name = server_dn +default_bits = 2048 # RSA key size +encrypt_key = no # Protect private key +default_md = sha256 # MD to use +utf8 = yes # Input is UTF-8 +string_mask = utf8only # Emit UTF-8 strings +prompt = no # Prompt for DN +req_extensions = server_reqext # Desired extensions + +[ server_dn ] +0.domainComponent = {{ domain_component_0 }} +1.domainComponent = {{ domain_component_1 }} +organizationName = {{ organization }} +organizationalUnitName = {{ organizational_unit }} +commonName = {{ ssl_server_host_name }} + +[ server_reqext ] +basicConstraints = CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier = hash +#authorityKeyIdentifier = keyid,issuer:always +keyUsage = critical, digitalSignature, keyEncipherment +{%- if ssl_server_san is defined and not ssl_server_san == "" %} +subjectAltName = {{ ssl_server_san }} +{% endif %} + +#subjectAltName = @alt_names + +#[ alt_names ] +#DNS.1 = dev-adfs +#DNS.2 = dev-adfs.thegyee.com +#IP.1 = 172.16.0.41 + diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/user_cert.conf.j2 b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/user_cert.conf.j2 new file mode 100644 index 000000000..6ef43eeec --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/roles/pki/templates/user_cert.conf.j2 @@ -0,0 +1,27 @@ + +[ req ] +distinguished_name = client_dn +default_bits = 2048 # RSA key size +encrypt_key = no # Protect private key +default_md = sha256 # MD to use +utf8 = yes # Input is UTF-8 +string_mask = utf8only # Emit UTF-8 strings +prompt = no # Prompt for DN +req_extensions = client_reqext # Desired extensions + +[ client_dn ] +0.domainComponent = {{ domain_component_0 }} +1.domainComponent = {{ domain_component_1 }} +organizationName = {{ organization }} +organizationalUnitName = {{ organizational_unit }} +organizationName = {{ user_domain_name }} +commonName = {{ user_name }} + +[ client_reqext ] +basicConstraints = CA:FALSE +nsCertType = client, email +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +#authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection diff --git a/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/settings.yml.sample b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/settings.yml.sample new file mode 100644 index 000000000..3e85db865 --- /dev/null +++ b/AirGap-Harvester-Jenkins-Node-Infra/jeninks-airgap-harvester-installer-based-node/settings.yml.sample @@ -0,0 +1,35 @@ +--- +# Jenkins admin user configuration. This user have unrestricted access to +# Jenkins. +JENKINS_ADMIN_USERNAME: admin +JENKINS_ADMIN_PASSWORD: +JENKINS_ADMIN_EMAIL: + +# Password for the "jenkins" user. This is for setting up a Jenkins slave only. +JENKINS_USER_PASSWORD: jenkins + +# Jenkins dev user configuration. This user only have read access to the jobs. +JENKINS_DEV_USERNAME: harvester +JENKINS_DEV_PASSWORD: harvester + +# Jenkins public endpoint. If Jenkins is running behind a reversed proxy and +# the proxy IP is different than the Jenkins host, make sure to configure it +# correctly here. +JENKINS_PUBLIC_ENDPOINT: https://ci.harvesterhci.io/ + +# Github token used by Jenkins to post the result to the pull requests. +GITHUB_CI_USERNAME: harvester-ci +GITHUB_CI_PASSWORD: ghp_g9sDDSsehUePKAQBSzqpMZkVGDYkg0Rx6aJX + +# Decide whether to use a reversed proxy and whether to enable SSL for the +# reversed proxy. +# FIXME(gyee): we are using self-signed CA for the SSL certificates right now. +# To avoid browser warnings, we should be using certificates that are issued by +# a commercial CA. +JENKINS_USE_PROXY: True +JENKINS_PROXY_ENABLE_SSL: True + + +# Additional +SLACK_WEBHOOK_TOKEN: change-me +JENKINS_ENDPOINT_IP: "111.222.333.444" \ No newline at end of file