From 7e5a76e7996ebcba36536c8fecd31a95280f3417 Mon Sep 17 00:00:00 2001 From: B Stack Date: Wed, 6 Dec 2017 06:51:36 -0500 Subject: Initial commit --- README.md | 22 ++ defaults/main.yml.example | 2 + examples/certreq.yml | 20 ++ files/certreq.conf.example | 8 + files/certreq.sh | 336 ++++++++++++++++++++++++++++++ files/framework.sh | 406 +++++++++++++++++++++++++++++++++++++ handlers/main.yml | 8 + main.yml | 19 ++ tasks/1_certreq.yml | 41 ++++ tasks/2_generate_pfx.yml | 29 +++ tasks/main.yml | 9 + vars/prod1.example.com.yml.example | 1 + vars/prod1.yml.example | 5 + 13 files changed, 906 insertions(+) create mode 100644 README.md create mode 100644 defaults/main.yml.example create mode 100644 examples/certreq.yml create mode 100644 files/certreq.conf.example create mode 100755 files/certreq.sh create mode 100755 files/framework.sh create mode 100644 handlers/main.yml create mode 100644 main.yml create mode 100644 tasks/1_certreq.yml create mode 100644 tasks/2_generate_pfx.yml create mode 100644 tasks/main.yml create mode 120000 vars/prod1.example.com.yml.example create mode 100644 vars/prod1.yml.example diff --git a/README.md b/README.md new file mode 100644 index 0000000..649cb31 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# README.md +## Package: ansible role certreq + +This role, certreq, is designed to make it easy for a Linux machine to acquire a certificate signed by a Microsoft Subordinate CA + +Its main use is inside a playbook that resembles: + + - hosts: all + remote_user: ansible_rdu + roles: + - certreq + +Call the playbook with: +ansible-playbook -i /etc/ansible/patching/rdu/rdu_patch_list /etc/ansible/configuration/test/certreq.yml -l sw* + +The role generates a pkc12 file at /tmp/certnew.pfx with the client cert, ca cert chain, and client private key. + +# References +https://bgstack15.wordpress.com/2016/06/30/manipulating-ssl-certificates/ +fundamental curl statements https://stackoverflow.com/questions/31283476/submitting-base64-csr-to-a-microsoft-ca-via-curl/39722983#39722983 +Use template name, not "template display name" https://social.technet.microsoft.com/Forums/en-US/d5cafc77-3376-43ca-94fd-6b07f7cb193f/using-certutilcertreq-to-get-sccm-client-certs-nondomain-clients?forum=configmgrgeneral + diff --git a/defaults/main.yml.example b/defaults/main.yml.example new file mode 100644 index 0000000..d7ac9ec --- /dev/null +++ b/defaults/main.yml.example @@ -0,0 +1,2 @@ +certificate_template: "ConfigMgrLinuxClientCertificate" +ca_host: 'ca2.ad.example.com' diff --git a/examples/certreq.yml b/examples/certreq.yml new file mode 100644 index 0000000..e7af898 --- /dev/null +++ b/examples/certreq.yml @@ -0,0 +1,20 @@ +--- +# File: /etc/ansible/configuration/test/certreq.yml +# Author: bgstack15 +# Startdate: 2017-11-14 +# Title: Playbook that Deploys The Role certreq +# Purpose: Wrapper for the certreq role +# History: +# Usage: +# ansible-playbook -i /etc/ansible/patching/rdu/rdu_patch_list /etc/ansible/configuration/test/certreq.yml --become -u ansible_rdu -l sw* +# Use this playbook when you want to assign a new Microsoft CA-signed certificate to a Linux host for SCCM. +# Reference: +# ansible reference online +# Improve: +# Document: Below this line + +- hosts: all + roles: + - certreq + +... diff --git a/files/certreq.conf.example b/files/certreq.conf.example new file mode 100644 index 0000000..a956241 --- /dev/null +++ b/files/certreq.conf.example @@ -0,0 +1,8 @@ +CERTREQ_USER="ANONYMOUS" +CERTREQ_PASS="NOPASSWORD" +CERTREQ_WORKDIR="$( mktemp -d )" +CERTREQ_TEMPLATE="ConfigMgrLinuxClientCertificate" +CERTREQ_CNLONG="$( hostname -f )" +CERTREQ_CNSHORT="$( echo "${CERTREQ_CNLONG%%.*}" )" +CERTREQ_SUBJECT="/DC=com/DC=example/DC=ad/CN=${CERTREQ_CNSHORT}/CN=${CERTREQ_CNPARAM:-CERTREQ_CNPARAM}" +CERTREQ_CA="http://ca2.ad.example.com" diff --git a/files/certreq.sh b/files/certreq.sh new file mode 100755 index 0000000..dea652f --- /dev/null +++ b/files/certreq.sh @@ -0,0 +1,336 @@ +#!/bin/sh +# Filename: certreq.sh +# Location: atower201:/etc/ansible/roles/certreq/files/certreq.sh +# Author: bgstack15@gmail.com +# Startdate: 2017-11-17 09:13:53 +# Title: Script that Requests a Certificate from a Microsoft Sub-CA +# Purpose: Automate host certificate generation in a domain environment +# Package: ansible role certreq +# History: +# 2017-11-22 Added ca cert chain +# Usage: in ansible role certreq +# Microsoft CA cert templates have permissions on them. A user must be able to "enroll" on the template. +# Reference: ftemplate.sh 2017-10-10x; framework.sh 2017-10-09a +# fundamental curl statements https://stackoverflow.com/questions/31283476/submitting-base64-csr-to-a-microsoft-ca-via-curl/39722983#39722983 +# Improve: +fiversion="2017-10-10x" +certreqversion="2017-11-29a" + +usage() { + less -F >&2 <] +version ${certreqversion} + -d debug Show debugging info, including parsed variables. + -h usage Show this usage block. + -V version Show script version number. + -u username User to connect via ntlm to CA. Can be "username" or "domain\\username" + -p password + -w workdir Temp directory to work in. Default is a (mktemp -d). + -t template Template to request from CA. Default is "ConfigMgrLinuxClientCertificate" + --cn CN to request. Default is the \$( hostname -f ) + --ca CA hostname or base URL. Example: ca2.example.com +Return values under 1000: A non-zero value is the sum of the items listed here: + 0 Everything worked + 1 Cert file is still a CSR + 2 Cert file is html, probably due to permissions/credentials issue + 4 Return code of curl statement that saves cert file is non-zero + 8 Cert file does not contain whole certificate +16 Cert does not contain an issuer +Return values above 1000: +1001 Help or version info displayed +1002 Count or type of flaglessvals is incorrect +1003 Incorrect OS type +1004 Unable to find dependency +1005 Not run as root or sudo +ENDUSAGE +} + +# DEFINE FUNCTIONS + +# DEFINE TRAPS + +clean_certreq() { + # use at end of entire script if you need to clean up tmpfiles + #rm -f ${tmpfile} 1>/dev/null 2>&1 + if test -z "${CR_NC}"; + then + nohup /bin/bash </dev/null 2>&1 & +sleep "${CERTREQ_CLEANUP_SEC:-300}" ; /bin/rm -r "${CERTREQ_WORKDIR}" 2>/dev/null ; +EOF +#sleep "${CERTREQ_CLEANUP_SEC:-300}" ; /bin/rm -r "${CERTREQ_WORKDIR}" 2>/dev/null ; echo "slash-dollar-0=\"\$0\" slash-slash-dollar-0=\"\\$0\"" > /dev/pts/2 ; + fi + +} + +CTRLC() { + # use with: trap "CTRLC" 2 + # useful for controlling the ctrl+c keystroke + : +} + +CTRLZ() { + # use with: trap "CTRLZ" 18 + # useful for controlling the ctrl+z keystroke + : +} + +parseFlag() { + flag="$1" + hasval=0 + case ${flag} in + # INSERT FLAGS HERE + "d" | "debug" | "DEBUG" | "dd" ) setdebug; ferror "debug level ${debug}";; + "usage" | "help" | "h" ) usage; exit 1001;; + "V" | "fcheck" | "version" ) ferror "${scriptfile} version ${certreqversion}"; exit 1001;; + "u" | "user" | "username" ) getval; CERTREQ_USER="${tempval}";; + "p" | "pass" | "password" ) getval; CERTREQ_PASS="${tempval}";; + "w" | "work" | "workdir" ) getval; CERTREQ_WORKDIR="${tempval}";; + "t" | "temp" | "template" ) getval; CERTREQ_TEMPLATE="${tempval}";; + "cn" | "common-name" | "commonname" ) getval; CERTREQ_CNPARAM="${tempval}";; + "ca" | "certauthority" | "cauthority" ) getval; CERTREQ_CAPARAM="${tempval}";; + "c" | "conf" | "conffile" | "config" ) getval; conffile="${tempval}";; + "nc" | "nocleanup" ) CR_NC=1;; + esac + + debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}"; } +} + +# DETERMINE LOCATION OF FRAMEWORK +while read flocation; do if test -f ${flocation} && test "$( sh ${flocation} --fcheck )" -ge 20170608; then frameworkscript="${flocation}"; break; fi; done <&2 && exit 1004 + +# INITIALIZE VARIABLES +# variables set in framework: +# today server thistty scriptdir scriptfile scripttrim +# is_cronjob stdin_piped stdout_piped stderr_piped sendsh sendopts +. ${frameworkscript} || echo "$0: framework did not run properly. Continuing..." 1>&2 +infile1= +outfile1= +#logfile=${scriptdir}/${scripttrim}.${today}.out # defined farther down +define_if_new interestedparties "bgstack15@gmail.com" +# SIMPLECONF +define_if_new default_conffile "/tmp/certreq.conf" +define_if_new defuser_conffile ~/.config/certreq/certreq.conf + +# REACT TO OPERATING SYSTEM TYPE +case $( uname -s ) in + Linux) [ ];; + *) echo "${scriptfile}: 3. Indeterminate OS: $( uname -s )" 1>&2 && exit 1003;; +esac + +## REACT TO ROOT STATUS +#case ${is_root} in +# 1) # proper root +# [ ] ;; +# sudo) # sudo to root +# [ ] ;; +# "") # not root at all +# #ferror "${scriptfile}: 5. Please run as root or sudo. Aborted." +# #exit 1005 +# [ ] +# ;; +#esac + +# SET CUSTOM SCRIPT AND VALUES +#setval 1 sendsh sendopts<&2 +} + +# MAIN LOOP +{ + # GENERATE PRIVATE KEY + openssl req -new -nodes \ + -out "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" \ + -keyout "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.key" \ + -subj "${CERTREQ_SUBJECT}" + CERT="$( cat "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" | tr -d '\n\r' )" + DATA="Mode=newreq&CertRequest=${CERT}&C&TargetStoreFlags=0&SaveCert=yes" + CERT="$( echo ${CERT} | sed -e 's/+/%2B/g' | tr -s ' ' '+' )" + CERTATTRIB="CertificateTemplate:${CERTREQ_TEMPLATE}" + + # SUBMIT CERTIFICATE SIGNING REQUEST + OUTPUTLINK="$( curl -k -u "${CERTREQ_USER}:${CERTREQ_PASS}" --ntlm \ + "${CERTREQ_CA}/certsrv/certfnsh.asp" \ + -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ + -H 'Accept-Encoding: gzip, deflate' \ + -H 'Accept-Language: en-US,en;q=0.5' \ + -H 'Connection: keep-alive' \ + -H "Host: ${CERTREQ_CAHOST}" \ + -H "Referer: ${CERTREQ_CA}/certsrv/certrqxt.asp" \ + -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + --data "Mode=newreq&CertRequest=${CERT}&CertAttrib=${CERTATTRIB}&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=" | grep -A 1 'function handleGetCert() {' | tail -n 1 | cut -d '"' -f 2 )" + CERTLINK="${CERTREQ_CA}/certsrv/${OUTPUTLINK}" + + # FETCH SIGNED CERTIFICATE + curl -k -u "${CERTREQ_USER}:${CERTREQ_PASS}" --ntlm $CERTLINK \ + -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ + -H 'Accept-Encoding: gzip, deflate' \ + -H 'Accept-Language: en-US,en;q=0.5' \ + -H 'Connection: keep-alive' \ + -H "Host: ${CERTREQ_CAHOST}" \ + -H "Referer: ${CERTREQ_CA}/certsrv/certrqxt.asp" \ + -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ + -H 'Content-Type: application/x-www-form-urlencoded' > "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" + finaloutput=$? + + # GET NUMBER OF CURRENT CA CERT + RESPONSE="$( curl -s -k -u "${CERTREQ_USER}:${CERTREQ_PASS}" --ntlm \ + "${CERTREQ_CA}/certsrv/certcarc.asp" \ + -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ + -H 'Accept-Encoding: gzip, deflate' \ + -H 'Accept-Language: en-US,en;q=0.5' \ + -H 'Connection: keep-alive' \ + -H "Host: ${CERTREQ_CAHOST}" \ + -H "Referer: ${CERTREQ_CA}/certsrv/certrqxt.asp" \ + -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ + -H 'Content-Type: application/x-www-form-urlencoded' )" + CURRENTNUM="$( echo "${RESPONSE}" | grep -cE 'Option' )" + + # GET LATEST CA CERT CHAIN + CURRENT_P7B="$( curl -s -k -u "${CERTREQ_USER}:${CERTREQ_PASS}" --ntlm \ + "${CERTREQ_CA}/certsrv/certnew.p7b?ReqID=CACert&Renewal=${CURRENTNUM}" \ + -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \ + -H 'Accept-Encoding: gzip, deflate' \ + -H 'Accept-Language: en-US,en;q=0.5' \ + -H 'Connection: keep-alive' \ + -H "Host: ${CERTREQ_CAHOST}" \ + -H "Referer: ${CERTREQ_CA}/certsrv/certrqxt.asp" \ + -H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \ + -H 'Content-Type: application/x-www-form-urlencoded' )" + + # CONVERT TO PEM + echo "${CURRENT_P7B}" | openssl pkcs7 -print_certs -out "${CERTREQ_TEMPFILE}" + + # RENAME TO PROPER FILENAME + # will read only the first cert, so get domain of issuer of it. + CA_DOMAIN="$( openssl x509 -in "${CERTREQ_TEMPFILE}" -noout -issuer 2>&1 | sed -r -e 's/^.*CN=[A-Za-z0-9]+\.//;' )" + CHAIN_FILE="chain-${CA_DOMAIN}.crt" + mv -f "${CERTREQ_TEMPFILE}" "${CERTREQ_WORKDIR}/${CHAIN_FILE}" 1>/dev/null 2>&1 + +} 1> ${logfile} 2>&1 + +# CHECK EVERYTHING +failed=0 +openssloutput="$( openssl x509 -in "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" -noout -subject -issuer -startdate -enddate 2>/dev/null )" +grep -qE -- 'REQUEST--' "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" && failed=$(( failed + 1 )) +grep -qiE '\<\/?body\>' "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" && failed=$(( failed + 2 )) +test ${finaloutput} -ne 0 && failed=$(( failed + 4 )) +grep -qE -- '--END CERTIFICATE--' "${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" || failed=$(( failed + 8 )) +#echo "${openssloutput}" | grep -qE "subject.*${CERTREQ_SUBJECT}" || failed=$(( failed + 16 )) +echo "${openssloutput}" | grep -qE "issuer.*" || failed=$(( failed + 16 )) + +# if everything was successful, display information below +#if test ${failed} -eq 0; +#then + echo "workdir: ${CERTREQ_WORKDIR}" + echo "logfile: ${logfile}" + echo "certificate: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.crt" + echo "key: ${CERTREQ_WORKDIR}/${CERTREQ_CNPARAM}.key" + echo "chain: ${CERTREQ_WORKDIR}/${CHAIN_FILE}" +#fi +clean_certreq +exit "${failed}" + +# EMAIL LOGFILE +#${sendsh} ${sendopts} "${server} ${scriptfile} out" ${logfile} ${interestedparties} diff --git a/files/framework.sh b/files/framework.sh new file mode 100755 index 0000000..9ef9470 --- /dev/null +++ b/files/framework.sh @@ -0,0 +1,406 @@ +#!/bin/sh +# File: /tmp/framework.sh +# Author: bgstack15@gmail.com +# Startdate: 2014-06-02 15:22 +# Title: Framework for Common Elements in My Scripts +# Purpose: Library of common script elements +# Package: deployed by ansible +# History: fv2017-10-09a=fi2017-10-10x +# 2017-10-09a Stripped down for custom OL7 package version 1p3p1 +# 2017-11-17a Deployed via ansible +# Usage: dot-source this script in ftemplate.sh used by newscript.sh +# Reference: +# Improve: +fversion="2017-11-17a" + +# DEFINE FUNCTIONS + +isflag() { + # input: $1=word to parse + case "$1" in + --*) retval=2;; + -*) retval=1;; + *) retval=0;; + esac + echo $retval +} + +parseParam() { + # determines if --longname or -shortflagS that need individual parsing + trimParam=$( printf '%s' "${param}" | sed -n 's/--//p' ) + _rest= + if test -n "$trimParam"; + then + parseFlag $trimParam + else + #splitShortStrings + _i=2 + while test ${_i} -le ${#param}; + do + _j=$( expr ${_i} + 1) + #_char=$(expr substr "$param" $_i 1) + #_rest=$(expr substr "$param" $_j 255) + _char=$( printf '%s' "${param}" | cut -c ${_i}) + _rest=$( printf '%s' "${param}" | cut -c ${_j}-255) + parseFlag $_char + _i=$( expr ${_i} + 1) + done + fi +} + +getval() { + tempval= + if test -n "${_rest}"; + then + tempval="${_rest}" + hasval=1 + _i=255 # skip rest of splitShortStrings because found the value! + elif test -n "$nextparam" && test $(isflag "$nextparam") -eq 0; + then + tempval="$nextparam" + hasval=1 #DNE; is affected by ftemplate! + paramnum=$nextparamnum + fi +} + +debuglev() { + # call: debuglev 5 && ferror "debug level is at least a five!" + # added 2015-11-17 + localdebug=0; localcheck=0; + fisnum ${debug} && localdebug=${debug} + fisnum ${1} && localcheck=${1} + test $localdebug -ge $localcheck && return 0 || return 1 +} + +fisnum() { + # call: fisnum $1 && debug=$1 || debug=10 + fisnum=; + case $1 in + ''|*[!0-9]*) fisnum=1;; # invalid + *) fisnum=0;; # valid number + esac + return ${fisnum} +} + +fistruthy() { + # call: if fistruthy "$val"; then + local _return= + case "$( echo "${1}" | tr '[:upper:]' '[:lower:]' )" in + yes|1|y|true|always) _return=true;; + esac + test -n "${_return}"; return $? +} + +setval() { + # call: setval 0 value1 value2 value3 ... <&2 +} + +linecat() { + # call: linecat "foo" "bar" + # output: foobar + printf "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "${12}" "${13}" "${14}" "${15}" +} + +prepend() { while read prependinput; do linecat "$@" "$prependinput"; done; } +append() { while read appendinput; do linecat "$appendinput" "$@"; done; } + +setmailopts() { + setval 0 sendsh sendopts</dev/null | awk '/Bcast|broadcast/{print $2}' | tr -cd '[^0-9\.\n]' | head -n1 ) +thisos="$( uname -s )" +# get thisflavor and thisflavorversion. Examples: centos, ubuntu, redhat +if test -f /etc/os-release; +then + eval thisflavor=$( grep -iE "^\s*ID=" /etc/os-release 2>/dev/null | sed 's/^.*=//;' | tr 'A-Z' 'a-z' ) + eval thisflavorversion=$( grep -iE "^\s*PRETTY_NAME=" /etc/os-release 2>/dev/null | sed -e 's/^.*=//;' | tr -dc '0-9.' ) +elif test -f /etc/system-release && test $( wc -l < /etc/system-release 2>/dev/null ) -eq 1; +then + eval thisflavor=$( awk '{print $1}' < /etc/system-release 2>/dev/null | tr 'A-Z' 'a-z' ) + eval thisflavorversion=$( /dev/null | tr -dc '0-9.' ) +else + if test "${thisos}" = "FreeBSD"; then + thisflavor="$( uname -i )"; thisflavorversion="$( uname -r )"; + else + thisflavor="other" + thisflavorversion="unknown" + fi +fi +case "${thisos}" in FreeBSD) sed=gsed;; *) sed=sed;; esac + +# if framework is dot sourced then $0 will be "-bash" and screw things up +case ${0} in + "-bash") + scriptdir="$( pwd )" + scriptfile="dot-sourced";; + *) + scriptdir="$( cd $( dirname ${0} ); pwd )" + scriptfile="$( basename ${0} | sed 's!/./!/!g;s!\./!!g' )" + scripttrim="${scriptfile%%.sh}" + ;; +esac + +# SPECIAL RUNTIME-RELATED VARIABLES +thisppid=$( ps -p $$ -o ppid | awk 'NR>1' | tr -d ' ' ) +cronpid=$( ps -ef | grep -E "/c[r]on" | grep -vE "grep.*-E.*cron|[0-9]\s*vi " | awk '{print $2}' ) +test "$cronpid" = "$thisppid" && is_cronjob=1 +test ! -t 0 && stdin_piped=1 +test ! -t 1 && stdout_piped=1 +test ! -t 2 && stderr_piped=1 +test "$( tty 2>&1 )" = "not a tty" && stdin_local=1 +test "$USER" = "root" && is_root=1 +test -n "$SUDO_USER" && is_root="sudo" + +nullflagcount=0 +validateparams() { + # VALIDATE PARAMETERS + # scroll through all parameters and check for isflag. + # if isflag, get all flags listed. Also grab param#. + paramcount=$# + thiscount=0;thisopt=0;freeopt=0; + varsyet=0 + paramnum=0 + debug=0 + fallopts= + while test $paramnum -lt $paramcount; + do + paramnum=$( expr ${paramnum} + 1 ) + eval param=\${$paramnum} + nextparamnum=$( expr ${paramnum} + 1 ) + eval nextparam=\${$nextparamnum} + case $param in + "-") + if test "$varsyet" = "0"; + then + # first instance marks beginning of flags and parameters. + #Until then it was the names of variables to fill. + varsyet=1 + else + nullflagcount=$( expr ${nullflagcount} + 1 ) #useful for separating flags from something else? + debuglev 10 && ferror "null flag!" # second instance is null flag. + fi + ;; + esac + if test -n "$param"; + then + # parameter $param exists. + if test $(isflag $param) -gt 0; + then + # IS FLAG + parseParam + else + # IS VALUE + if test "$varsyet" = "0"; + then + thisopt=$( expr ${thisopt} + 1 ) + test "${param}" = "DEBUG" && debug=10 && thisopt=$( expr ${thisopt} - 1 ) || \ + eval "varname${thisopt}=${param}" + #varname[${thisopt}]="${param}" + debuglev 10 && ferror "var \"${param}\" named" + else + thiscount=$( expr ${thiscount} + 1 ) + test $thiscount -gt $thisopt && freeopt=$( expr ${freeopt} + 1 ) + #eval ${varname[${thiscount}]:-opt${freeopt}}="\"${param}\"" + eval "thisvarname=\${varname${thiscount}}" + test -z "${thisvarname}" && eval "thisvarname=opt${freeopt}" + eval "${thisvarname}=\"${param}\"" + eval fallopts=\"${fallopts} ${param}\" + debuglev 10 && ferror "${thisvarname} value: ${param}" + fi + fi + fi + done + fallopts="${fallopts# }" + if debuglev 10; + then + ferror "thiscount=$thiscount" + ferror "fallopts=$fallopts" + ferror "Framework $fversion" + ferror "Finput $fiversion" + fi +} + +# PROCEDURAL SECTION ( NOT MAIN HOWEVER; THIS IS STILL LIBRARY ) +echo " $@ " | grep -qiE -- "--fcheck" 1>/dev/null 2>&1 && { echo "$fversion" | sed 's/[^0-9]//g;'; } || setmailopts diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..1c4a4e5 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,8 @@ +--- +# handlers for ansible role: certreq + +- name: handler for XYZ app + debug: + msg: "You called this handler!" + +... diff --git a/main.yml b/main.yml new file mode 100644 index 0000000..839866a --- /dev/null +++ b/main.yml @@ -0,0 +1,19 @@ +--- +# File: /etc/ansible/roles/certreq/main.yml +# Author: bgstack15 +# Startdate: 2017-09-13 +# Title: Role that Generates a PKCS12 Certificate File From a Microsoft Sub-CA +# Purpose: Generates a cert from a Microsoft Sub-CA +# History: +# Usage: +# Reference: +# /etc/ansible/configuration/test/certreq.yml +# Improve: +# Documentation: README.txt + +- hosts: all + tasks: + - include: tasks/main.yml + handlers: + - include: handlers/main.yml +... diff --git a/tasks/1_certreq.yml b/tasks/1_certreq.yml new file mode 100644 index 0000000..e4f06d7 --- /dev/null +++ b/tasks/1_certreq.yml @@ -0,0 +1,41 @@ +--- +# Reference: Use template name, not "template display name" https://social.technet.microsoft.com/Forums/en-US/d5cafc77-3376-43ca-94fd-6b07f7cb193f/using-certutilcertreq-to-get-sccm-client-certs-nondomain-clients?forum=configmgrgeneral + +## read in custom variable, based on domain of the host +- name: read which CA to work with + include_vars: "{{ item }}" + with_first_found: + - '{{ ansible_dns.search[0] }}.yml' + - 'prod1.yml' + no_log: true + +- name: deploy dependencies + copy: + src: "{{ item.f }}" + dest: "/tmp/{{ item.f }}" + mode: "{{ item.m }}" + owner: root + group: root + with_items: + - { f: 'framework.sh', m: '0755' } + - { f: 'certreq.conf', m: '0644' } + changed_when: false + +- name: request certificate + script: certreq.sh -c /tmp/certreq.conf -u "{{ ca_user }}" -p "{{ ca_pass }}" -t "{{ ca_template }}" --ca "{{ ca_host }}" + register: certreq + +- debug: + msg: "{{ certreq }}" + +#- pause: +# prompt: 'DOES THE ABOVE LOOK OK?...................' + +- name: cleanup + file: + path: "/tmp/{{ item.f }}" + state: absent + with_items: + - { f: 'framework.sh', m: '0755' } + - { f: 'certreq.conf', m: '0644' } + changed_when: false diff --git a/tasks/2_generate_pfx.yml b/tasks/2_generate_pfx.yml new file mode 100644 index 0000000..ec45282 --- /dev/null +++ b/tasks/2_generate_pfx.yml @@ -0,0 +1,29 @@ +--- +# Reference: https://bgstack15.wordpress.com/2016/06/30/manipulating-ssl-certificates/ + +- name: save private key file as variable + shell: warn=no echo "{{ certreq.stdout }}" | awk '/^key:/{print $2;}' + register: privatekey + failed_when: privatekey.stdout_lines | length != 1 + changed_when: false +# privatekey.stdout + +- name: save public key file as variable + shell: warn=no echo "{{ certreq.stdout }}" | awk '/^certificate:/{print $2;}' + register: publickey + failed_when: publickey.stdout_lines | length != 1 + changed_when: false +# publickey.stdout + +- name: save cert chain file as variable + shell: warn=no echo "{{ certreq.stdout }}" | awk '/^chain:/{print $2;}' + register: chain + failed_when: chain.stdout_lines | length != 1 + changed_when: false +# chain.stdout + +- name: generate pkcs12 file + shell: warn=no openssl pkcs12 -export -in "{{ publickey.stdout }}" -inkey "{{ privatekey.stdout }}" -out /tmp/certnew.pfx -CAfile "{{ chain.stdout }}" -certfile "{{ chain.stdout }}" -passin pass:'' -passout pass:'' + register: pfx + +... diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..36de385 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- block: + #- ping: + - include_tasks: 1_certreq.yml + - include_tasks: 2_generate_pfx.yml + become: yes + become_user: root + become_method: sudo +... diff --git a/vars/prod1.example.com.yml.example b/vars/prod1.example.com.yml.example new file mode 120000 index 0000000..ac20d69 --- /dev/null +++ b/vars/prod1.example.com.yml.example @@ -0,0 +1 @@ +prod1.yml.example \ No newline at end of file diff --git a/vars/prod1.yml.example b/vars/prod1.yml.example new file mode 100644 index 0000000..aa6e215 --- /dev/null +++ b/vars/prod1.yml.example @@ -0,0 +1,5 @@ +domain: prod1.example.com +ca_host: ca2.prod1.example.com +ca_user: user +ca_pass: ExamplePW55* +ca_template: "ConfigMgrLinuxClientCertificate" -- cgit