totpcgi / 2fa: remove old totpci and files and roles.

Note: there are still some calls here in old fas in openshift, but we
will remove those when we remove old fas (likely as soon as zodbot is
ported over to noggin).

Signed-off-by: Kevin Fenzi <kevin@scrye.com>
This commit is contained in:
Kevin Fenzi 2021-05-17 13:00:56 -07:00
parent 6ac5a89b14
commit f23fd1b7a1
28 changed files with 0 additions and 1524 deletions

View file

@ -1,21 +0,0 @@
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";
};
};

View file

@ -1,27 +0,0 @@
pam_url:
{
settings:
{
{% if env == 'staging' %}
url = "https://fas-all.stg.phx2.fedoraproject.org:8443/"; # URI to fetch
{% elif datacenter == 'iad2' %}
url = "https://fas-all.iad2.fedoraproject.org:8443/"; # URI to fetch
{% else %}
url = "https://fas-all.vpn.fedoraproject.org:8443/"; # URI to fetch
{% endif %}
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";
};
};

View file

@ -1,21 +0,0 @@
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";
};
};

View file

@ -1,21 +0,0 @@
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";
};
};

View file

@ -1,9 +0,0 @@
#%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
account include system-auth
password include system-auth
session optional pam_keyinit.so revoke
session required pam_limits.so

View file

@ -1,6 +0,0 @@
#%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

View file

@ -94,16 +94,6 @@ backend freemedia-backend
#{% endif %}
# option httpchk GET /packages/_heartbeat
frontend totpcgiprovision-frontend
bind 0.0.0.0:10019
default_backend totpcgiprovision-backend
backend totpcgiprovision-backend
balance hdr(appserver)
http-check expect status 401
server fas01 fas01:8444 check inter 5s rise 1 fall 2
option httpchk GET /index.cgi
frontend blockerbugs-frontend
bind 0.0.0.0:10022
default_backend blockerbugs-backend
@ -289,28 +279,6 @@ backend ipa-backend
{% endif %}
option httpchk GET /ipa/ui/
# This is for TOTPCGI (legacy 2fa). It goes to the Openshift routers, which then passthrough TLS to the totpcgi pods
frontend totp-frontend
mode tcp
bind 0.0.0.0:8443
default_backend totp-backend
backend totp-backend
mode tcp
option tcplog
balance roundrobin
maxconn 16384
timeout queue 5000
timeout server 86400000
timeout connect 86400000
server os-node01 os-node01:443 weight 1 maxconn 16384
server os-node02 os-node02:443 weight 1 maxconn 16384
server os-node03 os-node03:443 weight 1 maxconn 16384
server os-node04 os-node04:443 weight 1 maxconn 16384
{% if env == "production" %}
server os-node05 os-node05:443 weight 1 maxconn 16384
{% endif %}
frontend krb5-frontend
mode tcp
bind 0.0.0.0:1088

View file

@ -1,26 +0,0 @@
<?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">
&copy; 2012 Red Hat, Inc. and others.
</div>
</div>
</body>
</html>

View file

@ -1,49 +0,0 @@
<?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 &raquo;"/>
</td>
</tr>
</table>
</form>
</div>
<div id="copyright">
&copy; 2012 Red Hat, Inc. and others.
</div>
</div>
</body>
</html>

View file

@ -1,53 +0,0 @@
<?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">
&copy; 2012 Red Hat, Inc. and others.
</div>
</div>
</body>
</html>

View file

@ -1,200 +0,0 @@
#!/usr/bin/python -ttW ignore::DeprecationWarning
##
# 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 logging
logging.basicConfig(level=logging.INFO)
import urllib2
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:
logging.exception("Problem connecting to FAS")
sys.exit(1)
backends = totpcgi.backends.Backends()
try:
backends.load_from_config(config)
except totpcgi.backends.BackendNotSupported, ex:
logging.exception("Backend engine not supported")
sys.exit(1)
### 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://yubikey:8080/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:
logging.warning(
"TOKEN FAILURE! user=%s, mode=%s, host=%s, message=%s",
user,
mode,
remote_host,
str(ex))
bad_request(str(ex))
logging.info(
"Token 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__':
try:
cgimain()
except Exception:
logging.exception("Server error during processing")
output = 'ERR\nInternal server error\n'
sys.stdout.write('Status: 500 SERVER ERROR\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)

View file

@ -1,21 +0,0 @@
pam_url:
{
settings:
{
url = "https://fas-all.iad2.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";
};
};

View file

@ -1,21 +0,0 @@
pam_url:
{
settings:
{
url = "https://fas-all.iad2.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";
};
};

View file

@ -1,21 +0,0 @@
pam_url:
{
settings:
{
url = "https://fas-all.stg.iad2.fedoraproject.org/"; # 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";
};
};

View file

@ -1,21 +0,0 @@
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";
};
};

