morph1904 / Tyger2

A Reverse Proxy Application
GNU General Public License v2.0
50 stars 11 forks source link

Feature Request: Specify routing internal / external #10

Open andyjhall opened 4 years ago

andyjhall commented 4 years ago

Is it possible to specify whether a request should be fulfilled based on the requester being internal / external to the network?

morph1904 commented 4 years ago

Hi Andy,

I am not sure about this because that is really a DNS thing.

For example, I run an internal DNS server that has my subdomains listed, and they all point to the IP of my Tyger2 instance, that way internal or not, my requests are proxied internally. If they are not listed in my local DNS then by default the local DNS will pass the request to an Internet DNS server and return my static IP creating a loop which my router set up blocks but others may not. Many router setups include a local DNS server you can configure,

There would probably be a way of implementing this but I am not sure of its use case.

Could you explain a little more as to why this feature is needed?

Morph.

morph1904 commented 4 years ago

Hi @andyjhall have you any response to the above? I am mapping out the next phase and if i need to factor this in.

Thanks Morph

andyjhall commented 4 years ago

My plan was just to have some services exposed externally and others internal only. I like to use tyger to redirect to all sorts of internal services but most of them I definitely wouldn’t want exposed externally.

On Mon, Oct 7, 2019 at 14:27 morph1904 notifications@github.com wrote:

Hi @andyjhall https://github.com/andyjhall have you any response to the above? I am mapping out the next phase and if i need to factor this in.

Thanks Morph

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/morph1904/Tyger2/issues/10?email_source=notifications&email_token=AAHRPCH7PQKTFW6NCUMBUT3QNM2KZA5CNFSM4I3SVQXKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAQKKAI#issuecomment-539010305, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHRPCEREKN7YEYXB3SSVUTQNM2KZANCNFSM4I3SVQXA .

-- Andrew Hall andyjhall[at]gmail.com

morph1904 commented 4 years ago

OK I see what you mean now. Leave it with me and I will look into it.

Morph

morph1904 commented 4 years ago

Hi @andyjhall,

I have some good news for you. I have tested some raw functionality and I can make it so that it allows or blocks individual IP's or IP' ranges (if it is set to allow, it blocks all others). This is working in Caddy only at the moment and I need to add all of the frontend and backend functions to allow this to be set on a per address/application basis.

I think we need some discussion on how best to implement this now.

The rule needs an end point eg. / or /login, the allow or block keyword and at least an ip or ip range (192.168.1.1 or 192.168.1.0/24). I have not yet tested if I can place this rule inside the proxy definition or at the address level or if it will let me do both.

For you would this be better applied to the application or the address?

Regards Lee

andyjhall commented 4 years ago

For me personally I normally only assign 1 address to 1 application however Id say it makes more sense at the address level rather than the application level.

On Mon, Oct 7, 2019 at 22:36 morph1904 notifications@github.com wrote:

Hi @andyjhall https://github.com/andyjhall,

I have some good news for you. I have tested some raw functionality and I can make it so that it allows or blocks individual IP's or IP' ranges (if it is set to allow, it blocks all others). This is working in Caddy only at the moment and I need to add all of the frontend and backend functions to allow this to be set on a per address/application basis.

I think we need some discussion on how best to implement this now.

The rule needs an end point eg. / or /login, the allow or block keyword and at least an ip or ip range (192.168.1.1 or 192.168.1.0/24). I have not yet tested if I can place this rule inside the proxy definition or at the address level or if it will let me do both.

For you would this be better applied to the application or the address?

Regards Lee

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/morph1904/Tyger2/issues/10?email_source=notifications&email_token=AAHRPCCUX6VYAD5GPXEJYVTQNOTUJA5CNFSM4I3SVQXKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAR4HPA#issuecomment-539214780, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHRPCAF2G6SWNFWRP3NCETQNOTUJANCNFSM4I3SVQXA .

-- Andrew Hall andyjhall[at]gmail.com

bugs181 commented 4 years ago

Adding to the discussion, I tried to do it the way @morph1904 did it above but it would not play nice with my setup due to SSL validation. It kept thinking it was a DNS-MITM attack and would fail.

How I went about it was this:

The script I wrote up is very specific to my requirements (Specific modem from ISP) but fortunately I made the script smart enough that you could type in any IP address you'd like to update for specific subdomains. It also uses the official Cloudflare API so it should work great for many years to come (barring any feature nerfs or premium costs if they come).

cloudflare-updater.js ```js 'use strict' const cloudflare = require('cloudflare') // https://www.npmjs.com/package/cloudflare // https://github.com/cloudflare/node-cloudflare const cf = cloudflare({ email: '...', key: '...', }) const zones = [ 'domain.com' ] const records = [ 'vpn.domain.com', ] async function updateCloudflareIP(IP) { console.log('Updating cloudflare IP:', IP) try { const zones = await loadZones() for await (let zone of zones) { const zoneInfo = { name: zone.name, id: zone.id } console.log('Reading zone records:', zoneInfo.name) const dnsRecords = await cf.dnsRecords.browse(zoneInfo.id) if (!dnsRecords.success || typeof dnsRecords.result !== 'object' || !Array.isArray(dnsRecords.result)) return console.log('Failed to read DNS configuration') let hasError = false for await (let nsRecord of dnsRecords.result) { if (!records.includes(nsRecord.name)) continue // If Cloudflare IP is already this IP, then don't update it. if (nsRecord.content === IP) continue try { console.log('Updating record:', nsRecord.name) await cf.dnsRecords.edit(zoneInfo.id, nsRecord.id, { type: 'A', name: nsRecord.name, content: IP, ttl: 1, proxied: false, }) } catch (err) { console.error('Failed to update DNS:', nsRecord.name) console.error(err) hasError = true } } if (!hasError) return IP else return null } } catch (err) { console.error(err) return null } } const loadedZones = [] async function loadZones() { if (loadedZones.length > 0) return loadedZones try { console.log('Finding zones') const browseResp = await cf.zones.browse() if (!browseResp.success || typeof browseResp.result !== 'object' || !Array.isArray(browseResp.result)) return console.log('Failed to read zones configuration') for (let zone of browseResp.result) { if (!zones.includes(zone.name)) continue const zoneInfo = { name: zone.name, id: zone.id } loadedZones.push(zoneInfo) } return loadedZones } catch (err) { console.error(err) return [] } } ```


And I use it like so:

async function checkAndUpdateIP() {
  if (!loginToken)
    loginToken = await login()

  const IP = await loadConnectionSummary()

  if (IP === changedIP) {
    //console.log('Nothing to do, IP has not changed')
  } else {
    changedIP = await updateCloudflareIP(IP)
  }
}

// Check IP change every 5 minutes
setInterval(async() => {
  await checkAndUpdateIP()
}, 5 * (60 * 1000))

NOTE: Written with minimal bandwidth limits in mind. It caches results and re-uses them where appropriate.


How this applies to your situation, is that I would use the above script and forward any internal routes to go to the docker containers, apps, etc. Then any public routes you want available but locked, you could forward to a login page of some kind. Either hosted on Caddy, or your own app. Anything you don't want publicly accessible, you just update the IP directly into the app (private LAN or container IP) bypassing any checks.

Hope this helps!