HTTPS Build Playbook - JuniperRios/Sec-260-Notes GitHub Wiki

Build two clean Rocky VMs into a working private CA and an HTTPS web server, from memory, inside a class period. How to practice in VMware Workstation The whole point is repetition, and repetition needs a fast reset. Snapshots give you that. Set up once: Build both VMs to the state the exam hands you: Host 1 with httpd installed, enabled and serving plain HTTP; Host 2 a bare Rocky install with nothing extra. Confirm they can ping each other and that ssh works between them. Create a user account on each with sudo rights, since root SSH is blocked by default. Write down both IPs. Set static addresses if your lab network hands out DHCP leases that move. Take a snapshot on each VM. Name them clean-start. Then every practice run: VM menu, Snapshot, Revert to clean-start, on both VMs. Start a timer. Build the whole thing from memory. No notes on the first attempt of the day. When you get stuck or fail, note exactly where, finish the run with notes open, then revert and do it again. Two things that save time: Put both VMs on the same host-only or NAT network so addresses stay predictable across reverts. Add each host to the other's /etc/hosts before you snapshot. Two lines per machine, and you stop typing IPs:

echo "192.168.1.10  web1" | sudo tee -a /etc/hosts
echo "192.168.1.20  ca1"  | sudo tee -a /etc/hosts

If the exam hands you genuinely clean VMs with nothing configured, practice that too — do at least two runs where you start from snapshot and set up networking and SSH yourself before touching OpenSSL.

Stage 1 — Build the CA (Host 2)

Target time: 5 minutes. Create the directory structure. Rocky 9 does not ship /etc/pki/CA, so you make it.

mkdir -p /etc/pki/CA/{certs,crl,newcerts,private}
chmod 700 /etc/pki/CA/private
touch /etc/pki/CA/index.txt
echo 1000 > /etc/pki/CA/serial
restorecon -Rv /etc/pki/CA

index.txt must exist and be empty. serial must hold a number. Miss either and signing fails with an I/O error. Confirm the config agrees with you.

grep -A5 '\[ CA_default \]' /etc/pki/tls/openssl.cnf

You want dir = /etc/pki/CA. If it says ./demoCA, every later command needs -config, or you edit that line now. Generate the CA private key.

openssl genrsa -aes256 -out /etc/pki/CA/private/cakey.pem 2048
chmod 600 /etc/pki/CA/private/cakey.pem

It asks you to invent a passphrase, twice. Nothing echoes. Pick one you will type the same way every run — Joyce310CA! works. Self-sign the CA certificate.

openssl req -new -x509 -days 3650 \
  -key /etc/pki/CA/private/cakey.pem \
  -out /etc/pki/CA/cacert.pem

-x509 is what turns a request into a finished self-signed certificate. Without it you get a CSR instead. Answer the prompts, and write down what you typed.

Country Name:               US
State or Province Name:     Vermont
Locality Name:              Burlington
Organization Name:          Joyce310
Organizational Unit Name:   Joyce310
Common Name:                Joyce310
Email Address:              (blank)

Country, State and Organization have to be repeated identically on Host 1. That is the single most common failure in this build. Checkpoint.

openssl x509 -in /etc/pki/CA/cacert.pem -noout -subject -issuer -dates

Subject and issuer identical means it self-signed correctly. Dates spanning ten years means -days 3650 took.

Stage 2 — Key and request (Host 1)

Target time: 3 minutes. Generate the server private key.

openssl genrsa -aes256 -out /etc/pki/tls/private/websrv.key 2048
chmod 600 /etc/pki/tls/private/websrv.key

Second passphrase to invent and record. Joyce310Web! keeps it distinct from the CA key in your head. Generate the certificate signing request.

openssl req -new \
  -key /etc/pki/tls/private/websrv.key \
  -out /etc/pki/tls/certs/websrv.csr

No -x509 here. That is the whole difference between this and Stage 1 — you want a request, not a certificate. Answer the prompts identically to Stage 1. Same Country, same State, Joyce310 three times. Then two prompts that are easy to fumble:

A challenge password []:          press Enter
An optional company name []:      press Enter

Both stay blank. These are the optional CSR attributes, not key passwords, and filling them in causes complaints at signing time. Checkpoint.

openssl req -in /etc/pki/tls/certs/websrv.csr -noout -subject

Read the subject line out loud against what you typed on the CA. If C, ST or O differ by so much as a letter, fix it now — regenerate just the CSR, the key is fine. What you just made. The CSR holds the server's public key, the subject fields, and a signature proving you hold the matching private key. None of it is secret. The private key does not move, now or ever.

Stage 3 — Transfer, sign, return

Target time: 4 minutes. Send the request to the CA. Run on Host 1.

scp /etc/pki/tls/certs/websrv.csr [email protected]:/tmp/

Then on Host 2:

sudo mv /tmp/websrv.csr /etc/pki/CA/

Use the normal-user-to-/tmp route by default. Rocky 9 ships PermitRootLogin prohibit-password, so scp ... root@host fails with Permission denied (publickey,...) and never even prompts for the password. Knowing that cold saves you five minutes of panic in the exam. First connection asks you to accept a host key. Type the full word yes. Sign it. On Host 2:

openssl ca \
  -in /etc/pki/CA/websrv.csr \
  -out /etc/pki/CA/certs/websrv.crt \
  -days 365

It asks for the CA key passphrase, prints the certificate details, then asks two questions. Answer y to both:

Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]:y

Answering n to either leaves no output file at all. Checkpoint.

openssl verify -CAfile /etc/pki/CA/cacert.pem /etc/pki/CA/certs/websrv.crt
cat /etc/pki/CA/index.txt

You want : OK from the first, and a line starting with V from the second. Return the signed certificate. On Host 2:

scp /etc/pki/CA/certs/websrv.crt [email protected]:/tmp/

On Host 1:

sudo mv /tmp/websrv.crt /root/
sudo chown root:root /root/websrv.crt
sudo ls -l /root/websrv.crt

/root is mode 700, so check it with sudo. Without it you get permission denied, which reads like the file is missing when it is not. This is the save point. If the exam is graded in stages, stop and verify everything here before touching Apache.

Stage 4 — Apache to HTTPS (Host 1)

Target time: 6 minutes. Everything here is Host 1 only. The CA needs nothing installed. Put the files where Apache reads them.

cp /root/websrv.crt /etc/pki/tls/certs/
chmod 644 /etc/pki/tls/certs/websrv.crt
chmod 600 /etc/pki/tls/private/websrv.key
chown root:root /etc/pki/tls/private/websrv.key
restorecon -Rv /etc/pki/tls/certs/websrv.crt /etc/pki/tls/private/websrv.key

Certificate 644 because it is public and every browser gets a copy. Key 600 because it is the secret half. restorecon matters because a file that came through /tmp carries the wrong SELinux label, and Apache will be denied read access with an error that looks nothing like a permissions problem. Confirm the key and certificate are a pair.

openssl x509 -noout -modulus -in /etc/pki/tls/certs/websrv.crt | openssl md5
openssl rsa -noout -modulus -in /etc/pki/tls/private/websrv.key | openssl md5

Identical hashes or you have the wrong file somewhere. Install mod_ssl.

dnf install mod_ssl -y
httpd -M | grep ssl

You want ssl_module (shared). Installing it creates /etc/httpd/conf.d/ssl.conf. Edit ssl.conf.

vi /etc/httpd/conf.d/ssl.conf

Two lines to change. In vi, /SSLCertificate jumps to the first, n to the next:

SSLCertificateFile /etc/pki/tls/certs/websrv.crt
SSLCertificateKeyFile /etc/pki/tls/private/websrv.key

They ship pointing at localhost.crt and localhost.key. Edit the live lines, not the commented examples above them. Strip the key passphrase. Apache cannot prompt for it under systemd, so this is not optional unless you configure a passphrase dialog.

cd /etc/pki/tls/private
cp websrv.key websrv.key.encrypted
openssl rsa -in websrv.key.encrypted -out websrv.key
chmod 600 websrv.key
restorecon -v websrv.key

It prompts for the passphrase one last time. Keep the .encrypted copy in case you are asked to show an encrypted key. Open the firewall.

firewall-cmd --permanent --add-service=https
firewall-cmd --reload
firewall-cmd --list-all

--permanent writes to disk without touching the running firewall. --reload is what makes it live. Forget the reload and nothing works while the config looks correct. Start it.

apachectl configtest
systemctl restart httpd
systemctl enable httpd
ss -lntp | grep :443

Syntax OK before you restart. If the restart fails, go straight to journalctl -xeu httpd --no-pager | tail -40 rather than guessing. Verification checklist Run these in order. Each one proves a stage. If one fails, you know exactly where to look. CA exists and self-signed correctly — Host 2:

