ITGOIT - Wiki

In this page, we will be installing the important steps that should be taken before using your Ubuntu Server in a production environment. This includes things like configuring security settings, creating backups, and optimizing performance. By following the guidelines outlined on this page, you can ensure that your Ubuntu Server is ready to handle the demands of a production environment and keep your data and applications secure. By the end of this page, you will have a solid understanding of what needs to be done to prepare your Ubuntu Server for production use. So, let's get started.

Set up SSH

sudo su
cd /etc/ssh
nano sshd_config

Locate the line that reads:
#PermitRootLogin prohibit-password
and modify it to:
PermitRootLogin yes

service sshd restart

If you are lazy then use the following command:

sudo sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && sudo service sshd restart

Update the root password to use SSH with root

passwd

Update Ubuntu

sudo apt update
sudo apt full-upgrade

Upgrade Ubuntu to newest distro

sudo apt update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
sudo apt-get autoremove -y
sudo apt install update-manager-core
sudo do-release-upgrade -d

Install net-tools

sudo apt install net-tools -y

Set timezone

timedatectl list-timezones
sudo timedatectl set-timezone Europe/Amsterdam
timedatectl

Set NTP server (optional)

sudo nano /etc/systemd/timesyncd.conf
[Time]
NTP=YOUR.NTP.IP.HERE
FallbackNTP=ntp.ubuntu.com
sudo timedatectl set-ntp off
sudo timedatectl set-ntp on
systemctl status systemd-timesyncd

Set Static IP Address

Enable Cloud-init if you use:

Cloud Environments: If you're using cloud infrastructure (AWS, Azure, Google Cloud, etc.), cloud-init can automate the initial configuration of instances, including setting static IPs.

Automated Configuration: It can handle more than just networking—like setting up users, SSH keys, and running initialization scripts, which can be beneficial for fully automating your server setup.

Consistency: Ensures that all instances are consistently configured according to predefined templates.

Disable Cloud-init if you use:
Bare-metal Servers: For physical servers or simple virtual machines where cloud-init features are not required, you can manually configure network settings. (set a static ip address)

Minimal Configuration: If your setup is simple and doesn’t require the extensive initialization capabilities of cloud-init, you might opt to configure the static IP directly using traditional methods.

Disable cloud init

sudo mkdir -p /etc/cloud/cloud.cfg.d && echo "network: {config: disabled}" | sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
sudo rm -rf /etc/netplan/50-cloud-init.yaml && netplan apply
ip addr show NETWORKCARDNAME

Enable cloud-init

sudo rm -rf /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg && sudo reboot

Configure Static IP Address

ip a
sudo nano /etc/netplan/00-installer-config.yaml

network config below is to restore the defaults (disables static ip). Replace ens33 with your network card name

network:
  version: 2
  ethernets:
    ens33:
      dhcp4: true
      dhcp6: true

Use network config below for static ip. Replace ens33 with your network card name

network:
  version: 2
  renderer: networkd # Uses systemd-networkd as the backend (standard for Ubuntu Server)
  ethernets:
    ens33: # The name of your physical network interface (check 'ip link' to find yours)
      
      # Disable auto-configuration (DHCP) to ensure the IP never changes
      dhcp4: no 
      dhcp6: no
      accept-ra: no # Disable IPv6 Router Advertisements (we want full manual control)
      
      addresses:
        - "192.168.1.95/24" # Static IPv4 Address
        - "2a02:f6e:c2ac:0:5a47:caff:fe76:9dd1/64" # Static IPv6 Address
      
      nameservers:
        addresses:
          - 1.1.1.1 # Cloudflare DNS (IPv4)
          - 1.0.0.1
          - "2606:4700:4700::1111" # Cloudflare DNS (IPv6) - Recommended if using IPv6
          - "2606:4700:4700::1001"
      
      routes:
        # IPv4 Default Gateway configuration
        - to: default
          via: 192.168.1.2
          on-link: true # Forces the route to be accepted even if the gateway seems unreachable initially
        
        # IPv6 Default Gateway configuration
        # "::/0" is the IPv6 equivalent of "0.0.0.0/0" (The entire internet)
        - to: "::/0"
          via: "fe80::229a:7dff:fea6:116d" # The Link-Local address of your router (found via another device on the network)
          on-link: true # CRITICAL: Required when the gateway is a Link-Local (fe80::) address
      
      wakeonlan: true # Keeps the network card active for Wake-on-LAN packets
sudo chmod 600 /etc/netplan/00-installer-config.yaml
sudo netplan apply

Disable DHCP

nano /etc/dhcpcd.conf
denyinterfaces INSERTNETWORKCARDNAME

Wake-on-LAN WOL (optional)

Make sure you have Wake-on-LAN (WOL) enabled in your BIOS. This is because WoL is a hardware-level feature that must be enabled in the firmware before it can be utilized by the operating system.

  1. Install the ethtool utility by running the following command:
sudo apt install ethtool
  1. Check the current state of WOL for your network card:
sudo ethtool NETWORKCARDNAME

Replace NETWORKCARDNAME with the name of your network interface.

  1. Enable WOL for your network card:
sudo ethtool -s NETWORKCARDNAME wol g

Replace NETWORKCARDNAME with the name of your network interface.

  1. Configure the network card to start with WOL enabled after a reboot:
sudo nano /etc/netplan/00-installer-config.yaml

Add the following lines to the configuration file for your network interface:

wakeonlan: true

Example:

network:
  version: 2
  renderer: networkd # or NetworkManager
  ethernets:
    enp87s0:
      dhcp4: false
      dhcp6: false
      addresses: [ "192.168.1.55/24" ]
      nameservers:
#       search: [sub.domain.tld, domain.tld]
       addresses: [1.1.1.1, 1.0.0.1]
      routes:
       - to: default # or 0.0.0.0/0
         via: 192.168.1.1
         on-link: true
      wakeonlan: true
  1. Apply the changes:
sudo netplan apply

Automaticly run Wake-on-LAN on start (optional)

sudo nano /etc/systemd/system/wol.service
[Unit]
Description=Enable Wake On Lan 
[Service]
Type=oneshot
ExecStart = /sbin/ethtool --change NETWORKCARDNAME wol g
[Install]
WantedBy=basic.target

Replace NETWORKCARDNAME with the name of your network interface.

Fix LVM

sudo lvm
lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
exit
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv

Oh My Zsh

sudo apt update
sudo apt install -y zsh
sudo apt install -y powerline fonts-powerline
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
Y

But why?

Improved productivity: Oh My Zsh provides many useful commands and aliases to help you navigate and use the command line more efficiently.

Customization: Oh My Zsh allows you to customize your command line interface with various themes, plugins and settings.

Better organization: Oh My Zsh organizes your command line environment with sensible defaults and conventions, making it easier to manage your files and directories.

Improved security: Oh My Zsh includes a number of security features, such as password protection for sensitive files and directories.

Autocompletion: Oh My Zsh provides autocompletion for a wide range of tools and programming languages.

Syntax highlighting: Oh My Zsh provides syntax highlighting, making it easier to read and understand your command line input.

Improved error handling: Oh My Zsh can help you identify and correct errors more quickly and easily.

Better performance: Oh My Zsh is designed to be lightweight and fast, making it a good choice for users with limited system resources.

Community support: Oh My Zsh has a large and active community that provides support, troubleshooting and feature development.

Set Ram (optional)

Less chance of crash due to too little ram.

For 8GB RAM

sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo nano /etc/fstab
/swapfile swap swap defaults 0 0

For 16GB RAM

sudo fallocate -l 16G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo nano /etc/fstab
/swapfile swap swap defaults 0 0

Usefull packages

Before getting packages, run sudo apt update

sudo apt update
sudo apt full-upgrade

Networking Utilities

sudo apt install net-tools -y

active/passive address reconnaissance

sudo apt install netdiscover -y

Disk Usage - advanced

sudo apt install ncdu -y

Disk Usage - basic

sudo apt install duf -y

System Info - fastfetch

sudo add-apt-repository ppa:zhangsongcui3371/fastfetch && sudo apt update && sudo apt install fastfetch

File Manager - vifm

sudo apt install vifm -y

LXD container

Please note that these steps are for Ubuntu 18.04 or later, for previous versions of Ubuntu the commands may differ.

  1. Install the LXD package:
sudo apt-add-repository ppa:ubuntu-lxc/lxd-stable
sudo apt-get update
sudo apt-get install lxd
  1. Initialize the LXD configuration:
sudo lxd init
  1. Choose the storage pool you want to use, and configure it as necessary.

  2. Create a new container:

sudo lxc launch ubuntu:18.04 mycontainer
  1. Start the container:
sudo lxc start mycontainer

Handy LXD container commands

sudo lxc exec mycontainer bash
sudo lxc stop mycontainer
sudo lxc delete mycontainer
sudo lxc list
sudo lxc info
sudo image list
sudo lxc image export

GPU Passthrough on Linux and Docker

Check if you can see you're GPU

lshw -c video

qemu-guest-agent is optional if you are virtualizing this machine

sudo apt install qemu-guest-agent 

build-essential is required for nvidia drivers to compile

sudo apt install build-essential
sudo apt install --no-install-recommends nvidia-cuda-toolkit nvidia-headless-535 nvidia-utils-535 libnvidia-encode-535
sudo apt install nvtop

