CresignSys Learn — Lesson 069

Written by

in

Build hosting-ssl

We now have:

hosting-create
hosting-list
hosting-info
hosting-health
hosting-repair

The next component is SSL automation.

The target command is:

sudo hosting-ssl example.com

It should convert:

HTTP

into:

HTTPS

without disturbing the existing website.


1. SSL Architecture

The complete flow becomes:

                hosting-ssl
                     │
                     ▼
              Check domain
                     │
                     ▼
              Check DNS
                     │
                     ▼
              Check HTTP
                     │
                     ▼
          Request certificate
                     │
                     ▼
             Let's Encrypt
                     │
                     ▼
             Configure Nginx
                     │
                     ▼
             nginx -t
                     │
                     ▼
           Reload Nginx
                     │
                     ▼
          Test HTTPS
                     │
                     ▼
          Enable auto-renewal

2. Most Important Rule

SSL provisioning must not break HTTP.

Bad workflow:

request certificate
      ↓
modify Nginx
      ↓
configuration fails
      ↓
website DOWN

Better:

HTTP working
      ↓
certificate
      ↓
generate HTTPS configuration
      ↓
validate
      ↓
activate

3. Why DNS Comes First

For:

example.com

the DNS record should point to your server:

example.com
     ↓
SERVER IP

For a subdomain:

learn.cresignsys.com
     ↓
SERVER IP

The certificate authority must be able to verify that the domain resolves correctly.


4. Check DNS

From the server:

dig +short example.com

or:

nslookup example.com

Expected:

YOUR_SERVER_IP

If DNS points somewhere else:

hosting-ssl
      ↓
STOP

5. Don’t Guess DNS

Your SSL script should never assume:

DNS is correct

It should verify the domain’s DNS resolution.

For a production system, DNS verification should be explicit.


6. Check HTTP First

Before SSL:

curl -I http://example.com

You want a valid HTTP response.

For example:

HTTP/1.1 200 OK

or potentially:

HTTP/1.1 301 Moved Permanently

depending on your existing configuration.


7. Why HTTP Must Work

With common HTTP-based certificate validation:

Certificate Authority
        ↓
http://example.com/.well-known/...
        ↓
Nginx
        ↓
server

If Nginx isn’t correctly serving the domain, certificate issuance can fail.


8. Install Certbot

On Ubuntu, check whether Certbot is already installed:

certbot --version

If not installed, install it using the package-management method appropriate for your Ubuntu deployment.

For a production CHP server, keep the certificate-management tool standardized rather than mixing several ACME clients.


9. Check Nginx Plugin

If using Certbot with Nginx:

certbot plugins

Look for:

nginx

The important idea is:

Certbot
   ↓
ACME
   ↓
Let's Encrypt

10. First Certificate Test

Before allowing automated production changes, use a staging/test environment when developing the automation.

This avoids hitting certificate authority rate limits while debugging.

The general flow is:

staging
 ↓
test successful
 ↓
production certificate

11. Why Staging Matters

Imagine your script has a bug and requests:

20 certificates

during development.

You could encounter certificate authority rate limits.

Therefore:

DEVELOPMENT
→ staging

PRODUCTION
→ production CA

12. Create hosting-ssl

Create:

sudo nano /usr/local/bin/hosting-ssl

Start:

#!/usr/bin/env bash

set -Eeuo pipefail

13. Require Root

if [[ "$EUID" -ne 0 ]]; then
    echo "ERROR: Run with sudo."
    exit 1
fi

14. Require Domain

