Pages

Monday, September 1, 2025

n8n

Install Node.js
$ wget -qO- https://deb.nodesource.com/setup_24.x | sudo bash
$ sudo apt install nodejs
$ sudo npm install -g npm@11.5.2
Install n8n globally.
$ sudo npm install n8n -g
$ chmod 600 .n8n/config

Create service file
$ mkdir -p ~/.config/systemd/user
$ cat ~/.config/systemd/user/n8n.service
[Unit]
Description=n8n service
After=network-online.target

[Service]
ExecStart=/usr/bin/n8n
Restart=on-failure
Environment="DB_SQLITE_POOL_SIZE=15"
Environment="N8N_RUNNERS_ENABLED=true"
Environment="N8N_SECURE_COOKIE=false"  # allow access over http
Environment="WEBHOOK_URL=http://domain.example.com"
Environment="N8N_DEFAULT_BINARY_DATA_MODE=filesystem"

[Install]
WantedBy=default.target

Reload the configuration
systemctl --user daemon-reload
Enable the Service
systemctl --user enable n8n
Start the Service
systemctl --user start n8n
Check status:
systemctl --user status n8n

Editor is now accessible via:
http://IP_Address:5678

Display IP Addresses
$ ip addr
List the process using the port.
$ lsof -i :5678

---------------------------------
Start vcXsrv
C:\> set DISPLAY=localhost:0.0
$ ssh -Y spark_admin@spark
--------------------------------------

Friday, January 24, 2025

Windows Subsytem - Ubuntu

Install latest NVIDIA Driver for GPU Support. 
Do not install any Linux display driver in WSL.

PS C:\> nvidia-smi

Turn Windows features on or off
Check the boxes for Virtual Machine Platform and Windows Subsystem for Linux
Click OK and restart computer.

Ensure you have the latest WSL kernel.
PS C:\> wsl --update

PS C:\> wsl --version

Display a list of available distributions
PS C:\> wsl --list --online
The following is a list of valid distributions that can be installed.
Install using 'wsl.exe --install <Distro>'.

NAME                            FRIENDLY NAME
Ubuntu                          Ubuntu
Debian                          Debian GNU/Linux
kali-linux                      Kali Linux Rolling
Ubuntu-18.04                    Ubuntu 18.04 LTS
Ubuntu-20.04                    Ubuntu 20.04 LTS
Ubuntu-22.04                    Ubuntu 22.04 LTS
Ubuntu-24.04                    Ubuntu 24.04 LTS
OracleLinux_7_9                 Oracle Linux 7.9
OracleLinux_8_7                 Oracle Linux 8.7
OracleLinux_9_1                 Oracle Linux 9.1
openSUSE-Leap-15.6              openSUSE Leap 15.6
SUSE-Linux-Enterprise-15-SP5    SUSE Linux Enterprise 15 SP5
SUSE-Linux-Enterprise-15-SP6    SUSE Linux Enterprise 15 SP6
openSUSE-Tumbleweed             openSUSE Tumbleweed

Increase swap space. Remove .txt extension.
PS C:\> type .wslconfig
[wsl2]
memory=4GB
swap=16GB
networkingMode=mirrored   # Use same Windows IP

Install a Windows Subsystem for Linux distribution.
PS C:\> wsl --install -d Ubuntu
Create a default Unix user account: 
New password:
Retype new password:
passwd: password updated successfully

$ cat /etc/os-release
NAME="Ubuntu"
VERSION_ID="24.04"

Update and Upgrade packages
$ sudo apt update
$ sudo apt list --upgradable
$ sudo apt ugrade

Enable systemd in /etc/wsl.conf
[boot]
systemd=true

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            3920         456        3500           2         156        3464
Swap:          16384           0       16384

Show the status of Windows Subsystem for Linux
PS C:\> wsl --status
Default Distribution: Ubuntu
Default Version: 2

PS C:\> wsl -l -v
  NAME      STATE           VERSION
* Ubuntu    Running         2

Set the distribution as the default
PS C:\> wsl -s <distro>

WSL2 instance IP
C:\> wsl hostname -I

Terminate the distribution
PS C:\Users\nbitra> wsl -t <distro>

Terminate the distribution and VM
PS C:\> wsl --shutdown -d Ubuntu

Launch the distribution
PS C:\> wsl -d Ubuntu

The VHDX file location
$HOME\AppData\Local\wsl\

Uninstall the distribution and delete the root filesystem
PS C:\> wsl --unregister <distro>
Unregistering...

To connect from another host to ComfyUI:

ComfyUI Port Forwarding from Windows host to WSL2 instance.
C:\> netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=<ComfyUI_Port> connectaddress=<WSL2_IP> connectport=<ComfyUI_Port>

Inbound Rule
C:\> netsh advfirewall firewall add rule name="ComfyUI" dir=in protocol=TCP localport=<ComfyUI_Port> action=allow

Saturday, October 20, 2018

OrbitDB: Decentralized Database on IPFS

OrbitDB is a serverless, distributed, peer-to-peer database. OrbitDB uses IPFS as it's data storage and IPFS Pubsub to automatically sync databases with peers. The metadata of databases is stored in data directory orbitdb. The data blocks saved in IPFS are in orbitdb/ipfs.

Download Node.js in /tmp from https://nodejs.org/dist/v8.12.0/node-v8.12.0-linux-x64.tar.xz

$ tar xvf /tmp/node-v8.12.0-linux-x64.tar.xz
$ ln -s node-v8.12.0-linux-x64 node

Update PATH in .bashrc with ~/node/bin

$ npm install ipfs -g --save

