Source file albatross_provision.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
let ( let* ) = Result.bind
let timestamps validity =
let now = Ptime_clock.now () in
match Ptime.add_span now (Ptime.Span.of_int_s (Duration.to_sec validity)) with
| None -> Error (`Msg "span too big - reached end of ptime")
| Some exp -> Ok (now, exp)
let rec safe f arg =
try Ok (f arg) with
| Unix.Unix_error (Unix.EINTR, _, _) -> safe f arg
| Unix.Unix_error (e, _, _) -> Error (`Msg (Unix.error_message e))
let append name data =
let buf = Bytes.unsafe_of_string data in
let nam = Fpath.to_string name in
let* fd = safe Unix.(openfile nam [ O_APPEND ; O_CREAT ; O_WRONLY ]) 0o644 in
let len = String.length data in
let rec go off =
let l = len - off in
let* w = safe (Unix.write fd buf off) l in
if l = w then Ok ()
else go (w + off)
in
let* () = go 0 in
safe Unix.close fd
let key_ids exts pub issuer =
let auth = Some (X509.Public_key.id issuer), X509.General_name.empty, None in
X509.Extension.(add Subject_key_id (false, X509.Public_key.id pub)
(add Authority_key_id (false, auth) exts))
let sign ?dbname ?certname extensions issuer key csr delta =
let* certname =
match certname with
| Some x -> Ok x
| None ->
match
X509.Distinguished_name.common_name X509.Signing_request.((info csr).subject)
with
| Some name -> Ok name
| None -> Error (`Msg "couldn't find name (no common name in CSR subject)")
in
let* valid_from, valid_until = timestamps delta in
let extensions =
match dbname with
| None -> extensions
| Some _ ->
let capub = X509.Private_key.public key in
key_ids extensions X509.Signing_request.((info csr).public_key) capub
in
let* cert =
Result.map_error
(fun e -> `Msg (Fmt.to_to_string X509.Validation.pp_signature_error e))
(X509.Signing_request.sign csr ~valid_from ~valid_until ~extensions key issuer)
in
let* () =
match dbname with
| None -> Ok ()
| Some dbname ->
append dbname (Printf.sprintf "%s %s\n" (Z.to_string (X509.Certificate.serial cert)) certname)
in
let enc = X509.Certificate.encode_pem cert in
Bos.OS.File.write Fpath.(v certname + "pem") (Cstruct.to_string enc)
let priv_key typ bits name =
let file = Fpath.(v name + "key") in
let* f_exists = Bos.OS.File.exists file in
if not f_exists then begin
Logs.info (fun m -> m "creating new %a key %a"
X509.Key_type.pp typ Fpath.pp file);
let priv = X509.Private_key.generate ~bits typ in
let pem = X509.Private_key.encode_pem priv in
let* () = Bos.OS.File.write ~mode:0o400 file (Cstruct.to_string pem) in
Ok priv
end else
let* s = Bos.OS.File.read file in
X509.Private_key.decode_pem (Cstruct.of_string s)
open Cmdliner
let nam =
let doc = "Name to provision" in
Arg.(required & pos 0 (some string) None & info [] ~doc ~docv:"VM")
let cacert =
let doc = "cacert" in
Arg.(required & pos 1 (some file) None & info [] ~doc ~docv:"CACERT")
let key =
let doc = "Private key" in
Arg.(value & opt (some file) None & info [ "key" ] ~doc)
let db =
let doc = "Database" in
Arg.(required & pos 0 (some string) None & info [] ~doc ~docv:"DB")
let mem =
let doc = "Memory to provision" in
Arg.(required & pos 2 (some int) None & info [] ~doc ~docv:"MEM")