Setting up VS Code with Rails, Elixir, JavaScript

No Comments

Let’s make sure we can start VS Code from the terminal:

Command + Shift + P
Type Shell
Select Command : Install code in PATH

Extensions

Rails

JavaScript

Git

Elixir

Personal Settings

"editor.formatOnSave": true,
  "editor.fontLigatures": true,
  "editor.fontFamily": "FiraCode-Retina",
  "editor.fontSize": 18,
  "editor.renderIndentGuides": true,
  "files.exclude": {
    "**/.git": true,
    "**/node_modules": true,
    "**/bower_components": true,
    "**/tmp": true,
    "tmp/**": true,
    "**/vendor": true,
    "vendor": true,
    ".bundle": true,
    ".github": true,
    ".sass-cache": true,
    "features/reports": true
  },

  "editor.tabSize": 2,
  "prettier.singleQuote": true,
  "workbench.colorTheme": "Monokai",
  "window.zoomLevel": 0,
  "editor.renderWhitespace": "boundary",
  "editor.renderControlCharacters": true,

  "ruby.lint": {
    "rubocop": true,
    "ruby": true,
    "fasterer": true,
    "reek": false,
    "ruby-lint": false
  },
  "editor.quickSuggestions": {
    "strings": true
  },

  "cucumberautocomplete.steps": [
    "features/step_definitions/*.rb",
    "features/step_definitions/**/*.rb",
    "features/step_definitions/**/**/*.rb"
  ],
  "cucumberautocomplete.syncfeatures": "features/*feature"
Categories: Development Setup

Elixir Phoenix Deployment with Distillery

No Comments

OS: Ubuntu 17.04/zesty

We’ll be deploying using a git hook. This is not intended to be used in a production environment, but works just fine for a personal side project.

Let’s start with the server setup
Erlang/Elixir

#Add Erlang Solutions repo
wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb

sudo apt-get update

#Install Erlang/OTP
sudo apt-get install esl-erlang

We’ll be using kiex for Elixir

#Install Elixir
\curl -sSL https://raw.githubusercontent.com/taylor/kiex/master/install | bash -s

#Add to .bashrc
test -s "$HOME/.kiex/scripts/kiex" && source "$HOME/.kiex/scripts/kiex"

kiex install 1.4.0
kiex use 1.4.0
kiex default 1.4.0

Let’s init a repo

apt-get install git-core
mkdir repos && cd repos
mkdir my_repo 
cd my_repo
git init --bare

Add a remote to your local repo:

git remote add some_name [email protected]_ADDRESS:repos/your_repo.git

Go back to the server and create a post-deploy hook. This will generate a new release after each git push.

Create post-receive file inside of your git hooks directory with this content:

#!/bin/bash -l

[[ -s "$HOME/.kiex/scripts/kiex" ]] && source "$HOME/.kiex/scripts/kiex"

GIT_REPO=$HOME/repos/your_repo.git
TMP_GIT_CLONE=$HOME/tmp/git/your_repo

cd $TMP_GIT_CLONE

PORT=4000 _build/prod/rel/{app_name}/bin/{app_name} stop
cd $HOME
rm -r $TMP_GIT_CLONE
git clone $GIT_REPO $TMP_GIT_CLONE
cd $TMP_GIT_CLONE

mix deps.get
cd assets
npm install
brunch build --production
cd ..
MIX_ENV=prod mix do compile, phx.digest, release --env=prod

MIX_ENV=prod mix ecto.migrate
PORT=4000 _build/prod/rel/{app_name}/bin/{app_name} start

This will stop your existing elixir app, remove old code, compile and release your new code, run the migration, and start the app on port 4000.

If you run it behind NGINX, set it up as a reverse proxy:

server {
  listen 80;
  listen [::]:80;

  server_name domain.com;

  location / {
     proxy_redirect off;
     proxy_pass http://127.0.0.1:4000/;
  }
}

Let’s go back to your Phoenix project and add distillery
Add to mix.exs deps:

  {:distillery, "~> 1.5"}

Initialize reliase config:

mix release.init

