Git basic

I’m a noob in using Git, and I’m using Github to host some of the code I wrote. Here are some basic commands to host a project with Github (or using Git revision control generally) (sometimes I forgot the command or the order of which command goes first)

Global setup:

Set up git

git config --global user.name "Your Name"
git config --global user.email [email protected]

Next steps:

mkdir project-name
cd project-name
git init
touch README
git add README
git commit -m 'first commit'
git remote add origin [email protected]:username/project-name.git
git push -u origin master

Existing Git Repo?

cd existing_git_repo
git remote add origin [email protected]:username/project-name.git
git push -u origin master

To commit changes (and optionally push changes to Github)

git add .
git commit -m 'commit message'
git push -u origin master

Common SVN users may find it confusing when using Git. Here’s some of the tips:

  • There are no centralized server or repo. You are the server / repo
  • In SVN, what’s in your computer is working copy and the center for committing changes is the repository, In Git, your working copy is your repository.
  • In SVN, project members checked out a working copy and commit changes to a centralized repository. In Git, everyone in a project can be the repository, can checkout (pull) and commit (push) changes to each other
    (Note: In Git, ‘checkout’ is called `pull`, ‘commit’ is saving changes to your own repository, ‘push’ is sending and merging changes to remote repository

Cloudflare & phpBB3

Session handling in phpBB3 requires a user to use one IP address per session, therefore if your IP address changed, you will be prompt to login again.

Cloudflare is a web caching service that cache static contents of a website by acting as the nameserver that manage the website domain name. Therefore, every request to the domain name will pass through Cloudflare system first and any cached contents will be served from Cloudflare servers instead of the web hosting server.

However, when using Cloudflare service, the REMOTE_ADDR value for PHP $_SERVER will follow Cloudflare servers IP address. Therefore, you might face problem when using phpBB (or any other web app that rely on session tied to the IP address) with this service.

So, we need to edit phpBB session.php file to grab the correct variable for the users’ real IP address. Edit includes/session.php

Find:

$this->ip = (!empty($_SERVER['REMOTE_ADDR'])) ? (string) $_SERVER['REMOTE_ADDR'] : '';

Replace with:

$this->ip = (!empty($_SERVER['HTTP_CF_CONNECTING_IP']))
          ? (string) $_SERVER['HTTP_CF_CONNECTING_IP']
          : ((!empty($_SERVER['REMOTE_ADDR'])) ? (string) $_SERVER['REMOTE_ADDR'] : '');

HTTP_CF_CONNECTING_IP is special header value passed by Cloudflare servers containing visitors’ real IP address, and in case this variable not set, we use the good ol REMOTE_ADDR

phpBB SEO Ultimate URL backslash in URL

In case someone stumbled upon this problem:

1. You download and install the new release of phpBB (3.0.9) and immediately integrate SEO Ultimate URL package
2. The setup went well except you found out that the page is looked ugly because the CSS wasn’t loaded – cause by the URL got backslash in it:

http://localhost//style.php …

To fix, you need to edit the data in the config table:
1. Access the phpBB3 config table (phpbb_config)
2. Lookup for config_name = ‘script_path’
3. You’ll see the value is ‘/’. Change this to ‘/’
4. Save the data

Clear the cache, and reload the page. You should see the pretty page back again!

Additional: I’m using localhost with custom port number (82) as testing platform. So in case you’re not getting the image out on the page (caused by the URL generated become http://localhost/style… instead of http://localhost:82/style…, so you need to edit the force server URL settings

1. Go to admin control panel, General > Server Settings
2. Set ‘Force Server URL’ to ‘Yes’

PHP Data Validation

WordPress data validation has some good reference in doing data validation, especially the philosophy part. For best practice, always use format correction first, then use whitelist.

// format correction
$action = (int) $_GET['action'];
// whitelist
switch ($action) {
    case 1:
        do_this();
        break;
    case 2:
        do_that();
        break;
    case 0:
    default:
        die("Don't know this action!");
}

However the above example is only for one input ($_GET[‘action’]). What if your web app (or controller) need  a few inputs, and surely you don’t want to write the same code over again, like this

// correct the format
$id = (int) $_GET['id'];
$name = (string) trim($_POST['name']);
$email = (string) trim($_POST['email');
$about = (string) htmlspecialchars(trim($_POST['about']), ENT_QUOTES, 'UTF-8');
// then do validation
if ($id == 0) {
    $id = 1;
}
if (empty($name)) {
    $msg = 'Name is required';
}
...

Most PHP frameworks I know whether validate the input one by one or using just one input source (GET or POST or COOKIE). It’s better to use $_REQUEST, since some input can be passed in <form> or just query string. E.g. /blog/post/?id=10&do=edit, then inside the page got <input type="hidden" name="do" value="save" />, now you can use both input from $_REQUEST[‘do’], which determine what action need to be done

$do = (string) $_REQUEST['do'];
switch ($do) {
    case 'edit':
        get_post($id);
        break;
    case 'save':
        save_post($id);
        break;
    case 'view':
    default:
        view_post($id);
        break;
}

With that pattern, here comes PHP filter functions, filter_input_array(). It receive an array of arguments, and source type (GET/POST etc.) and validate each of the input. It will return false on failed validation. This function is really convenient when we need to validate a long list of inputs.

However there’s a drawback. Since all the validation can be put in one input key argument (‘input_name’ => array(…list of various validators…), it is difficult to provide accurate error message, of which validation that failed. Therefore we can recreate another filter_input_array(), and put in the previous pattern (format first, then whitelist) into it. So basically the validation function work like this:

$input = validate_input(array(
    'id' => array('filter' => 'int', 'options' => array(
        'range' => array('max' => 1000, 'msg' => 'Max. value for ID is 1000')
    )),
    'name' => array('filter' => 'string', 'options' => array(
        'required' => array('value' => true, 'msg' => 'This input is required'),
        'alphanumeric' => array('value' => true, 'msg' => 'Name need to be alphanumeric')
    ))
));
// now we can use $input
$input['id'];
$input['name'];
// to get error msg
$err_msg['id']; // which may contain the error msg of specific validator
$err_msg['name'];
// to check if the whole form passed the validator or not, simply:
if (empty($err_msg)) {
    // form is valid
}

Additional note: String input.

To sanitize the string input, actually trim() is enough, and can be directly stored in database. You only need to sanitize the value only when outputting it. When echo() the value to HTML, make sure to always encode it first:

<p class="comments"><?php echo htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); ?></p>

This not only can prevent XSS issue, but also display the correct Unicode character. Avoid htmlentities() as it may corrupt the Unicode characters

Apache Caching Proxy Server

This setup is using Apache 2.2 bundled with XAMPP, in Windows 7

Create new config file: /xampp/apache/conf/extra/httpd-cache-proxy.conf

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so

Listen 3128
NameVirtualHost *:3128
<VirtualHost *:3128>
    ErrorLog "logs/proxy-error.log"
    CustomLog "logs/proxy-access.log" combined

    <IfModule mod_proxy.c>
      ProxyRequests On
      ProxyVia On
      <Proxy *>
        Order deny,allow
        Deny from all
        Allow from 127.0.0.1
      </Proxy>
    </IfModule>

    <IfModule mod_cache.c>
      LoadModule disk_cache_module modules/mod_disk_cache.so
      <IfModule mod_disk_cache.c>
        CacheEnable disk /
        CacheRoot "c:/xampp/apache/proxy/cache"
        CacheDirLevels 3
        CacheDirLength 5
        CacheMaxFileSize 10485760
        CacheMaxExpire 2592000
      </IfModule>
      ProxyTimeout 60
    </IfModule>
</VirtualHost>

Include this file to /xampp/apache/conf/httpd.conf

Include conf/extra/httpd-cache-proxy.conf

Make sure to create folder for CacheRoot. Restart Apache using XAMPP control panel or Windows Services (if you installed as service), and set browser’s proxy server to 127.0.0.1:3128.

Install app to Android SDK

Assuming you have installed Android SDK, (in my case, I don’t have Android Market installed)

  1. Download APK from internet, save to computer
  2. Copy APK from computer to emulator’s SD card using adb command. (Make sure Android emulator is running)
    android-sdk-windows/platform-tools/adb push file.apk /sdcard/
  3. Open browser in Android, point to file:///sdcard/file.apk
  4. Choose ‘Install this app’

Squid 2 cache configuration

Get source (get the latest stable version):

cd /tmp
wget http://www.squid-cache.org/Versions/v2/2.7/squid-2.7.STABLE9.tar.bz2

Install compiler

yum install gcc

Compile & install squid

./configure --prefix=/opt/squid
make all
make install

Squid configuration (in /opt/squid/etc folder):

squid.conf

acl all src all
acl manager proto cache_object
acl localhost src 127.0.0.1/32
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32
acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
acl SSL_ports port 443
acl Safe_ports port 80      # http
acl Safe_ports port 21      # ftp
acl Safe_ports port 443     # https
acl Safe_ports port 70      # gopher
acl Safe_ports port 210     # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280     # http-mgmt
acl Safe_ports port 488     # gss-http
acl Safe_ports port 591     # filemaker
acl Safe_ports port 777     # multiling http
acl CONNECT method CONNECT
http_access allow manager localhost
http_access deny manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost
http_access allow localnet
http_access deny all
icp_access allow localnet
icp_access deny all
http_port 3128
hierarchy_stoplist cgi-bin ?
cache_mem 2048 MB
maximum_object_size_in_memory 1024 KB
cache_dir ufs /var/cache/squid 10000 16 256
maximum_object_size 128 MB
access_log /var/log/squid/access.log squid
cache_log /var/log/squid/cache.log
cache_store_log /var/log/squid/store.log
pid_filename /var/run/squid.pid
netdb_filename /var/log/squid/netdb.state
storeurl_rewrite_children 50
refresh_pattern ^ftp: 1440 20% 10080 ignore-no-cache override-expire ignore-private
refresh_pattern ^http://[A-Za-z0-9]+.lscache[0-9].c.youtube.com    9999999 90% 999999999 ignore-no-cache override-expire ignore-private
refresh_pattern ^http://[a-z0-9]+.youtube.com                        9999999 90% 999999999 ignore-no-cache override-expire ignore-private
refresh_pattern ^http://[a-z]+.youtube.com                           9999999 90% 999999999 ignore-no-cache override-expire ignore-private
refresh_pattern ^http://[a-z0-9]+.ytimg.com                          9999999 90% 999999999 ignore-no-cache override-expire ignore-private
refresh_pattern ^http://*.youtube.com     9999999  90%  999999999 ignore-no-cache override-expire ignore-private
refresh_pattern get_video?video_id         9999999  90%  999999999 ignore-no-cache override-expire ignore-private
refresh_pattern youtube.com/get_video?    9999999  90%  999999999 ignore-no-cache override-expire ignore-private
refresh_pattern ^http://*.youtube.com/.*    9999999  100% 999999999 ignore-no-cache override-expire ignore-private
refresh_pattern (get_video?|videoplayback?|videodownload?)    10080 99.99999% 999999 override-expire ignore-reload ignore-private negative-ttl=0
refresh_pattern -i ^http://kh(.*?).google.com(.*?)/(.*?)$   999999 90% 9999999 override-expire ignore-reload ignore-no-cache ignore-private
refresh_pattern -i ^http://mt(.*?).google.com(.*?)/(.*?)$   999999 90% 9999999 override-expire ignore-reload ignore-no-cache ignore-private
refresh_pattern -i ^http://i(.*?).wikimapia.org(.*?)/(.*?)$ 999999 90% 9999999 override-expire ignore-reload ignore-no-cache ignore-private
refresh_pattern -i .(gif|png|jpg|jpeg|ico)$                                           999999 90% 9999999 override-expire ignore-reload ignore-no-cache ignore-private
refresh_pattern -i .(iso|avi|wav|mp3|mp4|mpeg|swf|flv|x-flv|mpg|wma|ogg|wmv|asx|asf)$ 999999 90% 9999999 override-expire ignore-reload ignore-no-cache ignore-private
refresh_pattern -i .(deb|rpm|exe|zip|tar|tgz|ram|rar|bin|ppt|doc|tiff|pdf|jar)$       999999 90% 9999999 override-expire ignore-reload ignore-no-cache ignore-private
refresh_pattern ^gopher:          1440 0%  1440
refresh_pattern -i (/cgi-bin/|?) 0    0%  0
refresh_pattern .                 0    40% 4320
acl store_rewrite_list url_regex -i .youtube.com/get_video?
acl store_rewrite_list url_regex -i .youtube.com/videoplayback.youtube.com/videoplay .youtube.com/get_video?
acl store_rewrite_list url_regex -i .youtube.[a-z][a-z]/videoplayback.youtube.[a-z][a-z]/videoplay .youtube.[a-z][a-z]/get_video?
acl store_rewrite_list url_regex -i .googlevideo.com/videoplayback.googlevideo.com/videoplay .googlevideo.com/get_video?
acl store_rewrite_list url_regex -i .google.com/videoplayback.google.com/videoplay .google.com/get_video?
acl store_rewrite_list url_regex -i .google.[a-z][a-z]/videoplayback.google.[a-z][a-z]/videoplay .google.[a-z][a-z]/get_video?
acl store_rewrite_list url_regex -i (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/videoplayback?
acl store_rewrite_list url_regex -i (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/videoplay?
acl store_rewrite_list url_regex -i (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/get_video?
acl store_rewrite_list url_regex -i http://video..*fbcdn.net.*.mp4.*
acl store_rewrite_list url_regex -i http://.[0-9].[0-9][0-9].channel.facebook.com/.*
acl store_rewrite_list url_regex -i http://.*.mp4?
acl store_rewrite_list url_regex -i http://www.facebook.com/ajax/flash/.*
acl store_rewrite_list url_regex -i http://.*.ak.fbcdn.net/.*
acl store_rewrite_list url_regex -i .geo.yahoo.com?
storeurl_access allow store_rewrite_list
storeurl_access deny all
storeurl_rewrite_program /opt/squid/etc/youtube.pl
redirect_program /opt/squid/etc/adzap.pl
quick_abort_min 500 KB
acl shoutcast rep_header X-HTTP09-First-Line ^ICY.[0-9]
upgrade_http0.9 deny shoutcast
acl apache rep_header Server ^Apache
broken_vary_encoding allow apache
cache_mgr [email protected]
cache_effective_user squid
cache_effective_group squid
dns_nameservers 208.67.222.222
dns_nameservers 208.67.220.220
visible_hostname datacenter
coredump_dir /var/cache/squid

adzap.pl – Get from http://adzapper.sourceforge.net/

wget http://adzapper.sourceforge.net/scripts/squid_redirect
mv squid_redirect adzap.pl
chmod +x adzap.pl

youtube.pl (this file need to set as executable too – chmox +x youtube.pl)

#!/usr/bin/perl
$|=1;
while (<>) {
@X = split;
$url = $X[0];
if ([email protected]^http://(.*?)/videoplayback?(.*)id=(.*?)&.*@squid://videos.youtube.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)/videoplayback?(.*)id=(.*?)@squid://videos.youtube.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)/videoplay?(.*)id=(.*?)&.*@squid://videos.youtube.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)/videoplay?(.*)id=(.*?)@squid://videos.youtube.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)/get_video?(.*)video_id=(.*?)&.*@squid://videos.youtube.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)/get_video?(.*)video_id=(.*?)@squid://videos.youtube.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)rapidshare(.*?)/files/(.*?)/(.*?)/(.*?)@squid://files.rapidshare.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)fbcdn.net/(.*?)/(.*?)/(.*?.jpg)@squid://files.facebook.INTERNAL/[email protected]){}
elsif
([email protected]^http://contenidos2(.*?)/(.*?)@squid://files.contenidos2.INTERNAL/[email protected]){}
elsif
([email protected]^http://cdn(.*?)/([0-9a-zA-Z_-]*?.flv)@squid://files.cdn.INTERNAL/[email protected]){}
elsif
([email protected]^http://web.vxv.com/data/media/(.*?)@squid://files.vxv.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)megaupload.com/files/(.*?)/(.*?)@squid://files.megaupload.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)mediafire.com/(.*?)/(.*?)@squid://files.megaupload.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)depositfiles.com/(.*?)/(.*?)/(.*?)@squid://files.megaupload.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?).files.youporn.com/(.*?)/([0-9a-zA-Z_-]*?.flv)?.*@squid://videos.youporn.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?).tube8.com/(.*?)/([0-9a-zA-Z_-]*?.flv)?.*@squid://videos.tube8.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?).tube8.com/(.*?)/([0-9a-zA-Z_-]*?.flv)@squid://videos.tube8.INTERNAL/[email protected]){}
elsif
([email protected]^http://(.*?)megaporn.com/files/(.*?)/(.*?)@squid://files.megaporn.INTERNAL/[email protected]){}
print "$urln"; }

Initialize squid swap directories

mkdir /var/cache/squid
chown squid.squid /var/cache/squid
/opt/squid/sbin/squid -z

Create log folder

mkdir /var/log/squid
chown squid.squid /var/log/squid

/etc/rc.d/init.d/squid

#!/bin/bash
### BEGIN INIT INFO
# Provides: squid
# chkconfig: - 90 25
# pidfile: /var/run/squid.pid
# config: /opt/squid/etc/squid.conf
# Short-Description: starting and stopping Squid Internet Object Cache
# Description: Squid - Internet Object Cache. Internet object caching is 
# 	a way to store requested Internet objects (i.e., data available 
# 	via the HTTP, FTP, and gopher protocols) on a system closer to the 
#	requesting site than to the source. Web browsers can then use the 
#	local Squid cache as a proxy HTTP server, reducing access time as 
#	well as bandwidth consumption.
### END INIT INFO

PATH=/usr/bin:/sbin:/bin:/usr/sbin
export PATH

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

#if [ -f /etc/sysconfig/squid ]; then
#  . /etc/sysconfig/squid
#fi

# don't raise an error if the config file is incomplete
# set defaults instead:
SQUID_OPTS=${SQUID_OPTS:-"-D"}
SQUID_PIDFILE_TIMEOUT=${SQUID_PIDFILE_TIMEOUT:-20}
SQUID_SHUTDOWN_TIMEOUT=${SQUID_SHUTDOWN_TIMEOUT:-100}

# determine the name of the squid binary
#[ -f /opt/squid/sbin/squid ] && SQUID=squid
SQUID=/opt/squid/sbin/squid

if [ $1 == 'status' ]; then
	[ -z "$SQUID" ] && exit 4
else
	[ -z "$SQUID" ] && exit 1
fi

prog="$SQUID"

# determine which one is the cache_swap directory
CACHE_SWAP=`sed -e 's/#.*//g' /opt/squid/etc/squid.conf | 
	grep cache_dir |  awk '{ print $3 }'`
[ -z "$CACHE_SWAP" ] && CACHE_SWAP=/var/spool/squid

RETVAL=0

probe() {
	# Check that networking is up.
	[ ${NETWORKING} = "no" ] && exit 1

	# check if the squid conf file is present
	[ -f /opt/squid/etc/squid.conf ] || exit 6
}    

start() {
	probe

    for adir in $CACHE_SWAP; do
		if [ ! -d $adir/00 ]; then
			 echo -n "init_cache_dir $adir... "
			 $SQUID -z -F -D >> /var/log/squid/squid.out 2>&1
		fi
    done
    echo -n $"Starting $prog: "
    $SQUID $SQUID_OPTS >> /var/log/squid/squid.out 2>&1
    RETVAL=$?
    if [ $RETVAL -eq 0 ]; then
       timeout=0;
       while : ; do
          [ ! -f /var/run/squid.pid ] || break
	  if [ $timeout -ge $SQUID_PIDFILE_TIMEOUT ]; then
	     RETVAL=1
	     break
	  fi
	  sleep 1 && echo -n "."
	  timeout=$((timeout+1))
       done
    fi
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SQUID
    [ $RETVAL -eq 0 ] && echo_success
    [ $RETVAL -ne 0 ] && echo_failure
    echo
    return $RETVAL
}

stop() {
    echo -n  $"Stopping $prog: "
    $SQUID -k check >> /var/log/squid/squid.out 2>&1
    RETVAL=$?
    if [ $RETVAL -eq 0 ] ; then
        $SQUID -k shutdown &
    	rm -f /var/lock/subsys/$SQUID
	timeout=0
 	while : ; do
		[ -f /var/run/squid.pid ] || break
		if [ $timeout -ge $SQUID_SHUTDOWN_TIMEOUT ]; then
		    echo
		    return 1
		fi
		sleep 2 && echo -n "."
		timeout=$((timeout+2))
    	done
	echo_success
	echo
    else
    	echo_failure
	echo
    fi
    return $RETVAL
}    

reload() {
    $SQUID $SQUID_OPTS -k reconfigure
}

restart() {
    stop
    start
}    

condrestart() {
    [ -e /var/lock/subsys/squid ] && restart || :
}

rhstatus() {
    status $SQUID && $SQUID -k check
}

case "$1" in
start)
    start
    ;;

stop)
    stop
    ;;

reload)
    reload
    ;;

restart)
    restart
    ;;

condrestart)
    condrestart
    ;;

status)
    rhstatus
    ;;

probe)
    probe
	return 0
    ;;

*)
    echo $"Usage: $0 {start|stop|status|reload|restart|condrestart|probe}"
    exit 2
esac

exit $?

Install squid service

chmod +x /etc/rc.d/init.d/squid
chkconfig --add squid
chkconfig squid on
service squid start

SSH Public Key Auth

This setup enable users to log in to remote machine without having to enter their password.

On local machine, generate public/private key pair:

ssh-keygen -t rsa

or use PuTTYgen

Send to remote machine:

cat ~/.ssh/id_rsa.pub | ssh [email protected] "cat - >> ~/.ssh/authorized_keys"

or

scp ~/.ssh/id_rsa.pub [email protected]:~/.ssh/authorized_keys

or use WinSCP

Note: when generating public keys using puttygen, reorganize the file content:

ssh-rsa <pub key string> [email protected]
# in one line

<pub key string> is:

--- BEGIN ...
Comment: ...
<pub key string>
--- END ...

Set permission:

chmod 600 ~/.ssh/authorized_keys

To login from local machine:

ssh [email protected]
# automatically logged in, no password prompt

or using PuTTy:

  • Connection > Data > Auto-login username: user
  • Connection > SSH > Auth > Private key file: *.ppk file generated by puttygen

Extra, SSH server configuration tweak (change those config values).

vim /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no