Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.toolbox
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Alain Takoudjou
slapos.toolbox
Commits
325666f9
Commit
325666f9
authored
Mar 28, 2017
by
Alain Takoudjou
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
certificate_authority: reimplement with new api
parent
3cdc9427
Changes
11
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
587 additions
and
295 deletions
+587
-295
slapos/certificate_authority/certificate_authority.py
slapos/certificate_authority/certificate_authority.py
+88
-41
slapos/certificate_authority/request.py
slapos/certificate_authority/request.py
+7
-4
slapos/certificate_authority/run.py
slapos/certificate_authority/run.py
+16
-4
slapos/certificate_authority/web/models.py
slapos/certificate_authority/web/models.py
+1
-16
slapos/certificate_authority/web/settings.py
slapos/certificate_authority/web/settings.py
+45
-32
slapos/certificate_authority/web/start_web.py
slapos/certificate_authority/web/start_web.py
+8
-1
slapos/certificate_authority/web/tools/certificate.py
slapos/certificate_authority/web/tools/certificate.py
+18
-41
slapos/certificate_authority/web/tools/error.py
slapos/certificate_authority/web/tools/error.py
+55
-0
slapos/certificate_authority/web/tools/tools.py
slapos/certificate_authority/web/tools/tools.py
+9
-0
slapos/certificate_authority/web/tools/users.py
slapos/certificate_authority/web/tools/users.py
+33
-0
slapos/certificate_authority/web/views.py
slapos/certificate_authority/web/views.py
+307
-156
No files found.
slapos/certificate_authority/certificate_authority.py
View file @
325666f9
...
@@ -198,6 +198,19 @@ class CertificateBase(object):
...
@@ -198,6 +198,19 @@ class CertificateBase(object):
else
:
else
:
return
False
return
False
def
checkCertificateValidity
(
self
,
ca_cert_file
,
cert_file
,
key_file
=
None
):
with
open
(
ca_cert_file
)
as
f_ca
:
ca_cert
=
f_ca
.
read
()
with
open
(
cert_file
)
as
f_cert
:
cert
=
f_cert
.
read
()
# XXX Considering only one trusted certificate here
if
not
self
.
verifyCertificateChain
(
cert
,
[
ca_cert
]):
return
False
if
key_file
:
return
self
.
validateCertAndKey
(
cert_file
,
key_file
)
return
True
def
generatePrivatekey
(
self
,
output_file
,
size
=
2048
):
def
generatePrivatekey
(
self
,
output_file
,
size
=
2048
):
key
=
crypto
.
PKey
()
key
=
crypto
.
PKey
()
key
.
generate_key
(
crypto
.
TYPE_RSA
,
size
)
key
.
generate_key
(
crypto
.
TYPE_RSA
,
size
)
...
@@ -215,7 +228,7 @@ class CertificateBase(object):
...
@@ -215,7 +228,7 @@ class CertificateBase(object):
def
generateCertificateRequest
(
self
,
key_file
,
output_file
,
cn
,
def
generateCertificateRequest
(
self
,
key_file
,
output_file
,
cn
,
country
,
state
,
locality
=
''
,
email
=
''
,
organization
=
''
,
country
,
state
,
locality
=
''
,
email
=
''
,
organization
=
''
,
organization_unit
=
''
,
digest
=
"sha
1
"
):
organization_unit
=
''
,
digest
=
"sha
256
"
):
with
open
(
key_file
)
as
fkey
:
with
open
(
key_file
)
as
fkey
:
key
=
crypto
.
load_privatekey
(
crypto
.
FILETYPE_PEM
,
fkey
.
read
())
key
=
crypto
.
load_privatekey
(
crypto
.
FILETYPE_PEM
,
fkey
.
read
())
...
@@ -238,20 +251,38 @@ class CertificateBase(object):
...
@@ -238,20 +251,38 @@ class CertificateBase(object):
os
.
chmod
(
output_file
,
0644
)
os
.
chmod
(
output_file
,
0644
)
def
checkCertificateValidity
(
self
,
ca_cert_file
,
cert_file
,
key_file
=
None
):
def
signData
(
self
,
key_file
,
data
,
digest
=
"sha256"
,
output_file
=
None
):
with
open
(
ca_cert_file
)
as
f_ca
:
"""
ca_cert
=
f_ca
.
read
()
Sign a data using digest and return signature. If output_file is provided
with
open
(
cert_file
)
as
f_cert
:
the signature will be written into the file.
cert
=
f_cert
.
read
()
"""
with
open
(
key_file
)
as
fkey
:
pkey
=
crypto
.
load_privatekey
(
crypto
.
FILETYPE_PEM
,
fkey
.
read
())
# XXX Considering only one trusted certificate here
sign
=
crypto
.
sign
(
pkey
,
data
,
digest
)
if
not
self
.
verifyCertificateChain
(
cert
,
[
ca_cert
]):
# data_base64 = base64.b64encode(sign)
return
False
if
output_file
is
None
:
return
False
return
sign
if
key_file
:
fd
=
os
.
open
(
output_file
,
return
self
.
validateCertAndKey
(
cert_file
,
key_file
)
os
.
O_CREAT
|
os
.
O_WRONLY
|
os
.
O_EXCL
|
os
.
O_TRUNC
,
return
True
0644
)
try
:
os
.
write
(
fd
,
sign
)
finally
:
os
.
close
(
fd
)
return
sign
def
verifyData
(
self
,
cert_string
,
signature
,
data
,
digest
=
"sha256"
):
"""
Verify the signature for a data string.
cert_string: is the certificate content as string
signature: is generate using 'signData' from the data to verify
data: content to verify
digest: by default is sha256, set the correct value
"""
x509
=
crypto
.
load_certificate
(
crypto
.
FILETYPE_PEM
,
cert
)
return
crypto
.
verify
(
x509
,
signature
,
data
,
digest
)
def
readCertificateRequest
(
self
,
csr
):
def
readCertificateRequest
(
self
,
csr
):
req
=
crypto
.
load_certificate_request
(
crypto
.
FILETYPE_PEM
,
csr
)
req
=
crypto
.
load_certificate_request
(
crypto
.
FILETYPE_PEM
,
csr
)
...
@@ -415,6 +446,11 @@ class CertificateAuthorityRequest(CertificateBase):
...
@@ -415,6 +446,11 @@ class CertificateAuthorityRequest(CertificateBase):
self
.
ca_url
=
ca_url
self
.
ca_url
=
ca_url
self
.
logger
=
logger
self
.
logger
=
logger
self
.
max_retry
=
max_retry
self
.
max_retry
=
max_retry
self
.
X509Extension
=
X509Extension
()
while
self
.
ca_url
.
endswith
(
'/'
):
# remove all / at end or ca_url
self
.
ca_url
=
self
.
ca_url
[:
-
1
]
if
self
.
logger
is
None
:
if
self
.
logger
is
None
:
self
.
logger
=
logging
.
getLogger
(
'Certificate Request'
)
self
.
logger
=
logging
.
getLogger
(
'Certificate Request'
)
...
@@ -446,7 +482,7 @@ class CertificateAuthorityRequest(CertificateBase):
...
@@ -446,7 +482,7 @@ class CertificateAuthorityRequest(CertificateBase):
if
os
.
path
.
exists
(
self
.
cacertificate
)
and
os
.
stat
(
self
.
cacertificate
).
st_size
>
0
:
if
os
.
path
.
exists
(
self
.
cacertificate
)
and
os
.
stat
(
self
.
cacertificate
).
st_size
>
0
:
return
return
ca_cert_url
=
'%s/
ge
t/cacert.pem'
%
self
.
ca_url
ca_cert_url
=
'%s/
cr
t/cacert.pem'
%
self
.
ca_url
self
.
logger
.
info
(
"getting CA certificate file %s"
%
ca_cert_url
)
self
.
logger
.
info
(
"getting CA certificate file %s"
%
ca_cert_url
)
response
=
None
response
=
None
while
not
response
or
response
.
status_code
!=
200
:
while
not
response
or
response
.
status_code
!=
200
:
...
@@ -479,7 +515,7 @@ class CertificateAuthorityRequest(CertificateBase):
...
@@ -479,7 +515,7 @@ class CertificateAuthorityRequest(CertificateBase):
data
=
{
'csr'
:
csr
}
data
=
{
'csr'
:
csr
}
retry
=
0
retry
=
0
sleep_time
=
10
sleep_time
=
10
request_url
=
'%s/
request
'
%
self
.
ca_url
request_url
=
'%s/
csr
'
%
self
.
ca_url
# Save Cert in tmp to check later
# Save Cert in tmp to check later
cert_temp
=
'%s.tmp'
%
self
.
certificate
cert_temp
=
'%s.tmp'
%
self
.
certificate
csr_key_file
=
'%s.key'
%
csr_file
csr_key_file
=
'%s.key'
%
csr_file
...
@@ -491,31 +527,34 @@ class CertificateAuthorityRequest(CertificateBase):
...
@@ -491,31 +527,34 @@ class CertificateAuthorityRequest(CertificateBase):
csr_key
=
fkey
.
read
()
csr_key
=
fkey
.
read
()
if
csr_key
:
if
csr_key
:
self
.
logger
.
info
(
"Csr was already sent to CA,
key is
: %s"
%
csr_key
)
self
.
logger
.
info
(
"Csr was already sent to CA,
using csr
: %s"
%
csr_key
)
else
:
else
:
response
=
self
.
_request
(
'p
os
t'
,
request_url
,
data
=
data
)
response
=
self
.
_request
(
'p
u
t'
,
request_url
,
data
=
data
)
while
(
not
response
or
response
.
status_code
!=
20
0
)
and
retry
<
self
.
max_retry
:
while
(
not
response
or
response
.
status_code
!=
20
1
)
and
retry
<
self
.
max_retry
:
self
.
logger
.
error
(
"%s: Failed to sen
d
CSR.
\
n
%s"
%
(
self
.
logger
.
error
(
"%s: Failed to sen
t
CSR.
\
n
%s"
%
(
response
.
status_code
,
response
.
text
))
response
.
status_code
,
response
.
text
))
self
.
logger
.
info
(
"will retry in %s seconds..."
%
sleep_time
)
self
.
logger
.
info
(
"will retry in %s seconds..."
%
sleep_time
)
time
.
sleep
(
sleep_time
)
time
.
sleep
(
sleep_time
)
retry
+=
1
retry
+=
1
response
=
self
.
_request
(
'p
os
t'
,
request_url
,
data
=
data
)
response
=
self
.
_request
(
'p
u
t'
,
request_url
,
data
=
data
)
if
response
.
status_code
!=
20
0
:
if
response
.
status_code
!=
20
1
:
raise
Exception
(
"ERROR: failed to p
os
t CSR after % retry. Exiting..."
%
retry
)
raise
Exception
(
"ERROR: failed to p
u
t CSR after % retry. Exiting..."
%
retry
)
self
.
logger
.
info
(
"CSR succefully sent."
)
self
.
logger
.
info
(
"CSR succefully sent."
)
self
.
logger
.
debug
(
"Server reponse with csr key is %s"
%
response
.
text
)
# Get csr Location from request header: http://xxx.com/csr/key
csr_key
=
response
.
text
self
.
logger
.
debug
(
"Csr location is: %s"
%
response
.
headers
[
'Location'
])
csr_key
=
response
.
headers
[
'Location'
].
split
(
'/'
)[
-
1
]
with
open
(
csr_key_file
,
'w'
)
as
fkey
:
with
open
(
csr_key_file
,
'w'
)
as
fkey
:
fkey
.
write
(
response
.
text
)
fkey
.
write
(
response
.
text
)
# csr is xxx.csr.pem so cert is xxx.cert.pem
self
.
logger
.
info
(
"Waiting for signed certificate..."
)
self
.
logger
.
info
(
"Waiting for signed certificate..."
)
reply_url
=
'%s/
get/%s.cert.pem'
%
(
self
.
ca_url
,
csr_key
)
reply_url
=
'%s/
crt/%s.cert.pem'
%
(
self
.
ca_url
,
csr_key
[:
-
8
]
)
response
=
self
.
_request
(
'get'
,
reply_url
)
response
=
self
.
_request
(
'get'
,
reply_url
)
while
not
response
or
response
.
status_code
!=
200
:
while
not
response
or
response
.
status_code
!=
200
:
...
@@ -535,7 +574,7 @@ class CertificateAuthorityRequest(CertificateBase):
...
@@ -535,7 +574,7 @@ class CertificateAuthorityRequest(CertificateBase):
os
.
close
(
fd
)
os
.
close
(
fd
)
os
.
unlink
(
cert_temp
)
os
.
unlink
(
cert_temp
)
else
:
else
:
if
auto_revoke
:
"""
if auto_revoke:
self.logger.error("Certificate validation failed. "
\
self.logger.error("Certificate validation failed. "
\
"The signed certificate is going to be revoked...")
"The signed certificate is going to be revoked...")
self.revokeCertificateRequest(cert_temp,
self.revokeCertificateRequest(cert_temp,
...
@@ -547,38 +586,46 @@ class CertificateAuthorityRequest(CertificateBase):
...
@@ -547,38 +586,46 @@ class CertificateAuthorityRequest(CertificateBase):
except OSError, e:
except OSError, e:
if e.errno != errno.ENOENT:
if e.errno != errno.ENOENT:
# raise
# raise
pass
pass
"""
raise
Exception
(
"Error: Certificate validation failed. "
\
raise
Exception
(
"Error: Certificate validation failed. "
\
"This signed certificate should be revoked!"
)
"This signed certificate should be revoked!"
)
self
.
logger
.
info
(
"Certificate correctly saved at %s."
%
self
.
certificate
)
self
.
logger
.
info
(
"Certificate correctly saved at %s."
%
self
.
certificate
)
def
revokeCertificateRequest
(
self
,
cert_file
,
key_name
,
message
=
""
):
def
revokeCertificateRequest
(
self
,
cert_file
,
message
=
""
):
"""
"""
Send a revocation request for the givent certificate to the master.
Send a revocation request for the givent certificate to the master.
"""
"""
sleep_time
=
10
sleep_time
=
10
retry
=
0
retry
=
0
cert
=
self
.
freadX509
(
cert_file
)
serial
=
'{0:x}'
.
format
(
int
(
cert
.
get_serial_number
()))
request_url
=
'%s/requestrevoke'
%
self
.
ca_url
data
=
{
'serial'
:
serial
,
'name'
:
key_name
,
'reason'
:
message
}
self
.
logger
.
info
(
"Sent Certificate revocation request for %s, serial=%s."
%
(
with
open
(
cert_file
)
as
f
:
key_name
,
serial
))
cert_string
=
f
.
read
()
cert
=
self
.
readX509
(
cert_string
)
digest
=
"sha256"
payload
=
json
.
dumps
(
dict
(
reason
=
message
,
cert
=
cert_string
))
signature
=
self
.
signData
(
self
.
key
,
payload
,
digest
)
request_url
=
'%s/crt/revoke'
%
self
.
ca_url
data
=
{
'digest'
:
digest
,
'payload'
:
payload
,
'signature'
:
signature
}
self
.
logger
.
info
(
"Sent Certificate revocation request for CN: %s."
%
(
cert
.
get_subject
().
CN
))
response
=
self
.
_request
(
'p
os
t'
,
request_url
,
data
=
data
)
response
=
self
.
_request
(
'p
u
t'
,
request_url
,
data
=
data
)
while
(
not
response
or
response
.
status_code
!=
20
0
)
and
retry
<
self
.
max_retry
:
while
(
not
response
or
response
.
status_code
!=
20
1
)
and
retry
<
self
.
max_retry
:
self
.
logger
.
error
(
"%s: Failed to send Rovocation request.
\
n
%s"
%
(
self
.
logger
.
error
(
"%s: Failed to send Rovocation request.
\
n
%s"
%
(
response
.
status_code
,
response
.
text
))
response
.
status_code
,
response
.
text
))
self
.
logger
.
info
(
"will retry in %s seconds..."
%
sleep_time
)
self
.
logger
.
info
(
"will retry in %s seconds..."
%
sleep_time
)
time
.
sleep
(
sleep_time
)
time
.
sleep
(
sleep_time
)
retry
+=
1
retry
+=
1
response
=
self
.
_request
(
'p
os
t'
,
request_url
,
data
=
data
)
response
=
self
.
_request
(
'p
u
t'
,
request_url
,
data
=
data
)
if
response
.
status_code
!=
20
0
:
if
response
.
status_code
!=
20
1
:
raise
Exception
(
"ERROR: failed to post revoke request after %s retry. Exiting..."
%
retry
)
raise
Exception
(
"ERROR: failed to post revoke request after %s retry. Exiting..."
%
retry
)
self
.
logger
.
info
(
"Certificate revocation request for %s
.cert.pem
successfully sent."
%
(
self
.
logger
.
info
(
"Certificate revocation request for %s successfully sent."
%
(
key_nam
e
))
cert_fil
e
))
slapos/certificate_authority/request.py
View file @
325666f9
...
@@ -50,9 +50,12 @@ def parseArguments():
...
@@ -50,9 +50,12 @@ def parseArguments():
parser
.
add_argument
(
'--organization_unit'
,
parser
.
add_argument
(
'--organization_unit'
,
default
=
'Company Unit'
,
default
=
'Company Unit'
,
help
=
'The Organisation Unit Name'
)
help
=
'The Organisation Unit Name'
)
parser
.
add_argument
(
'--auto_revoke'
,
default
=
True
,
action
=
"store_true"
,
parser
.
add_argument
(
'--revoke'
,
help
=
'Request Revoke Certificate if validation fail'
)
default
=
False
,
action
=
"store_true"
,
help
=
'Revoke the current certificate'
)
parser
.
add_argument
(
'--revoke_reason'
,
help
=
'Say why the certificat should be revoked'
)
return
parser
return
parser
...
@@ -84,7 +87,7 @@ def requestCertificateWeb():
...
@@ -84,7 +87,7 @@ def requestCertificateWeb():
cn
=
config
.
cn
,
country
=
config
.
country
,
state
=
config
.
state
,
cn
=
config
.
cn
,
country
=
config
.
country
,
state
=
config
.
state
,
locality
=
config
.
locality
,
email
=
config
.
email
,
locality
=
config
.
locality
,
email
=
config
.
email
,
organization
=
config
.
organization
,
organization
=
config
.
organization
,
organization_unit
=
config
.
organization_unit
,
digest
=
"sha
1
"
)
organization_unit
=
config
.
organization_unit
,
digest
=
"sha
256
"
)
ca
.
signCertificateWeb
(
config
.
csr_file
,
auto_revoke
=
config
.
auto_revoke
)
ca
.
signCertificateWeb
(
config
.
csr_file
,
auto_revoke
=
config
.
auto_revoke
)
slapos/certificate_authority/run.py
View file @
325666f9
...
@@ -13,7 +13,6 @@ import traceback
...
@@ -13,7 +13,6 @@ import traceback
from
flask_user
import
UserManager
,
SQLAlchemyAdapter
from
flask_user
import
UserManager
,
SQLAlchemyAdapter
from
flask_mail
import
Mail
from
flask_mail
import
Mail
from
slapos.certificate_authority.web.views
import
app
from
slapos.certificate_authority.web.views
import
app
from
slapos.certificate_authority.web.start_web
import
app
,
db
,
init_app
from
slapos.certificate_authority.certificate_authority
import
CertificateAuthority
from
slapos.certificate_authority.certificate_authority
import
CertificateAuthority
def
parseArguments
():
def
parseArguments
():
...
@@ -53,6 +52,9 @@ def parseArguments():
...
@@ -53,6 +52,9 @@ def parseArguments():
help
=
'Path for log output'
)
help
=
'Path for log output'
)
parser
.
add_argument
(
'--db_file'
,
parser
.
add_argument
(
'--db_file'
,
help
=
'Path of file to use to store User Account information. Default: $ca_dir/ca.db'
)
help
=
'Path of file to use to store User Account information. Default: $ca_dir/ca.db'
)
#parser.add_argument('--external_url',
# default='',
# help='The HTTP URL used to connect to CA server')
parser
.
add_argument
(
'--trusted_host'
,
parser
.
add_argument
(
'--trusted_host'
,
default
=
[],
default
=
[],
action
=
'append'
,
dest
=
'trusted_host_list'
,
action
=
'append'
,
dest
=
'trusted_host_list'
,
...
@@ -89,13 +91,16 @@ def start():
...
@@ -89,13 +91,16 @@ def start():
"""
"""
start certificate authority service
start certificate authority service
"""
"""
flask
.
config
.
Config
.
__getattr__
=
getConfig
options
=
parseArguments
()
options
=
parseArguments
()
if
not
options
.
ca_dir
:
if
not
options
.
ca_dir
:
options
.
ca_dir
=
os
.
getcwd
()
options
.
ca_dir
=
os
.
getcwd
()
else
:
else
:
options
.
ca_dir
=
os
.
path
.
abspath
(
options
.
ca_dir
)
options
.
ca_dir
=
os
.
path
.
abspath
(
options
.
ca_dir
)
os
.
environ
[
'CA_INSTANCE_PATH'
]
=
options
.
ca_dir
from
slapos.certificate_authority.web.start_web
import
app
,
db
,
init_app
flask
.
config
.
Config
.
__getattr__
=
getConfig
if
not
options
.
config_file
:
if
not
options
.
config_file
:
options
.
config_file
=
os
.
path
.
join
(
options
.
ca_dir
,
'openssl.cnf'
)
options
.
config_file
=
os
.
path
.
join
(
options
.
ca_dir
,
'openssl.cnf'
)
if
not
options
.
db_file
:
if
not
options
.
db_file
:
...
@@ -116,11 +121,15 @@ def start():
...
@@ -116,11 +121,15 @@ def start():
os
.
chdir
(
options
.
ca_dir
)
os
.
chdir
(
options
.
ca_dir
)
logger
=
getLogger
(
options
.
debug
,
options
.
log_file
)
logger
=
getLogger
(
options
.
debug
,
options
.
log_file
)
app
.
logger
.
addHandler
(
logger
)
ca
=
CertificateAuthority
(
options
.
openssl_bin
,
ca
=
CertificateAuthority
(
options
.
openssl_bin
,
openssl_configuration
=
options
.
config_file
,
certificate
=
options
.
cert_file
,
openssl_configuration
=
options
.
config_file
,
certificate
=
options
.
cert_file
,
key
=
options
.
key_file
,
crl
=
options
.
crl_file
,
ca_directory
=
options
.
ca_dir
)
key
=
options
.
key_file
,
crl
=
options
.
crl_file
,
ca_directory
=
options
.
ca_dir
)
app
.
config
.
from_object
(
'slapos.certificate_authority.web.settings'
)
if
options
.
debug
:
app
.
config
.
from_object
(
'slapos.certificate_authority.web.settings.Development'
)
else
:
app
.
config
.
from_object
(
'slapos.certificate_authority.web.settings.Production'
)
app
.
config
.
update
(
app
.
config
.
update
(
ca_dir
=
options
.
ca_dir
,
ca_dir
=
options
.
ca_dir
,
trusted_host_list
=
options
.
trusted_host_list
,
trusted_host_list
=
options
.
trusted_host_list
,
...
@@ -134,8 +143,12 @@ def start():
...
@@ -134,8 +143,12 @@ def start():
SQLALCHEMY_DATABASE_URI
=
'sqlite:///%s'
%
options
.
db_file
,
SQLALCHEMY_DATABASE_URI
=
'sqlite:///%s'
%
options
.
db_file
,
ca
=
ca
,
ca
=
ca
,
log_file
=
options
.
log_file
,
log_file
=
options
.
log_file
,
# base_url=(options.external_url or 'http://%s:%s' % (options.host, options.port))
)
)
if
os
.
path
.
exists
(
os
.
path
.
join
(
options
.
ca_dir
,
'local.setting.py'
)):
app
.
config
.
from_pyfile
(
'local.setting.py'
,
silent
=
True
)
for
key
in
[
'csr'
,
'req'
,
'cert'
,
'crl'
,
'key'
,
'newcert'
]:
for
key
in
[
'csr'
,
'req'
,
'cert'
,
'crl'
,
'key'
,
'newcert'
]:
try
:
try
:
path
=
app
.
config
[
'%s_dir'
%
key
]
path
=
app
.
config
[
'%s_dir'
%
key
]
...
@@ -154,7 +167,6 @@ def start():
...
@@ -154,7 +167,6 @@ def start():
# Initialize Flask extensions
# Initialize Flask extensions
init_app
()
init_app
()
app
.
logger
.
addHandler
(
logger
)
app
.
logger
.
info
(
"Certificate Authority server started on http://%s:%s"
%
(
app
.
logger
.
info
(
"Certificate Authority server started on http://%s:%s"
%
(
options
.
host
,
options
.
port
))
options
.
host
,
options
.
port
))
app
.
run
(
app
.
run
(
...
...
slapos/certificate_authority/web/models.py
View file @
325666f9
...
@@ -49,22 +49,7 @@ class Certificate(db.Model):
...
@@ -49,22 +49,7 @@ class Certificate(db.Model):
def
__repr__
(
self
):
def
__repr__
(
self
):
return
'<CertificateMap %r>'
%
(
self
.
serial
)
return
'<CertificateMap %r>'
%
(
self
.
serial
)
class
Revoke
(
db
.
Model
):
class
Revocation
(
db
.
Model
):
"""
This table contains information about certificate revocation
"""
__tablename__
=
'revoke'
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
comment
=
db
.
Column
(
db
.
Text
())
serial
=
db
.
Column
(
db
.
String
(
50
),
unique
=
True
)
revoke_date
=
db
.
Column
(
db
.
DateTime
)
# link to revoke request if was requested by users
revoke_request_id
=
db
.
Column
(
db
.
Integer
,
server_default
=
''
)
def
__repr__
(
self
):
return
'<CertificateMap %r>'
%
(
self
.
serial
)
class
RevokeRequest
(
db
.
Model
):
"""
"""
This table store certificate revocation request from users
This table store certificate revocation request from users
"""
"""
...
...
slapos/certificate_authority/web/settings.py
View file @
325666f9
import
os
import
os
# Application settings
APP_NAME
=
"Certificate Authority web app"
class
BaseConfig
(
object
):
# DO NOT use "DEBUG = True" in production environments
# Application settings
DEBUG
=
True
APP_NAME
=
"Certificate Authority web app"
# DO NOT use Unsecure Secrets in production environments
# DO NOT use Unsecure Secrets in production environments
# Generate a safe one with:
# Generate a safe one with:
# python -c "import os; print repr(os.urandom(24));"
# python -c "import os; print repr(os.urandom(24));"
SECRET_KEY
=
'This is an UNSECURE Secret. CHANGE THIS for production environments.'
SECRET_KEY
=
'This is an UNSECURE Secret. CHANGE THIS for production environments.'
# SQLAlchemy settings
# SQLAlchemy settings
SQLALCHEMY_DATABASE_URI
=
'sqlite:///ca.db'
SQLALCHEMY_DATABASE_URI
=
'sqlite:///ca.db'
SQLALCHEMY_TRACK_MODIFICATIONS
=
False
SQLALCHEMY_TRACK_MODIFICATIONS
=
False
CSRF_ENABLED
=
True
CSRF_ENABLED
=
True
# Flask-Mail settings
# Flask-Mail settings
# For smtp.gmail.com to work, you MUST set "Allow less secure apps" to ON in Google Accounts.
# For smtp.gmail.com to work, you MUST set "Allow less secure apps" to ON in Google Accounts.
# Change it in https://myaccount.google.com/security#connectedapps (near the bottom).
# Change it in https://myaccount.google.com/security#connectedapps (near the bottom).
MAIL_SERVER
=
'smtp.gmail.com'
MAIL_SERVER
=
'smtp.gmail.com'
MAIL_PORT
=
587
MAIL_PORT
=
587
MAIL_USE_SSL
=
False
MAIL_USE_SSL
=
False
MAIL_USE_TLS
=
True
MAIL_USE_TLS
=
True
MAIL_USERNAME
=
'yourname@gmail.com'
MAIL_USERNAME
=
'yourname@gmail.com'
MAIL_PASSWORD
=
'password'
MAIL_PASSWORD
=
'password'
MAIL_DEFAULT_SENDER
=
'"Your Name" <yourname@gmail.com>'
MAIL_DEFAULT_SENDER
=
'"Your Name" <yourname@gmail.com>'
# Used by email templates
# Used by email templates
USER_APP_NAME
=
"Certificate Authority"
USER_APP_NAME
=
"Certificate Authority"
# Internal application
# Internal view application
USER_AFTER_LOGIN_ENDPOINT
=
''
USER_AFTER_LOGIN_ENDPOINT
=
''
USER_AFTER_LOGOUT_ENDPOINT
=
''
USER_AFTER_LOGOUT_ENDPOINT
=
''
USER_ENABLE_USERNAME
=
True
USER_ENABLE_USERNAME
=
True
USER_ENABLE_EMAIL
=
False
USER_ENABLE_EMAIL
=
False
USER_ENABLE_REGISTRATION
=
False
USER_ENABLE_REGISTRATION
=
False
USER_ENABLE_CHANGE_USERNAME
=
False
USER_ENABLE_CHANGE_USERNAME
=
False
\ No newline at end of file
# Allowed digest for signature
CA_DIGEST_LIST
=
[
'sha256'
,
'sha384'
,
'sha512'
]
class
Development
(
BaseConfig
):
DEBUG
=
True
TESTING
=
True
class
Production
(
BaseConfig
):
# DO NOT use "DEBUG = True" in production environments
DEBUG
=
False
TESTING
=
False
\ No newline at end of file
slapos/certificate_authority/web/start_web.py
View file @
325666f9
...
@@ -2,9 +2,16 @@ from flask_sqlalchemy import SQLAlchemy
...
@@ -2,9 +2,16 @@ from flask_sqlalchemy import SQLAlchemy
from
flask_user
import
UserManager
,
SQLAlchemyAdapter
from
flask_user
import
UserManager
,
SQLAlchemyAdapter
from
flask
import
Flask
from
flask
import
Flask
from
flask_mail
import
Mail
from
flask_mail
import
Mail
from
slapos.certificate_authority.web.settings
import
BaseConfig
import
os
import
os
app
=
Flask
(
__name__
)
# CA_INSTANCE_PATH is the base directory of application, send to environ
app
=
Flask
(
__name__
,
instance_path
=
config_name
=
os
.
getenv
(
'CA_INSTANCE_PATH'
,
os
.
getcwd
()),
instance_relative_config
=
True
)
# Use default value so SQLALCHEMY will not warn because there is not db_uri
app
.
config
[
'SQLALCHEMY_DATABASE_URI'
]
=
BaseConfig
.
SQLALCHEMY_DATABASE_URI
db
=
SQLAlchemy
(
app
)
db
=
SQLAlchemy
(
app
)
def
init_app
():
def
init_app
():
...
...
slapos/certificate_authority/web/tools.py
→
slapos/certificate_authority/web/tools
/certificate
.py
View file @
325666f9
...
@@ -5,45 +5,20 @@ import time
...
@@ -5,45 +5,20 @@ import time
import
urllib
import
urllib
from
datetime
import
datetime
from
datetime
import
datetime
from
slapos.certificate_authority.web.start_web
import
app
,
db
from
slapos.certificate_authority.web.start_web
import
app
,
db
from
slapos.certificate_authority.web.models
import
(
User
,
Certificate
,
from
slapos.certificate_authority.web.models
import
(
Certificate
,
RevokeRequest
,
Revoke
,
CERT_STATUS_VALIDATED
,
CERT_STATUS_REVOKED
,
RevokeRequest
,
Revoke
,
CERT_STATUS_VALIDATED
,
CERT_STATUS_REVOKED
,
CERT_STATUS_PENDING
,
CERT_STATUS_REJECTED
)
CERT_STATUS_PENDING
,
CERT_STATUS_REJECTED
)
from
slapos.certificate_authority.certificate_authority
import
CertificateBase
def
find_or_create_user
(
first_name
,
last_name
,
email
,
username
,
password
):
class
CertificateTools
:
""" Find existing user or create new user """
user
=
User
.
query
.
filter
(
User
.
username
==
username
).
first
()
def
signCertificate
(
self
,
req_file
,
cert_id
):
if
not
user
:
user
=
User
(
email
=
email
,
first_name
=
first_name
,
last_name
=
last_name
,
username
=
username
,
password
=
app
.
user_manager
.
hash_password
(
password
),
active
=
True
,
confirmed_at
=
datetime
.
utcnow
()
)
db
.
session
.
add
(
user
)
db
.
session
.
commit
()
return
user
def
find_user
(
username
):
return
User
.
query
.
filter
(
User
.
username
==
username
).
first
()
def
get_string_num
(
number
):
if
number
<
10
:
return
'0%s'
%
number
return
str
(
number
)
class
CertificateTools
(
object
):
def
signCertificate
(
self
,
cert_id
,
req_file
):
"""
"""
Sign a certificate, cert_id is the name used by the user to download the cert
Sign a certificate, cert_id is the name used by the user to download the cert
"""
"""
csr_dest
=
os
.
path
.
join
(
app
.
config
.
csr_dir
,
'%s.csr.pem'
%
cert_id
)
csr_dest
=
os
.
path
.
join
(
app
.
config
.
csr_dir
,
'%s.csr.pem'
%
cert_id
)
cert_name
=
'%s.cert.pem'
%
cert_id
try
:
try
:
# Avoid signing two certificate at the same time (for unique serial)
# Avoid signing two certificate at the same time (for unique serial)
app
.
config
.
ca
.
_lock
()
app
.
config
.
ca
.
_lock
()
...
@@ -58,7 +33,7 @@ class CertificateTools(object):
...
@@ -58,7 +33,7 @@ class CertificateTools(object):
app
.
config
.
ca
.
signCertificateRequest
(
req_file
,
output
)
app
.
config
.
ca
.
signCertificateRequest
(
req_file
,
output
)
cert
=
app
.
config
.
ca
.
freadX509
(
output
)
cert
=
app
.
config
.
ca
.
freadX509
(
output
)
cert_db
=
Certificate
(
cert_db
=
Certificate
(
name
=
'%s.cert.pem'
%
cert_id
,
name
=
cert_name
,
serial
=
next_serial
,
serial
=
next_serial
,
filename
=
'%s.cert.pem'
%
next_serial
,
filename
=
'%s.cert.pem'
%
next_serial
,
common_name
=
cert
.
get_subject
().
CN
,
common_name
=
cert
.
get_subject
().
CN
,
...
@@ -89,17 +64,17 @@ class CertificateTools(object):
...
@@ -89,17 +64,17 @@ class CertificateTools(object):
finally
:
finally
:
app
.
config
.
ca
.
_unlock
()
app
.
config
.
ca
.
_unlock
()
def
addRevokeRequest
(
self
,
serial
,
hash_name
,
message
):
return
cert_name
cert_path
=
os
.
path
.
join
(
app
.
config
.
cert_dir
,
'%s.cert.pem'
%
serial
)
if
not
os
.
path
.
exists
(
cert_path
):
def
addRevokeRequest
(
self
,
cert
,
message
):
# This check is fast but 'serial'.cert.pem should the the cert filename in db
x509
=
app
.
config
.
ca
.
readX509
(
cert
)
return
False
serial
=
self
.
getSerialToInt
(
x509
)
cert
=
Certificate
.
query
.
filter
(
cert
=
Certificate
.
query
.
filter
(
Certificate
.
status
==
CERT_STATUS_VALIDATED
Certificate
.
status
==
CERT_STATUS_VALIDATED
).
filter
(
Certificate
.
serial
==
serial
).
first
()
).
filter
(
Certificate
.
serial
==
get_string_num
(
serial
)
).
first
()
if
not
cert
or
cert
.
name
!=
'%s.cert.pem'
%
hash_name
:
if
not
cert
or
cert
.
name
!=
'%s.cert.pem'
%
hash_name
:
# This certificate not found or not match
# This certificate not found or not match
or was revoked
return
False
return
False
# Create Request
# Create Request
...
@@ -161,8 +136,8 @@ class CertificateTools(object):
...
@@ -161,8 +136,8 @@ class CertificateTools(object):
return
""
return
""
def
getCertificateList
(
self
,
with_cacerts
=
True
):
def
getCertificateList
(
self
,
with_cacerts
=
True
):
ca_cert
=
app
.
config
.
ca
.
freadX509
(
app
.
config
.
ca
.
certificate
)
if
with_cacerts
:
if
with_cacerts
:
ca_cert
=
app
.
config
.
ca
.
freadX509
(
app
.
config
.
ca
.
certificate
)
data_list
=
[
data_list
=
[
{
{
'index'
:
1
,
'index'
:
1
,
...
@@ -170,6 +145,7 @@ class CertificateTools(object):
...
@@ -170,6 +145,7 @@ class CertificateTools(object):
'name'
:
os
.
path
.
basename
(
app
.
config
.
ca
.
certificate
),
'name'
:
os
.
path
.
basename
(
app
.
config
.
ca
.
certificate
),
'cn'
:
ca_cert
.
get_subject
().
CN
,
'cn'
:
ca_cert
.
get_subject
().
CN
,
'expiration_date'
:
datetime
.
strptime
(
ca_cert
.
get_notAfter
(),
"%Y%m%d%H%M%SZ"
),
'expiration_date'
:
datetime
.
strptime
(
ca_cert
.
get_notAfter
(),
"%Y%m%d%H%M%SZ"
),
'start_date'
:
datetime
.
strptime
(
ca_cert
.
get_notBefore
(),
"%Y%m%d%H%M%SZ"
)
},
},
{
{
'index'
:
2
,
'index'
:
2
,
...
@@ -177,6 +153,7 @@ class CertificateTools(object):
...
@@ -177,6 +153,7 @@ class CertificateTools(object):
'name'
:
os
.
path
.
basename
(
app
.
config
.
ca
.
ca_crl
),
'name'
:
os
.
path
.
basename
(
app
.
config
.
ca
.
ca_crl
),
'cn'
:
"Certificate Revocation List"
,
'cn'
:
"Certificate Revocation List"
,
'expiration_date'
:
'---'
,
'expiration_date'
:
'---'
,
'start_date'
:
'---'
,
}
}
]
]
index
=
3
index
=
3
...
...
slapos/certificate_authority/web/tools/error.py
0 → 100644
View file @
325666f9
import
os
class
Error
():
MISSING_PARAM
=
{
code
:
1
,
name
:
"MissingParameter"
,
message
:
"Parameter(s) required is missing or empty."
}
CSR_FORMAT
=
{
code
:
2
,
name
:
"FileFormat"
,
message
:
"Not a valid PEM certificate signing request"
}
CSR_INVALID_CN
=
{
code
:
3
,
name
:
"CertificateSigningRequestContent"
,
message
:
"Request does not contain a Common Name"
}
CERT_FORMAT
=
{
code
:
4
,
name
:
"FileFormat"
,
message
:
"Not a valid PEM certificate"
}
SIGNATURE_VERIFICATION
=
{
code
:
5
,
name
:
"SignatureMismatch"
,
message
:
"Signature verification failed. Request was not signed with the correct key"
}
BAD_SIGNATURE_DIGEST
=
{
code
:
6
,
name
:
"SignatureMismatch"
,
message
:
"Hash algorithm not supported"
}
CSR_CONTENT_MISMATCH
=
{
code
:
7
,
name
:
"CertificateSigningRequestContent"
,
message
:
"Request content does not match replaced certificate"
}
JSON_FORMAT
=
{
code
:
8
,
name
:
"JsonFormat"
,
message
:
"Not a valid Json content submitted"
}
PAYLOAD_CONTENT
=
{
code
:
9
,
name
:
"PayloadContentInvalid"
,
message
:
"Submitted payload parameter is not valid"
}
INVALID_DIGEST
=
{
code
:
4
,
name
:
"IvalidORNotAllowedDigest"
,
message
:
"The Digest submitted is not accepted by CA or is invalid"
}
slapos/certificate_authority/web/tools/tools.py
0 → 100644
View file @
325666f9
# -*- coding: utf-8 -*-
import
os
def
get_string_num
(
number
):
if
number
<
10
:
return
'0%s'
%
number
return
str
(
number
)
\ No newline at end of file
slapos/certificate_authority/web/tools/users.py
0 → 100644
View file @
325666f9
# -*- coding: utf-8 -*-
import
os
from
datetime
import
datetime
from
slapos.certificate_authority.web.start_web
import
app
,
db
from
slapos.certificate_authority.web.models
import
User
def
check_authentication
(
username
,
password
):
user
=
self
.
find_user
(
username
):
if
user
:
return
app
.
user_manager
.
hash_password
(
password
)
==
user
.
password
else
:
return
False
def
find_or_create_user
(
first_name
,
last_name
,
email
,
username
,
password
):
""" Find existing user or create new user """
user
=
User
.
query
.
filter
(
User
.
username
==
username
).
first
()
if
not
user
:
user
=
User
(
email
=
email
,
first_name
=
first_name
,
last_name
=
last_name
,
username
=
username
,
password
=
app
.
user_manager
.
hash_password
(
password
),
active
=
True
,
confirmed_at
=
datetime
.
utcnow
()
)
db
.
session
.
add
(
user
)
db
.
session
.
commit
()
return
user
def
find_user
(
username
):
return
User
.
query
.
filter
(
User
.
username
==
username
).
first
()
\ No newline at end of file
slapos/certificate_authority/web/views.py
View file @
325666f9
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment