First sekelton for shib-proxy.

This commit is contained in:
Johan Björklund 2024-10-28 13:32:11 +01:00
parent f4d620ba4d
commit f24f201d46
Signed by: bjorklund
GPG key ID: 5E8401339C7F5037
13 changed files with 787 additions and 0 deletions

View file

@ -0,0 +1,154 @@
#!/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)

View file

@ -0,0 +1,19 @@
# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file. Contents are based on https://ssl-config.mozilla.org
SSLEngine on
# Intermediate configuration, tweak to your needs
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLOptions +StrictRequire
# Add vhost name to log entries:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common

View file

@ -0,0 +1,22 @@
<Attributes xmlns="urn:mace:shibboleth:2.0:attribute-map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--
SWAMID standard attribute-map.xml for SAML 2.0
==============================================
The mappings are agreed to within the Shibboleth community or directly LDAP attribute names.
Version: 2017-01-04
REMEMBER to notify SWAMID saml-admins list when updating this file!
-->
<!-- eduPerson attributes until version 201310 -->
<Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" id="eppn">
<AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
</Attribute>
<Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.11" id="assurance"/>
<!-- Attribute to extract SWAMID Assurance Profiles -->
<Attribute name="urn:oasis:names:tc:SAML:attribute:assurance-certification" id="Assurance-Certification"/>
</Attributes>

View file

@ -0,0 +1,82 @@
<afp:AttributeFilterPolicyGroup
xmlns="urn:mace:shibboleth:2.0:afp:mf:basic"
xmlns:saml="urn:mace:shibboleth:2.0:afp:mf:saml"
xmlns:basic="urn:mace:shibboleth:2.0:afp:mf:basic"
xmlns:afp="urn:mace:shibboleth:2.0:afp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Shared rule for affiliation values. -->
<afp:PermitValueRule id="eduPersonAffiliationValues" xsi:type="OR">
<Rule xsi:type="AttributeValueString" value="faculty"/>
<Rule xsi:type="AttributeValueString" value="student"/>
<Rule xsi:type="AttributeValueString" value="staff"/>
<Rule xsi:type="AttributeValueString" value="alum"/>
<Rule xsi:type="AttributeValueString" value="member"/>
<Rule xsi:type="AttributeValueString" value="affiliate"/>
<Rule xsi:type="AttributeValueString" value="employee"/>
<Rule xsi:type="AttributeValueString" value="library-walk-in"/>
</afp:PermitValueRule>
<!--
Shared rule for all "scoped" attributes, but you'll have to manually apply it inside
an AttributeRule for each attribute you want to check.
-->
<afp:PermitValueRule id="ScopingRules" xsi:type="AND">
<Rule xsi:type="NOT">
<Rule xsi:type="AttributeValueRegex" regex="@"/>
</Rule>
<Rule xsi:type="saml:AttributeScopeMatchesShibMDScope"/>
</afp:PermitValueRule>
<afp:AttributeFilterPolicy>
<!-- This policy is in effect in all cases. -->
<afp:PolicyRequirementRule xsi:type="ANY"/>
<!-- Filter out undefined affiliations and ensure only one primary. -->
<afp:AttributeRule attributeID="affiliation">
<afp:PermitValueRule xsi:type="AND">
<RuleReference ref="eduPersonAffiliationValues"/>
<RuleReference ref="ScopingRules"/>
</afp:PermitValueRule>
</afp:AttributeRule>
<afp:AttributeRule attributeID="unscoped-affiliation">
<afp:PermitValueRuleReference ref="eduPersonAffiliationValues"/>
</afp:AttributeRule>
<afp:AttributeRule attributeID="primary-affiliation">
<afp:PermitValueRuleReference ref="eduPersonAffiliationValues"/>
</afp:AttributeRule>
<afp:AttributeRule attributeID="subject-id">
<afp:PermitValueRuleReference ref="ScopingRules"/>
</afp:AttributeRule>
<afp:AttributeRule attributeID="pairwise-id">
<afp:PermitValueRuleReference ref="ScopingRules"/>
</afp:AttributeRule>
<afp:AttributeRule attributeID="eppn">
<!-- Disabled scope check since the proxy does it for us and the proxies metadata doesn't include scopes from our customers.
<afp:PermitValueRuleReference ref="ScopingRules"/>
-->
</afp:AttributeRule>
<afp:AttributeRule attributeID="targeted-id">
<afp:PermitValueRuleReference ref="ScopingRules"/>
</afp:AttributeRule>
<!-- Require NameQualifier/SPNameQualifier match IdP and SP entityID respectively. -->
<afp:AttributeRule attributeID="persistent-id">
<afp:PermitValueRule xsi:type="saml:NameIDQualifierString"/>
</afp:AttributeRule>
<!-- Enforce that the values of schacHomeOrganization are a valid Scope. -->
<afp:AttributeRule attributeID="schacHomeOrganization">
<afp:PermitValueRule xsi:type="saml:AttributeValueMatchesShibMDScope" />
</afp:AttributeRule>
<!-- Catch-all that passes everything else through unmolested. -->
<afp:AttributeRule attributeID="*" permitAny="true"/>
</afp:AttributeFilterPolicy>
</afp:AttributeFilterPolicyGroup>

View file

@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View file

@ -0,0 +1,176 @@
## Copy from CNAAS, modifications for Sunet CERT
#
# General SSO documentation: https://wiki.sunet.se/x/sZGLBg
#
# @param hostname FQDN of the host this is running on.
#
# @param email Support email used in error messages etc.
#
# @param service_endpoint Location of service to reverse proxy for.
#
# @param groups
# List of user groups from sso_groups in global/overlay/etc/hiera/data/common.yaml. The
# default is a non-existing placeholder group, added to make the Apache config valid.
#
# @param passthrough List of paths to disable SAML protection for, e.g. API paths.
#
# @param x_remote_user
# If true, EPPN is put in the HTTP header X-Remote-User instead of REMOTE_USER.
#
# @param single_user
# If true, EPPN is discarded and X-Remote-User is set to "cnaas-user". This is useful in
# cases where the service we reverse proxy for can't create new accounts automatically.
# We use this only for Graylog at the time of writing.
#
# @param swamid_testing Set this to true if your SP is registered in swamid-testing.
#
# @param front_clients
# Hiera field, defined at common.yaml, with the the frontend IP prefixes that require access
# to port 443. Defaults to empty string.
#
class cnaas::sso(
$hostname,
$email,
$service_endpoint,
$groups = ['PLACEHOLDER'],
$passthrough = [],
$x_remote_user = false,
$swamid_testing = false,
$single_user = false,
$front_clients = '',
$translog = 'INFO',
$certbot = true,
) {
file { '/opt/sso':
ensure => directory,
}
#
# Apache files
#
file { '/opt/sso/apache':
ensure => directory,
}
file { '/opt/sso/apache/site.conf':
ensure => file,
content => template('soc/sso/apache-site.conf.erb'),
}
# SSL defaults copied from certbot:
# https://github.com/certbot/certbot/blob/master/certbot-apache/certbot_apache/_internal/tls_configs/current-options-ssl-apache.conf
file { '/opt/sso/apache/ssl.conf':
ensure => file,
content => file('soc/sso/apache-ssl.conf'),
}
file { '/opt/sso/apache/groups.txt':
ensure => file,
content => template('soc/sso/apache-groups.txt.erb')
}
#
# Shibboleth files
#
file { '/opt/sso/shibboleth':
ensure => directory,
}
file { '/opt/sso/shibboleth/shibboleth2.xml':
ensure => file,
content => template('soc/sso/shibboleth2.xml.erb'),
}
file { '/opt/sso/shibboleth/shibd.logger':
ensure => file,
content => template('soc/sso/shibd.logger.erb'),
}
file { '/opt/sso/shibboleth/attribute-map.xml':
ensure => file,
content => file('soc/sso/attribute-map.xml'),
}
file { '/opt/sso/shibboleth/md-signer2.crt':
ensure => file,
content => file('soc/sso/md-signer2.crt'),
}
sunet::snippets::secret_file { '/opt/sso/shibboleth/sp-key.pem':
hiera_key => 'sso_sp_key'
}
#
# Certbot
#
if $certbot {
package { ['certbot', 'python3-requests']:
ensure => 'latest',
}
file { '/etc/letsencrypt/acme-dns-auth.py':
ensure => file,
content => file('cnaas/sso/acme-dns-auth.py'),
mode => '0744',
}
file { '/etc/letsencrypt/renewal-hooks/deploy/soc-sso-reload':
ensure => file,
mode => '0700',
content => "#!/bin/sh -eu\ndocker exec sso service apache2 reload",
}
sunet::scriptherder::cronjob { 'le_renew':
cmd => '/bin/echo "Keeping this cronjob, but disabled to avoid scriptherder complainign about unknown check"',
special => 'daily',
}
}
#
# Docker
#
exec {"Create Docker network \"sso\" to talk to service":
# We OR with true to ignore errors, since the network often already exists.
# We specify a subnet so that services which have the option/requirement can
# specify this subnet as source of trusted proxies. This is used in Graylog,
# for example; see setting "trusted_proxies".
command => 'docker network create sso --subnet 172.29.0.0/24 || true'
}
file { '/opt/sso/docker-compose.yml':
ensure => file,
mode => '0600',
content => template('soc/sso/docker-compose.yml.erb'),
}
sunet::docker_compose_service { 'sso':
description => '',
compose_file => '/opt/sso/docker-compose.yml',
}
#
# NFT Rules
#
if 'wg0' in $facts['networking']['interfaces'].keys {
if $front_clients != '' {
$front_clients_exposed = hiera_array($front_clients,[])
sunet::nftables::docker_expose { 'clients_https' :
allow_clients => $front_clients_exposed,
port => 443,
iif => 'wg0',
}
}
}
sunet::nftables::docker_expose { 'apache_sso_https' :
allow_clients => ['0.0.0.0/0'],
port => 443,
iif => 'ens3',
}
}

