blob: cc8065cdf8ff77357cccfd0f2be9584d0db8a7c4 [file] [log] [blame]
chojoyceb0817032022-01-12 18:29:36 +08001# Copyright 2022 - The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14r"""Mkcert entry point.
15
16Mkcert will handle the SSL certificates process to secure WEB browser of
17a local or remote instance of an Android Virtual Device.
18"""
19
20import filecmp
21import logging
22import os
23import platform
chojoyce32ee70e2022-02-11 12:39:53 +080024import shutil
chojoyce8c7f7ad2022-02-18 19:55:13 +080025import stat
chojoyceb0817032022-01-12 18:29:36 +080026
27from acloud.internal import constants
28from acloud.internal.lib import utils
29
30logger = logging.getLogger(__name__)
31
32_CA_NAME = constants.SSL_CA_NAME
33_CERT_DIR = os.path.join(os.path.expanduser("~"), constants.SSL_DIR)
34_CA_KEY_PATH = os.path.join(_CERT_DIR, f"{_CA_NAME}.key")
35_CA_CRT_PATH = os.path.join(_CERT_DIR, f"{_CA_NAME}.pem")
36_CERT_KEY_PATH = os.path.join(_CERT_DIR, "server.key")
37_CERT_CSR_PATH = os.path.join(_CERT_DIR, "server.csr")
38_CERT_CRT_PATH = os.path.join(_CERT_DIR, "server.crt")
39_CA_EXT = "keyUsage=critical,keyCertSign"
40_CA_SUBJ="/OU=acloud/O=acloud development CA/CN=localhost"
41_CERT_SUBJ = "/OU=%s/O=acloud development CA" % platform.node()
42_TRUST_CA_PATH = os.path.join(constants.SSL_TRUST_CA_DIR,
43 f"{_CA_NAME}.crt")
44_CERT_CRT_EXT = ";".join(f"echo \"{ext}\"" for ext in [
45 "keyUsage = critical, digitalSignature, keyEncipherment",
46 "extendedKeyUsage = serverAuth",
47 "subjectAltName = DNS.1:localhost, IP.1:0.0.0.0, IP.2:::1"])
48
49# Generate a Root SSL Certificate.
50_CA_CMD = (f"openssl req -new -x509 -days 9999 -newkey rsa:2048 "
51 f"-sha256 -nodes -keyout \"{_CA_KEY_PATH}\" "
52 f"-out \"{_CA_CRT_PATH}\" -extensions v3_ca "
53 f"-subj \"{_CA_SUBJ}\" -addext \"{_CA_EXT}\"")
54
55# Trust the Root SSL Certificate.
chojoyce71855af2022-02-16 17:18:32 +080056_TRUST_CA_COPY_CMD = f"sudo cp -p {_CA_CRT_PATH} {_TRUST_CA_PATH}"
chojoyceb0817032022-01-12 18:29:36 +080057_UPDATE_TRUST_CA_CMD = "sudo update-ca-certificates"
58_TRUST_CHROME_CMD = (
59 "certutil -d sql:$HOME/.pki/nssdb -A -t TC "
60 f"-n \"{_CA_NAME}\" -i \"{_TRUST_CA_PATH}\"")
61
62# Generate an SSL SAN Certificate with the Root Certificate.
63_CERT_KEY_CMD = f"openssl genrsa -out \"{_CERT_KEY_PATH}\" 2048"
64_CERT_CSR_CMD = (f"openssl req -new -key \"{_CERT_KEY_PATH}\" "
65 f"-out \"{_CERT_CSR_PATH}\" -subj \"{_CERT_SUBJ}\"")
66_CERT_CRT_CMD = (
67 f"openssl x509 -req -days 9999 -in \"{_CERT_CSR_PATH}\" "
68 f"-CA \"{_CA_CRT_PATH}\" -CAkey \"{_CA_KEY_PATH}\" "
69 f"-CAcreateserial -out \"{_CERT_CRT_PATH}\" "
70 f"-extfile <({_CERT_CRT_EXT};)")
71
72# UnInstall the Root SSL Certificate.
73_UNDO_TRUST_CA_CMD = f"sudo rm {_TRUST_CA_PATH}"
74_UNDO_TRUST_CHROME_CMD = f"certutil -D -d sql:$HOME/.pki/nssdb -n \"{_CA_NAME}\""
75
76
77def Install():
78 """Install Root SSL Certificates by the openssl tool.
79
80 Generates a Root SSL Certificates and setup the host environment
81 to build a secure browser for WebRTC AVD.
82
83 Returns:
84 True when the Root SSL Certificates are generated and setup.
85 """
chojoyce32ee70e2022-02-11 12:39:53 +080086 if os.path.isdir(_CERT_DIR):
87 shutil.rmtree(_CERT_DIR)
88 os.mkdir(_CERT_DIR)
chojoyceb0817032022-01-12 18:29:36 +080089
90 if os.path.exists(_TRUST_CA_PATH):
91 UnInstall()
92
chojoyce32ee70e2022-02-11 12:39:53 +080093 utils.Popen(_CA_CMD, shell=True)
chojoyce8c7f7ad2022-02-18 19:55:13 +080094 # The rootCA.pem file should grant READ permission to others.
95 if not os.stat(_CA_CRT_PATH).st_mode & stat.S_IROTH:
96 os.chmod(_CA_CRT_PATH, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
chojoyce32ee70e2022-02-11 12:39:53 +080097 utils.Popen(_TRUST_CA_COPY_CMD, shell=True)
98 utils.Popen(_UPDATE_TRUST_CA_CMD, shell=True)
99 utils.Popen(_TRUST_CHROME_CMD, shell=True)
chojoyceb0817032022-01-12 18:29:36 +0800100
101 return IsRootCAReady()
102
103
104def AllocateLocalHostCert():
105 """Allocate localhost certificate by the openssl tool.
106
107 Generate an SSL SAN Certificate with the Root Certificate.
108
109 Returns:
110 True if the certificates is exist.
111 """
112 if not IsRootCAReady():
113 logger.debug("Can't load CA files.")
114 return False
115
116 if not os.path.exists(_CERT_KEY_PATH):
117 utils.Popen(_CERT_KEY_CMD, shell=True)
118 if not os.path.exists(_CERT_CSR_PATH):
119 utils.Popen(_CERT_CSR_CMD, shell=True)
120 if not os.path.exists(_CERT_CRT_PATH):
121 utils.Popen(_CERT_CRT_CMD, shell=True)
122
123 return IsCertificateReady()
124
125
126def IsRootCAReady():
127 """Check if the Root SSL Certificates are all ready.
128
129 Returns:
130 True if the Root SSL Certificates are exist.
131 """
132 for cert_file_name in [_CA_KEY_PATH, _CA_CRT_PATH, _TRUST_CA_PATH]:
133 if not os.path.exists(cert_file_name):
134 logger.debug("Root SSL Certificate: %s, does not exist",
135 cert_file_name)
136 return False
chojoyce8c7f7ad2022-02-18 19:55:13 +0800137 # TODO: this check can be delete when the mkcert mechanism is stable.
138 if not os.stat(_TRUST_CA_PATH).st_mode & stat.S_IROTH:
139 return False
chojoyceb0817032022-01-12 18:29:36 +0800140
141 if not filecmp.cmp(_CA_CRT_PATH, _TRUST_CA_PATH):
142 logger.debug("The trusted CA %s file is not the same with %s ",
143 _TRUST_CA_PATH, _CA_CRT_PATH)
144 return False
145 return True
146
147
148def IsCertificateReady():
149 """Check if the SSL SAN Certificates files are all ready.
150
151 Returns:
152 True if the SSL SAN Certificates files existed.
153 """
154 for cert_file_name in [_CERT_KEY_PATH, _CERT_CRT_PATH]:
155 if not os.path.exists(cert_file_name):
156 logger.debug("SSL SAN Certificate: %s, does not exist",
157 cert_file_name)
158 return False
159 return True
160
161
162def UnInstall():
163 """Uninstall a Root SSL Certificate.
164
165 Undo the Root SSL Certificate host setup.
166 """
167 utils.Popen(_UNDO_TRUST_CA_CMD, shell=True)
168 utils.Popen(_UPDATE_TRUST_CA_CMD, shell=True)
169 utils.Popen(_UNDO_TRUST_CHROME_CMD, shell=True)