ferdzo's blog Blog of a CS student for system and network administration, computer hardware and amateur radio

Continous Deployment of software using Github Actions

A couple of months ago, I wanted to seamlessly update the code of my small web app running on my server.  I was tired of using SFTP or copying code over SSH to the terminal editor every time I update something as small as the shade of a color. 

So I started looking for something more DevOps like. Some software for CI (Continuous Integration), CD(Continuous Delivery) and CD (Continuous Deployment). Mostly for Continuous Delivery because I don’t need integration considering I was doing the project alone. I looked into many solutions, such as CircleCI, GitLab CI/CD, Travis CI. They are all awesome tools, but they are not worth the hassle for small apps. They need to many configurations for something as simple as automatically downloading the new version from the git repository. Then I remembered the CI/CD platform Github made, Github Actions 

Github Actions

Github Actions is platform for CI/CD and has many more options. It’s integrated to the GitHub “ecosystem” and we can easily connect it to our repositories, integrate it with GitHub Pilot to compile and make releases for us and much more. It’s very simple to use, you create so-called Workflows for everything you need and attach them to your desired repositories. The Workflows are some type of YAML config files, you can make them yourself or use one already made. There are many available on the so-called Marketplace for  nearly every possible job you need, and you don’t have to bother much.

The way I’ve done this is very simple, Github Actions are called every time there is git push to the repository. After that, Github Actions run the workflow, in this case a simple SSH login and running git pull to the master/main branch. This is the simplest way, it’s the easiest, but it is not secure, nor very smart to do this. But for my case, a simple web app, still in development, without any need of security or stability whatsoever. 

Now let’s get started on how to do this.

First, we make a Github repository with our code, if we don’t already have. After that, we go to the Actions tab in our repository, and we press on New Workflow. Then we click on set up a workflow ourself. Next we get a page with an editor, where we write our Workflow file. The Workflow file is written in YAML. For those who don’t know, YAML is a data-serialization language like XML and JSON, and mostly used for configuration files. Now let’s get started with configuring our Workflow file. This is the code we need:

name: remote ssh command
on: [push]
jobs:

  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
    - name: executing remote ssh commands using password
      uses: appleboy/ssh-action@master
      with:
        host: $
        username: $
        key: $
        port: $
        script: |
          cd folder/
          git pull origin main

Here we have a simple workflow code. In the code, we connect to a host, and run git pull. The values for the host, username and other sensitive information is stored in the repository secrets. Repository secrets is a neat feature. It allows us to store sensitive information such as usernames, host addresses and passwords, without having them in our files. When the script is executed, they are automatically inserted. Now we need set up the Environment secrets. We go to the repository Settings, Secrets, Actions and we click on the New repository secret. In here, we need four secret variables, host, username, key (the SSH private key for the server) and the SSH port. Now in the script section of the file we put our simple script, in this place I change the folder to the repo folder and then run git pull in there. But this can be used for anything we want, any command can be run from here, so we are not limited. Now all we have to do is press Start commit. After this step, the Workflow file is commited to the repository and it will instantly try to Run. To check on the Workflow run log, we go to the Actions tab. If the run is sucessful there will be a ✅ and if the run failed there will be ❌. While the Workflow is still runing, 🟠 will appear.

Using Nginx as subdomain proxy for Weechat relay

Weechat is an IRC client with relay options that enables us to use external applications as the frontend for Weechat. For example, Glowingbear or Weechat for Android is such application. The relay can be directly exposed to the internet, and you can also use a self-signed SSL certificate, but I don’t really like the idea of doing that and exposing a possibly vulnerable app to the internet.

So I stumbled upon the idea of using Nginx to proxy the internal port to a custom subdomain and open it up to the internet. Using Nginx:

  1. It’s much more secure to open up ports and expose application such as Nginx, which is designed to do exactly that.

  2. Easier to use SSL certificates. It’s much easier to import and manage SSL certificates for Nginx, than exporting them and copying to the Weechat folder and importing them and so on and so on.

  3. Most of the time I have Nginx already on my server for various jobs, such as proxying other apps or hosting something else. So it is easy to add just one more config file and make it work perfectly and without any hassle.

We are doing this assuming the internal communication on the server is secure. So we can use SSL just on the Nginx side because if use SSL on both sides it gets much more complicated and it’s not worth the inconvenience. Anyway, if the internal communication on our server is not secure there’s no point in doing any of this.

Nginx configuration

First, we need to make a config file with the subdomain we are using in /etc/nginx/sites-available. In example, /etc/nginx/sites-available/wchat.example.xyz. After this step, we need to create a symbolic link for the config file to the /etc/nginx/sites-enabled/ folder with the command

ln -s /etc/nginx/sites-available/wchat.example.xyz /etc/nginx/sites-enabled/wchat.example.xyz

In the next step, we go back to the Nginx config file and set up the proxy. Our subdomain should point to the same IP address as our server. The proxy forwards all the connections to port 9001 on our local machine.

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
server {
    listen      443 ssl http2;
    server_name wchat.example.xyz;

    location / {
        proxy_pass http://127.0.0.1:9001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 4h;
    }

}

In server_name we write our domain and at proxy_pass the local IP address and the port of the Weechat relay. The other parameters can be copied and are not important.

SSL certificate using certbot

In this step, we need to make a new SSL certificate, in this case, we are using certbot to make a self-signed SSL certificate using Let’s Encrypt. To create a certificate we use the following command:

sudo certbot --nginx -d wchat.example.xyz 

Using the –nginx parameter, certbot will automatically configure Nginx with the SSL certificate.

Now, we test our Nginx configuration

nginx -t

If the test passes we can continue and restart our Nginx service

systemctl restart nginx.service

Configuring Weechat

After we configured our Nginx proxy, we are now going the configure the Weechat relay. We enter the following commands in Weechat

/set relay.network.password thepasswordiwant
/set relay.network.bind_address "127.0.0.1"
/relay add ipv4.weechat 9001

This will set up a Weechat password and set the relay listening port to 9001.

Connecting to the relay

And in the last step, we should try and connect to our relay. The port should be 443 and not 9001 because our proxy forwards the traffic from 443 to 9001. And port 9001 is not open to external connections.

Thanks for reading my first post on the blog and stay tuned for more new posts!

My first blog

This is my frist blog post, this blog is made with Jekyll and hosted on Github Pages.