Week 07 - TLS¶
Topic¶
Securing web and email services with TLS certificates. Obtaining certificates from a private Certificate Authority (HashiCorp Vault), enabling HTTPS on all Apache virtual hosts, configuring TLS for Postfix (SMTPS), and enabling IMAPS in Dovecot.
Company Requests¶
Ticket #701: Encrypt Web Traffic
"The security team has discovered that all four of our websites are served over unencrypted HTTP. This is unacceptable - customer data, login credentials, and email content are all visible to anyone intercepting the traffic. Implement HTTPS for all web services:
<vm_name>.sysadm.ee,blog.<vm_name>.sysadm.ee,inventory.<vm_name>.sysadm.ee, andmail.<vm_name>.sysadm.ee."
Ticket #702: Encrypt Email Traffic
"The same applies to our email infrastructure. Postfix and Dovecot must support TLS so that authentication credentials and email content are encrypted in transit. Internal-only connections may remain unencrypted for simplicity - this means traffic between Dovecot and Postfix (on the same VM) and between Apache and the inventory backend API do not need TLS."
Unencrypted traffic
In production, HTTP traffic on port 80 should be redirected to HTTPS (port 443). In this course, we keep port 80 open and functional so that scoring checks from previous weeks (Labs 04–06) continue to pass. This is not best practice - in a real environment, you would configure automatic HTTP-to-HTTPS redirects.
Similarly, with other non-encrypted endpoints (like email), the best practice is to disable them entirely, and only use encrypted versions. In this course, we skip this step to allow scoring checks from previous labs to pass.
Certificate Authority
For this course, we use a private Certificate Authority powered by HashiCorp Vault at https://vault.sysadm.ee. Since this CA is not publicly trusted, browsers will show security warnings until you import the CA certificate. You can verify certificates using openssl s_client with the -CAfile flag, or import the CA certificate into your browser (see the verification task).
Scoring Checks¶
- Check 7.1: Port 443 is open and accepts TLS connections.
- Method: TCP connection with TLS handshake to your VM on port 443.
- Expected: TLS handshake succeeds.
- Check 7.2:
<vm_name>.sysadm.eeanswers HTTPS with a valid CA-signed certificate.- Method: The scoring server sends an HTTPS request to your VM with
--resolveto<vm_name>.sysadm.ee. Certificate is validated against the course CA. - Expected: HTTPS response contains your VM's short name, and the certificate is trusted.
- Method: The scoring server sends an HTTPS request to your VM with
- Check 7.3:
blog.<vm_name>.sysadm.eeanswers HTTPS with WordPress content.- Method: HTTPS request with
--resolvetoblog.<vm_name>.sysadm.ee, validated against the course CA. - Expected: HTTPS response contains WordPress indicators (
wp-content/,wp-includes/), and the certificate is trusted.
- Method: HTTPS request with
- Check 7.4:
mail.<vm_name>.sysadm.eeanswers HTTPS with Roundcube.- Method: HTTPS request with
--resolvetomail.<vm_name>.sysadm.ee, validated against the course CA. - Expected: HTTPS response contains Roundcube indicators, and the certificate is trusted.
- Method: HTTPS request with
- Check 7.5: Port 465 (SMTPS) is open and accepts TLS connections.
- Method: TCP connection with TLS handshake to port 465.
- Expected: TLS handshake succeeds.
- Check 7.6: Port 993 (IMAPS) is open and accepts TLS connections.
- Method: TCP connection with TLS handshake to port 993.
- Expected: TLS handshake succeeds.
- Check 7.7: IMAPS certificate is properly CA-signed.
- Method: The scoring server connects to port 993 with
openssl s_clientand checks theVerify return code. - Expected: Verify return code is
0 (ok).
- Method: The scoring server connects to port 993 with
Required Documentation¶
Before starting this lab, review the following documentation:
- Concepts: TLS and Certificates
- SOP: Certificate Management
- Technologies: Apache HTTPD
- Technologies: Postfix
- Technologies: Dovecot
Tasks¶
Task 1: Obtain TLS Certificates from Vault¶
Get a certificate and private key from the course Certificate Authority (HashiCorp Vault) for your services.
Complete
- Open the Vault web UI at
https://vault.sysadm.eein your browser. - Choose LDAP as the authentication method and log in with your University of Tartu credentials.
- Navigate to the
pkisecrets engine and thesysadm.eerole. - Generate a certificate. You have two options:
- Per-service certificates: Generate separate certificates for each service (
<vm_name>.sysadm.ee,blog.<vm_name>.sysadm.ee,inventory.<vm_name>.sysadm.ee,mail.<vm_name>.sysadm.ee). - Wildcard certificate : Use
*.<vm_name>.sysadm.eeas the Common Name, and add<vm_name>.sysadm.eein the Alt Names field. A wildcard only covers subdomains - it does not match the bare domain itself.
- Per-service certificates: Generate separate certificates for each service (
- Make sure the format is set to PEM.
- Save the three pieces Vault returns - certificate, private key, and CA chain. Once you close the window, Vault will not show them again. If you lose them, generate new ones.
- Copy the files to your VM at the standard locations:
- Certificate:
/etc/pki/tls/certs/server.crt - Private key:
/etc/pki/tls/private/server.key - CA chain:
/etc/pki/tls/certs/ca-chain.crt
- Certificate:
- Set proper permissions on the private key:
chmod 600 /etc/pki/tls/private/server.key
Wildcard certificates and bare domains
A wildcard certificate for *.example.sysadm.ee covers blog.example.sysadm.ee, mail.example.sysadm.ee, etc. - but it does not cover example.sysadm.ee itself. You must add the bare domain in the Alt Names field (or alt_names in the CLI) for it to be included in the certificate, or use a separate certificate for the plain domain.
CA chain format
The CA chain Vault returns may contain multiple certificates. Make sure they are in correct PEM format - each certificate block must be separated by a newline, not a comma. The format must be:
-----BEGIN CERTIFICATE-----
<base64 data>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<base64 data>
-----END CERTIFICATE-----
Certificate lifetime
Certificates issued by Vault in this course have a default lifetime of 7 days (maximum 1 year). If your certificate expires, simply generate a new one from Vault and replace the files on your VM. Remember to restart the affected services after replacing certificates.
Another option is to specify a longer certificate lifetime when generating the certificate in Vault.
Reference: SOP: Certificate Management - Sign a Certificate via Vault, Concepts: TLS and Certificates - Certificate File Formats
Task 2: Enable HTTPS for Web Services¶
Install mod_ssl and configure HTTPS virtual hosts for all four websites.
Complete
- Install the
mod_sslpackage. - For each of your four virtual hosts (
<vm_name>.sysadm.ee,blog.<vm_name>.sysadm.ee,inventory.<vm_name>.sysadm.ee,mail.<vm_name>.sysadm.ee), add a port 443 virtual host block alongside the existing port 80 one. Each HTTPS virtual host needs the SSL directives pointing to your certificate, key, and CA chain files. - Open port 443 in
firewalldand your cloud security group. - Test the Apache configuration with
apachectl configtestand restart the service. - Verify each site works over HTTPS using
openssl s_clientwith the-CAfileflag. Check thatVerify return codeis0 (ok)for each.
Reference: SOP: Certificate Management - Install a Certificate in Apache, Concepts: TLS and Certificates - TLS in Apache
Task 3: Enable TLS for Postfix¶
Configure Postfix to support TLS on the SMTPS (465) port.
Complete
- Add TLS settings to
/etc/postfix/main.cf- set the certificate file, key file, CA file, security level, and TLS logging. - In
/etc/postfix/master.cf, enable the smtps block (port 465). Uncomment it and configure it for implicit TLS (smtpd_tls_wrappermode=yes) with SASL authentication. - Install
cyrus-sasl-plainif not already present. - Open port 465 in
firewalldand your cloud security group. - Restart Postfix.
- Verify with
openssl s_client -connect mail.<vm_name>.sysadm.ee:465 -CAfile /etc/pki/tls/certs/ca-chain.crtand check forVerify return code: 0 (ok).
Reference: SOP: Certificate Management - Install a Certificate in Postfix/Dovecot, Concepts: TLS and Certificates - TLS in Postfix
Task 4: Enable TLS for Dovecot¶
Configure Dovecot to serve IMAP over TLS (IMAPS) on port 993.
Complete
- Edit
/etc/dovecot/conf.d/10-ssl.confand configure SSL with the certificate and key paths. Note: Dovecot uses a<prefix before file paths - this is Dovecot-specific syntax that tells it to read the contents of the file. - Open port 993 in
firewalldand your cloud security group. - Restart Dovecot.
- Verify with
openssl s_client -connect mail.<vm_name>.sysadm.ee:993 -CAfile /etc/pki/tls/certs/ca-chain.crtand check forVerify return code: 0 (ok).
Reference: SOP: Certificate Management - Install a Certificate in Postfix/Dovecot, Concepts: TLS and Certificates - TLS in Dovecot
Ansible Tips¶
This section covers tips for automating the tasks in this lab with Ansible. You are not required to automate everything this week, but starting early will save you time later.
Requesting Certificates from Vault with Ansible¶
Install the community.hashi_vault Ansible collection:
ansible-galaxy collection install community.hashi_vault
Use the vault_pki_generate_certificate module to issue certificates automatically:
- name: Request a certificate from HashiCorp Vault PKI
community.hashi_vault.vault_pki_generate_certificate:
url: "https://vault.sysadm.ee"
auth_method: "ldap"
username: "{{ vault_ldap_username }}"
password: "{{ vault_ldap_password }}"
engine_mount_point: "pki"
role_name: "sysadm.ee"
common_name: "*.{{ hostname }}.sysadm.ee"
alt_names: "{{ hostname }}.sysadm.ee"
register: vault_pki_response
delegate_to: localhost
become: false
Key points about this task:
delegate_to: localhost- the Vault API call runs from your local machine, not the remote VM.become: false- no root privileges needed for the API call.register: vault_pki_response- saves the response (certificate, key, chain) in a variable.
Protecting Credentials¶
Never store university credentials in plain text
You need your university credentials to authenticate with Vault. Do not write them directly in your playbook or variable files.
Option 1: Interactive prompt (simplest)
Add vars_prompt to your playbook to ask for credentials at runtime:
- hosts: all
vars_prompt:
- name: vault_ldap_username
prompt: "Enter your university username"
private: false
- name: vault_ldap_password
prompt: "Enter your university password"
private: true
Option 2: Ansible Vault (more robust)
Encrypt sensitive variables with Ansible Vault (note: this is a different product from HashiCorp Vault):
ansible-vault encrypt_string --prompt
This prompts you for an encryption password and a variable name (e.g., vault_ldap_password), then outputs an encrypted blob you can put in a variable file. Run your playbook with --ask-vault-pass to decrypt at runtime.
Store the encrypted variable file separately and add it to .gitignore - even encrypted secrets should not be in version control.
Writing Certificate Files to the VM¶
Use the response from the Vault module to write certificate files:
- name: Write certificate files
ansible.builtin.copy:
content: "{{ item.content }}"
dest: "{{ item.path }}"
mode: "{{ item.mode }}"
with_items:
- { path: '/etc/pki/tls/certs/server.crt', content: "{{ vault_pki_response.data.certificate }}", mode: '0644' }
- { path: '/etc/pki/tls/certs/ca-chain.crt', content: "{{ vault_pki_response.data.ca_chain | join('\n') }}", mode: '0644' }
- { path: '/etc/pki/tls/private/server.key', content: "{{ vault_pki_response.data.private_key }}", mode: '0600' }
notify:
- restart httpd
- restart postfix
- restart dovecot
CA chain format
The CA chain is returned as a list. Use the join('\n') Jinja2 filter to convert it to a single PEM string with newline separators between certificates.
Handlers¶
Define handlers to restart services only when certificate files change:
# roles/tls/handlers/main.yml
- name: restart httpd
systemd:
name: httpd
state: restarted
- name: restart postfix
systemd:
name: postfix
state: restarted
- name: restart dovecot
systemd:
name: dovecot
state: restarted
Tags¶
Use tags to run only TLS-related tasks:
- { role: tls, tags: tls }
Run only the TLS role: ansible-playbook --tags=tls playbook.yml
Useful Modules¶
community.hashi_vault.vault_pki_generate_certificate- request certificates from Vaultcopy- write certificate/key content to filestemplate- deploy TLS-enabled virtual host configurationsfirewalld- open ports (443, 465, 993)systemd- restart services after config changesfile- set ownership and permissions on certificate files