Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file dns_certify_mirage.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185(* (c) 2018 Hannes Mehnert, all rights reserved *)openLwt.Infixletsrc=Logs.Src.create"dns_certify_mirage"~doc:"effectful DNS certify"moduleLog=(valLogs.src_logsrc:Logs.LOG)moduleMake(R:Mirage_random.S)(P:Mirage_clock.PCLOCK)(TIME:Mirage_time.S)(S:Mirage_stack.V4)=structmoduleD=Dns_mirage.Make(S)letstaging={|-----BEGIN CERTIFICATE-----
MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw
GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2
MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0
8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym
oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0
ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN
xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56
dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9
AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw
HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0
BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu
b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu
Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq
hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF
UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9
AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp
DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7
IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf
zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI
PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w
SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em
2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0
WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt
n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=
-----END CERTIFICATE-----|}letproduction={|-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----|}letnsupdate_csrflowhostkeynamezonednskeycsr=matchDns_certify.nsupdateR.generate(fun()->Ptime.v(P.now_d_ps()))~host~keyname~zonednskeycsrwith|Errors->Lwt.return(Errors)|Ok(out,cb)->D.send_tcp(D.flowflow)out>>=function|Error()->Lwt.return(Error(`Msg"tcp sending error"))|Ok()->D.read_tcpflow>|=function|Error()->Error(`Msg"tcp receive err")|Okdata->matchcbdatawith|Errore->Error(`Msg(Fmt.strf"nsupdate reply error %a"Dns_certify.pp_u_erre))|Ok()->Ok()letquery_certificateflowpublic_keyname=matchDns_certify.queryR.generatepublic_keynamewith|Errore->Lwt.return(Errore)|Ok(out,cb)->D.send_tcp(D.flowflow)out>>=function|Error()->Lwt.return(Error(`Msg"couldn't send tcp"))|Ok()->D.read_tcpflow>|=function|Error()->Error(`Msg"error while reading answer")|Okdata->matchcbdatawith|Errore->Errore|Okcert->Okcertletinitialise_csrhostnamemore_hostnamesseed=letprivate_key=letg,print=matchseedwith|None->(None,true)|Someseed->letseed=Cstruct.of_stringseedinSome(Nocrypto.Rng.(create~seed(moduleGenerators.Fortuna))),falseinletkey=Nocrypto.Rsa.generate?g4096in(ifprintthenletpem=X509.Private_key.encode_pem(`RSAkey)inLog.info(funm->m"using private key@.%s"(Cstruct.to_stringpem)));keyinletcsr=Dns_certify.signing_requesthostname~more_hostnames(`RSAprivate_key)inletpublic_key=`RSA(Nocrypto.Rsa.pub_of_privprivate_key)in(private_key,public_key,csr)letquery_certificate_or_csrflowpubhostnamekeynamezonednskeycsr=query_certificateflowpubhostname>>=function|Okcertificate->Log.info(funm->m"found certificate in DNS");Lwt.return(Okcertificate)|Error(`Msgmsg)->Log.err(funm->m"error %s"msg);Lwt.return(Error(`Msgmsg))|Error((`Decode_|`Bad_reply_|`Unexpected_reply_)ase)->Log.err(funm->m"query error %a, giving up"Dns_certify.pp_q_erre);Lwt.return(Error(`Msg"query error"))|Error`No_tlsa->Log.info(funm->m"no certificate in DNS, need to transmit the CSR");nsupdate_csrflowhostnamekeynamezonednskeycsr>>=function|Error(`Msgmsg)->Log.err(funm->m"failed to nsupdate TLSA %s"msg);Lwt.fail_with"nsupdate issue"|Ok()->letrecwait_for_cert?(retry=10)()=ifretry=0thenLwt.return(Error(`Msg"too many retries, giving up"))elsequery_certificateflowpubhostname>>=function|Okcertificate->Log.info(funm->m"finally found a certificate");Lwt.return(Okcertificate)|Error(`Msgmsg)->Log.err(funm->m"error while querying certificate %s"msg);Lwt.return(Error(`Msgmsg))|Error(#Dns_certify.q_errasq)->Log.info(funm->m"still waiting for certificate, got error %a"Dns_certify.pp_q_errq);TIME.sleep_ns(Duration.of_sec2)>>=fun()->wait_for_cert~retry:(predretry)()inwait_for_cert()letretrieve_certificate?(ca=`Staging)stack~dns_key~hostname?(additional_hostnames=[])?key_seeddnsport=(matchcawith|`Staging->Logs.warn(funm->m"staging environment - test use only")|`Production->Logs.warn(funm->m"production environment - take care what you do"));letkeyname,zone,dnskey=matchDns.Dnskey.name_key_of_stringdns_keywith|Ok(name,key)->letzone=Domain_name.(host_exn(drop_label_exn~amount:2name))in(name,zone,key)|Error(`Msgm)->invalid_arg("failed to parse dnskey: "^m)inletnot_subsubdomain=not(Domain_name.is_subdomain~subdomain~domain:zone)inifnot_subhostname||List.existsnot_subadditional_hostnamesthenLwt.fail_with"hostname not a subdomain of zone provided by dns_key"elseletpriv,pub,csr=initialise_csrhostnameadditional_hostnameskey_seedinS.TCPV4.create_connection(S.tcpv4stack)(dns,port)>>=function|Errore->Log.err(funm->m"error %a while connecting to name server, shutting down"S.TCPV4.pp_errore);Lwt.return(Error(`Msg"couldn't connect to name server"))|Okflow->letflow=D.of_flowflowinquery_certificate_or_csrflowpubhostnamekeynamezonednskeycsr>>=funcertificate->S.TCPV4.close(D.flowflow)>|=fun()->matchcertificatewith|Errore->Errore|Okcertificate->letca=matchcawith|`Production->production|`Staging->staginginmatchX509.Certificate.decode_pem(Cstruct.of_stringca)with|Okca->Ok(`Single([certificate;ca],priv))|Error(`Msgmsg)->Error(`Msgmsg)end