1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495(* (c) 2017 Hannes Mehnert, all rights reserved *)lettimestampsvalidity=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=letopenRresult.R.Infixinletbuf=Bytes.unsafe_of_stringdatainletnam=Fpath.to_stringnameinsafeUnix.(openfilenam[O_APPEND;O_CREAT;O_WRONLY])0o644>>=funfd->letlen=String.lengthdatainletrecgooff=letl=len-offinsafe(Unix.writefdbufoff)l>>=funw->ifl=wthenOk()elsego(w+off)ingo0>>=fun()->safeUnix.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=letopenRresult.R.Infixin(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)"))>>=funcertname->timestampsdelta>>=fun(valid_from,valid_until)->letextensions=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)capubinRresult.R.error_to_msg~pp_error:X509.Validation.pp_signature_error(X509.Signing_request.signcsr~valid_from~valid_until~extensionskeyissuer)>>=funcert->(matchdbnamewith|None->Ok()(* no DB! *)|Somedbname->appenddbname(Printf.sprintf"%s %s\n"(Z.to_string(X509.Certificate.serialcert))certname))>>=fun()->letenc=X509.Certificate.encode_pemcertinBos.OS.File.writeFpath.(vcertname+"pem")(Cstruct.to_stringenc)letpriv_key?(bits=2048)name=letopenRresult.R.Infixinletfile=Fpath.(vname+"key")inBos.OS.File.existsfile>>=function|false->Logs.info(funm->m"creating new RSA key %a"Fpath.ppfile);letpriv=`RSA(Mirage_crypto_pk.Rsa.generate~bits())inBos.OS.File.write~mode:0o400file(Cstruct.to_string(X509.Private_key.encode_pempriv))>>=fun()->Okpriv|true->Bos.OS.File.readfile>>=funs->X509.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")