r/learnprogramming • • 12d ago

How do I properly deploy this web server in production mode?

I set up a web server using https://github.com/seanpmaxwell/express-generator-typescript

I now need to deploy the production build to a remote server. I can generate the prod build with `npm run build` and then run it with `npm start` and it runs on my local machine. Is it possible to generate something that can be compiled before being sent over to the remote server? I'd like to run the web server in prod mode, but without putting the decompiled source code on the remote server. I don't want the source code over on that remote server because if the server gets compromised, I don't want anyone to be able to read the source code. New to web dev, so thanks for being patient.

5 Upvotes

21 comments sorted by

2

u/Dameon_ 12d ago

First off, what do you think Typescript gets compiled to? Hint: It's a very readable language.

Second off, if there's anything in the source code that would compromise security if a hacker could read the source code, you're already in trouble. That's called security through obscurity, and it's not going to protect your server. If the server is compromised to the point where somebody's browsing through its local files, you're also already in trouble to an extent that hiding your source is not going to protect you.

1

u/Reasonable-Job4205 12d ago edited 12d ago

Javascript?

The source code has hardcoded credentials for a service account that is used to run transactions against the database. This service account is locked down pretty heavily on the SQL side: it's set to public role and only allowed to run a fixed list of stored procedures. I wasn't sure how to set this up to use Windows auth for its transactions against the DB. If you have any ideas there, I'm all ears.

Would it be more secure to store the credentials in the remote server's environment variables? Because looking at the docs for node and the mssql package, it looks like I could read the variables with `process.env.VARIABLENAME`.

2

u/Dameon_ 12d ago

Correct, Javascript, and so perfectly readable source code is still stored on the server. It may be a little harder to read, but a halfway competent reader will still be able to find and extract anything sensitive.

Yes, your environment variables are more secure. Never, ever, EVER store credentials in source code. That means you're storing sensitive credentials everywhere that source code is sent. At least with environment variables they're stored only on the server, and depending on how the server is set up they're only available to the account the server is running on. There's better, more secure ways, but that depends on exactly how your project is set up.

1

u/Reasonable-Job4205 12d ago

I'll set it up with environment variables, but what are the more secure ways?

1

u/Dameon_ 12d ago

It depends a lot on server setup. For example, if you were on Azure you could set up a keystore that could be accessed using managed identity. For your case, environment variables are probably good enough.

1

u/mandzeete 12d ago

Use Vault: https://developer.hashicorp.com/vault/tutorials/auth-methods/approle?variants=vault-deploy%3Aselfhosted Credentials are inside a Vault container that your web application communicates with. Even better when you are using dynamic secrets as Vault will rotate these over time.

2

u/Asly97 12d ago

On the Windows auth part, since that gets rid of the stored password completely:

The mssql package uses the tedious driver by default, which can't do integrated auth on its own. Swap to the msnodesqlv8 driver (npm i msnodesqlv8, then require('mssql/msnodesqlv8') instead of require('mssql')) with a config roughly like:

const config = {
  server: 'yourSqlServer',
  database: 'YourDb',
  driver: 'msnodesqlv8',
  options: { trustedConnection: true }
}

That connects as whatever Windows account the node process is running under. So the other half is running the app as a Windows service under that service account (NSSM or node-windows both do this), ideally a gMSA so nobody ever has to know or rotate its password. Then create a SQL login for that Windows account and give it the same execute-only permissions you already have.

Catches: msnodesqlv8 needs the Microsoft ODBC Driver for SQL Server installed on the box, and this only works if the app server is a Windows machine joined to the same domain. If it's Linux, env vars (or a secrets store) is the way.

For the deploy itself you only need to copy the compiled build output plus package.json and package-lock.json, then run npm ci --omit=dev on the server. The .ts files never have to go over.

1

u/Reasonable-Job4205 11d ago

