Posts created by “Georgi Stefkoff”

Building CI/CD deployment with GitLab, Part 2: Setup Repository

Written by Georgi Stefkoff

This is the second part from the series of "Building CI/CD deployment with GitLab". If you haven't read the introduction blog, you check it here.

The plan

Here is an overview of what we will cover in this blog post:

  1. Set up the repository in GitLab.

  2. Cloning it locally

  3. Push the base branches

  4. Set up branch permission

Requirements

I assume that you already have a private GitLab repository (self-hosted or cloud-managed).

All the dev work will be done in an Ubuntu Desktop machine, but since we will be using mostly NodeJS, the commands will be valid for Windows systems also.

In this blogs post, we will not be focused over the development, so you can choose your favorite code editor or IDE. You can also may use Vim for editing the files (if you know how to exit after :) ).

Step 1: Set up the repository

The first this is to log in to your GitLab instance and create a new Blank Project:

On the next screen, enter the following details for your project:

  • Project Name - name of your project
  • Select the owner of the project - in my case is stefkoff
  • Project Slug - the slug will be automatically generated as you type the Project Name, but you can also change

it to something else

  • Visibility Level - choose whatever the project should be visible to unauthorized user, or to be private
  • Initialize repository with README - check the box, so GitLab will auto generate a README.md file for you

The rest of the fields are optional, and you can leave them empty of fill them with the desired information

Click on "Create Project" button, and you will be redirected to the repository home page:

Optional: Disable Auto DevOps:

In GitLab, Auto DevOps feature can be enabled for the newly created project. You will want to disable this feature, in order so save you pipeline execution time.

Auto DevOps will basically scan your code and build a pipeline that will test your code and scan for vulnerabilities.

You can disable "Auto DevOps" for the given project by going clicking the "Settings" button that is presented on the top of the page. It will lead you to the project's settings page, where you should click the "Expand" button in the section Auto DevOps

Then just disable the checkbox Default to Auto DevOps pipeline and save the changes.

*Note that, you can see from the screenshot above, that the Auto DevOps is enabled on instance lever. In order to disable it for all feature project, you need to have an admin access and configure this in the admin settings for the GitLab instance

Step 2: Clone the repository locally

In order to do some changes to the repository, we have to clone it locally, commit our changes and push the local commits to the origin server.

In order to do all of this, you need to have git installed locally. For Ubuntu, you can install it with the following commands:

Update the local package index

sudo apt update

Install git

sudo apt install git

Validate the installation:

git --version

If git was successfully installed, then from the last command you have to see the installed version.

Now when we have git installed, we have to clone the repository. Find some folder and execute the following command:

git clone git@gitlab.server.tld:username/project-name (replace the actual SSH from your GitLab project)

*Note: If you created your project as Private, you will not be able to clone the repository without providing an Authorization key and configure the GitLab user to have the copy of the public fingerprint of your Authorization key

Now, enter to the project directory, that was automatically created when you issue the clone command

cd awesome-project

If you list the directory content, you will see only one file README.md that was created by GitLab.

Step 3: Create the base branches

Now, we will create the two main branches that were required in our planning: staging and production

First create the staging branch:

git checkout -b staging

By the combination checkout -b <branch_name>, git will create a new branch and switch to it.

Now, push the staging branch to the remote:

git push -u origin staging

Now, we have to do the same for the production branch. *Note: we will start the production from the staging branch, since this will be desired workflow later. At this stage, it does not matter from where you will start the production branch, since we have only one file.

git checkout -b production

git push -u origin production

Now, if you open the Branches page (Repository page -> Repository Tab -> Branches), then you will see all the tree branches:

Step 4: Set up the branch permissions

As I explained in my Introduction blog post for these series, we will apply the following rules:

  • branches: staging and production can be pushed ONLY by Merge Request
  • all other can be pushed by anyone

In order to configure the Branch Permissions, you have to go to Project page -> Settings -> Repository and expand the Protected Branches section:

From there fill the form for the staging branch first:

  • Branch: staging
  • Allowed to merge: Developers + Maintainers
  • Allowed to push: No one
  • Allow to force push: No

Then hit the "Protect" button:

Now do the same thing for the production branch

Next

On the next blog post, we will actually write some basic apps and push them to the repository.

Building CI/CD deployment with GitLab, Part 1: Introduction

Written by Georgi Stefkoff

Introduction