openssl x509 -in /etc/pki/CA/cacert.pem -noout -subject -issuer -dates

Request matches the CA's policy — Host 2, compare these two:

openssl req -in /etc/pki/CA/websrv.csr -noout -subject
openssl x509 -in /etc/pki/CA/cacert.pem -noout -subject

Certificate was issued and recorded — Host 2:

openssl verify -CAfile /etc/pki/CA/cacert.pem /etc/pki/CA/certs/websrv.crt
cat /etc/pki/CA/index.txt
ls -l /etc/pki/CA/newcerts/

Certificate arrived at the save point — Host 1:

sudo openssl x509 -in /root/websrv.crt -noout -subject -issuer -dates

Key and certificate are a pair — Host 1:

openssl x509 -noout -modulus -in /etc/pki/tls/certs/websrv.crt | openssl md5
openssl rsa -noout -modulus -in /etc/pki/tls/private/websrv.key | openssl md5

Apache is serving TLS — Host 1:

ss -lntp | grep :443
curl -k https://localhost

The chain actually validates — Host 1:

openssl s_client -connect localhost:443 -CAfile /etc/pki/CA/cacert.pem </dev/null 2>/dev/null | grep "Verify return code"

Verify return code: 0 (ok) is the thing you built all of this to produce. Copy cacert.pem over from Host 2 first, or run this from the CA pointed at the web server's IP. Optional, if they want a clean curl — Host 1:

sudo cp /tmp/cacert.pem /etc/pki/ca-trust/source/anchors/joyce310-ca.pem
sudo update-ca-trust
curl --resolve Joyce310:443:127.0.0.1 https://Joyce310

Plain curl https://localhost will still fail with error 60 even after this, because the certificate's CN is Joyce310 and the URL says localhost. That is a name mismatch, not a trust failure. --resolve makes curl ask for the name the certificate actually carries. Bare command list for drilling Once you know why each line is there, practice against this. No explanations, no checkpoints. Cover it and try to reproduce it from memory, then compare. Host 2 — CA

mkdir -p /etc/pki/CA/{certs,crl,newcerts,private}
chmod 700 /etc/pki/CA/private
touch /etc/pki/CA/index.txt
echo 1000 > /etc/pki/CA/serial
restorecon -Rv /etc/pki/CA
openssl genrsa -aes256 -out /etc/pki/CA/private/cakey.pem 2048
chmod 600 /etc/pki/CA/private/cakey.pem
openssl req -new -x509 -days 3650 -key /etc/pki/CA/private/cakey.pem -out /etc/pki/CA/cacert.pem

Host 1 — key and request

openssl genrsa -aes256 -out /etc/pki/tls/private/websrv.key 2048
chmod 600 /etc/pki/tls/private/websrv.key
openssl req -new -key /etc/pki/tls/private/websrv.key -out /etc/pki/tls/certs/websrv.csr
scp /etc/pki/tls/certs/websrv.csr [email protected]:/tmp/

Host 2 — sign and return

sudo mv /tmp/websrv.csr /etc/pki/CA/
openssl ca -in /etc/pki/CA/websrv.csr -out /etc/pki/CA/certs/websrv.crt -days 365
openssl verify -CAfile /etc/pki/CA/cacert.pem /etc/pki/CA/certs/websrv.crt
scp /etc/pki/CA/certs/websrv.crt [email protected]:/tmp/

Host 1 — install and serve

sudo mv /tmp/websrv.crt /root/
cp /root/websrv.crt /etc/pki/tls/certs/
chmod 644 /etc/pki/tls/certs/websrv.crt
chmod 600 /etc/pki/tls/private/websrv.key
restorecon -Rv /etc/pki/tls/certs/websrv.crt /etc/pki/tls/private/websrv.key
dnf install mod_ssl -y
vi /etc/httpd/conf.d/ssl.conf
cd /etc/pki/tls/private && cp websrv.key websrv.key.encrypted
openssl rsa -in websrv.key.encrypted -out websrv.key
chmod 600 websrv.key && restorecon -v websrv.key
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
apachectl configtest
systemctl restart httpd && systemctl enable httpd

The two lines you edit in ssl.conf

SSLCertificateFile /etc/pki/tls/certs/websrv.crt
SSLCertificateKeyFile /etc/pki/tls/private/websrv.key

The final three cat commands

# Host 2
cat /etc/pki/CA/cacert.pem

# Host 1
cat /etc/pki/tls/private/websrv.key
cat /root/websrv.crt

Things that will bite you These are the failures that cost real time. Know them cold and most of the exam becomes typing. Subject fields must match. Country, State and Organization on the CSR have to be identical to the CA certificate, or openssl ca refuses with a message about the field being different between CA certificate and request. The default policy_match policy enforces it. Write your answers on paper during Stage 1 and read from the paper during Stage 2. -x509 is the whole difference. openssl req -new -x509 makes a self-signed certificate. openssl req -new alone makes a request. Mixing these up gives you a file that looks right and fails later. Root SSH is blocked by default. PermitRootLogin prohibit-password is the Rocky 9 default, so scp file root@host: dies with Permission denied (publickey,gssapi-keyex,...) without ever prompting. Your password being correct is irrelevant. Go through a normal user and /tmp. /etc/pki/CA does not exist until you make it. Rocky 9 ships ca-trust but not CA. They are unrelated: CA is for running a CA, ca-trust is the list of CAs this machine believes. index.txt and serial are mandatory. Empty index.txt, a number in serial. Without them signing fails with an I/O error that does not explain itself. Both signing prompts need y. Sign the certificate, then commit. Saying no to the second leaves no file and no obvious error. --permanent alone does nothing live. Always follow with firewall-cmd --reload. An encrypted key stops httpd. systemd gives Apache nowhere to prompt. Strip the passphrase with openssl rsa before the first restart, or expect a confusing startup failure. SELinux labels follow the file. Anything that travelled through /tmp carries user_tmp_t and Apache cannot read it. restorecon after every move. Check with ls -Z. /root is mode 700. ls /root as a normal user says permission denied, which looks like the file is missing. Use sudo. Certificate 644, key 600. The certificate is public — every client gets a copy. The key is not. curl error 60 is two different problems. Untrusted CA is fixed by update-ca-trust. A name mismatch between the URL and the certificate's CN is not, and needs --resolve, an /etc/hosts entry, or a reissued certificate carrying a SAN. When httpd will not start, read the log first. journalctl -xeu httpd --no-pager | tail -40 before you change anything. Guessing costs more than reading. Practice schedule Full build target: about 20 minutes once it is in your hands. First attempt will take an hour. That gap is normal and closes fast. Days 1 and 2 — with the playbook open. Two full runs each day, reverting snapshots between them. Read each command before you type it and say what it does. Speed does not matter yet; understanding the -x509 difference and the subject-matching rule does. Days 3 and 4 — playbook closed, notes allowed. Run from the bare command list only. When you stall, note where, then open the full section and read it. Keep a running list of your own stalls — that list is more useful to you than anything in this doc, because it is specific to what you forget. Days 5 and 6 — from memory, timed. Nothing open. Start the clock at snapshot revert, stop it when curl -k https://localhost returns the page. Aim to get under 25 minutes, then under 20. Day before — one clean run, then stop. One timed run in the morning to confirm it is there. Reread your stall list. Do not cram new material. Deliberately break things, at least twice. Once you can build it, practice recovering: Type a different State on the CSR than on the CA, then read the policy_match error and fix it. Skip restorecon after moving the certificate, then read what Apache says and fix it. Leave the key encrypted and try to start httpd, then read the journal and fix it. Forget firewall-cmd --reload and try to connect from the other VM. Recovering from a broken state under time pressure is most of what separates a good exam run from a bad one. You will almost certainly mistype something on the day. The question is whether you recognise the error message in five seconds or twenty minutes. The pcap item I have not seen the pcap lab, so I have not written this half. Tell me what it covered and I will add a matching drill section here. Useful to know: Which tool the lab used — Wireshark GUI, tshark, tcpdump, something else. What you were asked to find. Extracting credentials from plaintext protocols, following a TCP stream, identifying a scan, spotting a DNS anomaly, reconstructing a file transfer, and reading a TLS handshake are all common and need different approaches. Whether you answer questions about the capture or produce something from it. Whether display filters were part of the grading. If the capture work involved TLS, it connects directly to the build half — the handshake you will be able to see in Wireshark is the same one your Apache server performs, and openssl s_client -connect localhost:443 gives you a clean one to capture and study on your own VMs. That is a good way to prepare for both items at once.