Setting up Personal VPN Server

This is my second attempt at setting up a VPN service. This this time with lessons learned.
Server provider: RamNode
Price: $3/month
Virtualization: KVM
Specs: 512 GB RAM, 10 GB Storage, 1000 GB Bandwidth
Chosen OS: CentOS 7

After you set up your server, ssh into it.

Optionally, install and enable firewall for better security. Better to do before you run the vpn script. Otherwise, will have to manually update firewall rules.

yum update
# If you want vim:
yum search vim
yum install vim-enhanced.x86_64
yum install firewalld


systemctl enable firewalld
systemctl start firewalld

#Show configuration for the given zone:
firewall-cmd --zone=public --list-all

# add a port for your ssh console. Recommended to change from default 12
firewall-cmd --add-port 1234/tcp
firewall-cmd --add-port 1234/tcp --permanent
vim /etc/ssh/sshd_config
# update Port to 1234 (or whatever you picked above)
# Uncomment Protocol 2 - it provides better security
service sshd restart


# this retrieves the vpn script - see https://github.com/Nyr/openvpn-install
wget https://git.io/vpn -O openvpn-install.sh && bash openvpn-install.sh


# if you plan to run VPN on port 443
firewall-cmd --zone=public --add-port=443/udp
firewall-cmd --permanent --zone=public --add-port=443/udp
vim /etc/openvpn/server.conf
#add "port 443" or change to 443 if there is already an entry

#in the generated .ovpn file make the same change for the port
# should look something like this: remote your_server_ip 443


# additional security feature (not necessary for VPN)
yum install epel-release
yum install fail2ban
yum install fail2ban-systemd
cp -pf /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
systemctl enable fail2ban
vim /etc/fail2ban/jail.local
#set to somehting like this
------------------------
[DEFAULT]
# ban hosts for 300 seconds:
bantime = 300
maxretry = 2

[sshd]
port = your_ssh_port
enabled = true

#Override /etc/fail2ban/jail.d/00-firewalld.conf:
banaction = iptables-multiport


#if SELinux is enabled (check with sestatus)
[selinux-ssh]
port     = ssh
logpath  = %(auditd_log)s
enabled = true


#Check logs:
/var/log/fail2ban.log
------------------------
systemctl restart fail2ban



# if you need more users, just execute the script again
sh openvpn-install.sh

This generates a single some_name.ovpn file that you can download and use with an OpenVPN client. Hopefully, you now have a personal VPN server for roughly $30 a year.

Disabling ipv6
If you’re not planning on using ipv6, may as well disable it. Unfortunately fail2ban doesn’t support it yet.

sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1

Multiple instances of OpenVPN

If you want to run multiple instances of vpn (say a typical 1194 udp and another one on 443 tcp to avoid blocking)
Add a new config in /etc/openvpn
For simplicity, you can copy existing server.conf and lets name it server2.conf.

Update server2.conf to use a different port and change the server ip (let’s say if original server.conf has ip of 10.8.0.0, let’s give the new config
10.8.2.0:

port 443
proto tcp
server 10.8.2.0 255.255.255.0

Start your new openvpn instance:

systemctl start [email protected]_443_tcp

If using firewalld, add new rules:

firewall-cmd --zone=trusted --add-source=10.8.2.0/24
firewall-cmd --permanent --zone=trusted --add-source=10.8.2.0/24
firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.2.0/24 -j SNAT --to your_server_ip
firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.2.0/24 -j SNAT --to your_server_ip

Restart your new openvpn server:

systemctl restart [email protected]_443_tcp

If you’d like to use the same .ovpn config on the client side to connect to both ports, add this to client config:

server-poll-timeout 4
remote your_ip 1194 udp
remote your_ip 443 tcp
remote your_ip 1194 udp
remote your_ip 1194 udp

This will try to connect to 1194 udp first, then if unsuccessful, it’ll try 443 tcp, then 1194
In order for this to work, both configs on the server must have the same settings (except for IP’s of course)

OpenDNS
If using OpenDNS head to https://dashboard.opendns.com/ and set it up

Auto-update all CentOS packages

yum -y install yum-cron

vim /etc/yum/yum-cron.conf
# set:
# download_updates = yes
# apply_updates = yes
sudo systemctl enable yum-cron.service
systemctl start yum-cron

Harden SSH
vim /etc/ssh/sshd_config

Ciphers aes128-ctr,aes192-ctr,aes256-ctr
KexAlgorithms ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256
MACs hmac-sha2-256,hmac-sha2-512

A nice resource to test the speed: https://speedof.me/.

Phoenix and Ecto. First Steps

Valid as of Phoenix 1.2

Show routes:
mix phoenix.routes

Generating resources:
mix phoenix.gen.html Post posts –no-model
mix phoenix.gen.json Post posts

Ecto
Types

  • :string
  • :integer
  • :map
  • :binary_id
  • :float
  • :boolean

Writing Queries
Two ways

  1. Query
    import Ecto.Query
    
    from p in context.Posts
    where p.Title.Contains("Stuff")
    select p;
    
  2. Expression
    MyApp.Post
    |> where(titlle: "Stuff")
    |> limit(1)
    

Making changes – https://hexdocs.pm/ecto/Ecto.Changeset.html
changeset = Post.changeset(post, %{title: “updated”})
Repo.update(changeset)
Repo.delete(post)

Migrations
Generate migration:
mix ecto.gen.migration [migration_name] -r [repo]

Generate schema:
mix phoenix.gen.model [Schema] [table] [fields] -r [repo]

Run/Rollback migration
mix ecto.migrate -r [repo]
mix ecto.rollback -r [repo]

Generate migration:
Does not generate schema module:
mix ecto.gen.migration [migration_name] -r [repo]

Generates both schema model and a migration:
mix phoenix.gen.model [Schema] [table] [fields] -r [repo]

To avoid specifying repo all the time:
config :my_app, ecto_repos: [MyApp.Repo]

Starting with Phoenix. Installation.

Prerequisites (valid as of Phoenix 1.2)

Mac

  • Elixir – brew install elixir
  • node – brew install node
  • Postgres – Postgres.app

Windows
For Windows, can use Chocolatey to install node and Elixir. Postgres seems to be outdated as of time of this post. Or download from source and install:

Add to Windows path PostgreSQL (dependent on PostgreSQL version), so we can use psql on command line –
C:\Program Files\PostgreSQL\9.6\bin
C:\Program Files\PostgreSQL\9.6\lib

Install phoenix:

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

Alternatively, download a specific version from https://github.com/phoenixframework/archives.

Generate and run a new project:

$ mix phoenix.new my_first_app
$ mix ecto.create
$ npm install
$ iex -S mix phoenix.server
iex> :observer.start

Developing a Simple app with Action Cable and Devise on Heroku

As I continue to learn Rails, this is my experience developing a Rails application using Action Cable (allows communication using web sockets)

Finished product: Story Estimate

Assuming we already generated a new Rails app, lets start with authentication using Devise gem.
Add “gem ‘devise'” to your gem file.

Run a few commands to generate your users:
$ rails generate devise:install
$ rails generate devise User
$ rails generate devise:views
$ rails db:migrate

For the sake of this application, I want to enable guest users. Lets allow that:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def current_or_guest_user
    current_user || guest_user
  end

  # find guest_user object associated with the current session,
  # creating one as needed
  def guest_user(with_retry = true)
    # Cache the value the first time it's gotten.
    @cached_guest_user ||= User.find(session[:guest_user_id] ||= create_guest_user.id)

  rescue ActiveRecord::RecordNotFound # if session[:guest_user_id] invalid
    session[:guest_user_id] = nil
    guest_user if with_retry
  end

  private

  def create_guest_user
    u = User.create(email: "guest_#{Time.now.to_i}#{rand(100)}@example.com")
    u.save!(validate: false)
    session[:guest_user_id] = u.id
    u
  end
end

Now, if we want to sign in a user as a guest, we’d call sign_in(current_or_guest_user)

Next thing, we want to authorize our Action Cable connections with Devise users. In order to do that, we need to add some hooks in the config/initialazers. This will add current user id to user’s cookie:

config/initialazers/warden_hooks.rb

Warden::Manager.after_set_user do |user,auth,opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = user.id
end

Warden::Manager.before_logout do |user, auth, opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = nil
end

With this, we can configure rails connection:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected
      def find_verified_user
        if verified_user = User.find_by(id: cookies.signed['user.id'])
          verified_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

Now, time to generate a new channel:
rails g channel channel_name optional_method
It may look someting like this:

class CoolChannel < ApplicationCable::Channel
  BASE_CHANNEL = 'BaseChannel'.freeze

  def subscribed
    stream_from "#{BASE_CHANNEL}#{params['some_key']}"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def some_action(data)
    value_from_client = data['value_0']
    ActionCable.server.broadcast\
      "#{BASE_CHANNEL}#{data['some_key']}",
      data
  end
end

This will create a new custom-named channel based on whatever we want to send in params from the client. some_action is the method that will be called by the clients to send a message. In this case we just rebroadcast the message to all clients.

To make a call from the client to the server, go to the .js or .coffee file for the earlier generated channel (should be in javascripts/channels). We can call back to the server:

jQuery(document).on 'turbolinks:load', ->
  session_slug = $('#session_slug').val()
  player_name = $('#player_id').val()
  $stats =$('#statistics')

  App.estimation_session = App.cable.subscriptions.create {
    channel: "CoolChannel",
    },
    connected: ->

    disconnected: ->
      # Called when the subscription has been terminated by the server

    received: (data) ->
      value_from_server = data.value_0

    some_action: (value_0, value_1)->
      @perform 'some_action',
               value_0: value_0,
               value_1: value_1

When deploying to Heroku, we need to Add Redis To Go add-in (or any other Redis add-in such as Heroku Redi. Then update cable.yml to use redis:
adapter: redis
url: YOUR_URL

To find Redis connection URL, run:
heroku config | grep redis

Also, we need to add redis gem:
gem ‘redis’, ‘~> 3.2’

git push heroku master

Some useful resources:

Ruby Inheritance Rules Simplified

A simple principle to understand Ruby inheritance is LIFO (Last In First Out).

 

  1. Ruby will search the object you’re calling it from
  2. It will search it in any included modules, starting with the last loaded module
  3. It will go in the superclass (technically, modules get in the inheritance hierarchy as well, but whatever)

When the method is found, the search stops.

 

A Note About Calling super.
The same rule applies if you use super. If you have the same method defined in both module and a parent class, the module method will be called.
Here’s a simple example:

module Test
  def abc
    puts 'abc from Module'
  end
end

class TestClassParent
  def abc
    puts 'abc from parent'
  end
end

class TestClassChild &amp;lt; TestClassParent
  include Test

  def abc
    super
  end
end


TestClassChild.new.abc

Running this will result in:

abc from Module

Debugging Rails

Install pry, pry-byebug, and pry-rails gems.
To add a breakpoint, type binding.pry wherever you want your breakpoint to happen.
In console use coninue, step, next, finish commands:

step: Step execution into the next line or method. Takes an optional numeric argument to step multiple times.

next: Step over to the next line within the same frame. Also takes an optional numeric argument to step multiple lines.

finish: Execute until current stack frame returns.

continue: Continue program execution and end the Pry session.

To add shortcuts to ^ commands, add to ~/.pryrc:
Pry.commands.alias_command ‘c’, ‘continue’ rescue nil
Pry.commands.alias_command ‘s’, ‘step’ rescue nil
Pry.commands.alias_command ‘n’, ‘next’ rescue nil
Pry.commands.alias_command ‘f’, ‘finish’ rescue nil

Setting Up My Mac Dev Environment

Set up a package manager: Homebrew

Istall iterm2

ZSH

Replace bash with zsh (it offers some nice plugins and themes).

brew install zsh zsh-completions

Install oh-my-zsh – “A delightful community-driven (with 1,000+ contributors) framework for managing your zsh configuration.”

sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

To update oh-my-zsh, run upgrade_oh_my_zsh in zsh shell.

Pick your set of plugins (edit .zshrc file):
plugins=(git brew gem ruby rvm rails yarn npm)

Pick a theme (.zshrc):

ZSH_THEME="af-magic"
If you're on OSX Sierra, then you need another step:
create ~/.ssh/config with this content:
Host *
  UseKeychain yes
  AddKeysToAgent yes
  IdentityFile ~/.ssh/id_rsa

Generate github SSH Keys – https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/

Add generated ssh key to your keychain, so you don’t have to reenter your ssh passphrase:

 ssh-add -K ~/.ssh/id_rsa 

Rails/Ruby/RVM/rbenv

If using rvm with zsh, add this to your .zshrc:

[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"

If you use rbenv, you may need to add this to your .zshrc:

export PATH="$HOME/.rbenv/shims :$PATH"
eval "$(rbenv init -)"

Exercism

For exercism zsh completions, add

if [ -f ~/.config/exercism/exercism_completion.zsh ]; then
. ~/.config/exercism/exercism_completion.zsh
fi

And run

$ mkdir -p ~/.config/exercism/
$ curl http://cli.exercism.io/exercism_completion.zsh > ~/.config/exercism/exercism_completion.zsh

git standup

git-standup

brew install git-standup

fzf is a general-purpose command-line fuzzy finder

https://github.com/junegunn/fzf#key-bindings-for-command-line

brew install fzf
# Install shell extensions
/usr/local/opt/fzf/install

autojump – a faster way to navigate your filesystem

https://github.com/wting/autojump

brew install autojump

# add autoload to .zshrc
[[ -s $(brew --prefix)/etc/profile.d/autojump.sh ]] &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp; . $(brew --prefix)/etc/profile.d/autojump.sh

fkill – Fabulously kill processes

https://github.com/sindresorhus/fkill-cli
https://github.com/SamVerschueren/alfred-fkill

npm install --global fkill-cli
npm install --global alfred-fkill

Mac apps

Git alias (add to .gitconfig)

Show recent branches:
[alias]
recent = “for-each-ref –sort=-committerdate –count=10 –format=’%(refname:short)’ refs/heads/”

Python

Install Anaconda – https://www.anaconda.com/distribution/#download-section

Update your .bashrc or .zshrc with

export PATH="$HOME/anaconda3/bin:$PATH"

Deploying Rails App to Heroku

My first experience installing a Rails app on Heroku. A pretty easy way to deploy Rails apps.

If you made any changes to your gemfile, make sure to run
bundle install –without production
Even if you already have all of the gems installed. This updates Gemfile.lock, which will be used by Heroku.

If you are on Windows, make sure to remove “.exe” from bundle, rails, and rake inside of your bin folder:
#!/usr/bin/env ruby.exe

Create a free account on Heroku if you don’t have one yet. Download the Heroku Toolbelt.
From the command line:

heroku login
cd path_to_you_app
heroku create

This will create a new Heroku application and set up the remote Git repo. You can optionally specify a name for your app in “heroku create” command, or you can rename it later.

Now, lets push the code to Heroku and run all the DB commands if necessary:

git push heroku master
heroku run rake db:migrate
heroku run rake db:seed
heroku open

To update you app, just push to heroku master.

To get information about your Heroku apps:

heroku apps
heroku ps
heroku logs
heroku run console

The last command will launch a remote Rails console session.

To rename your app:
heroku rename new_name

To destroy your app:
heroku destroy app_name

Starting with RSpec in Rails

Add RSpec related gems to gemfile:

group :development, :test do 
  gem 'rspec-rails'
end

group :test do
  gem "capybara"
end

Capybara is a framework for testing web applications; allows to navigate your app programmatically.

Install your RSpec gems: bundle install

Generate RSpec templates:
generate rspec: rails g rspec:install

To view all available generators: rails g

Since RSpec uses test database, run your migrations for test environment:
rails db:migrate RAILS_ENV=test
If this is the first run, may need to set up the database by running RAILS_ENV=test rake db:reset

To run all specs: rspec
To add formatting: rspec -format doc (or use shortcut: rspec -f doc)
To always use the same formatting, add -format doc to .rspec file.

To use helper files for RSpecs
Uncomment this line in rails_helper.rb:
Dir[Rails.root.join(‘spec/support/**/*.rb’)].each { |f| require f }
And place all your helper methods inside of spec/support directory.

Sample RSpec file:

require 'rails_helper'

describe 'Navigating movies' do
  it 'allows navigation from the detail page to the listing page' do
    movie = Movie.create(movie_attributes)

    visit movie_url(movie)

    click_link 'All Movies'

    expect(current_path).to eq(root_path)
  end