summaryrefslogtreecommitdiff
path: root/src/conf_mode/vyos_cert.py
blob: fb4644d5a3c3ac1030ad81a524620dc657b29739 (plain)
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
#!/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():
    vyos_cert = vyos.defaults.vyos_cert_data

    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)