Myster-Tee / TinfoilWebServer

Install your packages from your own server
GNU General Public License v3.0
124 stars 26 forks source link

TinfoilWebServer

Description

Build your custom Nintendo Switch shop.

TinfoilWebServer is a simple and efficient cross platform web server application aiming at serving your personal Nintendo Switch packages (NSP, NSZ, XCI, etc.) to Tinfoil.

All served files must have "[titleid]" in the file name to be recognized by Tinfoil to show up in "New Games", "New DLC", and "New Updates".
Official Tinfoil documentation here.

Download

Releases page here.

* If you're downloading using macOS Safari, download your desired package by right-clicking on the link and selecting "Download Linked File", so Safari doesn't unzip the package automatically. Then you can unzip it afterwards using macOS's built-in Archive Utility.

Requirements

The requirements depend on the version you choose to download.

Framework-Dependent version

This version is lightweight but you'll need to install the ASP.NET Core Runtime 8.X.X before running the server.

Framework-Independent version

No requirements but heavyweight.

Running the server

  1. Unzip the desired distribution to a location of your choice
  2. Create a TinfoilWebServer.config.json according to your needs
  3. Start the server according to the chosen distribution

By default, the TinfoilWebServer.config.json file will be searched in the program's current directory.
A template of TinfoilWebServer.config.json can be downloaded in the assets of the releases page here.

Windows

Run

TinfoilWebServer.exe

Linux and macOS

Run

./TinfoilWebServer

Portable version - Windows, Linux and macOS (Framework required)

Run

dotnet TinfoilWebServer.dll

Command line options

-c, --config        Custom location of the configuration file.
-d, --currentDir    Change the current directory.
-s, --winService    Run the server as a Windows service.
--sha256            Compute SHA256 passwords interactively.
--help              Display this help screen.
--version           Display version information.

Setting up Tinfoil on your Switch

  1. Launch Tinfoil
  2. Go to File Browser
  3. Press [-] button to add a new server
  4. Set Protocol to HTTP or HTTPS according to the server configuration
  5. Set Host to any host pointing to your server (or the server IP address)
    The server IP address is logged at server startup.
  6. If authentication is enabled, set Username and Password to one of the allowed users
  7. Set Title to a name of your choice

Download speed note: having a download/installation speed of ~10MB from Tinfoil is normal.
This low speed is only due to hardware limitation of Nintendo Switch, and is not at all related to some server limitation.

TinfoilWebServer.config.json format

{
  "ServedDirectories": string[],        // ex: ["C:\\SomeDir\\WindowsDirWithPackages", "/dev/sda1/LinuxDirWithPackages", ".", "/Users/yourname/Documents/macOSDirWithPackages"] !!! Don't forget to escape backslashes with another one !!! No need to escape spaces
  "StripDirectoryNames": boolean,       // «true» to remove directories names in URLs of served files, «false» otherwise
  "ServeEmptyDirectories": boolean,     // «true» to serve empty directories, «false» otherwise (has no effect when "StripDirectoryNames" is «true»)
  "AllowedExt": string[],               // List of file extensions to serve (default is ["xci", "nsz", "nsp", "xcz", "zip"])
  "MessageOfTheDay": string,            // The welcome message displayed when Tinfoil successfully contacts the server
  "CustomIndexPath": string,            // The path to a custom JSON file to be merged with the served index
  "Cache": {
    "AutoDetectChanges": boolean,       // «true» to auto-refresh the list of served files when a file system change is detected in the served directories, «false» otherwise
    "PeriodicRefreshDelay": string      // Periodic delay for forcing the refresh of served files, format is «[d'.']hh':'mm':'ss['.'fffffff]», ex: "01:30:15" for 1h30m15s, set «null» to disable
  },
  "Authentication": {
    "Enabled": boolean,                 // «true» to enable authentication, «false» otherwise
    "WebBrowserAuthEnabled": boolean,   // «true» to enable the native Web Browser login prompt when not authenticated (has no effect when "Authentication.Enabled" is «false»)
    "PwdType": string,                  // Defines the format of user passwords. Can be either "Sha256" or "Plaintext". Default is "Sha256".
    "Users": [                          // List of allowed users (use a comma as separator for declaring multiple users)
      {
        "Name": string,                 // The user name
        "Pwd": string,                  // The password
        "MaxFingerprints": number,      // The maximum number of fingerprints allowed for this user (default is 1)
        "MessageOfTheDay": string,      // Custom message for the user
        "CustomIndexPath": string       // The path to a custom JSON file for this user to be merged with the served index
      }
    ]
  },
  "FingerprintsFilter": {
    "Enabled" : boolean,                // «true» to enable fingerprints validation filter, «false» otherwise (default is true)
    "FingerprintsFilePath": string      // The path to the file where to save allowed fingerprints
    "MaxFingerprints" : number          // The maximum number of global fingerprints allowed
  },
  "Blacklist": {
    "Enabled": boolean,                 // Enable or disable the IP blacklisting feature
    "FilePath": string,                 // The path of the file where to save blacklisted IPs
    "MaxConsecutiveFailedAuth": number, // The maximum number of consecutive unauthenticated requests to reach for blacklisting an IP
    "IsBehindProxy": boolean            // When set to true, incoming IP address will be taken fromFo "X-Forwarded-For" header otherwise it will be taken from TCP/IP protocol
  },
  "Kestrel": {                          // Web server configuration, see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-6.0 for more information
    "Endpoints": {
      "Http": {
        "Url": string                   // The HTTP host (or IP address) and port that the server should listen to (ex: "http://0.0.0.0:80", "http://*:80/", "http://somedomain.com")
      },
      "HttpsInlineCertAndKeyFile": {    // See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-6.0 for more examples and possibilities
        "Url": string,                  // The HTTPS host (or IP address) and port that the server should listen to (ex: "https://somedomain.com", "https://somedomain.com:8081")
        "Certificate": {
          "Path": string,               // The path to the certificate file (ex: "MyCertificate.cer")
          "KeyPath": string             // The path to the private key file (ex: "MyPrivateKey.key")
        }
      }
    },
    "Limits": {
      "MaxConcurrentConnections": number, // Sets the maximum number of open connection
    }
  },
  "Logging": {                          // See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0 for more information
    "LogLevel": {
      "Default": string                 // The global log level, can be one of "Trace", "Debug", "Information", "Warning", "Error", "Critical", or "None"
    },
    "Console": {                        // See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0 for more information
      "LogLevel": {
        "Default": string
      },
      "FormatterOptions": {
        "Format": string,               // The custom log format (see below for more information)
        "ExceptionFormat": string       // The custom exception format (see below for more information)
      } 
    },
    "File": {                           // See https://github.com/nreco/logging#how-to-use for more information
      "Path": string,
      "Append": boolean,
      "MinLevel": string,
      "FileSizeLimitBytes": number,
      "MaxRollingFiles": number,
      "FormatterOptions": {
        "Format": string,               // The custom log format (see below for more information)
        "ExceptionFormat": string       // The custom exception format (see below for more information)
      } 
    }
  }
}

When "Kestrel" configuration is omitted, server default listens to http://localhost:5000/ and https://localhost:5001.

Custom Index

Specifying a custom index in the configuration file allows to set (or combine) any extra property described in Tinfoil Custom Index documentation.
For example, using the JSON below, it is possible to enrich the served files with custom files out of served directories.

{
  "files": ["https://some/other/url1", "https://some/gdrive/folder"] // Will be combined with served files
}

IP Blacklisting

File format is text, one line per blacklisted IP. Empty lines will be ignored. Use # to write comments.

Fingerprints

A fingerprint consists in a unique Nintendo Switch device identifier. Tinfoil emits a fingerprint only when requesting the index, but not when requesting files. Thus specifying fingerprints in configuration will not prevent a user from downloading files.

When fingerprints are both allowed globally and at user level, server will check for a valid fingerprint among allowed user fingerprints and globally allowed fingerprints.

Custom log format

You can specify a custom log format using keywords between braces.
Some keywords accept options in the format of {SomeKeyword:SomeOptions}.

Example:

"Format" : "> {Date:yyyy-MM-dd@HH:mm:ss}-{LogLevel:U}-{Category}: {Message}{NewLine}{Exception}"

Supported keywords for Format field:

Supported keywords for ExceptionFormat field:

Running as a Windows service

Server can be run as a Windows service. To to this, service must first be registered using sc.exe, PowerShell New-Service cmdlet or any other method.
Don't forget to specify the --winService command line parameter when registering the service.

Note: by default, Windows services are run in %WinDir%\System32, you can change the current directory using --currentDir command line parameter.

# PowerShell service registration example
New-Service TinfoilWebServer -BinaryPathName "<FullPathToExe> --winService --config ""<SomePathToConfigJson>"" --currentDir ""<SomePathToCurrentDir>"""

Sharing game saves

TinfoilWebServer can serve game saves by allowing "zip" file extension (by default).
To share your own saves, copy Tinfoil saves from SDCard/switch/tinfoil/saves/**/*.zip to any of the served directories, preserving original file name.

Security considerations and recommendations

If you plan to open your server to the Internet network (WAN) instead of local network (LAN) only, I would highly recommend you to:

  1. Setup HTTPS only
  2. Enable authentication to restrict access to some specific users
  3. Serve directories containining only Nintendo Switch files (without personal data)
  4. Restrict served file extensions to Nintendo Switch files
  5. Setup StripDirectoryNames setting to true to hide your personal folder tree organization
  6. Enable IP blacklistng feature
  7. Enable fingerprints filter feature
  8. Set Authentication.PwdType to "Sha256" to avoid storing plaintext user passwords in your config file

Similar Projects

If you want to create your personal NSP Shop then check out these other similar projects: