├── acme
└── .gitignore
├── lsws
└── .gitignore
├── sites
├── .gitignore
└── localhost
│ ├── html
│ └── .gitignore
│ └── logs
│ └── .gitignore
├── logs
└── .gitignore
├── .gitignore
├── bin
├── dev
│ ├── list-flagged-files.sh
│ ├── no-skip-worktree-conf.sh
│ └── skip-worktree-conf.sh
├── container
│ ├── certhookctl.sh
│ ├── pkginstallctl.sh
│ ├── serialctl.sh
│ ├── domainctl.sh
│ ├── owaspctl.sh
│ └── appinstallctl.sh
├── appinstall.sh
├── domain.sh
├── webadmin.sh
├── demosite.sh
├── database.sh
└── acme.sh
├── .env
├── .github
└── workflows
│ └── docker.yml
├── .travis
├── main.sh
└── verify.sh
├── LICENSE
├── .travis.yml.bk
├── docker-compose.yml
└── README.md
/acme/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/lsws/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/sites/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/sites/localhost/html/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/sites/localhost/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | data
2 | latest.yml
3 | lsws-dockerfiles
4 |
--------------------------------------------------------------------------------
/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 | LSWS_VERSION=6.3.4
3 | PHP_VERSION=lsphp84
4 | PHPMYADMIN_VERSION=5.2.3
5 | MYSQL_DATABASE=wordpress
6 | MYSQL_ROOT_PASSWORD=your_root_password
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 |
--------------------------------------------------------------------------------
/.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.
--------------------------------------------------------------------------------
/.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: hTQk4ZYbrD4eDnwJsrIhDyuw4WfVMkpP0w26AecHS6edQEFkS1LJS77elFQDgg4SlL8K0xdHrTwqMl5JL9LV+2TZ77xUpX0pB7H9toG1rEqxv4v7bKH7umHCvy0YLzfoz5IjcAHkVqX6/MKcp0M1DQ0Ts1TuTzk5IPacekIbCTRXDdqljKIhvGgDfXZWh0dYkSbA9xoPzeN9GXzSSa+uXqQWk7q2uCdXh4stWFOTR8ZJIHqTyUq1+tNTLAM8b3pw/F0uevCKoHIWljqpwLgg/Chu2JuGnOOV91Jwks7nXikgByiRH8ygE2J0HFjBdNCZZU1BffuYqgziZrVDpFjUJkrp5Tku+SCd76O0NmLz5PgBo6BJL0/p3Sa1N2/b4cdrUFbU0W9DVDVLMinTAOlRbhq/iuldAlBLJVTgOpKsbAQ5AqmceGsVk/JYTK8WzCw0Y0LFNiWlVWtUYSQZo887UEozSl415OEtM0kkiosOUxyd305m/HWBzS7pyHSGp6kum3SIwVLjYsHSAx5WRVA38Gg5OOmKwLHDy5RaN+1u0AN66TUGs4GSfzJNjL0+C6I66EMGCRXMerJNMf9r9dnVKsKUNwvaheCZe9IszGxubmlBrPxLLpczplkGrYTv0aRQsgMWTSGLttEek3j3p7p+ntDNWDaveDCdppipM5EjJ4M=
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 | - ./.travis/main.sh
25 |
26 | install:
27 | - git clone https://github.com/litespeedtech/lsws-docker-env.git
28 | - docker-compose up -d
29 |
30 | before_script:
31 | - docker image ls
32 | - sleep 10
33 |
34 | script:
35 | - ./.travis/verify.sh
36 |
37 | after_script:
38 | - docker-compose stop
39 | - docker-compose rm -f
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/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/litespeed:${LSWS_VERSION}-${PHP_VERSION}
19 | env_file:
20 | - .env
21 | volumes:
22 | - ./lsws/conf:/usr/local/lsws/conf
23 | - ./lsws/admin/conf:/usr/local/lsws/admin/conf
24 | - ./bin/container:/usr/local/bin
25 | - ./sites:/var/www/vhosts/
26 | - ./acme:/root/.acme.sh/
27 | - ./logs:/usr/local/lsws/logs/
28 | ports:
29 | - 80:80
30 | - 443:443
31 | - 443:443/udp
32 | - 7080:7080
33 | restart: always
34 | environment:
35 | TZ: ${TimeZone}
36 | networks:
37 | - default
38 | phpmyadmin:
39 | image: phpmyadmin/phpmyadmin:${PHPMYADMIN_VERSION}
40 | ports:
41 | - 8080:80
42 | environment:
43 | DATABASE_HOST: mysql
44 | restart: always
45 | networks:
46 | - default
47 | redis:
48 | image: "redis:alpine"
49 | logging:
50 | driver: none
51 | volumes:
52 | - ./redis/data:/data
53 | - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
54 | environment:
55 | - REDIS_REPLICATION_MODE=master
56 | restart: always
57 | networks:
58 | - default
59 | networks:
60 | default:
61 | driver: bridge
62 |
--------------------------------------------------------------------------------
/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 | echow '-H, --help'
17 | echo "${EPACE}${EPACE}Display help and exit."
18 | exit 0
19 | }
20 |
21 | check_input(){
22 | if [ -z "${1}" ]; then
23 | help_message
24 | exit 1
25 | fi
26 | }
27 |
28 | install_packages(){
29 | if [ "${1}" = 'wordpress' ]; then
30 | docker compose exec litespeed /bin/bash -c "pkginstallctl.sh --package ed"
31 | docker compose exec litespeed /bin/bash -c "pkginstallctl.sh --package unzip"
32 | fi
33 | }
34 |
35 | app_download(){
36 | install_packages ${1}
37 | docker compose exec litespeed bash -c "appinstallctl.sh --app ${1} --domain ${2}"
38 | bash bin/webadmin.sh -r
39 | exit 0
40 | }
41 |
42 | main(){
43 | app_download "${APP_NAME}" "${DOMAIN}"
44 | }
45 |
46 | check_input ${1}
47 | while [ ! -z "${1}" ]; do
48 | case ${1} in
49 | -[hH] | -help | --help)
50 | help_message
51 | ;;
52 | -[aA] | -app | --app) shift
53 | check_input "${1}"
54 | APP_NAME="${1}"
55 | ;;
56 | -[dD] | -domain | --domain) shift
57 | check_input "${1}"
58 | DOMAIN="${1}"
59 | ;;
60 | *)
61 | help_message
62 | ;;
63 | esac
64 | shift
65 | done
66 |
67 | main
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/bin/container/pkginstallctl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | MA_COMPOSER='/usr/local/bin/composer'
3 | EPACE=' '
4 |
5 | echoY() {
6 | echo -e "\033[38;5;148m${1}\033[39m"
7 | }
8 | echoG() {
9 | echo -e "\033[38;5;71m${1}\033[39m"
10 | }
11 | echoR()
12 | {
13 | echo -e "\033[38;5;203m${1}\033[39m"
14 | }
15 | echow(){
16 | FLAG=${1}
17 | shift
18 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
19 | }
20 |
21 | help_message(){
22 | echo -e "\033[1mOPTIONS\033[0m"
23 | echow '-A, -app [wordpress|magento] -D, --domain [DOMAIN_NAME]'
24 | echo "${EPACE}${EPACE}Example: appinstallctl.sh --app wordpress --domain example.com"
25 | echow '-H, --help'
26 | echo "${EPACE}${EPACE}Display help and exit."
27 | exit 0
28 | }
29 |
30 | install_ed(){
31 | if [ ! -f /bin/ed ]; then
32 | echo "Install ed package.."
33 | apt-get install ed -y > /dev/null 2>&1
34 | ed -V > /dev/null 2>&1
35 | if [ ${?} != 0 ]; then
36 | echoR 'Issue with ed, Please check!'
37 | fi
38 | fi
39 | }
40 |
41 | install_unzip(){
42 | if [ ! -f /usr/bin/unzip ]; then
43 | echoG "Install unzip package"
44 | apt-get install unzip -y > /dev/null 2>&1
45 | unzip -v > /dev/null 2>&1
46 | if [ ${?} != 0 ]; then
47 | echoR 'Issue with unzip, Please check!'
48 | fi
49 | fi
50 | }
51 |
52 |
53 | case ${1} in
54 | -[pP] | -package | --package) shift
55 | if [ -z "${1}" ]; then
56 | help_message
57 | fi
58 | case ${1} in
59 | ed)
60 | install_ed
61 | ;;
62 | unzip)
63 | install_unzip
64 | ;;
65 | esac
66 | ;;
67 | -[hH] | -help | --help)
68 | help_message
69 | ;;
70 | *)
71 | help_message
72 | ;;
73 | esac
--------------------------------------------------------------------------------
/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/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/demosite.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | source .env
3 | APP='wordpress'
4 | CONT_NAME='litespeed'
5 | DOC_FD=''
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 | case ${1} in
16 | "1")
17 | echow "Script will get 'DOMAIN' and 'database' info from .env file, then auto setup virtual host and the wordpress site for you."
18 | echo -e "\033[1mOPTIONS\033[0m"
19 | echow '-W, --wordpress'
20 | echo "${EPACE}${EPACE}Example: lsws1clk.sh -W. If no input, script will still install wordpress by default"
21 | echow '-H, --help'
22 | echo "${EPACE}${EPACE}Display help and exit."
23 | exit 0
24 | ;;
25 | "2")
26 | echow 'Service finished, enjoy your accelarated LiteSpeed server!'
27 | ;;
28 | esac
29 | }
30 |
31 | domain_filter(){
32 | if [ ! -n "${DOMAIN}" ]; then
33 | echo "Parameters not supplied, please check!"
34 | exit 1
35 | fi
36 | DOMAIN="${1}"
37 | DOMAIN="${DOMAIN#http://}"
38 | DOMAIN="${DOMAIN#https://}"
39 | DOMAIN="${DOMAIN#ftp://}"
40 | DOMAIN="${DOMAIN#scp://}"
41 | DOMAIN="${DOMAIN#scp://}"
42 | DOMAIN="${DOMAIN#sftp://}"
43 | DOMAIN=${DOMAIN%%/*}
44 | }
45 |
46 | gen_root_fd(){
47 | DOC_FD="./sites/${1}/"
48 | if [ -d "./sites/${1}" ]; then
49 | echo -e "[O] The root folder \033[32m${DOC_FD}\033[0m exist."
50 | else
51 | echo "Creating - document root."
52 | bash bin/domain.sh -add ${1}
53 | echo "Finished - document root."
54 | fi
55 | }
56 |
57 | create_db(){
58 | if [ ! -n "${MYSQL_DATABASE}" ] || [ ! -n "${MYSQL_USER}" ] || [ ! -n "${MYSQL_PASSWORD}" ]; then
59 | echo "Parameters not supplied, please check!"
60 | exit 1
61 | else
62 | bash bin/database.sh -D ${1} -U ${MYSQL_USER} -P ${MYSQL_PASSWORD} -DB ${MYSQL_DATABASE}
63 | fi
64 | }
65 |
66 | store_credential(){
67 | if [ -f ${DOC_FD}/.db_pass ]; then
68 | echo '[O] db file exist!'
69 | else
70 | echo 'Storing database parameter'
71 | cat > "${DOC_FD}/.db_pass" << EOT
72 | "Database":"${MYSQL_DATABASE}"
73 | "Username":"${MYSQL_USER}"
74 | "Password":"$(echo ${MYSQL_PASSWORD} | tr -d "'")"
75 | EOT
76 | fi
77 | }
78 |
79 | install_packages(){
80 | if [ "${1}" = 'wordpress' ]; then
81 | docker compose exec litespeed /bin/bash -c "pkginstallctl.sh --package ed"
82 | docker compose exec litespeed /bin/bash -c "pkginstallctl.sh --package unzip"
83 | fi
84 | }
85 |
86 | app_download(){
87 | install_packages ${1}
88 | docker compose exec -T ${CONT_NAME} bash -c "appinstallctl.sh --app ${1} --domain ${2}"
89 | }
90 |
91 | lsws_restart(){
92 | bash bin/webadmin.sh -r
93 | }
94 |
95 | main(){
96 | domain_filter ${DOMAIN}
97 | gen_root_fd ${DOMAIN}
98 | create_db ${DOMAIN}
99 | store_credential
100 | app_download ${APP} ${DOMAIN}
101 | lsws_restart
102 | }
103 |
104 | while [ ! -z "${1}" ]; do
105 | case ${1} in
106 | -[hH] | -help | --help)
107 | help_message 1
108 | ;;
109 | -[wW] | --wordpress)
110 | APP='wordpress'
111 | ;;
112 | *)
113 | help_message 1
114 | ;;
115 | esac
116 | shift
117 | done
118 | main
119 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LiteSpeed WordPress Docker Container
2 | [](https://github.com/litespeedtech/lsws-docker-env/actions/)
3 | [](https://hub.docker.com/r/litespeedtech/litespeed)
4 | [
](litespeedtech.com/slack)
5 | [
](https://twitter.com/litespeedtech)
6 |
7 | Install a Lightweight WordPress container with LiteSpeed stable version based on Ubuntu 24.04 Linux.
8 |
9 | ### Prerequisites
10 | 1. [Install Docker](https://www.docker.com/)
11 | 2. [Install Docker Compose](https://docs.docker.com/compose/)
12 |
13 | ## Configuration
14 | Edit the `.env` file to update the demo site domain, default MySQL user, and password.
15 | Feel free to check [Docker hub Tag page](https://hub.docker.com/repository/docker/litespeedtech/litespeed/tags) if you want to update default litespeed and php versions.
16 |
17 | ## Installation
18 | Clone this repository or copy the files from this repository into a new folder:
19 | ```
20 | git clone https://github.com/litespeedtech/lsws-docker-env.git
21 | ```
22 | Open a terminal, `cd` to the folder in which `docker-compose.yml` is saved, and run:
23 | ```
24 | docker compose up
25 | ```
26 |
27 | Note: If you wish to run a single web server container, please see the [usage method here](https://github.com/litespeedtech/lsws-dockerfiles#usage).
28 |
29 |
30 | ## Components
31 | The docker image installs the following packages on your system:
32 |
33 | |Component|Version|
34 | | :-------------: | :-------------: |
35 | |Linux|Ubuntu 24.04|
36 | |LiteSpeed|[Latest version](https://www.litespeedtech.com/products/litespeed-web-server/download)|
37 | |MariaDB|[Stable version: 11.4](https://hub.docker.com/_/mariadb)|
38 | |PHP|[Latest version](http://rpms.litespeedtech.com/debian/)|
39 | |LiteSpeed Cache|[Latest from WordPress.org](https://wordpress.org/plugins/litespeed-cache/)|
40 | |ACME|[Latest from ACME official](https://github.com/acmesh-official/get.acme.sh)|
41 | |WordPress|[Latest from WordPress](https://wordpress.org/download/)|
42 | |phpMyAdmin|[Latest from dockerhub](https://hub.docker.com/r/bitnami/phpmyadmin/)|
43 | |Redis|[Latest from dockerhub](https://hub.docker.com/_/redis/)|
44 |
45 | ## Data Structure
46 | Cloned project
47 | ```bash
48 | ├── acme
49 | ├── bin
50 | │ └── container
51 | ├── data
52 | │ └── db
53 | ├── logs
54 | │ ├── access.log
55 | │ ├── error.log
56 | │ ├── lsrestart.log
57 | │ └── stderr.log
58 | ├── lsws
59 | │ ├── admin-conf
60 | │ └── conf
61 | ├── sites
62 | │ └── localhost
63 | ├── LICENSE
64 | ├── README.md
65 | └── docker-compose.yml
66 | ```
67 |
68 | * `acme` contains all applied certificates from Lets Encrypt
69 |
70 | * `bin` contains multiple CLI scripts to allow you add or delete virtual hosts, install applications, upgrade, etc
71 |
72 | * `data` stores the MySQL database
73 |
74 | * `logs` contains all of the web server logs and virtual host access logs
75 |
76 | * `lsws` contains all web server configuration files
77 |
78 | * `sites` contains the document roots (the WordPress application will install here)
79 |
80 | ## Usage
81 | ### Starting a Container
82 | Start the container with the `up` or `start` methods:
83 | ```
84 | docker compose up
85 | ```
86 | You can run with daemon mode, like so:
87 | ```
88 | docker compose up -d
89 | ```
90 | The container is now built and running.
91 |
92 | Note: The container will auto-apply a 15-day trial license. Please contact LiteSpeed to extend the trial, or apply your own license, [starting from $0](https://www.litespeedtech.com/pricing).
93 |
94 | ### Stopping a Container
95 | ```
96 | docker compose stop
97 | ```
98 | ### Removing Containers
99 | To stop and remove all containers, use the `down` command:
100 | ```
101 | docker compose down
102 | ```
103 | ### Setting the WebAdmin Password
104 | We strongly recommend you set your personal password right away.
105 | ```
106 | bash bin/webadmin.sh my_password
107 | ```
108 | ### Starting a Demo Site
109 | After running the following command, you should be able to access the WordPress installation with the configured domain. By default the domain is http://localhost.
110 | ```
111 | bash bin/demosite.sh
112 | ```
113 | ### Creating a Domain and Virtual Host
114 | ```
115 | bash bin/domain.sh [-A, --add] example.com
116 | ```
117 | > Please ignore SSL certificate warnings from the server. They happen if you haven't applied the certificate.
118 | ### Deleting a Domain and Virtual Host
119 | ```
120 | bash bin/domain.sh [-D, --del] example.com
121 | ```
122 | ### Creating a Database
123 | You can either automatically generate the user, password, and database names, or specify them. Use the following to auto generate:
124 | ```
125 | bash bin/database.sh [-D, --domain] example.com
126 | ```
127 | Use this command to specify your own names, substituting `user_name`, `my_password`, and `database_name` with your preferred values:
128 | ```
129 | bash bin/database.sh [-D, --domain] example.com [-U, --user] USER_NAME [-P, --password] MY_PASS [-DB, --database] DATABASE_NAME
130 | ```
131 | ### Installing a WordPress Site
132 | To preconfigure the `wp-config` file, run the `database.sh` script for your domain, before you use the following command to install WordPress:
133 | ```
134 | ./bin/appinstall.sh [-A, --app] wordpress [-D, --domain] example.com
135 | ```
136 |
137 | ### Connecting to Redis
138 | 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.
139 |
140 | ### Installing ACME
141 | We need to run the ACME installation command the **first time only**.
142 | With email notification:
143 | ```
144 | ./bin/acme.sh [-I, --install] [-E, --email] EMAIL_ADDR
145 | ```
146 | ### Applying a Let's Encrypt Certificate
147 | Use the root domain in this command, and it will check for a certificate and automatically apply one with and without `www`:
148 | ```
149 | ./bin/acme.sh [-D, --domain] example.com
150 | ```
151 |
152 | Other parameters:
153 |
154 | * [`-r`, `--renew`]: Renew a specific domain with -D or --domain parameter if posibile. To force renew, use -f parameter.
155 |
156 | * [`-R`, `--renew-all`]: Renew all domains if possible. To force renew, use -f parameter.
157 |
158 | * [`-f`, `-F`, `--force`]: Force renew for a specific domain or all domains.
159 |
160 | * [`-v`, `--revoke`]: Revoke a domain.
161 |
162 | * [`-V`, `--remove`]: Remove a domain.
163 |
164 | ### Updating Web Server
165 | To upgrade the web server to latest stable version, run the following:
166 | ```
167 | bash bin/webadmin.sh [-U, --upgrade]
168 | ```
169 | ### Applying OWASP ModSecurity
170 | Enable OWASP `mod_secure` on the web server:
171 | ```
172 | bash bin/webadmin.sh [-M, --mod-secure] enable
173 | ```
174 | Disable OWASP `mod_secure` on the web server:
175 | ```
176 | bash bin/webadmin.sh [-M, --mod-secure] disable
177 | ```
178 | >Please ignore ModSecurity warnings from the server. They happen if some of the rules are not supported by the server.
179 | ### Applying license to LSWS
180 | Apply your license with command:
181 | ```
182 | bash bin/webadmin.sh [-S, --serial] YOUR_SERIAL
183 | ```
184 | Apply trial license to server with command:
185 | ```
186 | bash bin/webadmin.sh [-S, --serial] TRIAL
187 | ```
188 |
189 | ### Accessing the Database
190 | After installation, you can use phpMinAdmin 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.
191 |
192 | ## Customization
193 | If you want to customize the image by adding some packages, e.g. `lsphp83-pspell`, just extend it with a Dockerfile.
194 | 1. We can create a `custom` folder and a `custom/Dockerfile` file under the main project.
195 | 2. Add the following example code to `Dockerfile` under the custom folder
196 | ```
197 | FROM litespeedtech/litespeed:latest
198 | RUN apt-get update && apt-get install lsphp83-pspell
199 | ```
200 | 3. Add `build: ./custom` line under the "image: litespeedtech" of docker-compose file. So it will looks like this
201 | ```
202 | litespeed:
203 | image: litespeedtech/litespeed:${LSWS_VERSION}-${PHP_VERSION}
204 | build: ./custom
205 | ```
206 | 4. Build and start it with command:
207 | ```
208 | docker compose up --build
209 | ```
210 |
211 | ## Support & Feedback
212 | If you still have a question after using LiteSpeed Docker, you have a few options.
213 | * Join [the GoLiteSpeed Slack community](https://litespeedtech.com/slack/) for real-time discussion
214 | * Post to [the LiteSpeed Forums](https://www.litespeedtech.com/support/forum/) for community support
215 | * Reporting any issue on [Github lsws-docker-env](https://github.com/litespeedtech/lsws-docker-env/issues) project
216 |
217 | **Pull requests are always welcome**
--------------------------------------------------------------------------------
/bin/container/appinstallctl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DEFAULT_VH_ROOT='/var/www/vhosts'
4 | VH_DOC_ROOT=''
5 | APP=''
6 | DOMAIN=''
7 | WWW_UID=''
8 | WWW_GID=''
9 | USER='1000'
10 | WPCONSTCONF=''
11 | DB_HOST='mysql'
12 | PLUGINLIST="litespeed-cache.zip"
13 | THEME='twentytwenty'
14 | LSDIR='/usr/local/lsws'
15 | EMAIL='test@example.com'
16 | APP_ACCT=''
17 | APP_PASS=''
18 | SKIP_WP=0
19 | app_skip=0
20 | EPACE=' '
21 |
22 | echoY() {
23 | echo -e "\033[38;5;148m${1}\033[39m"
24 | }
25 | echoG() {
26 | echo -e "\033[38;5;71m${1}\033[39m"
27 | }
28 | echoR()
29 | {
30 | echo -e "\033[38;5;203m${1}\033[39m"
31 | }
32 | echow(){
33 | FLAG=${1}
34 | shift
35 | echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
36 | }
37 |
38 | help_message(){
39 | case ${1} in
40 | "1")
41 | echo -e "\033[1mOPTIONS\033[0m"
42 | echow '-A, -app [wordpress|magento] -D, --domain [DOMAIN_NAME]'
43 | echo "${EPACE}${EPACE}Example: appinstallctl.sh --app wordpress --domain example.com"
44 | echow '-H, --help'
45 | echo "${EPACE}${EPACE}Display help and exit."
46 | exit 0
47 | ;;
48 | "2")
49 | echow 'Service finished, enjoy your accelarated LiteSpeed server!'
50 | ;;
51 | esac
52 | }
53 |
54 | check_input(){
55 | if [ -z "${1}" ]; then
56 | help_message 1
57 | exit 1
58 | fi
59 | }
60 |
61 | linechange(){
62 | LINENUM=$(grep -n "${1}" ${2} | cut -d: -f 1)
63 | if [ -n "${LINENUM}" ] && [ "${LINENUM}" -eq "${LINENUM}" ] 2>/dev/null; then
64 | sed -i "${LINENUM}d" ${2}
65 | sed -i "${LINENUM}i${3}" ${2}
66 | fi
67 | }
68 |
69 | ck_ed(){
70 | if [ ! -f /bin/ed ]; then
71 | echo 'ed package not exist, please check!'
72 | exit 1
73 | fi
74 | }
75 |
76 | ck_unzip(){
77 | if [ ! -f /usr/bin/unzip ]; then
78 | echo 'unzip package not exist, please check!'
79 | exit 1
80 | fi
81 | }
82 |
83 | gen_pass(){
84 | APP_STR=$(shuf -i 100-999 -n1)
85 | APP_PASS=$(openssl rand -hex 16)
86 | APP_ACCT="admin${APP_STR}"
87 | MA_BACK_URL="admin_${APP_STR}"
88 | }
89 |
90 | get_owner(){
91 | WWW_UID=$(stat -c "%u" ${DEFAULT_VH_ROOT})
92 | WWW_GID=$(stat -c "%g" ${DEFAULT_VH_ROOT})
93 | if [ ${WWW_UID} -eq 0 ] || [ ${WWW_GID} -eq 0 ]; then
94 | WWW_UID=1000
95 | WWW_GID=1000
96 | echo "Set owner to ${WWW_UID}"
97 | fi
98 | }
99 |
100 | get_db_pass(){
101 | if [ -f ${DEFAULT_VH_ROOT}/${1}/.db_pass ]; then
102 | SQL_DB=$(grep -i Database ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
103 | SQL_USER=$(grep -i Username ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
104 | SQL_PASS=$(grep -i Password ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
105 | else
106 | echo 'db pass file can not locate, skip wp-config pre-config.'
107 | fi
108 | }
109 |
110 | set_vh_docroot(){
111 | if [ -d ${DEFAULT_VH_ROOT}/${1}/html ]; then
112 | VH_ROOT="${DEFAULT_VH_ROOT}/${1}"
113 | VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${1}/html"
114 | WPCONSTCONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.json"
115 | else
116 | echo "${DEFAULT_VH_ROOT}/${1}/html does not exist, please add domain first! Abort!"
117 | exit 1
118 | fi
119 | }
120 |
121 | check_sql_native(){
122 | local COUNTER=0
123 | local LIMIT_NUM=100
124 | until [ "$(curl -v mysql:3306 2>&1 | grep -i 'native\|Connected')" ]; do
125 | echo "Counter: ${COUNTER}/${LIMIT_NUM}"
126 | COUNTER=$((COUNTER+1))
127 | if [ ${COUNTER} = 10 ]; then
128 | echo '--- MySQL is starting, please wait... ---'
129 | elif [ ${COUNTER} = ${LIMIT_NUM} ]; then
130 | echo '--- MySQL is timeout, exit! ---'
131 | exit 1
132 | fi
133 | sleep 1
134 | done
135 | }
136 |
137 | install_wp_plugin(){
138 | for PLUGIN in ${PLUGINLIST}; do
139 | wget -q -P ${VH_DOC_ROOT}/wp-content/plugins/ https://downloads.wordpress.org/plugin/${PLUGIN}
140 | if [ ${?} = 0 ]; then
141 | ck_unzip
142 | unzip -qq -o ${VH_DOC_ROOT}/wp-content/plugins/${PLUGIN} -d ${VH_DOC_ROOT}/wp-content/plugins/
143 | else
144 | echo "${PLUGINLIST} FAILED to download"
145 | fi
146 | done
147 | rm -f ${VH_DOC_ROOT}/wp-content/plugins/*.zip
148 | }
149 |
150 | config_wp_htaccess(){
151 | if [ ! -f ${VH_DOC_ROOT}/.htaccess ]; then
152 | touch ${VH_DOC_ROOT}/.htaccess
153 | fi
154 | cat << EOM > ${VH_DOC_ROOT}/.htaccess
155 | # BEGIN WordPress
156 |
157 | RewriteEngine On
158 | RewriteBase /
159 | RewriteRule ^index\.php$ - [L]
160 | RewriteCond %{REQUEST_FILENAME} !-f
161 | RewriteCond %{REQUEST_FILENAME} !-d
162 | RewriteRule . /index.php [L]
163 |
164 | # END WordPress
165 | EOM
166 | }
167 |
168 |
169 | get_theme_name(){
170 | THEME_NAME=$(grep WP_DEFAULT_THEME ${VH_DOC_ROOT}/wp-includes/default-constants.php | grep -v '!' | awk -F "'" '{print $4}')
171 | echo "${THEME_NAME}" | grep 'twenty' >/dev/null 2>&1
172 | if [ ${?} = 0 ]; then
173 | THEME="${THEME_NAME}"
174 | fi
175 | }
176 |
177 | set_lscache(){
178 | wget -q -O ${WPCONSTCONF} https://raw.githubusercontent.com/litespeedtech/lscache_wp/refs/heads/master/data/const.default.json
179 | if [ -f ${WPCONSTCONF} ]; then
180 | sed -ie 's/"object": .*"/"object": '\"true\"'/g' ${WPCONSTCONF}
181 | sed -ie 's/"object-kind": .*"/"object-kind": '\"true\"'/g' ${WPCONSTCONF}
182 | sed -ie 's/"object-host": .*"/"object-host": '\"redis\"'/g' ${WPCONSTCONF}
183 | sed -ie 's/"object-port": .*"/"object-port": '\"6379\"'/g' ${WPCONSTCONF}
184 | fi
185 |
186 | THEME_PATH="${VH_DOC_ROOT}/wp-content/themes/${THEME}"
187 | if [ ! -f ${THEME_PATH}/functions.php ]; then
188 | cat >> "${THEME_PATH}/functions.php" <>/dev/null 2>&1
201 | 2i
202 | require_once( WP_CONTENT_DIR.'/../wp-admin/includes/plugin.php' );
203 | \$path = 'litespeed-cache/litespeed-cache.php' ;
204 | if (!is_plugin_active( \$path )) {
205 | activate_plugin( \$path ) ;
206 | rename( __FILE__ . '.bk', __FILE__ );
207 | }
208 | .
209 | w
210 | q
211 | END
212 | fi
213 | }
214 |
215 | preinstall_wordpress(){
216 | get_db_pass ${DOMAIN}
217 | if [ ! -f ${VH_DOC_ROOT}/wp-config.php ] && [ -f ${VH_DOC_ROOT}/wp-config-sample.php ]; then
218 | cp ${VH_DOC_ROOT}/wp-config-sample.php ${VH_DOC_ROOT}/wp-config.php
219 | NEWDBPWD="define('DB_PASSWORD', '${SQL_PASS}');"
220 | linechange 'DB_PASSWORD' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
221 | NEWDBPWD="define('DB_USER', '${SQL_USER}');"
222 | linechange 'DB_USER' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
223 | NEWDBPWD="define('DB_NAME', '${SQL_DB}');"
224 | linechange 'DB_NAME' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
225 | NEWDBPWD="define('DB_HOST', '${DB_HOST}');"
226 | linechange 'DB_HOST' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
227 | elif [ -f ${VH_DOC_ROOT}/wp-config.php ]; then
228 | echo "${VH_DOC_ROOT}/wp-config.php already exist, exit !"
229 | exit 1
230 | else
231 | echo 'Skip!'
232 | exit 2
233 | fi
234 | }
235 |
236 | app_wordpress_dl(){
237 | if [ ! -f "${VH_DOC_ROOT}/wp-config.php" ] && [ ! -f "${VH_DOC_ROOT}/index.php" ]; then
238 | wp core download \
239 | --allow-root \
240 | --quiet
241 | else
242 | echoR 'wordpress or other file already exist, please manually clean up the document root folder, abort!'
243 | exit 1
244 | fi
245 | }
246 |
247 | change_owner(){
248 | chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${DOMAIN}
249 | }
250 |
251 | store_access(){
252 | cat << EOM > ${VH_ROOT}/.app_access
253 | Account: ${APP_ACCT}
254 | Password: ${APP_PASS}
255 | Admin_URL: ${MA_BACK_URL}
256 | EOM
257 | }
258 |
259 | show_access(){
260 | echoG '----------------------------------------'
261 | echoY "Account: ${APP_ACCT}"
262 | echoY "Password: ${APP_PASS}"
263 | echoY "Admin_URL: ${MA_BACK_URL}"
264 | echoG '----------------------------------------'
265 | }
266 |
267 | main(){
268 | set_vh_docroot ${DOMAIN}
269 | get_owner
270 | gen_pass
271 | cd ${VH_DOC_ROOT}
272 | if [ "${APP}" = 'wordpress' ] || [ "${APP}" = 'W' ]; then
273 | check_sql_native
274 | app_wordpress_dl
275 | preinstall_wordpress
276 | install_wp_plugin
277 | config_wp_htaccess
278 | get_theme_name
279 | set_lscache
280 | change_owner
281 | exit 0
282 | else
283 | echo "APP: ${APP} not support, exit!"
284 | exit 1
285 | fi
286 | help_message 2
287 | }
288 |
289 | check_input ${1}
290 | while [ ! -z "${1}" ]; do
291 | case ${1} in
292 | -[hH] | -help | --help)
293 | help_message 1
294 | ;;
295 | -[aA] | -app | --app) shift
296 | check_input "${1}"
297 | APP="${1}"
298 | ;;
299 | -[dD] | -domain | --domain) shift
300 | check_input "${1}"
301 | DOMAIN="${1}"
302 | ;;
303 | *)
304 | help_message 1
305 | ;;
306 | esac
307 | shift
308 | done
309 | main
310 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------