123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103(* (c) 2017 Hannes Mehnert, all rights reserved *)let(let*)=Result.bindlettimestampsvalidity=letnow=Ptime_clock.now()inmatchPtime.add_spannow(Ptime.Span.of_int_s(Duration.to_secvalidity))with|None->Error(`Msg"span too big - reached end of ptime")|Someexp->Ok(now,exp)letrecsafefarg=tryOk(farg)with|Unix.Unix_error(Unix.EINTR,_,_)->safefarg|Unix.Unix_error(e,_,_)->Error(`Msg(Unix.error_messagee))(* TODO: is this useful elsewhere? *)letappendnamedata=letbuf=Bytes.unsafe_of_stringdatainletnam=Fpath.to_stringnameinlet*fd=safeUnix.(openfilenam[O_APPEND;O_CREAT;O_WRONLY])0o644inletlen=String.lengthdatainletrecgooff=letl=len-offinlet*w=safe(Unix.writefdbufoff)linifl=wthenOk()elsego(w+off)inlet*()=go0insafeUnix.closefdletkey_idsextspubissuer=letauth=Some(X509.Public_key.idissuer),X509.General_name.empty,NoneinX509.Extension.(addSubject_key_id(false,X509.Public_key.idpub)(addAuthority_key_id(false,auth)exts))letsign?dbname?certnameextensionsissuerkeycsrdelta=let*certname=matchcertnamewith|Somex->Okx|None->matchX509.Distinguished_name.common_nameX509.Signing_request.((infocsr).subject)with|Somename->Okname|None->Error(`Msg"couldn't find name (no common name in CSR subject)")inlet*valid_from,valid_until=timestampsdeltainletextensions=matchdbnamewith|None->extensions(* evil hack to avoid issuer + public key for CA cert *)|Some_->letcapub=X509.Private_key.publickeyinkey_idsextensionsX509.Signing_request.((infocsr).public_key)capubinlet*cert=Result.map_error(fune->`Msg(Fmt.to_to_stringX509.Validation.pp_signature_errore))(X509.Signing_request.signcsr~valid_from~valid_until~extensionskeyissuer)inlet*()=matchdbnamewith|None->Ok()(* no DB! *)|Somedbname->appenddbname(Printf.sprintf"%s %s\n"(Z.to_string(X509.Certificate.serialcert))certname)inletenc=X509.Certificate.encode_pemcertinBos.OS.File.writeFpath.(vcertname+"pem")(Cstruct.to_stringenc)letpriv_keytypbitsname=letfile=Fpath.(vname+"key")inlet*f_exists=Bos.OS.File.existsfileinifnotf_existsthenbeginLogs.info(funm->m"creating new %a key %a"X509.Key_type.pptypFpath.ppfile);letpriv=X509.Private_key.generate~bitstypinletpem=X509.Private_key.encode_pemprivinlet*()=Bos.OS.File.write~mode:0o400file(Cstruct.to_stringpem)inOkprivendelselet*s=Bos.OS.File.readfileinX509.Private_key.decode_pem(Cstruct.of_strings)openCmdlinerletnam=letdoc="Name to provision"inArg.(required&pos0(somestring)None&info[]~doc~docv:"VM")letcacert=letdoc="cacert"inArg.(required&pos1(somefile)None&info[]~doc~docv:"CACERT")letkey=letdoc="Private key"inArg.(value&opt(somefile)None&info["key"]~doc)letdb=letdoc="Database"inArg.(required&pos0(somestring)None&info[]~doc~docv:"DB")letmem=letdoc="Memory to provision"inArg.(required&pos2(someint)None&info[]~doc~docv:"MEM")