commit fb0137cd2c7ca7a20ed9dc3a72b55e3e9457477f Author: Arvydas Silanskas Date: Sat Sep 28 11:03:12 2024 +0300 first diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..4284110 --- /dev/null +++ b/README.adoc @@ -0,0 +1,26 @@ += Docker based Jenkins setup through Configuration-as-Code + +== Running + +. Run `create-keys.sh` to generate SSH keys used for Jenkins controller to talk to Jenkins agent; +. Run `echo -n 'password' > adminpassword` to setup password for admin user (watchout to not add newlines); +. Run `docker compose up -d`; +. (Optionally) Add `update.sh` script to be run by cron periodically. + +If all went well jenkins should be reachable on `localhost:8080`, login with user `admin` and password from step 2. + +== Workflow for setting up jobs + +. A pull request is initiated on a git platform where this configuration is hosted, with necessary changes in `jenkins.yml` jobs section; +. Maintainer(s) review the change, merge if appropriate; +. Either periodically, manually, or on some way set up trigger, machine hosting Jenkins controller does a `git pull` and `docker compose up -d --build`, after which the changes should appear on CI. + +== User permissions and per-project secrets + +Jobs often need secrets, however these secrets should be scoped per-user / project. A solution is therefore to use folders and matrix authentication plugin. For each user or project a top level folder should be created. In this folder administrator configures full permissions to necessary users (folder view -> Configure -> General -> Enable project-based-security). Users are then able to and edit secrets, but only for their jobs. + +== Points of Improvement + +. Externalize user management (eg LDAP); +. Use vaults for secret storage; +. Change agent from persistent ssh to an adhoc provisioned instance. diff --git a/create-keys.sh b/create-keys.sh new file mode 100644 index 0000000..83c47e9 --- /dev/null +++ b/create-keys.sh @@ -0,0 +1,5 @@ +rm -f id_rsa +rm -f id_rsa.pub +ssh-keygen -t rsa -b 4096 -f ./id_rsa + +(echo -n 'JENKINS_AGENT_SSH_PUBKEY=' ; cat id_rsa.pub) > agent.env diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0bdf008 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +services: + jenkins-controller: + build: + context: . + dockerfile: jenkins.Dockerfile + environment: + - CASC_JENKINS_CONFIG=/jenkins.yml + volumes: + - ./jenkins.yml:/jenkins.yml + - ./id_rsa:/secrets/id_rsa + - ./adminpassword:/secrets/adminpassword + - jenkins-data:/var/jenkins_home + ports: + - 8080:8080 + jenkins-agent: + env_file: ./agent.env + build: + context: . + dockerfile: jenkins-agent.Dockerfile + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + +volumes: + jenkins-data: diff --git a/jenkins-agent.Dockerfile b/jenkins-agent.Dockerfile new file mode 100644 index 0000000..d77196a --- /dev/null +++ b/jenkins-agent.Dockerfile @@ -0,0 +1,4 @@ +FROM jenkins/ssh-agent:latest +COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/ +RUN groupadd docker -g 971 +RUN usermod -aG docker jenkins diff --git a/jenkins.Dockerfile b/jenkins.Dockerfile new file mode 100644 index 0000000..31397e0 --- /dev/null +++ b/jenkins.Dockerfile @@ -0,0 +1,2 @@ +FROM jenkins/jenkins:jdk17 +RUN jenkins-plugin-cli --plugins 'configuration-as-code:latest ssh-slaves:latest credentials-binding:latest subversion:latest job-dsl:latest pipeline-model-definition:latest pipeline-build-step:latest ansible:latest git:latest workflow-aggregator:latest docker-workflow:latest ws-cleanup:latest pipeline-graph-view:latest ssh-agent:latest matrix-auth:latest' diff --git a/jenkins.yml b/jenkins.yml new file mode 100644 index 0000000..7da76f5 --- /dev/null +++ b/jenkins.yml @@ -0,0 +1,88 @@ +jenkins: + systemMessage: "Jenkins CASC" + numExecutors: 0 + nodes: + - permanent: + mode: NORMAL + name: "agent1" + labelString: "docker" + numExecutors: 8 + remoteFS: "/home/jenkins/agent" + launcher: + SSHLauncher: + host: "jenkins-agent" + port: 22 + credentialsId: agent_ssh + launchTimeoutSeconds: 60 + maxNumRetries: 3 + retryWaitTime: 30 + sshHostKeyVerificationStrategy: + manuallyTrustedKeyVerificationStrategy: + requireInitialManualTrust: false + authorizationStrategy: + projectMatrix: + entries: + - group: + name: "authenticated" + permissions: + - "Job/Read" + - "Overall/Read" + - user: + name: "admin" + permissions: + - "Overall/Administer" + - user: + name: "anonymous" + permissions: + - "Job/Read" + - "Overall/Read" + securityRealm: + local: + allowsSignup: false + users: + - id: admin + name: admin + password: "${readFile:/secrets/adminpassword}" + +appearance: + pipelineGraphView: + showGraphOnBuildPage: true + showGraphOnJobPage: true + +credentials: + system: + domainCredentials: + - credentials: + - basicSSHUserPrivateKey: + scope: GLOBAL + id: agent_ssh + username: "jenkins" + description: "SSH key for jenkins agent" + privateKeySource: + directEntry: + privateKey: "${readFile:/secrets/id_rsa}" + +jobs: + - script: > + folder('index_scheme_org') { + displayName: 'index.scheme.org' + } + - script: > + folder('index_scheme_org2') { + displayName: 'index.scheme.org2' + } + - script: > + multibranchPipelineJob('index_scheme_org/build') { + displayName: 'Build and deploy' + branchSources { + git { + id('github') + remote('https://github.com/schemeorg-community/index.scheme.org') + } + } + orphanedItemStrategy { + discardOldItems { + numToKeep(10) + } + } + } diff --git a/update.sh b/update.sh new file mode 100644 index 0000000..a5ca375 --- /dev/null +++ b/update.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +git fetch +if [[ $(git rev-parse HEAD) != $(git rev-parse @{u}) ]] +then + echo 'Behind remote config; updating' + git pull + docker compose up -d --build +fi +