Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions modules/weko-accounts/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,38 @@ def test_shib_login(client,redis_connect,users,mocker):
assert "Server error has occurred. Please contact server " \
"administrator." in called_kwargs.get("ams_error", "")

# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_sp_login_source -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-accounts/.tox/c1/tmp
def test_shib_sp_login_source(app, client, mocker):
"""The attributes of the SP are accepted only from the allowed addresses."""
url = url_for("weko_accounts.shib_sp_login")
mock_parse = mocker.patch("weko_accounts.views.parse_attributes",
return_value=({}, True))
data = {"Shib-Session-ID": "dummy"}

# other addresses are rejected before the attributes are read
res = client.post(url, data=data,
environ_base={"REMOTE_ADDR": "203.0.113.10"})
assert res.status_code == 403
mock_parse.assert_not_called()

# the loopback address is accepted (the default of the test client)
client.post(url, data=data, environ_base={"REMOTE_ADDR": "127.0.0.1"})
assert mock_parse.called

# the allowed addresses follow the configuration
mock_parse.reset_mock()
app.config["WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS"] = ["203.0.113.10"]
try:
client.post(url, data=data,
environ_base={"REMOTE_ADDR": "203.0.113.10"})
assert mock_parse.called
res = client.post(url, data=data,
environ_base={"REMOTE_ADDR": "127.0.0.1"})
assert res.status_code == 403
finally:
app.config["WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS"] = ["127.0.0.1", "::1"]


#def shib_sp_login():
# .tox/c1/bin/pytest --cov=weko_accounts tests/test_views.py::test_shib_sp_login -vv -s --cov-branch --cov-report=term --basetemp=/code/modules/weko-workflow/.tox/c1/tmp
def test_shib_sp_login(client, redis_connect,mocker, db, users):
Expand Down
8 changes: 8 additions & 0 deletions modules/weko-accounts/weko_accounts/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
WEKO_ACCOUNTS_SHIB_LOGIN_ENABLED = False
"""Enable Shibboleth user login system."""

WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS = ['127.0.0.1', '::1']
"""Addresses allowed to post the attributes of the Shibboleth SP.

The attributes are posted by the login script of the SP (nginx/login.py),
which runs on the web server and posts to the loopback address. The address
is the one nginx passes as REMOTE_ADDR.
"""

WEKO_ACCOUNTS_SHIB_CACHE_PREFIX = 'Shib-Session-'
"""Shibboleth cache prefix info."""

Expand Down
21 changes: 21 additions & 0 deletions modules/weko-accounts/weko_accounts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,27 @@ def generate_random_str(length=128):
)


def shib_sp_source_required(f):
"""Accept the request only from the addresses of the Shibboleth SP.

The attributes of the IdP are taken from the request itself, so they must
come only from the login script of the SP. The address is checked against
``WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS``, and any other request is rejected
with 403.
"""
@wraps(f)
def decorated(*args, **kwargs):
allowed = current_app.config.get(
'WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS', [])
if request.remote_addr not in allowed:
current_app.logger.warning(
'Shibboleth SP attributes from a disallowed address: {}'.format(
request.remote_addr))
abort(403)
return f(*args, **kwargs)
return decorated


def parse_attributes():
"""Parse arguments from environment variables."""
attrs = {}
Expand Down
4 changes: 3 additions & 1 deletion modules/weko-accounts/weko_accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
from weko_logging.activity_logger import UserActivityLogger

from .api import ShibUser, sync_shib_gakunin_map_groups
from .utils import generate_random_str, parse_attributes
from .utils import generate_random_str, parse_attributes, \
shib_sp_source_required


_app = LocalProxy(lambda: current_app.extensions['weko-admin'].app)
Expand Down Expand Up @@ -544,6 +545,7 @@ def find_user_by_email(shib_attributes):
return user

