Add totpcgi role.
This commit is contained in:
parent
07e24f8448
commit
6af7a7b8a7
20 changed files with 1174 additions and 0 deletions
|
@ -41,6 +41,7 @@
|
||||||
- rsyncd
|
- rsyncd
|
||||||
- fas_server
|
- fas_server
|
||||||
- sudo
|
- sudo
|
||||||
|
- totpcgi
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- include: "{{ tasks }}/yumrepos.yml"
|
- include: "{{ tasks }}/yumrepos.yml"
|
||||||
|
|
26
roles/totpcgi/files/html/error.html
Normal file
26
roles/totpcgi/files/html/error.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>Fedora Project Google Authenticator provisioning - Error</title>
|
||||||
|
<link href="${css_root}provisioning.css" rel="stylesheet" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="mainwindow" class="status">
|
||||||
|
<div id="title">Fedora Project - Error</div>
|
||||||
|
<div id="mainarea">
|
||||||
|
<p class="error">
|
||||||
|
$errormsg
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You can <a href="$action_url">try again</a> or you can contact
|
||||||
|
the Fedora Infrastructure team at <a href="mailto:admin@fedoraproject.org">admin@fedoraproject.org</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="copyright">
|
||||||
|
© 2012 Red Hat, Inc. and others.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
49
roles/totpcgi/files/html/login.html
Normal file
49
roles/totpcgi/files/html/login.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>Fedora Project Google Authenticator provisioning</title>
|
||||||
|
<link href="${css_root}provisioning.css" rel="stylesheet" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="mainwindow" class="login">
|
||||||
|
<div id="title">Fedora Project</div>
|
||||||
|
<div id="mainarea">
|
||||||
|
<div class="message">
|
||||||
|
Please log in to obtain your new Google Authenticator
|
||||||
|
secret.
|
||||||
|
</div>
|
||||||
|
<form id="login_form" name="login_form" action="$action_url" method="post">
|
||||||
|
<table border="0" align="center">
|
||||||
|
<tr>
|
||||||
|
<td class="fieldname">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="username" name="username" class="inputfield"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="fieldname">
|
||||||
|
<label for="pincode">Pincode:</label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" id="pincode" name="pincode" class="inputfield"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="fieldname"></td>
|
||||||
|
<td>
|
||||||
|
<input type="submit" value="Submit »"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="copyright">
|
||||||
|
© 2012 Red Hat, Inc. and others.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
53
roles/totpcgi/files/html/totp.html
Normal file
53
roles/totpcgi/files/html/totp.html
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>Fedora Project Google Authenticator Provisioning</title>
|
||||||
|
<link href="${css_root}provisioning.css" rel="stylesheet" type="text/css" />
|
||||||
|
<link href="${css_root}provisioning-print.css" rel="stylesheet" media="print" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="mainwindow" class="totp">
|
||||||
|
<div id="title">Fedora Project Google Authenticator Provisioning</div>
|
||||||
|
<div id="mainarea">
|
||||||
|
<div id="qrcode">
|
||||||
|
$qrcode_embed
|
||||||
|
</div>
|
||||||
|
<div id="explanation">
|
||||||
|
<p>
|
||||||
|
Your new Google Authenticator token has been issued.
|
||||||
|
To import this token into your device, simply go to your
|
||||||
|
Google Authenticator app, select the option to add an
|
||||||
|
account, and then select "Scan Barcode". Point the camera
|
||||||
|
at the QR Barcode displayed next to this message. Google
|
||||||
|
Authenticator will then import your new token into the
|
||||||
|
device. It should be ready to use immediately.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="scratch_tokens">
|
||||||
|
<p>
|
||||||
|
If the administrator permitted the use of scratch tokens,
|
||||||
|
you should see them listed below. If you lose access to
|
||||||
|
your Google Authenticator device, you should be able to
|
||||||
|
use one of these tokens to gain emergency access to your
|
||||||
|
account. Please write them down.
|
||||||
|
</p>
|
||||||
|
<div id="scratch_tokens_list">
|
||||||
|
$scratch_tokens
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="support_requests">
|
||||||
|
<p>
|
||||||
|
If you require any help with your Google Authenticator
|
||||||
|
token or experience any difficulty importing it into
|
||||||
|
your mobile device, please email
|
||||||
|
<a href="mailto:admin@fedoraproject.org">admin@fedoraproject.org</a>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="copyright">
|
||||||
|
© 2012 Red Hat, Inc. and others.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
189
roles/totpcgi/files/index.cgi
Normal file
189
roles/totpcgi/files/index.cgi
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
#!/usr/bin/python -tt
|
||||||
|
##
|
||||||
|
# Copyright (C) 2012 by Konstantin Ryabitsev and contributors
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# 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, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
# 02111-1307, USA.
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import cgi
|
||||||
|
import syslog
|
||||||
|
import logging
|
||||||
|
import urllib2
|
||||||
|
|
||||||
|
import cgitb
|
||||||
|
cgitb.enable()
|
||||||
|
|
||||||
|
import totpcgi
|
||||||
|
import totpcgi.backends
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
# blindly assume it's the config file
|
||||||
|
config_file = sys.argv[1]
|
||||||
|
else:
|
||||||
|
config_file = '/etc/totpcgi/totpcgi.conf'
|
||||||
|
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
from fedora.client import AuthError
|
||||||
|
from fedora.client.fasproxy import FasProxyClient
|
||||||
|
|
||||||
|
config = ConfigParser.RawConfigParser()
|
||||||
|
config.read(config_file)
|
||||||
|
|
||||||
|
require_pincode = config.getboolean('main', 'require_pincode')
|
||||||
|
success_string = config.get('main', 'success_string')
|
||||||
|
|
||||||
|
fas_url = config.get('main', 'fas_url')
|
||||||
|
try:
|
||||||
|
fas = FasProxyClient(fas_url)
|
||||||
|
except Exception, e:
|
||||||
|
syslog.syslog(syslog.LOG_CRIT, 'Problem connecting to fas %s' % e)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
backends = totpcgi.backends.Backends()
|
||||||
|
|
||||||
|
try:
|
||||||
|
backends.load_from_config(config)
|
||||||
|
except totpcgi.backends.BackendNotSupported, ex:
|
||||||
|
syslog.syslog(syslog.LOG_CRIT,
|
||||||
|
'Backend engine not supported: %s' % ex)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
syslog.openlog('totp.cgi', syslog.LOG_PID, syslog.LOG_AUTH)
|
||||||
|
|
||||||
|
### Begin custom Fedora Functions
|
||||||
|
|
||||||
|
def google_auth_fas_pincode_verify(user, pincode):
|
||||||
|
if not fas.verify_password(user, pincode):
|
||||||
|
raise totpcgi.UserPincodeError('User Password Error')
|
||||||
|
|
||||||
|
backends.pincode_backend.verify_user_pincode = google_auth_fas_pincode_verify
|
||||||
|
|
||||||
|
client_id = '1'
|
||||||
|
|
||||||
|
def parse_token(token):
|
||||||
|
if token > 44:
|
||||||
|
otp = token[-44:]
|
||||||
|
if otp.startswith('ccccc'):
|
||||||
|
return token[:-44], otp
|
||||||
|
|
||||||
|
# Not a password + yubikey
|
||||||
|
return False
|
||||||
|
|
||||||
|
class YubikeyAuthenticator(object):
|
||||||
|
auth_regex = re.compile('^status=(?P<rc>\w{2})')
|
||||||
|
def __init__(self, require_pincode=False):
|
||||||
|
self.require_pincode = require_pincode
|
||||||
|
|
||||||
|
def verify_user_token(self, user, token):
|
||||||
|
# Parse the token apart into a password and token
|
||||||
|
password, otp = parse_token(token)
|
||||||
|
|
||||||
|
# Verify token against yubikey server
|
||||||
|
server_prefix = 'http://localhost/yk-val/verify?id='
|
||||||
|
server_url = server_prefix + client_id + "&otp=" + otp
|
||||||
|
|
||||||
|
fh = urllib2.urlopen(server_url)
|
||||||
|
|
||||||
|
for line in fh:
|
||||||
|
match = self.auth_regex.search(line.strip('\n'))
|
||||||
|
if match:
|
||||||
|
if match.group('rc') == 'OK':
|
||||||
|
# Yubikey token is valid
|
||||||
|
break
|
||||||
|
raise totpcgi.VerifyFailed(line.split('=')[1])
|
||||||
|
else:
|
||||||
|
raise totpcgi.VerifyFailed('yk-val returned malformed response')
|
||||||
|
|
||||||
|
|
||||||
|
# Verify that the yubikey token belongs to the user
|
||||||
|
# As a side effect, verify the password is good as well
|
||||||
|
# if the user+password are wrong, this will raise a fedora.client.AuthError
|
||||||
|
try:
|
||||||
|
response = fas.send_request('/config/list/%s/yubikey' % user,
|
||||||
|
auth_params={'username': user, 'password': password})
|
||||||
|
except AuthError, e:
|
||||||
|
raise totpcgi.VerifyFailed('User Password Error: %s' % e)
|
||||||
|
if not response[1].configs.prefix or not response[1].configs.enabled:
|
||||||
|
raise totpcgi.VerifyFailed('Yubikey OTP unconfigured')
|
||||||
|
elif len(response[1].configs.prefix) != 12:
|
||||||
|
raise totpcgi.VerifyFailed('Invalid Yubikey OTP prefix')
|
||||||
|
if not otp.startswith(response[1].configs.prefix):
|
||||||
|
raise totpcgi.VerifyFailed('Unauthorized/Invalid OTP')
|
||||||
|
|
||||||
|
# Okay, everything passed
|
||||||
|
return 'Valid yubikey returned'
|
||||||
|
|
||||||
|
|
||||||
|
### End of custom Fedora Functions
|
||||||
|
|
||||||
|
def bad_request(why):
|
||||||
|
output = 'ERR\n' + why + '\n'
|
||||||
|
sys.stdout.write('Status: 400 BAD REQUEST\n')
|
||||||
|
sys.stdout.write('Content-type: text/plain\n')
|
||||||
|
sys.stdout.write('Content-Length: %s\n' % len(output))
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
|
||||||
|
sys.stdout.write(output)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def cgimain():
|
||||||
|
form = cgi.FieldStorage()
|
||||||
|
|
||||||
|
must_keys = ('user', 'token', 'mode')
|
||||||
|
|
||||||
|
for must_key in must_keys:
|
||||||
|
if must_key not in form:
|
||||||
|
bad_request("Missing field: %s" % must_key)
|
||||||
|
|
||||||
|
user = form.getfirst('user')
|
||||||
|
token = form.getfirst('token')
|
||||||
|
mode = form.getfirst('mode')
|
||||||
|
|
||||||
|
remote_host = os.environ['REMOTE_ADDR']
|
||||||
|
|
||||||
|
if mode != 'PAM_SM_AUTH':
|
||||||
|
bad_request('We only support PAM_SM_AUTH')
|
||||||
|
|
||||||
|
if parse_token(token):
|
||||||
|
ga = YubikeyAuthenticator(require_pincode)
|
||||||
|
else:
|
||||||
|
# totp/googleauth
|
||||||
|
ga = totpcgi.GoogleAuthenticator(backends, require_pincode)
|
||||||
|
|
||||||
|
try:
|
||||||
|
status = ga.verify_user_token(user, token)
|
||||||
|
except Exception, ex:
|
||||||
|
syslog.syslog(syslog.LOG_NOTICE,
|
||||||
|
'Failure: user=%s, mode=%s, host=%s, message=%s' % (user, mode,
|
||||||
|
remote_host, str(ex)))
|
||||||
|
bad_request(str(ex))
|
||||||
|
|
||||||
|
syslog.syslog(syslog.LOG_NOTICE,
|
||||||
|
'Success: user=%s, mode=%s, host=%s, message=%s' % (user, mode,
|
||||||
|
remote_host, status))
|
||||||
|
|
||||||
|
sys.stdout.write('Status: 200 OK\n')
|
||||||
|
sys.stdout.write('Content-type: text/plain\n')
|
||||||
|
sys.stdout.write('Content-Length: %s\n' % len(success_string))
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
|
||||||
|
sys.stdout.write(success_string)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cgimain()
|
21
roles/totpcgi/files/pam_url.conf
Normal file
21
roles/totpcgi/files/pam_url.conf
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
pam_url:
|
||||||
|
{
|
||||||
|
settings:
|
||||||
|
{
|
||||||
|
url = "https://fas-all.phx2.fedoraproject.org:8443/"; # URI to fetch
|
||||||
|
returncode = "OK"; # The remote script/cgi should return a 200 http code and this string as its only results
|
||||||
|
userfield = "user"; # userfield name to send
|
||||||
|
passwdfield = "token"; # passwdfield name to send
|
||||||
|
extradata = "&do=login"; # extradata to send
|
||||||
|
prompt = "Password+Token: "; # password prompt
|
||||||
|
};
|
||||||
|
|
||||||
|
ssl:
|
||||||
|
{
|
||||||
|
verify_peer = true; # Should we verify SSL ?
|
||||||
|
verify_host = true; # Should we verify the CN in the SSL cert?
|
||||||
|
client_cert = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side certificate
|
||||||
|
client_key = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side key (can be same file as above if a single cert)
|
||||||
|
ca_cert = "/etc/pki/tls/private/totpcgi-ca.cert";
|
||||||
|
};
|
||||||
|
};
|
21
roles/totpcgi/files/pam_url.conf.fakefas01
Normal file
21
roles/totpcgi/files/pam_url.conf.fakefas01
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
pam_url:
|
||||||
|
{
|
||||||
|
settings:
|
||||||
|
{
|
||||||
|
url = "https://fas-all.phx2.fedoraproject.org:8443/"; # URI to fetch
|
||||||
|
returncode = "OK"; # The remote script/cgi should return a 200 http code and this string as its only results
|
||||||
|
userfield = "user"; # userfield name to send
|
||||||
|
passwdfield = "token"; # passwdfield name to send
|
||||||
|
extradata = "&do=login"; # extradata to send
|
||||||
|
prompt = "Password+Token: "; # password prompt
|
||||||
|
};
|
||||||
|
|
||||||
|
ssl:
|
||||||
|
{
|
||||||
|
verify_peer = true; # Should we verify SSL ?
|
||||||
|
verify_host = true; # Should we verify the CN in the SSL cert?
|
||||||
|
client_cert = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side certificate
|
||||||
|
client_key = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side key (can be same file as above if a single cert)
|
||||||
|
ca_cert = "/etc/pki/tls/private/totpcgi-ca.cert";
|
||||||
|
};
|
||||||
|
};
|
21
roles/totpcgi/files/pam_url.conf.stg
Normal file
21
roles/totpcgi/files/pam_url.conf.stg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
pam_url:
|
||||||
|
{
|
||||||
|
settings:
|
||||||
|
{
|
||||||
|
url = "https://fas-all.stg.phx2.fedoraproject.org:8443/"; # URI to fetch
|
||||||
|
returncode = "OK"; # The remote script/cgi should return a 200 http code and this string as its only results
|
||||||
|
userfield = "user"; # userfield name to send
|
||||||
|
passwdfield = "token"; # passwdfield name to send
|
||||||
|
extradata = "&do=login"; # extradata to send
|
||||||
|
prompt = "Password+Token: "; # password prompt
|
||||||
|
};
|
||||||
|
|
||||||
|
ssl:
|
||||||
|
{
|
||||||
|
verify_peer = true; # Should we verify SSL ?
|
||||||
|
verify_host = true; # Should we verify the CN in the SSL cert?
|
||||||
|
client_cert = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side certificate
|
||||||
|
client_key = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side key (can be same file as above if a single cert)
|
||||||
|
ca_cert = "/etc/pki/tls/private/totpcgi-ca.cert";
|
||||||
|
};
|
||||||
|
};
|
21
roles/totpcgi/files/pam_url.conf.vpn
Normal file
21
roles/totpcgi/files/pam_url.conf.vpn
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
pam_url:
|
||||||
|
{
|
||||||
|
settings:
|
||||||
|
{
|
||||||
|
url = "https://fas-all.vpn.fedoraproject.org:8443/"; # URI to fetch
|
||||||
|
returncode = "OK"; # The remote script/cgi should return a 200 http code and this string as its only results
|
||||||
|
userfield = "user"; # userfield name to send
|
||||||
|
passwdfield = "token"; # passwdfield name to send
|
||||||
|
extradata = "&do=login"; # extradata to send
|
||||||
|
prompt = "Password+Token: "; # password prompt
|
||||||
|
};
|
||||||
|
|
||||||
|
ssl:
|
||||||
|
{
|
||||||
|
verify_peer = true; # Should we verify SSL ?
|
||||||
|
verify_host = true; # Should we verify the CN in the SSL cert?
|
||||||
|
client_cert = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side certificate
|
||||||
|
client_key = "/etc/pki/tls/private/totpcgi.pem"; # file to use as client-side key (can be same file as above if a single cert)
|
||||||
|
ca_cert = "/etc/pki/tls/private/totpcgi-ca.cert";
|
||||||
|
};
|
||||||
|
};
|
265
roles/totpcgi/files/provisioning.cgi
Normal file
265
roles/totpcgi/files/provisioning.cgi
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
#!/usr/bin/python -tt
|
||||||
|
##
|
||||||
|
# Copyright (C) 2012 by Konstantin Ryabitsev and contributors
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# 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, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
# 02111-1307, USA.
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import cgi
|
||||||
|
import syslog
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import cgitb
|
||||||
|
cgitb.enable()
|
||||||
|
|
||||||
|
import totpcgi
|
||||||
|
import totpcgi.backends
|
||||||
|
import totpcgi.utils
|
||||||
|
|
||||||
|
import qrcode
|
||||||
|
from qrcode.image import svg
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
from string import Template
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
# blindly assume it's the config file
|
||||||
|
config_file = sys.argv[1]
|
||||||
|
else:
|
||||||
|
config_file = '/etc/totpcgi/provisioning.conf'
|
||||||
|
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
config = ConfigParser.RawConfigParser()
|
||||||
|
config.read(config_file)
|
||||||
|
|
||||||
|
backends = totpcgi.backends.Backends()
|
||||||
|
|
||||||
|
try:
|
||||||
|
backends.load_from_config(config)
|
||||||
|
except totpcgi.backends.BackendNotSupported, ex:
|
||||||
|
syslog.syslog(syslog.LOG_CRIT,
|
||||||
|
'Backend engine not supported: %s' % ex)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
syslog.openlog('provisioning.cgi', syslog.LOG_PID, syslog.LOG_AUTH)
|
||||||
|
|
||||||
|
def bad_request(config, why):
|
||||||
|
templates_dir = config.get('secret', 'templates_dir')
|
||||||
|
fh = open(os.path.join(templates_dir, 'error.html'))
|
||||||
|
tpt = Template(fh.read())
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
vals = {
|
||||||
|
'action_url': config.get('secret', 'action_url'),
|
||||||
|
'css_root': config.get('secret', 'css_root'),
|
||||||
|
'errormsg': cgi.escape(why)
|
||||||
|
}
|
||||||
|
|
||||||
|
out = tpt.safe_substitute(vals)
|
||||||
|
|
||||||
|
sys.stdout.write('Status: 400 BAD REQUEST\n')
|
||||||
|
sys.stdout.write('Content-type: text/html\n')
|
||||||
|
sys.stdout.write('Content-Length: %s\n' % len(out))
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
|
||||||
|
sys.stdout.write(out)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def show_qr_code(data):
|
||||||
|
qr = qrcode.QRCode(
|
||||||
|
version=1,
|
||||||
|
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
||||||
|
box_size=5,
|
||||||
|
border=4)
|
||||||
|
|
||||||
|
qr.add_data(data)
|
||||||
|
qr.make(fit=True)
|
||||||
|
|
||||||
|
img = qr.make_image()
|
||||||
|
|
||||||
|
fh = StringIO()
|
||||||
|
img.save(fh)
|
||||||
|
out = fh.getvalue()
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
sys.stdout.write('Status: 200 OK\n')
|
||||||
|
sys.stdout.write('Content-type: image/png\n')
|
||||||
|
sys.stdout.write('Content-Length: %s\n' % len(out))
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
|
||||||
|
sys.stdout.write(out)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def show_login_form(config):
|
||||||
|
templates_dir = config.get('secret', 'templates_dir')
|
||||||
|
fh = open(os.path.join(templates_dir, 'login.html'))
|
||||||
|
tpt = Template(fh.read())
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
vals = {
|
||||||
|
'action_url': config.get('secret', 'action_url'),
|
||||||
|
'css_root': config.get('secret', 'css_root')
|
||||||
|
}
|
||||||
|
|
||||||
|
out = tpt.safe_substitute(vals)
|
||||||
|
|
||||||
|
sys.stdout.write('Status: 200 OK\n')
|
||||||
|
sys.stdout.write('Content-type: text/html\n')
|
||||||
|
sys.stdout.write('Content-Length: %s\n' % len(out))
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
|
||||||
|
sys.stdout.write(out)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def show_totp_page(config, user, gaus):
|
||||||
|
# generate provisioning URI
|
||||||
|
tpt = Template(config.get('secret', 'totp_user_mask'))
|
||||||
|
totp_user = tpt.safe_substitute(username=user)
|
||||||
|
totp_qr_uri = gaus.totp.provisioning_uri(totp_user)
|
||||||
|
|
||||||
|
action_url = config.get('secret', 'action_url')
|
||||||
|
|
||||||
|
qrcode_embed = '<img src="%s?qrcode=%s"/>' % (action_url, totp_qr_uri)
|
||||||
|
|
||||||
|
templates_dir = config.get('secret', 'templates_dir')
|
||||||
|
fh = open(os.path.join(templates_dir, 'totp.html'))
|
||||||
|
tpt = Template(fh.read())
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
if gaus.scratch_tokens:
|
||||||
|
scratch_tokens = '<br/>'.join(gaus.scratch_tokens)
|
||||||
|
else:
|
||||||
|
scratch_tokens = ' '
|
||||||
|
|
||||||
|
vals = {
|
||||||
|
'action_url': action_url,
|
||||||
|
'css_root': config.get('secret', 'css_root'),
|
||||||
|
'qrcode_embed': qrcode_embed,
|
||||||
|
'scratch_tokens': scratch_tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
out = tpt.safe_substitute(vals)
|
||||||
|
|
||||||
|
sys.stdout.write('Status: 200 OK\n')
|
||||||
|
sys.stdout.write('Content-type: text/html\n')
|
||||||
|
sys.stdout.write('Content-Length: %s\n' % len(out))
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
|
||||||
|
sys.stdout.write(out)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def generate_secret(config):
|
||||||
|
encrypt_secret = config.getboolean('secret', 'encrypt_secret')
|
||||||
|
window_size = config.getint('secret', 'window_size')
|
||||||
|
rate_limit = config.get('secret', 'rate_limit')
|
||||||
|
|
||||||
|
# scratch tokens don't make any sense with encrypted secret
|
||||||
|
if not encrypt_secret:
|
||||||
|
scratch_tokens_n = config.getint('secret', 'scratch_tokens_n')
|
||||||
|
else:
|
||||||
|
scratch_tokens_n = 0
|
||||||
|
|
||||||
|
(times, secs) = rate_limit.split(',')
|
||||||
|
rate_limit = (int(times), int(secs))
|
||||||
|
|
||||||
|
gaus = totpcgi.utils.generate_secret(rate_limit, window_size,
|
||||||
|
scratch_tokens_n)
|
||||||
|
|
||||||
|
return gaus
|
||||||
|
|
||||||
|
|
||||||
|
def cgimain():
|
||||||
|
form = cgi.FieldStorage()
|
||||||
|
|
||||||
|
if 'qrcode' in form:
|
||||||
|
#if os.environ['HTTP_REFERER'].find(os.environ['SERVER_NAME']) == -1:
|
||||||
|
# bad_request(config, 'Sorry, you failed the HTTP_REFERER check')
|
||||||
|
|
||||||
|
qrcode = form.getfirst('qrcode')
|
||||||
|
show_qr_code(qrcode)
|
||||||
|
|
||||||
|
remote_host = os.environ['REMOTE_ADDR']
|
||||||
|
|
||||||
|
try:
|
||||||
|
trust_http_auth = config.getboolean('secret', 'trust_http_auth')
|
||||||
|
except ConfigParser.NoOptionError:
|
||||||
|
trust_http_auth = False
|
||||||
|
|
||||||
|
if trust_http_auth and os.environ.has_key('REMOTE_USER'):
|
||||||
|
user = os.environ['REMOTE_USER']
|
||||||
|
pincode = None
|
||||||
|
|
||||||
|
syslog.syslog(syslog.LOG_NOTICE,
|
||||||
|
'Success (http-auth): user=%s, host=%s' % (user, remote_host))
|
||||||
|
|
||||||
|
else:
|
||||||
|
must_keys = ('username', 'pincode')
|
||||||
|
|
||||||
|
for must_key in must_keys:
|
||||||
|
if must_key not in form:
|
||||||
|
show_login_form(config)
|
||||||
|
|
||||||
|
user = form.getfirst('username')
|
||||||
|
pincode = form.getfirst('pincode')
|
||||||
|
|
||||||
|
# start by verifying the pincode
|
||||||
|
try:
|
||||||
|
backends.pincode_backend.verify_user_pincode(user, pincode)
|
||||||
|
except Exception, ex:
|
||||||
|
syslog.syslog(syslog.LOG_NOTICE,
|
||||||
|
'Failure: user=%s, host=%s, message=%s' % (user, remote_host,
|
||||||
|
str(ex)))
|
||||||
|
bad_request(config, str(ex))
|
||||||
|
|
||||||
|
# pincode verified
|
||||||
|
syslog.syslog(syslog.LOG_NOTICE,
|
||||||
|
'Success: user=%s, host=%s' % (user, remote_host))
|
||||||
|
|
||||||
|
# is there an existing secret for this user?
|
||||||
|
exists = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
backends.secret_backend.get_user_secret(user, pincode)
|
||||||
|
except totpcgi.UserNotFound:
|
||||||
|
# if we got it, then there isn't an existing secret in place
|
||||||
|
exists = False
|
||||||
|
|
||||||
|
if exists:
|
||||||
|
syslog.syslog(syslog.LOG_NOTICE,
|
||||||
|
'Secret exists: user=%s, host=%s' % (user, remote_host))
|
||||||
|
bad_request(config, 'Existing secret found. It must be removed first.')
|
||||||
|
|
||||||
|
# now generate the secret and store it
|
||||||
|
|
||||||
|
gaus = generate_secret(config)
|
||||||
|
|
||||||
|
# if we don't need to encrypt the secret, set pincode to None
|
||||||
|
encrypt_secret = config.getboolean('secret', 'encrypt_secret')
|
||||||
|
if not encrypt_secret:
|
||||||
|
pincode = None
|
||||||
|
|
||||||
|
backends.secret_backend.save_user_secret(user, gaus, pincode)
|
||||||
|
# purge all old state, as it's now obsolete
|
||||||
|
|
||||||
|
backends.state_backend.delete_user_state(user)
|
||||||
|
|
||||||
|
show_totp_page(config, user, gaus)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cgimain()
|
11
roles/totpcgi/files/sudo.pam
Normal file
11
roles/totpcgi/files/sudo.pam
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#%PAM-1.0
|
||||||
|
auth required pam_env.so
|
||||||
|
auth sufficient pam_url.so config=/etc/pam_url.conf
|
||||||
|
auth requisite pam_succeed_if.so uid >= 500 quiet
|
||||||
|
auth required pam_deny.so
|
||||||
|
|
||||||
|
auth include system-auth
|
||||||
|
account include system-auth
|
||||||
|
password include system-auth
|
||||||
|
session optional pam_keyinit.so revoke
|
||||||
|
session required pam_limits.so
|
6
roles/totpcgi/files/sudo.pam.dev.fedoraproject.org
Normal file
6
roles/totpcgi/files/sudo.pam.dev.fedoraproject.org
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#%PAM-1.0
|
||||||
|
auth include system-auth
|
||||||
|
account include system-auth
|
||||||
|
password include system-auth
|
||||||
|
session optional pam_keyinit.so revoke
|
||||||
|
session required pam_limits.so
|
6
roles/totpcgi/files/sudo.pam.qa.fedoraproject.org
Normal file
6
roles/totpcgi/files/sudo.pam.qa.fedoraproject.org
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#%PAM-1.0
|
||||||
|
auth include system-auth
|
||||||
|
account include system-auth
|
||||||
|
password include system-auth
|
||||||
|
session optional pam_keyinit.so revoke
|
||||||
|
session required pam_limits.so
|
34
roles/totpcgi/files/totpcgi-httpd.conf
Normal file
34
roles/totpcgi/files/totpcgi-httpd.conf
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
Listen 8443
|
||||||
|
<VirtualHost 10.5.126.30:8443 10.5.126.25:8443 10.5.126.26:8443>
|
||||||
|
# Load this module locally here.
|
||||||
|
LoadModule suexec_module modules/mod_suexec.so
|
||||||
|
|
||||||
|
ServerAdmin admin@fedoraproject.org
|
||||||
|
DocumentRoot /var/www/totpcgi
|
||||||
|
ServerName fas-all.phx2.fedoraproject.org:8443
|
||||||
|
ErrorLog /var/log/httpd/totpcgi-error.log
|
||||||
|
SuexecUserGroup totpcgi totpcgi
|
||||||
|
|
||||||
|
# Use this for totp.cgi
|
||||||
|
AddHandler cgi-script .cgi
|
||||||
|
DirectoryIndex index.cgi
|
||||||
|
|
||||||
|
# Or use this for totp.fcgi:
|
||||||
|
#AddHandler fcgid-script .fcgi
|
||||||
|
#DirectoryIndex index.fcgi
|
||||||
|
|
||||||
|
SSLEngine on
|
||||||
|
SSLCertificateFile /etc/pki/totpcgi/totpcgi-server.crt
|
||||||
|
SSLCertificateKeyFile /etc/pki/totpcgi/totpcgi-server.key
|
||||||
|
SSLCACertificateFile /etc/pki/totpcgi/totpcgi-ca.crt
|
||||||
|
|
||||||
|
SSLVerifyClient require
|
||||||
|
SSLVerifyDepth 10
|
||||||
|
|
||||||
|
CustomLog /var/log/httpd/totpcgi-ssl-request-log \
|
||||||
|
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
|
||||||
|
|
||||||
|
<Directory "/var/www/totpcgi">
|
||||||
|
Options ExecCGI
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
34
roles/totpcgi/files/totpcgi-httpd.conf.stg
Normal file
34
roles/totpcgi/files/totpcgi-httpd.conf.stg
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
Listen 8443
|
||||||
|
<VirtualHost *:8443>
|
||||||
|
# Load this module locally here.
|
||||||
|
LoadModule suexec_module modules/mod_suexec.so
|
||||||
|
|
||||||
|
ServerAdmin admin@fedoraproject.org
|
||||||
|
DocumentRoot /var/www/totpcgi
|
||||||
|
ServerName fas-all.stg.phx2.fedoraproject.org:8443
|
||||||
|
ErrorLog /var/log/httpd/totpcgi-error.log
|
||||||
|
SuexecUserGroup totpcgi totpcgi
|
||||||
|
|
||||||
|
# Use this for totp.cgi
|
||||||
|
AddHandler cgi-script .cgi
|
||||||
|
DirectoryIndex index.cgi
|
||||||
|
|
||||||
|
# Or use this for totp.fcgi:
|
||||||
|
#AddHandler fcgid-script .fcgi
|
||||||
|
#DirectoryIndex index.fcgi
|
||||||
|
|
||||||
|
SSLEngine on
|
||||||
|
SSLCertificateFile /etc/pki/totpcgi/totpcgi-server.crt
|
||||||
|
SSLCertificateKeyFile /etc/pki/totpcgi/totpcgi-server.key
|
||||||
|
SSLCACertificateFile /etc/pki/totpcgi/totpcgi-ca.crt
|
||||||
|
|
||||||
|
SSLVerifyClient require
|
||||||
|
SSLVerifyDepth 10
|
||||||
|
|
||||||
|
CustomLog /var/log/httpd/totpcgi-ssl-request-log \
|
||||||
|
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
|
||||||
|
|
||||||
|
<Directory "/var/www/totpcgi">
|
||||||
|
Options ExecCGI
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
33
roles/totpcgi/files/totpcgi-httpd.conf.vpn
Normal file
33
roles/totpcgi/files/totpcgi-httpd.conf.vpn
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<VirtualHost 192.168.1.38:8443 192.168.1.39:8443 192.168.1.49:8443>
|
||||||
|
# Load this module locally here.
|
||||||
|
LoadModule suexec_module modules/mod_suexec.so
|
||||||
|
|
||||||
|
ServerAdmin admin@fedoraproject.org
|
||||||
|
DocumentRoot /var/www/totpcgi
|
||||||
|
ServerName fas-all.vpn.fedoraproject.org:8443
|
||||||
|
ErrorLog /var/log/httpd/totpcgi-error.log
|
||||||
|
SuexecUserGroup totpcgi totpcgi
|
||||||
|
|
||||||
|
# Use this for totp.cgi
|
||||||
|
AddHandler cgi-script .cgi
|
||||||
|
DirectoryIndex index.cgi
|
||||||
|
|
||||||
|
# Or use this for totp.fcgi:
|
||||||
|
#AddHandler fcgid-script .fcgi
|
||||||
|
#DirectoryIndex index.fcgi
|
||||||
|
|
||||||
|
SSLEngine on
|
||||||
|
SSLCertificateFile /etc/pki/totpcgi/totpcgi-server-vpn.crt
|
||||||
|
SSLCertificateKeyFile /etc/pki/totpcgi/totpcgi-server-vpn.key
|
||||||
|
SSLCACertificateFile /etc/pki/totpcgi/totpcgi-ca.crt
|
||||||
|
|
||||||
|
SSLVerifyClient require
|
||||||
|
SSLVerifyDepth 10
|
||||||
|
|
||||||
|
CustomLog /var/log/httpd/totpcgi-ssl-request-log \
|
||||||
|
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
|
||||||
|
|
||||||
|
<Directory "/var/www/totpcgi">
|
||||||
|
Options ExecCGI
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
220
roles/totpcgi/tasks/main.yml
Normal file
220
roles/totpcgi/tasks/main.yml
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
- name: install needed packages
|
||||||
|
yum: pkg={{ item }} state=present
|
||||||
|
with_items:
|
||||||
|
- mod_auth_psql
|
||||||
|
- totpcgi
|
||||||
|
- totpcgi-selinux
|
||||||
|
- totpcgi-provisioning
|
||||||
|
- python-qrcode
|
||||||
|
- httpd
|
||||||
|
- mod_ssl
|
||||||
|
tags:
|
||||||
|
- packages
|
||||||
|
|
||||||
|
- name: add totpcgi user
|
||||||
|
user: name=totpcgi uid=430 state=present home=/var/lib/totpcgi createhome=yes system=yes
|
||||||
|
tags:
|
||||||
|
- config
|
||||||
|
|
||||||
|
- name: Install the cgi apache configuration files
|
||||||
|
template: >
|
||||||
|
src={{ item }}.j2 dest=/etc/httpd/conf.d/{{ item }}
|
||||||
|
owner=root group=root mode=0444
|
||||||
|
with_items:
|
||||||
|
- provisioning-httpd.conf
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
notify:
|
||||||
|
- restart apache
|
||||||
|
|
||||||
|
- name: create directories
|
||||||
|
file: path=/etc/{{ item.path }} state=directory owner=root group=totpcgi mode=750
|
||||||
|
with_items:
|
||||||
|
- pki/totpcgi
|
||||||
|
- totpcgi
|
||||||
|
- totpcgi/templates
|
||||||
|
- totpcgi/totp
|
||||||
|
|
||||||
|
- name: copy index file over
|
||||||
|
copy: >
|
||||||
|
src=html
|
||||||
|
dest=/etc/totpcgi/templates/html
|
||||||
|
owner=root
|
||||||
|
group=totpcgiprov
|
||||||
|
mode=0750
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
|
||||||
|
- name: copy index file over
|
||||||
|
copy: >
|
||||||
|
src=provisioning.cgi
|
||||||
|
dest=/var/www/totpcgi-provisioning/index.cgi
|
||||||
|
owner=totpcgiprov
|
||||||
|
group=totpcgiprov
|
||||||
|
mode=0550
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
|
||||||
|
- name: copy index file over
|
||||||
|
copy: >
|
||||||
|
src=index.cgi
|
||||||
|
dest=/var/www/totpcgi/index.cgi
|
||||||
|
owner=totpcgiprov
|
||||||
|
group=totpcgiprov
|
||||||
|
mode=0550
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
|
||||||
|
- name: copy totpcgi.conf file over
|
||||||
|
template: >
|
||||||
|
src=totpcgi.conf.j2
|
||||||
|
dest=/etc/totpcgi/totpcgi.conf
|
||||||
|
owner=root
|
||||||
|
group=totpcgiprov
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
|
||||||
|
# staging certs
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src={{ puppet_secure }}/2fa-certs/keys/fas-all.stg.phx2.fedoraproject.org.crt
|
||||||
|
dest=/etc/pki/totpcgi/totpcgi-server.crt
|
||||||
|
owner=root
|
||||||
|
group=totpcgi
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "staging"
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src={{ puppet_secure }}/fa-certs/keys/fas-all.stg.phx2.fedoraproject.org.key
|
||||||
|
dest=/etc/pki/totpcgi/totpcgi-server.key
|
||||||
|
owner=root
|
||||||
|
group=totpcgi
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "staging"
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src=totpcgi-httpd.conf.stg
|
||||||
|
dest=/etc/httpd/conf.d/totpcgi.conf
|
||||||
|
owner=root
|
||||||
|
group=root
|
||||||
|
mode=0444
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "staging"
|
||||||
|
|
||||||
|
# prod certs
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src={{ puppet_secure }}/2fa-certs/keys/fas-all.phx2.fedoraproject.org.crt
|
||||||
|
dest=/etc/pki/totpcgi/totpcgi-server.crt
|
||||||
|
owner=root
|
||||||
|
group=totpcgi
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "production"
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src={{ puppet_secure }}/fa-certs/keys/fas-all.phx2.fedoraproject.org.key
|
||||||
|
dest=/etc/pki/totpcgi/totpcgi-server.key
|
||||||
|
owner=root
|
||||||
|
group=totpcgi
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "production"
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src=totpcgi-httpd.conf
|
||||||
|
dest=/etc/httpd/conf.d/totpcgi.conf
|
||||||
|
owner=root
|
||||||
|
group=root
|
||||||
|
mode=0444
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "production"
|
||||||
|
|
||||||
|
# vpn certs
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src={{ puppet_secure }}/2fa-certs/keys/fas-all.phx2.fedoraproject.org.crt
|
||||||
|
dest=/etc/pki/totpcgi/totpcgi-server.crt
|
||||||
|
owner=root
|
||||||
|
group=totpcgi
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "production"
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src={{ puppet_secure }}/fa-certs/keys/fas-all.phx2.fedoraproject.org.key
|
||||||
|
dest=/etc/pki/totpcgi/totpcgi-server.key
|
||||||
|
owner=root
|
||||||
|
group=totpcgi
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "production"
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src=totpcgi-httpd.conf
|
||||||
|
dest=/etc/httpd/conf.d/totpcgi.conf
|
||||||
|
owner=root
|
||||||
|
group=root
|
||||||
|
mode=0444
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
when: env == "production"
|
||||||
|
#
|
||||||
|
# TODO: vpn certs
|
||||||
|
#
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
copy: >
|
||||||
|
src={{ puppet_private }}/2fa-certs/keys/ca.crt
|
||||||
|
dest=/etc/pki/totpcgi/totpcgi-ca.crt
|
||||||
|
owner=root
|
||||||
|
group=totpcgi
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
||||||
|
|
||||||
|
- name: copy server cert file over
|
||||||
|
template: >
|
||||||
|
src=provisioning.conf.j2
|
||||||
|
dest=/etc/totpcgi/provisioning.conf
|
||||||
|
owner=root
|
||||||
|
group=totpcgiprov
|
||||||
|
mode=0640
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
- config
|
44
roles/totpcgi/templates/provisioning-httpd.conf.j2
Normal file
44
roles/totpcgi/templates/provisioning-httpd.conf.j2
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
Listen 8444
|
||||||
|
<VirtualHost *:8444>
|
||||||
|
LoadModule suexec_module modules/mod_suexec.so
|
||||||
|
|
||||||
|
DocumentRoot /var/www/totpcgi-provisioning
|
||||||
|
ServerName fas01.stg.phx2.fedoraproject.org:8444
|
||||||
|
ErrorLog /var/log/httpd/totpcgi-provisioning-error.log
|
||||||
|
SuexecUserGroup totpcgiprov totpcgiprov
|
||||||
|
|
||||||
|
AddHandler cgi-script .cgi
|
||||||
|
DirectoryIndex index.cgi
|
||||||
|
|
||||||
|
Header set Cache-Control no-cache
|
||||||
|
Header set Expires 0
|
||||||
|
|
||||||
|
#SSLEngine on
|
||||||
|
#SSLCertificateFile /etc/pki/totpcgi/totpcgi-server.crt
|
||||||
|
#SSLCertificateKeyFile /etc/pki/totpcgi/totpcgi-server.key
|
||||||
|
#SSLCACertificateFile /etc/pki/totpcgi/totpcgi-ca.crt
|
||||||
|
|
||||||
|
#CustomLog /var/log/httpd/totpcgi-provisioning-ssl-request-log \
|
||||||
|
# "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
|
||||||
|
|
||||||
|
<Directory "/var/www/totpcgi-provisioning">
|
||||||
|
Options ExecCGI
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Location />
|
||||||
|
AuthType Basic
|
||||||
|
AuthName "Fedora totpcgi"
|
||||||
|
|
||||||
|
Auth_PG_host db-fas
|
||||||
|
Auth_PG_port 5432
|
||||||
|
Auth_PG_user fasreadonly
|
||||||
|
Auth_PG_pwd {{ fasReadOnlyPassword }}
|
||||||
|
Auth_PG_database fas2
|
||||||
|
Auth_PG_pwd_table people
|
||||||
|
Auth_PG_uid_field username
|
||||||
|
Auth_PG_pwd_field password
|
||||||
|
Auth_PG_pwd_whereclause " and status='active'"
|
||||||
|
|
||||||
|
Require valid-user
|
||||||
|
</Location>
|
||||||
|
</VirtualHost>
|
88
roles/totpcgi/templates/provisioning.conf.j2
Normal file
88
roles/totpcgi/templates/provisioning.conf.j2
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
[secret]
|
||||||
|
# Whether to encrypt the secret when we generate it. Encrypting the secret
|
||||||
|
# with the user's pincode means that even if the .totp file is leaked, an
|
||||||
|
# attacker will not be able to get the secret without knowing the user's
|
||||||
|
# pincode. The downside is that if a user forgets their pincode, both the
|
||||||
|
# pincode and the secret will need to be fully re-provisioned.
|
||||||
|
# Setting to "True" will also turn off scratch-token support.
|
||||||
|
encrypt_secret = False
|
||||||
|
|
||||||
|
# You can allow for some clock drift between the client and server by setting
|
||||||
|
# the permitted window size. Window size is calculated in 10-second intervals,
|
||||||
|
# so a window size of 6 allows clock drift of 60 seconds in either direction.
|
||||||
|
window_size = 3
|
||||||
|
|
||||||
|
# First value is the number of times. Second value is the number of seconds.
|
||||||
|
# So, "3, 30" means "3 falures within 30 seconds"
|
||||||
|
rate_limit = 3, 30
|
||||||
|
|
||||||
|
# How many scratch tokens to generate. Note, that this setting is ignored
|
||||||
|
# if encrypt_secret is set to True.
|
||||||
|
scratch_tokens_n = 5
|
||||||
|
|
||||||
|
# This identifies the token in the Google Authenticator application. It comes
|
||||||
|
# very handy when users have more than one token, so set this to something
|
||||||
|
# descriptive of your environment.
|
||||||
|
{% if environment == "staging" %}
|
||||||
|
totp_user_mask = $username@stg.fedoraproject.org
|
||||||
|
{% else %}
|
||||||
|
totp_user_mask = $username@fedoraproject.org
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Used by provisioning.cgi
|
||||||
|
# Where the provisioning CGI is located, with regards to the web root.
|
||||||
|
action_url = /totpcgiprovision/index.cgi
|
||||||
|
|
||||||
|
# Used by provisioning.cgi
|
||||||
|
# Where provisioning.css and provisioning-print.css are located with regards
|
||||||
|
# to the web root.
|
||||||
|
css_root = /totpcgiprovision/
|
||||||
|
|
||||||
|
# Used by provisioning.cgi
|
||||||
|
# Where to find the templates files.
|
||||||
|
templates_dir = /etc/totpcgi/templates
|
||||||
|
|
||||||
|
# Used by provisioning.cgi
|
||||||
|
# Whether to rely on HTTP auth to handle authentication.
|
||||||
|
# As we don't get the password, only the username, turning this on
|
||||||
|
# will automatically set encrypt_secret to false.
|
||||||
|
#
|
||||||
|
# Be careful turning this on.
|
||||||
|
trust_http_auth = True
|
||||||
|
|
||||||
|
|
||||||
|
[pincode]
|
||||||
|
# Which hashing mechanism to use. Valid entries: md5, bcrypt, sha256, sha512
|
||||||
|
usehash = sha256
|
||||||
|
|
||||||
|
# Whether to compile the DBM database (only meaningful with the file backend)
|
||||||
|
makedb = True
|
||||||
|
|
||||||
|
# The backends are pretty much the same as in totpcgi.conf, except if you
|
||||||
|
# are using the postgresql secret backend, you need to connect as a user
|
||||||
|
# that is allowed to modify user records (e.g. totpcgi_admin).
|
||||||
|
[secret_backend]
|
||||||
|
;engine = file
|
||||||
|
;secrets_dir = /etc/totpcgi/totp
|
||||||
|
|
||||||
|
; For PostgreSQL backend:
|
||||||
|
engine = pgsql
|
||||||
|
pg_connect_string = user={{ totpcgiadminDBUser }} password={{ totpcgiadminDBPassword }} host=db-fas01 dbname=totpcgi
|
||||||
|
|
||||||
|
[pincode_backend]
|
||||||
|
engine = pgsql
|
||||||
|
pg_connect_string = user={{ totpcgiadminDBUser }} password={{ totpcgiadminDBPassword }} host=db-fas01 dbname=totpcgi
|
||||||
|
|
||||||
|
; For LDAP backend (simple bind auth):
|
||||||
|
;engine = ldap
|
||||||
|
;ldap_url = ldaps://ipa.example.com:636/
|
||||||
|
;ldap_dn = uid=$username,cn=users,cn=accounts,dc=example,dc=com
|
||||||
|
;ldap_cacert = /etc/pki/tls/certs/ipa-ca.crt
|
||||||
|
|
||||||
|
[state_backend]
|
||||||
|
;engine = file
|
||||||
|
;state_dir = /var/lib/totpcgi
|
||||||
|
|
||||||
|
; For PostgreSQL backend:
|
||||||
|
engine = pgsql
|
||||||
|
pg_connect_string = user={{ totpcgiadminDBUser }} password={{ totpcgiadminDBPassword }} host=db-fas01 dbname=totpcgi
|
31
roles/totpcgi/templates/totpcgi.conf.j2
Normal file
31
roles/totpcgi/templates/totpcgi.conf.j2
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[main]
|
||||||
|
require_pincode = True
|
||||||
|
success_string = OK
|
||||||
|
{% if env == "staging" %}
|
||||||
|
fas_url = https://admin.stg.fedoraproject.org/accounts/
|
||||||
|
{% else %}
|
||||||
|
fas_url = https://admin.fedoraproject.org/accounts/
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
[secret_backend]
|
||||||
|
; For PostgreSQL backend:
|
||||||
|
engine = pgsql
|
||||||
|
pg_connect_string = user={{ totpcgiDBUser }} password={{ totpcgiDBPassword }} host=db-fas01 dbname=totpcgi
|
||||||
|
|
||||||
|
[pincode_backend]
|
||||||
|
engine = pgsql
|
||||||
|
pg_connect_string = user={{ totpcgiDBUser }} password={{ totpcgiDBPassword }} host=db-fas01 dbname=totpcgi
|
||||||
|
|
||||||
|
; For LDAP backend (simple bind auth):
|
||||||
|
;engine = ldap
|
||||||
|
;ldap_url = ldaps://ipa.example.com:636/
|
||||||
|
;ldap_dn = uid=$username,cn=users,cn=accounts,dc=example,dc=com
|
||||||
|
;ldap_cacert = /etc/pki/tls/certs/ipa-ca.crt
|
||||||
|
|
||||||
|
[state_backend]
|
||||||
|
;engine = file
|
||||||
|
;state_dir = /var/lib/totpcgi
|
||||||
|
|
||||||
|
; For PostgreSQL backend:
|
||||||
|
engine = pgsql
|
||||||
|
pg_connect_string = user={{ totpcgiDBUser }} password={{ totpcgiDBPassword }} host=db-fas01 dbname=totpcgi
|
Loading…
Add table
Add a link
Reference in a new issue