Update config/prod.exs

  config :app_name, AiPhoenixWeb.Endpoint,
    http: [port: {:system, "PORT"}],
    url: [host: "localhost", port: {:system, "PORT"}], # This is critical for ensuring web-sockets properly authorize.
    cache_static_manifest: "priv/static/cache_manifest.json",
    server: true,
    root: ".",
    version: Mix.Project.config[:version]

If using PostgreSQL, let’s install Postgresql and create a deploy user for our database:

sudo apt-get install postgresql postgresql-contrib libpq-dev
sudo su - postgres
createuser --pwprompt deploy
createdb -O deploy my_app_name_production
Exit

The last step to to “git push” and verify that everything works.

Categories: Elixir, Phoenix

NGINX Subdomains

No Comments

In the example below we serve index.html from /var/www/html/your_site directory when accessing “domain.com”; and we send all requests to local application server running locally on port 4000 when accessing subdomain.domain.com

File: /etc/nginx/sites-enabled/default

server {
        listen 80;
        listen [::]:80;

        server_name domain.com;

        location / {
                root /var/www/html/your_site;
                try_files $uri /index.html;
        }
}

server {
  listen 80;
  listen [::]:80;

  server_name subdomain.domain.com;

  location / {
     proxy_redirect off;
     proxy_pass http://127.0.0.1:4000/;
  }
}
Categories: Linux

Deploying React to Linux Server with Git Push

No Comments

Selected VPS: Linode, 1GB Ram, 20 GB SSD, 1 TB transfer
OS: Ubuntu 17.04
Web Server: Ngnix
If you’d like to try Linode, I would greatly appreciate using this referral link – Linode: SSD Cloud Hosting & Linux Servers

Start with regular updates

apt-get update && apt-get upgrade

Set up fail2ban and Firewall

I’m installing fail2ban 0.10 since it supports ipv6. At the time of this post, it is not available as a regular package.

wget https://github.com/fail2ban/fail2ban/archive/0.10.0.tar.gz
tar -xvzf 0.10.0.tar.gz
python3 setup.py install

#To enable fail2ban as an automatic service, copy the script for your distro from the files directory to /etc/init.d.

cp files/debian-initd /etc/init.d/fail2ban
update-rc.d fail2ban defaults
service fail2ban start

#Add local jail
awk '{ printf "# "; print; }' /etc/fail2ban/jail.conf | sudo tee /etc/fail2ban/jail.local
vim /etc/fail2ban/jail.local

uncomment sshd section and add
enabled = true

sudo apt-get install sendmail iptables-persistent
sudo service fail2ban start

Firewall ( allow established connections, traffic generated by the server itself, traffic destined for our SSH and web server ports. We will drop all other traffic):

sudo service fail2ban stop
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
sudo iptables -A INPUT -j DROP

# easy way to rate-limit ssh with ufw:
# technically, we could do all of the iptables stuff with ufw
ufw enable
ufw limit ssh

If using IPv6:

ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT # (replace with your undisclosed port)
ip6tables -A INPUT -p icmpv6 -j ACCEPT
ip6tables -A INPUT -j REJECT
ip6tables -A FORWARD -j REJECT

View iptables rules:

sudo iptables -S

Save iptables rules:

sudo dpkg-reconfigure iptables-persistent
sudo service fail2ban start

SSH

vim /etc/ssh/sshd_config

#Add or uncomment (if using Ubuntu < 17.04)
protocol 2

#Add allowed ciphers
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

Restart and test ssh config:

service sshd restart
#returns nothing if everything configured properly
sshd -t

NGINX

sudo apt-get install software-properties-common
sudo add-apt-repository ppa:nginx/stable
sudo apt-get install nginx
service nginx status

Update /etc/nginx/sites-enabled/default

root /var/www/html/your_site;

location / {
# Some comments...
try_files $uri /index.html;   # ADD THIS
}

sudo service nginx restart

Installing React Dependencies

# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
 
#install node (apt-get repo has an older version of Node)
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt-get install -y build-essential

GIT Push Deploy

Let’s set up git on the server

apt-get install git-core
mkdir repos && cd repos
mkdir your_site.git
cd your_site.git
git init --bare