if [[ $# -ne 1 ]]; then
    echo "Usage: hosting-ssl DOMAIN"
    exit 1
fi

DOMAIN="$1"

15. Locate Site

STATE_DIR="/var/lib/cresignsys/sites"
SITE_DIR="${STATE_DIR}/${DOMAIN}"
SITE_CONF="${SITE_DIR}/site.conf"

Then:

if [[ ! -f "$SITE_CONF" ]]; then
    echo "ERROR: CresignSys site not found."
    exit 1
fi

16. Load Metadata

source "$SITE_CONF"

Now the script knows:

DOMAIN
SITE_ID
SITE_USER
WEB_ROOT
PHP_VERSION
PHP_SOCKET
STATUS

17. Verify Required Commands

Check:

command -v certbot
command -v dig
command -v nginx
command -v curl

If anything important is missing:

ERROR: Required dependency missing.

Stop before modifying anything.


18. Check Nginx

if ! systemctl is-active --quiet nginx; then
    echo "ERROR: Nginx is not running."
    exit 1
fi

19. Check Existing Configuration

NGINX_CONF="/etc/nginx/sites-available/${DOMAIN}.conf"

Verify:

if [[ ! -f "$NGINX_CONF" ]]; then
    echo "ERROR: Nginx site configuration not found."
    exit 1
fi

20. Validate Nginx Before SSL

Always:

nginx -t

If it fails:

SSL provisioning
      ↓
STOP

Fix the existing website first:

hosting-repair example.com --nginx

21. Check DNS

A simple check:

DNS_IP="$(dig +short "$DOMAIN" A | tail -n 1)"

Then display:

Domain:
example.com

Resolved IP:
xxx.xxx.xxx.xxx

22. Compare With Server IP

You can determine the server’s public addresses using the server/network configuration.

But be careful with:

IPv4
IPv6
NAT
reverse proxies
CDNs
load balancers

A simplistic:

DNS IP == local interface IP

check is not universally correct.

Therefore DNS validation should be configurable.


23. HTTP Validation

Run:

curl -fsSI --max-time 10 "http://${DOMAIN}"

If this fails:

ERROR: HTTP validation failed.

Do not request SSL yet.


24. Why This Prevents Problems

The dependency chain is:

DNS
 ↓
HTTP
 ↓
Certificate
 ↓
HTTPS

You shouldn’t attempt step 3 when step 2 is broken.


25. Certificate Request

Once DNS and HTTP are confirmed, request the certificate.

Conceptually:

certbot certonly \
    --nginx \
    -d "$DOMAIN"

For automated server provisioning, use non-interactive settings and a defined administrative email.

Do not hard-code a personal email address into the platform.


26. Why certonly?

We want CHP to remain the owner of the Nginx configuration.

Therefore:

Certbot
   ↓
certificate management

CHP
   ↓
Nginx configuration

This gives you greater control.

An alternative architecture is to let Certbot modify Nginx automatically, but mixing two configuration managers can make your platform harder to reason about.


27. Certificate Location

Certificates are commonly maintained under:

/etc/letsencrypt/

For a domain, you’ll typically find a lineage such as:

/etc/letsencrypt/live/example.com/

with certificate-related files.

Don’t copy private keys into:

/storage/websites/example.com/public/

Never.


28. Private Key Security

The TLS private key is sensitive.

It belongs in:

/etc/letsencrypt/

or another protected system location managed by your certificate system.

It must never be:

publicly accessible
downloadable
stored in WordPress
stored under document root

29. Generate HTTPS Nginx Configuration

Your Nginx configuration now needs:

HTTP
HTTPS

A common architecture is:

HTTP :80
   ↓
redirect
   ↓
HTTPS :443

and:

HTTPS :443
   ↓
PHP-FPM

30. HTTP Server Block

Conceptually:

server {
    listen 80;
    server_name example.com;

    return 301 https://$host$request_uri;
}

But there is an important issue:

ACME HTTP validation may need access to the challenge path.

Therefore your certificate issuance and final redirect configuration need to be coordinated.


31. HTTPS Server Block

Conceptually:

server {
    listen 443 ssl http2;
    server_name example.com;

    root /storage/websites/example.com/public;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/csp1027.sock;
    }
}

Exact TLS directives should be maintained in a tested central template rather than copied independently for every site.


32. Don’t Duplicate Configuration

Your architecture should eventually use:

/etc/cresignsys/templates/
├── nginx-http.conf
└── nginx-https.conf

or one carefully designed template with SSL variables.


33. Certificate Variables

The template needs:

DOMAIN
WEB_ROOT
PHP_SOCKET
SSL_CERTIFICATE
SSL_CERTIFICATE_KEY

For example:

SSL_CERTIFICATE:
/etc/letsencrypt/live/example.com/fullchain.pem

SSL_CERTIFICATE_KEY:
/etc/letsencrypt/live/example.com/privkey.pem

34. Validate Before Activation

Generate the configuration.

Then:

nginx -t

Only if successful:

systemctl reload nginx

This rule remains unchanged.


35. Test HTTPS

After reload:

curl -I https://example.com

Expected:

HTTP/2 200

or another legitimate successful/redirect response depending on your configuration.


36. Verify Certificate

Use:

openssl s_client \
    -connect "${DOMAIN}:443" \
    -servername "$DOMAIN" \
    </dev/null

This lets you inspect the TLS connection.

For automated health checking, a simpler certificate-expiry inspection can also be used.


37. Certificate Expiry

A certificate has an expiration date.

Your platform should know:

Certificate:
ACTIVE

Expires:
2026-11-11

Days remaining:
90

The exact lifetime depends on the certificate authority’s current policies.


38. Why Renewal Is Essential

SSL automation isn’t complete when the certificate is issued.

It is complete only when:

certificate
   ↓
renewal
   ↓
renewal validation
   ↓
Nginx reload

works automatically.


39. Check Renewal Configuration

Certbot typically provides a renewal mechanism through the system’s scheduled service/timer.

Check:

systemctl list-timers | grep -i certbot

Depending on how Certbot was installed, the exact scheduling mechanism can differ.


40. Test Renewal

Do not wait until the certificate is nearly expired.

Use the certificate tool’s dry-run renewal mechanism.

For Certbot:

certbot renew --dry-run

This tests the renewal process without replacing the production certificate.


41. Renewal Architecture

The desired system:

Certificate
     │
     ▼
Renewal Scheduler
     │
     ▼
Renew Certificate
     │
     ▼
Validate Nginx
     │
     ▼
Reload Nginx
     │
     ▼
HTTPS Health Check

42. Important: Renewal Must Not Break Nginx

A renewal system should verify:

new certificate
       ↓
Nginx configuration
       ↓
nginx -t
       ↓
reload

before declaring success.


43. Update site.conf

After successful SSL:

SSL_STATUS=ACTIVE

Add:

SSL_CERTIFICATE=/etc/letsencrypt/live/example.com/fullchain.pem
SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/example.com/privkey.pem

Again, storing the path is fine; don’t store the private key itself.


44. Update Site State

Before:

STATUS=ACTIVE
SSL_STATUS=NOT_CONFIGURED

After:

STATUS=ACTIVE
SSL_STATUS=ACTIVE

45. Health Check Changes

Previously:

HTTP ✓

Now:

HTTP → redirect
HTTPS ✓
Certificate ✓

Your hosting-health command should understand the new expected state.


46. Final Health Model

For an SSL-enabled website:

DNS              ✓
HTTP             ✓
HTTPS            ✓
Certificate      ✓
Certificate age  ✓
Nginx            ✓
PHP-FPM          ✓
PHP socket       ✓
WordPress        ✓
Database         ✓
Filesystem       ✓

47. HTTP Redirect Test

After SSL:

curl -I http://example.com

Expected:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/...

Then:

curl -I https://example.com

should succeed.


48. Avoid Redirect Loops

A common mistake is creating:

HTTP → HTTPS
HTTPS → HTTP

or proxy configurations that cause:

HTTP
 ↓
HTTPS
 ↓
HTTP
 ↓
HTTPS

Your health check should detect this.


49. HTTPS Redirect Architecture

The desired flow is:

http://example.com
        │
        ▼
301
        │
        ▼
https://example.com
        │
        ▼
WordPress

Never:

HTTPS → HTTP

unless deliberately required for a specific architecture.


50. WordPress URL Update

After enabling HTTPS, WordPress must know that its canonical URL is HTTPS.

Check:

sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" option get siteurl

and:

sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" option get home

They should eventually reflect:

https://example.com

51. Update WordPress URLs Carefully

A controlled command can update:

wp option update siteurl "https://example.com"
wp option update home "https://example.com"

But this is only part of the HTTPS migration.

Existing database content may contain:

http://example.com

inside serialized data or content.


52. Don’t Use Blind SQL Replacement

Avoid:

UPDATE wp_posts
SET post_content = REPLACE(...)

as a generic WordPress migration technique.

WordPress data can contain serialized structures.

Use WP-CLI’s WordPress-aware search/replace functionality for appropriate migrations.


53. Mixed Content

After HTTPS:

Page = HTTPS
Image = HTTP
CSS = HTTP
JS = HTTP

can produce:

mixed content

The site may technically load but still have insecure resources.

Therefore SSL health should eventually include an application-level check.


54. SSL Is More Than a Certificate

A working HTTPS deployment means:

DNS
+
certificate
+
Nginx
+
HTTPS
+
WordPress URL
+
resources
+
renewal

not simply:

certificate exists

55. hosting-ssl Output

A good result:

CresignSys SSL
==============

Domain: example.com

[OK] DNS resolution
[OK] HTTP service
[OK] Certificate issued
[OK] Certificate files
[OK] Nginx configuration
[OK] Nginx reload
[OK] HTTPS response
[OK] Certificate verification
[OK] WordPress HTTPS URL

SSL Status: ACTIVE

56. Failure Example

Suppose DNS is wrong:

CresignSys SSL
==============

Domain: example.com

[FAIL] DNS resolution

SSL Status: NOT CONFIGURED

HTTP website has not been modified.

That final line is extremely important.


57. Another Failure

Suppose certificate issuance succeeds but Nginx validation fails:

[OK] Certificate issued
[OK] Certificate files
[FAIL] Nginx configuration

SSL Status: ERROR

Existing HTTP configuration preserved.

The site should remain available over its previous working configuration.


58. This Requires Atomic Configuration Updates

Don’t overwrite the working Nginx configuration directly.

Instead:

current.conf

then generate:

current.conf.tmp

Validate:

nginx -t

Then replace:

current.conf.tmp
       ↓
current.conf

and reload.


59. Why This Matters

You want:

WORKING CONFIG
      │
      ▼
NEW CONFIG
      │
      ▼
VALIDATE
      │
   ┌──┴──┐
   ▼     ▼
PASS   FAIL
 │       │
 ▼       ▼
activate preserve old

This is a general safe deployment pattern.


60. SSL State Machine

The SSL subsystem can have:

NOT_CONFIGURED
      ↓
REQUESTING
      ↓
ISSUED
      ↓
CONFIGURING
      ↓
ACTIVE

Failure:

REQUESTING
     ↓
ERROR

or:

CONFIGURING
     ↓
ERROR

61. Renewal States

Later:

ACTIVE
  ↓
RENEWAL_DUE
  ↓
RENEWING
  ↓
RENEWED
  ↓
ACTIVE

If renewal fails:

RENEWAL_ERROR

This allows the control panel to warn administrators before a certificate expires.


62. Certificate Monitoring

Your dashboard could eventually show:

DOMAIN                    SSL
------------------------------------------------
learn.cresignsys.com      ACTIVE — 72 days
shop.cresignsys.com       ACTIVE — 81 days
example.com               ERROR
oldsite.com               EXPIRING — 8 days

This is much more useful than simply:

SSL: ON

63. Add SSL to hosting-info

Now:

sudo hosting-info example.com

can show:

SSL Status       : ACTIVE
Certificate      : /etc/letsencrypt/live/example.com/fullchain.pem
Certificate Exp. : 2026-11-11

64. Add SSL to hosting-health

The health check becomes:

[OK] DNS
[OK] HTTP
[OK] HTTPS
[OK] Certificate
[OK] PHP-FPM
[OK] PHP Socket
[OK] Nginx
[OK] WordPress
[OK] Database

65. Current CHP Architecture

We now have:

                       CHP
                        │
       ┌────────────────┼────────────────┐
       │                │                │
       ▼                ▼                ▼
 Provisioning      Observability       Repair
       │                │                │
hosting-create     hosting-list      hosting-repair
                   hosting-info
                   hosting-health
                        │
                        ▼
                       SSL
                        │
                        ▼
                  hosting-ssl

66. Full Website Lifecycle

The lifecycle is becoming:

CREATE
  ↓
HTTP
  ↓
SSL
  ↓
HTTPS
  ↓
HEALTH
  ↓
REPAIR
  ↓
BACKUP
  ↓
SUSPEND
  ↓
RESTORE
  ↓
DELETE

67. Lesson 069 — Core Principle

SSL automation should be treated as a deployment process, not a single certificate command.

The safe workflow is:

DNS
 ↓
HTTP
 ↓
Certificate
 ↓
Temporary configuration
 ↓
Nginx validation
 ↓
Activation
 ↓
HTTPS test
 ↓
WordPress HTTPS
 ↓
Renewal test
 ↓
HEALTHY

And the most important safety rule is:

Never sacrifice a working website merely because SSL provisioning failed.


Next Lesson — 070

Build hosting-backup

The next major component is data protection.

We will design:

sudo hosting-backup example.com

to back up:

WordPress files
      +
MySQL database
      +
site configuration
      +
SSL metadata

and produce a structured backup such as:

/backup/cresignsys/example.com/
├── 2026-08-13/
│   ├── files.tar.zst
│   ├── database.sql.gz
│   ├── site.conf
│   └── manifest.json

Then we can safely make hosting-repair, hosting-restore, and eventually automatic disaster recovery work together.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *