Commit 477ba2b3 authored by Krasimir Angelov's avatar Krasimir Angelov

Add skeleton Pages internal API

Basic `/internal/pages` endpoint that will be used for Pages virtual
domains internal API. The endpoint is currently behind feature flag and
provides authetication similar to how Workhorse is authenticating with
the GitLab.
parent 9d38778f
......@@ -65,6 +65,7 @@ eslint-report.html
/vendor/gitaly-ruby
/builds*
/.gitlab_workhorse_secret
/.gitlab_pages_shared_secret
/webpack-report/
/knapsack/
/rspec_flaky/
......
......@@ -321,6 +321,9 @@ production: &base
# external_https: ["1.1.1.1:443", "[2001::1]:443"] # If defined, enables custom domain and certificate support in GitLab Pages
admin:
address: unix:/home/git/gitlab/tmp/sockets/private/pages-admin.socket # TCP connections are supported too (e.g. tcp://host:port)
# File that contains the shared secret key for verifying access for gitlab-pages.
# Default is '.gitlab_pages_shared_secret' relative to Rails.root (i.e. root of the GitLab app).
# secret_file: /home/git/gitlab/.gitlab_pages_shared_secret
## Mattermost
## For enabling Add to Mattermost button
......
......@@ -292,6 +292,7 @@ Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pa
Settings.pages['admin'] ||= Settingslogic.new({})
Settings.pages.admin['certificate'] ||= ''
Settings.pages['secret_file'] ||= Rails.root.join('.gitlab_pages_shared_secret')
#
# Geo
......
......@@ -119,6 +119,7 @@ module API
mount ::API::GroupVariables
mount ::API::ImportGithub
mount ::API::Internal::Base
mount ::API::Internal::Pages
mount ::API::Issues
mount ::API::JobArtifacts
mount ::API::Jobs
......
# frozen_string_literal: true
module API
# Pages Internal API
module Internal
class Pages < Grape::API
before do
not_found! unless Feature.enabled?(:pages_internal_api)
authenticate_gitlab_pages_request!
end
helpers do
def authenticate_gitlab_pages_request!
unauthorized! unless Gitlab::Pages.verify_api_request(headers)
end
end
namespace 'internal' do
namespace 'pages' do
get "/" do
status :ok
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Pages
class Pages
VERSION = File.read(Rails.root.join("GITLAB_PAGES_VERSION")).strip.freeze
INTERNAL_API_REQUEST_HEADER = 'Gitlab-Pages-Api-Request'.freeze
include JwtAuthenticatable
class << self
def verify_api_request(request_headers)
decode_jwt_for_issuer('gitlab-pages', request_headers[INTERNAL_API_REQUEST_HEADER])
rescue JWT::DecodeError
false
end
def secret_path
Gitlab.config.pages.secret_file
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Pages do
let(:pages_shared_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
before do
allow(described_class).to receive(:secret).and_return(pages_shared_secret)
end
describe '.verify_api_request' do
let(:payload) { { 'iss' => 'gitlab-pages' } }
it 'returns false if fails to validate the JWT' do
encoded_token = JWT.encode(payload, 'wrongsecret', 'HS256')
headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
expect(described_class.verify_api_request(headers)).to eq(false)
end
it 'returns the decoded JWT' do
encoded_token = JWT.encode(payload, described_class.secret, 'HS256')
headers = { described_class::INTERNAL_API_REQUEST_HEADER => encoded_token }
expect(described_class.verify_api_request(headers)).to eq([{ "iss" => "gitlab-pages" }, { "alg" => "HS256" }])
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe API::Internal::Pages do
describe "GET /internal/pages" do
let(:pages_shared_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
before do
allow(Gitlab::Pages).to receive(:secret).and_return(pages_shared_secret)
end
def query_host(host, headers = {})
get api("/internal/pages"), headers: headers, params: { host: host }
end
context 'feature flag disabled' do
before do
stub_feature_flags(pages_internal_api: false)
end
it 'responds with 404 Not Found' do
query_host('pages.gitlab.io')
expect(response).to have_gitlab_http_status(404)
end
end
context 'feature flag enabled' do
context 'not authenticated' do
it 'responds with 401 Unauthorized' do
query_host('pages.gitlab.io')
expect(response).to have_gitlab_http_status(401)
end
end
context 'authenticated' do
def query_host(host)
jwt_token = JWT.encode({ 'iss' => 'gitlab-pages' }, Gitlab::Pages.secret, 'HS256')
headers = { Gitlab::Pages::INTERNAL_API_REQUEST_HEADER => jwt_token }
super(host, headers)
end
it 'responds with 200 OK' do
query_host('pages.gitlab.io')
expect(response).to have_gitlab_http_status(200)
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment