forked from SUNET/soc-ops
Preparing vul dashbaord and sso.
This commit is contained in:
parent
42ac3c8c42
commit
5684d1b892
4 changed files with 16 additions and 158 deletions
|
@ -1,11 +1,13 @@
|
||||||
# Note that the matching is done with re.match()
|
# Note that the matching is done with re.match()
|
||||||
'.*\.cert\.sunet\.se$':
|
'.*\.cert\.sunet\.se$':
|
||||||
soc:
|
soc:
|
||||||
|
sunet::startship:
|
||||||
sunet::server:
|
sunet::server:
|
||||||
fail2ban: false
|
fail2ban: false
|
||||||
ssh_allow_from_anywhere: false
|
ssh_allow_from_anywhere: false
|
||||||
|
|
||||||
'^internal-sto1-dev-vulndash-1.cert.sunet.se$':
|
'^vul-dashboard-test.cert.sunet.se$':
|
||||||
|
sunet::dockerhost2:
|
||||||
# soc::sso:
|
# soc::sso:
|
||||||
# hostname: 'vd-dev.cert.sunet.se'
|
# hostname: 'vd-dev.cert.sunet.se'
|
||||||
# email: 'cert@cert.sunet.se'
|
# email: 'cert@cert.sunet.se'
|
||||||
|
@ -16,7 +18,6 @@
|
||||||
# certbot: false
|
# certbot: false
|
||||||
|
|
||||||
test-sso-proxy1.cert.sunet.se:
|
test-sso-proxy1.cert.sunet.se:
|
||||||
sunet::starship:
|
|
||||||
sunet::dockerhost2:
|
sunet::dockerhost2:
|
||||||
sunet::certbot::acmed:
|
sunet::certbot::acmed:
|
||||||
soc::satosa:
|
soc::satosa:
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import requests
|
|
||||||
import sys
|
|
||||||
|
|
||||||
### EDIT THESE: Configuration values ###
|
|
||||||
|
|
||||||
# URL to acme-dns instance
|
|
||||||
ACMEDNS_URL = "https://acme-d.sunet.se"
|
|
||||||
# Path for acme-dns credential storage
|
|
||||||
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"
|
|
||||||
# Whitelist for address ranges to allow the updates from
|
|
||||||
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]
|
|
||||||
ALLOW_FROM = []
|
|
||||||
# Force re-registration. Overwrites the already existing acme-dns accounts.
|
|
||||||
FORCE_REGISTER = False
|
|
||||||
|
|
||||||
### DO NOT EDIT BELOW THIS POINT ###
|
|
||||||
### HERE BE DRAGONS ###
|
|
||||||
|
|
||||||
DOMAIN = os.environ["CERTBOT_DOMAIN"]
|
|
||||||
if DOMAIN.startswith("*."):
|
|
||||||
DOMAIN = DOMAIN[2:]
|
|
||||||
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN
|
|
||||||
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]
|
|
||||||
|
|
||||||
|
|
||||||
class AcmeDnsClient(object):
|
|
||||||
"""
|
|
||||||
Handles the communication with ACME-DNS API
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, acmedns_url):
|
|
||||||
self.acmedns_url = acmedns_url
|
|
||||||
|
|
||||||
def register_account(self, allowfrom):
|
|
||||||
"""Registers a new ACME-DNS account"""
|
|
||||||
|
|
||||||
if allowfrom:
|
|
||||||
# Include whitelisted networks to the registration call
|
|
||||||
reg_data = {"allowfrom": allowfrom}
|
|
||||||
res = requests.post(self.acmedns_url+"/register",
|
|
||||||
data=json.dumps(reg_data))
|
|
||||||
else:
|
|
||||||
res = requests.post(self.acmedns_url+"/register")
|
|
||||||
if res.status_code == 201:
|
|
||||||
# The request was successful
|
|
||||||
return res.json()
|
|
||||||
else:
|
|
||||||
# Encountered an error
|
|
||||||
msg = ("Encountered an error while trying to register a new acme-dns "
|
|
||||||
"account. HTTP status {}, Response body: {}")
|
|
||||||
print(msg.format(res.status_code, res.text))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def update_txt_record(self, account, txt):
|
|
||||||
"""Updates the TXT challenge record to ACME-DNS subdomain."""
|
|
||||||
update = {"subdomain": account['subdomain'], "txt": txt}
|
|
||||||
headers = {"X-Api-User": account['username'],
|
|
||||||
"X-Api-Key": account['password'],
|
|
||||||
"Content-Type": "application/json"}
|
|
||||||
res = requests.post(self.acmedns_url+"/update",
|
|
||||||
headers=headers,
|
|
||||||
data=json.dumps(update))
|
|
||||||
if res.status_code == 200:
|
|
||||||
# Successful update
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
msg = ("Encountered an error while trying to update TXT record in "
|
|
||||||
"acme-dns. \n"
|
|
||||||
"------- Request headers:\n{}\n"
|
|
||||||
"------- Request body:\n{}\n"
|
|
||||||
"------- Response HTTP status: {}\n"
|
|
||||||
"------- Response body: {}")
|
|
||||||
s_headers = json.dumps(headers, indent=2, sort_keys=True)
|
|
||||||
s_update = json.dumps(update, indent=2, sort_keys=True)
|
|
||||||
s_body = json.dumps(res.json(), indent=2, sort_keys=True)
|
|
||||||
print(msg.format(s_headers, s_update, res.status_code, s_body))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
class Storage(object):
|
|
||||||
def __init__(self, storagepath):
|
|
||||||
self.storagepath = storagepath
|
|
||||||
self._data = self.load()
|
|
||||||
|
|
||||||
def load(self):
|
|
||||||
"""Reads the storage content from the disk to a dict structure"""
|
|
||||||
data = dict()
|
|
||||||
filedata = ""
|
|
||||||
try:
|
|
||||||
with open(self.storagepath, 'r') as fh:
|
|
||||||
filedata = fh.read()
|
|
||||||
except IOError as e:
|
|
||||||
if os.path.isfile(self.storagepath):
|
|
||||||
# Only error out if file exists, but cannot be read
|
|
||||||
print("ERROR: Storage file exists but cannot be read")
|
|
||||||
sys.exit(1)
|
|
||||||
try:
|
|
||||||
data = json.loads(filedata)
|
|
||||||
except ValueError:
|
|
||||||
if len(filedata) > 0:
|
|
||||||
# Storage file is corrupted
|
|
||||||
print("ERROR: Storage JSON is corrupted")
|
|
||||||
sys.exit(1)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
"""Saves the storage content to disk"""
|
|
||||||
serialized = json.dumps(self._data)
|
|
||||||
try:
|
|
||||||
with os.fdopen(os.open(self.storagepath,
|
|
||||||
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:
|
|
||||||
fh.truncate()
|
|
||||||
fh.write(serialized)
|
|
||||||
except IOError as e:
|
|
||||||
print("ERROR: Could not write storage file.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def put(self, key, value):
|
|
||||||
"""Puts the configuration value to storage and sanitize it"""
|
|
||||||
# If wildcard domain, remove the wildcard part as this will use the
|
|
||||||
# same validation record name as the base domain
|
|
||||||
if key.startswith("*."):
|
|
||||||
key = key[2:]
|
|
||||||
self._data[key] = value
|
|
||||||
|
|
||||||
def fetch(self, key):
|
|
||||||
"""Gets configuration value from storage"""
|
|
||||||
try:
|
|
||||||
return self._data[key]
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Init
|
|
||||||
client = AcmeDnsClient(ACMEDNS_URL)
|
|
||||||
storage = Storage(STORAGE_PATH)
|
|
||||||
|
|
||||||
# Check if an account already exists in storage
|
|
||||||
account = storage.fetch(DOMAIN)
|
|
||||||
if FORCE_REGISTER or not account:
|
|
||||||
# Create and save the new account
|
|
||||||
account = client.register_account(ALLOW_FROM)
|
|
||||||
storage.put(DOMAIN, account)
|
|
||||||
storage.save()
|
|
||||||
|
|
||||||
# Display the notification for the user to update the main zone
|
|
||||||
msg = "Please add the following CNAME record to your main DNS zone:\n{}"
|
|
||||||
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])
|
|
||||||
print(msg.format(cname))
|
|
||||||
|
|
||||||
# Update the TXT record in acme-dns instance
|
|
||||||
client.update_txt_record(account, VALIDATION_TOKEN)
|
|
File diff suppressed because one or more lines are too long
|
@ -38,7 +38,7 @@ class soc::sso(
|
||||||
$single_user = false,
|
$single_user = false,
|
||||||
$front_clients = '',
|
$front_clients = '',
|
||||||
$satosa = true,
|
$satosa = true,
|
||||||
$satosa_certbot = true,
|
$satosa_certbot = false,
|
||||||
$translog = 'INFO',
|
$translog = 'INFO',
|
||||||
$proxy = 'https://shared-sso-proxy1.cert.sunet.se/idp',
|
$proxy = 'https://shared-sso-proxy1.cert.sunet.se/idp',
|
||||||
$norpan = false,
|
$norpan = false,
|
||||||
|
|
Loading…
Add table
Reference in a new issue