Add HealthPlix fetch callback endpoint and Apache proxy rollout templates
This commit is contained in:
parent
d230d8960d
commit
cc5018568b
|
|
@ -553,6 +553,7 @@ $healthplixPermFilter = 'permission:admin.access,admin.settings';
|
|||
$routes->post('HealthplixGateway/generate_token', 'HealthplixGateway::generateToken', ['filter' => $healthplixPermFilter]);
|
||||
$routes->post('HealthplixGateway/register_patient', 'HealthplixGateway::registerPatient', ['filter' => $healthplixPermFilter]);
|
||||
$routes->post('HealthplixGateway/book_appointment', 'HealthplixGateway::bookAppointment', ['filter' => $healthplixPermFilter]);
|
||||
$routes->post('healthplix/fetch', 'HealthplixCallback::fetch');
|
||||
$routes->get('Opd_prescription/fhir_bundle/(:num)', 'Opd_prescription::fhir_bundle/$1');
|
||||
$routes->get('Opd_prescription/fhir_bundle/(:num)/(:num)', 'Opd_prescription::fhir_bundle/$1/$2');
|
||||
$routes->get('Opd_prescription/fhir_bundle_history/(:num)', 'Opd_prescription::fhir_bundle_history/$1');
|
||||
|
|
|
|||
81
app/Controllers/HealthplixCallback.php
Normal file
81
app/Controllers/HealthplixCallback.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
class HealthplixCallback extends BaseController
|
||||
{
|
||||
public function fetch()
|
||||
{
|
||||
$rawBody = (string) $this->request->getBody();
|
||||
$decoded = $this->request->getJSON(true);
|
||||
$payload = is_array($decoded) ? $decoded : [];
|
||||
|
||||
$expectedSecret = $this->resolveExpectedSecret();
|
||||
$providedSecret = trim((string) $this->request->getHeaderLine('X-Healthplix-Secret'));
|
||||
|
||||
if ($expectedSecret !== '' && ! hash_equals($expectedSecret, $providedSecret)) {
|
||||
$this->logFetchEvent('error', 401, [
|
||||
'message' => 'Invalid callback secret',
|
||||
'payload_bytes' => strlen($rawBody),
|
||||
'payload_keys' => array_keys($payload),
|
||||
], 'Unauthorized callback request');
|
||||
|
||||
return $this->response->setStatusCode(401)->setJSON([
|
||||
'ok' => 0,
|
||||
'error_text' => 'Unauthorized callback request',
|
||||
]);
|
||||
}
|
||||
|
||||
$this->logFetchEvent('success', 200, [
|
||||
'message' => 'Callback payload received',
|
||||
'payload_bytes' => strlen($rawBody),
|
||||
'payload_keys' => array_keys($payload),
|
||||
]);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'message' => 'HealthPlix fetch callback received',
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveExpectedSecret(): string
|
||||
{
|
||||
$secret = trim((string) hospital_setting_value('HEALTHPLIX_FETCH_SECRET', ''));
|
||||
if ($secret !== '') {
|
||||
return $secret;
|
||||
}
|
||||
|
||||
return trim((string) env('HEALTHPLIX_FETCH_SECRET', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $response
|
||||
*/
|
||||
private function logFetchEvent(string $status, int $responseCode, array $response, string $errorMessage = ''): void
|
||||
{
|
||||
try {
|
||||
if (! $this->db || ! method_exists($this->db, 'tableExists') || ! $this->db->tableExists('abdm_api_logs')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->table('abdm_api_logs')->insert([
|
||||
'channel' => 'healthplix',
|
||||
'event_type' => 'healthplix.fetch.callback',
|
||||
'endpoint' => (string) $this->request->getUri()->getPath(),
|
||||
'http_method' => strtoupper($this->request->getMethod()),
|
||||
'entity_type' => 'callback',
|
||||
'entity_id' => trim((string) ($this->request->getHeaderLine('X-Request-Id') ?: 'n/a')),
|
||||
'request_json' => json_encode([
|
||||
'ip' => $this->request->getIPAddress(),
|
||||
'content_type' => (string) $this->request->getHeaderLine('Content-Type'),
|
||||
], JSON_UNESCAPED_SLASHES),
|
||||
'response_code' => $responseCode,
|
||||
'response_json' => json_encode($response, JSON_UNESCAPED_SLASHES),
|
||||
'status' => $status,
|
||||
'error_message' => $errorMessage,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@ class HealthplixSettings extends BaseController
|
|||
return view('Setting/Admin/healthplix_settings', [
|
||||
'healthplix_enabled' => $this->readSettingValue('HEALTHPLIX_ENABLED') === '1' ? '1' : '0',
|
||||
'healthplix_base_url' => $this->readSettingValue('HEALTHPLIX_BASE_URL') ?: 'https://consultation-edge-dev.healthplix.com',
|
||||
'healthplix_fetch_secret_masked' => $this->maskKey($this->readSettingValue('HEALTHPLIX_FETCH_SECRET')),
|
||||
'healthplix_fetch_secret_exists' => $this->readSettingValue('HEALTHPLIX_FETCH_SECRET') !== '',
|
||||
'healthplix_tenant_id' => $this->readSettingValue('HEALTHPLIX_TENANT_ID'),
|
||||
'healthplix_tenant_key_masked' => $this->maskKey($this->readSettingValue('HEALTHPLIX_TENANT_KEY')),
|
||||
'healthplix_tenant_key_exists' => $this->readSettingValue('HEALTHPLIX_TENANT_KEY') !== '',
|
||||
|
|
@ -47,6 +49,7 @@ class HealthplixSettings extends BaseController
|
|||
$fields = [
|
||||
'HEALTHPLIX_ENABLED' => $this->request->getPost('healthplix_enabled') === '1' ? '1' : '0',
|
||||
'HEALTHPLIX_BASE_URL' => trim((string) $this->request->getPost('healthplix_base_url')),
|
||||
'HEALTHPLIX_FETCH_SECRET' => trim((string) $this->request->getPost('healthplix_fetch_secret')),
|
||||
'HEALTHPLIX_TENANT_ID' => trim((string) $this->request->getPost('healthplix_tenant_id')),
|
||||
'HEALTHPLIX_TENANT_KEY' => trim((string) $this->request->getPost('healthplix_tenant_key')),
|
||||
'HEALTHPLIX_DOCTOR_IDENTIFIER' => trim((string) $this->request->getPost('healthplix_doctor_identifier')),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,20 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Fetch Callback Secret</label>
|
||||
<input type="password" class="form-control" id="healthplix_fetch_secret" placeholder="Enter to set/update callback secret">
|
||||
<div class="form-text" id="healthplix_fetch_secret_hint">
|
||||
<?php if (!empty($healthplix_fetch_secret_exists)) : ?>
|
||||
<span class="text-success">✓ Secret saved (<?= esc($healthplix_fetch_secret_masked ?? '') ?>). Leave blank to keep existing.</span>
|
||||
<?php else : ?>
|
||||
No callback secret configured yet.
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<h6 class="mb-2">API Endpoints <span class="badge bg-secondary">V1</span></h6>
|
||||
<div class="form-text mb-2">Paths are relative to the Base URL above. Pre-filled from HealthPlix API Specification Doc v1.7.</div>
|
||||
|
|
@ -140,6 +154,7 @@
|
|||
return {
|
||||
healthplix_enabled: $('#healthplix_enabled').is(':checked') ? '1' : '0',
|
||||
healthplix_base_url: ($('#healthplix_base_url').val() || '').trim(),
|
||||
healthplix_fetch_secret: ($('#healthplix_fetch_secret').val() || '').trim(),
|
||||
healthplix_tenant_header_name: ($('#healthplix_tenant_header_name').val() || '').trim(),
|
||||
healthplix_tenant_id: ($('#healthplix_tenant_id').val() || '').trim(),
|
||||
healthplix_tenant_key: ($('#healthplix_tenant_key').val() || '').trim(),
|
||||
|
|
@ -163,6 +178,15 @@
|
|||
}
|
||||
});
|
||||
|
||||
$('#healthplix_fetch_secret').on('input', function() {
|
||||
var val = $(this).val().trim();
|
||||
if (val.length > 0) {
|
||||
$('#healthplix_fetch_secret_hint').html('<span class="text-warning">✎ New secret will be saved on Save Settings.</span>');
|
||||
} else {
|
||||
$('#healthplix_fetch_secret_hint').html('<span class="text-muted">Leave blank to keep existing callback secret.</span>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#btn_save_healthplix').on('click', function() {
|
||||
postJson('<?= base_url('setting/admin/healthplix-settings/save') ?>', payload(), function(data) {
|
||||
if ((data.update || 0) !== 1) {
|
||||
|
|
@ -178,7 +202,17 @@
|
|||
} else {
|
||||
$('#healthplix_tenant_key_hint').html('<span class="text-muted">Leave blank to keep existing key.</span>');
|
||||
}
|
||||
|
||||
var savedSecret = ($('#healthplix_fetch_secret').val() || '').trim();
|
||||
if (savedSecret.length > 0) {
|
||||
var maskedSecret = savedSecret.length <= 8 ? '********' : (savedSecret.substring(0, 4) + '****' + savedSecret.slice(-4));
|
||||
$('#healthplix_fetch_secret_hint').html('<span class="text-success">✓ Secret saved (' + maskedSecret + '). Leave blank to keep existing.</span>');
|
||||
} else {
|
||||
$('#healthplix_fetch_secret_hint').html('<span class="text-muted">Leave blank to keep existing callback secret.</span>');
|
||||
}
|
||||
|
||||
$('#healthplix_tenant_key').val('');
|
||||
$('#healthplix_fetch_secret').val('');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
156
docs/HEALTHPLIX_APACHE_PROXY_CENTOS7.md
Normal file
156
docs/HEALTHPLIX_APACHE_PROXY_CENTOS7.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
# HealthPlix Reverse Proxy Setup (CentOS 7 + Apache)
|
||||
|
||||
This guide is for a central public Apache server that forwards each hospital domain to its own local HMS over VPN.
|
||||
|
||||
Example topology:
|
||||
- Public Apache server: 139.59.13.39
|
||||
- Hospital A domain: 100001.dhms.in -> A record -> 139.59.13.39
|
||||
- Hospital A HMS private VPN IP: 10.7.0.32
|
||||
- Hospital B domain: 100002.dhms.in -> A record -> 139.59.13.39
|
||||
- Hospital B HMS private VPN IP: 10.8.0.45
|
||||
|
||||
Recommended exposure model:
|
||||
- Expose only callback/fetch path to internet.
|
||||
- Do not expose full HMS unless required.
|
||||
|
||||
## 1) Install and enable required Apache modules
|
||||
|
||||
Run on CentOS 7 Apache host:
|
||||
|
||||
sudo yum install -y mod_ssl
|
||||
sudo httpd -M | egrep "proxy|ssl|headers|rewrite"
|
||||
|
||||
If any are missing, enable in Apache config and restart httpd.
|
||||
|
||||
## 2) Reusable virtual-host template (callback-only)
|
||||
|
||||
Create one file per hospital in /etc/httpd/conf.d/
|
||||
|
||||
Template file name:
|
||||
- /etc/httpd/conf.d/DOMAIN.conf
|
||||
|
||||
Template content:
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName DOMAIN
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ https://DOMAIN/$1 [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName DOMAIN
|
||||
|
||||
SSLEngine On
|
||||
SSLCertificateFile /etc/letsencrypt/live/DOMAIN/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/DOMAIN/privkey.pem
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
SSLProxyEngine On
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
# Callback/fetch endpoint only
|
||||
ProxyPass /healthplix/fetch http://VPN_HMS_IP/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch http://VPN_HMS_IP/healthplix/fetch
|
||||
|
||||
ErrorLog /var/log/httpd/DOMAIN-healthplix-error.log
|
||||
CustomLog /var/log/httpd/DOMAIN-healthplix-access.log combined
|
||||
</VirtualHost>
|
||||
|
||||
Replace:
|
||||
- DOMAIN with hospital DNS name (example 100001.dhms.in)
|
||||
- VPN_HMS_IP with local HMS VPN IP (example 10.7.0.32)
|
||||
|
||||
## 3) Hardened variant (recommended)
|
||||
|
||||
Use this inside the :443 VirtualHost when HealthPlix source IPs and secret are available.
|
||||
|
||||
# 3.1 Limit endpoint to known HealthPlix source IPs
|
||||
<Location /healthplix/fetch>
|
||||
Require all denied
|
||||
Require ip 203.0.113.10
|
||||
Require ip 203.0.113.11
|
||||
</Location>
|
||||
|
||||
# 3.2 Shared-secret header validation at edge
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} ^/healthplix/fetch$
|
||||
RewriteCond %{HTTP:X-Healthplix-Secret} !^REPLACE_WITH_STRONG_SECRET$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
# 3.3 Restrict methods
|
||||
<Location /healthplix/fetch>
|
||||
<LimitExcept POST>
|
||||
Require all denied
|
||||
</LimitExcept>
|
||||
</Location>
|
||||
|
||||
# 3.4 Body size limits (adjust to payload)
|
||||
LimitRequestBody 1048576
|
||||
|
||||
Notes:
|
||||
- Replace placeholder IPs with official HealthPlix outbound IPs.
|
||||
- Replace secret with a long random value.
|
||||
- Validate same secret again in HMS controller before processing.
|
||||
|
||||
## 4) Multi-hospital mapping examples
|
||||
|
||||
Hospital A:
|
||||
- Domain: 100001.dhms.in
|
||||
- Target: 10.7.0.32
|
||||
|
||||
Hospital B:
|
||||
- Domain: 100002.dhms.in
|
||||
- Target: 10.8.0.45
|
||||
|
||||
Hospital C:
|
||||
- Domain: 100003.dhms.in
|
||||
- Target: 10.9.0.20
|
||||
|
||||
All domains can point to same public IP 139.59.13.39.
|
||||
Apache routes by ServerName.
|
||||
|
||||
## 5) Enable and reload Apache
|
||||
|
||||
sudo apachectl configtest
|
||||
sudo systemctl reload httpd
|
||||
|
||||
If using firewalld:
|
||||
|
||||
sudo firewall-cmd --add-service=http --permanent
|
||||
sudo firewall-cmd --add-service=https --permanent
|
||||
sudo firewall-cmd --reload
|
||||
|
||||
## 6) HMS application-side settings
|
||||
|
||||
For outbound sync from HMS to HealthPlix:
|
||||
- Keep HEALTHPLIX_BASE_URL as official HealthPlix API base URL.
|
||||
- Keep token/patient/appointment paths as currently configured.
|
||||
|
||||
For inbound fetch/callback from HealthPlix:
|
||||
- Give HealthPlix callback URL as:
|
||||
https://DOMAIN/healthplix/fetch
|
||||
|
||||
## 7) Validation checklist
|
||||
|
||||
1. DNS resolves correctly:
|
||||
nslookup DOMAIN
|
||||
2. TLS certificate is valid:
|
||||
curl -Iv https://DOMAIN/healthplix/fetch
|
||||
3. Reverse proxy reaches HMS private node:
|
||||
curl -i -X POST https://DOMAIN/healthplix/fetch -H "X-Healthplix-Secret: REPLACE_WITH_STRONG_SECRET" -d '{}'
|
||||
4. Apache logs show routed request in domain-specific log file.
|
||||
5. HMS receives request and returns expected response.
|
||||
|
||||
## 8) Operations notes
|
||||
|
||||
- Keep one vhost file per hospital for isolation.
|
||||
- Keep one log file pair per hospital for easier troubleshooting.
|
||||
- Rotate logs and monitor 4xx/5xx rates.
|
||||
- Keep VPN routes and ACLs documented and version-controlled.
|
||||
- Do not log bearer tokens or patient-sensitive payloads in plaintext.
|
||||
51
docs/apache-proxy-templates/generated/100001.dhms.in.conf
Normal file
51
docs/apache-proxy-templates/generated/100001.dhms.in.conf
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<VirtualHost *:80>
|
||||
ServerName 100001.dhms.in
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ https://100001.dhms.in/$1 [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName 100001.dhms.in
|
||||
|
||||
SSLEngine On
|
||||
SSLCertificateFile /etc/letsencrypt/live/100001.dhms.in/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/100001.dhms.in/privkey.pem
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
SSLProxyEngine On
|
||||
SSLProxyVerify require
|
||||
SSLProxyCheckPeerName on
|
||||
SSLProxyCheckPeerExpire on
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
# Replace with official HealthPlix source IPs
|
||||
<Location /healthplix/fetch>
|
||||
Require all denied
|
||||
Require ip 203.0.113.10
|
||||
Require ip 203.0.113.11
|
||||
<LimitExcept POST>
|
||||
Require all denied
|
||||
</LimitExcept>
|
||||
</Location>
|
||||
|
||||
# Replace with your strong shared secret
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} ^/healthplix/fetch$
|
||||
RewriteCond %{HTTP:X-Healthplix-Secret} !^CHANGE_ME_TO_STRONG_SECRET$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
LimitRequestBody 1048576
|
||||
|
||||
# Use internal DNS name reachable via VPN so backend TLS/certificate can be used.
|
||||
ProxyPass /healthplix/fetch https://khrc.dhms.in/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch https://khrc.dhms.in/healthplix/fetch
|
||||
|
||||
ErrorLog /var/log/httpd/100001-healthplix-error.log
|
||||
CustomLog /var/log/httpd/100001-healthplix-access.log combined
|
||||
</VirtualHost>
|
||||
47
docs/apache-proxy-templates/generated/100002.dhms.in.conf
Normal file
47
docs/apache-proxy-templates/generated/100002.dhms.in.conf
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<VirtualHost *:80>
|
||||
ServerName 100002.dhms.in
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ https://100002.dhms.in/$1 [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName 100002.dhms.in
|
||||
|
||||
SSLEngine On
|
||||
SSLCertificateFile /etc/letsencrypt/live/100002.dhms.in/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/100002.dhms.in/privkey.pem
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
SSLProxyEngine On
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
# Replace with official HealthPlix source IPs
|
||||
<Location /healthplix/fetch>
|
||||
Require all denied
|
||||
Require ip 203.0.113.10
|
||||
Require ip 203.0.113.11
|
||||
<LimitExcept POST>
|
||||
Require all denied
|
||||
</LimitExcept>
|
||||
</Location>
|
||||
|
||||
# Replace with your strong shared secret
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} ^/healthplix/fetch$
|
||||
RewriteCond %{HTTP:X-Healthplix-Secret} !^CHANGE_ME_TO_STRONG_SECRET$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
LimitRequestBody 1048576
|
||||
|
||||
ProxyPass /healthplix/fetch http://10.8.0.45/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch http://10.8.0.45/healthplix/fetch
|
||||
|
||||
ErrorLog /var/log/httpd/100002-healthplix-error.log
|
||||
CustomLog /var/log/httpd/100002-healthplix-access.log combined
|
||||
</VirtualHost>
|
||||
47
docs/apache-proxy-templates/generated/100003.dhms.in.conf
Normal file
47
docs/apache-proxy-templates/generated/100003.dhms.in.conf
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<VirtualHost *:80>
|
||||
ServerName 100003.dhms.in
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ https://100003.dhms.in/$1 [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName 100003.dhms.in
|
||||
|
||||
SSLEngine On
|
||||
SSLCertificateFile /etc/letsencrypt/live/100003.dhms.in/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/100003.dhms.in/privkey.pem
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
SSLProxyEngine On
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
# Replace with official HealthPlix source IPs
|
||||
<Location /healthplix/fetch>
|
||||
Require all denied
|
||||
Require ip 203.0.113.10
|
||||
Require ip 203.0.113.11
|
||||
<LimitExcept POST>
|
||||
Require all denied
|
||||
</LimitExcept>
|
||||
</Location>
|
||||
|
||||
# Replace with your strong shared secret
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} ^/healthplix/fetch$
|
||||
RewriteCond %{HTTP:X-Healthplix-Secret} !^CHANGE_ME_TO_STRONG_SECRET$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
LimitRequestBody 1048576
|
||||
|
||||
ProxyPass /healthplix/fetch http://10.9.0.20/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch http://10.9.0.20/healthplix/fetch
|
||||
|
||||
ErrorLog /var/log/httpd/100003-healthplix-error.log
|
||||
CustomLog /var/log/httpd/100003-healthplix-access.log combined
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# Local-First Validation Checklist (My Host + VPN Server)
|
||||
|
||||
## A) My Host local test (10.7.0.31 machine)
|
||||
|
||||
1. Start CI4 locally:
|
||||
php spark serve --host localhost --port 8080
|
||||
|
||||
2. Add hosts entry from my-host/hosts.example:
|
||||
127.0.0.1 100001.localtest
|
||||
|
||||
3. Enable local Apache vhost:
|
||||
my-host/100001.localtest.conf
|
||||
|
||||
4. Reload local Apache.
|
||||
|
||||
5. Test through local domain (same callback path):
|
||||
curl -i -X POST http://100001.localtest/healthplix/fetch -H "X-Healthplix-Secret: CHANGE_ME_TO_STRONG_SECRET" -d '{}'
|
||||
|
||||
Expected:
|
||||
- Request reaches CI4 route at localhost:8080/healthplix/fetch.
|
||||
|
||||
## B) VPN server test (10.7.0.1)
|
||||
|
||||
1. Install config:
|
||||
vpn-server/100001.dhms.in.conf -> /etc/httpd/conf.d/100001.dhms.in.conf
|
||||
|
||||
2. Validate and reload Apache:
|
||||
apachectl configtest
|
||||
systemctl reload httpd
|
||||
|
||||
3. From your VPN client machine (10.7.0.31), verify DNS and TLS:
|
||||
nslookup 100001.dhms.in
|
||||
curl -Iv https://100001.dhms.in/healthplix/fetch
|
||||
|
||||
4. Verify callback path through edge proxy:
|
||||
curl -i -X POST https://100001.dhms.in/healthplix/fetch -H "X-Healthplix-Secret: CHANGE_ME_TO_STRONG_SECRET" -d '{}'
|
||||
|
||||
Expected:
|
||||
- Edge server accepts request.
|
||||
- Request is forwarded to khrc.dhms.in over VPN.
|
||||
- HMS responds with your fetch endpoint response.
|
||||
|
||||
## Notes
|
||||
|
||||
- Replace placeholder source IPs and secret before production.
|
||||
- If backend HMS certificate is private/self-signed, import internal CA cert to proxy trust store.
|
||||
- Keep healthplix/fetch endpoint method-restricted to POST.
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Local machine Apache config for pre-production testing
|
||||
# Purpose: test callback flow locally by forwarding to CI4 dev server (php spark serve)
|
||||
# Place on local Apache host and adapt paths/ports for your OS.
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName 100001.localtest
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "http"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
# Keep same route shape as production, but target local spark server.
|
||||
ProxyPass /healthplix/fetch http://localhost:8080/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch http://localhost:8080/healthplix/fetch
|
||||
|
||||
ErrorLog logs/100001-localtest-error.log
|
||||
CustomLog logs/100001-localtest-access.log combined
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Add to your local hosts file for local-domain testing
|
||||
# Windows path: C:\Windows\System32\drivers\etc\hosts
|
||||
# Linux path: /etc/hosts
|
||||
|
||||
127.0.0.1 100001.localtest
|
||||
|
||||
# Optional for testing real VPN domain from your machine over VPN
|
||||
# 10.7.0.1 100001.dhms.in
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# CentOS 7 VPN/public proxy server config
|
||||
# Place on VPN server: /etc/httpd/conf.d/100001.dhms.in.conf
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName 100001.dhms.in
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ https://100001.dhms.in/$1 [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName 100001.dhms.in
|
||||
|
||||
SSLEngine On
|
||||
SSLCertificateFile /etc/letsencrypt/live/100001.dhms.in/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/100001.dhms.in/privkey.pem
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
SSLProxyEngine On
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
# Replace with official HealthPlix source IPs
|
||||
<Location /healthplix/fetch>
|
||||
Require all denied
|
||||
Require ip 203.0.113.10
|
||||
Require ip 203.0.113.11
|
||||
<LimitExcept POST>
|
||||
Require all denied
|
||||
</LimitExcept>
|
||||
</Location>
|
||||
|
||||
# Replace with your strong shared secret
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} ^/healthplix/fetch$
|
||||
RewriteCond %{HTTP:X-Healthplix-Secret} !^CHANGE_ME_TO_STRONG_SECRET$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
LimitRequestBody 1048576
|
||||
|
||||
# Production target: HMS server over VPN
|
||||
ProxyPass /healthplix/fetch https://khrc.dhms.in/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch https://khrc.dhms.in/healthplix/fetch
|
||||
|
||||
ErrorLog /var/log/httpd/100001-healthplix-error.log
|
||||
CustomLog /var/log/httpd/100001-healthplix-access.log combined
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Copy to /etc/httpd/conf.d/DOMAIN.conf and replace placeholders.
|
||||
# This variant includes source IP allowlist and shared-secret header check.
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName __DOMAIN__
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ https://__DOMAIN__/$1 [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName __DOMAIN__
|
||||
|
||||
SSLEngine On
|
||||
SSLCertificateFile /etc/letsencrypt/live/__DOMAIN__/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/__DOMAIN__/privkey.pem
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
SSLProxyEngine On
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
# Allow only known HealthPlix source IPs (replace with official list)
|
||||
<Location /healthplix/fetch>
|
||||
Require all denied
|
||||
Require ip __HEALTHPLIX_IP_1__
|
||||
Require ip __HEALTHPLIX_IP_2__
|
||||
<LimitExcept POST>
|
||||
Require all denied
|
||||
</LimitExcept>
|
||||
</Location>
|
||||
|
||||
# Block requests without valid shared secret header
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} ^/healthplix/fetch$
|
||||
RewriteCond %{HTTP:X-Healthplix-Secret} !^__STRONG_SHARED_SECRET__$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
# Limit payload size to 1 MB (adjust as needed)
|
||||
LimitRequestBody 1048576
|
||||
|
||||
ProxyPass /healthplix/fetch http://__VPN_HMS_IP__/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch http://__VPN_HMS_IP__/healthplix/fetch
|
||||
|
||||
ErrorLog /var/log/httpd/__DOMAIN__-healthplix-error.log
|
||||
CustomLog /var/log/httpd/__DOMAIN__-healthplix-access.log combined
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# Copy to /etc/httpd/conf.d/DOMAIN.conf and replace placeholders.
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName __DOMAIN__
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ https://__DOMAIN__/$1 [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName __DOMAIN__
|
||||
|
||||
SSLEngine On
|
||||
SSLCertificateFile /etc/letsencrypt/live/__DOMAIN__/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/__DOMAIN__/privkey.pem
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
SSLProxyEngine On
|
||||
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
RequestHeader append X-Forwarded-For %{REMOTE_ADDR}s
|
||||
|
||||
ProxyTimeout 60
|
||||
Timeout 60
|
||||
|
||||
ProxyPass /healthplix/fetch http://__VPN_HMS_IP__/healthplix/fetch
|
||||
ProxyPassReverse /healthplix/fetch http://__VPN_HMS_IP__/healthplix/fetch
|
||||
|
||||
ErrorLog /var/log/httpd/__DOMAIN__-healthplix-error.log
|
||||
CustomLog /var/log/httpd/__DOMAIN__-healthplix-access.log combined
|
||||
</VirtualHost>
|
||||
Loading…
Reference in New Issue
Block a user