Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

Commit

Permalink
Merge pull request #40 from ahayden/alb
Browse files Browse the repository at this point in the history
Local reverse proxy to internal service
  • Loading branch information
ahayden authored Jul 1, 2020
2 parents 38756d1 + 164bbf9 commit d0bf9a2
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 8 deletions.
59 changes: 59 additions & 0 deletions src/access.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python

import jwt
import requests
import base64
import json
import boto3

from mod_python import apache

region = 'us-east-1'

def headerparserhandler(req):

jwt = req.headers_in['x-amzn-oidc-data'] #proxy.conf ensures this header exists

if session_user(jwt) == approved_user():
return apache.OK
else:
return apache.HTTP_UNAUTHORIZED #the userid claim does not match the userid tag

def approved_user():
meta = requests.get('http://169.254.169.254/latest/meta-data/instance-id')
instance_id = meta.text

ec2 = boto3.resource('ec2',region)
vm = ec2.Instance(instance_id)

#TODO handle exception on multiple tags in this list
for tags in vm.tags:
if tags["Key"] == 'Protected/AccessApprovedCaller':
approved_caller = tags["Value"]

return approved_caller.split(':')[1] #return userid portion of tag

def session_user(encoded_jwt):

# The x-amzn-oid-data header is a base64-encoded JWT signed by the ALB
# validating the signature of the JWT means the payload is authentic
# per http://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html
# Step 1: Get the key id from JWT headers (the kid field)
#encoded_jwt = headers.dict['x-amzn-oidc-data']
jwt_headers = encoded_jwt.split('.')[0]

decoded_jwt_headers = base64.b64decode(jwt_headers)
decoded_jwt_headers = decoded_jwt_headers.decode("utf-8")
decoded_json = json.loads(decoded_jwt_headers)
kid = decoded_json['kid']

# Step 2: Get the public key from regional endpoint
url = 'https://public-keys.auth.elb.' + region + '.amazonaws.com/' + kid
req = requests.get(url)
pub_key = req.text

# Step 3: Get the payload
payload = jwt.decode(encoded_jwt, pub_key, algorithms=['ES256'])
#TODO handle validation errors, this call validates the signature

return payload['userid']
43 changes: 35 additions & 8 deletions src/playbook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,48 @@
shell: "aptdcon --add-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu {{ ansible_distribution_release }}-cran35/' && aptdcon --refresh"

- name: install packages
shell: "yes | aptdcon --hide-terminal --install 'zlib1g-dev libclang-dev libssl-dev libffi-dev libcurl4-openssl-dev libapparmor1 libssl1.0.0 r-base r-base-dev libxml2-dev'"
shell: "yes | aptdcon --hide-terminal --install 'zlib1g-dev libclang-dev libssl-dev libffi-dev libcurl4-openssl-dev libapparmor1 libssl1.0.0 r-base r-base-dev libxml2-dev apache2 ssl-cert libapache2-mod-python python3-boto3'"

- name: Download RStudio Server
get_url: url=https://download2.rstudio.org/server/bionic/amd64/rstudio-server-1.2.5033-amd64.deb dest=/tmp/rstudio.deb

- name: Install RStudio Server
command: dpkg -i /tmp/rstudio.deb

- name: Overwrite rstudio web config
copy:
dest: /etc/rstudio/rserver.conf
content: |
www-address=127.0.0.1 #Only serve on internal interface
www-port=8787
- name: Replace rstudio-server service with no auth
copy:
src: rstudio-server.service
dest: /etc/systemd/system/

- name: Add JWT and instance tag verifing script
copy:
src: access.py
dest: /usr/lib/cgi-bin/access.py
owner: www-data
group: www-data
mode: 0755

- name: Add config for local rev proxy to internal port
copy:
src: proxy.conf
dest: /etc/apache2/sites-available/proxy.conf

- name: Enable modules
command: a2enmod ssl proxy proxy_http rewrite python headers

- name: Enable proxy site
command: a2ensite proxy

- name: Disable default
command: a2dissite 000-default

# Install essential R packages
- name: Install synapser
shell: "R -e \"install.packages('synapser', repos=c('http://ran.synapse.org', 'http://cran.fhcrc.org'))\""
Expand All @@ -36,10 +70,3 @@

- name: Install BiocManager
shell: "R -e \"install.packages('BiocManager')\""

# reticulate has python path issues
# - name: Install reticulate
# shell: "R -e \"install.packages('reticulate')\""

# - pip:
# name: synapseclient
18 changes: 18 additions & 0 deletions src/proxy.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<VirtualHost _default_:443>

SSLEngine On
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

ProxyRequests Off
ProxyPreserveHost On

<LocationMatch /EC2_INSTANCE_ID/>
AddHandler mod_python .py
PythonPath "sys.path+['/usr/lib/cgi-bin', 'usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages']"
PythonHandler access
PythonHeaderParserHandler access
ProxyPass http://localhost:8787/
ProxyPassReverse http://localhost:8787/$0
</LocationMatch>
</VirtualHost>
16 changes: 16 additions & 0 deletions src/rstudio-server.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[Unit]
Description=RStudio Server
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/rstudio-server.pid
User=ubuntu
ExecStart=/usr/lib/rstudio-server/bin/rserver --auth-none 1
ExecStop=/usr/bin/killall -TERM rserver
KillMode=none
Restart=on-failure

[Install]
WantedBy=multi-user.target

0 comments on commit d0bf9a2

Please sign in to comment.