Skip to content
Open
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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ Dockerfile
README.md
deploy.sh
spec
.bundle/
vendor/bundle/
*.gem
# Ignore your local lockfile so the container generates its own if needed
Gemfile.lock
77 changes: 66 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,29 +1,84 @@
ARG BASE_IMAGE=ruby:2.7-alpine
ARG APP_ROOT=/app
ARG BUNDLE_DIR=/usr/local/bundle

FROM $BASE_IMAGE AS build-env

ARG APP_ROOT
ARG BUNDLE_DIR

ENV BUNDLE_PATH=$BUNDLE_DIR
ENV BUNDLE_BIN=$BUNDLE_DIR/bin
ENV PATH="${BUNDLE_BIN}:${PATH}"

WORKDIR $APP_ROOT
RUN apk update && apk upgrade && \
apk add build-base && \
rm -rf /var/cache/apk/*

# 1. Install dependencies needed to compile native extensions (Nokogiri)
RUN apk update && apk add --no-cache \
build-base \
libxml2-dev \
libxslt-dev \
xz-dev \
zlib-dev

# 2. Update rubygems and bundler for better compatibility with modern gems
RUN gem update --system 3.4.22 && gem install bundler -v 2.4.22

# 3. Install Gems
COPY Gemfile* ./
RUN bundle install --without=development test

# 4. Ensure lock + install resolve the same way
RUN bundle config set --global path "$BUNDLE_PATH" && \
bundle config set --global without "development test" && \
bundle config set --global force_ruby_platform true && \
bundle lock --add-platform ruby && \
bundle lock --remove-platform aarch64-linux x86_64-linux aarch64-linux-musl x86_64-linux-musl || true && \
bundle install && \
rm -rf .bundle/config && \
rm -rf $BUNDLE_PATH/cache/*.gem


FROM $BASE_IMAGE AS final

ARG APP_ROOT
ARG BUNDLE_DIR
ARG BUILD_DATE
ARG SOURCE
ARG REVISION
ARG BUNDLE_DIR=/usr/local/bundle

ENV BUNDLE_PATH=$BUNDLE_DIR
ENV BUNDLE_BIN=$BUNDLE_DIR/bin
ENV BUNDLE_WITHOUT="development:test"
ENV BUNDLE_FORCE_RUBY_PLATFORM=true
ENV BUNDLE_IGNORE_CONFIG=true
ENV BUNDLE_FROZEN=true
ENV GEM_HOME=$BUNDLE_DIR
ENV PATH="${BUNDLE_BIN}:${PATH}"
ENV RACK_ENV=production
ENV PORT=9292

WORKDIR $APP_ROOT

# 1. Install RUNTIME dependencies (needed to actually run the compiled gems)
RUN apk update && apk add --no-cache libxml2 libxslt libstdc++

# 2. Copy the gems from the builder
COPY --from=build-env $BUNDLE_PATH $BUNDLE_PATH

# 3. Ensure the lockfile generated in build-env is the one we use
COPY --from=build-env $APP_ROOT/Gemfile.lock ./Gemfile.lock

# 4. Copy the application code
COPY . .

# 5. Tell Bundler specifically to use the global path and ignore local configs
RUN bundle config set --local path $BUNDLE_PATH && \
bundle config set --local without 'development test'

CMD ["bundle", "exec", "puma"]

LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.title="SamlProxy"
LABEL org.opencontainers.image.created="$BUILD_DATE"
LABEL org.opencontainers.image.source="$SOURCE"
LABEL org.opencontainers.image.revision="$REVISION"
WORKDIR $APP_ROOT
COPY --from=build-env $BUNDLE_DIR $BUNDLE_DIR
COPY . .
ENV RACK_ENV=production
ENV PORT=9292
CMD bundle exec puma
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ gem 'puma', '~> 6.0'
gem 'ruby-saml', '~> 1.14'
gem 'sinatra', '~> 3.0'
gem 'sinatra-contrib', '~> 3.0'
gem 'base64', '~> 0.3.0'

group :development do
gem 'overcommit', '~> 0.59.1'
Expand Down
4 changes: 3 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ GEM
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
ast (2.4.2)
base64 (0.3.0)
childprocess (4.1.0)
coderay (1.1.3)
crack (0.4.5)
Expand Down Expand Up @@ -107,6 +108,7 @@ PLATFORMS
ruby

DEPENDENCIES
base64 (~> 0.3.0)
overcommit (~> 0.59.1)
pry (~> 0.14.1)
puma (~> 6.0)
Expand All @@ -122,4 +124,4 @@ DEPENDENCIES
webmock (~> 3.18)

BUNDLED WITH
2.3.16
2.3.7
11 changes: 10 additions & 1 deletion config/mappings.yaml.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,14 @@ mappings:
production:
<% ENV.each do |key, value| %>
<% next unless key.start_with?('SAML_MAPPINGS_') %>
<%= key.delete_prefix('SAML_MAPPINGS_').downcase %>: "<%= ENV[key] %>"
<%= key.delete_prefix('SAML_MAPPINGS_').downcase %>: "<%= value %>"
<% end %>

groups:
priority: '<%= ENV['SAML_GROUP_PRIORITY'] %>'
attribute: '<%= ENV['SAML_GROUP_ATTRIBUTE'] %>'
mappings:
<% ENV.each do |key, value| %>
<% next unless key.start_with?('SAML_GROUP_MAPPINGS_') %>
<%= key.delete_prefix('SAML_GROUP_MAPPINGS_') %>: "<%= value %>"
<% end %>
41 changes: 38 additions & 3 deletions saml_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,50 @@ def saml_settings
end

def valid?(saml_response)
saml_response.is_valid? &&
Rack::Utils.secure_compare(session[:csrf], params[:RelayState])
csrf = session[:csrf]
relay = params[:RelayState]

return false if csrf.nil? || relay.nil?

saml_response.is_valid? && Rack::Utils.secure_compare(csrf, relay)
end

def parse_priority(value)
case value
when Array
value.map(&:to_s)
when String
# Accept comma, pipe, or whitespace separated lists
value.split(/[,\|\s]+/)
else
[]
end.map(&:strip).reject(&:empty?)
end

def update_session(saml_response)
session[:authed] = true
session[:mappings] = {}

# Configurable priority order (array of role names)
role_priority = parse_priority(settings.groups[:priority])

settings.mappings.each do |attr, header|
session[:mappings][header] = saml_response.attributes[attr]
if attr == settings.groups[:attribute]
roles = saml_response.attributes
.multi(attr)
.map { |group_id| settings.groups[:mappings][group_id] }
.compact
.map(&:to_s)
.map(&:strip)
.reject(&:empty?)
.uniq

selected = role_priority.find { |r| roles.include?(r) } || roles.first
halt 401 unless selected
session[:mappings][header] = selected.to_s
else
session[:mappings][header] = saml_response.attributes[attr]
end
end
end
end