@blueprint.route('/shib/login', methods=['POST'])
@shib_sp_source_required
def shib_sp_login():
"""The request from shibboleth sp.

Expand Down
6 changes: 5 additions & 1 deletion nginx/login.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
}else{
$next='%2F';
}
$url = $base."/weko/shib/login?next=".$next;
// Post to the loopback address. WEKO accepts the attributes only from the
// addresses in WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS, and nginx allows the
// POST to /weko/shib/login only from the loopback address.
$url = $_SERVER['REQUEST_SCHEME']."://127.0.0.1/weko/shib/login?next=".$next;
$curl = curl_init();
$post_args=[];
$post_args['SHIB_ATTR_USER_NAME']=$_SERVER['HTTP_WEKOID'];
Expand All @@ -32,6 +35,7 @@
$cookie=tempnam(sys_get_temp_dir(),'cookie_');
//set options
curl_setopt($curl,CURLOPT_URL,$url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Host: '.$_SERVER['SERVER_NAME']));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
Expand Down
10 changes: 8 additions & 2 deletions nginx/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
next = qs['next'][0]
else:
next = '%2F'
url = base_url + '/weko/shib/login?next=' + next
# Post to the loopback address. WEKO accepts the attributes only from the
# addresses in WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS, and nginx allows the
# POST to /weko/shib/login only from the loopback address.
url = os.environ.get('REQUEST_SCHEME') + '://127.0.0.1' \
+ '/weko/shib/login?next=' + next
headers = {'Host': os.environ.get('HTTP_HOST')}

# Get the fastcgi_params
fastcgi_params = []
Expand All @@ -47,7 +52,8 @@
cookie_jar = cookiejar.LWPCookieJar(temp_path)

# Request to the Shibboleth login
response = requests.post(url, data=data, verify=False, cookies=cookie_jar)
response = requests.post(url, data=data, headers=headers, verify=False,
cookies=cookie_jar)
response.raise_for_status()
redirect = response.text

Expand Down
35 changes: 35 additions & 0 deletions nginx/weko-ams-restricted.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ map $request_uri $new {
include /etc/nginx/redirect_list.map;
}

# POST to /weko/shib/login is allowed only from the loopback address of the
# connection (see location = /weko/shib/login).
map "$request_method:$realip_remote_addr" $weko_shib_sp_denied {
default 1;
"~^(GET|HEAD):" 0;
"POST:127.0.0.1" 0;
"POST:::1" 0;
}

server {
listen 80;
return 301 https://$host$request_uri;
Expand Down Expand Up @@ -163,6 +172,32 @@ server {
# proxy_set_header X-Forwarded-Proto $scheme;
# }

# The attributes of the Shibboleth SP are posted only by the login script
# on this server (secure/login.py, login.php), over the loopback address.
# The address is the one of the connection, before real_ip rewrites it with
# X-Forwarded-For, so that a trusted proxy address cannot be used to pose
# as the loopback address. WEKO checks the same address
# (WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS).
location = /weko/shib/login {
if ($weko_shib_sp_denied) {
return 403;
}
uwsgi_pass app_server;
include uwsgi_params;
uwsgi_param REMOTE_ADDR $realip_remote_addr;

uwsgi_buffering off;
uwsgi_request_buffering off;

uwsgi_buffer_size 32k;
uwsgi_buffers 8 32k;
uwsgi_busy_buffers_size 32k;

uwsgi_param Host $host;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $scheme;
}

location /weko/shib {
uwsgi_pass app_server;
include uwsgi_params;
Expand Down
35 changes: 35 additions & 0 deletions nginx/weko-ams.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ map $request_uri $new {
include /etc/nginx/redirect_list.map;
}

# POST to /weko/shib/login is allowed only from the loopback address of the
# connection (see location = /weko/shib/login).
map "$request_method:$realip_remote_addr" $weko_shib_sp_denied {
default 1;
"~^(GET|HEAD):" 0;
"POST:127.0.0.1" 0;
"POST:::1" 0;
}

server {
listen 80;
return 301 https://$host$request_uri;
Expand Down Expand Up @@ -183,6 +192,32 @@ server {
# proxy_set_header X-Forwarded-Proto $scheme;
# }

# The attributes of the Shibboleth SP are posted only by the login script
# on this server (secure/login.py, login.php), over the loopback address.
# The address is the one of the connection, before real_ip rewrites it with
# X-Forwarded-For, so that a trusted proxy address cannot be used to pose
# as the loopback address. WEKO checks the same address
# (WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS).
location = /weko/shib/login {
if ($weko_shib_sp_denied) {
return 403;
}
uwsgi_pass app_server;
include uwsgi_params;
uwsgi_param REMOTE_ADDR $realip_remote_addr;

uwsgi_buffering off;
uwsgi_request_buffering off;

uwsgi_buffer_size 32k;
uwsgi_buffers 8 32k;
uwsgi_busy_buffers_size 32k;

uwsgi_param Host $host;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $scheme;
}

location /weko/shib {
uwsgi_pass app_server;
include uwsgi_params;
Expand Down
35 changes: 35 additions & 0 deletions nginx/weko.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ map $request_uri $new {
include /etc/nginx/redirect_list.map;
}

# POST to /weko/shib/login is allowed only from the loopback address of the
# connection (see location = /weko/shib/login).
map "$request_method:$realip_remote_addr" $weko_shib_sp_denied {
default 1;
"~^(GET|HEAD):" 0;
"POST:127.0.0.1" 0;
"POST:::1" 0;
}

server {
listen 80;
return 301 https://$host$request_uri;
Expand Down Expand Up @@ -198,6 +207,32 @@ server {
# proxy_set_header X-Forwarded-Proto $scheme;
# }

# The attributes of the Shibboleth SP are posted only by the login script
# on this server (secure/login.py, login.php), over the loopback address.
# The address is the one of the connection, before real_ip rewrites it with
# X-Forwarded-For, so that a trusted proxy address cannot be used to pose
# as the loopback address. WEKO checks the same address
# (WEKO_ACCOUNTS_SHIB_SP_ALLOWED_ADDRS).
location = /weko/shib/login {
if ($weko_shib_sp_denied) {
return 403;
}
uwsgi_pass app_server;
include uwsgi_params;
uwsgi_param REMOTE_ADDR $realip_remote_addr;

uwsgi_buffering off;
uwsgi_request_buffering off;

uwsgi_buffer_size 32k;
uwsgi_buffers 8 32k;
uwsgi_busy_buffers_size 32k;

uwsgi_param Host $host;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $scheme;
}

location /weko/shib {
uwsgi_pass app_server;
include uwsgi_params;
Expand Down
Loading