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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#!/usr/bin/env python3
#
# Copyright (C) 2019 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
import sys
import os
import tempfile
import pathlib
import ssl
import vyos.defaults
from vyos.config import Config
from vyos import ConfigError
from vyos.util import cmd
from vyos import airbag
airbag.enable()
vyos_conf_scripts_dir = vyos.defaults.directories['conf_mode']
# XXX: this model will need to be extended for tag nodes
dependencies = [
'https.py',
]
def status_self_signed(cert_data):
# check existence and expiration date
path = pathlib.Path(cert_data['conf'])
if not path.is_file():
return False
path = pathlib.Path(cert_data['crt'])
if not path.is_file():
return False
path = pathlib.Path(cert_data['key'])
if not path.is_file():
return False
# check if certificate is 1/2 past lifetime, with openssl -checkend
end_days = int(cert_data['lifetime'])
end_seconds = int(0.5*60*60*24*end_days)
checkend_cmd = 'openssl x509 -checkend {end} -noout -in {crt}'.format(end=end_seconds, **cert_data)
try:
cmd(checkend_cmd, message='Called process error')
return True
except OSError as err:
if err.errno == 1:
return False
print(err)
# XXX: This seems wrong to continue on failure
# implicitely returning None
def generate_self_signed(cert_data):
san_config = None
if ssl.OPENSSL_VERSION_INFO < (1, 1, 1, 0, 0):
san_config = tempfile.NamedTemporaryFile()
with open(san_config.name, 'w') as fd:
fd.write('[req]\n')
fd.write('distinguished_name=req\n')
fd.write('[san]\n')
fd.write('subjectAltName=DNS:vyos\n')
openssl_req_cmd = ('openssl req -x509 -nodes -days {lifetime} '
'-newkey rsa:4096 -keyout {key} -out {crt} '
'-subj "/O=Sentrium/OU=VyOS/CN=vyos" '
'-extensions san -config {san_conf}'
''.format(san_conf=san_config.name,
**cert_data))
else:
openssl_req_cmd = ('openssl req -x509 -nodes -days {lifetime} '
'-newkey rsa:4096 -keyout {key} -out {crt} '
'-subj "/O=Sentrium/OU=VyOS/CN=vyos" '
'-addext "subjectAltName=DNS:vyos"'
''.format(**cert_data))
try:
cmd(openssl_req_cmd, message='Called process error')
except OSError as err:
print(err)
# XXX: seems wrong to ignore the failure
os.chmod('{key}'.format(**cert_data), 0o400)
with open('{conf}'.format(**cert_data), 'w') as f:
f.write('ssl_certificate {crt};\n'.format(**cert_data))
f.write('ssl_certificate_key {key};\n'.format(**cert_data))
if san_config:
san_config.close()
def get_config(config=None):
vyos_cert = vyos.defaults.vyos_cert_data
if config:
conf = config
else:
conf = Config()
if not conf.exists('service https certificates system-generated-certificate'):
return None
else:
conf.set_level('service https certificates system-generated-certificate')
if conf.exists('lifetime'):
lifetime = conf.return_value('lifetime')
vyos_cert['lifetime'] = lifetime
return vyos_cert
def verify(vyos_cert):
return None
def generate(vyos_cert):
if vyos_cert is None:
return None
if not status_self_signed(vyos_cert):
generate_self_signed(vyos_cert)
def apply(vyos_cert):
for dep in dependencies:
command = '{0}/{1}'.format(vyos_conf_scripts_dir, dep)
cmd(command, raising=ConfigError)
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
sys.exit(1)
|