Set up a post-push hook
cd /repos/your_app.git/hooks
touch post-receive

#!/bin/bash -l

GIT_REPO=$HOME/repos/your_app.git
TMP_GIT_CLONE=$HOME/tmp/git/your_app
PUBLIC_WWW=/var/www/html

git clone $GIT_REPO $TMP_GIT_CLONE
cd $TMP_GIT_CLONE
yarn install
yarn build
rm -rf $PUBLIC_WWW/your_app_bup
mv $PUBLIC_WWW/your_app $PUBLIC_WWW/your_app_bup
cp -a build/. $PUBLIC_WWW/your_app
rm -Rf $TMP_GIT_CLONE
exit

Run on post-receiv:

chmod +x post-receive

On your local machine:

git remote add linode [email protected]_server_address:repos/your_app.git
git push linode master
Categories: git, Linux, React

Agile Estimation Tools

No Comments

What’s the best online story estimation tool you ask? Well, Story Estimate sounds like one of the best options out there. Sure, I may be a little biased, since I wrote it, but still, it’s pretty good.

Nonetheless, I’ll pretend to be impartial and give a “fair” overview of several options.

  • Story Estimate – in the name of “fairness”, starting with the best tool out there. It is free, presents a modern UI, has no known vulnerabilities. It allows to use multiple estimation techniques (planning poker, T-shirt sizes…), including custom ones (specify whatever you want for your votes). Simple to use. Best of all, it is written by me. What not to like?
  • Pointing Poker – a little outdated UI, but a pretty nice tool, also free. Unfortunately, it is open to JavaScript injection. Which, in a way, can be thought of as a feature (you can inject some fun JavaScript scripts), as long as you can trust your teammates not to inject any harmful scripts.
  • Planning Poker – the original planning poker. Free tier is limited (up to 5 players), paid version has some nice integrations with other tools (JIRA, TFS…). It’s a little more complicated, takes a little longer to setup, requires a user account.
  • PlanITPoker – Doesn’t require an account, although you can create one. Not served over https – frankly, unforgivable in the age of services like CloudFlare and Let’s Encrypt, which can generate a free SSL certificates for your. But not bad in terms of functionality. Also, a little more involved to setup.
  • Firepoker – Another free and open source tool. Extra points for being open source on GitHub. It’s a little limited in terms of the points you can select (offers two presets – powers of 2 and a slightly modified Fibonacci sequence). Also open to JavaScript injection, and is not served over https.
Categories: Agile Tags: Tags:

RSpec, Action Cable, and Capybara (As of Rails 5.1.2)

No Comments

Gems in Gemfile:

group :test, :development do
  gem 'database_cleaner'
  gem "rspec-rails", "~> 3.6.0"
  gem 'selenium-webdriver'
end

group :test do
  gem "capybara", "~> 2.14.0"
end

rails_helper:

config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, js: true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

  #Puma makes it possible to run RSpec with ActionCable
  Capybara.server = :puma

  Capybara.register_driver :selenium_chrome do |app|
    Capybara::Selenium::Driver.new(app, browser: :chrome)
  end
  Capybara.javascript_driver = :selenium_chrome

And, the driver:

brew install chromedriver
Categories: Rails, Ruby, Web

Setting up a cron job on Mac

No Comments

In this example, we’re setting up a job, which will run at 08:00 am daily and clean rails logs

Step 1 – create a file with what we want to run (delete_logs.sh):

#!/bin/(shell) 

cd ~/code/rails_project
/Users/username/.rbenv/shims/bundle exec rake log:clear

Setp 2 – set up the cron scheduler:

$ env EDITOR=vim crontab -e

# Add the line to run the script at 8am daily:

0 8 * * * sh ~/scripts/delete_logs.sh >/tmp/stdout.log 2>/tmp/stderr.log
Categories: Uncategorized

Configuring ESLint and Prettier in Atom

No Comments

Install eslint and optional plugins:

npm install -g eslint eslint-plugin-react

Install atom plugins:

linter-eslint
prettier-atom