run these commands only if you run Rancher/Kubernetes

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt update && sudo apt install -y nvidia-container-toolkit
sudo apt install nvidia-container-runtime
sudo apt install -y nvidia-docker2
N
reboot

If sudo apt update does not work visit this link: https://github.com/NVIDIA/nvidia-docker/issues/1626

To see the GPU UUID run this command

nvidia-smi --query-gpu=gpu_name,gpu_bus_id,vbios_version,uuid --format=csv

Kernel-based Virtual Machine (KVM)

  1. Check if your CPU supports hardware virtualization:

The command should return a non-zero value, indicating that your system supports hardware virtualization.

egrep -c '(vmx|svm)' /proc/cpuinfo
sudo kvm-ok
  1. Install the KVM and related packages:
sudo apt install qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils
  1. Add your user to the libvirtd group:
sudo adduser myusername kvm
  1. Log out and log back in for the changes to take effect.

  2. Verify that the installation was successful:

The command should return an empty list, indicating that no virtual machines are currently running.

virsh list --all
  1. To create a new virtual machine you can use the virt-install command, for example:
sudo virt-install --name=myVM --ram=1024 --vcpus=1 --disk path=/var/lib/libvirt/images/myVM.img,size=10 --cdrom /path/to/ubuntu.iso

Or you can install the GUI to create a new virtual machine

sudo apt install virt-manager
virt-manager
  1. To start the virtual machine use:
virsh start myVM
  1. To connect to the virtual machine use:
virsh console myVM

FusionPBX (Call Center)

FusionPBX - Ubuntu install

Install FusionPBX on Ubuntu 24.04 LTS. Higher versions are not support YET.
https://ubuntu.com/download/server#downloads

sudo su
wget -O - https://raw.githubusercontent.com/fusionpbx/fusionpbx-install.sh/master/ubuntu/pre-install.sh | sh;

Optional:
Before installing FusionPBX checkout the config file to make any changes for example the domain to use
cd /usr/src/fusionpbx-install.sh/ubuntu/resources/ && nano config.sh

cd /usr/src/fusionpbx-install.sh/ubuntu && ./install.sh

Before restarting the server note down the username password information that was displayed

reboot

FusionPBX - Debian install

Install FusionPBX on Debian 12 Higher versions are not support YET.
https://cdimage.debian.org/debian-cd/12.10.0/amd64/iso-dvd/debian-12.10.0-amd64-DVD-1.iso

sudo su
wget -O - https://raw.githubusercontent.com/fusionpbx/fusionpbx-install.sh/master/debian/pre-install.sh | sh;

Optional:
Before installing FusionPBX checkout the config file to make any changes for example the domain to use
cd /usr/src/fusionpbx-install.sh/debian/resources/ && nano config.sh

cd /usr/src/fusionpbx-install.sh/debian && ./install.sh

Before restarting the server note down the username password information that was displayed

reboot

Configure SIP Profile

SIP Profile configuration is typically not required for internal calls within the same network (e.g., FusionPBX and phones on 192.168.1.0/24). The default rtp-ip and sip-ip ($${local_ip_v4}, auto-detected as the FUSIONPBX_SERVER_IP) work for the internal profile.

Configure SIP Profiles only if:

otherwise skip this step and go to: ## Add SSL

For the external profile with NAT:

See FusionPBX NAT Documentation for details.

If you experience call issues then follow the three steps below:

  1. Go to: Advanced -> SIP Profiles

  2. Open External:
    rtp-ip $${local_ip_v4} (Replace this with the FusionPBX server IP)
    sip-ip $${local_ip_v4} (Replace this with the FusionPBX server IP)

  3. Open Internal:
    rtp-ip $${local_ip_v4} (Replace this with the FusionPBX server IP)
    sip-ip $${local_ip_v4} (Replace this with the FusionPBX server IP)

Add SSL

cd /usr/src/fusionpbx-install.sh/ubuntu/resources && ./letsencrypt.sh

EXAMPLE
Domain Name: fpbx.arcadeparty.lan
Email Address: [email protected]

Create admin user

  1. Go to Accounts > Users

  2. Click on ADD

EXAMPLE
Username: pbxadmin
Password: P@ssWord!123
Confirm Password: P@ssWord!123
Email: [email protected]
Language: English - United States en-us
Time Zone: America/New_York
First Name: John
Last Name: Smith
Organization: arcadeparty
Groups: superadmin

Add Domain

  1. Go to Advanced > Domains

  2. Click on ADD

EXAMPLE
Name: fpbx.arcadeparty.lan
Description: Main domain

  1. Click on SAVE

Add Access Control