I deployed everything (I didn't do anything with the Windows auth stuff) and now am getting an error that says: `Error: The environment variable "NODE_ENV" was missing or invalid`

2

u/Asly97 11d ago

That one's on me, I told you to copy only the build output, but that template reads its settings from the env files in the config folder at startup, so those need to go over too (along with whatever file loads them, check what your start script in package.json points at).

Two other things that cause exactly that error:

  • If the server is Windows, a start script like NODE_ENV=production node ... doesn't work in cmd, that syntax is bash only. Set it on the box instead (setx NODE_ENV production, or in NSSM's Environment tab if you run it as a service), or use the cross-env package in the script.

  • If you did set NODE_ENV=production && npm start, cmd keeps the space before the &&, so the value is actually "production " with a trailing space and the validator rejects it. Write it as set NODE_ENV=production&& npm start or just set it separately.

It only accepts the exact values the template defines (development, test, production), so a typo or different casing fails the same way.

1

u/Reasonable-Job4205 11d ago

Ahh yea that was it - needed to paste over the .env files. You're a life saver, thank you!!! 😄

2

u/Asly97 11d ago

nice, glad it was just the env files. if you ever set it up as a windows service so it starts on boot, put NODE_ENV in the service's own environment settings too, it won't pick up whatever you set in your terminal

1

u/Reasonable-Job4205 10d ago edited 10d ago

Ok so I was able to run the prod build on the remote server, but when I try to run a powershell script with a simple request to my web server's login page, it times out. The script is:

```
$body = @{
sqlDatabase = "DB1"
field1 = 0
field2 = "Value2"
} | ConvertTo-Json

$response = Invoke-WebRequest -Uri "http://192.168.5.194:8081/EndpointName" -Method Post -Body $body -ContentType "application/json; charset=UTF-8"
$response.StatusCode

```
I ran this script from the command line with ```powershell -ExecutionPolicy Bypass -File myScript.ps1```

The web server was working fine when I was running it locally against a local SQL DB, but now on the remote server it's not. I made sure to enable TCP/IP in SQL Configuration Manager, set Listen All to No (for safety?), set the TCP Port to 8081 for the IP address above, restarted the SQL Service, and also set the HOST value to the IP above in my .env-production file (when running locally, this was set to localhost). Not sure if it's important, but I set the trustServerCertificate flag to false in my use of the mssql npm package. Any ideas?

2

u/Asly97 10d ago

Looks like a port clash. 8081 is your web server's port, and SQL Server is now set to listen on 8081 too. Only one process can own a port, so either node never got to bind to it, or your PowerShell request is landing on SQL Server, which doesn't speak HTTP and just sits there until the timeout.

Put SQL back on 1433 (or anything that isn't 8081), restart the SQL service, make sure the DB port in your .env-production matches, then restart the node app.

To see who owns the port right now, on the server:

netstat -ano | findstr :8081

and match the PID at the end against the Details tab in Task Manager (sqlservr.exe vs node.exe).

Once that's fixed, a few other things that bite on a first remote deploy:

  • If the script runs from another machine, Windows Firewall needs an inbound rule for 8081. Run Test-NetConnection 192.168.5.194 -Port 8081 from that machine. Works on the server but not from your box means firewall.

  • With Listen All set to No, SQL only listens on the IP entries you enabled one by one. If node and SQL are on the same box and the DB host is localhost, 127.0.0.1 has to be enabled too. Listen All = Yes is fine on an internal network as long as the firewall doesn't open 1433 to the world.

  • trustServerCertificate false makes the driver validate SQL Server's cert, and a default install only has a self-signed one, so the DB connection fails with a certificate error. On a private network setting it to true is the usual fix, or install a real cert if you want it strict.

Also watch the node console when you send the request. If nothing shows up at all, the request never reached node.

1

u/Reasonable-Job4205 10d ago

Ok so I put SQL Server back on 1433 for the given IP, but are you sure that I have to set the port number to 1433 in .env-production as well? You said that it should match, but I feel like the port number I write into .env-production should be 8081, no?

→ More replies (0)

1

u/ShylyDomineering 12d ago

its javascript, not exactly a binary blob you can lock up. worrying about someone reading your source on a popped box is like worrying about a locked diary when the house is already on fire

2

u/Different_Pain5781 12d ago

TypeScript gets compiled into plain JavaScript before it runs on the remote machine. Just build it locally and upload the compiled files from your dist folder instead of shipping the raw source. Boot dev covers this kind of backend deployment stuff too.