SelfHost RustDesk using Docker and Plesk

Setting up your own RustDesk (Community Version) using Docker seems to be simple. But what i did not found was a HowTo do this using a VServer that is running Plesk. Some things should be easier / using more automation than doing everything manually.

So here is my HowTo do this.

Add a new Subdomain

Login to your Plesk and add a new Subodmain like rustdesk.my.domain or rd.my.domain.

Also we will directly activy Let’s Encrypt so that our RustDesk will be protected using https.

It is free and Plesk will automatically renew it.

Activate Docker

RustDesk describes how to set up Docker / links to it.

Those manually steps can be easily done by using the Docker Extension which is free. (There are Options for Remote Node Management that need to be paid for but for simple use cases it is enough).

So let’s go to Extensions and search for Docker. The offical Plesk Docker Extension should show up.

Get RustDesk Docker Image

In the new Docker Area we can now navigate to Images and Search for “rustdesk”

We require the rustdesk/rustdesk-server image. If you click on it

you are now able to download the latest version.

Use Docker-Compose directly

Plesk currently cannot use Docker Compose.

It will be available in the future (see https://talk.plesk.com/threads/how-to-use-docker-compose.373539/) but is not yet available.

So we will need to work on the Console.

mkdir /opt/rustdesk

cd /opt/rustdesk

vi docker-compose.yml

Content of docker-compose:

networks:
  rustdesk-net:
    external: false
services:
  hbbs:
    container_name: hbbs
    image: rustdesk/rustdesk-server:latest
    ports:
      - 21115:21115
      - 21116:21116
      - 21116:21116/udp
      - 21118:21118
    environment:
      - ALWAYS_USE_RELAY=Y
    command: hbbs -r 127.0.0.1:21117 -k _
    volumes:
      - ./hbbs:/root
    networks:
      - rustdesk-net
    depends_on:
      - hbbr
    restart: unless-stopped

  hbbr:
    container_name: hbbr
    image: rustdesk/rustdesk-server:latest
    ports:
       - 21117:21117
       - 21119:21119
    command: hbbr -k _
    volumes:
      - ./hbbr:/root
    networks:
      - rustdesk-net
    restart: unless-stopped

This will setup required services and store the data besides our docker-compose.yml.

Also using “-k _” it will not publicly be accessible without the public-key for the Relay-Server. Also we do not want a direkt Client2Client connection, everything should go over out https secured Relay (ALWAYS_USE_RELAY=Y).

Other HowTo’s expose the ports directly on your server (opening it in the firewall like in ufw for Ubuntu). Here we setup a private network so that the services may communicate together but it will not be externally availble.

It should now start using

docker compose up -d

We are also now able to see it running and access logs… in Plesk.

Make it available to the “World”

The Docker Plesk Extensions added a new option to add Proxy-Rules to our Subdomain so that it will be running behing our https Proxy.

Click on it on your Subdomain and we will be able to add a new Rule:

The only required Port is 21117 from the hbbr Container.

Configure Client

Now after downloading the Client from the offical RustDesk Page we can start it.

It will also inform you to use your own Server. to do this open the Menu and go to the Network Settings.

There we will configure our ID/Relay-Server.

It is enough to set the ID-Server to our Subdomain.

Also we will need the public Key. (which you should only give to people who should have access to your Server).

To get your public-Key you can access it from the Console using:

cat /opt/rustdesk/hbbs/id_ed25519.pub

The output is your Key. After pressing OK you should be good to go.

Zabbix detect Public IP Change

If you do not have a fixed IP you may already also be using something like DynDns to access your own Home-Network.

If you are also using Zabbix for Monitoring you may want to include an additional Information Layer to be notfied, when your public IP changes.

Reason? I myself also have had the problem that the DynDns Update failed and i had no information what my new “public IP” was. If Zabbix sends an EMail (it has had new Internet-Access) than you may be able to “recover” and dial in again (VPN…) using the IP and not your Dynamic DNS Name.

Getting public IP

How can you get your public IP?

There are a lot of Services to get it. Just use google.

Eg. ipfy “https://api.ipify.org/?format=plain” will provide it.

You may also use a simple custom solution on your own VServer or Webhosting-Package/Domain like setting up a PHP-Script to send back your IP.

Example (myip.php):

 <?php
echo $_SERVER['REMOTE_ADDR'];
?>

Create Zabbix Host

If you have already created a Host (see Zabbix Internet Connectivity Check) you can use it. Or else create it.

Create Item to get public IP

The Web Scenarios do not work for this Use-Case.

But there is a HTTPAgent in Zabbix.

Go to items and Create a new one.

Name: GetPublicIP

Type: “HTTP agent”

Key: public_ip

Type of Information: Character

URL:

Custom: https://my.domain/myip.php

Or a public available Service URL

Update Interval: 5m

Press Add.

Create Trigger

Now go to Triggers and Create a new one.

Name: PublicIPChange {ITEM.VALUE}

Severity: Information

Description: Public IP has Changed {ITEM.VALUE}

Expression:

(last(/Network connectivity checks/public_ip,#1)<>last(/Network connectivity checks/public_ip,#2))>0

Press Add and that’s it. Now if you want to test it just disconnect and reconnect to the internet and Zabbix will send an Notification that your IP changed. (Also 5min later it will also be automatically resolved).

Zabbix Internet Connectivity Check

Simple HowTo create a Internet Connectivity Check for Zabbix.

Create new Host

Create a new Host named “Network connectivity checks”

You do not need to add any Interfaces.

Add Web Scenario to Host

On our newly created Host go to “Web Scenarios”.

Press “Create web Scenario”

You can use your own Server or check Google.

For our Example we are going to use google.

Name of new Scenario: “Check Google Connectivity”

Update-Interval: 5m

Agent: Zabbix

Go to the Steps Tab and press “Add”

Name: “Google”

URL: “https://www.google.com”

Required Status Code: 200

Press the Add-Button to add the Step and than Add to add the Scenario.

Create a Trigger

Now we go to the Triggers Tab and pres “Create trigger”.

Name: “Google Accessibility {ITEM.VALUE}”

Severity: High

Expression:

length(last(/Network connectivity checks/web.test.error[Check Google Connectivity]))>0 and last(/Network connectivity checks/web.test.fail[Check Google Connectivity])>0

Press Add and we are Finished.

Now if you want to test it, disconnect from the Internet and wait a moment.

A new Problem should be created. If you have In-House Mailing (no internet contection required) you should get a new Problem-Notification from Zabbix.

Knowing order of Rails 4 Engine or Railtie Initializer execution and their duration

In Rails 3 i found a nice article to know how to hook in your engine or railtie initializers.

It printed the order of initializer execution. With Rails 4 this did not work any longer.

But i since i did not need it any longer, i removed it.

Now i wanted to know how long did my initializers take? Reason: Somehow it got slower with some changes i did.

So i came up with this little piece of code that prints the name of the executed initializer and how long it took. So now i can detmine in the logs when a initializer was executed and which took too long.


module RailsInitializerTimeLogging
  module TimeLoggedInitializer
    def self.included(base)
      base.send :alias_method, :run_without_timelogging, :run
      base.send :alias_method, :run, :run_with_timelogging
    end

    def run_with_timelogging(*args)
      beginning = Time.now
      begin
        run_without_timelogging(*args)
      ensure
        puts "executed initializer: #{name} took #{Time.now - beginning} seconds"
      end
    end
  end
end

Rails::Initializable::Initializer.send :include, RailsInitializerTimeLogging::TimeLoggedInitializer

And require it before your application:


require 'rails_initializer_time_logging'

module YourApplication
  class Application < Rails::Application
    ....
  end
end

Stale PID-Files in development

Somehow in my Development-Environment (Windows with Eclipse) webrick comes up with stale PID-Files.

I stop the Server, start it again and sometimes (not always) i get the message:

A server is already running.

This happend since i updated to rails-Version X.

But now i finally figured out (after googling and googling and finding nothing helpful) how to make it work again.

In application.rb before declaration of your own application insert:


require 'stale_pid_cleaner'
StalePidCleaner.check

StalePidCleaner:


class StalePidCleaner
  def self.check
    fName = "./tmp/pids/server.pid"
    if(File.exists?(fName))
      puts "found pidfile: " + fName.to_s
      pid = File.read(fName).to_i
      if(pid < 1)
        puts "invalid pid? " + pid.to_s
      else
        if(!alive?(pid))
          puts "cleaning stale pidfile"
          File.delete(fName)
        end
      end
    end
  end

  private
  def self.alive?(pid)
    begin
      Process.kill(0, pid)
      true
     rescue Errno::ESRCH, TypeError # Process is dead
       false
     end
   end
end

Hope it helps also someone else

Paperclip 4.0 and non deactivatable MediaType Spoofing

Paperclip 4 has an enhancement to detect MediaType Spoofing.

I’m with them, security is good and it’s better something like this is integrated.

But it was a change that you cannot turn off and when it does not work, you’ve got to live with it.

In my case:

  • Tests started failing, even when type was correctly set
  • The command that was executed did not work / was not found (did not look deeper into it)
  • SWFUpload and Paperclip and Spoofing Detection did not seem to work together.

So till it can be officially turned off (found an issue where it seems they want to integrate this) i came up with this as an solution:


module SpoofingFix
  def self.included(base)
    base.send :alias_method, :original_spoofed?, :spoofed?
    base.send :alias_method, :spoofed?, :fixed_spoofed?
  end

  def fixed_spoofed?
    false
  end

end

Paperclip::MediaTypeSpoofDetector.send :include, SpoofingFix

We just say it is always unspoofed and so we can work like before 😉

detecting bots by user-agent in rails

First i thought using the browser-gem and browser.bot? would give me the correct answer. But somehow it did not work. So i am now using this litte snippet to detect bots.


class BotDetection

  def self.bot?(agent)
    matches = nil
    matches = agent.match(/(facebook|postrank|voyager|twitterbot|googlebot|slurp|butterfly|pycurl|tweetmemebot|metauri|evrinid|reddit|digg|sitebot|msnbot|robot)/mi) if agent
    return (agent.nil? or matches)
   end

end

In my Base-Controller:


def bot_request?
  uAgent = request.env["HTTP_USER_AGENT"]
  if(uAgent.nil?)
    false
  else
    BotDetection.bot?(uAgent)
  end
end

A list of user-agents may be found at http://www.user-agents.org/

ActiveRecord disallow changes using unchangeable validation

Not allowing changes to be made to e.g. the login of the user only by the UI is in my eyes not enough.
The model should also ensure, that changes cannot be made once assigned.

So i tried to find an validation that does this for me.
But the once i found did not quite work as i expected.

So i wrote my own that i want to share:

class User < ActiveRecord::Base
  validates_unchangeable :login
end

unchangeable_validation.rb

require 'active_support/concern'
require 'active_model'
require 'active_record/base'

module UnchangeableValidation
  extend ActiveSupport::Concern

  class UnchangeableValidator < ActiveModel::EachValidator

    def initialize(options)
      super(options)
    end

    def validate_each(object, attribute, value)
      if(!object.new_record? && value.present?)
        abChanged = attribute.to_s + "_changed?"
        if(object.send(abChanged.to_sym))
          object.errors[attribute] << (options[:message] || "cannot be changed once assigned")
        end
      end
    end
 end

 module ClassMethods

   def validates_unchangeable(*attr_names)
     validates_with UnchangeableValidation::UnchangeableValidator, _merge_attributes(attr_names)
   end
 end
end

ActiveRecord::Base.send(:include, UnchangeableValidation)

unchangeable_validation_test.rb

require 'test_helper'

class UnchangeableValidationTest <  ActiveSupport::TestCase

  def test_login_unchangeable
    instance = User.new
    instance.login = "login"
    instance.save!
    instance.login = "changed"
    instance.save
    assert_equal instance.errors[:login].first, "cannot be changed once assigned"
  end

end

Squirrel SQL Client and SQLite Databases

Working with RubyOnRails i use sqlite in development.

In my dev-environment (windows) i used to use SQLiteBrowser to look into the DB. But the usability was not what i was used to.

So i tried to get Squirrel SQL Client to work with SQLite and had my problems integrating the two together.

Why?

Because i googled and googled and did not find a working answer.

In the end it was easy.

  1. Download Squirrel SQL Client http://squirrel-sql.sourceforge.net/
  2. Get this jdbc-Adapter for SQLite: https://bitbucket.org/xerial/sqlite-jdbc/overview
  3. Throw the lib in the lib-Directory of SQuirrel
  4. Startup Squirrel
  5. Create new Driver
    1. Name: SQLite
    2. Example-Url: jdbc:sqlite:<PATH>/mydatabase.db
    3. Website-Url: https://bitbucket.org/xerial/sqlite-jdbc/wiki/Usage
    4. Class-Name: org.sqlite.JDBC
  6. Add Alises for your DB and connect to them