Skip to content

Making a server 2

Juniper Rios edited this page Oct 1, 2026 · 4 revisions

OpenSSL Certificate Authority Lab — Rocky 9.6 Runbook.md

OpenSSL Certificate Authority Lab — Rocky 9.6 Runbook Oct 1, 2026 · @Juniper Overview This builds a private Certificate Authority on one Rocky 9.6 VM and uses it to issue a TLS certificate to an Apache web server on a second VM. Host 1 — Web Server Host 2 — Certificate Authority Example address 192.168.1.10 192.168.1.20 Running now httpd on HTTP nothing Generates websrv.key, websrv.csr cakey.pem, cacert.pem Installs mod_ssl yes no Opens port 443 yes no Substitute your real IPs throughout. All commands run as root. Sticky-note constraints Organization Name, Organizational Unit Name and Common Name are Joyce310 on both hosts, every time you are prompted. Country and State must also match between the CA certificate and the web server request. The default policy_match policy rejects a mismatch, and this is the single most common failure in the lab. Record the password you set on each private key. There is no recovery. "No passphrases" refers to the two optional CSR attributes — A challenge password and An optional company name. Press Enter to leave both blank. It does not mean skipping the key passwords. Flow of the lab Build the CA directory structure on Host 2. Generate the CA key and self-sign cacert.pem. Generate the web server key and a certificate signing request on Host 1. scp the request to Host 2. Sign it, producing websrv.crt. scp the signed certificate back to /root on Host 1. Save point. Configure Apache for TLS and verify. Phase 1 — Build the CA directory (Host 2) Rocky 9 often does not ship /etc/pki/CA, so create it. mkdir -p makes the parent for you.

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

{certs,crl,...} is brace expansion — bash rewrites it into four separate paths. index.txt must exist and start empty. serial must hold a hex number; openssl ca issues that serial, then increments it. restorecon fixes SELinux labels, worth running because you created the directory by hand rather than getting it from an RPM. /etc/pki/CA is not /etc/pki/ca-trust Two unrelated things that live next door to each other. Path Purpose /etc/pki/CA/ Working directory for running a CA /etc/pki/ca-trust/ The system trust store — the list of CAs this machine believes, used by curl, git, Python, Firefox Seeing ca-trust but not CA is normal, not a broken install. ca-trust becomes relevant only at the verification stage. Confirm the config points where you think

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

You want dir = /etc/pki/CA. If it reads dir = ./demoCA, either edit that line or pass -config to every openssl ca and openssl req command. What lives where Path Purpose private/cakey.pem CA private key — the crown jewels cacert.pem CA self-signed public certificate index.txt Flat-file database of every certificate issued serial Next serial number to hand out newcerts/ Archive copy of each signed cert, named by serial certs/ Where the issued certificate is written Phases 2–3 — CA key and self-signed certificate (Host 2) Generate the CA private key

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

genrsa generates an RSA keypair — the private key file mathematically contains the public key too. -aes256 encrypts the file, which is what triggers Enter PEM pass phrase. 2048 is the modulus size in bits. The passphrase prompt is asking you to invent a password, not recall one. Type anything of 4 characters or more, twice. Nothing echoes while you type; that is normal, not a frozen terminal. A worked example is Joyce310CA! — but whatever you type is what it becomes, so write it down. Losing this password means rebuilding the CA from scratch. 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 the twist: instead of emitting a signing request, req self-signs and emits a finished certificate. That is what makes a root CA. -days 3650 is a ten-year lifetime, normal for a root. -key reuses the existing key rather than generating a new one. Answer the prompts exactly

Country Name (2 letter code) [XX]:US
State or Province Name []:<your state>
Locality Name []:<your city>
Organization Name []:Joyce310
Organizational Unit Name []:Joyce310
Common Name []:Joyce310
Email Address []:<blank is fine>

Write down the Country and State you used. Phase 4 must match them. Verify

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

Subject and issuer should be identical — that is the signature of a self-signed root. Dates should span ten years. Phase 4 — Web server key and CSR (Host 1)

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