If you are working alone on the development process, pushing directly to the production server is not so bad. Of course, if all tests are passing before this. But working in a large team (4+ developers), first will lead to many-many merge conflicts, and the track for the deployments will be lost, if there is not properly configured CI/CD workflow.

Honestly, in my personal experience I do not like when I hear: Fix the bug and push ASAP!. We are all humans and we make mistakes. Even a small typo could lead that, the clients will not have trust in the software that we are delivering.

Having a good CI/CD workflow, together with automated tests and manual QA testing, will ensure that the software is properly working as the client's expectations.

NOTE: in the entire series, I will not going to talk about securing the application. I'm planning to write a blog for the securing application, but in order to keep this thread not so annoying, I will write this in a completely separate blog post

In the next few posts, I'm going to show you how you can automate the testing and the deployment of your application.

CI/CD is a key concept these days for good software development. Managing good Continues Integrations (CI) and allowing your QAs to test the fixes that are pushed by the developer that can be encapsulated from other unnecessary pieces that do not have to be deployed at the given stage. Continues Delivery (CD) on another hand will help you mentaining a stable production state and not just push any fix to production and loosing track on the deployment.

In the next few series of posts, I will show you how we can automate all of this with GitLab and their powerful pipelines. We will use some Docker containers for the staging servers.

What we will do?

We will play a simple scenario of CI/CD of a small frontend app + node.js as backend server, MongoDB as a database, and an NGINX server as a reverse proxy server to all of this.

On the end we should be able to play the following scenario:

  1. The developer pushes a fix to a feature branch.
  2. A CI pipeline will be executed on the previous push to validate that all tests are passing.
  3. The developer will create a Merge Request (MR) to the staging branch
  4. A CI pipeline will be executed and will build an encapsulated staging server for the possible merged fix ONLY and the QAs will receive a link to the staging environment
  5. QAs will test the fix and will approve the merge request.
  6. Once a week, a weekly release will be initiated from the state of the staging branch and the MR will be created from the staging branch into the weekly
  7. CI will again be executed to prepare an encapsulated staging server with all of the weekly fixes. QAs will receive the weekly staging link to test the fixes
  8. QAs will test the fixes and will approve the merge request
  9. When the weekly release is tested and approved, it will be merged by a CD pipeline to the production branch and will be deployed to the production server

There will be another workflow of the hotfixes that needs to go directly to the production as soon as possible:

  1. Developer pushes a fix to the hotfix branch
  2. A CI pipeline will be executed on the pushed branch to validate that all tests are passing
  3. The developer will create the MR into the production branch
  4. A CI pipeline will be executed and will build an encapsulated staging server for the possible merged fix ONLY and the QAs will receive a link to the staging environment
  5. QAs will test the fix and will approve the MR
  6. A CD pipeline will be executed and the production server will be deployed with the merged fix

Branch model

We are going to use mainly 5 branches:

  1. Feature branch: named like feature/ISSUE_KEY

  2. Hotfix branch: named like hotfix/ISSUE_KEY

  3. Staging branch: named staging

  4. Weekly release branch: named like weekly/VERSION_NUMBER

  5. Production branch: named production

Branch permissions

We will have strict branch permissions to be sure that no one could push untested code, or code that didn't go through the CI/CD pipeline. Here are the branch permissions that we will have:

  • Everyone can push to the feature or hotfix branches

  • staging, weekly and production branches can be pushed ONLY by MR

What servers we will use?

We will need the following servers (or a VMs) to do all of the desired workflow:

  1. GitLab server - self-managed or cloud-managed.

  2. Ubuntu server that we will use for the dynamic staging environments. There we will install docker and we will use docker composer to build encapsulated staging environments that will contain only the provided fix.

  3. Production server - we will have a production server. Ideally, this could be a combination of 4 separate servers (one for FE, one for BE, one for the proxy, and one from the DB). Also, this could be done by kubernetes, but this will not be covered in these series. We will deploy all of the apps on a single server, to focus on the CI/CD and not on the correct infrastructure.

What will follow

I will divide the tutorial into the following posts to encapsulate the tools that we will do:

  1. Configure GitLab and the repository settings
  2. Configure the Apps (FE, BE, DB, proxy). Here we will configure the docker-compose instruction that will help us with the dynamic staging environments
  3. Configure that staging server, where the dynamic staging environments will be hosted
  4. Configure the production server
  5. Configure the CI/CD pipelines