Create .eslintrc either in the root of your application (or your root path if you want it to be global)

Add simple configuration rules:

{
    "plugins": [
        "react"
    ],
    "parserOptions": {
        "ecmaVersion": 6,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "env": {
        "es6":     true,
        "browser": true,
        "node":    true,
        "mocha":   true
    },   
    "extends": [
        "eslint:recommended", 
        "plugin:react/recommended"
    ],    
    "rules": {
    }
}

Optionally, update atom prettier-atom to format on save (in plugin settings)

Categories: JavaScript

Deploying Rails Action Cable to Linux

No Comments

Selected VPS: Linode, 1GB Ram, 20 GB SSD, 1 TB transfer
OS: Ubuntu 16.04 LTS
App server: Passenger
Web Server: Ngnix

If you’d like to try Linode, I would greatly appreciate using this referral link – Linode: SSD Cloud Hosting & Linux Servers

Guide

First step – boot your server from Linode Manager Dashboard and ssh into it

General Server Updates

apt-get update && apt-get upgrade

#If desired to run auto-updates
apt-get install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades

Set timezone:

dpkg-reconfigure tzdata
sudo service rsyslog restart

Security:

fail2ban

sudo apt-get install fail2ban

awk '{ printf "# "; print; }' /etc/fail2ban/jail.conf | sudo tee /etc/fail2ban/jail.local
vim /etc/fail2ban/jail.conf

uncomment sshd section and add
enabled = true

sudo apt-get install sendmail iptables-persistent
sudo service fail2ban start

Firewall ( allow established connections, traffic generated by the server itself, traffic destined for our SSH and web server ports. We will drop all other traffic):

sudo service fail2ban stop
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
sudo iptables -A INPUT -j DROP

#easy way to rate-limit ssh with ufw:
# technically, we could do all of the iptables stuff with ufw
ufw enable
ufw limit ssh

If using IPv6:

ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT # (replace with your undisclosed port)
ip6tables -A INPUT -p icmpv6 -j ACCEPT
ip6tables -A INPUT -j REJECT
ip6tables -A FORWARD -j REJECT

View iptables rules:

sudo iptables -S

Save iptables rules:

sudo dpkg-reconfigure iptables-persistent
sudo service fail2ban start

Create a deploy user

sudo adduser deploy
sudo adduser deploy sudo
su deploy

Installing Ruby (2.4)

Prereq’s:

sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev nodejs

cd
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL

rbenv install 2.4.0
rbenv global 2.4.0
ruby -v

gem install bundler

Nginx

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

# Add Passenger APT repository
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update

# Install Passenger & Nginx
sudo apt-get install -y nginx-extras passenger

Block anything you don’t want in Nginx

vim /etc/nginx/sites-enabled/default
location ~ ^/(wp-admin|wp-content|wp-login) {
 deny all;
}

Set Cache headers

location ^~ /assets/ {
        gzip_static on;
        expires 1d;
        add_header Cache-Control "public";
}

location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
  expires 30d;
  access_log off;
  add_header Cache-Control "public";
}

Prevent access by IP

vim /etc/nginx/sites-enabled/default
server {
        listen 80;
        listen 443;
        server_name _put_server_ip_here;
        return 404;
}



Start Nginx:
sudo service nginx start


Nginx config:
sudo vim /etc/nginx/nginx.conf
Uncomment Phusion config – include /etc/nginx/passenger.conf;



Update passenger config:
sudo vim /etc/nginx/passenger.conf

passenger_ruby /home/deploy/.rbenv/shims/ruby;
passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
#passenger_ruby /usr/bin/passenger_free_ruby;



Restart Nginx
sudo service nginx restart

Test Nginx Config with gixy

We will need python-pip in order to install gixy:

apt-get install python-pip
pip install --upgrade pip
pip -V

pip install gixy

gixy

Postgres

sudo apt-get install postgresql postgresql-contrib libpq-dev

Create a Database with a user (Make sure to change the app name):

sudo su - postgres
createuser --pwprompt deploy
createdb -O deploy my_app_name_production
exit

Capistrano

On your local machine add to your Gemfile:

