├── .circleci └── config.yml ├── .gitignore ├── .python-version ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── defaults └── main.yml ├── docs ├── integration.md ├── manual_config.md └── terraform.md ├── handlers └── main.yml ├── meta └── main.yml ├── tasks ├── logging.yml ├── main.yml ├── nginx.yml └── ssh.yml ├── templates └── rsyslog.conf ├── terraform ├── aws.tf ├── modules │ ├── instances │ │ ├── master.tf │ │ ├── outputs.tf │ │ └── vars.tf │ └── networking │ │ ├── outputs.tf │ │ ├── security_group.tf │ │ └── vars.tf ├── outputs.tf └── vars.tf └── tests ├── ansible.cfg ├── files ├── Jenkinsfile └── job.xml ├── group_vars └── all │ ├── .keep │ └── ssh.yml ├── hosts-docker ├── requirements.yml ├── secrets-example.yml └── test.yml /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | # Needs to match repository name; when the role is included normally, it will be `gsa.jenkins`. Also, needs to be an absolute path due to 5 | # https://circleci.com/docs/2.0/local-jobs/#relative-path-for-working_directory 6 | working_directory: /etc/jenkins-deploy 7 | docker: 8 | - image: docker 9 | steps: 10 | - checkout 11 | - setup_remote_docker 12 | - run: 13 | name: Install packages 14 | command: apk add --update-cache ansible git make 15 | - run: 16 | name: Start remote container 17 | command: make start_container 18 | - run: 19 | name: Install Ansible roles 20 | command: make install_roles 21 | - run: 22 | name: Run Ansible playbook 23 | command: make test_ci 24 | validate_terraform: 25 | working_directory: /etc/jenkins-deploy 26 | docker: 27 | - image: hashicorp/terraform 28 | steps: 29 | - checkout 30 | - run: 31 | name: Install Make 32 | command: apk add --update make 33 | - run: 34 | name: Set up Terraform 35 | command: make terraform_init 36 | - run: 37 | name: Check Terraform syntax 38 | command: make validate_terraform 39 | workflows: 40 | version: 2 41 | build_and_test: 42 | jobs: 43 | - build 44 | - validate_terraform 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ansible 2 | *.retry 3 | secrets.yml 4 | tests/roles/ 5 | 6 | ## Terraform ## 7 | *.tfvars 8 | # Compiled files 9 | *.tfstate* 10 | # Module directory 11 | .terraform/ 12 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 2.7.11 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Welcome! 2 | 3 | We're so glad you're thinking about contributing to an 18F open source project! If you're unsure about anything, just ask -- or submit the issue or pull request anyway. The worst that can happen is you'll be politely asked to change something. We love all friendly contributions. 4 | 5 | We want to ensure a welcoming environment for all of our projects. Our staff follow the [18F Code of Conduct](https://github.com/18F/code-of-conduct/blob/master/code-of-conduct.md) and all contributors should do the same. 6 | 7 | We encourage you to read this project's CONTRIBUTING policy (you are here), its [LICENSE](LICENSE.md), and its [README](README.md). 8 | 9 | If you have any questions or want to read more, check out the [18F Open Source Policy GitHub repository](https://github.com/18f/open-source-policy), or just [shoot us an email](mailto:18f@gsa.gov). 10 | 11 | ## Public domain 12 | 13 | This project is in the public domain within the United States, and 14 | copyright and related rights in the work worldwide are waived through 15 | the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/). 16 | 17 | All contributions to this project will be released under the CC0 18 | dedication. By submitting a pull request, you are agreeing to comply 19 | with this waiver of copyright interest. 20 | 21 | ## Development 22 | 23 | ### Testing locally 24 | 25 | 1. [Install the CircleCI CLI.](https://circleci.com/docs/2.0/local-jobs/#installing-the-cli-locally) 26 | 1. Run the tests. 27 | 28 | ```sh 29 | make test 30 | ``` 31 | 32 | ### Testing in AWS 33 | 34 | See [the documentation](docs/integration.md). 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | As a work of the United States government, this project is in the 2 | public domain within the United States. 3 | 4 | Additionally, we waive copyright and related rights in the work 5 | worldwide through the CC0 1.0 Universal public domain dedication. 6 | 7 | ## CC0 1.0 Universal summary 8 | 9 | This is a human-readable summary of the [Legal Code (read the full text)](https://creativecommons.org/publicdomain/zero/1.0/legalcode). 10 | 11 | ### No copyright 12 | 13 | The person who associated a work with this deed has dedicated the work to 14 | the public domain by waiving all rights to the work worldwide 15 | under copyright law, including all related and neighboring rights, to the 16 | extent allowed by law. 17 | 18 | You can copy, modify, distribute and perform the work, even for commercial 19 | purposes, all without asking permission. 20 | 21 | ### Other information 22 | 23 | In no way are the patent or trademark rights of any person affected by CC0, 24 | nor are the rights that other persons may have in the work or in how the 25 | work is used, such as publicity or privacy rights. 26 | 27 | Unless expressly stated otherwise, the person who associated a work with 28 | this deed makes no warranties about the work, and disclaims liability for 29 | all uses of the work, to the fullest extent permitted by applicable law. 30 | When using or citing the work, you should not imply endorsement by the 31 | author or the affirmer. 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TERRAFORM_DIR := terraform 2 | PLAYBOOK_DIR := tests 3 | SSH_USER := ec2-user 4 | 5 | all: terraform ansible 6 | 7 | install_roles: 8 | cd $(PLAYBOOK_DIR) && ansible-galaxy install -r requirements.yml 9 | 10 | terraform_init: 11 | cd $(TERRAFORM_DIR) && terraform init -backend=false 12 | 13 | .PHONY: terraform 14 | terraform: 15 | cd $(TERRAFORM_DIR) && terraform apply 16 | 17 | INVENTORY_PATH := $(shell which terraform-inventory) 18 | .PHONY: ansible 19 | ansible: install_roles 20 | cd $(PLAYBOOK_DIR) && TF_STATE=../$(TERRAFORM_DIR)/terraform.tfstate ansible-playbook --inventory-file=$(INVENTORY_PATH) --become --user=$(SSH_USER) test.yml 21 | 22 | destroy: 23 | cd $(TERRAFORM_DIR) && terraform destroy 24 | 25 | validate_terraform: 26 | cd $(TERRAFORM_DIR) && terraform validate 27 | 28 | validate_ansible: 29 | cd $(PLAYBOOK_DIR) && ansible-playbook --syntax-check test.yml 30 | 31 | validate: validate_terraform validate_ansible 32 | 33 | start_container: 34 | docker run -it --privileged -d --name remote centos/systemd 35 | 36 | test_ci: 37 | cd $(PLAYBOOK_DIR) && ansible-playbook -i hosts-docker test.yml 38 | 39 | test: 40 | circleci build --job validate_terraform 41 | # CircleCI CLI doesn't clean up remote containers 42 | docker rm -f remote || true 43 | circleci build 44 | docker rm -f remote 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jenkins Bootstrap [![CircleCI](https://circleci.com/gh/GSA/jenkins-deploy.svg?style=svg)](https://circleci.com/gh/GSA/jenkins-deploy) 2 | 3 | This repository is reusable deployment code/configuration of Jenkins, which gets you up and running with a production-grade Jenkins quickly. 4 | 5 | ## Integration 6 | 7 | See [the documentation](docs/integration.md). 8 | 9 | ## Reusable pieces 10 | 11 | ### Terraform modules 12 | 13 | See [the documentation](docs/terraform.md). 14 | 15 | ### Ansible role 16 | 17 | #### Requirements 18 | 19 | None. 20 | 21 | #### Role variables 22 | 23 | For any variables marked `sensitive`, you are strongly encouraged to store the values in an [Ansible Vault](https://docs.ansible.com/ansible/playbooks_vault.html). 24 | 25 | ##### Required 26 | 27 | * `jenkins_admin_password` - store in a [Vault](https://docs.ansible.com/ansible/playbooks_vault.html) 28 | * `jenkins_external_hostname` 29 | 30 | * SSH key - information about how to generate in [Usage](#usage) section below. 31 | * `jenkins_ssh_key_passphrase` (sensitive) 32 | * `jenkins_ssh_private_key_data` (sensitive) 33 | * `jenkins_ssh_public_key_data` 34 | * [SSL configuration](https://github.com/jdauphant/ansible-role-ssl-certs#examples) (sensitive) 35 | * The [key data](https://github.com/jdauphant/ansible-role-ssl-certs#example-to-deploy-a-ssl-certificate-stored-in-variables) approach is recommended. 36 | 37 | ##### Optional 38 | 39 | See [`defaults/main.yml`](defaults/main.yml). 40 | 41 | #### Dependencies 42 | 43 | * [`geerlingguy.jenkins`](https://galaxy.ansible.com/geerlingguy/jenkins/) 44 | * [`gsa.https-proxy`](https://github.com/GSA/ansible-https-proxy) 45 | * [`srsp.oracle-java`](https://galaxy.ansible.com/srsp/oracle-java/) 46 | 47 | #### Usage 48 | 49 | 1. Generate an SSH key. 50 | 51 | ```sh 52 | ssh-keygen -t rsa -b 4096 -f temp.key -C "group-email+jenkins@some.gov" 53 | # enter a passphrase - store in Vault as vault_jenkins_ssh_key_passphrase 54 | 55 | cat temp.key 56 | # store in Vault as vault_jenkins_ssh_private_key_data 57 | 58 | cat temp.key.pub 59 | # store as jenkins_ssh_public_key_data 60 | 61 | rm temp.key* 62 | ``` 63 | 64 | 1. Include the role and required variables. Example: 65 | 66 | ```yaml 67 | # requirements.yml 68 | - src: https://github.com/GSA/jenkins-deploy 69 | name: gsa.jenkins 70 | 71 | # group_vars/all/vars.yml 72 | jenkins_ssh_user: jenkins 73 | jenkins_ssh_public_key_data: | 74 | ssh-rsa ... group-email+jenkins@some.gov 75 | 76 | # group_vars/jenkins/vars.yml 77 | jenkins_external_hostname: ... 78 | jenkins_ssh_key_passphrase: "{{ vault_jenkins_ssh_key_passphrase }}" 79 | jenkins_ssh_private_key_data: "{{ vault_jenkins_ssh_private_key_data }}" 80 | ssl_certs_local_cert_data: "{{ vault_ssl_certs_local_cert_data }}" 81 | ssl_certs_local_privkey_data: "{{ vault_ssl_certs_local_privkey_data }}" 82 | 83 | # group_vars/jenkins/vault.yml (encrypted) 84 | vault_jenkins_ssh_key_passphrase: ... 85 | vault_jenkins_ssh_private_key_data: | 86 | -----BEGIN RSA PRIVATE KEY----- 87 | ... 88 | -----END RSA PRIVATE KEY----- 89 | vault_ssl_certs_local_cert_data: | 90 | -----BEGIN CERTIFICATE----- 91 | ... 92 | -----END CERTIFICATE----- 93 | vault_ssl_certs_local_privkey_data: | 94 | -----BEGIN RSA PRIVATE KEY----- 95 | ... 96 | -----END RSA PRIVATE KEY----- 97 | 98 | # playbooks/jenkins.yml 99 | - hosts: jenkins 100 | become: true 101 | roles: 102 | - gsa.jenkins 103 | 104 | # playbooks/other.yml 105 | # hosts that Jenkins is going to run playbooks against 106 | - hosts: other 107 | become: true 108 | tasks: 109 | - name: Create Jenkins user 110 | user: 111 | name: "{{ jenkins_ssh_user }}" 112 | group: wheel 113 | - name: Set up SSH key for Jenkins 114 | authorized_key: 115 | user: "{{ jenkins_ssh_user }}" 116 | key: "{{ jenkins_ssh_public_key_data }}" 117 | # ...other host setup tasks... 118 | ``` 119 | 120 | 1. Run the Terraform (if applicable) and the playbook. 121 | 1. Ensure you can log into Jenkins (at `jenkins_external_hostname`). 122 | 1. Follow the [manual configuration steps](docs/manual_config.md) 123 | 124 | ## License 125 | 126 | CC0 127 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | jenkins_audit_file_path: /var/log/jenkins/audit.log.0 3 | # the homepage may not be accessible if anonymous users don't have read permissions, so check the login page 4 | jenkins_health_check_path: /login 5 | 6 | jenkins_ssh_user: jenkins 7 | 8 | ### Plugins ### 9 | 10 | # For more information on each plugin, visit 11 | # 12 | # https://plugins.jenkins.io/ 13 | # 14 | # Note that removing items from this list won't uninstall them. 15 | jenkins_plugins: 16 | - ansible 17 | - ansicolor 18 | - audit-trail 19 | - blueocean 20 | - github 21 | - pipeline-utility-steps 22 | - role-strategy 23 | - ssh-agent 24 | - timestamper 25 | - workflow-aggregator 26 | - ws-cleanup 27 | - saml 28 | - periodicbackup 29 | - aws-credentials 30 | - packer 31 | - docker 32 | 33 | # Use this variable if you want to append to the recommended `jenkins_plugins` above. 34 | additional_jenkins_plugins: [] 35 | -------------------------------------------------------------------------------- /docs/integration.md: -------------------------------------------------------------------------------- 1 | # Example configuration 2 | 3 | If you want to create a standalone Jenkins with the example out-of-the-box configuration provided in this repository, do the following: 4 | 5 | ### Setup 6 | 7 | 1. Install dependencies. 8 | * [Terraform](https://www.terraform.io/) 9 | * [Ansible](http://docs.ansible.com/ansible/intro_installation.html) 10 | * [Terraform Inventory](https://github.com/adammck/terraform-inventory) 11 | 1. [Configure Terraform.](https://www.terraform.io/docs/providers/aws/#authentication) 12 | 1. Initialize Terraform. 13 | 14 | ```sh 15 | cd terraform 16 | terraform init 17 | ``` 18 | 19 | 1. Create an Ansible secrets file. 20 | 21 | ```sh 22 | cp tests/secrets-example.yml tests/group_vars/all/secrets.yml 23 | # set values for the variables 24 | ``` 25 | 26 | ### Usage 27 | 28 | Simple! Just run `make`. Use `make destroy` to tear it down. 29 | -------------------------------------------------------------------------------- /docs/manual_config.md: -------------------------------------------------------------------------------- 1 | # Manual configuration 2 | 3 | The following steps need to be done by hand. 4 | 5 | ### Access control 6 | 7 | 1. Follow [the Role-Based Strategy guide](https://plugins.jenkins.io/role-strategy#RoleStrategyPlugin-Userguide). 8 | * [More information about the various permissions](https://wiki.jenkins.io/display/JENKINS/Matrix-based+security) 9 | 1. Create a `developer` role with all but the `Agent` and `SCM` options. 10 | 1. Assign the role by adding a `group` called `authenticated`, selecting the `developer` checkbox. 11 | 12 | ### Audit trail 13 | 14 | 1. Click `Manage Jenkins`. 15 | 1. Click `Configure System`. 16 | 1. Under `Audit Trail`, click `Add Logger`, then `Log file`. 17 | 1. For `Log Location`, enter `/var/log/jenkins/audit.log`. 18 | 1. For `Log File Size`, enter `50` MB. 19 | 1. For `Log File Count`, enter `10`. 20 | 1. Click `Save`. 21 | 22 | ## Credentials 23 | 24 | 1. Visit `https://JENKINS_EXTERNAL_HOSTNAME/credentials/store/system/domain/_/newCredentials` 25 | 1. Fill in the form: 26 | 1. `Kind`: `SSH Username with private key` 27 | 1. `Scope`: `Global (Jenkins, nodes, items, all child items, etc)` 28 | 1. `Username`: `jenkins` 29 | 1. `Private Key`: `From the Jenkins master ~/.ssh` 30 | 1. `Passphrase`: the value from `vault_jenkins_ssh_key_passphrase` 31 | 1. `ID`: `jenkins-ssh-key` 32 | 1. `Description`: (empty) 33 | 1. Click `OK`. 34 | 1. Use these Credentials from your Jobs. 35 | 36 | ## Adding users 37 | 38 | For each user: 39 | 40 | 1. Generate a password. Here's an easy way on Linux: 41 | 42 | ```sh 43 | cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9!@#\$%^&\*\(\)\+=_' | head -c 64 44 | ``` 45 | 46 | 1. View the people list. 47 | 1. Click `Manage Jenkins`. 48 | 1. Click `Manage Users`. 49 | 1. If they exist already: 50 | 1. Click their username. 51 | 1. Click `Configure`. 52 | 1. Correct their `Full Name`. 53 | 1. Set their password. 54 | 1. If they don't, create a user for them. 55 | 1. Click `Create User`. 56 | 1. For the `Username`, use the first part of their GSA email. This will ensure that [it matches up to their commits](https://support.cloudbees.com/hc/en-us/articles/204498804-How-People-is-managed-by-Jenkins). 57 | 1. Send the password via [Fugacious](https://fugacious.18f.gov/). 58 | 59 | ## Other 60 | 61 | * Subscribe to [Jenkins security advisories](https://jenkins.io/security/). 62 | * When creating jobs/pipelines, don't include any spaces or special characters in the name, as this can break things in confusing ways. 63 | * Make sure to do cleanup, to prevent disk space from filling up. 64 | * [Discard old builds](https://jenkins.io/doc/book/pipeline/syntax/#available-options) 65 | * [Remove the workspace](https://jenkins.io/doc/pipeline/tour/post/#cleaning-up-and-notifications) 66 | -------------------------------------------------------------------------------- /docs/terraform.md: -------------------------------------------------------------------------------- 1 | # Terraform modules 2 | 3 | To create the Jenkins infrastructure, include the [Terraform modules](https://www.terraform.io/docs/modules/index.html) alongside your existing Terraform code: 4 | 5 | ```hcl 6 | # optional - use if you want a Jenkins-specific security group 7 | module "jenkins_networking" { 8 | source = ".//terraform/modules/networking" 9 | 10 | vpc_id = "${}" 11 | } 12 | 13 | module "jenkins_instances" { 14 | source = ".//terraform/modules/instances" 15 | 16 | subnet_id = "${ 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | true 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/group_vars/all/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GSA/jenkins-deploy/af222abb4d0c97a1d7647cd36d92184382525f0b/tests/group_vars/all/.keep -------------------------------------------------------------------------------- /tests/group_vars/all/ssh.yml: -------------------------------------------------------------------------------- 1 | --- 2 | jenkins_ssh_private_key_data: DUMMY_KEY 3 | jenkins_ssh_public_key_data: DUMMY_KEY 4 | -------------------------------------------------------------------------------- /tests/hosts-docker: -------------------------------------------------------------------------------- 1 | remote ansible_connection=docker 2 | -------------------------------------------------------------------------------- /tests/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # gsa.jenkins role dependencies, which should match what's in `dependencies` list of `../meta/main.yml`. 3 | - src: geerlingguy.jenkins 4 | - src: srsp.oracle-java 5 | - src: https://github.com/GSA/ansible-https-proxy 6 | name: gsa.https-proxy 7 | - src: mauromedda.ansible_role_terraform 8 | - src: mauromedda.ansible_role_terragrunt -------------------------------------------------------------------------------- /tests/secrets-example.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # note changing this after the initial setup has no effect 3 | jenkins_admin_password: ... 4 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | name: Set up Jenkins 4 | pre_tasks: 5 | - set_fact: 6 | jenkins_external_hostname: "{{ inventory_hostname }}" 7 | roles: 8 | # needs to match repository name; when the role is included normally, it will be `gsa.jenkins` 9 | - jenkins-deploy 10 | tasks: 11 | 12 | - name: Install jenkins_job dependencies 13 | yum: 14 | name: python-lxml 15 | 16 | - name: Set up pipeline 17 | jenkins_job: 18 | config: "{{ lookup('file', 'files/job.xml') }}" 19 | name: test-auto 20 | user: "{{ jenkins_admin_username }}" 21 | password: "{{ jenkins_admin_password }}" 22 | --------------------------------------------------------------------------------