View file

@ -0,0 +1,3 @@
<%- scope.call_function('safe_hiera',['sso_groups']).each do | group, users | -%>
<%= group %>: <% users.each.with_index do | user, i | %><%= user %><%= ' ' if i < (users.size - 1) %><% end %>
<%- end -%>

View file

@ -0,0 +1,63 @@
<IfModule mod_ssl.c>
<VirtualHost *:443>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
ServerName <%= @hostname %>
ServerAdmin <%= @email %>
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
<Location />
AuthType shibboleth
ShibRequestSetting requireSession On
<%- if @x_remote_user -%>
RequestHeader set X-Remote-User %{REMOTE_USER}s
<%- elsif @single_user -%>
RequestHeader set X-Remote-User soc-user
<%- else -%>
ShibUseHeaders On
<%- end -%>
AuthGroupFile /etc/apache2/groups.txt
Require group <% @groups.each.with_index do |group, i| %><%= group %><%= ' ' if i < (@groups.size - 1) %><% end %>
</Location>
<%- @passthrough.each do |path| -%>
<Location <%= path %>>
AuthType None
Require all granted
</Location>
<%- end -%>
ProxyPass "/" "<%= @service_endpoint %>/"
ProxyPassReverse "/" "<%= @service_endpoint %>/"
UseCanonicalName On
ProxyPreserveHost On
ServerAlias <%= @hostname %>
SSLCertificateFile /etc/letsencrypt/live/<%= @hostname %>/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/<%= @hostname %>/privkey.pem
Include /etc/apache2/ssl.conf
</VirtualHost>
</IfModule>

View file

@ -0,0 +1,23 @@
version: "3"
services:
sso:
container_name: sso
image: "docker.sunet.se/apache-shib"
ports:
- "443:443"
networks:
- sso
volumes:
- /etc/letsencrypt:/etc/letsencrypt
- ./apache/site.conf:/etc/apache2/sites-enabled/site.conf
- ./apache/ssl.conf:/etc/apache2/ssl.conf
- ./apache/groups.txt:/etc/apache2/groups.txt
- ./shibboleth/shibboleth2.xml:/etc/shibboleth/shibboleth2.xml
- ./shibboleth/shibd.logger:/etc/shibboleth/shibd.logger
- ./shibboleth/attribute-map.xml:/etc/shibboleth/attribute-map.xml
- ./shibboleth/md-signer2.crt:/etc/shibboleth/md-signer2.crt
- ./shibboleth/sp-cert.pem:/etc/shibboleth/sp-cert.pem
- ./shibboleth/sp-key.pem:/etc/shibboleth/sp-key.pem
networks:
sso:
external: true

View file