You can proceed with the next step Building CI/CD deployment with GitLab, Part 2: Setup Repository

Build home VoIP server with asterisk

Written by Georgi Stefkoff

Background

Have you ever want to build a home VoIP system? I will tell you - you can do this and you do not have to know everything about SIP, trunking, VLAN, QoS and so many network principals in order to build a small home VoIP server. You can follow this article and build your own VoIP server (as I did a my home) using asterisk as a PBX and two very cheap old VoIP phones (2 SwissVoice IP10S, at price for $5 each). Of course you will need a router or a switch at least, but I supposed that you already have a SOHO router running DHCP server.

Inspiration

My inspiration for this project comes from the fact that most of the time, I'm at my basement, and my wife have to call me. Of course she could call me by the phone or using FaceTime or Messager, but there is no fun about this :) . btw, my wife do not like the hole idea, but she just do not understand the effort

Equipment

As I mentioned above, I will use my home router (Mikrotik RB3100), but this is not too important, if your router supports DCHP server. You can use a regular managed or unmanaged switch, but you have to configure all IP address manually and gateways.

Also, I will be using two SwissVoice IP10S VoIP phones:

These phones a quite old, and there could be some issues with the connectivity. Currently, I have a small issue related to the noise cancellation of these phones, but for home usage you can skip a single character from your wife.

If you can not find any cheap VoIP phones, and you do not invest some money in old hardware, you can still test the installation with one PC (Laptop) and a smartphone. I have played the same scenario with running Windows 11 and MiroSIP and iPhone 14 with installed Zoiper. They both support SIP protocol and you can use them. If you are using an Android, you can search at the Google Play Store for a SIP and find any suitable app that can do the job.

Requirements

For this tutorial, you will need a Linux machine (physical or virtual) with running Ubuntu 20.04 or higher

Installation

Note: In this installation, we will install asterisk from the public apt repository. This meanst that the asterisk will be precompiled for your system and it will be installed as it was compiled. This is not the recommended way, but for the sake of the tutorial and the home usage, I will stay with this option. The better way is to download the source code and compile it on your system with all of the libraries. In this way, you can include additional modules, like Bluetooth trunk and so on. Later I will add another article of how you will do this.

  1. Update and upgrade the system
sudo apt update
sudo apt upgrade
  1. Install asterisk

sudo apt install asterisk

  1. Validate asterisk is working

Check the systemd service:

sudo systemctl status asterisk

Simple output:

● asterisk.service - Asterisk PBX
     Loaded: loaded (/lib/systemd/system/asterisk.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2024-04-26 13:11:01 UTC; 6h ago
       Docs: man:asterisk(8)
   Main PID: 16825 (asterisk)
      Tasks: 70 (limit: 9388)
     Memory: 45.3M
        CPU: 3min 59.898s
     CGroup: /system.slice/asterisk.service
             ├─16825 /usr/sbin/asterisk -g -f -p -U asterisk
             └─16826 astcanary /var/run/asterisk/alt.asterisk.canary.tweet.tweet.tweet 16825

If you want to enable asterisk service to run on system startup, execute the following command:

sudo sytemctl enable asterisk

Start the asterisk console:

sudo asterisk -rvvv

Simple output:

Asterisk 18.10.0~dfsg+~cs6.10.40431411-2, Copyright (C) 1999 - 2021, Sangoma Technologies Corporation and others.
Created by Mark Spencer <markster@digium.com>
Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.
This is free software, with components licensed under the GNU General Public
License version 2 and other licenses; you are welcome to redistribute it under
certain conditions. Type 'core show license' for details.
=========================================================================
Connected to Asterisk 18.10.0~dfsg+~cs6.10.40431411-2 currently running on stefkoff-voip-server (pid = 16825)
stefkoff-voip-server*CLI> 

From this console you can various things like showing useful information about the SPI peers, dial plan and so on.

Configure SIP peers

In order the VoIP phone to connect to your PBX, we have to configure the SIP peers first.

  1. Create a backup of /etc/asterisk/sip.conf

sudo cp /etc/asterisk/sip.conf /etc/asterisk/sip.conf.bak

  1. Edit /etc/asterisk/sip.conf

sudo vim /etc/asterisk/sip.conf

I will suggest you to remove all of the content, if you are not familiar with asterisk and add the new one. That is why we create the backup file.

Add the following likes to the file: NOTE: ; is a comment line you can skip it you your file. It is just for explanation.

[general]
; almost everything uses a context. Here we specify the context for the general configuration. This context is very important, and if you mix it, asterisk may not work as expected. 
context=internal
; do not allow unauthorized user (not mentioned above) to connect to the system
allowguest=no
; RFC3578 overlap dialing support
allowoverlap=no
; UDP port to listen for SIP connections
bindport=5060
; address to listen at. You can set 0.0.0.0 in order to listen from all interfaces. Here I've configured to listen to a specific IP address, which is assigned to the NIC of the virtual server
bindaddr=192.168.88.43
; disable DNS SRV lookups on outbound calls
srvlookup=no
; first disallow all codecs
disallow=all
; Allow codecs in order of preference
allow=ulaw
; the simple explanation of this option is to reject the unauthorized request, without telling the reason
alwaysauthreject=yes
; Do not directly connect the RTP session between the peers. This options is very important, if one of the peers is behind a NAT. With setting this option to yes, the connection may not be established properlly
directmedia=no
; do use nat, if case when all devices are on the same bridge domain. If one of them is from another network, the you should probably set it to auto_force_rport
nat=no
; do not use session timers. All devices will have to handle the reconnect. If you plan to build a dynamic clients, then probably you will need to configure session timers and proper expiration of the peers
session-timers=refuse
; specify the local network IP address range. This is used for the NAT. Here we set it to no, but it is good so asterisk knows which address are on the same network
localnet=192.168.88.0/255.255.255.0

; [7003] is the SIP username
[7003]
; friend means that asterisk will accept all incoming calls, when the authentication is valid
type=friend
; dynamic means that the peer will request it present by himself and it IP address will be learned
host=dynamic
; secret of the user. Here I've set the same as the username, but in real life, this is not a good idea
secret=7003
; again, specify the context that this peer will be valid. Note that this is the same context as above
context=internal

; This is the same as above, but with different username
[7004]
type=friend
host=dynamic
secret=7004
context=internal

As you can see, I give a brief explanation of what each option is doing. It is good to consult with the docs about the rest of the options. btw, you can check all options in the original sip.conf file, that we make a backup from it

  1. Edit /etc/asterisk/extensions.conf

Now, we are going to configure our dial plan. The dial plan is a set of rules of how the SIP peers will dial a different extension.

First, as before, create a backup for the extensions file:

sudo cp /etc/asterisk/extensions.conf /etc/asterisk/extensions.conf.bak

Open the extensions file:

sudo vim /etc/asterisk/extensions.conf

Remove all content (as before in sip.conf) and add the following lines:

; context again. We have to match the same context that the SIP user is configured, otherwise, the dial plan wont be executed for the given user
[internal]
; `exten` is shorten for extension
; first we specify the extension number (7003 in this case) Note that, here this matches the same as the username of the SIP peer. You can give another extension you want
; 1 means that this is the first step for the given dial plan
; here we first accept the dial first, before redirecting to anywhere. Note that, answer do not means that the user dialing will be answered, but it is like a entry point when you will redirect the dial plan
exten => 7003,1,Answer()
; Dial(SIP/7003,60) redirect the call to SIP peer 7003. Here you can see that the extension can be different from the SIP peer username
; second parameter is the timeout of the dial. If the timeout is reached, the next operation will be executed.
exten => 7003,2,Dial(SIP/7003,60)
; if there is no answer, we will play a "Nobody available" sound. Note: there are a lot of precompiled sounds in asterisk
exten => 7003,3,Playback(vm-nobodyavail)
; next, open the user's mailbox so the dialer can leave a message to the user
exten => 7003,4,VoiceMail(7003@voip)
; end the call
exten => 7003,5,Hangup()

; the following section is the same as in above, but with different extension and SIP peer
exten => 7004,1,Answer()
exten => 7004,2,Dial(SIP/7004,60)
exten => 7004,3,Playback(vm-nobodyavail)
exten => 7004,4,VoiceMail(7004@voip)
exten => 7004,5,Hangup()

; this is an another dial plan that the user 7003 can open his Voice Mail.
; when the user dial 8003, there will be some messages, and the user needs to follow the instruction in order to manipulate the VoiceMail
; note about @voip. Here we specify the context of the voice mail. You will see this in the next item
exten => 8003,1,VoicemailMain(7003@voip)
exten => 8003,2,Hangup()

; same as the previous VoiceMail, but for peer 7004
exten => 8004,1,VoicemailMain(7004@voip)
exten => 8004,2,Hangup()
  1. Edit /etc/asterisk/voicemail.conf

Now, we will configure our voice mail

Add the following lines at the end of the file:

; context
[voip]
; SIP username
; the format is following:
; mailbox => password[,FirstName LastName[,email addr[,pager addr[,options[|options]]]]]
; you can configure a password for the voice mail, and once the user dial the voice mail extension, he will be prompted to enter the password first through the keypad
; in our case we do not have any password, because I do not manage to enter any password from IP10S phones. This will be the case of my next investigation
7003 =>
; SIP username
7004 =>
  1. Edit /etc/asterisk/modules.conf

When I build my asterisk PBX, I have to disable few modules, because I could not run the VoiceMail. You can add the following lines to the end of the file or if they are already there, you can skip this step

Open /etc/asterisk/modules.conf

sudo vim /etc/asterisk/modules.conf

Add the following lines:

noload => app_voicemail_imap.so
noload => app_voicemail_odbc.so

With these lines, we are telling that the VoiceMail will not have to read or send the voice mail to some mail or database. Only physical recordings will be used.

  1. Reload asterisk

First enter in asterisk console:

sudo asterisk -r

then execute the reload command:

reload

Test it

So far, so good. Until now, we should have a working asterisk setup and VoIP server. Now it is time to configure the IP10S phones.

  1. Power your phones

In my case, when I purchased my phones, they come without a power jack (that is why it was just $5 each). The good part is that the most of the VoIP phones supports PoE (802.3af). I've brought a PoE injector and powered my phones directly.

  1. Wait for the IP connectivity

Once you power on you phones, they will try to assign their IP address by DHCP server. This should take some time, but at the end it should be all connected.

  1. Configure the SIP connection

First, find the IP address of you phones. Once you know them, go one by one and configure the SIP connection:

First, open the built-in web interface of the phone: In my case it was at http://192.168.88.18

Then you click on the Administrator link. You will be prompted for a username and password. You can try the default one: admin:admin.

On the left menu click on SIP Configuration.

Add the following settings, that you have configured in your sip.conf file accordingly:

Save the configuration.

You are ready to test.

Now, when you dial one of the phone to the another, you have to be able to make a call.

Can I go to production with this?

Using asterisk for production, depends to your knowledge of asterisk itself, SIP, RTP, etc. There are a lot of prebuild server that can be used for this job, but YES, you can build a configuration that can be used for a small office, even connecting it with a SIP trunk, provided from your mobile operator.

Also, there are a lot of item to consider, when you are building an enterprise VoIP server with asterisk. Here is a some important items to consider:

  1. VLAN - it is a best (and recommended) practice that the VoIP traffic should be separated from the data traffic. In this way, the phone call will not be compromised and will stay at a dedicated VLAN that is only accessible by the VoIP network. For this to happen, you need to have an managed switches and configure the VLAN for each port that a VoIP phone will be connected. Note that, most of the VoIP phones have an extra RJ45 port for connection a computer. This is limit the UTP cables that needs to go for a phone and computer. The idea here is to setup a trunk port for the VoIP phone with the correct tagged VLAN number. VoIP phones, could be configured to be VLAN aware and they can process with the tagged frames. VoIP phones have an internal switch chip that will bridge with the computer NIC. When you configure you switch VLAN trunk port, you can specify the native VLAN that the untagged traffic should belongs. This is because the VoIP phone will tag the traffic from the phone itself with the configured VLAN number, but the computer will leave as untagged and the switch will know what VLAN should bellongs to the untagged frames.

In our example we do not use any VLANs, because this tutorial will be endless if I need to explain what is VLAN and how to configure them.

Note that, if you are not using VLAN tagging, rest of the items will be invalid for improvement.

  1. QoS - VoIP phones that support tagged VLANs can perform a QoS tagging in the 801.1q header. In this way, you can configure you switch to prioritize the outgoing traffic for the phones and leave at least 15MB guarantee traffic (for example) for the VoIP phone network. Imagine that you have a lot of employees and all of them have a VoIP phone and a PC. What will happen when all of them start looking at YouTube vides? Boom. RTP session will have a huge dropping frames, because the switch could not handle all of the traffic and send everything to the uplink. When you configure QoS properly for the VoIP, you will have some guarantee traffic for the RTP session and the switch will send then to the top of the outgoing queue and they will be less congested.

Conclusion

Setting up a VoIP server is not an easy job, when you need to do an enterprise server. Here we have covered a basic setup that will works with two phones. It is really depends on your hardware how many phones you can support: Connectivity speed and CPU. I highly recommend readying Asterisk: The Definitive Guide to learn more about asterisk and see what awesome things you can do with this awesome tool.

Configure Postfix to use SMPT relay when sending emails via PHP mail function

Written by Georgi Stefkoff

Background

Do you ever need to use a SMPT relay when sending email throw PHP mail() function? Probably yes. One way use to use a third party library like phpmailer/phpmailer. This is nice and everything is handled by phpmailer so you do not have to do anything else. But where is the issue? Here: Adding more and more 3rd party libraries increase the application size. In some cases this is a critical and you need to avoid this. And here, I will show you how to configure all of this, in order to save some space.

Requirements

  1. I assume that you have already have a web server with running apache2|nginx and php and the application is up-and-running.
  2. All of the above commands are valid for Ubuntu 18.04 and above
  3. Assuming that you have a mail server like PirvateEmail, MainGun, SendGrid that you want to use it as a relay.

Step 1: Install postfix

First you update packages registry and upgrade the server:

sudo apt update && sudo apt upgrade

Install the libsasl2-modules package:

sudo apt-get install libsasl2-modules

Install Postfix:

sudo apt install postfix

You will be asked to select the environment, and you will want to select Internet Site. Then you will be asked to confirm your hostname. By default it will be populated by the host machine hostname. Make sure you set it as your server FQDM, example: site.domain.tld.

Once postfix is installed, open /etc/postfix/main.cf and change myhostname variable like this:

myhostname = site.domain.tld

For now, save the file. We will return back to it later.

Configure SALS username and password

Usernames and passwords are store in the file /etc/postfix/sasl_passwd. You may not have this file on a fresh installation, but do not worry. We will create it. If it is already existing just append the following content at the end of the file.

Open /etc/postfix/sasl_passwd and add the following rows:

[mail.server.com]:587 username:password

Replace mail.server.com with your mail server. Replace username and password with the credentials that you have for the email server.

NOTE: your mail server could have different port than 587 and you have to be aware of this. By default, Outgoing SMPT port for TLS/STARTTLS is 587

Save the file and exit. Create a Hash database file for Postfix using the postmap command. This command creates a new file named sasl_passwd.db in the /etc/postfix/ directory.

sudo postmap /etc/postfix/sasl_passwd

Secure hashed password (optional)

If you are using multi-user server, and do not want the rest of the users to see your saved sasl passwords, you can follow the steps below in order to restrict the access of the password files.

sudo chown root:root /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo chmod 0600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

Configure the Relay

Now, we will configure postfix to send the Outgoing emails via an email server (relay).

First open the main postfix configuration file /etc/postfix/main.cf

Find the variable relayhost and change it to relayhost = [mail.server.com]:587. If the variable do not exists, add it to the end of the file.

Now add the following lines to the end of the file (in the fresh installation, these variables will probably wont be configured):

# enable SASL authentication
smtp_sasl_auth_enable = yes
# disallow methods that allow anonymous authentication.
smtp_sasl_security_options = noanonymous
# where to find sasl_passwd
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
# Enable STARTTLS encryption
smtp_use_tls = yes
# where to find CA certificates
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

Save the file and restart postfix:

sudo systemctl restart postfix

Override the send email address (optional)

By default, if no sender email is specified, postfix will et the send to be with the user that runs the application that needs to send an email. In your case, most of the time it will be www-data. If your mail server do not have such a user, then an error will be thrown that the user do not have access to send any emails. If you need postfix to override the send email address, follow the steps below:

  1. Open psotfix main configuration file /etc/postfix/main.cfg

2.Add the following line at the end of the file:

sender_canonical_maps = static:user@server.com

  1. Save the file and restart postfix:

sudo systemctl restart postfix

Conclusion

Now you should be able so send emails from you PHP application. These emails will be forwarded to you email server and will be sent to the receiver.

You save some space by skipping third-party library :)

Feel free to add you feedback if this post was helpful to you.

My First blog

Written by Georgi Stefkoff

Hi all, I'm going to start my first blog here. I will use this open-source platform to write my own articles so I can help one of you or just to remember thing for myself :)

Stay tunned for the next posts.