# How to Securely Access Jenkins Over HTTPS Using DuckDNS & Nginx

Jenkins is an essential tool for automating software builds, deployments, and CI/CD pipelines. If you're running Jenkins on an AWS EC2 instance, exposing it securely over the internet is crucial. By default, Jenkins runs on **port 8080** over HTTP, which isn't ideal for security.

In this guide, we'll walkthrough how to set up **DuckDNS** with **Nginx** as a reverse proxy and secure it with **Let's Encrypt SSL certificates**.

Let's get started!!!

## **Prerequisites**

Before we begin, ensure you have the following:

✅ An **AWS EC2 instance** running Linux (Amazon Linux, Ubuntu, or CentOS) with Jenkins installed.  
✅ **Nginx** installed as a reverse proxy.  
✅ A **DuckDNS subdomain** (e.g., **jenkins-projectlab1.duckdns.org**).  
✅ A **security group** that allows inbound traffic on ports **8080 (Jenkins), 80 (HTTP)** and **443 (HTTPS)**.

If you haven’t set up Jenkins yet. You will have to install OpenJDK 17 or newer which is required by Jenkins. For this tutorial I’ll launch an Amazon linux 2023 EC2 instance (t2 medium). You can use any instance you like by make sure you there is enough storage and memory (2 CPU minimum).

First install java:

```bash
sudo dnf install -y java-17-amazon-corretto
#verify installation
java -version
```

## **Now Proceed with Jenkins Installation**

After installing Java, continue with these steps:

**a.** Add the Jenkins repository:

```bash
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
```

**b.** Install Jenkins:

```bash
sudo dnf install -y jenkins
```

**c.** Enable and start Jenkins:

```bash
sudo systemctl enable --now jenkins

#Check if Jenkins is running
sudo systemctl status jenkins
```

By default, Jenkins runs on **http://your-ec2-ip:8080**. Let’s make it accessible via **https://jenkins-projectlab1.duckdns.org.**

## **Step 1: Set Up DuckDNS for Your Domain**