@ -0,0 +1,140 @@
<SPConfig xmlns="urn:mace:shibboleth:3.0:native:sp:config"
xmlns:conf="urn:mace:shibboleth:3.0:native:sp:config"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
clockSkew="180">
<!--
By default, in-memory StorageService, ReplayCache, ArtifactMap, and SessionCache
are used. See example-shibboleth2.xml for samples of explicitly configuring them.
-->
<!-- The ApplicationDefaults element is where most of Shibboleth's SAML bits are defined. -->
<ApplicationDefaults entityID="https://<%= @hostname %>"
REMOTE_USER="eppn subject-id"
metadataAttributePrefix="Meta-">
<!--
Controls session lifetimes, address checks, cookie handling, and the protocol handlers.
You MUST supply an effectively unique handlerURL value for each of your applications.
The value defaults to /Shibboleth.sso, and should be a relative path, with the SP computing
a relative value based on the virtual host. Using handlerSSL="true", the default, will force
the protocol to be https. You should also set cookieProps to "https" for SSL-only sites.
Note that while we default checkAddress to "false", this has a negative impact on the
security of your site. Stealing sessions via cookie theft is much easier with this disabled.
-->
<Sessions lifetime="28800" timeout="3600" relayState="ss:mem"
checkAddress="false" handlerSSL="true" cookieProps="https">
<!--
Configures SSO for a default IdP. To allow for >1 IdP, remove
entityID property and adjust discoveryURL to point to discovery service.
If you use this, you have to remove the SessionInitiator below.
<SSO entityID="https://swamididp.example.org
discoveryProtocol="SAMLDS" discoveryURL="https://ds.example.org/DS/WAYF">
SAML2 SAML1
</SSO>
-->
<!-- SAML and local-only logout. -->
<Logout>SAML2 Local</Logout>
<SessionInitiator type="Chaining" Location="/DS/Login" id="swamid-ds-default" relayState="cookie">
<SessionInitiator type="SAML2" defaultACSIndex="1" acsByIndex="false" template="bindingTemplate.html"/>
<SessionInitiator type="Shib1" defaultACSIndex="5"/>
<%- if @swamid_testing -%>
<SessionInitiator type="SAMLDS" URL="https://ds-test.swamid.se/role/idp.ds"/>
<%- else -%>
<SessionInitiator type="SAMLDS" URL="https://service.seamlessaccess.org/ds"/>
<%- end -%>
</SessionInitiator>
<!--
md:AssertionConsumerService locations handle specific SSO protocol bindings,
such as SAML 2.0 POST or SAML 1.1 Artifact. The isDefault and index attributes
are used when sessions are initiated to determine how to tell the IdP where and
how to return the response.
-->
<md:AssertionConsumerService Location="/SAML2/POST" index="1"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
conf:ignoreNoPassive="true"/>
<!-- Extension service that generates "approximate" metadata based on SP configuration. -->
<Handler type="MetadataGenerator" Location="/Metadata" signing="false"/>
<!-- Status reporting service. -->
<Handler type="Status" Location="/Status" acl="127.0.0.1 ::1"/>
<!-- Session diagnostic service. -->
<Handler type="Session" Location="/Session" showAttributeValues="false"/>
<!-- JSON feed of discovery information. -->
<Handler type="DiscoveryFeed" Location="/DiscoFeed"/>
<!--
md:ArtifactResolutionService locations resolve artifacts issued when using the
SAML 2.0 HTTP-Artifact binding on outgoing messages, generally uses SOAP.
-->
<md:ArtifactResolutionService Location="/Artifact/SOAP" index="1"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"/>
</Sessions>
<!--
Allows overriding of error template information/filenames. You can
also add attributes with values that can be plugged into the templates.
-->
<Errors supportContact="<%= @email %>"
helpLocation="/about.html"
styleSheet="/shibboleth-sp/main.css"/>
<!-- Example of remotely supplied batch of signed metadata. -->
<!-- SWAMID Metadata -->
<%- if @swamid_testing -%>
<MetadataProvider
type="XML"
url="https://mds.swamid.se/md/swamid-testing-idp-1.0.xml"
backingFilePath="swamid-testing-idp-1.0.xml" reloadInterval="300">
<MetadataFilter type="Signature" certificate="md-signer2.crt"/>
</MetadataProvider>
<%- elsif @satosa -%>
<MetadataProvider
type="XML"
path="frontend.xml"/>
<%- else -%>
<MetadataProvider
type="XML"
url="http://mds.swamid.se/md/swamid-idp.xml"
backingFilePath="swamid-idp.xml" reloadInterval="300">
<MetadataFilter type="Signature" certificate="md-signer2.crt"/>
</MetadataProvider>
<%- end -%>
<!-- Example of locally maintained metadata. -->
<!--
<MetadataProvider type="XML" file="partner-metadata.xml"/>
-->
<!-- Map to extract attributes from SAML assertions. -->
<AttributeExtractor type="XML" validate="true" reloadChanges="false" path="attribute-map.xml"/>
<!-- Use a SAML query if no attributes are supplied during SSO. -->
<AttributeResolver type="Query" subjectMatch="true"/>
<!-- Default filtering policy for recognized attributes, lets other data pass. -->
<AttributeFilter type="XML" validate="true" path="attribute-policy.xml"/>
<!-- Simple file-based resolver for using a single keypair. -->
<CredentialResolver type="File" key="sp-key.pem" certificate="sp-cert.pem"/>
</ApplicationDefaults>
<!-- Policies that determine how to process and authenticate runtime messages. -->
<SecurityPolicyProvider type="XML" validate="true" path="security-policy.xml"/>
<!-- Low-level configuration about protocols and bindings available for use. -->
<ProtocolProvider type="XML" validate="true" reloadChanges="false" path="protocols.xml"/>
</SPConfig>