If configured INCORRECTLY it will result in User has not been found or UNALLOCATED_NUMBER

  1. Go to Advanced > Access Controls

  2. Open providers

For Nodes:

EXAMPLE
Type: allow
CIDR: 192.168.1.0/24
Description: VoIPin

  1. Click on SAVE

Add Gateway

  1. Go to Accounts > Gateways

  2. Click on ADD

EXAMPLE
Gateway: Primary GW
Username: 5596916644
Password: P@ssWord!123
From User:
From Domain:
Proxy: 64.136.174.30
Realm: 64.136.174.30
Expire Seconds: 800
Register: False
Retry Seconds: 30
Context: public
Profile: external
Description: primary gateway

Secondary gateway is optional

EXAMPLE
Gateway: Secondary GW
Username: 5596916644
Password: P@ssWord!123
From User:
From Domain:
Proxy: 64.136.173.22
Realm: 64.136.173.22
Expire Seconds: 800
Register: False
Retry Seconds: 30
Context: public
Profile: external
Description: Secondary gateway

Add IVR

  1. Go to Applications > Recordings

  2. Click on ADD and choose your WAV audio file

  3. Click on UPLOAD

  4. Go to Applications > IVR Menus

  5. Click on ADD

EXAMPLE
Name: Generic IVR
Extension: 8000
Language: en-us callie
Greet Long: your WAV audio file
Greet Short:
Options:
Timeout: 3000
Exit Action: Hangup
Direct Dial: True
Ring Back:
Caller ID Name Prefix:
Context: fpbx.arcadeparty.lan
Description: generic ivr menu

Add Destination

  1. Go to Dialplan > Destinations

  2. Click on ADD

EXAMPLE
Type: Inbound
Country Code: country calling code. For example the Netherlands, 31 (without the + sign)
Destination: 5596916644
Caller ID Name:
Caller ID Number:
Context: Public
Conditions:
Actions: 8000 Generic IVR
User:
Group:
Caller ID Name Prefix:
Record:
Hold Music: your WAV audio file
Distinctive Ring:
Account Code:
Usage: Voice
Domain: fpbx.arcadeparty.lan
Order: 100
Description: DID01

Add Outbound Route

  1. Go to Dialplan > Outbound Routes

  2. Click on ADD

EXAMPLE
Gateway: Primary GW
Alternate 1: Secondary GW
Alternate 2: Tertiary GW
Dialplan Expression: your country region
Prefix: your country calling code
Limit:
Account Code:
Toll allow:
PIN Numbers: False
Order: 100
Enabled: True
Description: outbound route01

Add Extension

Import extension (faster)

  1. Go to Accounts > Extensions

  2. Click on IMPORT

  3. For Data you can use the template below with your preferences

extension,password,effective_caller_id_name,effective_caller_id_number,outbound_caller_id_name,outbound_caller_id_number,hold_music,user_context,description,enabled
1000,1000,Dipper Pines,1000,arcadeparty,1234567890,default,192.168.1.126,"1000 ext",true
1001,1001,Mabel Pines,1001,arcadeparty,1234567890,default,192.168.1.126,"1001 ext",true
1002,1002,Wendy Corduroy,1002,arcadeparty,1234567890,default,192.168.1.126,"1002 ext",true
1003,1003,Stanley Pines,1003,arcadeparty,1234567890,default,192.168.1.126,"1003 ext",true

Add extension manually (slower)

  1. Go to Accounts > Extensions

  2. Click on ADD

EXAMPLE
Extension: 1000
Range: 1
Voicemail Password: 1000
Account Code:
Device Provisioning:
Effective Caller ID Name: Dipper Pines
Effective Caller ID Number: 1000
Outbound Caller ID Name: arcadeparty
Outbound Caller ID Number: 1234567890
Emergency Caller ID Name:
Emergency Caller ID Number:
Directory Full Name: Dipper Pines
Directory Visible: True
Directory Extension Visible: True
Max Registrations:
Limit Max: 5
Limit Destination:
Voicemail Enabled:
Voicemail Mail To :
Transcription Enabled: False
Voicemail File: Audio File Attachment
Voicemail Keep Local: True
Missed Call:
Toll Allow:
Call Timeout: 30
Call Group:
Call Screen: False
Record: Disabled
Hold Music: Default
Type: Default
Domain:
Context:
Description: ext 4000

Add custom bind action

  1. Go to Dialplan > Dialplan Manager

  2. In the searchbar, Search for bind

  3. Select bind_digit_action

  4. Click on COPY then click CONTINUE

  5. Open the copy of bind_digit_action

EXAMPLE
Name: custom_bind_action
Number:
Hostname:
Context: global
Continue: True
Order: 085
Destination: False
Domain: Global
Enabled: True
Description: custom bind action

  1. On the second Data field below local paste the following:
local,*7,exec:execute_extension,park+7000 XML ${context},${bind_target},${bind_target}
  1. Set Tag to Action

  2. Set Type to bind_digit_action

  3. Set Order to 52

Add custom park

  1. Go to Dialplan > Dialplan Manager

  2. In the searchbar, Search for park

  3. for valet_park set it to False by clicking on True

  4. Select valet_park_in and valet_park_out

  5. Click on COPY then click CONTINUE

  6. Open the copy of valet_park_in

EXAMPLE
Name: custom_park_in
Number: 7000
Hostname:
Context: global
Continue: False
Order: 470
Destination: False
Domain: Global
Enabled: True
Description: custom park in

  1. For destination_number under data paste the following:
^(park\+)?(7000)$
  1. For valet_park under data paste the following:
7000@${domain_name} auto in 7001 7099
  1. for valet_announce_slot=disable under Enabled set it to False

  2. Click on SAVE

  3. Go to Dialplan > Dialplan Manager

  4. In the searchbar, Search for park

  5. Open the copy of valet_park_out

EXAMPLE
Name: custom_park_out
Number: 7001-7099
Hostname:
Context: global
Continue: False
Order: 475
Destination: False
Domain: Global
Enabled: True
Description: custom park out

  1. For destination_number under data paste the following:
^(?:park\+)?(70[0-9][0-9])$
  1. For valet_park under data paste the following:
7000@${domain_name} $1
  1. Click on SAVE

Add custom page

  1. Go to Dialplan > Dialplan Manager

  2. In the searchbar, Search for page

  3. Select page-extension

  4. Click on COPY then click CONTINUE

  5. Open the copy of page-extension

EXAMPLE
Name: custom-page-extension
Number: 8ext
Hostname:
Context:
Continue: False
Order: 250
Destination: False
Domain:
Enabled: True
Description: custom page extension

  1. For Destination_number under data paste the following:
^\8(\d{2,7})$
  1. for pin_number= under Enabled set it to False

  2. Click on SAVE

  3. Go to Dialplan > Dialplan Manager

  4. In the searchbar, Search for page

  5. For page-extension set it to False by clicking on True

Troubleshooting

Always check the logs

sudo tail -f /var/log/freeswitch/freeswitch.log

User has not been found

Error code: UNALLOCATED_NUMBER or User has not been found

  1. Go to Advanced > Access Controls

  2. Open providers

  3. Remove the node

Reset admin password

cd /etc/fusionpbx
mv config.conf config1.conf
cat config1.conf | grep password

Reset SSL

Errors like ERR_CONNECTION_REFUSED can be fixed by reverting all SSL changes to default

rm -rf /usr/local/sbin/dehydrated && rm -rf /usr/src/dehydrated && rm -rf /etc/dehydrated && rm -rf /usr/src/dns-01-manual && rm -rf /var/www/dehydrated && rm -rf /etc/nginx/sites-available/fusionpbx && touch /etc/nginx/sites-available/fusionpbx
nano /etc/nginx/sites-available/fusionpbx

Copy paste the following fusionpbx nginx config file:

server {
	listen 127.0.0.1:80;
	server_name 127.0.0.1;
	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	client_max_body_size 80M;
	client_body_buffer_size 128k;

	location / {
		root /var/www/fusionpbx;
		index index.php;
	}

	location ~ \.php$ {
		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
		#fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi_params;
		fastcgi_param   SCRIPT_FILENAME /var/www/fusionpbx$fastcgi_script_name;
	}

	# Allow the upgrade routines to run longer than normal
	location = /core/upgrade/index.php {
		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
		#fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi_params;
		fastcgi_param   SCRIPT_FILENAME /var/www/fusionpbx$fastcgi_script_name;
		fastcgi_read_timeout 15m;
	}

	# Disable viewing .htaccess & .htpassword & .db & .git
	location ~ .htaccess {
		deny all;
	}
	location ~ .htpassword {
		deny all;
	}
	location ~^.+.(db)$ {
		deny all;
	}
	location ~ /.git/ {
		deny all;
	}
}

