├── acme
└── .gitignore
├── lsws
└── .gitignore
├── logs
└── .gitignore
├── sites
├── .gitignore
└── localhost
│ ├── html
│ └── .gitignore
│ └── logs
│ └── .gitignore
├── .gitignore
├── bin
├── dev
│ ├── list-flagged-files.sh
│ ├── no-skip-worktree-conf.sh
│ └── skip-worktree-conf.sh
├── container
│ ├── certhookctl.sh
│ ├── serialctl.sh
│ ├── domainctl.sh
│ ├── owaspctl.sh
│ └── appinstallctl.sh
├── appinstall.sh
├── domain.sh
├── demosite.sh
├── webadmin.sh
├── database.sh
├── acme.sh
└── mkcert.sh
├── .env
├── .github
└── workflows
│ └── docker.yml
├── .gitattributes
├── .travis
├── main.sh
└── verify.sh
├── LICENSE
├── .travis.yml.bk
├── docker-compose.yml
└── README.md
/acme/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/lsws/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/sites/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/sites/localhost/html/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/sites/localhost/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | data
2 | latest.yml
3 | config
4 | lsws/conf
5 | certs
--------------------------------------------------------------------------------
/bin/dev/list-flagged-files.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | git ls-files -v|grep '^S'
3 |
--------------------------------------------------------------------------------
/bin/dev/no-skip-worktree-conf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --no-skip-worktree" \;
3 |
--------------------------------------------------------------------------------
/bin/dev/skip-worktree-conf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --skip-worktree" \;
3 |
4 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | TimeZone=America/New_York
2 | OLS_VERSION=1.8.4
3 | PHP_VERSION=lsphp84
4 | PHPMYADMIN_VERSION=5.2.3
5 | MYSQL_ROOT_PASSWORD=your_root_password
6 | MYSQL_DATABASE=wordpress
7 | MYSQL_USER=wordpress
8 | MYSQL_PASSWORD=your_password
9 | DOMAIN=localhost
10 |
--------------------------------------------------------------------------------
/bin/container/certhookctl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | BOTCRON='/var/spool/cron/crontabs/root'
3 |
4 | cert_hook(){
5 | grep 'acme' ${BOTCRON} >/dev/null
6 | if [ ${?} = 0 ]; then
7 | grep 'lswsctrl' ${BOTCRON} >/dev/null
8 | if [ ${?} = 0 ]; then
9 | echo 'Hook already exist, skip!'
10 | else
11 | sed -i 's/--cron/--cron --renew-hook "\/usr\/local\/lsws\/bin\/lswsctrl restart"/g' ${BOTCRON}
12 | fi
13 | else
14 | echo "[X] ${BOTCRON} does not exist, please check it later!"
15 | fi
16 | }
17 |
18 | cert_hook
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: docker-build
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | pull_request:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | test:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Setup
18 | run: |
19 | docker compose version
20 | docker compose up -d
21 | docker image ls
22 | sleep 10
23 | - name: Verify
24 | run: bash .travis/verify.sh
25 | - name: Clean up
26 | run: |
27 | docker compose stop
28 | docker compose rm -f
29 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | #
2 | # Configure line ending normalisation for this repository.
3 | # See http://schacon.github.io/git/gitattributes.html for more information.
4 | #
5 | # Also each developer should configure the old style normalisation on her workstation
6 | # (see http://timclem.wordpress.com/2012/03/01/mind-the-end-of-your-line/):
7 | #
8 | # Windows user should use: git config --global core.autocrlf = true
9 | # Unix/Linux users should use: git config --global core.autocrlf = input
10 | #
11 |
12 | # Auto detect text files and perform LF normalization
13 | * text=auto
14 |
15 | *.txt text
16 | *.xml text diff=xml
17 | # Shell scripts require LF
18 | *.sh text eol=lf
19 | # Batch scripts require CRLF
20 | *.bat text eol=crlf
21 |
--------------------------------------------------------------------------------
/.travis/main.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 |
5 | setup_dependencies() {
6 | echo "INFO: Setting up dependencies."
7 | sudo apt-get install git -y
8 | sudo rm /usr/local/bin/docker-compose
9 | curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` \
10 | > docker-compose
11 | chmod +x docker-compose
12 | sudo mv docker-compose /usr/local/bin
13 | docker-compose --version
14 | }
15 |
16 | update_docker_configuration() {
17 | echo "INFO: Updating docker configuration."
18 | echo '{
19 | "experimental": true,
20 | "storage-driver": "overlay2",
21 | "max-concurrent-downloads": 50,
22 | "max-concurrent-uploads": 50
23 | }' | sudo tee /etc/docker/daemon.json
24 | sudo service docker restart
25 | }
26 |
27 | main() {
28 | setup_dependencies
29 | update_docker_configuration
30 | echo "SUCCESS: Done! Finished setting up Travis machine."
31 | }
32 |
33 | main
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 - 2022 Litespeedtech
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/bin/appinstall.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | APP_NAME=''
3 | DOMAIN=''
4 | EPACE=' '
5 |
6 | echow(){
7 | FLAG=${1}
8 | shift
9 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
10 | }
11 |
12 | help_message(){
13 | echo -e "\033[1mOPTIONS\033[0m"
14 | echow '-A, --app [app_name] -D, --domain [DOMAIN_NAME]'
15 | echo "${EPACE}${EPACE}Example: appinstall.sh -A wordpress -D example.com"
16 | echo "${EPACE}${EPACE}Will install WordPress CMS under the example.com domain"
17 | echow '-H, --help'
18 | echo "${EPACE}${EPACE}Display help and exit."
19 | exit 0
20 | }
21 |
22 | check_input(){
23 | if [ -z "${1}" ]; then
24 | help_message
25 | exit 1
26 | fi
27 | }
28 |
29 | app_download(){
30 | docker compose exec litespeed su -c "appinstallctl.sh --app ${1} --domain ${2}"
31 | bash bin/webadmin.sh -r
32 | exit 0
33 | }
34 |
35 | main(){
36 | app_download ${APP_NAME} ${DOMAIN}
37 | }
38 |
39 | check_input ${1}
40 | while [ ! -z "${1}" ]; do
41 | case ${1} in
42 | -[hH] | -help | --help)
43 | help_message
44 | ;;
45 | -[aA] | -app | --app) shift
46 | check_input "${1}"
47 | APP_NAME="${1}"
48 | ;;
49 | -[dD] | -domain | --domain) shift
50 | check_input "${1}"
51 | DOMAIN="${1}"
52 | ;;
53 | *)
54 | help_message
55 | ;;
56 | esac
57 | shift
58 | done
59 |
60 | main
--------------------------------------------------------------------------------
/.travis.yml.bk:
--------------------------------------------------------------------------------
1 | language: shell
2 | os: linux
3 |
4 | notifications:
5 | email:
6 | on_success: never
7 | on_failure: always
8 | slack:
9 | secure: mSj4SYM4weApU3Ct+nqdaHkLw0J/q5+VH1q0LYnviZ06UpRU/N6lricfu9ihgND2VJ+cwfuQpAegdI1cDFzxDRpZpnzU9Db4N7OW5cDkb8eHpy6XhjQYi5KqWfgamh2UwiqYGgoQBc4gXhGDlChjJQopM+qPesHO9y/ucFAjxdlkEHVdZKNYDiVfiOKCGBzDuP+PbOPiZiqQFBgmFs1YLLrrQ7y5dgdoiai2I72MAN0kngoNB9ZsUgtQ63WTdgPKJOiX+oQMMXgYoP0+9iIhS6/cKHs64Z7jPreYYuWWMTnQPdvaIgh4ASIhUE6FVI5SdFxmajVik8SMlRK1rQApQLJ9wOJammUJHCSI4jfEVQ5H2og9R3+BA0qspBQVZXMTCYfX10Up1tmL+Kev1Za335v2z046gzX4aTiWBxi1I9mYmnYKQiGuaIG5crkPodIAeS9HX/DulMUhPRpa0Djwi7ZJlCAzfuEGDgAlWt/oWfIw66unTY/G6cEaxeEbZ3Ho+bPy48dRxhYW5kRHR1OuHqqfNULYeAGm6AIF3ng+2GjvXh6rhqmstBh/myROqM3X7ofUzEJRo9ow+hDroZLE4mfavn4UAnQybN7FfzuJiOoYTmcws7JzYD8b/G+Aynjw04m0ojDDJN0fVgQ+qltMoCfFMhx8FscF/QjGo/T/zc4=
10 | template:
11 | - "Repo %{repository_slug} *%{result}* build (<%{build_url}|#%{build_number}>) for commit (<%{compare_url}|%{commit}>)"
12 | - "%{author}: _%{commit_message}_"
13 | - "Execution time: *%{duration}*"
14 | - "Message: *%{message}*"
15 | on_success: always
16 |
17 | services:
18 | - docker
19 |
20 | env:
21 | - DOCKER_COMPOSE_VERSION=1.25.0
22 |
23 | before_install:
24 | - bash .travis/main.sh
25 |
26 | install:
27 | - git clone https://github.com/litespeedtech/ols-docker-env.git
28 | - docker-compose up -d
29 |
30 | before_script:
31 | - docker image ls
32 | - sleep 10
33 |
34 | script:
35 | - bash .travis/verify.sh
36 |
37 | after_script:
38 | - docker-compose stop
39 | - docker-compose rm -f
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bin/domain.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | CONT_NAME='litespeed'
3 | EPACE=' '
4 |
5 | echow(){
6 | FLAG=${1}
7 | shift
8 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
9 | }
10 |
11 | help_message(){
12 | echo -e "\033[1mOPTIONS\033[0m"
13 | echow "-A, --add [domain_name]"
14 | echo "${EPACE}${EPACE}Example: domain.sh -A example.com, will add the domain to Listener and auto create a new virtual host."
15 | echow "-D, --del [domain_name]"
16 | echo "${EPACE}${EPACE}Example: domain.sh -D example.com, will delete the domain from Listener."
17 | echow '-H, --help'
18 | echo "${EPACE}${EPACE}Display help and exit."
19 | }
20 |
21 | check_input(){
22 | if [ -z "${1}" ]; then
23 | help_message
24 | exit 1
25 | fi
26 | }
27 |
28 | add_domain(){
29 | check_input ${1}
30 | docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --add ${1}"
31 | if [ ! -d "./sites/${1}" ]; then
32 | mkdir -p ./sites/${1}/{html,logs,certs}
33 | fi
34 | bash bin/webadmin.sh -r
35 | }
36 |
37 | del_domain(){
38 | check_input ${1}
39 | docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --del ${1}"
40 | bash bin/webadmin.sh -r
41 | }
42 |
43 | check_input ${1}
44 | while [ ! -z "${1}" ]; do
45 | case ${1} in
46 | -[hH] | -help | --help)
47 | help_message
48 | ;;
49 | -[aA] | -add | --add) shift
50 | add_domain ${1}
51 | ;;
52 | -[dD] | -del | --del | --delete) shift
53 | del_domain ${1}
54 | ;;
55 | *)
56 | help_message
57 | ;;
58 | esac
59 | shift
60 | done
61 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | mysql:
3 | image: mariadb:11.4
4 | logging:
5 | driver: none
6 | command: ["--max-allowed-packet=512M"]
7 | volumes:
8 | - "./data/db:/var/lib/mysql:delegated"
9 | environment:
10 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
11 | MYSQL_DATABASE: ${MYSQL_DATABASE}
12 | MYSQL_USER: ${MYSQL_USER}
13 | MYSQL_PASSWORD: ${MYSQL_PASSWORD}
14 | restart: always
15 | networks:
16 | - default
17 | litespeed:
18 | image: litespeedtech/openlitespeed:${OLS_VERSION}-${PHP_VERSION}
19 | container_name: litespeed
20 | env_file:
21 | - .env
22 | volumes:
23 | - ./lsws/conf:/usr/local/lsws/conf
24 | - ./lsws/admin-conf:/usr/local/lsws/admin/conf
25 | - ./bin/container:/usr/local/bin
26 | - ./sites:/var/www/vhosts/
27 | - ./acme:/root/.acme.sh/
28 | - ./logs:/usr/local/lsws/logs/
29 | ports:
30 | - 80:80
31 | - 443:443
32 | - 443:443/udp
33 | - 7080:7080
34 | restart: always
35 | environment:
36 | TZ: ${TimeZone}
37 | networks:
38 | - default
39 | phpmyadmin:
40 | image: phpmyadmin/phpmyadmin:${PHPMYADMIN_VERSION}
41 | env_file:
42 | - .env
43 | ports:
44 | - 8080:80
45 | environment:
46 | PMA_HOST: mysql
47 | restart: always
48 | networks:
49 | - default
50 | redis:
51 | image: "redis:alpine"
52 | logging:
53 | driver: none
54 | # command: redis-server --requirepass 8b405f60665e48f795752e534d93b722
55 | volumes:
56 | - ./redis/data:/data
57 | - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
58 | environment:
59 | - REDIS_REPLICATION_MODE=master
60 | restart: always
61 | networks:
62 | - default
63 | networks:
64 | default:
65 | driver: bridge
66 |
--------------------------------------------------------------------------------
/bin/container/serialctl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | LSDIR='/usr/local/lsws'
3 | EPACE=' '
4 |
5 | echow(){
6 | FLAG=${1}
7 | shift
8 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
9 | }
10 |
11 | help_message(){
12 | echo -e "\033[1mOPTIONS\033[0m"
13 | echow '-S, --serial [YOUR_SERIAL|TRIAL]'
14 | echo "${EPACE}${EPACE}Will apply and register the serial to LSWS."
15 | echow '-H, --help'
16 | echo "${EPACE}${EPACE}Display help and exit."
17 | exit 0
18 | }
19 |
20 | check_input(){
21 | if [ -z "${1}" ]; then
22 | help_message
23 | exit 1
24 | fi
25 | }
26 |
27 | backup_old(){
28 | if [ -f ${1} ] && [ ! -f ${1}_old ]; then
29 | mv ${1} ${1}_old
30 | fi
31 | }
32 |
33 | detect_ols(){
34 | if [ -e ${LSDIR}/bin/openlitespeed ]; then
35 | echo '[X] Detect OpenLiteSpeed, abort!'
36 | exit 1
37 | fi
38 | }
39 |
40 | apply_serial(){
41 | detect_ols
42 | check_input ${1}
43 | echo ${1} | grep -i 'trial' >/dev/null
44 | if [ ${?} = 0 ]; then
45 | echo 'Apply Trial License'
46 | if [ ! -e ${LSDIR}/conf/serial.no ] && [ ! -e ${LSDIR}/conf/license.key ]; then
47 | rm -f ${LSDIR}/conf/trial.key*
48 | wget -P ${LSDIR}/conf -q http://license.litespeedtech.com/reseller/trial.key
49 | echo 'Apply trial finished'
50 | else
51 | echo "Please backup and remove your existing license, apply abort!"
52 | exit 1
53 | fi
54 | else
55 | echo "Apply Serial number: ${1}"
56 | backup_old ${LSDIR}/conf/serial.no
57 | backup_old ${LSDIR}/conf/license.key
58 | backup_old ${LSDIR}/conf/trial.key
59 | echo "${1}" > ${LSDIR}/conf/serial.no
60 | ${LSDIR}/bin/lshttpd -r
61 | if [ -f ${LSDIR}/conf/license.key ]; then
62 | echo '[O] Apply success'
63 | else
64 | echo '[X] Apply failed, please check!'
65 | exit 1
66 | fi
67 | fi
68 | }
69 |
70 | check_input ${1}
71 | while [ ! -z "${1}" ]; do
72 | case ${1} in
73 | -[hH] | -help | --help)
74 | help_message
75 | ;;
76 | -[sS] | -serial | --serial) shift
77 | apply_serial "${1}"
78 | ;;
79 | *)
80 | help_message
81 | ;;
82 | esac
83 | shift
84 | done
--------------------------------------------------------------------------------
/bin/demosite.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | source .env
3 | APP_NAME='wordpress'
4 | CONT_NAME='litespeed'
5 | DOC_FD=''
6 |
7 | echow(){
8 | FLAG=${1}
9 | shift
10 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
11 | }
12 |
13 | help_message(){
14 | case ${1} in
15 | "1")
16 | echow "Script will get 'DOMAIN' and 'database' info from .env file, then auto setup virtual host and the wordpress site for you."
17 | exit 0
18 | ;;
19 | "2")
20 | echow 'Service finished, enjoy your accelarated LiteSpeed server!'
21 | ;;
22 | esac
23 | }
24 |
25 | domain_filter(){
26 | if [ ! -n "${DOMAIN}" ]; then
27 | echo "Parameters not supplied, please check!"
28 | exit 1
29 | fi
30 | DOMAIN="${1}"
31 | DOMAIN="${DOMAIN#http://}"
32 | DOMAIN="${DOMAIN#https://}"
33 | DOMAIN="${DOMAIN#ftp://}"
34 | DOMAIN="${DOMAIN#scp://}"
35 | DOMAIN="${DOMAIN#scp://}"
36 | DOMAIN="${DOMAIN#sftp://}"
37 | DOMAIN=${DOMAIN%%/*}
38 | }
39 |
40 | gen_root_fd(){
41 | DOC_FD="./sites/${1}/"
42 | if [ -d "./sites/${1}" ]; then
43 | echo -e "[O] The root folder \033[32m${DOC_FD}\033[0m exist."
44 | else
45 | echo "Creating - document root."
46 | bash bin/domain.sh -add ${1}
47 | echo "Finished - document root."
48 | fi
49 | }
50 |
51 | create_db(){
52 | if [ ! -n "${MYSQL_DATABASE}" ] || [ ! -n "${MYSQL_USER}" ] || [ ! -n "${MYSQL_PASSWORD}" ]; then
53 | echo "Parameters not supplied, please check!"
54 | exit 1
55 | else
56 | bash bin/database.sh -D ${1} -U ${MYSQL_USER} -P ${MYSQL_PASSWORD} -DB ${MYSQL_DATABASE}
57 | fi
58 | }
59 |
60 | store_credential(){
61 | if [ -f ${DOC_FD}/.db_pass ]; then
62 | echo '[O] db file exist!'
63 | else
64 | echo 'Storing database parameter'
65 | cat > "${DOC_FD}/.db_pass" << EOT
66 | "Database":"${MYSQL_DATABASE}"
67 | "Username":"${MYSQL_USER}"
68 | "Password":"$(echo ${MYSQL_PASSWORD} | tr -d "'")"
69 | EOT
70 | fi
71 | }
72 |
73 | app_download(){
74 | docker compose exec -T ${CONT_NAME} su -c "appinstallctl.sh --app ${1} --domain ${2}"
75 | }
76 |
77 | lsws_restart(){
78 | bash bin/webadmin.sh -r
79 | }
80 |
81 | main(){
82 | domain_filter ${DOMAIN}
83 | gen_root_fd ${DOMAIN}
84 | create_db ${DOMAIN}
85 | store_credential
86 | app_download ${APP_NAME} ${DOMAIN}
87 | lsws_restart
88 | help_message 2
89 | }
90 |
91 | while [ ! -z "${1}" ]; do
92 | case ${1} in
93 | -[hH] | -help | --help)
94 | help_message 1
95 | ;;
96 | *)
97 | help_message 1
98 | ;;
99 | esac
100 | shift
101 | done
102 | main
--------------------------------------------------------------------------------
/bin/webadmin.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | CONT_NAME='litespeed'
3 | EPACE=' '
4 |
5 | echow(){
6 | FLAG=${1}
7 | shift
8 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
9 | }
10 |
11 | help_message(){
12 | echo -e "\033[1mOPTIONS\033[0m"
13 | echow '[Enter Your PASSWORD]'
14 | echo "${EPACE}${EPACE}Example: webadmin.sh MY_SECURE_PASS, to update web admin password immediatly."
15 | echow '-R, --restart'
16 | echo "${EPACE}${EPACE}Will gracefully restart LiteSpeed Web Server."
17 | echow '-M, --mod-secure [enable|disable]'
18 | echo "${EPACE}${EPACE}Example: webadmin.sh -M enable, will enable and apply Mod_Secure OWASP rules on server"
19 | echow '-U, --upgrade'
20 | echo "${EPACE}${EPACE}Will upgrade web server to latest stable version"
21 | echow '-S, --serial [YOUR_SERIAL|TRIAL]'
22 | echo "${EPACE}${EPACE}Will apply your serial number to LiteSpeed Web Server."
23 | echow '-H, --help'
24 | echo "${EPACE}${EPACE}Display help and exit."
25 | exit 0
26 | }
27 |
28 | check_input(){
29 | if [ -z "${1}" ]; then
30 | help_message
31 | exit 1
32 | fi
33 | }
34 |
35 | lsws_restart(){
36 | docker compose exec -T ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
37 | }
38 |
39 | apply_serial(){
40 | docker compose exec ${CONT_NAME} su -c "serialctl.sh --serial ${1}"
41 | lsws_restart
42 | }
43 |
44 | mod_secure(){
45 | if [ "${1}" = 'enable' ] || [ "${1}" = 'Enable' ]; then
46 | docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --enable"
47 | lsws_restart
48 | elif [ "${1}" = 'disable' ] || [ "${1}" = 'Disable' ]; then
49 | docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --disable"
50 | lsws_restart
51 | else
52 | help_message
53 | fi
54 | }
55 |
56 | ls_upgrade(){
57 | echo 'Upgrade web server to latest stable version.'
58 | docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/admin/misc/lsup.sh 2>/dev/null'
59 | }
60 |
61 | set_web_admin(){
62 | echo 'Update web admin password.'
63 | local LSADPATH='/usr/local/lsws/admin'
64 | docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c \
65 | 'if [ -e /usr/local/lsws/admin/fcgi-bin/admin_php ]; then \
66 | echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
67 | else echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php5 -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
68 | fi';
69 | }
70 |
71 | main(){
72 | set_web_admin ${1}
73 | }
74 |
75 | check_input ${1}
76 | while [ ! -z "${1}" ]; do
77 | case ${1} in
78 | -[hH] | -help | --help)
79 | help_message
80 | ;;
81 | -[rR] | -restart | --restart)
82 | lsws_restart
83 | ;;
84 | -M | -mode-secure | --mod-secure) shift
85 | mod_secure ${1}
86 | ;;
87 | -lsup | --lsup | --upgrade | -U) shift
88 | ls_upgrade
89 | ;;
90 | -[sS] | -serial | --serial) shift
91 | apply_serial ${1}
92 | ;;
93 | *)
94 | main ${1}
95 | ;;
96 | esac
97 | shift
98 | done
--------------------------------------------------------------------------------
/.travis/verify.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 | EX_DM='example.com'
5 |
6 | install_demo(){
7 | ./bin/demosite.sh
8 | }
9 |
10 | verify_lsws(){
11 | curl -sIk http://localhost:7080/ | grep -i LiteSpeed
12 | if [ ${?} = 0 ]; then
13 | echo '[O] https://localhost:7080/'
14 | else
15 | echo '[X] https://localhost:7080/'
16 | exit 1
17 | fi
18 | }
19 |
20 | verify_page(){
21 | curl -sIk http://localhost:80/ | grep -i WordPress
22 | if [ ${?} = 0 ]; then
23 | echo '[O] http://localhost:80/'
24 | else
25 | echo '[X] http://localhost:80/'
26 | curl -sIk http://localhost:80/
27 | exit 1
28 | fi
29 | curl -sIk https://localhost:443/ | grep -i WordPress
30 | if [ ${?} = 0 ]; then
31 | echo '[O] https://localhost:443/'
32 | else
33 | echo '[X] https://localhost:443/'
34 | curl -sIk https://localhost:443/
35 | exit 1
36 | fi
37 | }
38 |
39 | verify_phpadmin(){
40 | curl -sIk http://localhost:8080/ | grep -i phpMyAdmin
41 | if [ ${?} = 0 ]; then
42 | echo '[O] http://localhost:8080/'
43 | else
44 | echo '[X] http://localhost:8080/'
45 | exit 1
46 | fi
47 | }
48 |
49 | verify_add_vh_wp(){
50 | echo "Setup a WordPress site with ${EX_DM} domain"
51 | bash bin/domain.sh --add "${EX_DM}"
52 | bash bin/database.sh --domain "${EX_DM}"
53 | bash bin/appinstall.sh --app wordpress --domain "${EX_DM}"
54 | curl -sIk http://${EX_DM}:80/ --resolve ${EX_DM}:80:127.0.0.1 | grep -i WordPress
55 | if [ ${?} = 0 ]; then
56 | echo "[O] http://${EX_DM}:80/"
57 | else
58 | echo "[X] http://${EX_DM}:80/"
59 | curl -sIk http://${EX_DM}:80/
60 | exit 1
61 | fi
62 | }
63 | verify_del_vh_wp(){
64 | echo "Remove ${EX_DM} domain"
65 | bash bin/domain.sh --del ${EX_DM}
66 | if [ ${?} = 0 ]; then
67 | echo "[O] ${EX_DM} VH is removed"
68 | else
69 | echo "[X] ${EX_DM} VH is not removed"
70 | exit 1
71 | fi
72 | echo "Remove examplecom DataBase"
73 | bash bin/database.sh --delete -DB examplecom
74 | }
75 |
76 | verify_owasp(){
77 | echo 'Updating LSWS'
78 | bash bin/webadmin.sh --upgrade 2>&1 /dev/null
79 | echo 'Enabling OWASP'
80 | bash bin/webadmin.sh --mod-secure enable
81 | curl -sIk http://localhost:80/phpinfo.php | awk '/HTTP/ && /403/'
82 | if [ ${?} = 0 ]; then
83 | echo '[O] OWASP enable'
84 | else
85 | echo '[X] OWASP enable'
86 | curl -sIk http://localhost:80/phpinfo.php | awk '/HTTP/ && /403/'
87 | exit 1
88 | fi
89 | bash bin/webadmin.sh --mod-secure disable
90 | curl -sIk http://localhost:80/phpinfo.php | grep -i WordPress
91 | if [ ${?} = 0 ]; then
92 | echo '[O] OWASP disable'
93 | else
94 | echo '[X] OWASP disable'
95 | curl -sIk http://localhost:80/phpinfo.php
96 | exit 1
97 | fi
98 | }
99 |
100 |
101 | main(){
102 | verify_lsws
103 | verify_phpadmin
104 | install_demo
105 | verify_page
106 | verify_owasp
107 | verify_add_vh_wp
108 | verify_del_vh_wp
109 | }
110 | main
--------------------------------------------------------------------------------
/bin/container/domainctl.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | CK_RESULT=''
3 | LSDIR='/usr/local/lsws'
4 | LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
5 | OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
6 | EPACE=' '
7 |
8 | echow(){
9 | FLAG=${1}
10 | shift
11 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
12 | }
13 |
14 | help_message(){
15 | echo -e "\033[1mOPTIONS\033[0m"
16 | echow '-A, --add [DOMAIN_NAME]'
17 | echo "${EPACE}${EPACE}Will add domain to listener and creat a virtual host from template"
18 | echow '-D, --del [DOMAIN_NAME]'
19 | echo "${EPACE}${EPACE}Will delete domain from listener"
20 | echow '-H, --help'
21 | echo "${EPACE}${EPACE}Display help."
22 | }
23 |
24 | check_lsv(){
25 | if [ -f ${LSDIR}/bin/openlitespeed ]; then
26 | LSV='openlitespeed'
27 | elif [ -f ${LSDIR}/bin/litespeed ]; then
28 | LSV='lsws'
29 | else
30 | echo 'Version not exist, abort!'
31 | exit 1
32 | fi
33 | }
34 |
35 | dot_escape(){
36 | ESCAPE=$(echo ${1} | sed 's/\./\\./g')
37 | }
38 |
39 | check_duplicate(){
40 | CK_RESULT=$(grep -E "${1}" ${2})
41 | }
42 |
43 | fst_match_line(){
44 | FIRST_LINE_NUM=$(grep -n -m 1 ${1} ${2} | awk -F ':' '{print $1}')
45 | }
46 | fst_match_after(){
47 | FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
48 | }
49 | lst_match_line(){
50 | fst_match_after ${1} ${2} ${3}
51 | LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
52 | }
53 |
54 | check_input(){
55 | if [ -z "${1}" ]; then
56 | help_message
57 | exit 1
58 | fi
59 | }
60 |
61 | check_www(){
62 | CHECK_WWW=$(echo ${1} | cut -c1-4)
63 | if [[ ${CHECK_WWW} == www. ]] ; then
64 | echo 'www domain shoudnt be passed!'
65 | exit 1
66 | fi
67 | }
68 |
69 | www_domain(){
70 | check_www ${1}
71 | WWW_DOMAIN=$(echo www.${1})
72 | }
73 |
74 | add_ls_domain(){
75 | fst_match_line 'docker.xml' ${LS_HTTPD_CONF}
76 | NEWNUM=$((FIRST_LINE_NUM+2))
77 | sed -i "${NEWNUM}i \ \ \ \ \ \ \n \ \ \ \ \ \ \ ${DOMAIN}\n \ \ \ \ \ \ \ ${DOMAIN},${WWW_DOMAIN}\n \ \ \ \ \ \ " ${LS_HTTPD_CONF}
78 | }
79 |
80 | add_ols_domain(){
81 | perl -0777 -p -i -e 's/(vhTemplate docker \{[^}]+)\}*(^.*listeners.*$)/\1$2
82 | member '${DOMAIN}' {
83 | vhDomain '${DOMAIN},${WWW_DOMAIN}'
84 | }/gmi' ${OLS_HTTPD_CONF}
85 | }
86 |
87 | add_domain(){
88 | check_lsv
89 | dot_escape ${1}
90 | DOMAIN=${ESCAPE}
91 | www_domain ${1}
92 | if [ "${LSV}" = 'lsws' ]; then
93 | check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
94 | if [ "${CK_RESULT}" != '' ]; then
95 | echo "# It appears the domain already exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
96 | exit 1
97 | fi
98 | add_ls_domain
99 | elif [ "${LSV}" = 'openlitespeed' ]; then
100 | check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
101 | if [ "${CK_RESULT}" != '' ]; then
102 | echo "# It appears the domain already exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
103 | exit 1
104 | fi
105 | add_ols_domain
106 | fi
107 | }
108 |
109 | del_ls_domain(){
110 | fst_match_line "*${1}" ${LS_HTTPD_CONF}
111 | FIRST_LINE_NUM=$((FIRST_LINE_NUM-1))
112 | lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} ''
113 | sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
114 | }
115 |
116 | del_ols_domain(){
117 | fst_match_line ${1} ${OLS_HTTPD_CONF}
118 | lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
119 | sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
120 | }
121 |
122 | del_domain(){
123 | check_lsv
124 | dot_escape ${1}
125 | DOMAIN=${ESCAPE}
126 | if [ "${LSV}" = 'lsws' ]; then
127 | check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
128 | if [ "${CK_RESULT}" = '' ]; then
129 | echo "# Domain non-exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
130 | exit 1
131 | fi
132 | del_ls_domain ${1}
133 | elif [ "${LSV}" = 'openlitespeed' ]; then
134 | check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
135 | if [ "${CK_RESULT}" = '' ]; then
136 | echo "# Domain non-exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
137 | exit 1
138 | fi
139 | del_ols_domain ${1}
140 | fi
141 | }
142 |
143 | check_input ${1}
144 | while [ ! -z "${1}" ]; do
145 | case ${1} in
146 | -[hH] | -help | --help)
147 | help_message
148 | ;;
149 | -[aA] | -add | --add) shift
150 | add_domain ${1}
151 | ;;
152 | -[dD] | -del | --del | --delete) shift
153 | del_domain ${1}
154 | ;;
155 | *)
156 | help_message
157 | ;;
158 | esac
159 | shift
160 | done
--------------------------------------------------------------------------------
/bin/database.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | source .env
3 |
4 | DOMAIN=''
5 | SQL_DB=''
6 | SQL_USER=''
7 | SQL_PASS=''
8 | ANY="'%'"
9 | SET_OK=0
10 | EPACE=' '
11 | METHOD=0
12 |
13 | echow(){
14 | FLAG=${1}
15 | shift
16 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
17 | }
18 |
19 | help_message(){
20 | echo -e "\033[1mOPTIONS\033[0m"
21 | echow '-D, --domain [DOMAIN_NAME]'
22 | echo "${EPACE}${EPACE}Example: database.sh -D example.com"
23 | echo "${EPACE}${EPACE}Will auto-generate Database/username/password for the domain"
24 | echow '-D, --domain [DOMAIN_NAME] -U, --user [xxx] -P, --password [xxx] -DB, --database [xxx]'
25 | echo "${EPACE}${EPACE}Example: database.sh -D example.com -U USERNAME -P PASSWORD -DB DATABASENAME"
26 | echo "${EPACE}${EPACE}Will create Database/username/password by given"
27 | echow '-R, --delete -DB, --database [xxx] -U, --user [xxx]'
28 | echo "${EPACE}${EPACE}Example: database.sh -r -DB DATABASENAME -U USERNAME"
29 | echo "${EPACE}${EPACE}Will delete the database (require) and username (optional) by given"
30 | echow '-H, --help'
31 | echo "${EPACE}${EPACE}Display help and exit."
32 | exit 0
33 | }
34 |
35 | check_input(){
36 | if [ -z "${1}" ]; then
37 | help_message
38 | exit 1
39 | fi
40 | }
41 |
42 | specify_name(){
43 | check_input ${SQL_USER}
44 | check_input ${SQL_PASS}
45 | check_input ${SQL_DB}
46 | }
47 |
48 | auto_name(){
49 | SQL_DB="${TRANSNAME}"
50 | SQL_USER="${TRANSNAME}"
51 | SQL_PASS="'${RANDOM_PASS}'"
52 | }
53 |
54 | gen_pass(){
55 | RANDOM_PASS="$(openssl rand -base64 12)"
56 | }
57 |
58 | trans_name(){
59 | TRANSNAME=$(echo ${1} | tr -d '.&&-')
60 | }
61 |
62 | display_credential(){
63 | if [ ${SET_OK} = 0 ]; then
64 | echo "Database: ${SQL_DB}"
65 | echo "Username: ${SQL_USER}"
66 | echo "Password: $(echo ${SQL_PASS} | tr -d "'")"
67 | fi
68 | }
69 |
70 | store_credential(){
71 | if [ -d "./sites/${1}" ]; then
72 | if [ -f ./sites/${1}/.db_pass ]; then
73 | mv ./sites/${1}/.db_pass ./sites/${1}/.db_pass.bk
74 | fi
75 | cat > "./sites/${1}/.db_pass" << EOT
76 | "Database":"${SQL_DB}"
77 | "Username":"${SQL_USER}"
78 | "Password":"$(echo ${SQL_PASS} | tr -d "'")"
79 | EOT
80 | else
81 | echo "./sites/${1} not found, abort credential store!"
82 | fi
83 | }
84 |
85 | check_db_access(){
86 | docker compose exec -T mysql su -c "mariadb -uroot --password=${MYSQL_ROOT_PASSWORD} -e 'status'" >/dev/null 2>&1
87 | if [ ${?} != 0 ]; then
88 | echo '[X] DB access failed, please check!'
89 | exit 1
90 | fi
91 | }
92 |
93 | check_db_exist(){
94 | docker compose exec -T mysql su -c "test -e /var/lib/mysql/${1}"
95 | if [ ${?} = 0 ]; then
96 | echo "Database ${1} already exist, skip DB creation!"
97 | exit 0
98 | fi
99 | }
100 |
101 | check_db_not_exist(){
102 | docker compose exec -T mysql su -c "test -e /var/lib/mysql/${1}"
103 | if [ ${?} != 0 ]; then
104 | echo "Database ${1} doesn't exist, skip DB deletion!"
105 | exit 0
106 | fi
107 | }
108 |
109 | db_setup(){
110 | docker compose exec -T mysql su -c 'mariadb -uroot --password=${MYSQL_ROOT_PASSWORD} \
111 | -e "CREATE DATABASE '${SQL_DB}';" \
112 | -e "GRANT ALL PRIVILEGES ON '${SQL_DB}'.* TO '${SQL_USER}'@'${ANY}' IDENTIFIED BY '${SQL_PASS}';" \
113 | -e "FLUSH PRIVILEGES;"'
114 | SET_OK=${?}
115 | }
116 |
117 | db_delete(){
118 | if [ "${SQL_DB}" == '' ]; then
119 | echo "Database parameter is required!"
120 | exit 0
121 | fi
122 | if [ "${SQL_USER}" == '' ]; then
123 | SQL_USER="${SQL_DB}"
124 | fi
125 | check_db_not_exist ${SQL_DB}
126 | docker compose exec -T mysql su -c 'mariadb -uroot --password=${MYSQL_ROOT_PASSWORD} \
127 | -e "DROP DATABASE IF EXISTS '${SQL_DB}';" \
128 | -e "DROP USER IF EXISTS '${SQL_USER}'@'${ANY}';" \
129 | -e "FLUSH PRIVILEGES;"'
130 | echo "Database ${SQL_DB} and User ${SQL_USER} are deleted!"
131 | }
132 |
133 | auto_setup_main(){
134 | check_input ${DOMAIN}
135 | gen_pass
136 | trans_name ${DOMAIN}
137 | auto_name
138 | check_db_exist ${SQL_DB}
139 | check_db_access
140 | db_setup
141 | display_credential
142 | store_credential ${DOMAIN}
143 | }
144 |
145 | specify_setup_main(){
146 | specify_name
147 | check_db_exist ${SQL_DB}
148 | check_db_access
149 | db_setup
150 | display_credential
151 | store_credential ${DOMAIN}
152 | }
153 |
154 | main(){
155 | if [ ${METHOD} == 1 ]; then
156 | db_delete
157 | exit 0
158 | fi
159 | if [ "${SQL_USER}" != '' ] && [ "${SQL_PASS}" != '' ] && [ "${SQL_DB}" != '' ]; then
160 | specify_setup_main
161 | else
162 | auto_setup_main
163 | fi
164 | }
165 |
166 | check_input ${1}
167 | while [ ! -z "${1}" ]; do
168 | case ${1} in
169 | -[hH] | -help | --help)
170 | help_message
171 | ;;
172 | -[dD] | -domain| --domain) shift
173 | DOMAIN="${1}"
174 | ;;
175 | -[uU] | -user | --user) shift
176 | SQL_USER="${1}"
177 | ;;
178 | -[pP] | -password| --password) shift
179 | SQL_PASS="'${1}'"
180 | ;;
181 | -db | -DB | -database| --database) shift
182 | SQL_DB="${1}"
183 | ;;
184 | -[rR] | -del | --del | --delete)
185 | METHOD=1
186 | ;;
187 | *)
188 | help_message
189 | ;;
190 | esac
191 | shift
192 | done
193 | main
--------------------------------------------------------------------------------
/bin/container/owaspctl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | LSDIR='/usr/local/lsws'
3 | OWASP_DIR="${LSDIR}/conf/owasp"
4 | CRS_DIR='owasp-modsecurity-crs'
5 | RULE_FILE='modsec_includes.conf'
6 | LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
7 | OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
8 | EPACE=' '
9 | OWASP_V='4.3.0'
10 |
11 | echow(){
12 | FLAG=${1}
13 | shift
14 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
15 | }
16 |
17 | help_message(){
18 | echo -e "\033[1mOPTIONS\033[0m"
19 | echow '-E, --enable'
20 | echo "${EPACE}${EPACE}Will Enable mod_secure module with latest OWASP version of rules"
21 | echow '-D, --disable'
22 | echo "${EPACE}${EPACE}Will Disable mod_secure module with latest OWASP version of rules"
23 | echow '-H, --help'
24 | echo "${EPACE}${EPACE}Display help and exit."
25 | exit 0
26 | }
27 |
28 | check_lsv(){
29 | if [ -f ${LSDIR}/bin/openlitespeed ]; then
30 | LSV='openlitespeed'
31 | elif [ -f ${LSDIR}/bin/litespeed ]; then
32 | LSV='lsws'
33 | else
34 | echo 'Version not exist, abort!'
35 | exit 1
36 | fi
37 | }
38 |
39 | check_input(){
40 | if [ -z "${1}" ]; then
41 | help_message
42 | exit 1
43 | fi
44 | }
45 |
46 | mk_owasp_dir(){
47 | if [ -d ${OWASP_DIR} ] ; then
48 | rm -rf ${OWASP_DIR}
49 | fi
50 | mkdir -p ${OWASP_DIR}
51 | if [ ${?} -ne 0 ] ; then
52 | echo "Unable to create directory: ${OWASP_DIR}, exit!"
53 | exit 1
54 | fi
55 | }
56 |
57 | fst_match_line(){
58 | FIRST_LINE_NUM=$(grep -n -m 1 "${1}" ${2} | awk -F ':' '{print $1}')
59 | }
60 | fst_match_after(){
61 | FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
62 | }
63 | lst_match_line(){
64 | fst_match_after ${1} ${2} ${3}
65 | LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
66 | }
67 |
68 | enable_ols_modsec(){
69 | grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
70 | if [ ${?} -eq 0 ] ; then
71 | echo "Already configured for modsecurity."
72 | else
73 | echo 'Enable modsecurity'
74 | sed -i "s=module cache=module mod_security {\nmodsecurity on\
75 | \nmodsecurity_rules \`\nSecRuleEngine On\n\`\nmodsecurity_rules_file \
76 | ${OWASP_DIR}/${RULE_FILE}\n ls_enabled 1\n}\
77 | \n\nmodule cache=" ${OLS_HTTPD_CONF}
78 | fi
79 | }
80 |
81 | enable_ls_modsec(){
82 | grep '1' ${LS_HTTPD_CONF} >/dev/null 2>&1
83 | if [ ${?} -eq 0 ] ; then
84 | echo "LSWS already configured for modsecurity"
85 | else
86 | echo 'Enable modsecurity'
87 | sed -i \
88 | "s=0=1=" ${LS_HTTPD_CONF}
89 | sed -i \
90 | "s==\n\
91 | \n\
92 | ModSec\n\
93 | 1\n\
94 | include ${OWASP_DIR}/${RULE_FILE}\n\
95 | =" ${LS_HTTPD_CONF}
96 | fi
97 | }
98 |
99 | enable_modsec(){
100 | if [ "${LSV}" = 'lsws' ]; then
101 | enable_ls_modsec
102 | elif [ "${LSV}" = 'openlitespeed' ]; then
103 | enable_ols_modsec
104 | fi
105 | }
106 |
107 | disable_ols_modesec(){
108 | grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
109 | if [ ${?} -eq 0 ] ; then
110 | echo 'Disable modsecurity'
111 | fst_match_line 'module mod_security' ${OLS_HTTPD_CONF}
112 | lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
113 | sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
114 | else
115 | echo 'Already disabled for modsecurity'
116 | fi
117 | }
118 |
119 | disable_ls_modesec(){
120 | grep '0' ${LS_HTTPD_CONF}
121 | if [ ${?} -eq 0 ] ; then
122 | echo 'Already disabled for modsecurity'
123 | else
124 | echo 'Disable modsecurity'
125 | sed -i \
126 | "s=1=0=" ${LS_HTTPD_CONF}
127 | fst_match_line 'censorshipRuleSet' ${LS_HTTPD_CONF}
128 | lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} '/censorshipRuleSet'
129 | sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
130 | fi
131 | }
132 |
133 | disable_modsec(){
134 | check_lsv
135 | if [ "${LSV}" = 'lsws' ]; then
136 | disable_ls_modesec
137 | elif [ "${LSV}" = 'openlitespeed' ]; then
138 | disable_ols_modesec
139 | fi
140 | }
141 |
142 | install_unzip(){
143 | if [ ! -f /usr/bin/unzip ]; then
144 | echo 'Install Unzip'
145 | apt update >/dev/null 2>&1
146 | apt-get install unzip -y >/dev/null 2>&1
147 | fi
148 | }
149 |
150 | backup_owasp(){
151 | if [ -d ${OWASP_DIR} ]; then
152 | echo "Detect ${OWASP_DIR} folder exist, move to ${OWASP_DIR}.$(date +%F).bk"
153 | if [ -d ${OWASP_DIR}.$(date +%F).bk ]; then
154 | rm -rf ${OWASP_DIR}.$(date +%F).bk
155 | fi
156 | mv ${OWASP_DIR} ${OWASP_DIR}.$(date +%F).bk
157 | fi
158 | }
159 |
160 | install_owasp(){
161 | cd ${OWASP_DIR}
162 | echo 'Download OWASP rules'
163 | wget -q https://github.com/coreruleset/coreruleset/archive/refs/tags/v${OWASP_V}.zip
164 | unzip -qq v${OWASP_V}.zip
165 | rm -f v${OWASP_V}.zip
166 | mv coreruleset-* ${CRS_DIR}
167 | }
168 |
169 | configure_owasp(){
170 | echo 'Config OWASP rules.'
171 | cd ${OWASP_DIR}
172 | if [ -f ${CRS_DIR}/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example ]; then
173 | mv ${CRS_DIR}/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example ${CRS_DIR}/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
174 | fi
175 | if [ -f ${CRS_DIR}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example ]; then
176 | mv ${CRS_DIR}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example ${CRS_DIR}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
177 | fi
178 | if [ -f ${RULE_FILE} ]; then
179 | mv ${RULE_FILE} ${RULE_FILE}.bk
180 | fi
181 | echo 'include modsecurity.conf' >> ${RULE_FILE}
182 | if [ -f ${CRS_DIR}/crs-setup.conf.example ]; then
183 | mv ${CRS_DIR}/crs-setup.conf.example ${CRS_DIR}/crs-setup.conf
184 | echo "include ${CRS_DIR}/crs-setup.conf" >> ${RULE_FILE}
185 | fi
186 | ALL_RULES="$(ls ${CRS_DIR}/rules/ | grep 'REQUEST-\|RESPONSE-')"
187 | echo "${ALL_RULES}" | while read LINE; do echo "include ${CRS_DIR}/rules/${LINE}" >> ${RULE_FILE}; done
188 | echo 'SecRuleEngine On' > modsecurity.conf
189 | chown -R lsadm ${OWASP_DIR}
190 | }
191 |
192 | main_owasp(){
193 | backup_owasp
194 | mk_owasp_dir
195 | install_unzip
196 | install_owasp
197 | configure_owasp
198 | check_lsv
199 | enable_modsec
200 | }
201 |
202 | check_input ${1}
203 | while [ ! -z "${1}" ]; do
204 | case ${1} in
205 | -[hH] | -help | --help)
206 | help_message
207 | ;;
208 | -[eE] | -enable | --enable)
209 | main_owasp
210 | ;;
211 | -[dD] | -disable | --disable)
212 | disable_modsec
213 | ;;
214 | *)
215 | help_message
216 | ;;
217 | esac
218 | shift
219 | done
220 |
--------------------------------------------------------------------------------
/bin/container/appinstallctl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | DEFAULT_VH_ROOT='/var/www/vhosts'
3 | VH_DOC_ROOT=''
4 | VHNAME=''
5 | APP_NAME=''
6 | DOMAIN=''
7 | WWW_UID=''
8 | WWW_GID=''
9 | WPCONSTCONF=''
10 | PUB_IP=$(curl -s http://checkip.amazonaws.com)
11 | DB_HOST='mysql'
12 | PLUGINLIST="litespeed-cache.zip"
13 | THEME='twentytwenty'
14 | EPACE=' '
15 |
16 | echow(){
17 | FLAG=${1}
18 | shift
19 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
20 | }
21 |
22 | help_message(){
23 | echo -e "\033[1mOPTIONS\033[0m"
24 | echow '-A, -app [wordpress] -D, --domain [DOMAIN_NAME]'
25 | echo "${EPACE}${EPACE}Example: appinstallctl.sh --app wordpress --domain example.com"
26 | echow '-H, --help'
27 | echo "${EPACE}${EPACE}Display help and exit."
28 | exit 0
29 | }
30 |
31 | check_input(){
32 | if [ -z "${1}" ]; then
33 | help_message
34 | exit 1
35 | fi
36 | }
37 |
38 | linechange(){
39 | LINENUM=$(grep -n "${1}" ${2} | cut -d: -f 1)
40 | if [ -n "${LINENUM}" ] && [ "${LINENUM}" -eq "${LINENUM}" ] 2>/dev/null; then
41 | sed -i "${LINENUM}d" ${2}
42 | sed -i "${LINENUM}i${3}" ${2}
43 | fi
44 | }
45 |
46 | ck_ed(){
47 | if [ ! -f /bin/ed ]; then
48 | echo "Install ed package.."
49 | apt-get install ed -y > /dev/null 2>&1
50 | fi
51 | }
52 |
53 | ck_unzip(){
54 | if [ ! -f /usr/bin/unzip ]; then
55 | echo "Install unzip package.."
56 | apt-get install unzip -y > /dev/null 2>&1
57 | fi
58 | }
59 |
60 | get_owner(){
61 | WWW_UID=$(stat -c "%u" ${DEFAULT_VH_ROOT})
62 | WWW_GID=$(stat -c "%g" ${DEFAULT_VH_ROOT})
63 | if [ ${WWW_UID} -eq 0 ] || [ ${WWW_GID} -eq 0 ]; then
64 | WWW_UID=1000
65 | WWW_GID=1000
66 | echo "Set owner to ${WWW_UID}"
67 | fi
68 | }
69 |
70 | get_db_pass(){
71 | if [ -f ${DEFAULT_VH_ROOT}/${1}/.db_pass ]; then
72 | SQL_DB=$(grep -i Database ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
73 | SQL_USER=$(grep -i Username ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
74 | SQL_PASS=$(grep -i Password ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
75 | else
76 | echo 'db pass file can not locate, skip wp-config pre-config.'
77 | fi
78 | }
79 |
80 | set_vh_docroot(){
81 | if [ "${VHNAME}" != '' ]; then
82 | VH_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}"
83 | VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}/html"
84 | WPCONSTCONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.json"
85 | elif [ -d ${DEFAULT_VH_ROOT}/${1}/html ]; then
86 | VH_ROOT="${DEFAULT_VH_ROOT}/${1}"
87 | VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${1}/html"
88 | WPCONSTCONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.json"
89 | else
90 | echo "${DEFAULT_VH_ROOT}/${1}/html does not exist, please add domain first! Abort!"
91 | exit 1
92 | fi
93 | }
94 |
95 | check_sql_native(){
96 | local COUNTER=0
97 | local LIMIT_NUM=100
98 | until [ "$(curl -v mysql:3306 2>&1 | grep -i 'native\|Connected')" ]; do
99 | echo "Counter: ${COUNTER}/${LIMIT_NUM}"
100 | COUNTER=$((COUNTER+1))
101 | if [ ${COUNTER} = 10 ]; then
102 | echo '--- MySQL is starting, please wait... ---'
103 | elif [ ${COUNTER} = ${LIMIT_NUM} ]; then
104 | echo '--- MySQL is timeout, exit! ---'
105 | exit 1
106 | fi
107 | sleep 1
108 | done
109 | }
110 |
111 | install_wp_plugin(){
112 | for PLUGIN in ${PLUGINLIST}; do
113 | wget -q -P ${VH_DOC_ROOT}/wp-content/plugins/ https://downloads.wordpress.org/plugin/${PLUGIN}
114 | if [ ${?} = 0 ]; then
115 | ck_unzip
116 | unzip -qq -o ${VH_DOC_ROOT}/wp-content/plugins/${PLUGIN} -d ${VH_DOC_ROOT}/wp-content/plugins/
117 | else
118 | echo "${PLUGINLIST} FAILED to download"
119 | fi
120 | done
121 | rm -f ${VH_DOC_ROOT}/wp-content/plugins/*.zip
122 | }
123 |
124 | set_htaccess(){
125 | if [ ! -f ${VH_DOC_ROOT}/.htaccess ]; then
126 | touch ${VH_DOC_ROOT}/.htaccess
127 | fi
128 | cat << EOM > ${VH_DOC_ROOT}/.htaccess
129 | # BEGIN WordPress
130 |
131 | RewriteEngine On
132 | RewriteBase /
133 | RewriteRule ^index\.php$ - [L]
134 | RewriteCond %{REQUEST_FILENAME} !-f
135 | RewriteCond %{REQUEST_FILENAME} !-d
136 | RewriteRule . /index.php [L]
137 |
138 | # END WordPress
139 | EOM
140 | }
141 |
142 | get_theme_name(){
143 | THEME_NAME=$(grep WP_DEFAULT_THEME ${VH_DOC_ROOT}/wp-includes/default-constants.php | grep -v '!' | awk -F "'" '{print $4}')
144 | echo "${THEME_NAME}" | grep 'twenty' >/dev/null 2>&1
145 | if [ ${?} = 0 ]; then
146 | THEME="${THEME_NAME}"
147 | fi
148 | }
149 |
150 | set_lscache(){
151 | wget -q -O ${WPCONSTCONF} https://raw.githubusercontent.com/litespeedtech/lscache_wp/refs/heads/master/data/const.default.json
152 | if [ -f ${WPCONSTCONF} ]; then
153 | sed -ie 's/"object": .*"/"object": '\"true\"'/g' ${WPCONSTCONF}
154 | sed -ie 's/"object-kind": .*"/"object-kind": '\"true\"'/g' ${WPCONSTCONF}
155 | sed -ie 's/"object-host": .*"/"object-host": '\"redis\"'/g' ${WPCONSTCONF}
156 | sed -ie 's/"object-port": .*"/"object-port": '\"6379\"'/g' ${WPCONSTCONF}
157 | fi
158 | THEME_PATH="${VH_DOC_ROOT}/wp-content/themes/${THEME}"
159 | if [ ! -f ${THEME_PATH}/functions.php ]; then
160 | cat >> "${THEME_PATH}/functions.php" <>/dev/null 2>&1
173 | 2i
174 | require_once( WP_CONTENT_DIR.'/../wp-admin/includes/plugin.php' );
175 | \$path = 'litespeed-cache/litespeed-cache.php' ;
176 | if (!is_plugin_active( \$path )) {
177 | activate_plugin( \$path ) ;
178 | rename( __FILE__ . '.bk', __FILE__ );
179 | }
180 | .
181 | w
182 | q
183 | END
184 | fi
185 | }
186 |
187 | preinstall_wordpress(){
188 | if [ "${VHNAME}" != '' ]; then
189 | get_db_pass ${VHNAME}
190 | else
191 | get_db_pass ${DOMAIN}
192 | fi
193 | if [ ! -f ${VH_DOC_ROOT}/wp-config.php ] && [ -f ${VH_DOC_ROOT}/wp-config-sample.php ]; then
194 | cp ${VH_DOC_ROOT}/wp-config-sample.php ${VH_DOC_ROOT}/wp-config.php
195 | NEWDBPWD="define('DB_PASSWORD', '${SQL_PASS}');"
196 | linechange 'DB_PASSWORD' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
197 | NEWDBPWD="define('DB_USER', '${SQL_USER}');"
198 | linechange 'DB_USER' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
199 | NEWDBPWD="define('DB_NAME', '${SQL_DB}');"
200 | linechange 'DB_NAME' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
201 | #NEWDBPWD="define('DB_HOST', '${PUB_IP}');"
202 | NEWDBPWD="define('DB_HOST', '${DB_HOST}');"
203 | linechange 'DB_HOST' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
204 | elif [ -f ${VH_DOC_ROOT}/wp-config.php ]; then
205 | echo "${VH_DOC_ROOT}/wp-config.php already exist, exit !"
206 | exit 1
207 | else
208 | echo 'Skip!'
209 | exit 2
210 | fi
211 | }
212 |
213 | app_wordpress_dl(){
214 | if [ ! -f "${VH_DOC_ROOT}/wp-config.php" ] && [ ! -f "${VH_DOC_ROOT}/wp-config-sample.php" ]; then
215 | wp core download \
216 | --allow-root \
217 | --quiet
218 | else
219 | echo 'wordpress already exist, abort!'
220 | exit 1
221 | fi
222 | }
223 |
224 | change_owner(){
225 | if [ "${VHNAME}" != '' ]; then
226 | chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${VHNAME}
227 | else
228 | chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${DOMAIN}
229 | fi
230 | }
231 |
232 | main(){
233 | set_vh_docroot ${DOMAIN}
234 | get_owner
235 | cd ${VH_DOC_ROOT}
236 | if [ "${APP_NAME}" = 'wordpress' ] || [ "${APP_NAME}" = 'wp' ]; then
237 | check_sql_native
238 | app_wordpress_dl
239 | preinstall_wordpress
240 | install_wp_plugin
241 | set_htaccess
242 | get_theme_name
243 | set_lscache
244 | change_owner
245 | exit 0
246 | else
247 | echo "APP: ${APP_NAME} not support, exit!"
248 | exit 1
249 | fi
250 | }
251 |
252 | check_input ${1}
253 | while [ ! -z "${1}" ]; do
254 | case ${1} in
255 | -[hH] | -help | --help)
256 | help_message
257 | ;;
258 | -[aA] | -app | --app) shift
259 | check_input "${1}"
260 | APP_NAME="${1}"
261 | ;;
262 | -[dD] | -domain | --domain) shift
263 | check_input "${1}"
264 | DOMAIN="${1}"
265 | ;;
266 | -vhname | --vhname) shift
267 | VHNAME="${1}"
268 | ;;
269 | *)
270 | help_message
271 | ;;
272 | esac
273 | shift
274 | done
275 | main
276 |
--------------------------------------------------------------------------------
/bin/acme.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | EMAIL=''
3 | NO_EMAIL=''
4 | DOMAIN=''
5 | INSTALL=''
6 | UNINSTALL=''
7 | TYPE=0
8 | CONT_NAME='litespeed'
9 | ACME_SRC='https://raw.githubusercontent.com/Neilpang/acme.sh/master/acme.sh'
10 | EPACE=' '
11 | RENEW=''
12 | RENEW_ALL=''
13 | FORCE=''
14 | REVOKE=''
15 | REMOVE=''
16 |
17 | echow(){
18 | FLAG=${1}
19 | shift
20 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
21 | }
22 |
23 | help_message(){
24 | case ${1} in
25 | "1")
26 | echo 'You will need to install acme script at the first time.'
27 | echo 'Please run acme.sh --install --email example@example.com'
28 | ;;
29 | "2")
30 | echo -e "\033[1mOPTIONS\033[0m"
31 | echow '-D, --domain [DOMAIN_NAME]'
32 | echo "${EPACE}${EPACE}Example: acme.sh --domain example.com"
33 | echo "${EPACE}${EPACE}will auto detect and apply for both example.com and www.example.com domains."
34 | echow '-H, --help'
35 | echo "${EPACE}${EPACE}Display help and exit."
36 | echo -e "\033[1m Only for the First time\033[0m"
37 | echow '--install --email [EMAIL_ADDR]'
38 | echo "${EPACE}${EPACE}Will install ACME with the Email provided"
39 | echow '-r, --renew'
40 | echo "${EPACE}${EPACE}Renew a specific domain with -D or --domain parameter if posibile. To force renew, use -f parameter."
41 | echow '-R, --renew-all'
42 | echo "${EPACE}${EPACE}Renew all domains if possible. To force renew, use -f parameter."
43 | echow '-f, -F, --force'
44 | echo "${EPACE}${EPACE}Force renew for a specific domain or all domains."
45 | echow '-v, --revoke'
46 | echo "${EPACE}${EPACE}Revoke a domain."
47 | echow '-V, --remove'
48 | echo "${EPACE}${EPACE}Remove a domain."
49 | exit 0
50 | ;;
51 | "3")
52 | echo 'Please run acme.sh --domain [DOMAIN_NAME] to apply certificate'
53 | exit 0
54 | ;;
55 | esac
56 | }
57 |
58 | check_input(){
59 | if [ -z "${1}" ]; then
60 | help_message 2
61 | fi
62 | }
63 |
64 | domain_filter(){
65 | if [ -z "${1}" ]; then
66 | help_message 3
67 | fi
68 | DOMAIN="${1}"
69 | DOMAIN="${DOMAIN#http://}"
70 | DOMAIN="${DOMAIN#https://}"
71 | DOMAIN="${DOMAIN#ftp://}"
72 | DOMAIN="${DOMAIN#scp://}"
73 | DOMAIN="${DOMAIN#scp://}"
74 | DOMAIN="${DOMAIN#sftp://}"
75 | DOMAIN=${DOMAIN%%/*}
76 | }
77 |
78 | email_filter(){
79 | local EMAIL_CLEAN="${1%\"}"
80 | EMAIL_CLEAN="${EMAIL_CLEAN#\"}"
81 |
82 | CKREG="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"
83 |
84 | if [[ "${EMAIL_CLEAN}" =~ ${CKREG} ]]; then
85 | echo -e "[O] The E-mail \033[32m${EMAIL_CLEAN}\033[0m is valid."
86 | else
87 | echo -e "[X] The E-mail \e[31m${EMAIL_CLEAN}\e[39m is invalid"
88 | exit 1
89 | fi
90 | }
91 |
92 | cert_hook(){
93 | echo '[Start] Adding ACME hook'
94 | docker compose exec ${CONT_NAME} su -s /bin/bash -c "certhookctl.sh"
95 | echo '[End] Adding ACME hook'
96 | }
97 |
98 | www_domain(){
99 | CHECK_WWW=$(echo ${1} | cut -c1-4)
100 | if [[ ${CHECK_WWW} == www. ]] ; then
101 | DOMAIN=$(echo ${1} | cut -c 5-)
102 | else
103 | DOMAIN=${1}
104 | fi
105 | WWW_DOMAIN="www.${DOMAIN}"
106 | }
107 |
108 | domain_verify(){
109 | curl -Is http://${DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
110 | if [ ${?} = 0 ]; then
111 | echo -e "[O] The domain name \033[32m${DOMAIN}\033[0m is accessible."
112 | TYPE=1
113 | curl -Is http://${WWW_DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
114 | if [ ${?} = 0 ]; then
115 | echo -e "[O] The domain name \033[32m${WWW_DOMAIN}\033[0m is accessible."
116 | TYPE=2
117 | else
118 | echo -e "[!] The domain name ${WWW_DOMAIN} is inaccessible."
119 | fi
120 | else
121 | echo -e "[X] The domain name \e[31m${DOMAIN}\e[39m is inaccessible, please verify."
122 | exit 1
123 | fi
124 | }
125 |
126 | install_acme(){
127 | echo '[Start] Install ACME'
128 | if [ "${1}" = 'true' ]; then
129 | docker compose exec litespeed su -c "
130 | cd &&
131 | wget ${ACME_SRC} &&
132 | chmod 755 acme.sh &&
133 | ./acme.sh --install --cert-home ~/.acme.sh/certs &&
134 | /root/.acme.sh/acme.sh --set-default-ca --server letsencrypt &&
135 | rm ~/acme.sh
136 | "
137 | elif [ "${2}" != '' ]; then
138 | email_filter \"${2}\"
139 | docker compose exec litespeed su -c "
140 | cd &&
141 | wget ${ACME_SRC} &&
142 | chmod 755 acme.sh &&
143 | ./acme.sh --install --cert-home ~/.acme.sh/certs --accountemail ${2} &&
144 | /root/.acme.sh/acme.sh --set-default-ca --server letsencrypt &&
145 | rm ~/acme.sh
146 | "
147 | else
148 | help_message 1
149 | exit 1
150 | fi
151 | echo '[End] Install ACME'
152 | }
153 |
154 | uninstall_acme(){
155 | echo '[Start] Uninstall ACME'
156 | docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --uninstall"
157 | echo '[End] Uninstall ACME'
158 | exit 0
159 | }
160 |
161 | check_acme(){
162 | echo '[Start] Checking ACME'
163 | docker compose exec ${CONT_NAME} su -c "test -f /root/.acme.sh/acme.sh"
164 | if [ ${?} != 0 ]; then
165 | install_acme "${NO_EMAIL}" "${EMAIL}"
166 | cert_hook
167 | help_message 3
168 | fi
169 | echo '[End] Checking ACME'
170 | }
171 |
172 | lsws_restart(){
173 | docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
174 | }
175 |
176 | doc_root_verify(){
177 | if [ "${DOC_ROOT}" = '' ]; then
178 | DOC_PATH="/var/www/vhosts/${1}/html"
179 | else
180 | DOC_PATH="${DOC_ROOT}"
181 | fi
182 | docker compose exec ${CONT_NAME} su -c "[ -e ${DOC_PATH} ]"
183 | if [ ${?} -eq 0 ]; then
184 | echo -e "[O] The document root folder \033[32m${DOC_PATH}\033[0m does exist."
185 | else
186 | echo -e "[X] The document root folder \e[31m${DOC_PATH}\e[39m does not exist!"
187 | exit 1
188 | fi
189 | }
190 |
191 | install_cert(){
192 | echo '[Start] Apply Lets Encrypt Certificate'
193 | if [ ${TYPE} = 1 ]; then
194 | docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -w ${DOC_PATH}"
195 | elif [ ${TYPE} = 2 ]; then
196 | docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -d www.${1} -w ${DOC_PATH}"
197 | else
198 | echo 'unknown Type!'
199 | exit 2
200 | fi
201 | echo '[End] Apply Lets Encrypt Certificate'
202 | }
203 |
204 | renew_acme(){
205 | echo '[Start] Renew ACME'
206 | if [ "${FORCE}" = 'true' ]; then
207 | docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1} --force"
208 | else
209 | docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1}"
210 | fi
211 | echo '[End] Renew ACME'
212 | lsws_restart
213 | }
214 |
215 | renew_all_acme(){
216 | echo '[Start] Renew all ACME'
217 | if [ "${FORCE}" = 'true' ]; then
218 | docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all --force"
219 | else
220 | docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all"
221 | fi
222 | echo '[End] Renew all ACME'
223 | lsws_restart
224 | }
225 |
226 | revoke(){
227 | echo '[Start] Revoke a domain'
228 | docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --revoke --domain ${1}"
229 | echo '[End] Revoke a domain'
230 | lsws_restart
231 | }
232 |
233 | remove(){
234 | echo '[Start] Remove a domain'
235 | docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --remove --domain ${1}"
236 | echo '[End] Remove a domain'
237 | lsws_restart
238 | }
239 |
240 | main(){
241 | if [ "${RENEW_ALL}" = 'true' ]; then
242 | renew_all_acme
243 | exit 0
244 | elif [ "${RENEW}" = 'true' ]; then
245 | renew_acme ${DOMAIN}
246 | exit 0
247 | elif [ "${REVOKE}" = 'true' ]; then
248 | revoke ${DOMAIN}
249 | exit 0
250 | elif [ "${REMOVE}" = 'true' ]; then
251 | remove ${DOMAIN}
252 | exit 0
253 | fi
254 |
255 | check_acme
256 | domain_filter ${DOMAIN}
257 | www_domain ${DOMAIN}
258 | domain_verify
259 | doc_root_verify ${DOMAIN}
260 | install_cert ${DOMAIN}
261 | lsws_restart
262 | }
263 |
264 | check_input ${1}
265 | while [ ! -z "${1}" ]; do
266 | case ${1} in
267 | -[hH] | -help | --help)
268 | help_message 2
269 | ;;
270 | -[dD] | -domain | --domain) shift
271 | check_input "${1}"
272 | DOMAIN="${1}"
273 | ;;
274 | -[iI] | --install )
275 | INSTALL=true
276 | ;;
277 | -[uU] | --uninstall )
278 | UNINSTALL=true
279 | uninstall_acme
280 | ;;
281 | -[fF] | --force )
282 | FORCE=true
283 | ;;
284 | -[r] | --renew )
285 | RENEW=true
286 | ;;
287 | -[R] | --renew-all )
288 | RENEW_ALL=true
289 | ;;
290 | -[v] | --revoke )
291 | REVOKE=true
292 | ;;
293 | -[V] | --remove )
294 | REMOVE=true
295 | ;;
296 | -[eE] | --email ) shift
297 | check_input "${1}"
298 | EMAIL="${1}"
299 | ;;
300 | *)
301 | help_message 2
302 | ;;
303 | esac
304 | shift
305 | done
306 |
307 | main
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenLiteSpeed WordPress Docker Container
2 |
3 | 
4 |
5 | [](https://github.com/litespeedtech/ols-docker-env/actions/)
6 | [](https://hub.docker.com/r/litespeedtech/openlitespeed)
7 | [](https://litespeedtech.com/slack)
8 | [](https://twitter.com/litespeedtech)
9 |
10 | Install a lightweight WordPress container with OpenLiteSpeed Edge or Stable version based on Ubuntu 24.04 Linux.
11 |
12 | ## Prerequisites
13 |
14 | 1. [Install Docker](https://www.docker.com/)
15 | 2. [Install Docker Compose](https://docs.docker.com/compose/)
16 |
17 | ## Configuration
18 |
19 | Edit the `.env` file to update the demo site domain, default MySQL user, and password.
20 | Feel free to check [Docker hub Tag page](https://hub.docker.com/repository/docker/litespeedtech/openlitespeed/tags) if you want to update default openlitespeed and php versions.
21 |
22 | ## Installation
23 |
24 | Clone this repository or copy the files from this repository into a new folder:
25 |
26 | ```bash
27 | git clone https://github.com/litespeedtech/ols-docker-env.git
28 | ```
29 |
30 | Open a terminal, `cd` to the folder in which `docker compose.yml` is saved, and run:
31 |
32 | ```bash
33 | docker compose up
34 | ```
35 |
36 | Note: If you wish to run a single web server container, please see the [usage method here](https://github.com/litespeedtech/ols-dockerfiles#usage).
37 |
38 | ## Components
39 |
40 | The docker image installs the following packages on your system:
41 |
42 | |Component|Version|
43 | | :-------------: | :-------------: |
44 | |Linux|Ubuntu 24.04|
45 | |OpenLiteSpeed|[Latest version](https://hub.docker.com/r/litespeedtech/openlitespeed)|
46 | |MariaDB|[Stable version: 11.4](https://hub.docker.com/_/mariadb)|
47 | |PHP|[Latest version](http://rpms.litespeedtech.com/debian/)|
48 | |LiteSpeed Cache|[Latest from WordPress.org](https://wordpress.org/plugins/litespeed-cache/)|
49 | |ACME|[Latest from ACME official](https://github.com/acmesh-official/get.acme.sh)|
50 | |WordPress|[Latest from WordPress](https://wordpress.org/download/)|
51 | |phpMyAdmin|[Latest from dockerhub](https://hub.docker.com/r/phpmyadmin/phpmyadmin/)|
52 | |Redis|[Latest from dockerhub](https://hub.docker.com/_/redis/)|
53 |
54 | ## Data Structure
55 |
56 | Cloned project
57 |
58 | ```bash
59 | ├── acme
60 | ├── bin
61 | │ └── container
62 | ├── data
63 | │ └── db
64 | ├── logs
65 | │ ├── access.log
66 | │ ├── error.log
67 | │ ├── lsrestart.log
68 | │ └── stderr.log
69 | ├── lsws
70 | │ ├── admin-conf
71 | │ └── conf
72 | ├── sites
73 | │ └── localhost
74 | ├── LICENSE
75 | ├── README.md
76 | └── docker-compose.yml
77 | ```
78 |
79 | * `acme` contains all applied certificates from Lets Encrypt
80 |
81 | * `bin` contains multiple CLI scripts to allow you add or delete virtual hosts, install applications, upgrade, etc
82 |
83 | * `data` stores the MySQL database
84 |
85 | * `logs` contains all of the web server logs and virtual host access logs
86 |
87 | * `lsws` contains all web server configuration files
88 |
89 | * `sites` contains the document roots (the WordPress application will install here)
90 |
91 | ## Usage
92 |
93 | ### Starting a Container
94 |
95 | Start the container with the `up` or `start` methods:
96 |
97 | ```bash
98 | docker compose up
99 | ```
100 |
101 | You can run with daemon mode, like so:
102 |
103 | ```bash
104 | docker compose up -d
105 | ```
106 |
107 | The container is now built and running.
108 |
109 | ### Stopping a Container
110 |
111 | ```bash
112 | docker compose stop
113 | ```
114 |
115 | ### Removing Containers
116 |
117 | To stop and remove all containers, use the `down` command:
118 |
119 | ```bash
120 | docker compose down
121 | ```
122 |
123 | ### Setting the WebAdmin Password
124 |
125 | We strongly recommend you set your personal password right away.
126 |
127 | ```bash
128 | bash bin/webadmin.sh my_password
129 | ```
130 |
131 | ### Starting a Demo Site
132 |
133 | After running the following command, you should be able to access the WordPress installation with the configured domain. By default the domain is .
134 |
135 | ```bash
136 | bash bin/demosite.sh
137 | ```
138 |
139 | ### Creating a Domain and Virtual Host
140 |
141 | ```bash
142 | bash bin/domain.sh [-A, --add] example.com
143 | ```
144 |
145 | > Please ignore SSL certificate warnings from the server. They happen if you haven't applied the certificate.
146 | >
147 | ### Deleting a Domain and Virtual Host
148 |
149 | ```bash
150 | bash bin/domain.sh [-D, --del] example.com
151 | ```
152 |
153 | ### Creating a Database
154 |
155 | You can either automatically generate the user, password, and database names, or specify them. Use the following to auto generate:
156 |
157 | ```bash
158 | bash bin/database.sh [-D, --domain] example.com
159 | ```
160 |
161 | Use this command to specify your own names, substituting `user_name`, `my_password`, and `database_name` with your preferred values:
162 |
163 | ```bash
164 | bash bin/database.sh [-D, --domain] example.com [-U, --user] USER_NAME [-P, --password] MY_PASS [-DB, --database] DATABASE_NAME
165 | ```
166 |
167 | ### Installing a WordPress Site
168 |
169 | To preconfigure the `wp-config` file, run the `database.sh` script for your domain, before you use the following command to install WordPress:
170 |
171 | ```bash
172 | bash bin/appinstall.sh [-A, --app] wordpress [-D, --domain] example.com
173 | ```
174 |
175 | ### Connecting to Redis
176 |
177 | Go to [WordPress > LSCache Plugin > Cache > Object](https://docs.litespeedtech.com/lscache/lscwp/cache/#object-tab), select **Redis** method and input `redis` to the Host field.
178 |
179 | ### Install ACME
180 |
181 | We need to run the ACME installation command the **first time only**.
182 | With email notification:
183 |
184 | ```bash
185 | bash bin/acme.sh [-I, --install] [-E, --email] EMAIL_ADDR
186 | ```
187 |
188 | ### Applying a Let's Encrypt Certificate
189 |
190 | Use the root domain in this command, and it will check for a certificate and automatically apply one with and without `www`:
191 |
192 | ```bash
193 | bash bin/acme.sh [-D, --domain] example.com
194 | ```
195 |
196 | Other parameters:
197 |
198 | * [`-r`, `--renew`]: Renew a specific domain with -D or --domain parameter if posibile. To force renew, use -f parameter.
199 |
200 | * [`-R`, `--renew-all`]: Renew all domains if possible. To force renew, use -f parameter.
201 |
202 | * [`-f`, `-F`, `--force`]: Force renew for a specific domain or all domains.
203 |
204 | * [`-v`, `--revoke`]: Revoke a domain.
205 |
206 | * [`-V`, `--remove`]: Remove a domain.
207 |
208 | ### Using mkcert for Local Development SSL
209 |
210 | For local development domains (`.test`, `.local`, `.dev`, etc.), you can use `mkcert` to generate trusted SSL certificates without warnings.
211 |
212 | #### Installing mkcert
213 |
214 | First-time installation (Windows with Chocolatey):
215 |
216 | ```bash
217 | bash bin/mkcert.sh --install
218 | ```
219 |
220 | This will:
221 |
222 | * Install `mkcert` via Chocolatey
223 | * Create and install a local Certificate Authority (CA) in your system trust store
224 |
225 | #### Generating Local SSL Certificate
226 |
227 | After adding a domain to your environment, generate an SSL certificate:
228 |
229 | ```bash
230 | bash bin/mkcert.sh [-D, --domain] example.test
231 | ```
232 |
233 | This will:
234 |
235 | 1. Check if the domain exists in your configuration
236 | 2. Generate certificates for `example.test` and `www.example.test`
237 | 3. Create a `dockerLocal` template with SSL configuration
238 | 4. Copy certificates to the container
239 | 5. Move the domain from the standard template to the SSL-enabled template
240 | 6. Restart OpenLiteSpeed
241 |
242 | Your domain will now be accessible via HTTPS with a trusted certificate at `https://example.test`
243 |
244 | #### Removing Local SSL Certificate
245 |
246 | To remove the SSL certificate and revert to HTTP:
247 |
248 | ```bash
249 | bash bin/mkcert.sh [-R, --remove] [-D, --domain] example.test
250 | ```
251 |
252 | This will:
253 |
254 | 1. Remove the domain from the `dockerLocal` template
255 | 2. Move it back to the standard `docker` template
256 | 3. Delete certificate files from both host and container
257 | 4. Clean up empty templates if no other domains use SSL
258 | 5. Restart OpenLiteSpeed
259 |
260 | > **Important**: You must add the domain to your environment first using `bash bin/domain.sh --add example.test` before generating certificates.
261 |
262 | ### Update Web Server
263 |
264 | To upgrade the web server to latest stable version, run the following:
265 |
266 | ```bash
267 | bash bin/webadmin.sh [-U, --upgrade]
268 | ```
269 |
270 | ### Apply OWASP ModSecurity
271 |
272 | Enable OWASP `mod_secure` on the web server:
273 |
274 | ```bash
275 | bash bin/webadmin.sh [-M, --mod-secure] enable
276 | ```
277 |
278 | Disable OWASP `mod_secure` on the web server:
279 |
280 | ```bash
281 | bash bin/webadmin.sh [-M, --mod-secure] disable
282 | ```
283 |
284 | >Please ignore ModSecurity warnings from the server. They happen if some of the rules are not supported by the server.
285 | >
286 | ### Accessing the Database
287 |
288 | After installation, you can use phpMyAdmin to access the database by visiting `http://127.0.0.1:8080` or `https://127.0.0.1:8443`. The default username is `root`, and the password is the same as the one you supplied in the `.env` file.
289 |
290 | ## Customization
291 |
292 | If you want to customize the image by adding some packages, e.g. `lsphp83-pspell`, just extend it with a Dockerfile.
293 |
294 | 1. We can create a `custom` folder and a `custom/Dockerfile` file under the main project.
295 | 2. Add the following example code to `Dockerfile` under the custom folder
296 |
297 | ```bash
298 | FROM litespeedtech/openlitespeed:latest
299 | RUN apt-get update && apt-get install lsphp83-pspell -y
300 | ```
301 |
302 | 3. Add `build: ./custom` line under the "image: litespeedtech" of docker-composefile. So it will looks like this
303 |
304 | ```bash
305 | litespeed:
306 | image: litespeedtech/openlitespeed:${OLS_VERSION}-${PHP_VERSION}
307 | build: ./custom
308 | ```
309 |
310 | 4. Build and start it with command:
311 |
312 | ```bash
313 | docker compose up --build
314 | ```
315 |
316 | ## Support & Feedback
317 |
318 | If you still have a question after using OpenLiteSpeed Docker, you have a few options.
319 |
320 | * Join [the GoLiteSpeed Slack community](https://litespeedtech.com/slack) for real-time discussion
321 | * Post to [the OpenLiteSpeed Forums](https://forum.openlitespeed.org/) for community support
322 | * Reporting any issue on [Github ols-docker-env](https://github.com/litespeedtech/ols-docker-env/issues) project
323 |
324 | **_Pull requests are always welcome!_**
325 |
--------------------------------------------------------------------------------
/bin/mkcert.sh:
--------------------------------------------------------------------------------
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. Must be used with --domain."
27 | echo "${EPACE}${EPACE}Example: mkcert.sh --remove --domain example.test"
28 | echow '-H, --help'
29 | echo "${EPACE}${EPACE}Display help and exit"
30 | exit 0
31 | }
32 |
33 | check_input(){
34 | if [ -z "${1}" ]; then
35 | help_message
36 | fi
37 | }
38 |
39 | domain_filter(){
40 | if [ -z "${1}" ]; then
41 | echo "[X] Domain name is required!"
42 | exit 1
43 | fi
44 | DOMAIN="${1}"
45 | DOMAIN="${DOMAIN#http://}"
46 | DOMAIN="${DOMAIN#https://}"
47 | DOMAIN="${DOMAIN#ftp://}"
48 | DOMAIN="${DOMAIN%%/*}"
49 | }
50 |
51 | www_domain(){
52 | CHECK_WWW=$(echo ${1} | cut -c1-4)
53 | if [[ ${CHECK_WWW} == www. ]] ; then
54 | DOMAIN=$(echo ${1} | cut -c 5-)
55 | else
56 | DOMAIN=${1}
57 | fi
58 | WWW_DOMAIN="www.${DOMAIN}"
59 | }
60 |
61 | check_mkcert() {
62 | echo "[Start] Checking mkcert installation..."
63 |
64 | if MKCERT_CMD=$(command -v mkcert.exe 2>/dev/null || command -v mkcert 2>/dev/null); then
65 | echo "[✔] mkcert found at: ${MKCERT_CMD}"
66 | else
67 | echo "[✖] mkcert not found!"
68 | echo "→ Please run 'bash bin/mkcert.sh --install' or install it manually."
69 | echo " Windows: choco install mkcert"
70 | echo " (Linux/macOS support can be added here later)"
71 | exit 1
72 | fi
73 |
74 | echo "[End] mkcert check completed."
75 | }
76 |
77 | install_mkcert() {
78 | echo "[Start] Installing mkcert..."
79 | case "$(uname -s)" in
80 | Linux*) OS="linux" ;;
81 | Darwin*) OS="mac" ;;
82 | MINGW*|MSYS*|CYGWIN*|Windows*) OS="windows" ;;
83 | *) echo "[X] Unsupported OS: $(uname -s)"; exit 1 ;;
84 | esac
85 | echo "[*] Detected OS: $OS"
86 | if command -v mkcert >/dev/null 2>&1 || command -v mkcert.exe >/dev/null 2>&1; then
87 | echo "[O] mkcert is already installed."
88 | echo "[!] Ensuring local CA is installed..."
89 | (command -v mkcert.exe >/dev/null 2>&1 && mkcert.exe -install || mkcert -install)
90 | echo "[O] Local CA configured."
91 | return 0
92 | fi
93 | case "$OS" in
94 | windows)
95 | if ! command -v choco >/dev/null 2>&1 && ! command -v choco.exe >/dev/null 2>&1; then
96 | echo "[X] Chocolatey not found!"
97 | echo "Install it first: https://chocolatey.org/install"
98 | exit 1
99 | fi
100 | choco install mkcert -y
101 | ;;
102 | mac)
103 | if ! command -v brew >/dev/null 2>&1; then
104 | echo "[X] Homebrew not found!"
105 | echo "Install it from https://brew.sh/"
106 | exit 1
107 | fi
108 | brew install mkcert nss
109 | ;;
110 | linux)
111 | if command -v apt >/dev/null 2>&1; then
112 | sudo apt update -y && sudo apt install -y mkcert libnss3-tools
113 | elif command -v dnf >/dev/null 2>&1; then
114 | sudo dnf install -y mkcert nss-tools
115 | elif command -v yum >/dev/null 2>&1; then
116 | sudo yum install -y mkcert nss-tools
117 | elif command -v zypper >/dev/null 2>&1; then
118 | sudo zypper install -y mkcert mozilla-nss-tools
119 | else
120 | echo "[X] Unsupported Linux distro. Install manually:"
121 | echo "→ https://github.com/FiloSottile/mkcert"
122 | exit 1
123 | fi
124 | ;;
125 | esac
126 | if command -v mkcert >/dev/null 2>&1 || command -v mkcert.exe >/dev/null 2>&1; then
127 | echo "[O] mkcert installed successfully."
128 | echo "[!] Creating local CA..."
129 | (command -v mkcert.exe >/dev/null 2>&1 && mkcert.exe -install || mkcert -install)
130 | echo "[O] Local CA configured."
131 | echo "[End] mkcert installation complete."
132 | else
133 | echo "[X] mkcert installation failed!"
134 | exit 1
135 | fi
136 | }
137 |
138 | create_cert_dir(){
139 | if [ ! -d "${CERT_DIR}" ]; then
140 | echo "[!] Creating certificate directory: ${CERT_DIR}"
141 | mkdir -p "${CERT_DIR}"
142 | fi
143 | }
144 |
145 | domain_verify(){
146 | local domain="${1}"
147 | local doc_path="/var/www/vhosts/${domain}/html"
148 |
149 | echo "[!] Checking if domain '${domain}' has been added..."
150 |
151 | if docker compose exec -T ${CONT_NAME} bash -c "[ -d ${doc_path} ]" 2>/dev/null; then
152 | echo -e "[O] Domain \033[32m${domain}\033[0m exists (document root found)"
153 | return 0
154 | else
155 | echo -e "[X] Domain \033[31m${domain}\033[0m has NOT been added yet!"
156 | echo "[!] Document root not found: ${doc_path}"
157 | echo "[!] Please add this domain first using: bash bin/domain.sh -a ${domain}"
158 | exit 1
159 | fi
160 | }
161 |
162 | generate_cert(){
163 | echo '[Start] Generating SSL certificate'
164 | www_domain "${DOMAIN}"
165 | create_cert_dir
166 | mkdir -p "${CERT_DIR}/${DOMAIN}"
167 | cd "${CERT_DIR}/${DOMAIN}"
168 | echo -e "[!] Generating certificate for: \033[32m${DOMAIN}\033[0m and \033[32m${WWW_DOMAIN}\033[0m"
169 |
170 | ${MKCERT_CMD} -key-file key.pem -cert-file cert.pem "${DOMAIN}" "${WWW_DOMAIN}" >/dev/null 2>&1
171 | if [ ${?} = 0 ]; then
172 | echo -e "[O] Certificate generated successfully"
173 | echo "[!] Certificate files:"
174 | echo "${EPACE}Cert: ${CERT_DIR}/${DOMAIN}/cert.pem"
175 | echo "${EPACE}Key: ${CERT_DIR}/${DOMAIN}/key.pem"
176 | else
177 | echo "[X] Failed to generate certificate"
178 | cd ../..
179 | rm -rf "${CERT_DIR}/${DOMAIN}"
180 | exit 1
181 | fi
182 | cd - > /dev/null
183 | echo '[End] Generating SSL certificate'
184 | }
185 |
186 | create_local_template(){
187 | echo '[Start] Creating docker-local.conf template'
188 | local source_file="/usr/local/lsws/conf/templates/docker.conf"
189 | local dest_file="/usr/local/lsws/conf/templates/docker-local.conf"
190 | if docker compose exec -T ${CONT_NAME} bash -c "[ -f ${dest_file} ]" 2>/dev/null; then
191 | echo "[i] Template file already exists: ${dest_file}"
192 | echo '[End] Creating docker-local.conf template'
193 | return 0
194 | fi
195 |
196 | docker compose exec -T ${CONT_NAME} bash -c "
197 | # Copy template file
198 | cp ${source_file} ${dest_file}
199 |
200 | # Remove old vhssl block and last closing brace
201 | sed -i '/^ vhssl {/,/^ }/d; \$d' ${dest_file}
202 |
203 | # Append new vhssl configuration
204 | cat >> ${dest_file} <<'VHSSL_EOF'
205 | vhssl {
206 | keyFile /usr/local/lsws/conf/cert/\$VH_NAME/key.pem
207 | certFile /usr/local/lsws/conf/cert/\$VH_NAME/cert.pem
208 | certChain 1
209 | }
210 | }
211 | VHSSL_EOF
212 |
213 | # Fix ownership and permissions
214 | chown nobody:nogroup ${dest_file} 2>/dev/null || chown lsadm:lsadm ${dest_file}
215 | chmod 644 ${dest_file}
216 | "
217 |
218 | echo -e "[O] Template \033[32mdocker-local.conf\033[0m created successfully!"
219 | echo -e " SSL certificates path: /usr/local/lsws/conf/cert/\$VH_NAME/"
220 | echo '[End] Creating docker-local.conf template'
221 | }
222 |
223 | register_local_template() {
224 | echo '[Start] Registering vhTemplate: dockerLocal'
225 | local config_file="/usr/local/lsws/conf/httpd_config.conf"
226 | local template_name="dockerLocal"
227 | local template_path="conf/templates/docker-local.conf"
228 |
229 | docker compose exec -T ${CONT_NAME} bash -c "
230 | if ! grep -q 'vhTemplate ${template_name} {' ${config_file}; then
231 | cat >> ${config_file} </dev/null'
415 |
416 | if [ ${?} = 0 ]; then
417 | echo -e "[O] OpenLiteSpeed restarted successfully"
418 | else
419 | echo "[X] Failed to restart OpenLiteSpeed"
420 | fi
421 | }
422 |
423 | main(){
424 | if [ "${INSTALL}" = 'true' ]; then
425 | install_mkcert
426 | exit 0
427 | fi
428 | domain_filter "${DOMAIN}"
429 | if [ "${REMOVE}" = 'true' ]; then
430 | remove_cert
431 | exit 0
432 | fi
433 | check_mkcert
434 | domain_verify "${DOMAIN}"
435 | generate_cert
436 | configure_litespeed
437 | }
438 |
439 | check_input ${1}
440 | while [ ! -z "${1}" ]; do
441 | case ${1} in
442 | -[hH] | -help | --help)
443 | help_message
444 | ;;
445 | -[dD] | -domain | --domain)
446 | shift
447 | check_input "${1}"
448 | DOMAIN="${1}"
449 | ;;
450 | -[iI] | --install)
451 | INSTALL=true
452 | ;;
453 | -[rR] | --remove)
454 | REMOVE=true
455 | ;;
456 | *)
457 | help_message
458 | ;;
459 | esac
460 | shift
461 | done
462 |
463 | main
--------------------------------------------------------------------------------