$ jsipfs init
initializing ipfs node at $HOME/.jsipfs
generating 2048-bit RSA keypair...done
peer identity: QmSANGMer1x8YGHYtoer8kStRXuWCj1DN1F28zKeW3JwwR
to get started, enter:

jsipfs cat /ipfs/QmfGBRT6BbWJd7yUc2uYdaUZJBbnEFvTqehPFoSMQ6wgdr/readme

$ jsipfs daemon
Initializing daemon...
Swarm listening on /ip4/127.0.0.1/tcp/4003/ws/ipfs/QmSANGMer1x8YGHYtoer8kStRXuWCj1DN1F28zKeW3JwwR
Swarm listening on /ip4/127.0.0.1/tcp/4002/ipfs/QmSANGMer1x8YGHYtoer8kStRXuWCj1DN1F28zKeW3JwwR
Swarm listening on /ip4/192.168.1.15/tcp/4002/ipfs/QmSANGMer1x8YGHYtoer8kStRXuWCj1DN1F28zKeW3JwwR
API is listening on: /ip4/127.0.0.1/tcp/5002
Gateway (readonly) is listening on: /ip4/127.0.0.1/tcp/9090
Daemon is ready

Web Console URL   : http://127.0.0.1:5002/webui
Local Gateway URL : http://127.0.0.1:9090/ipfs/

$ ls mysite
img index.html
$ jsipfs add -r mysite
added QmcMN2wqoun88SVF5own7D5LUpnHwDA6ALZnVdFXhnYhAs mysite/img/spacecat.jpg
added QmS8tC5NJqajBB5qFhcA1auav14iHMnoMZJWfmr4k3EY6w mysite/img
added QmYh6HbZhHABQXrkQZ4aRRSoSa6bb9vaKoHeumWex6HRsT mysite/index.html
added QmYeAiiK1UfB8MGLRefok1N7vBTyX8hGPuMXZ4Xq1DPyt7 mysite/

$ npm install orbit-db-cli -g --save

Create database. Type can be one of eventlog|feed|docstore|keyvalue|counter
$ orbitdb create hello feed
/orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello

Add an entry to database
$ orbitdb add /orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello "world"
Added QmZze55TaD55uu4TqTdkvRpwyXubNXXZRkqfqF9RbHbupP

Query the database
$ orbitdb get /orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello
"world"

Delete an entry from a database.
$ orbitdb del /orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello QmZze55TaD55uu4TqTdkvRpwyXubNXXZRkqfqF9RbHbupP
Deleted QmZze55TaD55uu4TqTdkvRpwyXubNXXZRkqfqF9RbHbupP

$ orbitdb get /orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello
Database '/orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello' is empty!

Show information about a database
$ orbitdb info /orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello
/orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello
> Type: feed
> Owner: /orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello
> Data file: ./orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello
> Entries: 2
> Oplog length: 2 / 2
> Write-access:
> 04ea354bb28cfaada7c61d926103face8d357f6d829c80737e19a0423a75147db224ae4a19e2ca35abd09818af403b300fe0a31003534fc8f1800a8ed506c33a2e

Remove a database locally.
$ orbitdb drop /orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello yes
Dropped database '/orbitdb/QmeBA9kqJkYb83xDttsZbrxjbFejj3pjdF9j52eSGCVxYW/hello'
------------------------------

$ orbitdb create demo docstore
/orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo

$ orbitdb put /orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo "{\"_id\":1,\"name\":\"FRANK\"}" --indexBy name
Added document 'FRANK'

$ orbitdb get /orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo "FRANK"
Searching for 'FRANK' from '/orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo'
┌──────────────────────────────────────────────────┬───┐
│name                                                                                                         │_id   │
├──────────────────────────────────────────────────┼───┤
│FRANK                                                                                                      │1     │
└──────────────────────────────────────────────────┴───┘
Found 1 matches (1 ms)

$ orbitdb replicate /orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo --progress
Swarm listening on /ip4/127.0.0.1/tcp/41771/ipfs/QmPyG6LGz8E6qHbq5XiafdmJo584NEKiitXfKqcTo53DV3
Swarm listening on /ip4/192.168.1.15/tcp/41771/ipfs/QmPyG6LGz8E6qHbq5XiafdmJo584NEKiitXfKqcTo53DV3
Loading '/orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo' (docstore)
Loading '/orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo' ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/1 |   0.0% | 00:00:00
████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ 1/1 | 100.0% | 00:00:00
Replicating '/orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo' 
████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ 1/1 | 100.0% | 00:00:00

$ orbitdb del /orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo "FRANK"
Deleted FRANK

$ orbitdb drop /orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo yes
Dropped database '/orbitdb/QmQzVvyVB2ZYTApek7VaejWA5gmt51S8gwdk1XnwYdN4Tq/demo'
--------------------

$ orbitdb create demo keyvalue
/orbitdb/QmSjz8cQRGHBX8vC6SRt4pMnxfUJzW6rYJhgpPd2kaHBDT/demo

$ orbitdb set /orbitdb/QmSjz8cQRGHBX8vC6SRt4pMnxfUJzW6rYJhgpPd2kaHBDT/demo "volume" 100
'volume' set to '100' (QmcnimRojRMgzKLZkHnHDnQ6G5hf1JW9mLdK6XW5WzUfqm)

$ orbitdb get /orbitdb/QmSjz8cQRGHBX8vC6SRt4pMnxfUJzW6rYJhgpPd2kaHBDT/demo volume
100

$ orbitdb del /orbitdb/QmSjz8cQRGHBX8vC6SRt4pMnxfUJzW6rYJhgpPd2kaHBDT/demo volume
Deleted volume