server {
	listen 80;
	server_name fusionpbx;

	#redirect letsencrypt to dehydrated
	location ^~ /.well-known/acme-challenge {
		default_type "text/plain";
		auth_basic "off";
		alias /var/www/dehydrated;
	}

	#rewrite rule - send to https with an exception for provisioning
	if ($uri !~* ^.*(provision|xml_cdr).*$) {
		rewrite ^(.*) https://$host$1 permanent;
		break;
	}

	#REST api
	if ($uri ~* ^.*/api/.*$) {
		rewrite ^(.*)/api/(.*)$ $1/api/index.php?rewrite_uri=$2 last;
		break;
	}

	#algo
	rewrite "^.*/provision/algom([A-Fa-f0-9]{12})\.conf" /app/provision/?mac=$1&file=algom%7b%24mac%7d.conf last;

	#mitel
	rewrite "^.*/provision/MN_([A-Fa-f0-9]{12})\.cfg" /app/provision/index.php?mac=$1&file=MN_%7b%24mac%7d.cfg last;
	rewrite "^.*/provision/MN_Generic.cfg" /app/provision/index.php?mac=08000f000000&file=MN_Generic.cfg last;

	#grandstream
	rewrite "^.*/provision/cfg([A-Fa-f0-9]{12})(\.(xml|cfg))?$" /app/provision/?mac=$1;
	rewrite "^.*/provision/pb/([A-Fa-f0-9]{12})/phonebook\.xml$" /app/provision/?mac=$1&file=phonebook.xml; 
	#grandstream-wave softphone by ext because Android doesn't pass MAC.
	rewrite "^.*/provision/([0-9]{5})/cfg([A-Fa-f0-9]{12}).xml$" /app/provision/?ext=$1;

	#aastra
	rewrite "^.*/provision/aastra.cfg$" /app/provision/?mac=$1&file=aastra.cfg;
	#rewrite "^.*/provision/([A-Fa-f0-9]{12})(\.(cfg))?$" /app/provision/?mac=$1 last;

	#yealink
	#rewrite "^.*/provision/(y[0-9]{12})(\.cfg|\.boot)?$" /app/provision/index.php?file=$1$2;
	rewrite "^.*/provision/(y[0-9]{12})(\.cfg)?$" /app/provision/index.php?file=$1.cfg;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})(\.(xml|cfg))?$" /app/provision/index.php?mac=$1 last;

	#polycom
	rewrite "^.*/provision/000000000000.cfg$" "/app/provision/?mac=$1&file={%24mac}.cfg";
	#rewrite "^.*/provision/sip_330(\.(ld))$" /includes/firmware/sip_330.$2;
	rewrite "^.*/provision/features.cfg$" /app/provision/?mac=$1&file=features.cfg;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-sip.cfg$" /app/provision/?mac=$1&file=sip.cfg;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-phone.cfg$" /app/provision/?mac=$1;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-registration.cfg$" "/app/provision/?mac=$1&file={%24mac}-registration.cfg";
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-directory.xml$" "/app/provision/?mac=$1&file={%24mac}-directory.xml";

	#cisco
	rewrite "^.*/provision/file/(.*\.(xml|cfg))" /app/provision/?file=$1 last;

	#Escene
	rewrite "^.*/provision/([0-9]{1,11})_Extern.xml$"       "/app/provision/?ext=$1&file={%24mac}_extern.xml" last;
	rewrite "^.*/provision/([0-9]{1,11})_Phonebook.xml$"    "/app/provision/?ext=$1&file={%24mac}_phonebook.xml" last;

	#Vtech
	rewrite "^.*/provision/VCS754_([A-Fa-f0-9]{12})\.cfg$" /app/provision/?mac=$1;
	rewrite "^.*/provision/pb([A-Fa-f0-9-]{12,17})/directory\.xml$" /app/provision/?mac=$1&file=directory.xml;

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	client_max_body_size 80M;
	client_body_buffer_size 128k;

	location / {
		root /var/www/fusionpbx;
		index index.php;
	}

	location ~ \.php$ {
		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
		#fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi_params;
		fastcgi_param   SCRIPT_FILENAME /var/www/fusionpbx$fastcgi_script_name;
	}

	# Allow the upgrade routines to run longer than normal
	location = /core/upgrade/index.php {
		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
		#fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi_params;
		fastcgi_param   SCRIPT_FILENAME /var/www/fusionpbx$fastcgi_script_name;
		fastcgi_read_timeout 15m;
	}

	# Disable viewing .htaccess & .htpassword & .db & .git
	location ~ .htaccess {
		deny all;
	}
	location ~ .htpassword {
		deny all;
	}
	location ~^.+.(db)$ {
		deny all;
	}
	location ~ /.git/ {
		deny all;
	}
}

server {
	listen 443 ssl;
	server_name fusionpbx;

	ssl_certificate         /etc/ssl/certs/nginx.crt;
	ssl_certificate_key     /etc/ssl/private/nginx.key;
	ssl_protocols           TLSv1 TLSv1.1 TLSv1.2;
	ssl_ciphers             HIGH:!ADH:!MD5:!aNULL;
	#ssl_dhparam

	#redirect letsencrypt to dehydrated
	location ^~ /.well-known/acme-challenge {
		default_type "text/plain";
		auth_basic "off";
		alias /var/www/dehydrated;
	}

	#REST api
	if ($uri ~* ^.*/api/.*$) {
		rewrite ^(.*)/api/(.*)$ $1/api/index.php?rewrite_uri=$2 last;
		break;
	}

	#message media
	rewrite "^/app/messages/media/(.*)/(.*)" /app/messages/message_media.php?id=$1&action=download last;

	#algo
	rewrite "^.*/provision/algom([A-Fa-f0-9]{12})\.conf" /app/provision/?mac=$1&file=algom%7b%24mac%7d.conf last;

	#mitel
	rewrite "^.*/provision/MN_([A-Fa-f0-9]{12})\.cfg" /app/provision/index.php?mac=$1&file=MN_%7b%24mac%7d.cfg last;
	rewrite "^.*/provision/MN_Generic.cfg" /app/provision/index.php?mac=08000f000000&file=MN_Generic.cfg last;

	#grandstream
	rewrite "^.*/provision/cfg([A-Fa-f0-9]{12})(\.(xml|cfg))?$" /app/provision/?mac=$1;
	rewrite "^.*/provision/pb/([A-Fa-f0-9]{12})/phonebook\.xml$" /app/provision/?mac=$1&file=phonebook.xml; 
	#grandstream-wave softphone by ext because Android doesn't pass MAC.
	rewrite "^.*/provision/([0-9]{5})/cfg([A-Fa-f0-9]{12}).xml$" /app/provision/?ext=$1;

	#aastra
	rewrite "^.*/provision/aastra.cfg$" /app/provision/?mac=$1&file=aastra.cfg;
	#rewrite "^.*/provision/([A-Fa-f0-9]{12})(\.(cfg))?$" /app/provision/?mac=$1 last;

	#yealink
	#rewrite "^.*/provision/(y[0-9]{12})(\.cfg|\.boot)?$" /app/provision/index.php?file=$1$2;
	rewrite "^.*/provision/(y[0-9]{12})(\.cfg)?$" /app/provision/index.php?file=$1.cfg;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})(\.(xml|cfg))?$" /app/provision/index.php?mac=$1 last;

	#polycom
	rewrite "^.*/provision/000000000000.cfg$" "/app/provision/?mac=$1&file={%24mac}.cfg";
	#rewrite "^.*/provision/sip_330(\.(ld))$" /includes/firmware/sip_330.$2;
	rewrite "^.*/provision/features.cfg$" /app/provision/?mac=$1&file=features.cfg;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-sip.cfg$" /app/provision/?mac=$1&file=sip.cfg;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-phone.cfg$" /app/provision/?mac=$1;
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-registration.cfg$" "/app/provision/?mac=$1&file={%24mac}-registration.cfg";
	rewrite "^.*/provision/([A-Fa-f0-9]{12})-directory.xml$" "/app/provision/?mac=$1&file={%24mac}-directory.xml";

	#cisco
	rewrite "^.*/provision/file/(.*\.(xml|cfg))" /app/provision/?file=$1 last;

	#Escene
	rewrite "^.*/provision/([0-9]{1,11})_Extern.xml$"       "/app/provision/?ext=$1&file={%24mac}_extern.xml" last;
	rewrite "^.*/provision/([0-9]{1,11})_Phonebook.xml$"    "/app/provision/?ext=$1&file={%24mac}_phonebook.xml" last;

	#Vtech
	rewrite "^.*/provision/VCS754_([A-Fa-f0-9]{12})\.cfg$" /app/provision/?mac=$1;
	rewrite "^.*/provision/pb([A-Fa-f0-9-]{12,17})/directory\.xml$" /app/provision/?mac=$1&file=directory.xml;

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	client_max_body_size 80M;
	client_body_buffer_size 128k;

	location / {
		root /var/www/fusionpbx;
		index index.php;
	}

	location ~ \.php$ {
		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
		#fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi_params;
		fastcgi_param   SCRIPT_FILENAME /var/www/fusionpbx$fastcgi_script_name;
	}

	# Allow the upgrade routines to run longer than normal
	location = /core/upgrade/index.php {
		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
		#fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi_params;
		fastcgi_param   SCRIPT_FILENAME /var/www/fusionpbx$fastcgi_script_name;
		fastcgi_read_timeout 15m;
	}

	# Disable viewing .htaccess & .htpassword & .db & .git
	location ~ .htaccess {
		deny all;
	}
	location ~ .htpassword {
		deny all;
	}
	location ~^.+.(db)$ {
		deny all;
	}
	location ~ /.git/ {
		deny all;
	}
}
sudo systemctl restart nginx

Still not working? try install script (there should be no data loss when reinstalling)

cd /usr/src/fusionpbx-install.sh/ubuntu && ./install.sh

