Monday, April 10, 2017

resize EBS

~$ lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  120G  0 disk
`-xvda1 202:1    0    8G  0 part /

~$ sudo parted /dev/xvda resizepart 1
Warning: Partition /dev/xvda1 is being used. Are you sure you want to continue?
Yes/No? yes
End?  [8590MB]? 120G
Information: You may need to update /etc/fstab.

~$ sudo /sbin/resize2fs /dev/xvda1
resize2fs 1.42.12 (29-Aug-2014)
Filesystem at /dev/xvda1 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 7
The filesystem on /dev/xvda1 is now 29296875 (4k) blocks long.


Grow filesystem to match disk size

Compare against and try to let a single page.

First check the partition layout. In the following example we have a 30GB disk with a 15GB partition for /. We have to extend this partition.

$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 30G 0 disk ├─sda1 8:1 0 15G 0 part / ├─sda2 8:2 0 1K 0 part └─sda5 8:5 0 975M 0 part [SWAP] sr0 11:0 1 46K 0 rom

Then we disable the SWAP partition:

$ sudo swapoff /dev/sda5 $ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 30G 0 disk ├─sda1 8:1 0 15G 0 part / ├─sda2 8:2 0 1K 0 part └─sda5 8:5 0 975M 0 part sr0 11:0 1 46K 0 rom

Now we remove all three partitions:

$ sudo fdisk /dev/sda Welcome to fdisk (util-linux 2.36.1). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. Command (m for help): d Partition number (1,2,5, default 5): Partition 5 has been deleted. Command (m for help): d Partition number (1,2, default 2): Partition 2 has been deleted. Command (m for help): d Selected partition 1 Partition 1 has been deleted.
Confirm if it is really important to maintain the ext4 signature.
Confirm if it is really needed to perform the following steps in the same fdisk session.

And while still in the same fdisk session, re-create the partitions maintaining the ext4 signature. Determine the size of the first partition (28GB) as the total disk space (30GB) minus ~20% to ~25% of the assigned RAM memory (20% of 10GB is 2GB):

Command (m for help): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): p Partition number (1-4, default 1): 1 First sector (2048-62914559, default 2048): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-62914559, default 62914559): +28G Created a new partition 1 of type 'Linux' and of size 28 GiB. Partition #1 contains a ext4 signature. Do you want to remove the signature? [Y]es/[N]o: N Command (m for help): n Partition type p primary (1 primary, 0 extended, 3 free) e extended (container for logical partitions) Select (default p): e Partition number (2-4, default 2): 2 First sector (58722304-62914559, default 58722304): Last sector, +/-sectors or +/-size{K,M,G,T,P} (58722304-62914559, default 62914559): Created a new partition 2 of type 'Extended' and of size 2 GiB. Command (m for help): n Adding logical partition 5 First sector (58724352-62914559, default 58724352): Last sector, +/-sectors or +/-size{K,M,G,T,P} (58724352-62914559, default 62914559): Created a new partition 5 of type 'Linux' and of size 2 GiB. Command (m for help): w The partition table has been altered. Syncing disks. $ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 30G 0 disk ├─sda1 8:1 0 28G 0 part / ├─sda2 8:2 0 1K 0 part └─sda5 8:5 0 2G 0 part sr0 11:0 1 46K 0 rom

Then re-configure the SWAP:

$ sudo mkswap /dev/sda5 Setting up swapspace version 1, size = 2 GiB (2145382400 bytes) no label, UUID=204fbbfb-79bf-4a72-870d-2a94288da6ba

And update /etc/fstab with the new UUID for the SWAP partition.

Confirm that the following is really needed. https://www.claudiokuenzler.com/blog/1138/update-initramfs-warning-tools-configuration-sets-resume-no-matching-swap-device. It seems it doesn’t happen for the development instances under the “temp_jhablutzel” location. Maybe Veeam related?

Then edit /etc/initramfs-tools/conf.d/resume with the new UUID as well.

Then:

sudo update-initramfs -k all -u sudo update-grub

Then:

sudo resize2fs /dev/sda1

Then reboot.

And finally check the disk layout once again:

$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 30G 0 disk ├─sda1 8:1 0 28G 0 part / ├─sda2 8:2 0 1K 0 part └─sda5 8:5 0 2G 0 part [SWAP] sr0 11:0 1 46K 0 rom
Provide instructions on how to set up Veeam backups depending on the specific requirements of this VM.

Wednesday, February 15, 2017

compile nginx-1.1x.x

#apt-get install build-essential git
#apt-get install libssl-dev zlib1g-dev libpcre3-dev libgd2-xpm-dev libgeoip-dev libxslt1-dev libxml2-dev

#wget https://www.openssl.org/source/openssl-1.0.2l.tar.gz
#tar -xzvf openssl-1.0.2l.tar.gz
#git clone https://github.com/nbs-system/naxsi.git

#wget http://nginx.org/download/nginx-1.12.0.tar.gz
#tar -xzvf nginx-1.12.0.tar.gz
#cd nginx-1.12.0
#./configure --with-cc-opt='-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' --with-ld-opt=-Wl,-z,relro --prefix=/usr/share/nginx --sbin-path=/usr/local/sbin --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --user=www-data --group=www-data --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module  --with-http_sub_module --with-http_xslt_module --with-mail --with-mail_ssl_module --with-http_v2_module --with-http_slice_module --with-file-aio --with-http_secure_link_module  --with-openssl=../openssl-1.0.2l --add-module=../naxsi/naxsi_src
#make
#make install

Here is content of nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
        worker_connections 4096;
        multi_accept on;
        use epoll;
}
http {
        server_tokens off;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        types_hash_max_size 2048;
        client_max_body_size 50M;
        client_body_buffer_size 1m;
        client_body_timeout 15;
        client_header_timeout 15;
        keepalive_timeout 2 2;
        send_timeout 15;

        include /etc/nginx/mime.types;
        error_log /var/log/nginx/error.log;
        access_log off;
        charset                         utf-8;
        gzip on;
        gzip_disable "msie6";
        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_min_length 1100;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

        open_file_cache max=2000 inactive=20s;
        open_file_cache_valid 60s;
        open_file_cache_min_uses 5;
        open_file_cache_errors off;

        include /etc/nginx/naxsi-core.rules;

        include /etc/nginx/sites-enabled/*.conf;

}

Here is content of naxsi-core.rules
##################################
## INTERNAL RULES IDS:1-999     ##
##################################
#@MainRule "msg:weird request, unable to parse" id:1;
#@MainRule "msg:request too big, stored on disk and not parsed" id:2;
#@MainRule "msg:invalid hex encoding, null bytes" id:10;
#@MainRule "msg:unknown content-type" id:11;
#@MainRule "msg:invalid formatted url" id:12;
#@MainRule "msg:invalid POST format" id:13;
#@MainRule "msg:invalid POST boundary" id:14;
#@MainRule "msg:invalid JSON" id:15;
#@MainRule "msg:empty POST" id:16;
#@MainRule "msg:libinjection_sql" id:17;
#@MainRule "msg:libinjection_xss" id:18;

##################################
## SQL Injections IDs:1000-1099 ##
##################################
MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;
MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001;
MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002;
## Hardcore rules
MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003;
MainRule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004;
MainRule "str:|" "msg:mysql keyword (|)"  "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005;
MainRule "str:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006;
## end of hardcore rules
MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007;
MainRule "str:;" "msg:semicolon" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008;
MainRule "str:=" "msg:equal sign in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009;
MainRule "str:(" "msg:open parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010;
MainRule "str:)" "msg:close parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011;
MainRule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013;
MainRule "str:," "msg:comma" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015;
MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016;
MainRule "str:@@" "msg:double arobase (@@)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1017;

###############################
## OBVIOUS RFI IDs:1100-1199 ##
###############################
MainRule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100;
MainRule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101;
MainRule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102;
MainRule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103;
MainRule "str:sftp://" "msg:sftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1104;
MainRule "str:zlib://" "msg:zlib:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1105;
MainRule "str:data://" "msg:data:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1106;
MainRule "str:glob://" "msg:glob:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1107;
MainRule "str:phar://" "msg:phar:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1108;
MainRule "str:file://" "msg:file:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1109;
MainRule "str:gopher://" "msg:gopher:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1110;

#######################################
## Directory traversal IDs:1200-1299 ##
#######################################
MainRule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200;
MainRule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202;
MainRule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203;
MainRule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204;
MainRule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205;
#MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206;

########################################
## Cross Site Scripting IDs:1300-1399 ##
########################################
MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302;
MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303;
MainRule "str:[" "msg:open square backet ([), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310;
MainRule "str:]" "msg:close square bracket (]), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311;
MainRule "str:~" "msg:tilde (~) character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312;
MainRule "str:`"  "msg:grave accent (`)" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314;
MainRule "rx:%[2|3]."  "msg:double encoding" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315;

####################################
## Evading tricks IDs: 1400-1500 ##
####################################
MainRule "str:&#" "msg:utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400;
MainRule "str:%U" "msg:M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401;

#############################
## File uploads: 1500-1600 ##
#############################

MainRule "rx:\.ph|\.asp|\.ht" "msg:asp/php file upload" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500;

Here is content of /lib/systemd/system/nginx.service

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
User=root
Group=root
PermissionsStartOnly=true
ExecStartPre=/bin/mkdir -p /var/lib/nginx
ExecStartPre=/bin/chown -R www-data:www-data /var/lib/nginx
PIDFile=/run/nginx.pid
ExecStartPre=/usr/local/sbin/nginx -t
ExecStart=/usr/local/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

#systemctl enable nginx.service

Friday, February 3, 2017

How to Create a Shared Directory for All Users in Linux

As a system administrator, you may have a certain directory that you want to give read/write access to every user on a Linux server. In this guide, we will review how to enable write access to all users on a particular directory (shared directory) in Linux.
This calls for setting the appropriate access permissions, and the most effective as well as reliable method to allocating a common group for all the users who will share or have write access to the specific directory.
So, start by creating the directory and common group in case it doesn’t already exist on the system as follows:
$ sudo mkdir -p /var/www/reports/
$ sudo groupadd project 
Then add an existing user who will have write access to the directory: /var/www/reports/ to the group project as below.
$ sudo usermod -a -G project tecmint 
The flags and arguments used in the above command are:
  1. -a – which adds the user to the supplementary group.
  2. -G – specifies the group name.
  3. project – group name.
  4. tecmint – existing username.
Afterwards, proceed to configure the appropriate permissions on the directory, where the option -Renables recursive operations into subdirectories:
$ sudo chgrp -R project /var/www/reports/
$ sudo chmod -R 2775 /var/www/reports/
Explaining the permissions 2775 in the chmod command above:
  1. 2 – turns on the setGID bit, implying–newly created subfiles inherit the same group as the directory, and newly created subdirectories inherit the set GID bit of the parent directory.
  2. 7 – gives rwx permissions for owner.
  3. 7 – gives rwx permissions for group.
  4. 5 – gives rx permissions for others.
You can create more system users and add them to the directory group as follows:
$ sudo useradd -m -c "Aaron Kili" -s/bin/bash -G project aaronkilik
$ sudo useradd -m -c "John Doo" -s/bin/bash -G project john
$ sudo useradd -m -c "Ravi Saive" -s/bin/bash -G project ravi
Then create subdirectories where the new users above will store their project reports:
$ sudo mkdir -p /var/www/reports/aaronkilik_reports
$ sudo mkdir -p /var/www/reports/johndoo_reports
$ sudo mkdir -p /var/www/reports/ravi_reports
Now you can create files/folders and share with other users on the same group.

Monday, December 19, 2016

create self signed cert

Thanks to jpmens@gmail.com

Generate CA
#!/usr/bin/env bash
#(@)generate-CA.sh - Create CA key-pair and server key-pair signed by CA

# Copyright (c) 2013-2016 Jan-Piet Mens <jpmens()gmail.com>
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. Neither the name of mosquitto nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

#
# Usage:
# ./generate-CA.sh  creates ca.crt and server.{key,crt}
# ./generate-CA.sh hostname creates hostname.{key,crt}
# ./generate-CA.sh client email creates email.{key,crt}
#
# Set the following optional environment variables before invocation
# to add the specified IP addresses and/or hostnames to the subjAltName list
# These contain white-space-separated values
#
# IPLIST="172.13.14.15 192.168.1.1"
# HOSTLIST="a.example.com b.example.com"

set -e

export LANG=C

kind=server

if [ $# -ne 2 ]; then
 kind=server
 host=$(hostname -f)
 if [ -n "$1" ]; then
  host="$1"
 fi
else
 kind=client
 CLIENT="$2"
fi

[ -z "$USER" ] && USER=root

DIR=${TARGET:='.'}
# A space-separated list of alternate hostnames (subjAltName)
# may be empty ""
ALTHOSTNAMES=${HOSTLIST}
ALTADDRESSES=${IPLIST}
CA_ORG='/O=OwnTracks.org/OU=generate-CA/emailAddress=nobody@example.net'
CA_DN="/CN=An MQTT broker${CA_ORG}"
CACERT=${DIR}/ca
SERVER="${DIR}/${host}"
SERVER_DN="/CN=${host}$CA_ORG"
keybits=2048
openssl=$(which openssl)
MOSQUITTOUSER=${MOSQUITTOUSER:=$USER}

# Signature Algorithm. To find out which are supported by your
# version of OpenSSL, run `openssl dgst -help` and set your
# signature algorithm here. For example:
#
# defaultmd="-sha256"
#
defaultmd="-sha512"

function maxdays() {
 nowyear=$(date +%Y)
 years=$(expr 2032 - $nowyear)
 days=$(expr $years '*' 365)

 echo $days
}

function getipaddresses() {
 /sbin/ifconfig |
  grep -v tunnel |
  sed -En '/inet6? /p' |
  sed -Ee 's/inet6? (addr:)?//' |
  awk '{print $1;}' |
  sed -e 's/[%/].*//' |
  egrep -v '(::1|127\.0\.0\.1)' # omit loopback to add it later
}


function addresslist() {

 ALIST=""
 for a in $(getipaddresses); do
  ALIST="${ALIST}IP:$a,"
 done
 ALIST="${ALIST}IP:127.0.0.1,IP:::1,"

 for ip in $(echo ${ALTADDRESSES}); do
  ALIST="${ALIST}IP:${ip},"
 done
 for h in $(echo ${ALTHOSTNAMES}); do
  ALIST="${ALIST}DNS:$h,"
 done
 ALIST="${ALIST}DNS:localhost"
 echo $ALIST

}

days=$(maxdays)

if [ -n "$CAKILLFILES" ]; then
 rm -f $CACERT.??? $SERVER.??? $CACERT.srl
fi

if [ ! -f $CACERT.crt ]; then

 #    ____    _    
 #   / ___|  / \   
 #  | |     / _ \  
 #  | |___ / ___ \ 
 #   \____/_/   \_\
 #                 

 # Create un-encrypted (!) key
 $openssl req -newkey rsa:${keybits} -x509 -nodes $defaultmd -days $days -extensions v3_ca -keyout $CACERT.key -out $CACERT.crt -subj "${CA_DN}"
 echo "Created CA certificate in $CACERT.crt"
 $openssl x509 -in $CACERT.crt -nameopt multiline -subject -noout

 chmod 400 $CACERT.key
 chmod 444 $CACERT.crt
 chown $MOSQUITTOUSER $CACERT.*
 echo "Warning: the CA key is not encrypted; store it safely!"
fi


if [ $kind == 'server' ]; then

 #   ____                           
 #  / ___|  ___ _ ____   _____ _ __ 
 #  \___ \ / _ \ '__\ \ / / _ \ '__|
 #   ___) |  __/ |   \ V /  __/ |   
 #  |____/ \___|_|    \_/ \___|_|   
 #                                  

 if [ ! -f $SERVER.key ]; then
  echo "--- Creating server key and signing request"
  $openssl genrsa -out $SERVER.key $keybits
  $openssl req -new $defaultmd \
   -out $SERVER.csr \
   -key $SERVER.key \
   -subj "${SERVER_DN}"
  chmod 400 $SERVER.key
  chown $MOSQUITTOUSER $SERVER.key
 fi

 if [ -f $SERVER.csr -a ! -f $SERVER.crt ]; then

  # There's no way to pass subjAltName on the CLI so
  # create a cnf file and use that.

  CNF=`mktemp /tmp/cacnf.XXXXXXXX` || { echo "$0: can't create temp file" >&2; exit 1; }
  sed -e 's/^.*%%% //' > $CNF <<\!ENDconfig
  %%% [ JPMextensions ]
  %%% basicConstraints        = critical,CA:false
  %%% nsCertType              = server
  %%% keyUsage                = nonRepudiation, digitalSignature, keyEncipherment
  %%% nsComment               = "Broker Certificate"
  %%% subjectKeyIdentifier    = hash
  %%% authorityKeyIdentifier  = keyid,issuer:always
  %%% subjectAltName          = $ENV::SUBJALTNAME
  %%% # issuerAltName           = issuer:copy
  %%% ## nsCaRevocationUrl       = http://mqttitude.org/carev/
  %%% ## nsRevocationUrl         = http://mqttitude.org/carev/
  %%% certificatePolicies     = ia5org,@polsection
  %%% 
  %%% [polsection]
  %%% policyIdentifier     = 1.3.5.8
  %%% CPS.1      = "http://localhost"
  %%% userNotice.1     = @notice
  %%% 
  %%% [notice]
  %%% explicitText            = "This CA is for a local MQTT broker installation only"
  %%% organization            = "OwnTracks"
  %%% noticeNumbers           = 1

!ENDconfig

  SUBJALTNAME="$(addresslist)"
  export SUBJALTNAME  # Use environment. Because I can. ;-)

  echo "--- Creating and signing server certificate"
  $openssl x509 -req $defaultmd \
   -in $SERVER.csr \
   -CA $CACERT.crt \
   -CAkey $CACERT.key \
   -CAcreateserial \
   -CAserial "${DIR}/ca.srl" \
   -out $SERVER.crt \
   -days $days \
   -extfile ${CNF} \
   -extensions JPMextensions

  rm -f $CNF
  chmod 444 $SERVER.crt
  chown $MOSQUITTOUSER $SERVER.crt
 fi
else
 #    ____ _ _            _   
 #   / ___| (_) ___ _ __ | |_ 
 #  | |   | | |/ _ \ '_ \| __|
 #  | |___| | |  __/ | | | |_ 
 #   \____|_|_|\___|_| |_|\__|
 #                            

 if [ ! -f $CLIENT.key ]; then
  echo "--- Creating client key and signing request"
  $openssl genrsa -out $CLIENT.key $keybits

  CNF=`mktemp /tmp/cacnf-req.XXXXXXXX` || { echo "$0: can't create temp file" >&2; exit 1; }
  # Mosquitto's use_identity_as_username takes the CN attribute
  # so we're populating that with the client's name
  sed -e 's/^.*%%% //' > $CNF <<!ENDClientconfigREQ
  %%% [ req ]
  %%% distinguished_name = req_distinguished_name
  %%% prompt   = no
  %%% output_password  = secret
  %%% 
  %%% [ req_distinguished_name ]
  %%% # O                       = OwnTracks
  %%% # OU                      = MQTT
  %%% # CN                      = Suzie Smith
  %%% CN                        = $CLIENT
  %%% # emailAddress            = $CLIENT
!ENDClientconfigREQ

  $openssl req -new $defaultmd \
   -out $CLIENT.csr \
   -key $CLIENT.key \
   -config $CNF
  chmod 400 $CLIENT.key
 fi

 if [ -f $CLIENT.csr -a ! -f $CLIENT.crt ]; then

  CNF=`mktemp /tmp/cacnf-cli.XXXXXXXX` || { echo "$0: can't create temp file" >&2; exit 1; }
  sed -e 's/^.*%%% //' > $CNF <<\!ENDClientconfig
  %%% [ JPMclientextensions ]
  %%% basicConstraints        = critical,CA:false
  %%% subjectAltName          = email:copy
  %%% nsCertType              = client,email
  %%% extendedKeyUsage        = clientAuth,emailProtection
  %%% keyUsage                = digitalSignature, keyEncipherment, keyAgreement
  %%% nsComment               = "Client Broker Certificate"
  %%% subjectKeyIdentifier    = hash
  %%% authorityKeyIdentifier  = keyid,issuer:always

!ENDClientconfig

  SUBJALTNAME="$(addresslist)"
  export SUBJALTNAME  # Use environment. Because I can. ;-)

  echo "--- Creating and signing client certificate"
  $openssl x509 -req $defaultmd \
   -in $CLIENT.csr \
   -CA $CACERT.crt \
   -CAkey $CACERT.key \
   -CAcreateserial \
   -CAserial "${DIR}/ca.srl" \
   -out $CLIENT.crt \
   -days $days \
   -extfile ${CNF} \
   -extensions JPMclientextensions

  rm -f $CNF
  chmod 444 $CLIENT.crt
 fi
fi

 Generate Client
#!/bin/bash
#  generate-client - Create client key-pair for MQTT signed by CA
#
#  The key is not encrypted so that the certificate can be used on
#  embedded devices.

# Copyright 2015 Jerry Dunmire <jedunmire-AT-gmail>
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. Neither the name of mosquitto nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

#
# Usage:
#   ./generate-client.sh client_name
#     creates client_name{key,crt}
set -e

if [ -n "$1" ]; then
    client="$1"
else
   echo "ERROR: missing client_name argument." >&2
   echo "USAGE: $0 client_name" >&2
   echo "exiting... " >&2
   exit 1
fi

[ -z "$USER" ] && USER=root

DIR=${TARGET:='.'}
# CA_ORG set to match generate-CA.sh
CA_ORG='/O=MQTTitude.org/emailAddress=nobody@example.net'
CACERT=${DIR}/ca
CLIENT="${DIR}/${client}"
CLIENT_DN="/CN=${client}$CA_ORG"
keybits=2048
openssl=$(which openssl)

function maxdays() {
    nowyear=$(date +%Y)
    years=$(expr 2032 - $nowyear)
    days=$(expr $years '*' 365)

    echo $days
}

days=$(maxdays)

if [ ! -f $CACERT.crt ]
then
    echo "ERROR: Could not find CA certificate: $CACERT.crt" >&2
    echo "Exiting..." >&2
    exit 1
fi

if [ ! -f $CLIENT.key ]
then
    echo "--- Creating client key and signing request"
    echo "--- WARNING: key is not encrypted, keep it safe!"
    $openssl genrsa -out $CLIENT.key $keybits
    $openssl req -new \
        -out $CLIENT.csr \
        -key $CLIENT.key \
        -subj "${CLIENT_DN}"
    chmod 400 $CLIENT.key
fi

if [ -f $CLIENT.csr -a ! -f $CLIENT.crt ]
then
    echo "--- Creating and signing client certificate"
    $openssl x509 -req \
        -in $CLIENT.csr \
        -CA $CACERT.crt \
        -CAkey $CACERT.key \
        -CAserial "${DIR}/ca.srl" \
        -out $CLIENT.crt \
        -days $days \
        -addtrust clientAuth

    chmod 444 $CLIENT.crt
fi