Dear all,
Is it possible to use a different discovery service depending on the SP
that sent a SAML AuthnRequest to SATOSA, or do I have to do that in the
discovery service's frontend somehow?
Best wishes,
Matthew
--
"The lyf so short, the craft so longe to lerne."
I want to again say "thanks" to Ioannis, Rainer, Scott, and everyone
else for their help and instruction during the various IdentityPython
and SATOSA meetings at TIIME this week. Chris Phillips and I were able
to get a SATOSA 3.4.8 deployment working in Chris's idp-installer test
bed. To that end I want to share my notes from the process, at the end
of which an interested party could perform a basic, end-to-end test of
the current SATOSA release using SAMLtest (https://samltest.id/)
1. I installed Ubuntu Server 18.04.1; run the following commands as root
to install the prerequisites:
```sh
apt update
apt dist-upgrade -y
apt install -y git python3-dev build-essential python3-pip libffi-dev
libssl-dev xmlsec1 libyaml-dev libxml2-utils
pip3 install --upgrade virtualenv
virtualenv -p python3 /opt/satosa
/opt/satosa/bin/pip install --upgrade pip setuptools
/opt/satosa/bin/pip install SATOSA
```
This is essentially the Docker image build process, only it uses the
current SATOSA release (etc.) on PyPI.
2. Copy
https://github.com/IdentityPython/SATOSA/tree/v3.4.8/docker/attributemap
s to /opt/satosa/attributemaps.
I'm not sure this is strictly necessary as the built-in pysaml2
attribute maps should be used by default, but it's what the Docker image
build process does.
3. Copy https://github.com/IdentityPython/SATOSA/tree/v3.4.8/example to
/opt/satosa/etc.
4. SATOSA doesn't have a default configuration, so you must provide it
yourself.
```sh
cp /opt/satosa/etc/proxy_conf.yaml.example \
/opt/satosa/etc/proxy_conf.yaml
cp /opt/satosa/etc/internal_attributes.yaml.example \
/opt/satosa/etc/internal_attributes.yaml
cp /opt/satosa/etc/plugins/frontends/saml2_frontend.yaml.example \
/opt/satosa/etc/plugins/frontends/saml2_frontend.yaml
cp /opt/satosa/etc/plugins/backends/saml2_backend.yaml.example \
/opt/satosa/etc/plugins/backends/saml2_backend.yaml
cp /opt/satosa/etc/plugins/microservices/static_attributes.yaml.example
\
/opt/satosa/etc/plugins/microservices/static_attributes.yaml
```
5. You may change the proxy URL (the value of BASE in
/opt/satosa/etc/proxy_conf.yaml), but it _must_ be a method plus
hostname without any trailing slash or path components, e.g.,
`https://proxy.example.com`, not `https://proxy.example.com/` nor
`https://proxy.example.com/satosa`. SATOSA must be hosted at the root
of your web site.
6. Comment out the `idp_blacklist_file` and `disco_srv` settings in
/opt/satosa/etc/plugins/backends/saml2_backend.yaml.
7. Generate IdP, SP, metadata signing, and web site keying material:
```sh
for i in frontend backend metadata https; do
openssl req -batch -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /opt/satosa/etc/$i.key -out /opt/satosa/etc/$i.crt \
-subj /CN=proxy.example.com
done
```
8. Download the SAMLtest metadata.
```sh
curl https://samltest.id/saml/sp > /opt/satosa/etc/sp.xml
curl https://samltest.id/saml/idp > /opt/satosa/etc/idp.xml
```
9. Generate the proxy metadata. (How you do this changes in future
releases of SATOSA.)
```sh
. /opt/satosa/bin/activate
cd /opt/satosa/etc
satosa-saml-metadata proxy_conf.yaml metadata.key metadata.crt
--split-frontend --split-backend --dir /opt/satosa/etc
xmllint --format /opt/satosa/etc/Saml2IDP_0.xml >
/opt/satosa/etc/proxy-idp.xml
xmllint --format /opt/satosa/etc/Saml2_0.xml >
/opt/satosa/etc/proxy-sp.xml
```
10. Edit the proxy metadata files to remove the `<ns1:Signature>`
element, else SAMLtest will be unable to load them due to an invalid
signature.
11. Upload the proxy metadata to SAMLtest
(https://samltest.id/upload.php)
12. SAMLtest doesn't release the eduPerson Targeted ID attribute, so
you'll need to change the last three lines of
/opt/satosa/etc/internal_attributes.yaml to the following (and before
anyone says anything, NEVER USE AN EMAIL ADDRESS AS AN IDENTIFIER---this
is just a quick hack to get SATOSA working):
```
hash: [mail]
user_id_from_attrs: [mail]
user_id_to_attr: mail
```
13. Start SATOSA:
```sh
. /opt/satosa/bin/activate
cd /opt/satosa/etc
gunicorn -b0.0.0.0:443 --keyfile https.key --certfile https.crt
satosa.wsgi:app
```
14. At this point you should be able to perform an IdP test
(https://samltest.id/start-idp-test/) by specifying the entity ID of the
proxy's front end, e.g., https://example.com/Saml2IDP/proxy.xml. The
SAMLtest SP will request authentication by your proxy IdP, causing your
proxy SP to request authentication by the SAMLtest IdP. If everything
works right, you will end up back at the SAMLtest SP:
SAMLtest SP ---AuthnRequest---> SATOSA front end (IdP)/back end (SP)
---AuthnRequest---> SAMLtest IdP
SAMLtest SP <---AuthnResponse--- SATOSA front end (IdP)/back end (SP)
<---AuthnResponse--- SAMLtest IdP
I hope this helps other adopters. If you have any questions, please
reply on list so everyone can benefit from the discussion.
Best wishes,
Matthew
--
"The lyf so short, the craft so longe to lerne."
Not knowing whether my satosa instance is fully working yet (see my
other thread) I'm now continuing to try to get the application
(eduMEET) to work with satosa's oidc frontend, as per the app's
published config example:
https://github.com/havfo/multiparty-meeting/blob/master/server/config/confi…
So I've made up a client_id and client_secret on the RP side and
provided the client with an issuerURL (base URL of satosa), let it
request all the scopes in the world and set its own redirect_uri.
With those all set I do see requests to satosa's .well-known endpoints
from the application in satosa logs, e.g.
Found registered endpoint: module name:'oidc', endpoint: .well-known/openid-configuration
(And of course accessing the endpoint myself I can see that it works
and produces JSON with its config.)
Now on the OP side (satosa oidc frontend) I haven't done any setup
for the client yet, so I guess the error in the log is to be expected:
Error in authn req: Unknown client_id
Now what would be the next steps to register that client?
The request from the client (according to satosa's logs) has these
query parameters (where cid and csec are the correct client_id and
client_secret, respectively):
client_id=cid&scope=openid+email+profile&response_type=code&redirect_uri=https%3A%2F%2Fexample.org%2Fauth%2Fcallback&state=e30%3D&client_secret=csec
My plugins/frontends/openid_connect_frontend.yaml looks like the
published example, essentially:
module: satosa.frontends.openid_connect.OpenIDConnectFrontend
name: oidc
config:
signing_key_path: /etc/satosa/oidc-provider.key
#db_uri: mongodb://db.example.com # optional: only support MongoDB, will default to in-memory storage if not specified
client_db_path: /etc/satosa/oidc-clients.json
provider:
client_registration_supported: True
response_types_supported: ['code', 'token', 'id_token']
subject_types_supported: ['public', 'pairwise']
scopes_supported: ['openid', 'email', 'profile']
Only that I tried to enable pretty much everything (all repose and
subject types, all scopes, client registration) since I had no idea
what the RP side wants, yet. (Seems I can remove all response types
except 'code', as per the log shown above.)
I don't have MongoDB set up yet since the comment above suggests an
in-memory store would be used, which is fine for my current testing.
And looking at _create_provider() at frontends/openid_connect.py the
code would use the file referenced by client_db_path if db_uri isn't
set even before falling back to storing it in a variable.
The file referenced in client_db_path exists, is writable by the user
satosa runs as, and currently contains only '{}' (without the quotes).
So IMO that should be sufficient.
Any hints on how to register the application?
The documenation is a bit sparse here
https://github.com/IdentityPython/SATOSA/blob/master/doc/README.md#frontend…
only mentioning that *without* dynamic client registration (which I
have enabled for now, but maybe the RP doesn't support it) I'd have to
manually create the data structures in MongoDB (or the file in
client_db_path) for my client, as per the oidc spec for Client
Registration Responses.
Could someone share a json sample to put into the file referenced by
client_db_path (if that's how it's supposed to work)?
Cheers,
-peter
Hi all,
We’ve started to use SATOSA to handle some… peculiar… vendors (mostly SaaS) and we’re liking it a lot so far!
One issue that we found I haven’t been able to figure out so I thought I’d try the list to see if we’re doing something wrong or we actually found a bug.
The flow/setup that we’re using is:
SaaS SP <-> SATOSA frontend <-> SATOSA backend <-> Shibboleth IDP.
In the AuthnRequest the SaaS SP asks to NameIDPolicy AllowCreate="true"
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified”.
The backend then sends an AuthnRequest to our IDP without any NameIDPolicy (which is fine) and by default our IDP chooses to use urn:oasis:names:tc:SAML:2.0:nameid-format:transient via Subject>NameID which is fine.
But then the frontend answers the SaaS SP with a urn:oasis:names:tc:SAML:2.0:nameid-format:transient via Subject>NameID which I think is weird and wrong. I mean the SaaS SP requested unspecified, shouldn’t that be what SATOSA answers with?
A work around that I have tried is setting config.sp_config.service.sp.name_id_policy_format: urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified AND making the Shibboleth IDP to prefer unspecified.
This makes the response from frontend to SaaS SP use unspecified like SaaS SP requested.
I don’t think this should be necessary. Am I wrong?
BR,
- Simon
Dear all,
You can find updated images on Docker Hub at https://hub.docker.com/_/satosa.
The biggest change I made had to do with supported platforms. I have explicitly dropped support for anything that isn't Linux on amd64/armv8. Unfortunately, I cannot figure out how to build images on other hardware architectures. The issue has to do with building the cryptography package (https://cryptography.io/) when a binary distribution (wheel) isn't available, which requires a working Rust toolchain on the target platform.
The second change I made had to do with the default configuration. With the demise of SAMLTest.id <http://samltest.id/>, I can no longer ship a working demo in the default configuration. I have some ideas as to potential workarounds, but for now, the default configuration is much less sophisticated than before. Of course, existing configurations will not be affected by this, only new from-scratch deployments.
We have one remaining issue on the container image repo, https://github.com/IdentityPython/satosa-docker/issues/7. I'm not sure how to address this one as it seems more properly an issue with SATOSA itself. I'd appreciate the community's guidance.
My future plans for the container image include CI improvements, starting with pre-commit hooks that invoke hadolint. If you all have any ideas, I'm all ears!
Best wishes,
Matthew
--
"The reason that ed is the standard editor is to remind you that
things could be worse, and once were." - Tim Lavoie in comp.lang.lisp
TL;DR: How can I get SaToSa to select one attribute's value from a
list of attribute names, not all of which may be present?
Ivan's recommendation from PR 222 doesn't work for me:
https://github.com/IdentityPython/SATOSA/pull/222#issuecomment-533238061
Context: I'm trying to use SaToSa as a SAML SP registered in a
saml2int identity federation (SAML2 backend module plugin) protecting
a bunch of OIDC RPs (openid connect frontend module plugin).
In the field not all SAML IDPs support all of the common identifier
attributes: Some may only support e.g. eduPersonPrincipalName while
others will also (or in an hypothetical future maybe: only) support
the "new" SAML standard identifier attributes Subject-ID and/or
Pairwise-ID. (And while I could hard-code 'eppn' as the lowest common
denominator for everyone that would nullify any benefits the new
standard identifier attributes might offer.)
FWIW, the Shibboleth SP calls its own support for this the REMOTE_USER
precedence list: The first attribute from a list of given attribute
names that has a value is chosen as the value for REMOTE_USER.
Implementing Ivan's recommendation from PR 222 (link above) doesn't
work for me, though. This fails:
attributes:
id:
openid: [sub]
saml: [subject-id, pairwise-id, eppn]
eppn:
openid: [eppn]
saml: [eduPersonPrincipalName]
user_id_from_attrs: [id]
The relevant log lines being:
skipped backend attribute ['subject-id', 'pairwise-id', 'eppn']: no value found
backend attribute ['eduPersonPrincipalName'] mapped to eppn (['foo(a)example.org'])
...
KeyError: 'id'
[ERROR] [satosa.proxy_server.__call__] Unknown error
I.e., eppn is available/mapped but generating the userid from the 'id'
attribute fails, seemingly because there is no 'id' attribute because
according to SaToSa the list ['eppn', 'subject-id', 'pairwise-id'] has
"no value found".
The available behaviour is unhelpful in two regards, AFAICT:
https://github.com/IdentityPython/SATOSA/pull/222#issuecomment-533240357
* It implements "all those attributes need to have a value (what
currently happens)" (Ivan's words from PR 222) when I've only ever
needed "at least one of these attributes needs to have a value" in
over a decade of running federations and federated services.
* The attributes I care about and would list as possible attribute
names are also *not* mutually exclusive, i.e., there may be none
(failure), some or all of them available from an IDP. In any case
I'd want the user id to only ever have the value of **one** such
attribute at maximum, even if multiple attributes may have values.
TBH, I can't see how the documented behaviour:
https://github.com/IdentityPython/SATOSA/tree/master/doc#user_id_from_attrs
> The attribute values of the attributes specified in this list will
> be concatenated and used as the subject identifier.
could ever be useful to anyone? What good would be the concatenation
of multiple attributes' values (e.g. ['subject-id', 'pairwise-id',
'eppn']) provided several of them were available?
"foobar@example.orgFOO987654321@example.orgFOO@example.org"
Whereas from my experience it's often necessary to support multiple
"acceptable alternatives" (different attributes for essentially the
same purpose/data) and therefore desireble to have the software pick
*one* attribute's value(s) from an ordered-by-preference list.
E.g. given ['subject-id', 'pairwise-id', 'eppn'] that would try
subject-id, then pairwise-id, then eppn and the first one that has a
value would be the one attribute whose values we'd return.
(I'd fail if none of the above were present/had a value.)
Is there a way to get that behaviour? I'm probably biased but to me that
would make a much saner default behaviour than what's available now.
Best regards,
-peter
I am writing to request assistance with releasing a custom attribute that is not listed as a requested attribute by one of our InCommon Service Providers (SPs). The attribute is present in the IDP response, but it appears that it is not being returned to the InCommon SP.
I have attempted to set the attribute as default for all SPs, but I would like to limit its release to specific SP if needed.
I have made some changes to the configuration files and observed the following log messages:
Filter: ['edupersonprincipalname', 'edupersonaffiliation', 'givenname', 'edupersonscopedaffiliation', 'mail', 'edupersontargetedid']
Attribute Statement: <ns0:AttributeStatement xmlns:ns0="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ns0:Attribute FriendlyName="mail" Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<ns0:Attribute FriendlyName="abcID" Name="urn:oid:1.3.6.1.4.18941.1.2.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns0:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">8989898</ns0:AttributeValue></ns0:Attribute>
<ns0:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">99999</ns0:NameID></ns0:AttributeValue></ns0:Attribute></ns0:AttributeStatement>
Unknown attribute name: <ns0:Attribute xmlns:ns0="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" FriendlyName="abcID" Name="urn:oid:1.3.6.1.4.18941.1.2.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns0:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">8989898</ns0:AttributeValue></ns0:Attribute>
skipped backend attribute ['abcID']: no value found
I have made the following changes to the configuration files:
Internal_attributes.yaml
Added
abcid:
saml: [abcID]
Backend.yaml
Added
custom_attribute_release:
"default":
include: ["abcID"]
allow_unknown_attributes: true
attribute_map_dir: [/etc/satosa/attributemaps]
Frontend.yaml
-No specific changes made
Saml_url.py
From area:
'urn:oid:1.3.6.1.4.18941.1.2.1.1': 'abcID',
To area:
'abcID': 'urn:oid:1.3.6.1.4.18941.1.2.1.1',
Could you please help me troubleshoot this issue and provide guidance on how to release the custom attribute as needed?
Also how could I release this attribute as the NameID for just the specific SP that requires it?
When configuring a saml2 frontend or backend plugin with remote
metadata (url, cert) will that metadata be automatically regularly
refreshed from the configured url?
If yes, is the purpose of the metadata reload endpoint (which can be
enabled with enable_metadata_reload) solely to reload that metadata
immediately instead of automatically refreshing it later?
(I.e., nice to have but not essential.)
If no, does that mean I'll always have to set enable_metadata_reload
to a truthy value *and* protect that URL with web server ACLs (to
avoid having my service DoS'ed remotely by triggering reload of remote
metadata) *and* have to implement a cronjob or systemd timer that
regularly accesses the metadata reload endpoint?
I don't find anything that would trigger automatic metadata refresh in
satosa, so I guess that must come from pysaml2. There's a statement
in pysaml2/docs/howto/config.rst about metadata refresh when using MDQ
but nothing about aggregates (metadata.remote.url).
I see a reload() function defined in pysaml2/src/saml2/mdstore.py but
I basically only see that being used in saml2.Entity and the changelog
for 7.1.1 says "This method is to be externally invoked".
Any pointers?
-peter
I have deployed Satosa with a saml2 backend (SP) plugin loading
multiple IDPs via remote metadata and oidc "frontend" (OP) plugin with
currently one configured RP (client_db_path).
Now it seems the ressource/application to be protected (using OIDC)
may be more of a multi-tenant system, meaning there are separate
application instances (to be connected to the OIDC OP side), exactly
one per SAML IDP hooked up to Satosa on the saml2 backend side. I.e.,
each SAML IDP has its own OIDC RP.
So a given application instance / OIDC RP would send an OIDC authn req
to Satosa as OP (single frontend, configured and shared across all RPs)
and that would invoke a single saml2 backend to send SAML authn reqs
to the various IDPs known via remote metadata.
It seems to me that a single Satosa instance with a single oidc
frontend and a single saml2 backend should be able to handle this just
fine, by merely adding all applications as individual OIDC RPs (and
all IDPs to the remote metadata)? There's nothing "multi-tenanty"
about such a setup, yet: Just multiple OIDC RPs and SAML IDPs sharing
a single protocol converting proxy.
I think the next piece missing then would be avoiding SAML IDP
Disovery, since this information could already be derived from each
application/RP/client_id, so asking the subject should be avoided.
Would anyone have a suggestion on how to achieve this? I can assign
client_id values as needed so with carefully chosen values (for
automated mapping probably string/regex comparison, for
manual/explicit/static mapping a dictionary object) it should be
possible to get from OIDC RP (client_id) to SAML IDP (entityID)
without having to involve the subject?
Best regards,
-peter
Can I (pre-)select a single/specific IDP to use with a saml2 backend
plugin even if that backend loads multiple IDPs from remote metadata?
The documentation[1] seems to say that I can't avoid IDP Discovery
whenever multiple IDPs are available:
> To allow the user to choose which target provider they want to
> authenticate with, the configuration parameter disco_srv, must be
> specified if the metadata given to the backend module contains more
> than one IdP.
(Why load multiple IDPs from remote metadata then when you want to
avoid IDP Discovery? Because those IDPs may be available in remotely
managed and regularly re-signed metadata. And there's no MDQ service
available to hand out an up-to-date, signed copy of only a single IDP
for Satosa to consume.)
Related to this question: Setting the 'idp' configuration parameter in
service -> sp within the saml2 backend plugin (as shown in the pysaml2
docs[2]) resulted in no observable change for Satosa (running 8.4.0
from the official docker image). Is that to be expected? Other
parameters from pysaml2 are included in Satosa's saml2 backend plugin
at the same level as the 'idp' parameter,
e.g. 'authn_requests_signed'. But maybe I'm imagining a relationship
here between Satosa and pysaml2 config parameters that doesn't exist?
```yaml```
module: satosa.backends.saml2.SAMLBackend
name: sp
config:
sp_config:
service:
sp:
idp: ['https://idp.example.org/entity']
```
Anyway, here's the behaviour I (don't) see:
With exactly one IDP as list member (as in the YAML above and also the
pysaml2 docs[2]) this did not circumvent IDP Discovery as I had
expected and would have been useful: With only a single IDP allowed
there's no point in offering other IDPs to the subject.
(See above for why it may be useful to have multiple IDPs known via
metadata but still circumvent IDP discovery by configuration.)
But also when configuring one IDP (still as per above) and picking
*another* IDP during IDP Discovery access to the configured RP (oidc
frontend) was granted just the same. I.e., I could not determine how
the selected IDP was somehow "not allowed to be used" (as per the
pysaml2 docs, not Satosa's).
So unless I'm Doing Things Wrong™ it seems there's no use being made
of the 'idp' parameter within the SAML SP config (saml2 backend
plugin) within Satosa? If so, could or should there be?
Best,
-peter
[1] https://github.com/IdentityPython/SATOSA/blob/master/doc/README.md#use-a-di…
[2] https://pysaml2.readthedocs.io/en/latest/howto/config.html#idp