The second command has no -x509, so the output is a signing request rather than a certificate. A worked example password for this key is Joyce310Web!; again, record whatever you actually type. Prompt answers Identical to Phase 3 — Country, State and Joyce310 for Organization, Organizational Unit and Common Name. Then:

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

Those two are the "no passphrases" hint. They are vestigial CSR attributes that serve no purpose here, and filling them in can cause complaints at signing time. Check before shipping

openssl req -in /etc/pki/tls/certs/websrv.csr -noout -text | head -20

-noout suppresses the base64 blob, -text prints the human-readable decode. Confirm the Subject line reads C=US, ST=..., O=Joyce310, OU=Joyce310, CN=Joyce310. What a CSR contains Three things: the web server's public key, the subject fields you typed, and a signature made with the private key proving you hold it. None of that is secret. The private key itself never leaves Host 1 — not in this lab, not in production. Testing a password you are unsure of

openssl rsa -in /etc/pki/tls/private/websrv.key -noout -check

RSA key ok means you guessed right. A bad-decrypt error means wrong password. Losing the web server key password costs twenty minutes — regenerate the key and CSR and get a new certificate signed. Losing the CA key password ends the CA. Phase 5 — Transfer the CSR to the CA Pre-flight on Host 2

systemctl status sshd
ss -lntp | grep ':22 '
ip -brief addr show

Healthy ss output looks like this — two lines, because sshd binds IPv4 and IPv6 separately:

LISTEN 0  128   0.0.0.0:22   0.0.0.0:*   users:(("sshd",pid=912,fd=3))
LISTEN 0  128      [::]:22      [::]:*   users:(("sshd",pid=912,fd=4))

-l listening only, -n numeric ports, -t TCP only, -p owning process. -p needs root; as a normal user the users:(...) field comes back empty, which looks alarming and is not. The trailing space in grep ':22 ' stops 2222 and 8022 from matching. The command (run on Host 1)

scp /etc/pki/tls/certs/websrv.csr root@192.168.1.20:/etc/pki/CA/

Piece Meaning scp Secure copy — a file transfer riding inside an SSH session /etc/pki/.../websrv.csr Source; a local path, so no colon root@ Account to log in as on the far side 192.168.1.20 The CA : Separator meaning "everything after this is a remote path". Omit it and scp makes a local file literally named root@192.168.1.20 /etc/pki/CA/ Destination directory. Trailing slash means "put it in here" rather than "rename it to this" Source then destination, same as cp. Root login will probably be refused RHEL 9 and Rocky 9 ship PermitRootLogin prohibit-password, so root may log in with a key but never with a password. If you see Permission denied (publickey,gssapi-keyex,...) with no password prompt at all, that is this, and your password being correct is irrelevant — it was never asked for. The clean workaround, which needs no config change:

# on Host 1
scp /etc/pki/tls/certs/websrv.csr labuser@192.168.1.20:/tmp/

# on Host 2
sudo mv /tmp/websrv.csr /etc/pki/CA/
sudo chown root:root /etc/pki/CA/websrv.csr

Or enable it on Host 2 by setting PermitRootLogin yes in /etc/ssh/sshd_config and running systemctl restart sshd. Check /etc/ssh/sshd_config.d/*.conf first — a drop-in file there loads earlier and its setting wins, so editing the main file alone can appear to do nothing. Confirm the subjects match before signing

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

Country, State and Organization must agree. Catching a mismatch here saves a cryptic policy_match error mid-signing. If they differ, regenerate just the CSR on Host 1 with corrected answers and re-copy — the key does not need redoing. Phase 6 — Sign the certificate (Host 2)

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

ca is the signing engine. It reads openssl.cnf, finds the CA key and certificate, checks the request against policy_match, consumes a serial number, writes a row into index.txt, and drops an archive copy in newcerts/. It prompts for the CA key password, displays the certificate details, then asks two questions:

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

Answer y to both. Answering n to either, or pressing Ctrl-C, leaves no output file — a common reason websrv.crt later turns out not to exist. Verify the chain

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

You want : OK. Confirm the signing actually recorded

cat /etc/pki/CA/index.txt
ls -l /etc/pki/CA/newcerts/

A line beginning V in index.txt means a valid certificate was issued. openssl ca always writes newcerts/<serial>.pem. If 1000.pem is there but certs/websrv.crt is not, the signing worked and only the -out path went somewhere unexpected:

cp /etc/pki/CA/newcerts/1000.pem /etc/pki/CA/certs/websrv.crt

If both are empty, re-run the signing command. Phases 7–8 — Return the certificate and the save point Copy it back (run on Host 2)

scp /etc/pki/CA/certs/websrv.crt root@192.168.1.10:/root/

Same root-login wall as Phase 5, now in the opposite direction — Host 1 refuses root password auth for the identical reason. Use the same workaround:

# on Host 2
scp /etc/pki/CA/certs/websrv.crt labuser@192.168.1.10:/tmp/

# on Host 1
sudo mv /tmp/websrv.crt /root/
sudo chown root:root /root/websrv.crt

Watch for the 100% completion line. If scp printed nothing or errored, the transfer did not happen regardless of how the command looked. If the certificate is not in /root

sudo ls -la /root/

/root is mode 700, so as a normal user ls /root returns permission denied — which reads like "not there" but is not. Use sudo, or sudo -i first. Still missing? Widen the net:

sudo find / -name "websrv.crt" 2>/dev/null

2>/dev/null discards the permission-denied noise from /proc and friends. Also check /root the file versus / the filesystem root — a destination written as :/root with no trailing slash, against a non-existent /root, would have created a file named root at /. Confirm it is the real thing

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

Subject CN=Joyce310, issuer your CA's Joyce310, dates spanning the next year. Final output — the three cat commands On Host 2:

cat /etc/pki/CA/cacert.pem

On Host 1:

cat /etc/pki/tls/private/websrv.key
cat /root/websrv.crt

The key shows Proc-Type: 4,ENCRYPTED in its header — that is the AES-256 password doing its job, and it is expected. If you later strip the password so Apache can start unattended, keep a copy of the encrypted original in case the encrypted header is what your instructor wants to see. Phases 9–13 — Apache TLS (Host 1 only) Everything below runs on the web server. Host 2 needs nothing installed — a CA is an offline signing station, and its work ended when the certificate was copied back. Place the files

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
ls -Z /etc/pki/tls/certs/websrv.crt /etc/pki/tls/private/websrv.key

The key was generated in place in Phase 4, so it is likely already correct — verify rather than assume. The permissions differ deliberately. The certificate is public; every browser that connects receives a copy, so 644 is right. The key is the secret half, so 600. Apache starts as root, reads the key, then drops to the apache user, so worker processes never need access. Labels should read cert_t. A file that came via /tmp may carry user_tmp_t, 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

Two identical hashes means they match. Different hashes means the certificate was signed from a different key than the one sitting here. Install mod_ssl

dnf install mod_ssl -y
httpd -M | grep ssl

This drops in /etc/httpd/conf.d/ssl.conf and mod_ssl.so. You want ssl_module (shared) in the output. Edit ssl.conf

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

Find these two directives, which ship pointing at Rocky's placeholder, and change them:

# from:
SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

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

In vi, /SSLCertificateFile jumps to the first match and n finds the next. Both live inside the <VirtualHost _default_:443> block. Make sure you are editing the live lines, not the commented examples above them — a leading # means Apache ignores the line entirely. While you are in there, set ServerName to match the hostname you will browse to.

apachectl configtest

Syntax OK is what you want. Firewall

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

--permanent writes to the on-disk config but does not touch the running firewall — that is why --reload follows, re-reading permanent rules into the live ruleset. Omit --permanent and the rule works immediately but vanishes on reboot. https is a named alias for tcp/443. The encrypted key will block startup Because you used -aes256, Apache needs the password to read the key, and systemctl gives it nowhere to prompt. Expect a failure, with journalctl -xeu httpd complaining about loading the server certificate or a bad decrypt. The standard fix is to strip the password:

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

openssl rsa reads the encrypted key, prompts for the password one last time, and writes it back out unencrypted. Keep the .encrypted copy for your cat output if the assignment expects the Proc-Type: 4,ENCRYPTED header. The tradeoff: anyone who can read that file now owns the key with no further obstacle, which is why mode 600 matters more once the password is gone. Essentially every production web server runs this way — the password only protects against offline theft of the file, and it makes unattended reboots impossible. The alternative is SSLPassPhraseDialog exec:/path/to/script, where the script echoes the password. That preserves the encrypted key but is largely security theater, since the password then sits in a file too. Start it

systemctl restart httpd
systemctl enable httpd
systemctl status httpd
ss -lntp | grep :443

Verification and trust curl error 60 is expected, not a failure It means TLS negotiated fine — curl received your certificate, read it, and could not find Joyce310 among the CAs it trusts. Nobody vouches for a CA you invented an hour ago. TLS verification runs two independent checks, and both must pass: Chain of trust — is this signed by a CA I trust? Fixed by the trust store. Name match — does the hostname in the URL appear in the certificate? Nothing in the trust store fixes this. Quick confirmation it is only trust

curl -k https://localhost

-k (also spelled --insecure) skips verification. If the page renders, the crypto works and trust is the only gap. The wget equivalent is wget --no-check-certificate. Install your CA into the trust store

# on Host 2
scp /etc/pki/CA/cacert.pem labuser@192.168.1.10:/tmp/

# on Host 1
sudo cp /tmp/cacert.pem /etc/pki/ca-trust/source/anchors/joyce310-ca.pem
sudo update-ca-trust
curl https://localhost

update-ca-trust reads everything in source/anchors/ and regenerates the bundles under extracted/ that curl, Python, git and others consult. Only ever drop files into anchors/ — extracted/ gets overwritten. If error 60 persists, read the new message

curl -v https://localhost 2>&1 | grep -iE "subject|issuer|SSL certificate"

A message about the certificate's name not matching is the second problem. Your CN is Joyce310 but you connected to localhost, and curl requires the hostname you typed to appear in the CN or SAN. Asking for the certificate by name Joyce310 is not a real hostname — no DNS record, no /etc/hosts entry — so curl https://Joyce310 alone returns Could not resolve host. --resolve lets you use the name in the URL while telling curl where to actually connect:

curl --resolve Joyce310:443:127.0.0.1 https://Joyce310

The format is strictly HOST:PORT:ADDRESS, colon-separated, no spaces. Curl builds a fake DNS entry that lives only for this one command — nothing is written to disk. What happens: curl parses the URL, sees hostname Joyce310, checks its --resolve list instead of DNS, opens a connection to 127.0.0.1:443, sends SNI: Joyce310 in the handshake, receives websrv.crt, validates the chain, then compares Joyce310 against CN=Joyce310 and passes. The port in --resolve must match the port curl is actually using. Joyce310:80:127.0.0.1 with an https:// URL silently does nothing. To see verification happen:

curl -v --resolve Joyce310:443:127.0.0.1 https://Joyce310

The payoff line is SSL certificate verify ok. From Host 2, point at the real interface instead: --resolve Joyce310:443:192.168.1.10. For a one-off without touching the trust store, --cacert replaces the system bundle for that command:

curl --cacert /etc/pki/CA/cacert.pem --resolve Joyce310:443:127.0.0.1 https://Joyce310

Other ways to fix the name problem Approach Command Scope --resolve curl --resolve Joyce310:443:127.0.0.1 https://Joyce310 One command, no root, port-specific, no browser --connect-to curl --connect-to Joyce310:443:localhost:443 https://Joyce310 Same, but takes a hostname on the right side /etc/hosts echo "127.0.0.1 Joyce310" | sudo tee -a /etc/hosts System-wide, persists, needs root, works in browsers System hostname hostnamectl set-hostname Joyce310 Also lines up Apache's ServerName Real DNS An A record for Joyce310 Overkill for two VMs Verify without curl

openssl s_client -connect localhost:443 -servername Joyce310 \
  -CAfile /etc/pki/CA/cacert.pem </dev/null

-servername sets SNI manually, -CAfile supplies the trust anchor. Verify return code: 0 (ok) is the most direct proof your CA chain works, with no name-matching interference. The real fix — reissue with a SAN The certificate has no Subject Alternative Name. Curl still falls back to checking CN when no SAN is present, but Firefox and Chrome dropped that fallback years ago and will reject the certificate regardless of trust store or hostname. For the assignment as written, -k or --resolve demonstrates the server works. For a green padlock in a browser, reissue. On Host 2:

cat > /etc/pki/CA/websrv_ext.cnf <<'EOF'
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = Joyce310
DNS.2 = localhost
DNS.3 = web.example.com
IP.1 = 127.0.0.1
IP.2 = 192.168.1.10
EOF

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

Directive Effect basicConstraints = CA:FALSE This certificate cannot sign others — it is an end-entity cert keyUsage Limits the key to signing handshakes and encrypting session keys extendedKeyUsage = serverAuth Marks it valid for TLS server identity specifically subjectAltName The list of names this certificate legitimately answers to List every name and IP you might connect by; a client matches against any entry. You will hit TXT_DB error number 2 because a certificate with that subject already exists — either echo "unique_subject = no" > /etc/pki/CA/index.txt.attr or revoke the old one with openssl ca -revoke /etc/pki/CA/newcerts/1000.pem. Then scp the new certificate over, restorecon, and restart httpd. Same key, so no new CSR. Files created Host Path Contents CA /etc/pki/CA/private/cakey.pem Encrypted RSA-2048 CA private key. Never leaves Host 2 CA /etc/pki/CA/cacert.pem Self-signed root certificate, 10-year validity CA /etc/pki/CA/index.txt One line per issued certificate — status, serial, subject CA /etc/pki/CA/serial Next serial number, incremented after each signing CA /etc/pki/CA/newcerts/1000.pem Archive copy of the web server certificate CA /etc/pki/CA/certs/websrv.crt The issued certificate CA /etc/pki/CA/websrv.csr The request, as received by scp Web /etc/pki/tls/private/websrv.key RSA-2048 server key, mode 600 Web /etc/pki/tls/certs/websrv.csr The request; disposable once signed Web /root/websrv.crt The signed certificate at the save point Web /etc/pki/tls/certs/websrv.crt Working copy Apache reads, mode 644 Web /etc/httpd/conf.d/ssl.conf Edited by you; .orig is your backup Web /etc/pki/ca-trust/source/anchors/joyce310-ca.pem Optional — makes this machine trust your CA Three paths that sound alike Path What it is /etc/pki/CA/cacert.pem Your CA's public certificate. Safe to distribute; this is what clients need in order to trust certificates you sign /etc/pki/CA/private/cakey.pem Your CA's private key. Never leaves Host 2 /etc/pki/tls/certs/ca-bundle.crt Rocky's bundle of public trusted roots, regenerated by update-ca-trust. Unrelated to your CA Syntax decoder OpenSSL subcommands Subcommand What it does genrsa Generates an RSA keypair; the private key file contains the public key too req Certificate request utility — creates CSRs, or self-signed certs with -x509 ca The signing engine; reads openssl.cnf, checks policy, consumes a serial, records the issue x509 Inspects or manipulates an existing certificate rsa Inspects or converts a key; also strips or adds passphrase encryption verify Checks a certificate against a trust anchor s_client Opens a TLS connection as a client, for testing Flags used Flag Meaning -aes256 Encrypt the key file with AES-256; triggers the passphrase prompt -out Destination file -in Input file -key Use an existing key rather than generating one -new Build a new request interactively -x509 Self-sign and emit a certificate instead of a request -days N Validity period -noout Suppress the base64 blob -text Print the human-readable decode -subject, -issuer, -dates Print just those fields -modulus Print the key modulus — identical for a matching key and certificate -check Validate a key, which requires decrypting it -CAfile Trust anchor to verify against -extfile Extensions file, used to add a SAN at signing time -servername Set the SNI field manually -config Point at a specific openssl.cnf Shell and system Command Meaning mkdir -p a/{b,c} Brace expansion into separate paths; -p makes parents and ignores existing chmod 600 / 644 / 700 Owner-only read-write / world-readable / owner-only directory restorecon -Rv Reset SELinux labels recursively, verbosely ls -Z Show SELinux context scp src user@host:/dst/ Secure copy over SSH; the colon marks the remote path ss -lntp Listening, numeric, TCP, with process — -p needs root firewall-cmd --permanent Write to disk only; needs --reload to take effect httpd -M List loaded Apache modules apachectl configtest Check config syntax without restarting journalctl -xeu httpd Service logs, explained, end-of-file, this unit 2>/dev/null Discard stderr — useful with find across / curl Flag Meaning -k / --insecure Skip certificate verification entirely --cacert FILE Use this trust anchor instead of the system bundle --resolve HOST:PORT:IP Fake DNS entry for this command only --connect-to HOST:PORT:HOST2:PORT2 Same idea, but the target is a hostname -v Verbose — shows the handshake and verification result Troubleshooting index Message or symptom Cause Fix countryName field is different between CA certificate and the request Phase 3 and Phase 4 subject answers diverged Regenerate the CSR with matching Country, State, Organization. The key is fine unable to load CA private key / bad decrypt Wrong password, or openssl ca is looking elsewhere Check dir under [ CA_default ] in /etc/pki/tls/openssl.cnf TXT_DB error number 2 A certificate with that exact subject already exists in index.txt echo "unique_subject = no" > /etc/pki/CA/index.txt.attr, or revoke the old one I/O error on serial or index.txt File missing or empty Redo Phase 1 Permission denied (publickey,gssapi-keyex,...) with no password prompt PermitRootLogin prohibit-password is the Rocky 9 default Copy via a normal user to /tmp, or set PermitRootLogin yes and restart sshd Permission denied, please try again after a prompt Genuine auth failure, or root has no password passwd -S root — P means set, NP/LK means not usable scp: /etc/pki/CA/websrv.csr: Permission denied after login succeeds Destination directory missing or wrong ownership ls -ld /etc/pki/CA, then chown/chmod/restorecon Connection refused sshd not running on the far side systemctl enable --now sshd No route to host / timeout Firewall or wrong subnet firewall-cmd --list-all; look for ssh Could not resolve hostname DNS Use the raw IP, or add entries to /etc/hosts on both machines Host key warning about a changed key VM was rebuilt ssh-keygen -R 192.168.1.20 bash: scp: command not found on the remote Minimal install dnf install openssh-clients; scp needs the binary on both ends websrv.crt not in /root Signing aborted, transfer failed, or you looked without sudo Check index.txt and newcerts/, then sudo ls -la /root/ SSLCertificateFile: file does not exist or is empty Typo in path, or the certificate never reached /etc/pki/tls/certs/ Verify the path in ssl.conf against ls -l Permission denied on the key, from Apache SELinux label, often user_tmp_t from a /tmp hop restorecon -Rv /etc/pki/tls/, then ausearch -m avc -ts recent unable to check private key Certificate and key are not a pair Compare moduli with the openssl md5 pipeline httpd fails to start, log mentions bad decrypt Encrypted key with nowhere to prompt Strip the password with openssl rsa, or configure SSLPassPhraseDialog Address already in use Something else holds 443 ss -lntp | grep :443 curl: (60) with "unable to get local issuer certificate" CA not in the trust store Install cacert.pem into source/anchors/ and run update-ca-trust curl: (60) with a name mismatch CN is Joyce310, URL says something else --resolve, /etc/hosts, or reissue with a SAN curl: (7) Failed to connect ... port 443 Nothing listening ss -lntp | grep :443 and systemctl status httpd Browser warns even after trust store install No SAN in the certificate Reissue with -extfile and a subjectAltName block First move for anything Apache-related

journalctl -xeu httpd --no-pager | tail -40

Clone this wiki locally