**a.** Go to [DuckDNS](https://www.duckdns.org/) and create an account if you don’t have one then sign in using your **GitHub** or **Google** account.  
**b.** Under “subdomains” enter a name (e.g., jenkins-projectlab1) and click add domain.  
**c.** Your full domain will be: jenkins-projectlab1.duckdns.org  
**d.** Copy your **EC2 Public IP** from AWS then go to DuckDNS and update the IP field with your EC2 public IP.

### **Set Up Automatic Updates (Recommended)**

Since EC2 instances often get dynamic IPs, use a script to update DuckDNS automatically, so you don’t have to manually copy the EC2 instance IP and paste in DuckDNS when the IP changes.

To write a script to automatically update DuckDNS with your EC2 instance IP follow the steps below:

**a**. Connect to your EC2 instance via SSH.

**b**. Create and cd into the directory\*\*:\*\*

```bash
mkdir -p ~/duckdns && cd ~/duckdns
```

**c.** Create an executable script to automatically update a new IP in DuckDNS when it changes:

```bash
nano duck.sh
```

**d**. Login into DuckDNS to get your token and domain name. Add the following lines (replace **&lt;YOUR-DUCKDNS-TOKEN&gt;** and **&lt;YOUR-DOMAIN**\&gt;) :

```bash
echo url="https://www.duckdns.org/update?domains=<YOUR-DOMAIN>&token=<YOUR-DUCKDNS-TOKEN>&ip=" | curl -k -o ~/duckdns/duck.log -K -
```

**e.** Save and exit.

**f.** Make the `duck.sh` file executable:

```bash
chmod +x duck.sh
```

**g.** Test the script:

```bash
./duck.sh

#Check duck.log to confirm the update:
cat duck.log
```

### **Automate Updates with Cron**

**Cron** is a time-based job scheduler in Unix/Linux systems used to automate tasks at specified intervals. It runs scheduled commands or scripts without manual intervention.

First install Cron on your EC2 Instance:

```bash
#For Amazon Linux / Amazon Linux 2
sudo yum install cronie -y

#For Ubuntu/Debian:
sudo apt update && sudo apt install cron -y

#Start and Enable the Cron Service
sudo systemctl start crond
sudo systemctl enable crond

#Verify Cron is Running
systemctl status crond
```

Next, Open the cron editor:

```bash
crontab -e
```

Add your cron job (e.g., updating DuckDNS every 5 minutes or however you choose):

```bash
#This updates your IP every 5 minutes.
*/5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1
```

### **Verify Your DuckDNS Domain**

Open your browser and go to: **http://jenkins-projectlab1.duckdns.org:8080**

**Note:** Ensure your EC2 security group allows traffic on port **8080**, **80** & **443** is for **Jenkins**, **HTTP** & **HTTPS** respectively.

## **Step 2: Install & Configure Nginx as a Reverse Proxy**

A **reverse proxy** is a server that sits between client devices and backend servers, forwarding client requests to the appropriate backend service.

For a better understanding, a **reverse proxy** is like a **middleman** between users and a website or app. When you visit a website, instead of connecting directly to the main server, your request first goes through the reverse proxy. The proxy then **forwards your request** to the correct backend server and sends the response back to you.

**Why Use a Reverse Proxy?**

**Security** – Hides the real server, protecting it from attacks. **Faster Loading** – Stores and delivers frequently used content quickly. **Load Balancing** – Spreads traffic across multiple servers to prevent overload. **HTTPS Support** – Handles encryption to secure connections.

To access Jenkins over HTTPS using Nginx as a reserve proxy follow these steps.

First install Nginx:

```bash
sudo yum install -y nginx  # For Amazon Linux/CentOS
sudo apt install -y nginx  # For Ubuntu/Debian

#Start and enable Nginx
sudo systemctl enable --now nginx
```

### **Configure Nginx for Jenkins**

Create a new Nginx configuration file:

```bash
sudo nano /etc/nginx/conf.d/jenkins.conf
```

Add the following content:

```bash
server {
    listen 80;
    server_name jenkins-projectlab1.duckdns.org;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

Save and exit (‘:wq’ if using vim editor).

Test and restart Nginx:

```bash
sudo nginx -t
sudo systemctl restart nginx
```

After execution of `sudo nginx -t` command, you should get “**syntax is ok**” & **“test is successful”** confirmation on your terminal.

At this point, your Jenkins should be accessible via **http://jenkins-projectlab1.duckdns.org**.

---

## **Step 3: Secure Jenkins with ‘Let's Encrypt’ SSL**

Now, let’s install **Certbot** to get a free SSL certificate so we can access Jenkins via HTTPS:

First, install Certbot:

```bash
#For Amazon Linux/CentOS:
sudo yum install -y certbot python3-certbot-nginx

#For Ubuntu/Debian
sudo apt install -y certbot python3-certbot-nginx
```

### **Generate an SSL Certificate**

Next step, run the codes below to generate an SSL Certificate for our DuckDNS domain:

```bash
sudo certbot --nginx -d jenkins-projectlab1.duckdns.org
```

Follow the prompts:

* Enter your **email** address.
    
* Agree to the **terms of service**.
    
* Certbot will configure Nginx for HTTPS automatically.
    

If it doesn’t, manually update your Nginx config:

```bash
sudo nano /etc/nginx/conf.d/jenkins.conf
```

Replace with:

```bash
server {
    listen 80;
    server_name your-subdomain.duckdns.org;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name jenkins-projectlab1.duckdns.org;

    ssl_certificate /etc/letsencrypt/live/jenkins-projectlab1.duckdns.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jenkins-projectlab1.duckdns.org/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

Save and restart Nginx:

```bash
sudo nginx -t
sudo systemctl restart nginx
```

**Note:** Certbot will most likely configure Nginx for HTTPS automatically hence you may not need to update manually.

## **Step 4: Enable Auto-Renewal for SSL**

Let's Encrypt certificates expire every **90 days**. To renew them automatically, set up a cron job:

```bash
sudo crontab -e
```

Add this line at the bottom:

```bash
0 0 * * * certbot renew --quiet && systemctl reload nginx
```

This will renew the SSL certificate **daily at midnight**.

---

## **Final Test** 🎉

Now, open your browser and visit **https://jenkins-projectlab1.duckdns.org**

Your Jenkins is now running securely over **HTTPS**! 🚀

---

## **Wrapping Up**

By following this guide, you:

✅ Configured **DuckDNS** for dynamic DNS.  
✅ Set up **Nginx** as a reverse proxy for Jenkins.  
✅ Secured Jenkins with **Let's Encrypt SSL**.  
✅ Enabled **auto-renewal** for SSL certificates.

Your Jenkins instance is now secure and accessible over HTTPS from anywhere, and you can use this process to assign a free domain to any IP or resource accessed via HTTPS.

Have questions? Let me know in the comments! 😊