gem 'capistrano', '~> 3.7', '>= 3.7.1'
gem 'capistrano-passenger', '~> 0.2.0'
gem 'capistrano-rails', '~> 1.2'

group :production do
  gem 'capistrano-rbenv', '~> 2.1'
end

Generate configs:

cap install STAGES=production

Update Capfile:

# If you are using rbenv add these lines:
# require 'capistrano/rbenv'
# set :rbenv_type, :user
# set :rbenv_ruby, '2.4.0'

Update deploy.rb:

set :application, "my_app_name"
set :repo_url, "[email protected]:me/my_repo.git"

set :deploy_to, '/home/deploy/my_app_name'

append :linked_files, "config/database.yml", "config/secrets.yml"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "vendor/bundle", "public/system", "public/uploads"

Update production.rb with the server IP:
server ‘127.0.0.1’, user: ‘deploy’, roles: %w{app db web}

Had to manually create database.yml and secrets.yml for now in /home/deploy/my_app_name/shared/config and copy connection data. This should be automated later

Update /etc/nginx/sites-enabled/default

server {
        listen 80;
        listen [::]:80 ipv6only=on;

        server_name mydomain.com;
        passenger_enabled on;
        rails_env    production;
        root         /home/deploy/my_app_name/current/public;
        
#action cable config:
        location /cable {
                 passenger_app_group_name app_name_websocket;
                 passenger_force_max_concurrent_requests_per_process 0;
        }
}

Redis

sudo apt-get install redis-server

sudo service nginx restart

Time Synchronization

sudo apt-get update
sudo apt-get install ntp

systemctl start ntp.service
sudo ntpq -p

Let’s Encrypt (optional step)

Install certbot

apt-get install software-properties-common
add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install certbot

Add to /etc/nginx/sites-available/default (this may be necessary for the Webroot plugin, which we’ll use to generate the certs)

        location ~ /.well-known {
                expires $expires;
        }

Verify config and restart:

nginx -t		
systemctl restart nginx

Get your webroot path from /etc/nginx/sites-available/default

Generate certs(replace domain an dweb-root-path):

certbot certonly --webroot --webroot-path=/var/www/html -d example.com -d www.example.com

Add a snippet:
vim /etc/nginx/snippets/ssl-example.com.conf
Paste the following:

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

And another one for ssl params:
vim /etc/nginx/snippets/ssl-params.conf

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

Update nginx config (this will allow both http and https):
vim /etc/nginx/sites-available/default

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;

    server_name example.com www.example.com;
    include snippets/ssl-example.com.conf;
    include snippets/ssl-params.conf;
...

Automate certificate renewal:
crontab -e

15 1 * * * /usr/bin/certbot renew --quiet --renew-hook "/bin/systemctl reload nginx"
20 1 * * * nginx -s reloadv

The above will run at 1:15 am every day, then reloads nginx config at 1:20 am.

To view certs expiration dates:

apt-get install  ssl-cert-check
ssl-cert-check -c /etc/letsencrypt/live/example.com/cert.pem

Additional Mac Setup

brew install ssh-copy-id
ssh-copy-id [email protected]
This will allow you to ssh without a password the next time you ssh.

Useful logs

cat /var/log/redis/redis-server.log
sudo cat /var/log/fail2ban.log
sudo cat /var/log/nginx/error.log

fail2ban grouped by IP:

awk '($(NF-1) = /Ban/){print $NF}' /var/log/fail2ban.log | sort | uniq -c | sort -n

Useful monitoring commands

Ram usage:
free -m
top

Space:
df -ah
du -hs * | sort -h

Network activity:
netstat -tulpn

Linode Monitoring – Longview

Linode also provides Longview – web UI to access your server stats. The free version is limited to the last 24 hours.
Here is the official guide
I had to update iptables rules and restart the longview service:

iptables -I INPUT -s longview.linode.com -j ACCEPT
iptables -I OUTPUT -d longview.linode.com -j ACCEPT 
sudo service longview restart

Useful links:

Now, while it’s fun figuring these things out, git push heroku master sounds a lot easier.

Categories: Rails, Ruby