Skip to content

Commit 40edce0

Browse files
committed
Add mkcert.sh script for SSL certificate generation and management
1 parent d073ce6 commit 40edce0

File tree

2 files changed

+354
-1
lines changed

2 files changed

+354
-1
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
data
22
latest.yml
33
config
4-
lsws/conf
4+
lsws/conf
5+
certs

bin/mkcert.sh

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
#!/usr/bin/env bash
2+
DOMAIN=''
3+
INSTALL=''
4+
REMOVE=''
5+
CONT_NAME='litespeed'
6+
CERT_DIR='./certs'
7+
EPACE=' '
8+
9+
echow(){
10+
FLAG=${1}
11+
shift
12+
echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
13+
}
14+
15+
help_message(){
16+
echo -e "\033[1mUSAGE\033[0m"
17+
echo "${EPACE}mkcert.sh [OPTIONS]"
18+
echo ""
19+
echo -e "\033[1mOPTIONS\033[0m"
20+
echow '-D, --domain [DOMAIN_NAME]'
21+
echo "${EPACE}${EPACE}Example: mkcert.sh --domain example.test"
22+
echo "${EPACE}${EPACE}Will create certificate for example.test and www.example.test"
23+
echow '-I, --install'
24+
echo "${EPACE}${EPACE}Install mkcert on Windows (requires Chocolatey)"
25+
echow '-R, --remove'
26+
echo "${EPACE}${EPACE}Remove certificate for a specific domain"
27+
echow '-H, --help'
28+
echo "${EPACE}${EPACE}Display help and exit"
29+
exit 0
30+
}
31+
32+
check_input(){
33+
if [ -z "${1}" ]; then
34+
help_message
35+
fi
36+
}
37+
38+
domain_filter(){
39+
if [ -z "${1}" ]; then
40+
echo "[X] Domain name is required!"
41+
exit 1
42+
fi
43+
DOMAIN="${1}"
44+
DOMAIN="${DOMAIN#http://}"
45+
DOMAIN="${DOMAIN#https://}"
46+
DOMAIN="${DOMAIN#ftp://}"
47+
DOMAIN="${DOMAIN%%/*}"
48+
}
49+
50+
www_domain(){
51+
CHECK_WWW=$(echo ${1} | cut -c1-4)
52+
if [[ ${CHECK_WWW} == www. ]] ; then
53+
DOMAIN=$(echo ${1} | cut -c 5-)
54+
else
55+
DOMAIN=${1}
56+
fi
57+
WWW_DOMAIN="www.${DOMAIN}"
58+
}
59+
60+
check_mkcert(){
61+
echo '[Start] Checking mkcert installation'
62+
63+
# Try .exe first (for WSL/Windows)
64+
if command -v mkcert.exe >/dev/null 2>&1; then
65+
MKCERT_CMD="mkcert.exe"
66+
echo -e "[O] mkcert is installed (using: mkcert.exe)"
67+
elif command -v mkcert >/dev/null 2>&1; then
68+
MKCERT_CMD="mkcert"
69+
echo -e "[O] mkcert is installed (using: mkcert)"
70+
else
71+
echo "[X] mkcert is not installed!"
72+
echo "[!] Please run: ./bin/mkcert.sh --install"
73+
echo "[!] Or install manually: choco install mkcert"
74+
exit 1
75+
fi
76+
77+
echo '[End] Checking mkcert'
78+
}
79+
80+
install_mkcert(){
81+
echo '[Start] Installing mkcert'
82+
83+
# Try Windows executable first (for WSL/Git Bash)
84+
choco.exe --version > /dev/null 2>&1
85+
CHOCO_CHECK=$?
86+
87+
# If .exe doesn't work, try without extension
88+
if [ ${CHOCO_CHECK} != 0 ]; then
89+
choco --version > /dev/null 2>&1
90+
CHOCO_CHECK=$?
91+
fi
92+
93+
if [ ${CHOCO_CHECK} != 0 ]; then
94+
echo "[X] Chocolatey is not installed or not in PATH!"
95+
echo "[!] Please install Chocolatey first: https://chocolatey.org/install"
96+
echo "[!] After installation, restart your terminal"
97+
exit 1
98+
fi
99+
100+
echo "[O] Chocolatey is installed"
101+
102+
# Check if mkcert already installed (try .exe first for WSL)
103+
mkcert.exe -version > /dev/null 2>&1
104+
MKCERT_CHECK=$?
105+
106+
if [ ${MKCERT_CHECK} != 0 ]; then
107+
mkcert -version > /dev/null 2>&1
108+
MKCERT_CHECK=$?
109+
fi
110+
111+
if [ ${MKCERT_CHECK} = 0 ]; then
112+
echo "[!] mkcert is already installed"
113+
MKCERT_VERSION=$(mkcert.exe -version 2>&1 || mkcert -version 2>&1 | head -n 1)
114+
echo "[!] Version: ${MKCERT_VERSION}"
115+
echo "[!] Running mkcert -install to ensure local CA is configured..."
116+
mkcert.exe -install || mkcert -install
117+
echo '[End] Installing mkcert'
118+
return 0
119+
fi
120+
121+
echo "[!] Installing mkcert via Chocolatey..."
122+
choco.exe install mkcert -y || choco install mkcert -y
123+
124+
if [ ${?} = 0 ]; then
125+
echo -e "[O] mkcert installed successfully"
126+
echo "[!] Running mkcert -install to create local CA..."
127+
mkcert.exe -install || mkcert -install
128+
echo '[End] Installing mkcert'
129+
else
130+
echo "[X] Failed to install mkcert"
131+
exit 1
132+
fi
133+
}
134+
135+
create_cert_dir(){
136+
if [ ! -d "${CERT_DIR}" ]; then
137+
echo "[!] Creating certificate directory: ${CERT_DIR}"
138+
mkdir -p "${CERT_DIR}"
139+
fi
140+
}
141+
142+
generate_cert(){
143+
echo '[Start] Generating SSL certificate'
144+
domain_filter "${DOMAIN}"
145+
www_domain "${DOMAIN}"
146+
147+
create_cert_dir
148+
149+
cd "${CERT_DIR}"
150+
151+
echo -e "[!] Generating certificate for: \033[32m${DOMAIN}\033[0m and \033[32m${WWW_DOMAIN}\033[0m"
152+
153+
# Use the detected mkcert command
154+
${MKCERT_CMD} "${DOMAIN}" "${WWW_DOMAIN}"
155+
156+
if [ ${?} = 0 ]; then
157+
echo -e "[O] Certificate generated successfully"
158+
159+
# Rename files to standard format
160+
CERT_FILE="${DOMAIN}+1.pem"
161+
KEY_FILE="${DOMAIN}+1-key.pem"
162+
163+
if [ -f "${CERT_FILE}" ] && [ -f "${KEY_FILE}" ]; then
164+
echo "[!] Certificate files:"
165+
echo "${EPACE}Cert: ${CERT_DIR}/${CERT_FILE}"
166+
echo "${EPACE}Key: ${CERT_DIR}/${KEY_FILE}"
167+
fi
168+
else
169+
echo "[X] Failed to generate certificate"
170+
exit 1
171+
fi
172+
173+
cd - > /dev/null
174+
echo '[End] Generating SSL certificate'
175+
}
176+
177+
configure_litespeed(){
178+
echo '[Start] Configuring OpenLiteSpeed'
179+
180+
CERT_FILE="${DOMAIN}+1.pem"
181+
KEY_FILE="${DOMAIN}+1-key.pem"
182+
183+
# Check if certificate files exist
184+
if [ ! -f "${CERT_DIR}/${CERT_FILE}" ] || [ ! -f "${CERT_DIR}/${KEY_FILE}" ]; then
185+
echo "[X] Certificate files not found!"
186+
exit 1
187+
fi
188+
189+
echo "[!] Configuring SSL for domain: ${DOMAIN}"
190+
191+
LSWS_CONF_DIR="/usr/local/lsws/conf"
192+
HTTPD_CONF="${LSWS_CONF_DIR}/httpd_config.conf"
193+
194+
# Copy certificates to container
195+
docker compose cp "${CERT_DIR}/${CERT_FILE}" ${CONT_NAME}:${LSWS_CONF_DIR}/cert/
196+
docker compose cp "${CERT_DIR}/${KEY_FILE}" ${CONT_NAME}:${LSWS_CONF_DIR}/cert/
197+
198+
echo "[O] Certificates copied to container"
199+
200+
# Backup config
201+
docker compose exec -T ${CONT_NAME} bash -c "cp ${HTTPD_CONF} ${HTTPD_CONF}.backup.\$(date +%Y%m%d_%H%M%S)"
202+
echo "[O] Config backed up"
203+
204+
# Kiểm tra xem đã có SSL Listener chưa
205+
HAS_SSL=$(docker compose exec -T ${CONT_NAME} bash -c "grep -c 'listener Default HTTPS' ${HTTPD_CONF}" | tr -d '\r')
206+
207+
if [ "${HAS_SSL}" = "0" ]; then
208+
echo '[!] Creating new SSL Listener...'
209+
210+
# Tạo SSL listener mới
211+
docker compose exec -T ${CONT_NAME} bash -c "cat >> ${HTTPD_CONF} <<'LISTENER_EOF'
212+
213+
listener Default HTTPS {
214+
address *:443
215+
secure 1
216+
keyFile ${LSWS_CONF_DIR}/cert/${KEY_FILE}
217+
certFile ${LSWS_CONF_DIR}/cert/${CERT_FILE}
218+
certChain 1
219+
sslProtocol 24
220+
enableSpdy 15
221+
map ${DOMAIN} ${DOMAIN}
222+
}
223+
LISTENER_EOF
224+
"
225+
echo '[O] SSL Listener created'
226+
else
227+
echo '[!] SSL Listener exists, updating...'
228+
229+
# Cập nhật cert paths
230+
docker compose exec -T ${CONT_NAME} bash -c "
231+
sed -i '/listener Default HTTPS/,/^}/s|keyFile.*| keyFile ${LSWS_CONF_DIR}/cert/${KEY_FILE}|' ${HTTPD_CONF}
232+
sed -i '/listener Default HTTPS/,/^}/s|certFile.*| certFile ${LSWS_CONF_DIR}/cert/${CERT_FILE}|' ${HTTPD_CONF}
233+
"
234+
echo '[O] Certificate paths updated'
235+
236+
# Kiểm tra xem domain đã được map chưa
237+
HAS_MAPPING=$(docker compose exec -T ${CONT_NAME} bash -c "grep -A 15 'listener Default HTTPS' ${HTTPD_CONF} | grep -c 'map.*${DOMAIN}'" | tr -d '\r')
238+
239+
if [ "${HAS_MAPPING}" = "0" ]; then
240+
# Thêm mapping
241+
docker compose exec -T ${CONT_NAME} bash -c "
242+
sed -i '/listener Default HTTPS/,/^}/ {
243+
/^}/i\ map ${DOMAIN} ${DOMAIN}
244+
}' ${HTTPD_CONF}
245+
"
246+
echo '[O] Domain mapping added to SSL Listener'
247+
else
248+
echo '[!] Domain mapping already exists'
249+
fi
250+
fi
251+
252+
echo ""
253+
echo "[!] Current SSL Listener configuration:"
254+
docker compose exec -T ${CONT_NAME} bash -c "grep -A 15 'listener Default HTTPS' ${HTTPD_CONF}"
255+
echo ""
256+
257+
if [ ${?} = 0 ]; then
258+
echo -e "[O] SSL configured for: \033[32m${DOMAIN}\033[0m"
259+
echo "[!] Restarting OpenLiteSpeed..."
260+
lsws_restart
261+
else
262+
echo "[X] Failed to configure SSL"
263+
exit 1
264+
fi
265+
266+
echo '[End] Configuring OpenLiteSpeed'
267+
}
268+
269+
lsws_restart(){
270+
docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
271+
if [ ${?} = 0 ]; then
272+
echo -e "[O] OpenLiteSpeed restarted successfully"
273+
else
274+
echo "[X] Failed to restart OpenLiteSpeed"
275+
fi
276+
}
277+
278+
remove_cert(){
279+
echo '[Start] Removing SSL certificate'
280+
domain_filter "${DOMAIN}"
281+
282+
CERT_FILE="${DOMAIN}+1.pem"
283+
KEY_FILE="${DOMAIN}+1-key.pem"
284+
285+
if [ -f "${CERT_DIR}/${CERT_FILE}" ]; then
286+
rm "${CERT_DIR}/${CERT_FILE}"
287+
echo -e "[O] Removed: ${CERT_DIR}/${CERT_FILE}"
288+
fi
289+
290+
if [ -f "${CERT_DIR}/${KEY_FILE}" ]; then
291+
rm "${CERT_DIR}/${KEY_FILE}"
292+
echo -e "[O] Removed: ${CERT_DIR}/${KEY_FILE}"
293+
fi
294+
295+
# Remove SSL listener config
296+
SSL_LISTENER="/usr/local/lsws/conf/cert/${DOMAIN}.xml"
297+
docker compose exec ${CONT_NAME} bash -c "[ -f ${SSL_LISTENER} ] && rm ${SSL_LISTENER}"
298+
299+
echo '[End] Removing SSL certificate'
300+
lsws_restart
301+
}
302+
303+
main(){
304+
if [ "${INSTALL}" = 'true' ]; then
305+
install_mkcert
306+
exit 0
307+
fi
308+
309+
if [ "${REMOVE}" = 'true' ]; then
310+
remove_cert
311+
exit 0
312+
fi
313+
314+
check_mkcert
315+
generate_cert
316+
configure_litespeed
317+
318+
echo ""
319+
echo -e "\033[1m[SUCCESS] SSL certificate setup completed!\033[0m"
320+
echo ""
321+
echo "Next steps:"
322+
echo "1. Add '${DOMAIN}' to your Windows hosts file (C:\Windows\System32\drivers\etc\hosts)"
323+
echo " Example: 127.0.0.1 ${DOMAIN} ${WWW_DOMAIN}"
324+
echo "2. Configure your virtual host to use SSL-${DOMAIN} listener"
325+
echo "3. Access https://${DOMAIN} in your browser"
326+
}
327+
328+
check_input ${1}
329+
while [ ! -z "${1}" ]; do
330+
case ${1} in
331+
-[hH] | -help | --help)
332+
help_message
333+
;;
334+
-[dD] | -domain | --domain)
335+
shift
336+
check_input "${1}"
337+
DOMAIN="${1}"
338+
;;
339+
-[iI] | --install)
340+
INSTALL=true
341+
;;
342+
-[rR] | --remove)
343+
REMOVE=true
344+
;;
345+
*)
346+
help_message
347+
;;
348+
esac
349+
shift
350+
done
351+
352+
main

0 commit comments

Comments
 (0)