1use pass_tool::{
2 actions::{
3 action, command, create_dir_perm, delete_file, install_apt_packages, many, perm,
4 start_service, stop_service, write_file, write_file_perm,
5 },
6 checks::{
7 check, is_file, path_is_missing, service_is_inactive, stdout_contains_once, user_is_root,
8 },
9 instruction, run_cli_with_input, Playbook,
10};
11
12const ABOUT: &str = "Example installing nginx webserver with letsencrypt certificate.
13This playbook will:
14 - configure firewall (will allow ssh, http, https)
15 - enable letsencrypt certificates for domain
16 - configure nginx static website with https support";
17const HELP: &str = "For configuration you need to provide comma separated values of your email address (for letsencrypt registration) and your domain name, example:
18
19```
20apply your_email@domain.com,your_domain.com
21```";
22const NGINX_CONF: &str = include_str!("https_webserver_nginx.conf");
23const CERTBOT_RENEW: &str = include_str!("https_webserver_certbot-renew");
24const INDEX_HTML: &str = include_str!("https_webserver_index.html");
25
26fn main() {
27 run_cli_with_input(
28 |input| {
29 let input = String::from_utf8(input.to_vec()).or(Err(HELP.to_owned()))?;
30 let mut parts = input.split(',');
31 let [Some(email), Some(domain)] = [parts.next(), parts.next()] else {
32 return Err(HELP.to_owned());
33 };
34 let nginx_conf = NGINX_CONF
35 .split("{--domain--}")
36 .collect::<Vec<_>>()
37 .join(domain);
38 Ok(Playbook::new(
39 "Install and configure nginx with https",
40 ABOUT,
41 [
42 user_is_root(),
43 check(
44 "Os is Ubuntu 20.04",
45 stdout_contains_once(["lsb_release", "-a"], "Ubuntu 20.04"),
46 ),
47 ],
48 [
49 instruction(action(
50 "Upgrade apt packages",
51 many([
52 command(["apt", "update", "-y"]),
53 command(["apt", "upgrade", "-y"]),
54 ]),
55 )),
56 instruction(install_apt_packages(["nginx", "certbot"])),
57 instruction(action(
58 "Configure firewall",
59 many([
60 command(["ufw", "allow", "ssh"]),
61 command(["ufw", "allow", "http"]),
62 command(["ufw", "allow", "https"]),
63 command(["ufw", "default", "deny", "incoming"]),
64 command(["ufw", "default", "allow", "outgoing"]),
65 ]),
66 ))
67 .with_env(check(
68 "Firewall is inactive",
69 stdout_contains_once(["ufw", "status"], "Status: inactive"),
70 )),
71 instruction(action("Stop nginx", stop_service("nginx"))),
72 instruction(action(
73 "Request ssl certificate",
74 command([
75 "certbot",
76 "certonly",
77 "--standalone",
78 "--agree-tos",
79 "--no-eff-email",
80 "-m",
81 email,
82 "-d",
83 domain,
84 ]),
85 ))
86 .with_env(check("Nginx is inactive", service_is_inactive("nginx")))
87 .confirm(check(
88 "Ssl certificate exists",
89 is_file(format!("/etc/letsencrypt/live/{domain}/fullchain.pem")),
90 )),
91 instruction(action(
92 "Enable certbot renew",
93 write_file_perm(
94 "/etc/cron.weekly/certbot-renew",
95 CERTBOT_RENEW,
96 perm(0o555, "root"),
97 ),
98 ))
99 .confirm(check(
100 "Certbot renew is enabled",
101 is_file("/etc/cron.weekly/certbot-renew"),
102 )),
103 instruction(action(
104 "Delete default nginx site",
105 delete_file("/etc/nginx/sites-enabled/default"),
106 ))
107 .confirm(check(
108 "Default nginx site deleted",
109 path_is_missing("/etc/nginx/sites-enabled/default"),
110 )),
111 instruction(action(
112 "Create pass demo site nginx configuration",
113 write_file("/etc/nginx/sites-enabled/pass-demo", nginx_conf),
114 ))
115 .confirm(check(
116 "Pass demo site nginx configuration is exists",
117 is_file("/etc/nginx/sites-enabled/pass-demo"),
118 )),
119 instruction(action(
120 "Create website files",
121 many([
122 create_dir_perm("/srv/pass-demo-site", perm(0o774, "www-data")),
123 write_file_perm(
124 "/srv/pass-demo-site/index.html",
125 INDEX_HTML,
126 perm(0o664, "www-data"),
127 ),
128 ]),
129 )),
130 instruction(action("Start nginx", start_service("nginx"))),
131 instruction(action("Start firewall", start_service("ufw"))),
132 instruction(action(
133 "Enable firewall",
134 command(["ufw", "--force", "enable"]),
135 )),
136 ],
137 ))
138 },
139 HELP,
140 include_str!("https_webserver.rs"),
141 );
142}