Jenkins LTS 2.580.2 on AWS User Guide
Overview
This guide covers launching and using the Jenkins LTS 2.580.2 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Apache Subversion (SVN) & CI/CD Tools listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Jenkins is the open source automation server for continuous integration and delivery. It runs jobs (builds, tests, deployments) on its own executors or on agents, triggered by hand, on a schedule, by the REST API or by the command line client, and keeps their history and console output.
The cloudimg image adds:
- No setup wizard and no shared password. Upstream's first start shows an unlock page with a one-time password; this image never does. First boot creates Jenkins' own user database with the
adminaccount and a 24 character password generated on your instance, and writes it to/etc/cloudimg-credentials.txt(root only). Jenkins refuses to start on this image until first boot has secured it. - Nothing shared between instances. The image ships an empty
JENKINS_HOME. Jenkins creates its ownsecret.key,master.keyand the keys that encrypt stored credentials at its first start on your instance, and first boot creates the HTTPS certificate and SSH host keys there too. - A credential for everything. Anonymous read access is off and there is no sign-up page: every page, the REST API and the CLI need a signed-in user or an API token. Logged-in users can do anything; add other accounts only for people you trust (see the CI-risk notes).
- The password only on the login form. The admin password is accepted only by Jenkins' sign-in page, which nginx rate-limits per client address (10 a minute, with a burst of 10). Scripts, the REST API and the CLI use an API token instead, which you create, name and revoke per use. CSRF protection is on.
- Jenkins on loopback behind nginx. Jenkins listens on
127.0.0.1:8080; nginx serves it on port 443 with a certificate generated on your instance, and port 80 only redirects. The inbound TCP agent port is off: agents connect over WebSocket through port 443. - No plugins. This is Jenkins core: freestyle jobs, the shell build step, the REST API, the CLI and Jenkins' own user database. Add the plugins you need from Manage Jenkins > Plugins.
- Builds kept away from the metadata service. Jenkins and every build it runs on the instance live in a systemd sandbox that denies the EC2 instance metadata service, with no capabilities, no privilege escalation (
sudo,su) and a read-only system outside Jenkins' own folders. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Jenkins LTS 2.580.2 (MIT, the upstream package from pkg.jenkins.io), OpenJDK 21 and nginx from Ubuntu 24.04, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Read this before you share it: builds run with Jenkins' own access. Builds run on this instance as the
jenkinsuser, the same account as Jenkins itself. Anyone who can create or change a job can run any command as that user, so job authors can read everything Jenkins keeps inJENKINS_HOME, including the keys that decrypt stored credentials. Only let trusted people create or change jobs, keep the instance metadata hop limit at 1, attach no instance profile (or a least-privilege one), and for untrusted work set the built-in node's executors to 0 and build on agents. Step 7 explains each point.Trademark notice. Jenkins is a registered trademark of LF Charities Inc. (the Jenkins project). cloudimg is not affiliated with, endorsed by or sponsored by the Jenkins project or LF Charities; the name is used only to identify the open source software in this image, which is the unmodified upstream Jenkins LTS release.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet.
- A security group that allows TCP 443 (Jenkins over HTTPS) and TCP 80 (redirects to HTTPS) from the networks your users work from, and TCP 22 (SSH) from your own network only. Jenkins needs no other inbound port: agents connect over 443.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Jenkins LTS 2.580.2 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair, a security group as above, and a subnet. Jenkins keeps its jobs, build history and workspaces on the root volume under /var/lib/jenkins; raise its size under Configure storage if you keep many builds or large workspaces. Under Advanced details, leave IAM instance profile empty (or choose a least-privilege one), set Metadata version to V2 only and Metadata response hop limit to 1.
With the AWS CLI (replace the values in angle brackets; this sets a 50 GiB root volume and no instance profile):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled,HttpPutResponseHopLimit=1 \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=50,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=jenkins}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Jenkins LTS 2.580.2 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot works out the instance's name, generates the certificate and the admin password, starts Jenkins once to create the admin account, proves over HTTPS that anonymous requests are refused and the new password is accepted, and only then opens the web server. It takes about two minutes:
systemctl is-active jenkins nginx jenkins-set-host
active
active
active
jenkins-set-host runs on every boot before nginx: it keeps the served name, the certificate and nginx's host rules in step (Step 9).
Only SSH (22) and nginx (80, 443) listen on the network. Jenkins (8080) is on the loopback address only (Java shows it as the IPv4-mapped [::ffff:127.0.0.1]). The :53 entries are Ubuntu's local DNS resolver, :323 is the time service on the loopback address and :68 is the DHCP client:
sudo ss -lntup | awk 'NR>1 {print $1, $5}' | sort -u
tcp 0.0.0.0:22
tcp 0.0.0.0:443
tcp 0.0.0.0:80
tcp 127.0.0.53%lo:53
tcp 127.0.0.54:53
tcp [::]:22
tcp [::]:443
tcp [::]:80
tcp [::ffff:127.0.0.1]:8080
udp 127.0.0.1:323
udp 127.0.0.53%lo:53
udp 127.0.0.54:53
udp 172.31.81.62%ens5:68
udp [::1]:323
Jenkins runs as the jenkins account on Ubuntu's OpenJDK 21, from the package's own jenkins.service with cloudimg's settings in /etc/systemd/system/jenkins.service.d/10-cloudimg.conf:
ps -o user:10=,args= -C java
java -version 2>&1 | head -1
jenkins /usr/bin/java -Djava.awt.headless=true -Djenkins.install.runSetupWizard=false -Djenkins.security.ignoreBasicAuth=true -Dhudson.model.UsageStatistics.disabled=true -XX:MaxRAMPercentage=50 -XX:+ExitOnOutOfMemoryError -jar /usr/share/java/jenkins.war --webroot=/var/cache/jenkins/war --httpPort=8080 --httpListenAddress=127.0.0.1
openjdk version "21.0.12.1" 2026-08-18
runSetupWizard=false turns the unlock wizard off (first boot has already secured Jenkins), ignoreBasicAuth=true makes Jenkins refuse a password sent as HTTP Basic (Step 4), and usage statistics are not sent. Jenkins may use up to half the instance's memory for its Java heap, so a larger instance type gives it more room without any change.
If nginx is not running, first boot has not finished, or it failed, in which case the web server is never opened. Check systemctl status jenkins-firstboot and sudo tail -40 /var/log/cloudimg/jenkins-firstboot.log.
Step 4: Get your address and password
sudo cat /etc/cloudimg-credentials.txt
The file is readable by root only and holds the address, the admin user name and password and the certificate fingerprint:
# Jenkins - generated on first boot. Unique to this instance; keep it safe.
# Sign in at JENKINS_URL as JENKINS_ADMIN_USER / JENKINS_ADMIN_PASSWORD (the login form; API calls and the CLI need an
# API token: your name > Security > API Token). Compare JENKINS_TLS_SHA256 with the certificate your browser shows (or
# install your own certificate: see the guide). Rotate the password: sudo jenkins-set-password
JENKINS_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
JENKINS_ADMIN_USER=admin
JENKINS_ADMIN_PASSWORD=<JENKINS_ADMIN_PASSWORD>
JENKINS_TLS_SHA256=15:1A:95:B3:94:79:1A:2D:4C:0E:A8:91:DC:F4:BC:AF:C8:80:66:6B:22:BD:AA:50:89:ED:C0:2C:F3:0E:BB:1B
To see everything but the password:
sudo grep -E '^JENKINS_(URL|ADMIN_USER|TLS_SHA256)=' /etc/cloudimg-credentials.txt
JENKINS_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
JENKINS_ADMIN_USER=admin
JENKINS_TLS_SHA256=15:1A:95:B3:94:79:1A:2D:4C:0E:A8:91:DC:F4:BC:AF:C8:80:66:6B:22:BD:AA:50:89:ED:C0:2C:F3:0E:BB:1B
You can confirm the whole path from the instance itself. The helper goes through nginx over HTTPS with the certificate pinned. It checks that 15 kinds of page, the script console and the CLI are refused without a credential, that there is no sign-up page, that a wrong password and the well known defaults are refused, that the password is refused when sent as HTTP Basic, and that this instance's password signs in on the login form:
sudo /usr/local/sbin/jenkins-verify.sh
JENKINS_ROUNDTRIP_OK (HTTPS: 15 routes 403 + scriptText 403 + jnlpJars/cli 401 anonymously, sign-up 404, wrong/default passwords refused, the password refused as HTTP Basic; the minted password accepted on the login form, session admin)
Change the password with jenkins-set-password. With no argument it generates a new 24 character password; with - it reads yours from standard input (at least 16 printable characters, no spaces). It never accepts the password as an argument, so it does not appear in the process list or your shell history. Jenkins restarts to apply it, so running builds are interrupted. The command updates /etc/cloudimg-credentials.txt, and it is also the way back in if the password is lost:
$ sudo jenkins-set-password
jenkins-set-password: the admin password was replaced (sudo cat /etc/cloudimg-credentials.txt)
To choose your own:
$ read -rs NEWPW && printf '%s\n' "$NEWPW" | sudo jenkins-set-password - ; unset NEWPW
jenkins-set-password: the admin password was replaced (sudo cat /etc/cloudimg-credentials.txt)
Step 5: Sign in
Open JENKINS_URL in your browser. The certificate is self-signed until you install your own (Step 9), so the browser warns once; compare the fingerprint it shows with JENKINS_TLS_SHA256. Jenkins shows its sign-in page; use admin and the password from Step 4:

The dashboard lists your jobs (none yet), the build queue and the built-in node's two executors. A red dot on the gear icon means Jenkins has a notice for administrators: open Manage Jenkins to read it.

Under Manage Jenkins > Security you can see what first boot configured: Jenkins' own user database with sign-up off, and Logged-in users can do anything with anonymous read access off. Keep me signed in sets a long-lived cookie; tick Disable "Keep me signed in" if you would rather every session expire.

Add accounts under Manage Jenkins > Users > Create User. Every account can do everything Jenkins can, including run any command on this instance through a job, so create accounts only for trusted people. For finer permissions, install the Matrix Authorization Strategy or Role-based Authorization Strategy plugin.
Step 6: Create and run a job
Choose Create a job (or New Item), give it a name, pick Freestyle project and OK. Under Build Steps add Execute shell and type the commands to run, then Save:

Choose Build Now, then the build number and Console Output. The shell step ran as the jenkins user on this instance; Running as SYSTEM is Jenkins' own way of saying the build has Jenkins' full authority inside Jenkins:

The same from a script, with an API token. The REST API and the CLI do not take your password; they take an API token, which you create under your name (top right), Security, API Token, Add new token. The token is shown once; keep it in a password manager and revoke it when you no longer need it. This round trip on the instance itself signs in on the login form, creates a token, uses it to create a job whose shell step also tries the metadata service, runs it and prints the console, then deletes the job and revokes the token. The password and the token are read from and written to files only, never printed or put on a command line:
D=$(mktemp -d) && cd "$D" && umask 077
HOST=$(sudo sed -n 's#^JENKINS_URL=https://\([^/]*\)/$#\1#p' /etc/cloudimg-credentials.txt)
B="https://${HOST}"
C=(-s --max-time 30 --cacert /etc/ssl/jenkins/jenkins.crt --resolve "${HOST}:443:127.0.0.1")
sudo sed -n 's/^JENKINS_ADMIN_PASSWORD=//p' /etc/cloudimg-credentials.txt | tr -d '\n' > pw
# 1. sign in on the login form (it needs the page's session cookie and its Jenkins-Crumb field)
curl "${C[@]}" -c jar -o login.html "$B/login"
grep -oE '<input[^>]*name="Jenkins-Crumb"[^>]*>' login.html | sed -nE 's/.*value="([^"]*)".*/\1/p' | head -1 | tr -d '\n' > crumb
curl "${C[@]}" -b jar -c jar -o /dev/null -w 'sign in: %{http_code}\n' --data-urlencode j_username=admin \
--data-urlencode j_password@pw --data-urlencode Jenkins-Crumb@crumb --data-urlencode from=/ "$B/j_spring_security_check"
curl "${C[@]}" -b jar -c jar "$B/crumbIssuer/api/json" | python3 -c 'import json,sys; print("Jenkins-Crumb: " + json.load(sys.stdin)["crumb"])' > crumb.hdr
# 2. create an API token for this script
curl "${C[@]}" -b jar -c jar -H @crumb.hdr --data newTokenName=guide-demo -o token.json \
"$B/me/descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken"
python3 -c 'import json,sys; d=json.load(open("token.json"))["data"]; open("auth.cfg","w").write("user = \"admin:%s\"\n" % d["tokenValue"]); open("uuid","w").write(d["tokenUuid"])'
T=("${C[@]}" -K auth.cfg)
# 3. create a freestyle job with one shell step, run it and print its console
cat > job.xml <<'XML'
<?xml version='1.1' encoding='UTF-8'?>
<project><description>Created by the cloudimg guide.</description><builders><hudson.tasks.Shell><command>echo "Hello from Jenkins as $(id -un)"
echo "metadata service from a build: $(curl -s -m 3 -o /dev/null -w '%{http_code}' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token || true)"
</command></hudson.tasks.Shell></builders></project>
XML
curl "${T[@]}" -H 'Content-Type: application/xml' --data-binary @job.xml -o /dev/null -w 'create job: %{http_code}\n' "$B/createItem?name=guide-demo"
curl "${T[@]}" -X POST -o /dev/null -w 'start build: %{http_code}\n' "$B/job/guide-demo/build"
for i in $(seq 1 60); do
R=$(curl "${T[@]}" -f "$B/job/guide-demo/lastBuild/api/json?tree=result" 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin).get("result") or "")' 2>/dev/null || true)
[ -n "$R" ] && break; sleep 2
done
curl "${T[@]}" "$B/job/guide-demo/lastBuild/consoleText" | grep -vE '^(Running as|Building in|\[guide-demo\])'
# 4. tidy up: delete the job, revoke the token, remove the files
curl "${T[@]}" -X POST -o /dev/null -w 'delete job: %{http_code}\n' "$B/job/guide-demo/doDelete"
curl "${C[@]}" -b jar -c jar -H @crumb.hdr --data-urlencode tokenUuid@uuid -o /dev/null -w 'revoke token: %{http_code}\n' \
"$B/me/descriptorByName/jenkins.security.ApiTokenProperty/revoke"
cd / && rm -rf "$D"
sign in: 302
create job: 200
start build: 201
Started by user admin
+ id -un
+ echo Hello from Jenkins as jenkins
Hello from Jenkins as jenkins
+ curl -s -m 3 -o /dev/null -w %{http_code} -X PUT -H X-aws-ec2-metadata-token-ttl-seconds: 60 http://169.254.169.254/latest/api/token
+ true
+ echo metadata service from a build: 000
metadata service from a build: 000
Finished: SUCCESS
delete job: 302
revoke token: 200
000 means the build could not reach the metadata service at all. From your workstation, use the same calls with your token in place of the sign-in, for example curl -u admin:<api-token> https://<host>/api/json.
The CLI works over HTTPS with WebSocket and an API token (the remoting CLI is gone from Jenkins, and nginx refuses /cli and /jnlpJars without credentials). Download jenkins-cli.jar with your token, then:
curl -fsS -u admin:<api-token> -o jenkins-cli.jar https://<host>/jnlpJars/jenkins-cli.jar
java -jar jenkins-cli.jar -s https://<host>/ -webSocket -auth admin:<api-token> who-am-i
Authenticated as: admin
While the certificate is self-signed, import it into a Java trust store and pass it with -Djavax.net.ssl.trustStore=..., or install your own certificate (Step 9).
Step 7: CI risk: who can run what
Jenkins is a tool for running other people's code, so who may define jobs is the main security decision. On this image:
- Builds run on the instance as the
jenkinsuser. The built-in node has 2 executors, so a freestyle shell step (or any build step) runs inside the Jenkins service, as the same account as Jenkins. - Job authors can read Jenkins' secrets. A build can read every file in
JENKINS_HOME(/var/lib/jenkins): the configuration,secrets/master.keyandsecrets/hudson.util.Secret, and with them every credential Jenkins stores. Treat can create or change a job as can act as Jenkins. Only trusted people should have accounts. - What a build cannot do here. The service's sandbox applies to every build on the built-in node: no
sudoorsu(no new privileges), no capabilities, a read-only system outside/var/lib/jenkins,/var/cache/jenkinsand/var/log/jenkins, a private/tmp, and no route to the instance metadata service (169.254.169.254 and its IPv6 address). Thejenkinsaccount has no login shell, is in no group but its own (notdocker), cannot usecron,at,systemd-runor lingering user services, and SSH refuses it, so a build cannot start a process outside the sandbox. The credentials file/etc/cloudimg-credentials.txt, the TLS key and the SSH host keys are readable by root only, so a build cannot read them. - Keep the hop limit at 1. The metadata block covers the Jenkins service. A metadata hop limit of 1 (Step 1) keeps any container you later run on this host from fetching IMDSv2 tokens as well.
- Attach no instance profile, or a least-privilege one. Other software on the instance, and root, can still reach the metadata service, so an instance profile's permissions are only as safe as the instance. Give Jenkins cloud credentials through Jenkins' credentials store (or an agent's own role) scoped to what the jobs need.
- Untrusted work goes on agents. To build pull requests or code from people you do not fully trust, set the built-in node's executors to 0 (Manage Jenkins > Nodes > Built-In Node > Configure > Number of executors) and run builds on separate agent machines that hold nothing Jenkins would miss.
The sandbox and the metadata block, as systemd applies them to Jenkins (and so to its builds) and nginx:
systemctl show jenkins -p User -p NoNewPrivileges -p ProtectSystem -p ProtectHome -p PrivateTmp -p CapabilityBoundingSet -p UMask
systemctl show jenkins nginx -p Id -p IPAddressDeny
UMask=0027
CapabilityBoundingSet=
User=jenkins
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
NoNewPrivileges=yes
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
Id=jenkins.service
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
Id=nginx.service
The jenkins account and the ways out of the sandbox that are closed:
id jenkins
getent passwd jenkins | cut -d: -f7
cat /etc/cron.allow
sudo sshd -T | grep -iE '^(denyusers|passwordauthentication|permitrootlogin)'
uid=997(jenkins) gid=987(jenkins) groups=987(jenkins)
/usr/sbin/nologin
root
permitrootlogin no
passwordauthentication no
denyusers jenkins
Step 8: Agents and plugins
Agents. The inbound TCP agent port is off, so agents use WebSocket through port 443. Under Manage Jenkins > Nodes > New Node, create a Permanent Agent with Launch agent by connecting it to the controller, tick Use WebSocket, and save. The node page then shows the command to start the agent on its machine; agent.jar downloads need your API token (curl -u admin:<api-token> ...) because nginx refuses /jnlpJars without credentials. To start agents over SSH instead, install the SSH Build Agents plugin. Use a machine for each agent that you are willing to let job authors control.
Plugins. There are no plugins on this image. Jenkins can reach the update center over the internet, so Manage Jenkins > Plugins > Available plugins installs them (Git, Pipeline, credentials bindings and so on). Each plugin runs inside Jenkins with its full access: install only what you need, keep them updated from the same page, and read the security warnings Jenkins shows there.
Step 9: Your own host name and certificate
On every boot jenkins-set-host works out the name to serve: the name you set, else the instance's public DNS name from the instance metadata service (IMDSv2). The Jenkins URL (Manage Jenkins > System) follows it:
systemctl is-enabled jenkins-set-host.service
sudo jenkins-set-hostname --boot
enabled
jenkins-set-hostname: unchanged (ec2-203-0-113-10.compute-1.amazonaws.com)
Use your own DNS name. Point a DNS record at the instance (an Elastic IP keeps the address fixed), then run sudo jenkins-set-hostname jenkins.example.com and sudo systemctl restart jenkins. The name is kept in /etc/cloudimg/jenkins-hostname for every later boot, the credentials file is updated, nginx serves that name and redirects the instance's other addresses to it, and browser requests from other origins are refused. A Jenkins URL you set by hand is left alone. Because the certificate in place was the one cloudimg generated, it is re-created for the new name.
Which certificates are replaced. Only a certificate cloudimg generated is ever re-created: its SHA-256 fingerprint is recorded, root owned, in /var/lib/cloudimg/jenkins-cert.sha256, and a certificate is treated as cloudimg's only while it matches that record:
sudo cat /var/lib/cloudimg/jenkins-cert.sha256
openssl x509 -in /etc/ssl/jenkins/jenkins.crt -noout -fingerprint -sha256
15:1A:95:B3:94:79:1A:2D:4C:0E:A8:91:DC:F4:BC:AF:C8:80:66:6B:22:BD:AA:50:89:ED:C0:2C:F3:0E:BB:1B
sha256 Fingerprint=15:1A:95:B3:94:79:1A:2D:4C:0E:A8:91:DC:F4:BC:AF:C8:80:66:6B:22:BD:AA:50:89:ED:C0:2C:F3:0E:BB:1B
Install your own certificate (from your CA, or Let's Encrypt with a DNS challenge) by putting the full chain and the key at the same paths. Anything there that does not match the record is yours and is never replaced, not on boot and not when the name changes:
sudo install -m 0644 -o root -g root fullchain.pem /etc/ssl/jenkins/jenkins.crt
sudo install -m 0600 -o root -g root privkey.pem /etc/ssl/jenkins/jenkins.key
sudo nginx -t && sudo systemctl reload nginx
Go back to a generated certificate by removing both files and running the helper with the name to serve:
$ sudo rm /etc/ssl/jenkins/jenkins.crt /etc/ssl/jenkins/jenkins.key
$ sudo jenkins-set-hostname jenkins.example.com
To return to the instance's own public name on every boot, sudo rm /etc/cloudimg/jenkins-hostname && sudo jenkins-set-hostname --boot.
Step 10: Backups and updates
Everything Jenkins stores is under /var/lib/jenkins: the configuration, jobs and build history, accounts, plugins and the secrets/ folder. Back it up with an EBS snapshot of the root volume, or stop Jenkins and copy the folder. The backup holds secrets/master.key, which decrypts every stored credential, so keep it as safe as the instance:
sudo systemctl stop jenkins
sudo tar -C /var/lib -czf /root/jenkins-backup.tar.gz jenkins
sudo systemctl start jenkins
Ubuntu packages, OpenJDK and nginx included, are updated by unattended security upgrades. Jenkins itself comes from the Jenkins project's LTS repository (pkg.jenkins.io/debian-stable, its signing key pinned), which unattended upgrades does not touch: sudo apt-get update && sudo apt-get install jenkins moves to the newest LTS release when you choose, and Jenkins restarts on it:
systemctl is-enabled unattended-upgrades apt-daily-upgrade.timer
cat /etc/apt/sources.list.d/jenkins.list
dpkg-query -W -f='${Package} ${Version}\n' jenkins
enabled
enabled
deb [signed-by=/usr/share/keyrings/jenkins-keyring.gpg] https://pkg.jenkins.io/debian-stable binary/
jenkins 2.580.2
Back up /var/lib/jenkins before an upgrade. cloudimg also publishes a new version of this listing for new Jenkins LTS releases.
Security notes
- Credential first. Anonymous users get the sign-in page and nothing else (
403for every page, the API and the script console). nginx answers/cliand/jnlpJarswithout credentials with401and/signupwith404. Sign-up is off. - Password or token, never both ways. The password works only on the sign-in form (rate-limited per client address); the REST API and CLI take API tokens, which a user can revoke one by one.
- Exact listeners. Only 22, 80 and 443 listen on the network (Step 3); Jenkins is on loopback and the TCP agent port is off. Port 80 only redirects to HTTPS. Open the ports in the security group only to the networks that need them.
- Forwarded headers. nginx sends Jenkins the configured host name,
https, port 443 and the real client address (X-Forwarded-Foris set from the connection, never passed through), drops client-suppliedForwardedheaders, and refuses state-changing requests and WebSocket upgrades from other origins. Responses carry HSTS. - Instance metadata. Jenkins, its builds, nginx, first boot and the helper commands are denied the EC2 instance metadata service by systemd (Step 7). Launch with IMDSv2 required and a hop limit of 1, as in Step 1.
- Secrets per instance. The admin password, Jenkins'
secret.key,master.keyand credential keys, the HTTPS certificate and the SSH host keys are all created on your instance's first boot; none is in the image.
Build provenance and licence
Jenkins LTS 2.580.2 is installed from the Jenkins project's Debian LTS repository with apt, with the repository's signing key pinned to fingerprint 5E386EADB55F01504CAE8BCF7198F4B714ABFC68; the build also checks the package's and the war's SHA-256. No plugin is installed:
head -3 /usr/share/doc/cloudimg/jenkins-BUILD-INFO.txt
sha256sum /usr/share/java/jenkins.war
sudo ls -A /var/lib/jenkins/plugins | wc -l
Jenkins LTS 2.580.2 on Ubuntu 24.04 (cloudimg AWS bundle member dev-cicd)
package jenkins=2.580.2 from https://pkg.jenkins.io/debian-stable (deb sha256 c0a735a91d20b6a0eb60052b81524f939d6eec58d6e8d9546414913f66c54630), Signed-By /usr/share/keyrings/jenkins-keyring.gpg = exactly key 5E386EADB55F01504CAE8BCF7198F4B714ABFC68
war /usr/share/java/jenkins.war sha256 7adc065557c52e464ce9221a5a6a3de642f7b02e00b6c01043c0e5835a1c61b6; MIT (core); plugins: none
7adc065557c52e464ce9221a5a6a3de642f7b02e00b6c01043c0e5835a1c61b6 /usr/share/java/jenkins.war
0
Jenkins core is MIT licensed; the libraries inside the war carry their own open source licences. Jenkins' source: github.com/jenkinsci/jenkins (tag jenkins-2.580.2). Jenkins security advisories are published at jenkins.io/security/advisories; cloudimg publishes a new version of this listing when an LTS release fixes one.
Troubleshooting
- "Invalid username or password". Read the password again from
/etc/cloudimg-credentials.txt(the user name isadmin). Afterjenkins-set-passwordthe old one is refused. 401from a script or the CLI with the right password. Scripts and the CLI need an API token, not the password (Step 6).403 No valid crumb was included in the request. A form post or script used a session cookie without the crumb. Use an API token for scripts; tokens need no crumb.429 Too Many Requests. Too many sign-in attempts from one address (10 a minute) or too many requests in a short time. Wait a minute.403on a form or WebSocket from another site, or the connection dropped with no answer. The request named a host or origin the instance does not serve. Use the name inJENKINS_URL, or set yours withjenkins-set-hostname(Step 9).- Builds stay in the queue. The built-in node has 0 executors and no agent is online (Step 8).
- A build cannot reach the metadata service or use
sudo. That is the sandbox (Step 7). Give jobs cloud credentials through Jenkins' credentials store or run them on an agent. - Logs:
journalctl -u jenkins,/var/log/nginx/access.logand/var/log/nginx/error.log, and/var/log/cloudimg/jenkins-firstboot.log.
Support
cloudimg images come with 24/7 support. Email support@cloudimg.co.uk with your AWS account ID, the instance ID and the output of sudo /usr/local/sbin/jenkins-verify.sh. For Jenkins itself, see the Jenkins documentation.