$ orbitdb drop /orbitdb/QmSjz8cQRGHBX8vC6SRt4pMnxfUJzW6rYJhgpPd2kaHBDT/demo yes
Dropped database '/orbitdb/QmSjz8cQRGHBX8vC6SRt4pMnxfUJzW6rYJhgpPd2kaHBDT/demo'
---------------------

$ npm install orbit-db -g --save

$ cat keyvalue.js

'use strict'

const IPFS = require('./node/lib/node_modules/ipfs')
const OrbitDB = require('./node/lib/node_modules/orbit-db')

// OrbitDB uses Pubsub which is an experimental feature
// and need to be turned on manually.

// Create IPFS instance
const ipfs = new IPFS({
  EXPERIMENTAL: {
    pubsub: true
  }
})

ipfs.on('error', (err) => console.error(err))

ipfs.on('ready', async () => {
  let db
  try {
// Create OrbitDB instance
    const orbitdb = new OrbitDB(ipfs)

// Create/Open a database
    const db = await orbitdb.kvstore('first_database')
// Load the database locally before using it
    await db.load()
// database address
    console.log(db.address.toString())
    // /orbitdb/Qmd8TmZrWASypEp4Er9tgWP4kCNQnW4ncSnvjvyHQ3EVSU/first-database
// Add an entry
    await db.put('name', 'hello')
// Get an entry
    console.log(db.get('name'))
// Query the database
// Delete an entry from a database
    await db.del(db.get('name'))
// Remove a database locally
    await db.drop(db.address)
  } catch (e) {
    console.error(e)
    process.exit(1)
  }
})

$ node keyvalue.js



Sunday, October 14, 2018

Hosting Website on IPFS

Interplanetary File System (IPFS) is an open source, peer-to-peer distributed hypermedia protocol that acts as a sort of combination of Kodemila, BitTorrent, and Git to create a distributed subsystem of the Internet. The objects in IPFS are content addressed.

Install IPFS.

$ cd /tmp
$ wget https://dist.ipfs.io/go-ipfs/v0.4.17/go-ipfs_v0.4.17_linux-amd64.tar.gz
$ tar zxvf /tmp/go-ipfs_v0.4.17_linux-amd64.tar.gz

Update $PATH with ~/go-ipfs in .bash_profile

Intialize the repository. IPFS stores all its settings and internal data in a directory called $HOME/.ipfs

$ ipfs init

initializing IPFS node at $HOME/.ipfs
generating 2048-bit RSA keypair...done
peer identity: Qmcpo2iLBikrdf1d6QU6vXuNb6P7hwrbNPW9kLAH8eG67z
to get started, enter:

ipfs cat /ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/readme

$ ipfs cat /ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/readme

Run the IPFS node

$ ipfs daemon

API server listening on /ip4/127.0.0.1/tcp/5001
Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080
Daemon is ready

List peers with open connections

$ ipfs swarm peers

Web Console URL   : http://127.0.0.1:5001/webui
Local Gateway URL : http://127.0.0.1:8080/ipfs

Add website content to IPFS

$ ls mysite
img index.html
$ ipfs add -r mysite                       --> Choose CIDv1 by passing a version flag --cid-version 1
added QmcMN2wqoun88SVF5own7D5LUpnHwDA6ALZnVdFXhnYhAs mysite/img/spacecat.jpg
added QmS8tC5NJqajBB5qFhcA1auav14iHMnoMZJWfmr4k3EY6w mysite/img
added QmYh6HbZhHABQXrkQZ4aRRSoSa6bb9vaKoHeumWex6HRsT mysite/index.html
added QmYeAiiK1UfB8MGLRefok1N7vBTyX8hGPuMXZ4Xq1DPyt7 mysite/

The hash on last line is root CID of the website.

Visit your site on local gateway node at http:/127.0.0.1:8080/ipfs/<rootCID>
Next, you can also visit it on another ipfs node by opening https://gateway.ipfs.io/ipfs/<rootCID> or https://ipfs.infura.io/ipfs/<rootCID>
When you make changes to the content, add it again to IPFS. We can get the newest version of the site at any time by publishing to IPNS. Interplanetary Name System (IPNS) is a system for creating and updating mutable links to IPFS content.

Publish to IPNS

$ ipfs name publish <NEW_rootCID>
Published to $PEER_ID: /ipfs/<NEW_rootCID>

To get rid of hashes, add DNS TXT record containing name as _dnslink and target as dnslink=/ipns/$PEER_ID
Now you should be able to view your site at http://127.0.0.1:8080/ipns/your.domain or at http://gateway.ipfs.io/ipns/your.domain
Add DNS CNAME record target as gateway.ipfs.io so that you can visit your site at http://your.domain

Add content with CID v1. The rootCID will be pinned recursively by default.
$ ipfs add -r --cid-version 1 mysite

List all objects pinned in the local storage.
$ ipfs pin ls --type=all

List only rootCIDs. 
$ ipfs pin ls --type=recursive

Unpin the target folder.
$ ipfs pin rm <rootCID>

Perform garbage collection on the repository
$ ipfs repo gc

The rootCID hash was removed from local storage, the data still exists on IPFS. The stale content is removed from the IPFS network after a period of time. So if you wanted your data to stay permanently on the network you had to pin it or use a Pinning Service.

Remote Pinning Service

As the local IPFS node is not always available, routine garbage collection and the data to stay permanently, it might be helpful to use remote pinning service.

Create an account and get an API token for free using email on https://nft.storage/