Connect a phone to FusionPBX

Android client - Linphone

  1. Install Linephone:
    https://play.google.com/store/apps/details?id=org.linphone&hl=en

  2. Add an account, select: Use a third party SIP account

  3. Below is a example account that can be used if you followed the extension import then click on login

Username: 1000
Password: 1000
Domain: 192.168.1.126
Display Name: Dipper Pines
Transport: UDP

iOS client - Linphone

  1. Install Linephone:
    https://apps.apple.com/us/app/linphone/id360065638

  2. click on Assistant and select: USE SIP ACCOUNT

  3. Below is a example account that can be used if you followed the extension import then click on login

Username: 1003
Password: 1003
Domain: 192.168.1.126
Display Name: Stanley Pines
Transport: UDP

Why FusionPBX?

Ansible

Install Ansible default

sudo apt-add-repository ppa:ansible/ansible
sudo apt update && sudo apt install ansible
cd /etc/ansible/
nano ansible.cfg
[defaults]
inventory=hosts
host_key_checking=false
sudo nano /etc/ansible/hosts
[master]
192.168.x.x # or hostname

[nodes]
192.168.x.x # or hostname


[master:vars]
ansible_ssh_user=master
ansible_ssh_pass=master

[nodes:vars]
ansible_ssh_user=myusername
ansible_ssh_pass=MysuporS3cretP@ass

Test the connection

ansible all -m ping

Configure Ansible

cd /etc/ansible/
mkdir playbooks && cd "$_"
nano apt-update.yml
- name: Perform apt update
  hosts: all
  become: yes
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
      register: apt_update_result

    - name: Display apt update result
      debug:
        var: apt_update_result

The following apt-update.yml is a example that will send a webhook message to discord

- name: Perform apt update
  hosts: all
  become: yes
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
      register: apt_update_result

    - name: Display apt update result
      debug:
        var: apt_update_result

    - name: Get current timestamp
      set_fact:
        timestamp: "{{ ansible_date_time.iso8601 }}"


    - name: Send message to Discord channel
      community.general.discord:
        webhook_id: "12345678901234567890"
        webhook_token: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        username: Ansible
        avatar_url: "https://i.imgflip.com/5o55va.png"
        embeds:
          - title: "APT Update Completed"
            description: "At server: {{ inventory_hostname }}"
            color: 0x0dc300
            footer:
              text: "Author: Ansible"
            image:
              url: "https://media.tenor.com/7kYaaqzfDG0AAAAd/yes-guy.gif"
            timestamp: "{{ ansible_date_time.iso8601 }}"
      when: apt_update_result.changed



ansible-playbook apt-update.yml

Install Ansible with python

Make sure you have python3 and pip installed

sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.10
sudo apt install python3-pip
python3 -m pip install --user ansible
python3 -m pip install --upgrade --user ansible

Configure Ansible with python

cd /home/myusername/
mkdir ansible && cd "$_"
nano ansible.cfg
[defaults]
inventory=hosts
host_key_checking=false
nano hosts
[master]
192.168.x.x # or hostname

[nodes]
192.168.x.x # or hostname


[master:vars]
ansible_ssh_user=master
ansible_ssh_pass=master

[nodes:vars]
ansible_ssh_user=myusername
ansible_ssh_pass=MysuporS3cretP@ass

Test the connection

ansible all -m ping

Terraform

sudo apt install unzip
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

Configure Terraform

This is a example container just so you can see how powerfull and easy terraform is

cd /home/myusername/terraform
mkdir homepage && cd "$_"
nano homepage.tf
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "3.0.2"
    }
  }
}

provider "docker" {}

resource "docker_container" "homepage" {
  image   = "ghcr.io/benphelps/homepage:latest"
  name    = "hpage"
  ports {
  internal = 3000
  external = 8101
  }
  restart = "unless-stopped"
  
  
  volumes {
    container_path = "/app/config"
    host_path      = "/home/myusername/terraform/homepage/config"
  }

  volumes {
    container_path = "/app/public/icons"
    host_path      = "/home/myusername/terraform/homepage/icons"
  }

  volumes {
    container_path = "/app/public/images"
    host_path      = "/home/myusername/terraform/homepage/images"
  }

  volumes {
    container_path = "/var/run/docker.sock"
    host_path      = "/var/run/docker.sock"
    read_only      = true
  }
}
terraform init
terraform apply

Webmin

curl -o setup-repos.sh https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh
sh setup-repos.sh

Configure Webmin

Change port

Replace NEW_PORT_NUMBER with any port you like

sudo sed -i 's/^port=.*/port=NEW_PORT_NUMBER/' /etc/webmin/miniserv.conf && sudo systemctl restart webmin