View file

@ -0,0 +1,72 @@
# set overall behavior
log4j.rootCategory=INFO, shibd_log, warn_log
# fairly verbose for DEBUG, so generally leave at INFO
log4j.category.XMLTooling.XMLObject=INFO
log4j.category.XMLTooling.KeyInfoResolver=INFO
log4j.category.Shibboleth.IPRange=INFO
log4j.category.Shibboleth.PropertySet=INFO
# raise for low-level tracing of SOAP client HTTP/SSL behavior
log4j.category.XMLTooling.libcurl=INFO
# useful categories to tune independently:
#
# tracing of SAML messages and security policies
#log4j.category.OpenSAML.MessageDecoder=DEBUG
#log4j.category.OpenSAML.MessageEncoder=DEBUG
#log4j.category.OpenSAML.SecurityPolicyRule=DEBUG
#log4j.category.XMLTooling.SOAPClient=DEBUG
# interprocess message remoting
#log4j.category.Shibboleth.Listener=DEBUG
# mapping of requests to applicationId
#log4j.category.Shibboleth.RequestMapper=DEBUG
# high level session cache operations
#log4j.category.Shibboleth.SessionCache=DEBUG
# persistent storage and caching
#log4j.category.XMLTooling.StorageService=DEBUG
# logs XML being signed or verified if set to DEBUG
log4j.category.XMLTooling.Signature.Debugger=INFO, sig_log
log4j.additivity.XMLTooling.Signature.Debugger=false
log4j.ownAppenders.XMLTooling.Signature.Debugger=true
# the tran log blocks the "default" appender(s) at runtime
# Level should be left at INFO for this category
log4j.category.Shibboleth-TRANSACTION=<%= @translog %>, tran_log
log4j.additivity.Shibboleth-TRANSACTION=false
log4j.ownAppenders.Shibboleth-TRANSACTION=true
# uncomment to suppress particular event types
#log4j.category.Shibboleth-TRANSACTION.AuthnRequest=WARN
#log4j.category.Shibboleth-TRANSACTION.Login=WARN
#log4j.category.Shibboleth-TRANSACTION.Logout=WARN
# define the appenders
log4j.appender.shibd_log=org.apache.log4j.RollingFileAppender
log4j.appender.shibd_log.fileName=/var/log/shibboleth/shibd.log
log4j.appender.shibd_log.maxFileSize=1000000
log4j.appender.shibd_log.maxBackupIndex=10
log4j.appender.shibd_log.layout=org.apache.log4j.PatternLayout
log4j.appender.shibd_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %x: %m%n
log4j.appender.warn_log=org.apache.log4j.RollingFileAppender
log4j.appender.warn_log.fileName=/var/log/shibboleth/shibd_warn.log
log4j.appender.warn_log.maxFileSize=1000000
log4j.appender.warn_log.maxBackupIndex=10
log4j.appender.warn_log.layout=org.apache.log4j.PatternLayout
log4j.appender.warn_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %x: %m%n
log4j.appender.warn_log.threshold=WARN
log4j.appender.tran_log=org.apache.log4j.RollingFileAppender
log4j.appender.tran_log.fileName=/var/log/shibboleth/transaction.log
log4j.appender.tran_log.maxFileSize=1000000
log4j.appender.tran_log.maxBackupIndex=20
log4j.appender.tran_log.layout=org.apache.log4j.PatternLayout
log4j.appender.tran_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S}|%c|%m%n
log4j.appender.sig_log=org.apache.log4j.FileAppender
log4j.appender.sig_log.fileName=/var/log/shibboleth/signature.log
log4j.appender.sig_log.layout=org.apache.log4j.PatternLayout
log4j.appender.sig_log.layout.ConversionPattern=%m