Add remote service
$ ipfs pin remote service add nft_storage https://nft.storage/api NFT_KEY

List remote service
$ ipfs pin remote service ls
nft_storage https://nft.storage/api

Remove remote service. 
$ ipfs pin remote service rm nft_storage

Pin the content remotely. In WebUI, Files > enter cid > click Browse > Click ...More > Set pinning > Check nft_storage > Click on Apply
$ ipfs pin remote add <root_CID> --service nft_storage

Unpin CID remotely
$ ipfs pin remote rm --cid <cid> --service nft_storage

Check pin status. 
https://api.nft.storage/check/<cid>
Pin Status column at https://nft.storage/files/























Sunday, June 10, 2018

Installing Vault for Streamsets


Download the Vault zip file from the Hashicorp Vault website:  https://www.vaultproject.io/downloads.html

Create vault user.
$ sudo useradd -r -g daemon -d /opt/vault -m -s /sbin/nologin -c "Vault user" vault
$ cd /tmp
$ wget https://releases.hashicorp.com/vault/0.10.1/vault_0.10.1_linux_amd64.zip
$ sudo mkdir -p /opt/vault/bin
$ cd /opt/vault/bin
$ sudo unzip /tmp/vault_0.10.1_linux_amd64.zip
$ sudo ln -s vault /usr/local/bin/vault
$ sudo chown -R vault:root /opt/vault
$ sudo chmod -R 755 /opt/vault
$ sudo mkdir /opt/vault/conf

Change to vault user
$ sudo su - vault -s /bin/bash

$ cd /opt/vault/conf
Create configuration file vault-conf.hcl
ui = true

listener "tcp" {
  address = "0.0.0.0:8200"
  tls_disable = 1
}

storage "s3" {
    bucket = "vault"
    region = "us-east-1"
}

disable_mlock=true

Start Vault
$ nohup vault server -config=/opt/vault/conf/vault-conf.hcl > /var/log/vault/vault-debug.log 2>&1 &
$ jobs
Verify Server is running
$ vault status
$ export VAULT_ADDR=http://0.0.0.0:8200
Intialize Vault
$ vault operator init
Copy keys to /opt/vault/keys/vault_keys.txt
This will output five Unseal Keys, which need to be used to unseal the Vault. The initialization defaults to minimum of three tokens to allow unseal. There will also be Initial Root Token in the output. It should only be used for initial configuration, while recurring operations should be done by policy-constrained tokens.
Unseal Vault with three different keys.
$ vault operator unseal

Authenticate with Vault as Initial Root Token to perform any operations on an unsealed Vault.
$ vault login

Enable Audit Logging
$ vault audit enable file file_path=/var/log/vault/vault-audit.log

List enabled auth methods
$ vault auth list
Path      Type     Description
----      ----     -----------
token/    token    token based credentials

Integrate Active Directory

Enable LDAP auth method.
$ vault auth enable ldap
Success! Enabled ldap auth method at: ldap/

$ vault write auth/ldap/config \
    binddn="cn=Lkup_user,ou=Standard,ou=Service Accounts,ou=Accounts,ou=abc,ou=External,ou=xyz,dc=example,dc=com" \
    bindpass='passwd' \
    deny_null_bind=true \
    discoverdn=false \
    groupattr="cn" \
    groupdn="OU=Standard,OU=Groups,OU=Prod,OU=abc,OU=External,OU=xyz,DC=example,DC=com" \
    groupfilter="(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}}))" \
    insecure_tls=true \
    starttls=false \
    url="ldap://ldap.example.com" \
    userattr="samaccountname" \
    userdn="OU=Users,OU=Accounts,OU=xyz,DC=example,DC=com"

# cat admin-policy.hcl
# List existing auth methods
path "sys/auth"
{
  capabilities = ["read"]
}

# Manage auth methods broadly across Vault
path "sys/auth/*"
{
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# List existing policies
path "sys/policy"
{
  capabilities = ["read"]
}

path "sys/policies"
{
  capabilities = ["read"]
}

# Create and manage ACL policies broadly across Vault
path "sys/policy/*"
{
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

path "sys/policies/*"
{
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# List existing KV secrets v2
path "secret"
{
  capabilities = ["read"]
}

# Create and manage KV secrets engine v2 broadly across Vault.
path "secret/*"
{
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# List existing databases
path "database"
{
  capabilities = ["read"]
}

# Create and manage databases broadly across Vault.
path "database/*"
{
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# List existing identities
path "identity"
{
  capabilities = ["read"]
}

# Create and manage identities broadly across Vault.
path "identity/*"
{
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# Read health checks
path "sys/health"
{
  capabilities = ["read", "sudo"]
}

# To perform capabilities of token
path "sys/capabilities"
{
  capabilities = ["create", "update"]
}

path "sys/capabilities-self"
{
  capabilities = ["create", "update"]
}

Create admin policy.
$ vault policy write admin admin-policy.hcl
Map LDAP group to policies. For example, map vault-admin group to admin policy.
$ vault write auth/ldap/groups/vault-admin policies=admin
-----------------------------------------------------------------------------------------
          AD Group                                      Vault Path
-----------------------------------------------------------------------------------------
vault-admin                                        secret/*
vault-admin-<env>-mysql        secret/<env>/MySQL/<key-name>
vault-admin-<env>-postgres    secret/<env>/PostgreSQL/<key-name>
vault-admin-<env>-oracle        secret/<env>/Oracle/<key-name>
vault-admin-<env>-hadoop      secret/<env>/Hadoop/<key-name>
vault-read-<env>-mysql          secret/<env>/MySQL/<key-name>
vault-read-<env>-postgres      secret/<env>/PostgreSQL/<key-name>
vault-read-<env>-oracle          secret/<env>/Oracle/<key-name>
vault-read-<env>-hadoop        secret/<env>/Hadoop/<key-name>


<env> - prod/impl/test/dev
<key-name> - oracle_instance or mysql_name etc

List enabled secret backends
$ vault secrets list
Path          Type         Description
----          ----         -----------
cubbyhole/    cubbyhole    per-token private secret storage
identity/     identity     identity store
secret/       kv           key/value secret storage
sys/          system       system endpoints used for control, policy and debugging

Disable KV secrets engine – version 1
$ vault secrets disable secret
Enable KV secrets engine - version 2
$ vault secrets enable -path=secret -description="key/value secret storage v2" -version=2 kv
List existing keys
$ vault kv list secret
$ vault kv put secret/prod/Oracle/<instance_name> username=<user> password=<passwd>
$ curl -s -H "X-Vault-Token: $VAULT_TOKEN" -x GET $VAULT_ADDR/v1/secret/data/prod/Oracle/<instance_name> | jq '.data.data.username,.data.data.password';

Configure AppRole for Streamsets

Enable AppRole auth method.
$ vault auth enable approle
Success! Enabled approle auth method at: approle/

$ vault auth list
Path        Type       Description
----        ----       -----------
approle/    approle    n/a
token/      token      token based credentials

Create a policy.
$ tee sdc-approle-pol.hcl <<EOF
# Login with AppRole
path "auth/approle/login" {
  capabilities = [ "create", "read" ]
}

# Mount the AppRole auth method
path "sys/auth/approle" {
  capabilities = [ "create", "read", "update", "delete", "sudo" ]
}

# Configure the AppRole auth method
path "sys/auth/approle/*" {
  capabilities = [ "create", "read", "update", "delete" ]
}

# Create and manage roles
path "auth/approle/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

# Write ACL policies
path "sys/policy/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

# Write test data
path "secret/test/MySQL/app-sdc/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}
EOF

$ vault policy write sdc-approle-pol sdc-approle-pol.hcl

$ vault policy list
admin
default
sdc-approle-pol
root

Create a role with policy attached.
$ vault write auth/approle/role/app-sdc policies="sdc-approle-pol" secret_id_ttl=120m token_ttl="60m" token_max_ttl="120m"

Read the role.
$ vault read auth/approle/role/app-sdc
Fetch RoleID for app-sdc role.
$ vault read auth/approle/role/app-sdc/role-id
Generate a new SecretID for app-sdc role.
$ vault write -f auth/approle/role/app-sdc/secret-id

$ vault read auth/approle/role/app-sdc/role-id
Key                 Value
---                   -----
role_id        36015ef7-875a-3765-4e74-e6b1ccdc5d3b
$ vault write -f auth/approle/role/app-sdc/secret-id
Key                   Value
---                   -----
secret_id             84d34edc-cb7f-0eb8-dc59-f91c47e9e9fe
secret_id_accessor    a8f01a19-ec71-f70c-c564-0457e7a6213a

Authenticate with Role ID and Secret ID­­­­ to receive AppRole token.
$ vault write auth/approle/login role_id="<role-id>" secret_id="<secret-id>"

Once the authentication successful, Vault will provide a token to the application that can be used to request secrets.
Streamsets can authenticate with Vault using the Role ID in properties, and Secret ID provided by a file.
Update credentialStore.vault.config.role.id= in /etc/sdc/credential-stores.properties
Create /etc/sdc/vault-secret-id file with <secret-id>

Using admin user’s token, write a Secret to the path secret/test/MySQL/app-sdc
$ vault kv put secret/test/MySQL/app-sdc sdc_mysql_user=<user> sdc_mysql_pass=<passwd>
Read secrets using the AppRole token.
$ VAULT_TOKEN=<AppRole_token> vault kv get secret/test/MySQL/app-sdc
Alternativtely, you can first authenticate with vault using the client_token.
$ vault login <AppRole_token>
$ vault kv get secret/test/MySQL/app-sdc

Launch Vault UI : http://vault_host:8200/ui

Use the following notation for credentials in streamsets pipeline to access the secrets in Vault.
${vault:read("secret/test/MySQL/app-sdc/sdc_mysql_user","value")}
${vault:read("secret/test/MySQL/app-sdc/sdc_mysql_pass","value")}

MySQL Dynamic Credentials with Vault


Enable the database secrets engine
$ vault secrets enable -description="database dynamic secret storage"  database
Configure Vault with MySQL plugin and connection information by passing role as read from any of the tables or update specific table and to be able to read from any of the tables etc. A role is a logical name that maps to a policy used to generate credentials.
$  vault write database/config/<mysql-name> \
    plugin_name=mysql-database-plugin \
    allowed_roles=<my-role> \
    connection_url="{{username}}:{{password}}@tcp(127.0.0.1:3306)/" \
    username="root" password="mysql"
Configure allowed role to create dynamic credential. The values within the {{value}} will be filled in by vault.
$ vault write database/roles/<my-role> \
    db_name=<mysql-name> \
    creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';" \
    default_ttl="1h" max_ttl="24h"

Create a policy. The client app needs to be able to read from the <my-role> role endpoint.
$ tee <my-role>-db-read.hcl <<EOF
path "database/creds/<my-role>" {
   capabilities = ["read"]
}
EOF
$ vault policy write <my-role>-db-read <my-role>-db-read.hcl

Create a new token with <my-role>-db-read policy
$ vault token create -policy="<my-role>-db-read" -wrap-ttl=20m

Generate a new set of credentials. They do not exist until they are accessed.
$ vault read database/creds/<my-role&gt;



Saturday, January 14, 2017

Install Hadoop, Hive and Spark in Cluster

Install Hadoop in cluster

On all Nodes

Update /etc/hosts
192.168.1.10 master-node-01
192.168.1.11 core-node-01
192.168.1.12 core-node-02

Uncomment in /etc/ssh/sshd_config
PasswordAuthentication yes

Configure passwordless SSH on master-node-01 only.

$ ssh-keygen
$ ssh-copy-id -i .ssh/id_rsa.pub hadoop@core-node-01
$ ssh-copy-id -i .ssh/id_rsa.pub hadoop@core-node-02

Install JDK

$ cd /tmp
$ wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http://www.oracle.com/; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u151-b12/e758a0de34e24606bca991d704f6dcbf/jdk-8u161-linux-x64.rpm
$ sudo yum localinstall jdk-8u161-linux-x64.rpm

You can also install on all nodes by looping them in the same command.

$ cat ~/hosts.lst
master-node-01
core-node-01
core-node-02

$ for each in `cat ~/hosts.lst`;do echo $each; scp jdk-8u161-linux-x64.rpm $each:/tmp; done
$ for each in `cat ~/hosts.lst`;do echo $each; ssh -t -q $each "sudo su - -c 'sudo yum -y localinstall /tmp/jdk-8u161-linux-x64.rpm'";done

$ wget http://download.oracle.com/otn-pub/java/jce/8/jce_policy-8.zip
$ unzip jce_policy-8.zip
$ sudo cp /tmp/*policy.jar /usr/java/jdk1.8.0_161/jre/lib/security/

$ for each in `cat ~/hosts.lst`;do echo $each; scp /tmp/*policy.jar $each:/tmp; done
$ for each in `cat ~/hosts.lst`;do echo $each; ssh -t -q $each "sudo su - -c 'sudo cp /tmp/*policy.jar /usr/java/jdk1.8.0_161/jre/lib/security/'";done

$ ls -l /usr/java
total 4
lrwxrwxrwx 1 root root   16 Jul 22  2014 default -> /usr/java/latest
drwxr-xr-x 9 root root 4096 Mar  6 19:20 jdk1.8.0_161
lrwxrwxrwx 1 root root   22 Mar  6 19:20 latest -> /usr/java/jdk1.8.0_161

Install Hadoop

$ cd /tmp
$ wget http://www.apache.org/dyn/closer.cgi/hadoop/common/hadoop-2.7.6/hadoop-2.7.6.tar.gz
$ cd /opt
$ sudo tar zxvf /tmp/hadoop-2.7.6.tar.gz
$ sudo ln -s hadoop-2.7.6 hadoop

Edit .bashrc

export JAVA_HOME=/usr/java/latest
export HADOOP_HOME=/opt/hadoop
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$HADOOP_HOME/bin

Edit hadoop-env.sh
$ cd $HADOOP_HOME/etc/hadoop
export JAVA_HOME=/usr/java/latest

Edit slaves

core-node-01
core-node-02

Edit core-site.xml

<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://master-node-01</value>            --> hostname or HA enabled logical URI
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/tmp/hadoop-${user.name}</value>
</property>
</configuration>

hdfs-site.xml on the NameNode:
The NameNode stores its metadata and edit logs.

<configuration>
<property>
<name>dfs.permissions.superusergroup</name>
<value>hadoop</value>
</property>
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///data1/dfs/nn</value>
</property>
</configuration>

$ sudo mkdir -p /data1/dfs/nn
$ sudo chown hdfs:hadoop /data1/dfs/nn
$ sudo chmod 700 /data1/dfs/nn

hdfs-site.xml on the DataNode:
Configure the disks on DataNode in a JBOD configuration. The DataNode stores HDFS blocks.

<configuration>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///data1/dfs/dn,file:///data2/dfs/dn</value>
</property>
</configuration>

$ sudo mkdir -p /data1/dfs/dn /data2/dfs/dn
$ sudo chown hdfs:hadoop /data1/dfs/dn /data2/dfs/dn
$ sudo chmod 700 /data1/dfs/dn /data2/dfs/dn

Edit mapred-site.xml

<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>

Edit yarn-site.xml

<configuration>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
<value>org.apache.hadoop.mapred.ShuffleHandler</value>
</property>
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value>${yarn.resourcemanager.hostname}:8031</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>${yarn.resourcemanager.hostname}:8030</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>${yarn.resourcemanager.hostname}:8032</value>
</property>
</configuration>

On master-node-01, format NameNode.

$ sudo -u hdfs hdfs namenode -format

Start Services

$ bin/start-dfs.sh
$ bin/start-yarn.sh

Create the /tmp directory.
$ sudo -u hdfs hdfs dfs -mkdir /tmp
$ sudo -u hdfs hdfs dfs -chmod -R 1777 /tmp

Check daemons on Master
$ jps
NameNode
SecondaryNameNode
ResourceManger

Check daemons on Slaves
$ jps
DataNode
NodeManager

HDFS NameNode                   : http://master-node-01:50070/
HDFS DataNode                     : http://core-node-01:50075/
YARN Resource Manager      : http://master-node-01:8088/
YARN NodeManager              : http://core-node-01:8042/
MapReduce JobHistoryServer : http://master-node-01:19888/

Install MySQL and JDBC Connector

On NameNode, install MySQL as per instructions at http://anandbitra.blogspot.com/2014/08/installing-mysql-server-on-centos-7.html

mysql> create database metastore DEFAULT CHARACTER SET utf8;
mysql> grant all on metastore.* TO 'hive'@'%' IDENTIFIED BY 'passwd';

Install the JDBC connector
Download JDBC Driver for MySQL from http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.35.tar.gz
$ tar zxvf  mysql-connector-java-5.1.35.tar.gz
$ sudo cp  mysql-connector-java-5.1.35/mysql-connector-java-5.1.35-bin.jar /usr/share/java/
$ sudo ln -s /usr/share/java/mysql-connector-java-5.1.35-bin.jar /usr/share/java/mysql-connector-java.jar

Install Hive in cluster

$ cd /tmp
$ wget http://apache.cs.utah.edu/hive/hive-2.3.2/apache-hive-2.3.2-bin.tar.gz
$ cd /opt
$ sudo tar zxvf /tmp/apache-hive-2.3.2-bin.tar.gz
$ sudo ln -s apache-hive-2.3.2 hive

Edit .bashrc

export HIVE_HOME=/opt/hive
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$HADOOP_HOME/bin:$HIVE_HOME/bin
export CLASSPATH=/opt/hadoop/lib/*:/opt/hive/lib/*

Edit hive-env.sh

$ cp hive-env.sh.template hive-env.sh
export JAVA_HOME=/usr/java/latest
export HADOOP_CONF_DIR=/opt/hadoop/etc/hadoop

Edit hive-site.xml

<configuration>
   <property>
      <name>javax.jdo.option.ConnectionURL</name>
      <value>jdbc:mysql://localhost/metastore?createDatabaseIfNotExist=true</value>
      <description>metadata is stored in a MySQL server</description>
   </property>
   <property>
      <name>javax.jdo.option.ConnectionDriverName</name>
      <value>com.mysql.jdbc.Driver</value>
      <description>MySQL JDBC driver class</description>
   </property>
   <property>
      <name>javax.jdo.option.ConnectionUserName</name>
      <value>hive</value>
      <description>user name for connecting to mysql server</description>
   </property>
   <property>
      <name>javax.jdo.option.ConnectionPassword</name>
      <value>passwd</value>
      <description>password for connecting to mysql server</description>
   </property>
</configuration>

$ hive
hive> use default;
hive> show tables;

Install Spark in cluster

$ cd /tmp
$ wget https://d3kbcqa49mib13.cloudfront.net/spark-2.2.0-bin-hadoop2.7.tgz
$ cd /opt
$ sudo tar zxvf /tmp/spark-2.2.0-bin-hadoop2.7.tgz
$ sudo ln -s spark-2.2.0-bin-hadoop2.7 spark

Edit .bashrc

export SPARK_HOME=/opt/spark
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$HADOOP_HOME/bin:$SPARK_HOME/bin

Edit spark-env.sh

$ cp spark-env.sh.template spark-env.sh
export JAVA_HOME=/usr/java/latest
export SPARK_WORKER_CORES=4
export HADOOP_CONF_DIR=/opt/hadoop/etc/hadoop

Add Slaves

$ cd $SPARK_HOME/conf

core-node-01
core-node-02

Start Spark Services

$ sbin/start-all.sh

Check daemons on Master
$ jps
Master

Check daemons on Slaves
$ jps
Worker

Spark HistoryServer : http://master-node-01:18080/

Configure Hive execution engine to use Spark
set hive.execution.engine=spark

$ pyspark

SparkContext available as sc, HiveContext available as sqlContext.
>>> from pyspark.context import SparkContext
>>> from pyspark.sql import HiveContext
>>> sqlContext = HiveContext(sc)
>>> sqlContext.sql("use default")
DataFrame[result: string]
>>> sqlContext.sql("show tables").show()


Tuesday, January 5, 2016

Nginx and Tomcat


Nginx is an open source, high performance reverse proxy, load balancer, and content cache, as well as providing extra layer of security for applications.

Add nginx Repository to /etc/yum.repos.d/nginx.repo on web server.

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/rhel/6/$basearch/
enabled=1
gpgcheck=1
gpgkey=http://nginx.org/keys/nginx_signing.key

Install nginx
$ sudo yum install nginx

Start nginx
$ sudo service nginx start

Ensure nginx starts at boot.
$ sudo chkconfig --list nginx
nginx           0:off   1:off   2:on    3:on    4:on    5:on    6:off

Adjust the parameters in /etc/nginx/nginx.conf for optimal performance.

worker_processess 1;   --> set to number of CPUs.
worker_rlimit_nofile 200000;
access_log off;
send_file on;
tcp_nopush on;
tcp_nodelay off;
keepalive_requests 100000;
keepalive_timeout 30;
reset_timedout_connection on;
client_body_timeout 10;
send_timeout 2;
gzip on;

Restrict access to nginx server

$ sudo iptables -I INPUT -p tcp -s 0.0.0.0/0 --dport 443 -j ACCEPT


Setup locally-mounted DVD as yum repository

Mount DVD media
$ mount -o loop /dev/sr0 /mnt

$ sudo cp /mnt/media.repo /etc/yum.repos.d/rhel6dvd.repo
$ sudo chmod 644 /etc/yum.repos.d/rhel6dvd.repo

Change gpgcheck=0 setting to 1 and add the below lines.

enabled=1
baseurl=file:///mnt/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

Clear the related caches.
$ sudo yum clean all
$ sudo subscription-manager clean

Install memcached 
$ sudo yum install php-pecl-memcached memcached

Change CACHESIZE and -l in /etc/memcached.conf
Locate -m parameter and change its value to at least 1GB
Locate -l parameter and change its value to 127.0.0.1 or localhost.

Start memcached
$ sudo service memcached start

Ensure memcached starts at boot.
$ sudo chkconfig memcached on

Nginx comes with built-in memcached module to obtain responses from a memcached server. If the content does not exist in cache, the module will raise an error which we catch and redirect to application server for processing.

# cat /etc/nginx/sites-available/default
server {
    listen 80;
    server_name domain;

    location / {
        set  $memcached_key    "$uri?$args";
        memcached_pass    127.0.0.1:11211;
        error_page        404 502 504 = @cache_miss;
    }
    location @cache_miss  {
        proxy_pass  http://app_server:8080/;
    }
}

Load Balancing

The requests are proxied to the application server group myapp1 in round-robin.

upstream myapp1 {
        server appsrv1.example.com;
        server appsrv2.example.com;
        server appsrv3.example.com;
    }

Create a Self-Signed SSL Certificate

Generate a Private Key
$ sudo openssl genrsa -des3 -out server.key 2048

Generate a CSR
$ sudo openssl req -new -key server.key -out server.csr

Remove Passphrase from Key
$ sudo openssl rsa -in server.key.org -out server.key

Generate Self-Signed Certificate
$ sudo openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Hardening SSL Configuration

    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 10m;

    ssl_prefer_server_ciphers     on;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers     ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
    add_header Strict-Transport-Security "max-age=31536000";


For the domains domain1 and domain2 to deliver content from different ROOT contexts for app1 and app2 with corresponding virtual hosts app1_host and app2_host respectively, deploy each application on tomcat instance under /opt/tomcat and then put a reverse proxy in front of them.

Create the file /etc/nginx/conf.d/domain1.conf with the following contents. Configure as above to use memcached and add hardening SSL after ssl_certicate_key.

server {
    listen    443;
    ssl  on;
    ssl_certificate     /etc/ssl/domain1.crt;
    ssl_certificate_key /etc/ssl/domain1.key;

    server_name  domain1;
    access_log /var/log/nginx/domain1.access.log;
    error_log /var/log/nginx/domain1.error.log;

    location / {
       proxy_set_header X-Real-IP  $remote_addr;
       proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header Host $host;
        proxy_pass http://app1_host:8080/;
    }
}

And then create the file /etc/nginx/conf.d/domain2.conf with the following contents:

server {
    listen    443;
    ssl  on;
    ssl_certificate     /etc/ssl/domain2.pem;
    ssl_certificate_key /etc/ssl/domain2.key;

    server_name  domain2;
    access_log /var/log/nginx/domain2.access.log;
    error_log /var/log/nginx/domain2.error.log;

    location / {
       proxy_set_header X-Real-IP  $remote_addr;
       proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header Host $host;
        proxy_pass https://app2_host:8080/;
    }
}

and then restart nginx.
$ sudo service nginx restart

Download Oracle JDK 8 and install on Application server

$ sudo wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.rpm"

$ sudo rpm -ivh jdk-8u66-linux-x64.rpm*

Download Tomcat 8 and install on Application server

$ cd /opt
$ sudo wget ftp://apache.cs.utah.edu/apache.org/tomcat/tomcat-8/v8.0.30/bin/apache-tomcat-8.0.30.tar.gz
$ sudo tar -xvf apache-tomcat-8.0.30.tar.gz
$ sudo ln -s apache-tomcat-8.0.30 tomcat

Access manager webapp by adding the following to $CATALINA_HOME/conf/tomcat-users.xml

  <role rolename="manager-gui"/>
  <user username="tomcat" password="s3cret" roles="manager-gui"/>

Start Tomcat server
$ sudo  /opt/tomcat/bin/startup.sh

If Tomcat is running successfully, you can see the Tomcat Welcome page at http://localhost:8080/

Configure SSL (Optional) 

Tomcat listens on port 8080. It can be found in /opt/apache-tomcat/conf/server.xml. Comment the following and configure SSL.

           <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

Create a local self-signed certificate.
$ sudo keytool -genkey -alias tomcat -keystore conf/keystore.jks -keyalg RSA -keysize 2048
$ sudo keytool -list -keystore keystore.jks

Uncomment https and add keystore info to server.xml

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
               keystoreFile="/conf/keystore.jks"
               keystorePass="changeit" />

Force webapp to work on SSL, add the following code to web.xml.

<security-constraint>
   <web-resource-collection>
        <web-resource-name>securedapp</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Configure Virtual Hosting

copy app1.war and app2.war in app1 and app2 folders respectively in /opt/tomcat

Virtual Hosting - Edit Engine portion in /opt/tomcat/server.xml

     <Host name="app1_host"  appBase="app1"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="domain1_access_log" suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
      </Host>
     <Host name="app2_host"  appBase="app2"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="domain2_access_log" suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
     </Host>
     &lt/Engine>
    
Restart tomcat server.

Auto stop/start tomcat


# cd /etc/rc.d/init.d
# cat tomcat
#!/bin/bash
# description: Tomcat Start Stop Restart
# processname: tomcat
JAVA_HOME=/opt/jdk1.8.0_66
export JAVA_HOME
PATH=$JAVA_HOME/bin:$PATH
export PATH
CATALINA_HOME=/opt/tomcat

case $1 in
start)
sh $CATALINA_HOME/bin/startup.sh
;;
stop)
sh $CATALINA_HOME/bin/shutdown.sh
;;
restart)
sh $CATALINA_HOME/bin/shutdown.sh
sh $CATALINA_HOME/bin/startup.sh
;;
esac
exit 0
 
# chmod 755 tomcat
# chkconfig --add tomcat
# chkconfig tomcat on
# chkconfig --list