View file

@ -1,265 +0,0 @@
#!/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(display=0, logdir="/tmp")
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 = '&nbsp;'
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()

View file

@ -1,11 +0,0 @@
#%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

View file

@ -1,6 +0,0 @@
#%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

View file

@ -1,6 +0,0 @@
#%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

View file

@ -1,236 +0,0 @@
- name: add totpcgi user
user: name=totpcgi uid=501 state=present home=/var/lib/totpcgi createhome=yes system=yes
tags:
- config
- name: install needed packages
package: name={{ item }} state=present
with_items:
- mod_auth_pgsql
- totpcgi
- totpcgi-selinux
- totpcgi-provisioning
- python-qrcode
- httpd
- mod_ssl
tags:
- packages
- name: Install the cgi apache configuration files
template: >
src={{ item.file }}.j2 dest=/etc/httpd/conf.d/{{ item.dest }}
owner=root group=root mode=0444
with_items:
- {file: provisioning-httpd.conf, dest: totpcgi-provisioning.conf }
tags:
- files
- config
notify:
- restart apache
- name: create directories
file: path=/etc/{{ item }} state=directory owner=root group=totpcgi mode=750
with_items:
- pki/totpcgi
- totpcgi/totp
- name: create template directory for totpcgiprov
file: path=/etc/{{ item }} state=directory owner=root group=totpcgiprov mode=750
with_items:
- totpcgi/templates
- name: create /etc/totpcgi with the proper rights and owners
file: path=/etc/{{ item }} state=directory owner=totpcgiprov group=totpcgi mode=750
with_items:
- totpcgi
- name: copy html files over
copy: >
src=html
dest=/etc/totpcgi/templates
owner=root
group=totpcgiprov
mode=0750
tags:
- files
- config
- name: copy provisioning 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=totpcgi
group=totpcgi
mode=0550
tags:
- files
- config
- name: copy totpcgi.conf file over
template: >
src=totpcgi.conf.j2
dest=/etc/totpcgi/totpcgi.conf
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
# staging certs
- name: copy staging server cert file over
copy: >
src={{ private }}/files/2fa-certs/keys/fas-all.stg.iad2.fedoraproject.org.crt
dest=/etc/pki/tls/certs/totpcgi-server.crt
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
when: env == "staging"
- name: copy staging server key file over
copy: >
src={{ private }}/files/2fa-certs/keys/fas-all.stg.iad2.fedoraproject.org.key
dest=/etc/pki/totpcgi/totpcgi-server.key
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
when: env == "staging"
- name: copy staging server conf file over
template: >
src=totpcgi-httpd.conf.stg.j2
dest=/etc/httpd/conf.d/totpcgi.conf
owner=root
group=root
mode=0444
tags:
- files
- config
- sslciphers
when: env == "staging"
# prod certs
- name: copy server cert file over
copy: >
src={{ private }}/files/2fa-certs/keys/fas-all.iad2.fedoraproject.org.crt
dest=/etc/pki/totpcgi/totpcgi-server.crt
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
notify:
- reload httpd
when: env == "production"
- name: copy server cert file over
copy: >
src={{ private }}/files/2fa-certs/keys/fas-all.iad2.fedoraproject.org.key
dest=/etc/pki/totpcgi/totpcgi-server.key
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
notify:
- reload httpd
when: env == "production"
- name: copy totpcgi httpd config
template: >
src=totpcgi-httpd.conf.j2
dest=/etc/httpd/conf.d/totpcgi.conf
owner=root
group=root
mode=0444
tags:
- files
- config
- sslciphers
notify:
- reload httpd
when: env == "production"
# vpn certs
- name: copy VPN server cert file over
copy: >
src={{ private }}/files/2fa-certs/keys/fas-all.vpn.fedoraproject.org.crt
dest=/etc/pki/totpcgi/totpcgi-server-vpn.crt
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
notify:
- reload httpd
when: env == "production"
- name: copy VPN server cert file over
copy: >
src={{ private }}/files/2fa-certs/keys/fas-all.vpn.fedoraproject.org.key
dest=/etc/pki/totpcgi/totpcgi-server-vpn.key
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
when: env == "production"
- name: copy VPN server cert file over
template: >
src=totpcgi-httpd.conf.vpn.j2
dest=/etc/httpd/conf.d/totpcgi-vpn.conf
owner=root
group=root
mode=0444
tags:
- files
- config
- sslciphers
when: env == "production"
- name: copy ca cert over
copy: >
src={{ private }}/files/2fa-certs/keys/ca.crt
dest=/etc/pki/totpcgi/totpcgi-ca.crt
owner=root
group=totpcgi
mode=0640
tags:
- files
- config
- name: copy provisioning.conf over
template: >
src=provisioning.conf.j2
dest=/etc/totpcgi/provisioning.conf
owner=root
group=totpcgiprov
mode=0640
tags:
- files
- config

View file

@ -1,138 +0,0 @@
{% macro load_file(filename) %}{% include filename %}{%- endmacro -%}
---
apiVersion: v1
kind: ConfigMap
metadata:
{% if objectname == "configmap-totpcgi-vpn.yml" %}
name: totpcgi-vpn
{% else %}
name: totpcgi
{% endif %}
labels:
app: fas
data:
totpcgi-ca.crt: |-
{{ lookup('file', '{{ private }}/files/2fa-certs/keys/ca.crt') | indent() }}
provisioning.conf: |-
{{ load_file('provisioning.conf.j2') | indent() }}
totpcgi.conf: |-
{{ load_file('totpcgi.conf.j2') | indent() }}
start.sh: |-
set -xe
rm -rf /httpdir/*
mkdir /httpdir/run
ln -s /etc/httpd/modules /httpdir/modules
truncate --size=0 /httpdir/accesslog /httpdir/errorlog
tail -qf /httpdir/accesslog /httpdir/errorlog &
exec /usr/sbin/httpd.worker -f /etc/totpcgi/httpd.conf -DFOREGROUND -DNO_DETACH
httpd.conf: |-
ServerRoot "/httpdir"
PidFile "/httpdir/httpd.pid"
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_anon_module modules/mod_authn_anon.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule include_module modules/mod_include.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule ext_filter_module modules/mod_ext_filter.so
LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so
LoadModule mime_module modules/mod_mime.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
LoadModule version_module modules/mod_version.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule auth_pgsql_module modules/mod_auth_pgsql.so
LoadModule cgi_module modules/mod_cgi.so
# There's so much in python(-fedora) that wants a valid homedir....
SetEnv HOME /httpdir
Listen 0.0.0.0:8080
Listen 0.0.0.0:8443 https
StartServers 4
MaxClients 300
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog /httpdir/accesslog combined
ErrorLog /httpdir/errorlog
LogLevel info
TypesConfig /etc/mime.types
AddDefaultCharset UTF-8
CoreDumpDirectory /tmp
DirectoryIndex index.cgi
<VirtualHost *:8080>
Header set Cache-Control no-cache
Header set Expires 0
Alias /totpcgiprovision /var/www/totpcgi-provisioning
{% if env == "staging" %}
Alias /totpcgiprovision/ /var/www/totpcgi-provisioning
{% endif %}
AddHandler cgi-script .cgi
DocumentRoot /var/www/totpcgi-provisioning
<Directory "/var/www/totpcgi-provisioning">
Options ExecCGI
</Directory>
<Location />
AuthType Basic
AuthName "Fedora totpcgi"
Auth_PG_host db-fas{{ env_suffix }}
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>
<VirtualHost *:8443>
{% if env == "staging" %}
ServerName fas-all.stg.iad2.fedoraproject.org:8443
{% elif objectname == "configmap-totpcgi-vpn.yml" %}
ServerName fas-all.vpn.fedoraproject.org:8443
{% elif datacenter == 'iad2' %}
ServerName fas-all.iad2.fedoraproject.org:8443
{% endif %}
SSLEngine on
{% if objectname == "configmap-totpcgi-vpn.yml" %}
SSLCertificateFile /etc/pki/totp/tls.crt
SSLCertificateKeyFile /etc/pki/totp/tls.key
{% else %}
SSLCertificateFile /etc/pki/totp/tls.crt
SSLCertificateKeyFile /etc/pki/totp/tls.key
{% endif %}
SSLCACertificateFile /etc/totpcgi/totpcgi-ca.crt
SSLHonorCipherOrder On
SSLCipherSuite {{ ssl_ciphers }}
SSLProtocol {{ ssl_protocols }}
AddHandler cgi-script .cgi
SSLVerifyClient require
SSLVerifyDepth 10
DocumentRoot /var/www/totpcgi
<Directory "/var/www/totpcgi">
Options ExecCGI
</Directory>
</VirtualHost>
error.html: |-
{{ lookup('file', '{{ roles_path }}/totpcgi/files/html/error.html') | indent() }}
login.html: |-
{{ lookup('file', '{{ roles_path }}/totpcgi/files/html/login.html') | indent() }}
totp.html: |-
{{ lookup('file', '{{ roles_path }}/totpcgi/files/html/totp.html') | indent() }}

View file

@ -1,44 +0,0 @@
Listen 8444
<VirtualHost *:8444>
LoadModule suexec_module modules/mod_suexec.so
DocumentRoot /var/www/totpcgi-provisioning
ServerName fas01.stg.iad2.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>

View file

@ -1,88 +0,0 @@
[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 = 6
# 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 env == "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
# 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{{ env_suffix }}.{{ datacenter }}.fedoraproject.org dbname=totpcgi
[pincode_backend]
engine = pgsql
pg_connect_string = user={{ totpcgiadminDBUser }} password={{ totpcgiadminDBPassword }} host=db-fas01{{ env_suffix }}.{{ datacenter }}.fedoraproject.org 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{{ env_suffix }}.{{ datacenter }}.fedoraproject.org dbname=totpcgi

View file

@ -1,37 +0,0 @@
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.iad2.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
SSLHonorCipherOrder On
SSLCipherSuite {{ ssl_ciphers }}
SSLProtocol {{ ssl_protocols }}
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>

View file

@ -1,37 +0,0 @@
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.iad2.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
SSLHonorCipherOrder On
SSLCipherSuite {{ ssl_ciphers }}
SSLProtocol {{ ssl_protocols }}
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>

View file

@ -1,36 +0,0 @@
<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
SSLHonorCipherOrder On
SSLCipherSuite {{ ssl_ciphers }}
SSLProtocol {{ ssl_protocols }}
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>

View file

@ -1,31 +0,0 @@
[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{{ env_suffix }}.{{ datacenter }}.fedoraproject.org dbname=totpcgi
[pincode_backend]
engine = pgsql
pg_connect_string = user={{ totpcgiDBUser }} password={{ totpcgiDBPassword }} host=db-fas01{{ env_suffix }}.{{ datacenter }}.fedoraproject.org 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{{ env_suffix }}.{{ datacenter }}.fedoraproject.org dbname=totpcgi

View file

@ -1,40 +0,0 @@
---
- name: install pam_url
package: name=pam_url state=present
tags:
- packages
- 2fa
- name: /etc/pki/tls/private/totpcgi.pem
copy: src="{{ private }}/files/2fa-certs/keys/{{ inventory_hostname }}.pem" dest=/etc/pki/tls/private/totpcgi.pem mode=0400
tags:
- config
- 2fa
- name: /etc/pki/tls/private/totpcgi-ca.cert
copy: src="{{ private }}/files/2fa-certs/keys/ca.crt" dest=/etc/pki/tls/private/totpcgi-ca.cert mode=0400
tags:
- config
- 2fa
- name: /etc/pam_url.conf - split for staging/phx2/everyone else
template: src={{ item }} dest=/etc/pam_url.conf mode=0644
with_first_found:
- "{{ files }}/2fa/pam_url.conf.{{ inventory_hostname }}"
- "{{ files }}/2fa/pam_url.conf.{{ ansible_domain }}"
- "{{ files }}/2fa/pam_url.conf.{{ datacenter }}"
- "{{ files }}/2fa/pam_url.conf.j2"
tags:
- config
- pam_url
- 2fa
- name: /etc/pam.d/sudo
copy: src={{ item }} dest=/etc/pam.d/sudo mode=0644
with_first_found:
- "{{ files }}/2fa/sudo.pam.{{ inventory_hostname }}"
- "{{ files }}/2fa/sudo.pam.{{ ansible_domain }}"
- "{{ files }}/2fa/sudo.pam"
tags:
- config
- 2fa