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.
This commit is contained in:
parent
9d38778f41
commit
477ba2b346
8 changed files with 132 additions and 1 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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
|
||||
|
|
27
lib/api/internal/pages.rb
Normal file
27
lib/api/internal/pages.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# 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
|
|
@ -1,7 +1,22 @@
|
|||
# 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
|
||||
|
|
29
spec/lib/gitlab/pages_spec.rb
Normal file
29
spec/lib/gitlab/pages_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# 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
|
54
spec/requests/api/internal/pages_spec.rb
Normal file
54
spec/requests/api/internal/pages_spec.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
# 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
|
Loading…
Reference in a new issue